mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-14 07:53:47 +00:00
feat(beacon): introduce soft blocks (#342)
* feat(beacon): introduce soft blocks * feat: update api.go * chore(ci): update CI * feat: update L1Origin * feat: update `verifyHeader` * test: update tests * feat: update consensus * feat: update consensus * feat: update genesis * feat: remove timestamp check in prepareWork * feat: merge changes in #281 * Update eth/catalyst/api.go Co-authored-by: maskpp <maskpp266@gmail.com> * Update internal/ethapi/taiko_preconf.go Co-authored-by: maskpp <maskpp266@gmail.com> * fix consensus test * revert commit f1df58 * fix consensus test (#349) * Update eth/catalyst/api.go Co-authored-by: maskpp <maskpp266@gmail.com> * feat: add back timestamp check in worker * add genesis * temp fix for old l1origin * nil value * feat: rename to `L1OriginLegacy` * feat: change `common.Big0` as the default value for legacy l1Origin, to make `IsSoftblock` return `false` * feat(beacon): change the reorg log level (#350) * use debug log level to avoid logging too many logs when frequently soft block reorg. * use debug log level to avoid logging too many logs when frequently soft block reorg. * feat: check --taiko flag --------- Co-authored-by: David <david@taiko.xyz> * add rlp optional flag (#353) * fix lint * fix test case * feat(l1Origin): remove the reverted l1Origins (#355) * remove the reverted l1Origins * feat: add more comments --------- Co-authored-by: David <david@taiko.xyz> * only forward txs * chore: update ci --------- Co-authored-by: maskpp <maskpp266@gmail.com> Co-authored-by: Jeffery Walsh <cyberhorsey@gmail.com>
This commit is contained in:
parent
5ef74219ab
commit
a2cbf904ea
26 changed files with 566 additions and 83 deletions
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
|
|
@ -2,7 +2,7 @@ name: "Push multi-arch docker image to GAR"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ taiko ]
|
branches: [taiko]
|
||||||
tags:
|
tags:
|
||||||
- "v*"
|
- "v*"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,11 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
cfg.Eth.OverrideVerkle = &v
|
cfg.Eth.OverrideVerkle = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHANGE(taiko): set preconfirmation forwarding URL.
|
||||||
|
if ctx.IsSet(utils.PreconfirmationForwardingURLFlag.Name) {
|
||||||
|
cfg.Eth.PreconfirmationForwardingURL = ctx.String(utils.PreconfirmationForwardingURLFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
|
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
|
||||||
|
|
||||||
// CHANGE(TAIKO): register Taiko RPC APIs.
|
// CHANGE(TAIKO): register Taiko RPC APIs.
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,7 @@ func init() {
|
||||||
metricsFlags,
|
metricsFlags,
|
||||||
)
|
)
|
||||||
// CHANGE(taiko): append Taiko flags into the original GETH flags
|
// CHANGE(taiko): append Taiko flags into the original GETH flags
|
||||||
app.Flags = append(app.Flags, &utils.TaikoFlag)
|
app.Flags = append(app.Flags, utils.TaikoFlag, utils.PreconfirmationForwardingURLFlag)
|
||||||
|
|
||||||
flags.AutoEnvVars(app.Flags, "GETH")
|
flags.AutoEnvVars(app.Flags, "GETH")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
@ -12,9 +13,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TaikoFlag = cli.BoolFlag{
|
TaikoFlag = &cli.BoolFlag{
|
||||||
Name: "taiko",
|
Name: "taiko",
|
||||||
Usage: "Taiko network",
|
Usage: "Taiko network",
|
||||||
|
Category: flags.TaikoCategory,
|
||||||
|
}
|
||||||
|
PreconfirmationForwardingURLFlag = &cli.StringFlag{
|
||||||
|
Name: "taiko.preconfirmationForwardingUrl",
|
||||||
|
Usage: "URL to forward RPC requests before confirmation",
|
||||||
|
Category: flags.TaikoCategory,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,12 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"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/state"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
@ -40,12 +42,13 @@ var (
|
||||||
// Taiko is a consensus engine used by L2 rollup.
|
// Taiko is a consensus engine used by L2 rollup.
|
||||||
type Taiko struct {
|
type Taiko struct {
|
||||||
chainConfig *params.ChainConfig
|
chainConfig *params.ChainConfig
|
||||||
|
chainDB ethdb.Database
|
||||||
taikoL2Address common.Address
|
taikoL2Address common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = new(Taiko)
|
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")
|
taikoL2AddressPrefix := strings.TrimPrefix(chainConfig.ChainID.String(), "0")
|
||||||
|
|
||||||
return &Taiko{
|
return &Taiko{
|
||||||
|
|
@ -56,6 +59,7 @@ func New(chainConfig *params.ChainConfig) *Taiko {
|
||||||
strings.Repeat("0", common.AddressLength*2-len(taikoL2AddressPrefix)-len(TaikoL2AddressSuffix)) +
|
strings.Repeat("0", common.AddressLength*2-len(taikoL2AddressPrefix)-len(TaikoL2AddressSuffix)) +
|
||||||
TaikoL2AddressSuffix,
|
TaikoL2AddressSuffix,
|
||||||
),
|
),
|
||||||
|
chainDB: chainDB,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +86,7 @@ func (t *Taiko) VerifyHeader(chain consensus.ChainHeaderReader, header *types.He
|
||||||
return consensus.ErrUnknownAncestor
|
return consensus.ErrUnknownAncestor
|
||||||
}
|
}
|
||||||
// Sanity checks passed, do a proper verification
|
// 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
|
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
|
||||||
|
|
@ -109,7 +113,7 @@ func (t *Taiko) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*type
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
err = consensus.ErrUnknownAncestor
|
err = consensus.ErrUnknownAncestor
|
||||||
} else {
|
} else {
|
||||||
err = t.verifyHeader(chain, header, parent, unixNow)
|
err = t.verifyHeader(header, parent, unixNow)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-abort:
|
case <-abort:
|
||||||
|
|
@ -121,11 +125,7 @@ func (t *Taiko) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*type
|
||||||
return abort, results
|
return abort, results
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Taiko) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, unixNow int64) error {
|
func (t *Taiko) verifyHeader(header, parent *types.Header, unixNow int64) error {
|
||||||
if header.Time > uint64(unixNow) {
|
|
||||||
return consensus.ErrFutureBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the header's extra-data section is of a reasonable size (<= 32 bytes)
|
// Ensure that the header's extra-data section is of a reasonable size (<= 32 bytes)
|
||||||
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
|
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
|
||||||
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
|
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
|
||||||
|
|
@ -171,6 +171,16 @@ func (t *Taiko) verifyHeader(chain consensus.ChainHeaderReader, header, parent *
|
||||||
return ErrEmptyWithdrawalsHash
|
return ErrEmptyWithdrawalsHash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
l1Origin, err := rawdb.ReadL1Origin(t.chainDB, header.Number)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the current block is not a soft block, then check the timestamp.
|
||||||
|
if l1Origin != nil && !l1Origin.IsSoftBlock() && header.Time > uint64(unixNow) {
|
||||||
|
return consensus.ErrFutureBlock
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ func init() {
|
||||||
config.ArrowGlacierBlock = nil
|
config.ArrowGlacierBlock = nil
|
||||||
config.Ethash = nil
|
config.Ethash = nil
|
||||||
config.Taiko = true
|
config.Taiko = true
|
||||||
testEngine = taiko.New(config)
|
testEngine = taiko.New(config, rawdb.NewMemoryDatabase())
|
||||||
|
|
||||||
taikoL2AddressPrefix := strings.TrimPrefix(config.ChainID.String(), "0")
|
taikoL2AddressPrefix := strings.TrimPrefix(config.ChainID.String(), "0")
|
||||||
|
|
||||||
|
|
@ -93,44 +93,7 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestBackend(t *testing.T) (*eth.Ethereum, []*types.Block) {
|
func generateTestChain(t *testing.T) ([]*types.Block, *eth.Ethereum) {
|
||||||
// Generate test chain.
|
|
||||||
blocks := generateTestChain()
|
|
||||||
|
|
||||||
// Create node
|
|
||||||
n, err := node.New(&node.Config{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("can't create new node: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Ethereum Service
|
|
||||||
config := ðconfig.Config{
|
|
||||||
Genesis: genesis,
|
|
||||||
}
|
|
||||||
|
|
||||||
ethservice, err := eth.New(n, config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("can't create new ethereum service: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import the test chain.
|
|
||||||
if err := n.Start(); err != nil {
|
|
||||||
t.Fatalf("can't start test node: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil {
|
|
||||||
t.Fatalf("can't import test blocks: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := ethservice.Engine().(*taiko.Taiko); !ok {
|
|
||||||
t.Fatalf("not use taiko engine")
|
|
||||||
}
|
|
||||||
|
|
||||||
return ethservice, blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateTestChain() []*types.Block {
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
|
||||||
generate := func(i int, g *core.BlockGen) {
|
generate := func(i int, g *core.BlockGen) {
|
||||||
g.OffsetTime(5)
|
g.OffsetTime(5)
|
||||||
|
|
||||||
|
|
@ -147,16 +110,56 @@ func generateTestChain() []*types.Block {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create node
|
||||||
|
n, err := node.New(&node.Config{
|
||||||
|
DataDir: t.TempDir(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't create new node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Ethereum Service
|
||||||
|
ethService, err := eth.New(n, ðconfig.Config{
|
||||||
|
Genesis: genesis,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't create new ethereum service: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := ethService.ChainDb()
|
||||||
|
|
||||||
gblock := genesis.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
gblock := genesis.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||||
|
|
||||||
blocks, _ := core.GenerateChain(genesis.Config, gblock, testEngine, db, 1, generate)
|
blocks, _ := core.GenerateChain(genesis.Config, gblock, testEngine, db, 1, generate)
|
||||||
|
|
||||||
blocks = append([]*types.Block{gblock}, blocks...)
|
blocks = append([]*types.Block{gblock}, blocks...)
|
||||||
return blocks
|
|
||||||
|
// Insert L1Origins.
|
||||||
|
for _, block := range blocks {
|
||||||
|
rawdb.WriteL1Origin(db, block.Number(), &rawdb.L1Origin{
|
||||||
|
BlockID: block.Number(),
|
||||||
|
L1BlockHeight: block.Number(),
|
||||||
|
L1BlockHash: block.Hash(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import the test chain.
|
||||||
|
if err := n.Start(); err != nil {
|
||||||
|
t.Fatalf("can't start test node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ethService.BlockChain().InsertChain(blocks[1:]); err != nil {
|
||||||
|
t.Fatalf("can't import test blocks: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := ethService.Engine().(*taiko.Taiko); !ok {
|
||||||
|
t.Fatalf("not use taiko engine")
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks, ethService
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVerifyHeader(t *testing.T) {
|
func TestVerifyHeader(t *testing.T) {
|
||||||
ethService, blocks := newTestBackend(t)
|
// Generate test chain.
|
||||||
|
blocks, ethService := generateTestChain(t)
|
||||||
|
|
||||||
for _, b := range blocks {
|
for _, b := range blocks {
|
||||||
err := testEngine.VerifyHeader(ethService.BlockChain(), b.Header())
|
err := testEngine.VerifyHeader(ethService.BlockChain(), b.Header())
|
||||||
|
|
|
||||||
|
|
@ -2269,7 +2269,12 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error {
|
||||||
} else {
|
} else {
|
||||||
// len(newChain) == 0 && len(oldChain) > 0
|
// len(newChain) == 0 && len(oldChain) > 0
|
||||||
// rewind the canonical chain to a lower point.
|
// rewind the canonical chain to a lower point.
|
||||||
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain))
|
// CHANGE(taiko): use debug log level to avoid logging too many logs when frequently soft block rollback.
|
||||||
|
if bc.chainConfig.Taiko {
|
||||||
|
log.Debug("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain))
|
||||||
|
} else {
|
||||||
|
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Acquire the tx-lookup lock before mutation. This step is essential
|
// Acquire the tx-lookup lock before mutation. This step is essential
|
||||||
// as the txlookups should be changed atomically, and all subsequent
|
// as the txlookups should be changed atomically, and all subsequent
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,22 @@ func (l L1Origin) MarshalJSON() ([]byte, error) {
|
||||||
type L1Origin struct {
|
type L1Origin struct {
|
||||||
BlockID *math.HexOrDecimal256 `json:"blockID" gencodec:"required"`
|
BlockID *math.HexOrDecimal256 `json:"blockID" gencodec:"required"`
|
||||||
L2BlockHash common.Hash `json:"l2BlockHash"`
|
L2BlockHash common.Hash `json:"l2BlockHash"`
|
||||||
L1BlockHeight *math.HexOrDecimal256 `json:"l1BlockHeight" gencodec:"required"`
|
L1BlockHeight *math.HexOrDecimal256 `json:"l1BlockHeight"`
|
||||||
L1BlockHash common.Hash `json:"l1BlockHash" gencodec:"required"`
|
L1BlockHash common.Hash `json:"l1BlockHash"`
|
||||||
|
BatchID *big.Int `json:"batchID"`
|
||||||
|
EndOfBlock bool `json:"endOfBlock"`
|
||||||
|
EndOfPreconf bool `json:"endOfPreconf"`
|
||||||
|
Preconfer common.Address `json:"preconfer"`
|
||||||
}
|
}
|
||||||
var enc L1Origin
|
var enc L1Origin
|
||||||
enc.BlockID = (*math.HexOrDecimal256)(l.BlockID)
|
enc.BlockID = (*math.HexOrDecimal256)(l.BlockID)
|
||||||
enc.L2BlockHash = l.L2BlockHash
|
enc.L2BlockHash = l.L2BlockHash
|
||||||
enc.L1BlockHeight = (*math.HexOrDecimal256)(l.L1BlockHeight)
|
enc.L1BlockHeight = (*math.HexOrDecimal256)(l.L1BlockHeight)
|
||||||
enc.L1BlockHash = l.L1BlockHash
|
enc.L1BlockHash = l.L1BlockHash
|
||||||
|
enc.BatchID = l.BatchID
|
||||||
|
enc.EndOfBlock = l.EndOfBlock
|
||||||
|
enc.EndOfPreconf = l.EndOfPreconf
|
||||||
|
enc.Preconfer = l.Preconfer
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,8 +42,12 @@ func (l *L1Origin) UnmarshalJSON(input []byte) error {
|
||||||
type L1Origin struct {
|
type L1Origin struct {
|
||||||
BlockID *math.HexOrDecimal256 `json:"blockID" gencodec:"required"`
|
BlockID *math.HexOrDecimal256 `json:"blockID" gencodec:"required"`
|
||||||
L2BlockHash *common.Hash `json:"l2BlockHash"`
|
L2BlockHash *common.Hash `json:"l2BlockHash"`
|
||||||
L1BlockHeight *math.HexOrDecimal256 `json:"l1BlockHeight" gencodec:"required"`
|
L1BlockHeight *math.HexOrDecimal256 `json:"l1BlockHeight"`
|
||||||
L1BlockHash *common.Hash `json:"l1BlockHash" gencodec:"required"`
|
L1BlockHash *common.Hash `json:"l1BlockHash"`
|
||||||
|
BatchID *big.Int `json:"batchID"`
|
||||||
|
EndOfBlock *bool `json:"endOfBlock"`
|
||||||
|
EndOfPreconf *bool `json:"endOfPreconf"`
|
||||||
|
Preconfer *common.Address `json:"preconfer"`
|
||||||
}
|
}
|
||||||
var dec L1Origin
|
var dec L1Origin
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -48,13 +60,23 @@ func (l *L1Origin) UnmarshalJSON(input []byte) error {
|
||||||
if dec.L2BlockHash != nil {
|
if dec.L2BlockHash != nil {
|
||||||
l.L2BlockHash = *dec.L2BlockHash
|
l.L2BlockHash = *dec.L2BlockHash
|
||||||
}
|
}
|
||||||
if dec.L1BlockHeight == nil {
|
if dec.L1BlockHeight != nil {
|
||||||
return errors.New("missing required field 'l1BlockHeight' for L1Origin")
|
l.L1BlockHeight = (*big.Int)(dec.L1BlockHeight)
|
||||||
}
|
}
|
||||||
l.L1BlockHeight = (*big.Int)(dec.L1BlockHeight)
|
if dec.L1BlockHash != nil {
|
||||||
if dec.L1BlockHash == nil {
|
l.L1BlockHash = *dec.L1BlockHash
|
||||||
return errors.New("missing required field 'l1BlockHash' for L1Origin")
|
}
|
||||||
|
if dec.BatchID != nil {
|
||||||
|
l.BatchID = dec.BatchID
|
||||||
|
}
|
||||||
|
if dec.EndOfBlock != nil {
|
||||||
|
l.EndOfBlock = *dec.EndOfBlock
|
||||||
|
}
|
||||||
|
if dec.EndOfPreconf != nil {
|
||||||
|
l.EndOfPreconf = *dec.EndOfPreconf
|
||||||
|
}
|
||||||
|
if dec.Preconfer != nil {
|
||||||
|
l.Preconfer = *dec.Preconfer
|
||||||
}
|
}
|
||||||
l.L1BlockHash = *dec.L1BlockHash
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,22 @@ func l1OriginKey(blockID *big.Int) []byte {
|
||||||
|
|
||||||
// L1Origin represents a L1Origin of a L2 block.
|
// L1Origin represents a L1Origin of a L2 block.
|
||||||
type L1Origin struct {
|
type L1Origin struct {
|
||||||
|
BlockID *big.Int `json:"blockID" gencodec:"required"`
|
||||||
|
L2BlockHash common.Hash `json:"l2BlockHash"`
|
||||||
|
L1BlockHeight *big.Int `json:"l1BlockHeight"`
|
||||||
|
L1BlockHash common.Hash `json:"l1BlockHash"`
|
||||||
|
BatchID *big.Int `json:"batchID" rlp:"optional"`
|
||||||
|
EndOfBlock bool `json:"endOfBlock" rlp:"optional"`
|
||||||
|
EndOfPreconf bool `json:"endOfPreconf" rlp:"optional"`
|
||||||
|
Preconfer common.Address `json:"preconfer" rlp:"optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// L1OriginLegacy represents the legacy L1Origin structure of a L2 block.
|
||||||
|
type L1OriginLegacy struct {
|
||||||
BlockID *big.Int `json:"blockID" gencodec:"required"`
|
BlockID *big.Int `json:"blockID" gencodec:"required"`
|
||||||
L2BlockHash common.Hash `json:"l2BlockHash"`
|
L2BlockHash common.Hash `json:"l2BlockHash"`
|
||||||
L1BlockHeight *big.Int `json:"l1BlockHeight" gencodec:"required"`
|
L1BlockHeight *big.Int `json:"l1BlockHeight"`
|
||||||
L1BlockHash common.Hash `json:"l1BlockHash" gencodec:"required"`
|
L1BlockHash common.Hash `json:"l1BlockHash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type l1OriginMarshaling struct {
|
type l1OriginMarshaling struct {
|
||||||
|
|
@ -40,6 +52,11 @@ type l1OriginMarshaling struct {
|
||||||
L1BlockHeight *math.HexOrDecimal256
|
L1BlockHeight *math.HexOrDecimal256
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsSoftBlock returns true if the L1Origin is a softblock.
|
||||||
|
func (l *L1Origin) IsSoftBlock() bool {
|
||||||
|
return l.BatchID != nil
|
||||||
|
}
|
||||||
|
|
||||||
// WriteL1Origin stores a L1Origin into the database.
|
// WriteL1Origin stores a L1Origin into the database.
|
||||||
func WriteL1Origin(db ethdb.KeyValueWriter, blockID *big.Int, l1Origin *L1Origin) {
|
func WriteL1Origin(db ethdb.KeyValueWriter, blockID *big.Int, l1Origin *L1Origin) {
|
||||||
data, err := rlp.EncodeToBytes(l1Origin)
|
data, err := rlp.EncodeToBytes(l1Origin)
|
||||||
|
|
@ -52,16 +69,40 @@ func WriteL1Origin(db ethdb.KeyValueWriter, blockID *big.Int, l1Origin *L1Origin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadL1Origin retrieves the given L2 block's L1Origin from database.
|
// DeleteL1Origin removes the L1Origin.
|
||||||
|
func DeleteL1Origin(db ethdb.KeyValueWriter, blockID *big.Int) {
|
||||||
|
_ = db.Delete(l1OriginKey(blockID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadL1Origin retrieves the L1Origin of the given blockID from the database.
|
||||||
func ReadL1Origin(db ethdb.KeyValueReader, blockID *big.Int) (*L1Origin, error) {
|
func ReadL1Origin(db ethdb.KeyValueReader, blockID *big.Int) (*L1Origin, error) {
|
||||||
data, _ := db.Get(l1OriginKey(blockID))
|
data, _ := db.Get(l1OriginKey(blockID))
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First try to decode the new version (with new fields).
|
||||||
l1Origin := new(L1Origin)
|
l1Origin := new(L1Origin)
|
||||||
if err := rlp.Decode(bytes.NewReader(data), l1Origin); err != nil {
|
if err := rlp.Decode(bytes.NewReader(data), l1Origin); err != nil {
|
||||||
return nil, fmt.Errorf("invalid L1Origin RLP bytes: %w", err)
|
// If decoding the new version fails, try to decode the legacy version (without new fields).
|
||||||
|
l1OriginLegacy := new(L1OriginLegacy)
|
||||||
|
if err := rlp.Decode(bytes.NewReader(data), &l1OriginLegacy); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid legacy L1Origin RLP bytes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If decoding legacy version succeeds, manually
|
||||||
|
// construct the new L1Origin with default values for the new fields.
|
||||||
|
l1Origin = &L1Origin{
|
||||||
|
BlockID: l1OriginLegacy.BlockID,
|
||||||
|
L2BlockHash: l1OriginLegacy.L2BlockHash,
|
||||||
|
L1BlockHeight: l1OriginLegacy.L1BlockHeight,
|
||||||
|
L1BlockHash: l1OriginLegacy.L1BlockHash,
|
||||||
|
// default value, as the new fields are not present in the legacy version.
|
||||||
|
BatchID: common.Big0,
|
||||||
|
EndOfBlock: false,
|
||||||
|
EndOfPreconf: false,
|
||||||
|
Preconfer: common.Address{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return l1Origin, nil
|
return l1Origin, nil
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ func TestL1Origin(t *testing.T) {
|
||||||
l1Origin, err := ReadL1Origin(db, testL1Origin.BlockID)
|
l1Origin, err := ReadL1Origin(db, testL1Origin.BlockID)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.NotNil(t, l1Origin)
|
require.NotNil(t, l1Origin)
|
||||||
|
assert.Equal(t, testL1Origin.BatchID, l1Origin.BatchID)
|
||||||
assert.Equal(t, testL1Origin.BlockID, l1Origin.BlockID)
|
assert.Equal(t, testL1Origin.BlockID, l1Origin.BlockID)
|
||||||
assert.Equal(t, testL1Origin.L2BlockHash, l1Origin.L2BlockHash)
|
assert.Equal(t, testL1Origin.L2BlockHash, l1Origin.L2BlockHash)
|
||||||
assert.Equal(t, testL1Origin.L1BlockHeight, l1Origin.L1BlockHeight)
|
assert.Equal(t, testL1Origin.L1BlockHeight, l1Origin.L1BlockHeight)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
InternalDevnetOntakeBlock = new(big.Int).SetUint64(0)
|
InternalDevnetOntakeBlock = common.Big0
|
||||||
|
PreconfDevnetOntakeBlock = common.Big0
|
||||||
HeklaOntakeBlock = new(big.Int).SetUint64(840_512)
|
HeklaOntakeBlock = new(big.Int).SetUint64(840_512)
|
||||||
MainnetOntakeBlock = new(big.Int).SetUint64(538_304)
|
MainnetOntakeBlock = new(big.Int).SetUint64(538_304)
|
||||||
)
|
)
|
||||||
|
|
@ -54,6 +55,10 @@ func TaikoGenesisBlock(networkID uint64) *Genesis {
|
||||||
chainConfig.ChainID = params.HeklaNetworkID
|
chainConfig.ChainID = params.HeklaNetworkID
|
||||||
chainConfig.OntakeBlock = HeklaOntakeBlock
|
chainConfig.OntakeBlock = HeklaOntakeBlock
|
||||||
allocJSON = taikoGenesis.HeklaGenesisAllocJSON
|
allocJSON = taikoGenesis.HeklaGenesisAllocJSON
|
||||||
|
case params.PreconfDevnetNetworkID.Uint64():
|
||||||
|
chainConfig.ChainID = params.PreconfDevnetNetworkID
|
||||||
|
chainConfig.OntakeBlock = PreconfDevnetOntakeBlock
|
||||||
|
allocJSON = taikoGenesis.PreconfDevnetGenesisAllocJSON
|
||||||
default:
|
default:
|
||||||
chainConfig.ChainID = params.TaikoInternalL2ANetworkID
|
chainConfig.ChainID = params.TaikoInternalL2ANetworkID
|
||||||
chainConfig.OntakeBlock = InternalDevnetOntakeBlock
|
chainConfig.OntakeBlock = InternalDevnetOntakeBlock
|
||||||
|
|
|
||||||
|
|
@ -33,3 +33,6 @@ var HeklaGenesisAllocJSON []byte
|
||||||
|
|
||||||
//go:embed mainnet.json
|
//go:embed mainnet.json
|
||||||
var MainnetGenesisAllocJSON []byte
|
var MainnetGenesisAllocJSON []byte
|
||||||
|
|
||||||
|
//go:embed preconf_devnet.json
|
||||||
|
var PreconfDevnetGenesisAllocJSON []byte
|
||||||
|
|
|
||||||
217
core/taiko_genesis/preconf_devnet.json
Normal file
217
core/taiko_genesis/preconf_devnet.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -44,10 +44,11 @@ import (
|
||||||
|
|
||||||
// EthAPIBackend implements ethapi.Backend and tracers.Backend for full nodes
|
// EthAPIBackend implements ethapi.Backend and tracers.Backend for full nodes
|
||||||
type EthAPIBackend struct {
|
type EthAPIBackend struct {
|
||||||
extRPCEnabled bool
|
extRPCEnabled bool
|
||||||
allowUnprotectedTxs bool
|
allowUnprotectedTxs bool
|
||||||
eth *Ethereum
|
eth *Ethereum
|
||||||
gpo *gasprice.Oracle
|
gpo *gasprice.Oracle
|
||||||
|
preconfirmationForwardingURL string // CHANGE(taiko): add preconfirmation forwarding URL
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainConfig returns the active chain configuration.
|
// ChainConfig returns the active chain configuration.
|
||||||
|
|
@ -431,3 +432,8 @@ func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, re
|
||||||
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
|
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
|
||||||
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
|
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPreconfirmationForwardingURL returns the URL to forward RPC requests before confirmation.
|
||||||
|
func (b *EthAPIBackend) GetPreconfirmationForwardingURL() string {
|
||||||
|
return b.preconfirmationForwardingURL
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,8 @@ type Ethereum struct {
|
||||||
lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
|
lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
|
||||||
|
|
||||||
shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
|
shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
|
||||||
|
|
||||||
|
PreconfirmationForwardingURL string // CHANGE(taiko): add preconfirmation forwarding URL
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Ethereum object (including the initialisation of the common Ethereum object),
|
// New creates a new Ethereum object (including the initialisation of the common Ethereum object),
|
||||||
|
|
@ -254,7 +256,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
eth.miner = miner.New(eth, config.Miner, eth.engine)
|
eth.miner = miner.New(eth, config.Miner, eth.engine)
|
||||||
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
|
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
|
||||||
|
|
||||||
eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil}
|
// CHANGE(taiko): set up the pre-confirmation forwarding URL
|
||||||
|
eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil, config.PreconfirmationForwardingURL}
|
||||||
if eth.APIBackend.allowUnprotectedTxs {
|
if eth.APIBackend.allowUnprotectedTxs {
|
||||||
log.Info("Unprotected transactions allowed")
|
log.Info("Unprotected transactions allowed")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -400,6 +400,11 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
// generating the payload. It's a special corner case that a few slots are
|
// generating the payload. It's a special corner case that a few slots are
|
||||||
// missing and we are requested to generate the payload in slot.
|
// missing and we are requested to generate the payload in slot.
|
||||||
} else if isTaiko { // CHANGE(taiko): reorg is allowed in L2.
|
} else if isTaiko { // CHANGE(taiko): reorg is allowed in L2.
|
||||||
|
// After inserting a new canonical head, we delete all L1Origin data of the pending soft blocks.
|
||||||
|
head := api.eth.BlockChain().CurrentBlock()
|
||||||
|
for number := head.Number.Uint64(); number > block.NumberU64(); number-- {
|
||||||
|
rawdb.DeleteL1Origin(api.eth.ChainDb(), new(big.Int).SetUint64(number))
|
||||||
|
}
|
||||||
if latestValid, err := api.eth.BlockChain().SetCanonical(block); err != nil {
|
if latestValid, err := api.eth.BlockChain().SetCanonical(block); err != nil {
|
||||||
return engine.ForkChoiceResponse{PayloadStatus: engine.PayloadStatusV1{Status: engine.INVALID, LatestValidHash: &latestValid}}, err
|
return engine.ForkChoiceResponse{PayloadStatus: engine.PayloadStatusV1{Status: engine.INVALID, LatestValidHash: &latestValid}}, err
|
||||||
}
|
}
|
||||||
|
|
@ -446,6 +451,17 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
if payloadAttributes != nil {
|
if payloadAttributes != nil {
|
||||||
// CHANGE(taiko): create a L2 block by Taiko protocol.
|
// CHANGE(taiko): create a L2 block by Taiko protocol.
|
||||||
if isTaiko {
|
if isTaiko {
|
||||||
|
// L1Origin is a required field in PayloadAttributesV1.
|
||||||
|
if payloadAttributes.L1Origin == nil {
|
||||||
|
return valid(nil), engine.InvalidPayloadAttributes.With(errors.New("L1Origin is nil"))
|
||||||
|
}
|
||||||
|
// If it's not a softblock creation request, then `L1BlockHash` and `L1BlockHeight` are required.
|
||||||
|
if payloadAttributes.L1Origin.BatchID == nil &&
|
||||||
|
(payloadAttributes.L1Origin.L1BlockHash == (common.Hash{}) ||
|
||||||
|
payloadAttributes.L1Origin.L1BlockHeight == nil) {
|
||||||
|
return valid(nil), engine.InvalidPayloadAttributes.With(errors.New("L1BlockHash or L1BlockHeight is nil"))
|
||||||
|
}
|
||||||
|
|
||||||
// No need to check payloadAttribute here, because all its fields are
|
// No need to check payloadAttribute here, because all its fields are
|
||||||
// marked as required.
|
// marked as required.
|
||||||
block, err := api.eth.Miner().SealBlockWith(
|
block, err := api.eth.Miner().SealBlockWith(
|
||||||
|
|
@ -488,8 +504,10 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
|
|
||||||
// Write L1Origin.
|
// Write L1Origin.
|
||||||
rawdb.WriteL1Origin(api.eth.ChainDb(), l1Origin.BlockID, l1Origin)
|
rawdb.WriteL1Origin(api.eth.ChainDb(), l1Origin.BlockID, l1Origin)
|
||||||
// Write the head L1Origin.
|
// Write the head L1Origin, only when it's not a soft block.
|
||||||
rawdb.WriteHeadL1Origin(api.eth.ChainDb(), l1Origin.BlockID)
|
if l1Origin.BatchID == nil {
|
||||||
|
rawdb.WriteHeadL1Origin(api.eth.ChainDb(), l1Origin.BlockID)
|
||||||
|
}
|
||||||
|
|
||||||
return valid(&id), nil
|
return valid(&id), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,9 @@ type Config struct {
|
||||||
|
|
||||||
// OverrideVerkle (TODO: remove after the fork)
|
// OverrideVerkle (TODO: remove after the fork)
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
|
|
||||||
|
// CHANGE(taiko): add preconfirmation forwarding URL
|
||||||
|
PreconfirmationForwardingURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateConsensusEngine creates a consensus engine for the given chain config.
|
// CreateConsensusEngine creates a consensus engine for the given chain config.
|
||||||
|
|
@ -165,7 +168,7 @@ type Config struct {
|
||||||
func CreateConsensusEngine(config *params.ChainConfig, db ethdb.Database) (consensus.Engine, error) {
|
func CreateConsensusEngine(config *params.ChainConfig, db ethdb.Database) (consensus.Engine, error) {
|
||||||
// CHANGE(taiko): use Taiko consensus engine when the --taiko flag is set.
|
// CHANGE(taiko): use Taiko consensus engine when the --taiko flag is set.
|
||||||
if config.Taiko {
|
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
|
// Geth v1.14.0 dropped support for non-merged networks in any consensus
|
||||||
// mode. If such a network is requested, reject startup.
|
// mode. If such a network is requested, reject startup.
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,10 @@ func TestHeadL1Origin(t *testing.T) {
|
||||||
L2BlockHash: headerHash,
|
L2BlockHash: headerHash,
|
||||||
L1BlockHeight: randomBigInt(),
|
L1BlockHeight: randomBigInt(),
|
||||||
L1BlockHash: randomHash(),
|
L1BlockHash: randomHash(),
|
||||||
|
BatchID: nil,
|
||||||
|
EndOfBlock: false,
|
||||||
|
EndOfPreconf: false,
|
||||||
|
Preconfer: common.Address{},
|
||||||
}
|
}
|
||||||
|
|
||||||
rawdb.WriteL1Origin(db, testL1Origin.BlockID, testL1Origin)
|
rawdb.WriteL1Origin(db, testL1Origin.BlockID, testL1Origin)
|
||||||
|
|
@ -78,6 +82,7 @@ func TestHeadL1Origin(t *testing.T) {
|
||||||
|
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, testL1Origin, l1OriginFound)
|
require.Equal(t, testL1Origin, l1OriginFound)
|
||||||
|
require.False(t, l1OriginFound.IsSoftBlock())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestL1OriginByID(t *testing.T) {
|
func TestL1OriginByID(t *testing.T) {
|
||||||
|
|
@ -89,6 +94,10 @@ func TestL1OriginByID(t *testing.T) {
|
||||||
L2BlockHash: headerHash,
|
L2BlockHash: headerHash,
|
||||||
L1BlockHeight: randomBigInt(),
|
L1BlockHeight: randomBigInt(),
|
||||||
L1BlockHash: randomHash(),
|
L1BlockHash: randomHash(),
|
||||||
|
BatchID: common.Big1,
|
||||||
|
EndOfBlock: false,
|
||||||
|
EndOfPreconf: false,
|
||||||
|
Preconfer: testAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
l1OriginFound, err := ec.L1OriginByID(context.Background(), testL1Origin.BlockID)
|
l1OriginFound, err := ec.L1OriginByID(context.Background(), testL1Origin.BlockID)
|
||||||
|
|
@ -102,6 +111,7 @@ func TestL1OriginByID(t *testing.T) {
|
||||||
|
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, testL1Origin, l1OriginFound)
|
require.Equal(t, testL1Origin, l1OriginFound)
|
||||||
|
require.True(t, l1OriginFound.IsSoftBlock())
|
||||||
}
|
}
|
||||||
|
|
||||||
// randomHash generates a random blob of data and returns it as a hash.
|
// randomHash generates a random blob of data and returns it as a hash.
|
||||||
|
|
|
||||||
|
|
@ -1974,6 +1974,18 @@ func (api *TransactionAPI) FillTransaction(ctx context.Context, args Transaction
|
||||||
// SendRawTransaction will add the signed transaction to the transaction pool.
|
// SendRawTransaction will add the signed transaction to the transaction pool.
|
||||||
// The sender is responsible for signing the transaction and using the correct nonce.
|
// The sender is responsible for signing the transaction and using the correct nonce.
|
||||||
func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) {
|
func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) {
|
||||||
|
// CHANGE(taiko): Forward the request to the preconf node if specified.
|
||||||
|
if forwardURL := api.b.GetPreconfirmationForwardingURL(); forwardURL != "" {
|
||||||
|
log.Info("Forwarding SendRawTransaction request", "forwardURL", forwardURL)
|
||||||
|
// Forward the raw transaction to the specified URL
|
||||||
|
h, err := forward[string](forwardURL, "eth_sendRawTransaction", []interface{}{input.String()})
|
||||||
|
if err == nil && h != nil {
|
||||||
|
return common.HexToHash(*h), nil
|
||||||
|
} else {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tx := new(types.Transaction)
|
tx := new(types.Transaction)
|
||||||
if err := tx.UnmarshalBinary(input); err != nil {
|
if err := tx.UnmarshalBinary(input); err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
|
|
|
||||||
|
|
@ -3400,3 +3400,8 @@ func testRPCResponseWithFile(t *testing.T, testid int, result interface{}, rpc s
|
||||||
func addressToHash(a common.Address) common.Hash {
|
func addressToHash(a common.Address) common.Hash {
|
||||||
return common.BytesToHash(a.Bytes())
|
return common.BytesToHash(a.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHANGE(taiko): add preconfirmation forwarding URL
|
||||||
|
func (b testBackend) GetPreconfirmationForwardingURL() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,9 @@ type Backend interface {
|
||||||
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
|
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
|
||||||
BloomStatus() (uint64, uint64)
|
BloomStatus() (uint64, uint64)
|
||||||
ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
|
ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
|
||||||
|
|
||||||
|
// CHANGE(taiko): add preconfirmation forwarding URL
|
||||||
|
GetPreconfirmationForwardingURL() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAPIs(apiBackend Backend) []rpc.API {
|
func GetAPIs(apiBackend Backend) []rpc.API {
|
||||||
|
|
|
||||||
94
internal/ethapi/taiko_preconf.go
Normal file
94
internal/ethapi/taiko_preconf.go
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
package ethapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rpcRequest struct {
|
||||||
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Params []interface{} `json:"params"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type rpcResponse struct {
|
||||||
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Result *json.RawMessage `json:"result"`
|
||||||
|
Error *struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
} `json:"error,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func forward[T any](forwardURL string, method string, params []interface{}) (*T, error) {
|
||||||
|
rpcReq := rpcRequest{
|
||||||
|
Jsonrpc: "2.0",
|
||||||
|
Method: method,
|
||||||
|
Params: params,
|
||||||
|
ID: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(rpcReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", forwardURL, bytes.NewBuffer(jsonData))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("failed to forward transaction, status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rpcResp rpcResponse
|
||||||
|
|
||||||
|
// Unmarshal the response into the struct
|
||||||
|
if err := json.Unmarshal(body, &rpcResp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for errors in the response
|
||||||
|
if rpcResp.Error != nil {
|
||||||
|
err := fmt.Errorf("RPC error %d: %s", rpcResp.Error.Code, rpcResp.Error.Message)
|
||||||
|
|
||||||
|
log.Error("forwarded request error", "err", err, "method", method, "params", params)
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("RPC error %d: %s", rpcResp.Error.Code, rpcResp.Error.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rpcResp.Result == nil {
|
||||||
|
log.Warn("forwarded request result is nil", "method", method)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the Result into the desired type
|
||||||
|
var result T
|
||||||
|
if err := json.Unmarshal(*rpcResp.Result, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
@ -405,3 +405,8 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *backendMock) Engine() consensus.Engine { return nil }
|
func (b *backendMock) Engine() consensus.Engine { return nil }
|
||||||
|
|
||||||
|
// CHANGE(taiko): add preconfirmation forwarding URL
|
||||||
|
func (b *backendMock) GetPreconfirmationForwardingURL() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ const (
|
||||||
MiscCategory = "MISC"
|
MiscCategory = "MISC"
|
||||||
TestingCategory = "TESTING"
|
TestingCategory = "TESTING"
|
||||||
DeprecatedCategory = "ALIASED (deprecated)"
|
DeprecatedCategory = "ALIASED (deprecated)"
|
||||||
|
// CHANGE(taiko): Add a new flag category for Taiko network
|
||||||
|
TaikoCategory = "TAIKO"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
||||||
|
|
@ -300,6 +300,7 @@ var NetworkNames = map[string]string{
|
||||||
JolnirNetworkID.String(): "Taiko Alpha-5 L2 (Jolnir)",
|
JolnirNetworkID.String(): "Taiko Alpha-5 L2 (Jolnir)",
|
||||||
KatlaNetworkID.String(): "Taiko Alpha-6 L2 (Katla)",
|
KatlaNetworkID.String(): "Taiko Alpha-6 L2 (Katla)",
|
||||||
HeklaNetworkID.String(): "Taiko Alpha-7 L2 (Hekla)",
|
HeklaNetworkID.String(): "Taiko Alpha-7 L2 (Hekla)",
|
||||||
|
PreconfDevnetNetworkID.String(): "Taiko Preconfirmation Devnet",
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainConfig is the core config which determines the blockchain settings.
|
// ChainConfig is the core config which determines the blockchain settings.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ var (
|
||||||
JolnirNetworkID = big.NewInt(167007)
|
JolnirNetworkID = big.NewInt(167007)
|
||||||
KatlaNetworkID = big.NewInt(167008)
|
KatlaNetworkID = big.NewInt(167008)
|
||||||
HeklaNetworkID = big.NewInt(167009)
|
HeklaNetworkID = big.NewInt(167009)
|
||||||
|
PreconfDevnetNetworkID = big.NewInt(167010)
|
||||||
)
|
)
|
||||||
|
|
||||||
var networkIDToChainConfig = map[*big.Int]*ChainConfig{
|
var networkIDToChainConfig = map[*big.Int]*ChainConfig{
|
||||||
|
|
@ -33,6 +34,7 @@ var networkIDToChainConfig = map[*big.Int]*ChainConfig{
|
||||||
JolnirNetworkID: TaikoChainConfig,
|
JolnirNetworkID: TaikoChainConfig,
|
||||||
KatlaNetworkID: TaikoChainConfig,
|
KatlaNetworkID: TaikoChainConfig,
|
||||||
HeklaNetworkID: TaikoChainConfig,
|
HeklaNetworkID: TaikoChainConfig,
|
||||||
|
PreconfDevnetNetworkID: TaikoChainConfig,
|
||||||
MainnetChainConfig.ChainID: MainnetChainConfig,
|
MainnetChainConfig.ChainID: MainnetChainConfig,
|
||||||
SepoliaChainConfig.ChainID: SepoliaChainConfig,
|
SepoliaChainConfig.ChainID: SepoliaChainConfig,
|
||||||
TestChainConfig.ChainID: TestChainConfig,
|
TestChainConfig.ChainID: TestChainConfig,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue