From da02459ddad80f0637172d3e434ba9e3a3d95244 Mon Sep 17 00:00:00 2001 From: jinhoonbang Date: Fri, 16 Feb 2024 13:00:48 -0800 Subject: [PATCH] add support for backwards mode in avalanche subnets (#12039) --- core/scripts/common/avalanche_subnet.go | 126 ++++++++++++++++++++++++ core/scripts/common/helpers.go | 29 +++++- 2 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 core/scripts/common/avalanche_subnet.go diff --git a/core/scripts/common/avalanche_subnet.go b/core/scripts/common/avalanche_subnet.go new file mode 100644 index 00000000000..238f193d2b8 --- /dev/null +++ b/core/scripts/common/avalanche_subnet.go @@ -0,0 +1,126 @@ +package common + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// AvaSubnetHeader is a copy of [github.com/ava-labs/subnet-evm/core/types.Header] to avoid importing the whole module. +type AvaSubnetHeader struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom AvaBloom `json:"logsBloom" gencodec:"required"` + Difficulty *big.Int `json:"difficulty" gencodec:"required"` + Number *big.Int `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Time uint64 `json:"timestamp" gencodec:"required"` + Extra []byte `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash"` + Nonce AvaBlockNonce `json:"nonce"` + BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` + BlockGasCost *big.Int `json:"blockGasCost" rlp:"optional"` +} + +func (h *AvaSubnetHeader) UnmarshalJSON(input []byte) error { + type Header struct { + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase *common.Address `json:"miner" gencodec:"required"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom *AvaBloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *AvaBlockNonce `json:"nonce"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + BlockGasCost *hexutil.Big `json:"blockGasCost" rlp:"optional"` + } + var dec Header + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ParentHash == nil { + return errors.New("missing required field 'parentHash' for Header") + } + h.ParentHash = *dec.ParentHash + if dec.UncleHash == nil { + return errors.New("missing required field 'sha3Uncles' for Header") + } + h.UncleHash = *dec.UncleHash + if dec.Coinbase == nil { + return errors.New("missing required field 'miner' for Header") + } + 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 { + return errors.New("missing required field 'transactionsRoot' for Header") + } + h.TxHash = *dec.TxHash + if dec.ReceiptHash == nil { + return errors.New("missing required field 'receiptsRoot' for Header") + } + h.ReceiptHash = *dec.ReceiptHash + if dec.Bloom == nil { + return errors.New("missing required field 'logsBloom' for Header") + } + h.Bloom = *dec.Bloom + if dec.Difficulty == nil { + return errors.New("missing required field 'difficulty' for Header") + } + 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 { + return errors.New("missing required field 'gasUsed' for Header") + } + 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 { + return errors.New("missing required field 'extraData' for Header") + } + 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) + } + if dec.BlockGasCost != nil { + h.BlockGasCost = (*big.Int)(dec.BlockGasCost) + } + return nil +} + +func (h *AvaSubnetHeader) Hash() common.Hash { + return rlpHash(h) +} diff --git a/core/scripts/common/helpers.go b/core/scripts/common/helpers.go index 0ed9929956a..0967991e62b 100644 --- a/core/scripts/common/helpers.go +++ b/core/scripts/common/helpers.go @@ -498,7 +498,20 @@ func GetRlpHeaders(env Environment, blockNumbers []*big.Int, getParentBlocks boo //fmt.Println("Calculated BH:", bh.String(), // "fetched BH:", h.Hash(), // "block number:", new(big.Int).Set(blockNum).Add(blockNum, offset).String()) + } else if IsAvaxSubnet(env.ChainID) { + var h AvaSubnetHeader + // Get child block since it's the one that has the parent hash in its header. + nextBlockNum := new(big.Int).Set(blockNum).Add(blockNum, offset) + err2 := env.Jc.CallContext(context.Background(), &h, "eth_getBlockByNumber", hexutil.EncodeBig(nextBlockNum), false) + if err2 != nil { + return nil, hashes, fmt.Errorf("failed to get header: %+v", err2) + } + rlpHeader, err2 = rlp.EncodeToBytes(h) + if err2 != nil { + return nil, hashes, fmt.Errorf("failed to encode rlp: %+v", err2) + } + hashes = append(hashes, h.Hash().String()) } else if IsPolygonEdgeNetwork(env.ChainID) { // Get child block since it's the one that has the parent hash in its header. @@ -570,12 +583,20 @@ func CalculateLatestBlockHeader(env Environment, blockNumberInput int) (err erro return err } -// IsAvaxNetwork returns true if the given chain ID corresponds to an avalanche network or subnet. +// IsAvaxNetwork returns true if the given chain ID corresponds to an avalanche network. func IsAvaxNetwork(chainID int64) bool { return chainID == 43114 || // C-chain mainnet - chainID == 43113 || // Fuji testnet - chainID == 335 || // DFK testnet - chainID == 53935 // DFK mainnet + chainID == 43113 // Fuji testnet +} + +// IsAvaxSubnet returns true if the given chain ID corresponds to an avalanche subnet. +func IsAvaxSubnet(chainID int64) bool { + return chainID == 335 || // DFK testnet + chainID == 53935 || // DFK mainnet + chainID == 955081 || // Nexon Dev + chainID == 595581 || // Nexon Test + chainID == 807424 || // Nexon QA + chainID == 847799 // Nexon Stage } func UpkeepLink(chainID int64, upkeepID *big.Int) string {