feat(eth): changes based on protocol Pacaya fork (#367)

* chore(taiko_genesis): update devnet Pacaya genesis JSONs

* feat: update ValidateAnchorTx

* feat: update ValidateAnchorTx

* feat: update l1Origin

* Revert "feat: update l1Origin"

This reverts commit 1337b37c051fe06ee87e4b7d6ec2b3857558e218.

* feat: update L1Origin

* feat: update genesis json

* feat: add DepositTxType

* feat: add DepositTxType

* chore: update ci

* feat: new defs

* feat: new defs for op-services

* feat: new defs for op-services

* feat: new defs for op-services

* feat: new defs for op-services

* feat: new defs for op-services

* feat: new defs for op-services

* feat: update L1Origin

* fix: fix lint errors

* add preconf devent configs back in

* Update core/rawdb/taiko_l1_origin.go

Co-authored-by: maskpp <maskpp266@gmail.com>

* feat: two more APIs

* feat: update api backend

* feat: update api backend

* feat: rename an API

* feat: rename an API

* feat: rename an API

* feat: update `BuildPayloadArgs`

* feat: update `BuildPayloadArgs`

* feat: update `BuildPayloadArgs`

* chore: update ci

* update preconf genesis

* build

* feat!: update ci

* feat: update devnet json

---------

Co-authored-by: maskpp <maskpp266@gmail.com>
Co-authored-by: Jeffery Walsh <cyberhorsey@gmail.com>
This commit is contained in:
David 2025-02-06 13:59:38 +08:00 committed by GitHub
parent 02597d73f9
commit 7bf5c0d259
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 445 additions and 54 deletions

View file

@ -2,7 +2,7 @@ name: "Push multi-arch docker image to GAR"
on:
push:
branches: [ taiko ]
branches: [taiko]
tags:
- "v*"
@ -19,9 +19,9 @@ jobs:
platform: linux/amd64
- runner: arc-runner-set-arm64
platform: linux/arm64
runs-on: ${{ matrix.runner }}
steps:
- name: Prepare Environment
run: |
@ -80,7 +80,7 @@ jobs:
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
cache-to: type=gha,mode=max
platforms: ${{ matrix.platform }}
push: true
tags: ${{ env.REGISTRY_IMAGE }}

View file

@ -10,10 +10,12 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
@ -34,6 +36,9 @@ var (
AnchorV2Selector = crypto.Keccak256(
[]byte("anchorV2(uint64,bytes32,uint32,(uint8,uint8,uint32,uint64,uint32))"),
)[:4]
AnchorV3Selector = crypto.Keccak256(
[]byte("anchorV3(uint64,bytes32,bytes32,uint32,(uint8,uint8,uint32,uint64,uint32),bytes32[])"),
)[:4]
AnchorGasLimit = uint64(250_000)
)
@ -41,11 +46,12 @@ var (
type Taiko struct {
chainConfig *params.ChainConfig
taikoL2Address common.Address
chainDB ethdb.Database
}
var _ = new(Taiko)
func New(chainConfig *params.ChainConfig) *Taiko {
func New(chainConfig *params.ChainConfig, chainDB ethdb.Database) *Taiko {
taikoL2AddressPrefix := strings.TrimPrefix(chainConfig.ChainID.String(), "0")
return &Taiko{
@ -56,6 +62,7 @@ func New(chainConfig *params.ChainConfig) *Taiko {
strings.Repeat("0", common.AddressLength*2-len(taikoL2AddressPrefix)-len(TaikoL2AddressSuffix)) +
TaikoL2AddressSuffix,
),
chainDB: chainDB,
}
}
@ -82,7 +89,7 @@ func (t *Taiko) VerifyHeader(chain consensus.ChainHeaderReader, header *types.He
return consensus.ErrUnknownAncestor
}
// Sanity checks passed, do a proper verification
return t.verifyHeader(chain, header, parent, time.Now().Unix())
return t.verifyHeader(header, parent, time.Now().Unix())
}
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
@ -109,7 +116,7 @@ func (t *Taiko) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*type
if parent == nil {
err = consensus.ErrUnknownAncestor
} else {
err = t.verifyHeader(chain, header, parent, unixNow)
err = t.verifyHeader(header, parent, unixNow)
}
select {
case <-abort:
@ -121,11 +128,7 @@ func (t *Taiko) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*type
return abort, results
}
func (t *Taiko) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, unixNow int64) error {
if header.Time > uint64(unixNow) {
return consensus.ErrFutureBlock
}
func (t *Taiko) verifyHeader(header, parent *types.Header, unixNow int64) error {
// Ensure that the header's extra-data section is of a reasonable size (<= 32 bytes)
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
@ -171,6 +174,16 @@ func (t *Taiko) verifyHeader(chain consensus.ChainHeaderReader, header, parent *
return ErrEmptyWithdrawalsHash
}
l1Origin, err := rawdb.ReadL1Origin(t.chainDB, header.Number)
if err != nil {
return err
}
// If the current block is not a preconfirmation block, then check the timestamp.
if l1Origin != nil && !l1Origin.IsPreconfBlock() && header.Time > uint64(unixNow) {
return consensus.ErrFutureBlock
}
return nil
}
@ -290,7 +303,9 @@ func (t *Taiko) ValidateAnchorTx(tx *types.Transaction, header *types.Header) (b
return false, nil
}
if !bytes.HasPrefix(tx.Data(), AnchorSelector) && !bytes.HasPrefix(tx.Data(), AnchorV2Selector) {
if !bytes.HasPrefix(tx.Data(), AnchorSelector) &&
!bytes.HasPrefix(tx.Data(), AnchorV2Selector) &&
!bytes.HasPrefix(tx.Data(), AnchorV3Selector) {
return false, nil
}

View file

@ -39,7 +39,7 @@ func init() {
config.ArrowGlacierBlock = nil
config.Ethash = nil
config.Taiko = true
testEngine = taiko.New(config)
testEngine = taiko.New(config, rawdb.NewMemoryDatabase())
taikoL2AddressPrefix := strings.TrimPrefix(config.ChainID.String(), "0")

View file

@ -18,8 +18,8 @@ func (l L1Origin) MarshalJSON() ([]byte, error) {
type L1Origin struct {
BlockID *math.HexOrDecimal256 `json:"blockID" gencodec:"required"`
L2BlockHash common.Hash `json:"l2BlockHash"`
L1BlockHeight *math.HexOrDecimal256 `json:"l1BlockHeight" gencodec:"required"`
L1BlockHash common.Hash `json:"l1BlockHash" gencodec:"required"`
L1BlockHeight *math.HexOrDecimal256 `json:"l1BlockHeight" rlp:"optional"`
L1BlockHash common.Hash `json:"l1BlockHash" rlp:"optional"`
}
var enc L1Origin
enc.BlockID = (*math.HexOrDecimal256)(l.BlockID)
@ -34,8 +34,8 @@ func (l *L1Origin) UnmarshalJSON(input []byte) error {
type L1Origin struct {
BlockID *math.HexOrDecimal256 `json:"blockID" gencodec:"required"`
L2BlockHash *common.Hash `json:"l2BlockHash"`
L1BlockHeight *math.HexOrDecimal256 `json:"l1BlockHeight" gencodec:"required"`
L1BlockHash *common.Hash `json:"l1BlockHash" gencodec:"required"`
L1BlockHeight *math.HexOrDecimal256 `json:"l1BlockHeight" rlp:"optional"`
L1BlockHash *common.Hash `json:"l1BlockHash" rlp:"optional"`
}
var dec L1Origin
if err := json.Unmarshal(input, &dec); err != nil {
@ -48,13 +48,11 @@ func (l *L1Origin) UnmarshalJSON(input []byte) error {
if dec.L2BlockHash != nil {
l.L2BlockHash = *dec.L2BlockHash
}
if dec.L1BlockHeight == nil {
return errors.New("missing required field 'l1BlockHeight' for L1Origin")
if dec.L1BlockHeight != nil {
l.L1BlockHeight = (*big.Int)(dec.L1BlockHeight)
}
l.L1BlockHeight = (*big.Int)(dec.L1BlockHeight)
if dec.L1BlockHash == nil {
return errors.New("missing required field 'l1BlockHash' for L1Origin")
if dec.L1BlockHash != nil {
l.L1BlockHash = *dec.L1BlockHash
}
l.L1BlockHash = *dec.L1BlockHash
return nil
}

View file

@ -31,8 +31,8 @@ func l1OriginKey(blockID *big.Int) []byte {
type L1Origin struct {
BlockID *big.Int `json:"blockID" gencodec:"required"`
L2BlockHash common.Hash `json:"l2BlockHash"`
L1BlockHeight *big.Int `json:"l1BlockHeight" gencodec:"required"`
L1BlockHash common.Hash `json:"l1BlockHash" gencodec:"required"`
L1BlockHeight *big.Int `json:"l1BlockHeight" rlp:"optional"`
L1BlockHash common.Hash `json:"l1BlockHash" rlp:"optional"`
}
type l1OriginMarshaling struct {
@ -40,6 +40,11 @@ type l1OriginMarshaling struct {
L1BlockHeight *math.HexOrDecimal256
}
// IsPreconfBlock returns true if the L1Origin is for a preconfirmation block.
func (l *L1Origin) IsPreconfBlock() bool {
return l.L1BlockHeight == nil
}
// WriteL1Origin stores a L1Origin into the database.
func WriteL1Origin(db ethdb.KeyValueWriter, blockID *big.Int, l1Origin *L1Origin) {
data, err := rlp.EncodeToBytes(l1Origin)

View file

@ -11,6 +11,7 @@ import (
var (
InternalDevnetOntakeBlock = new(big.Int).SetUint64(0)
PreconfDevnetOntakeBlock = common.Big0
HeklaOntakeBlock = new(big.Int).SetUint64(840_512)
MainnetOntakeBlock = new(big.Int).SetUint64(538_304)
)
@ -54,6 +55,10 @@ func TaikoGenesisBlock(networkID uint64) *Genesis {
chainConfig.ChainID = params.HeklaNetworkID
chainConfig.OntakeBlock = HeklaOntakeBlock
allocJSON = taikoGenesis.HeklaGenesisAllocJSON
case params.PreconfDevnetNetworkID.Uint64():
chainConfig.ChainID = params.PreconfDevnetNetworkID
chainConfig.OntakeBlock = PreconfDevnetOntakeBlock
allocJSON = taikoGenesis.PreconfDevnetGenesisAllocJSON
default:
chainConfig.ChainID = params.TaikoInternalL2ANetworkID
chainConfig.OntakeBlock = InternalDevnetOntakeBlock

View file

@ -33,3 +33,6 @@ var HeklaGenesisAllocJSON []byte
//go:embed mainnet.json
var MainnetGenesisAllocJSON []byte
//go:embed preconf_devnet.json
var PreconfDevnetGenesisAllocJSON []byte

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,74 @@
package types
import (
"bytes"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)
// CHANGES(taiko): make taiko-geth compatible with the op-service library.
const DepositTxType = 0x7E
type DepositTx struct {
// SourceHash uniquely identifies the source of the deposit
SourceHash common.Hash
// From is exposed through the types.Signer, not through TxData
From common.Address
// nil means contract creation
To *common.Address `rlp:"nil"`
// Mint is minted on L2, locked on L1, nil if no minting.
Mint *big.Int `rlp:"nil"`
// Value is transferred from L2 balance, executed after Mint (if any)
Value *big.Int
// gas limit
Gas uint64
// Field indicating if this transaction is exempt from the L2 gas limit.
IsSystemTransaction bool
// Normal Tx data
Data []byte
}
// copy creates a deep copy of the transaction data and initializes all fields.
func (tx *DepositTx) copy() TxData {
return nil
}
// accessors for innerTx.
func (tx *DepositTx) txType() byte { return DepositTxType }
func (tx *DepositTx) chainID() *big.Int { return common.Big0 }
func (tx *DepositTx) accessList() AccessList { return nil }
func (tx *DepositTx) data() []byte { return tx.Data }
func (tx *DepositTx) gas() uint64 { return tx.Gas }
func (tx *DepositTx) gasFeeCap() *big.Int { return new(big.Int) }
func (tx *DepositTx) gasTipCap() *big.Int { return new(big.Int) }
func (tx *DepositTx) gasPrice() *big.Int { return new(big.Int) }
func (tx *DepositTx) value() *big.Int { return tx.Value }
func (tx *DepositTx) nonce() uint64 { return 0 }
func (tx *DepositTx) to() *common.Address { return tx.To }
func (tx *DepositTx) isSystemTx() bool { return tx.IsSystemTransaction } // nolint:unused
func (tx *DepositTx) isAnchor() bool { return false }
func (tx *DepositTx) markAsAnchor() error { return ErrInvalidTxType }
func (tx *DepositTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
return dst.Set(new(big.Int))
}
func (tx *DepositTx) effectiveNonce() *uint64 { return nil } // nolint:unused
func (tx *DepositTx) rawSignatureValues() (v, r, s *big.Int) {
return common.Big0, common.Big0, common.Big0
}
func (tx *DepositTx) setSignatureValues(chainID, v, r, s *big.Int) {
// this is a noop for deposit transactions
}
func (tx *DepositTx) encode(b *bytes.Buffer) error {
return rlp.Encode(b, tx)
}
func (tx *DepositTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/internal/version"
@ -460,6 +461,8 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
return valid(nil), engine.InvalidPayloadAttributes.With(err)
}
// Use the tx list hash as the beacon root.
txListHash := crypto.Keccak256Hash(payloadAttributes.BlockMetadata.TxList[:])
// Cache the mined block for later use.
args := &miner.BuildPayloadArgs{
Parent: block.ParentHash(),
@ -468,6 +471,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
Random: block.MixDigest(),
Withdrawals: block.Withdrawals(),
Version: payloadVersion,
TxListHash: &txListHash,
}
id := args.Id()
payload, err := api.eth.Miner().BuildPayload(args, false)
@ -488,8 +492,11 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
// Write L1Origin.
rawdb.WriteL1Origin(api.eth.ChainDb(), l1Origin.BlockID, l1Origin)
// Write the head L1Origin.
rawdb.WriteHeadL1Origin(api.eth.ChainDb(), l1Origin.BlockID)
// Write the head L1Origin, only when it's not a preconfirmation block.
if !l1Origin.IsPreconfBlock() {
rawdb.WriteHeadL1Origin(api.eth.ChainDb(), l1Origin.BlockID)
}
return valid(&id), nil
}

View file

@ -165,7 +165,7 @@ type Config struct {
func CreateConsensusEngine(config *params.ChainConfig, db ethdb.Database) (consensus.Engine, error) {
// CHANGE(taiko): use Taiko consensus engine when the --taiko flag is set.
if config.Taiko {
return taiko.New(config), nil
return taiko.New(config, db), nil
}
// Geth v1.14.0 dropped support for non-merged networks in any consensus
// mode. If such a network is requested, reject startup.

View file

@ -60,6 +60,18 @@ func (s *TaikoAPIBackend) L1OriginByID(blockID *math.HexOrDecimal256) (*rawdb.L1
return l1Origin, nil
}
// SetHeadL1Origin sets the latest L2 block's corresponding L1 origin.
func (s *TaikoAPIBackend) SetHeadL1Origin(blockID *math.HexOrDecimal256) *big.Int {
rawdb.WriteHeadL1Origin(s.eth.ChainDb(), (*big.Int)(blockID))
return (*big.Int)(blockID)
}
// UpdateL1Origin updates the L2 block's corresponding L1 origin.
func (s *TaikoAPIBackend) UpdateL1Origin(l1Origin *rawdb.L1Origin) *rawdb.L1Origin {
rawdb.WriteL1Origin(s.eth.ChainDb(), l1Origin.BlockID, l1Origin)
return l1Origin
}
// GetSyncMode returns the node sync mode.
func (s *TaikoAPIBackend) GetSyncMode() (string, error) {
return s.eth.config.SyncMode.String(), nil

View file

@ -19,6 +19,17 @@ func (ec *Client) HeadL1Origin(ctx context.Context) (*rawdb.L1Origin, error) {
return res, nil
}
// SetHeadL1Origin sets the latest L2 block's corresponding L1 origin.
func (ec *Client) SetHeadL1Origin(ctx context.Context, blockID *big.Int) (*big.Int, error) {
var res *big.Int
if err := ec.c.CallContext(ctx, &res, "taiko_setHeadL1Origin", blockID); err != nil {
return nil, err
}
return res, nil
}
// L1OriginByID returns the L2 block's corresponding L1 origin.
func (ec *Client) L1OriginByID(ctx context.Context, blockID *big.Int) (*rawdb.L1Origin, error) {
var res *rawdb.L1Origin
@ -30,6 +41,17 @@ func (ec *Client) L1OriginByID(ctx context.Context, blockID *big.Int) (*rawdb.L1
return res, nil
}
// UpdateL1Origin sets the L2 block's corresponding L1 origin.
func (ec *Client) UpdateL1Origin(ctx context.Context, l1Origin *rawdb.L1Origin) (*rawdb.L1Origin, error) {
var res *rawdb.L1Origin
if err := ec.c.CallContext(ctx, &res, "taiko_updateL1Origin", l1Origin); err != nil {
return nil, err
}
return res, nil
}
// GetSyncMode returns the current sync mode of the L2 node.
func (ec *Client) GetSyncMode(ctx context.Context) (string, error) {
var res string

View file

@ -44,6 +44,7 @@ type BuildPayloadArgs struct {
Withdrawals types.Withdrawals // The provided withdrawals
BeaconRoot *common.Hash // The provided beaconRoot (Cancun)
Version engine.PayloadVersion // Versioning byte for payload id calculation.
TxListHash *common.Hash // CHANGE(taiko): The hash of the transaction list
}
// Id computes an 8-byte identifier by hashing the components of the payload arguments.
@ -57,6 +58,10 @@ func (args *BuildPayloadArgs) Id() engine.PayloadID {
if args.BeaconRoot != nil {
hasher.Write(args.BeaconRoot[:])
}
// CHANGE(taiko): include the transaction list hash in the payload id calculation
if args.TxListHash != nil {
hasher.Write(args.TxListHash[:])
}
var out engine.PayloadID
copy(out[:], hasher.Sum(nil)[:8])
out[0] = byte(args.Version)

View file

@ -300,6 +300,7 @@ var NetworkNames = map[string]string{
JolnirNetworkID.String(): "Taiko Alpha-5 L2 (Jolnir)",
KatlaNetworkID.String(): "Taiko Alpha-6 L2 (Katla)",
HeklaNetworkID.String(): "Taiko Alpha-7 L2 (Hekla)",
PreconfDevnetNetworkID.String(): "Taiko Preconfirmation Devnet",
}
// ChainConfig is the core config which determines the blockchain settings.

View file

@ -1,11 +1,38 @@
package params
import (
"encoding/binary"
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// To make taiko-geth compatible with the op-service library, we need to define the following constants and functions.
type ProtocolVersion [32]byte
type ProtocolVersionComparison int
func (p ProtocolVersion) String() string {
return ""
}
func (p ProtocolVersion) Compare(other ProtocolVersion) (cmp ProtocolVersionComparison) {
return 0
}
type ProtocolVersionV0 struct {
Build [8]byte
Major, Minor, Patch, PreRelease uint32
}
func (v ProtocolVersionV0) Encode() (out ProtocolVersion) {
copy(out[8:16], v.Build[:])
binary.BigEndian.PutUint32(out[16:20], v.Major)
binary.BigEndian.PutUint32(out[20:24], v.Minor)
binary.BigEndian.PutUint32(out[24:28], v.Patch)
binary.BigEndian.PutUint32(out[28:32], v.PreRelease)
return
}
func u64(val uint64) *uint64 { return &val }
// Network IDs
@ -20,6 +47,7 @@ var (
JolnirNetworkID = big.NewInt(167007)
KatlaNetworkID = big.NewInt(167008)
HeklaNetworkID = big.NewInt(167009)
PreconfDevnetNetworkID = big.NewInt(167010)
)
var networkIDToChainConfig = map[*big.Int]*ChainConfig{
@ -33,6 +61,7 @@ var networkIDToChainConfig = map[*big.Int]*ChainConfig{
JolnirNetworkID: TaikoChainConfig,
KatlaNetworkID: TaikoChainConfig,
HeklaNetworkID: TaikoChainConfig,
PreconfDevnetNetworkID: TaikoChainConfig,
MainnetChainConfig.ChainID: MainnetChainConfig,
SepoliaChainConfig.ChainID: SepoliaChainConfig,
TestChainConfig.ChainID: TestChainConfig,

View file

@ -61,6 +61,11 @@ func TestNetworkIDToChainConfigOrDefault(t *testing.T) {
HeklaNetworkID,
TaikoChainConfig,
},
{
"preconfDevnetNetworkID",
PreconfDevnetNetworkID,
TaikoChainConfig,
},
{
"mainnet",
MainnetChainConfig.ChainID,

View file

@ -41,6 +41,9 @@ const (
var null = json.RawMessage("null")
// CHANGE(taiko): make taiko-geth compatible with op-service
type JsonError = jsonError
type subscriptionResult struct {
ID string `json:"subscription"`
Result json.RawMessage `json:"result,omitempty"`