Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(beacon): introduce soft blocks #342

Merged
merged 35 commits into from
Jan 7, 2025
Merged
Changes from 14 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
aaf9be2
feat(beacon): introduce soft blocks
davidtaikocha Oct 28, 2024
028fdd2
feat: update api.go
davidtaikocha Oct 28, 2024
bafb545
Merge branch 'taiko' into softblock
davidtaikocha Oct 29, 2024
3ea9a79
chore(ci): update CI
davidtaikocha Oct 29, 2024
47538d5
feat: update L1Origin
davidtaikocha Oct 29, 2024
c4cbc82
feat: update `verifyHeader`
davidtaikocha Oct 30, 2024
43ab184
test: update tests
davidtaikocha Oct 31, 2024
5e6011a
feat: update consensus
davidtaikocha Oct 31, 2024
a50a061
feat: update consensus
davidtaikocha Oct 31, 2024
d93f445
Merge branch 'taiko' into softblock
davidtaikocha Nov 3, 2024
d4cb83f
feat: update genesis
davidtaikocha Nov 3, 2024
93ed527
feat: remove timestamp check in prepareWork
davidtaikocha Nov 3, 2024
266c8ca
feat: merge changes in #281
davidtaikocha Nov 5, 2024
2d3d130
Update eth/catalyst/api.go
davidtaikocha Nov 5, 2024
e8dccf2
Update internal/ethapi/taiko_preconf.go
davidtaikocha Nov 5, 2024
f1df584
fix consensus test
mask-pp Nov 6, 2024
c8a5a9b
Merge branch 'taiko' into softblock
mask-pp Nov 6, 2024
3f6b0f5
revert commit f1df58
mask-pp Nov 6, 2024
e692d34
fix consensus test (#349)
mask-pp Nov 6, 2024
11e6061
Update eth/catalyst/api.go
davidtaikocha Nov 6, 2024
185e4c3
feat: add back timestamp check in worker
davidtaikocha Nov 6, 2024
cf0c053
add genesis
cyberhorsey Nov 6, 2024
b06f6ba
temp fix for old l1origin
cyberhorsey Nov 6, 2024
fd9b48a
nil value
cyberhorsey Nov 6, 2024
c18e4db
feat: rename to `L1OriginLegacy`
davidtaikocha Nov 7, 2024
3cd4472
feat: change `common.Big0` as the default value for legacy l1Origin, …
davidtaikocha Nov 7, 2024
ca660bd
feat(beacon): change the reorg log level (#350)
mask-pp Nov 17, 2024
51d42ec
add rlp optional flag (#353)
mask-pp Nov 17, 2024
dd04f6c
fix lint
mask-pp Nov 17, 2024
5c649ba
fix test case
mask-pp Nov 17, 2024
e3eb72f
feat(l1Origin): remove the reverted l1Origins (#355)
mask-pp Nov 27, 2024
d39b410
only forward txs
cyberhorsey Dec 3, 2024
2572112
Merge branch 'softblock' of github.com:taikochain/taiko-geth into sof…
cyberhorsey Dec 3, 2024
cd16465
Merge branch 'taiko' into softblock
davidtaikocha Jan 6, 2025
d55da97
chore: update ci
davidtaikocha Jan 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ name: "Push multi-arch docker image to GAR"

on:
push:
branches: [ taiko ]
branches: [ taiko, softblock ]
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 }}
5 changes: 5 additions & 0 deletions cmd/geth/config.go
Original file line number Diff line number Diff line change
@@ -192,6 +192,11 @@ func makeFullNode(ctx *cli.Context) *node.Node {
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)

// CHANGE(TAIKO): register Taiko RPC APIs.
2 changes: 1 addition & 1 deletion cmd/geth/main.go
Original file line number Diff line number Diff line change
@@ -261,7 +261,7 @@ func init() {
metricsFlags,
)
// 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")

13 changes: 10 additions & 3 deletions cmd/utils/taiko_flags.go
Original file line number Diff line number Diff line change
@@ -5,16 +5,23 @@ import (

"github.com/ethereum/go-ethereum/eth"
"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/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/urfave/cli/v2"
)

var (
TaikoFlag = cli.BoolFlag{
Name: "taiko",
Usage: "Taiko network",
TaikoFlag = &cli.BoolFlag{
Name: "taiko",
Usage: "Taiko network",
Category: flags.TaikoCategory,
}
PreconfirmationForwardingURLFlag = &cli.StringFlag{
Name: "taiko.preconfirmationForwardingUrl",
Usage: "URL to forward RPC requests before confirmation",
Category: flags.TaikoCategory,
}
)

20 changes: 15 additions & 5 deletions consensus/taiko/consensus.go
Original file line number Diff line number Diff line change
@@ -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"
@@ -40,12 +42,13 @@ var (
// Taiko is a consensus engine used by L2 rollup.
type Taiko struct {
chainConfig *params.ChainConfig
chainDB ethdb.Database
taikoL2Address common.Address
}

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 +59,7 @@ func New(chainConfig *params.ChainConfig) *Taiko {
strings.Repeat("0", common.AddressLength*2-len(taikoL2AddressPrefix)-len(TaikoL2AddressSuffix)) +
TaikoL2AddressSuffix,
),
chainDB: chainDB,
}
}

@@ -82,7 +86,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 +113,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,8 +125,14 @@ 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) {
func (t *Taiko) verifyHeader(header, parent *types.Header, unixNow int64) error {
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.IsSoftblock() && header.Time > uint64(unixNow) {
return consensus.ErrFutureBlock
}

2 changes: 1 addition & 1 deletion consensus/taiko/consensus_test.go
Original file line number Diff line number Diff line change
@@ -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")

42 changes: 32 additions & 10 deletions core/rawdb/gen_taiko_l1_origin.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 21 additions & 4 deletions core/rawdb/taiko_l1_origin.go
Original file line number Diff line number Diff line change
@@ -29,17 +29,34 @@ func l1OriginKey(blockID *big.Int) []byte {

// L1Origin represents a L1Origin of a L2 block.
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"`
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"`
EndOfBlock bool `json:"endOfBlock"`
EndOfPreconf bool `json:"endOfPreconf"`
Preconfer common.Address `json:"preconfer"`
}

type l1OriginMarshaling struct {
BlockID *math.HexOrDecimal256
L1BlockHeight *math.HexOrDecimal256
}

// IsSoftblock returns true if the L1Origin is a softblock.
func (l *L1Origin) IsSoftblock() bool {
if l.BatchID == nil {
return true
mask-pp marked this conversation as resolved.
Show resolved Hide resolved
}

if l.BatchID.Cmp(common.Big0) == 0 && l.Preconfer == (common.Address{}) {
mask-pp marked this conversation as resolved.
Show resolved Hide resolved
return false
}

return true
}

// WriteL1Origin stores a L1Origin into the database.
func WriteL1Origin(db ethdb.KeyValueWriter, blockID *big.Int, l1Origin *L1Origin) {
data, err := rlp.EncodeToBytes(l1Origin)
7 changes: 6 additions & 1 deletion core/taiko_genesis.go
Original file line number Diff line number Diff line change
@@ -10,7 +10,8 @@ import (
)

var (
InternalDevnetOntakeBlock = new(big.Int).SetUint64(0)
InternalDevnetOntakeBlock = common.Big0
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
3 changes: 3 additions & 0 deletions core/taiko_genesis/genesis_alloc.go
Original file line number Diff line number Diff line change
@@ -33,3 +33,6 @@ var HeklaGenesisAllocJSON []byte

//go:embed mainnet.json
var MainnetGenesisAllocJSON []byte

//go:embed preconf_devnet.json
davidtaikocha marked this conversation as resolved.
Show resolved Hide resolved
var PreconfDevnetGenesisAllocJSON []byte
42 changes: 21 additions & 21 deletions core/taiko_genesis/internal_l2a.json

Large diffs are not rendered by default.

369 changes: 369 additions & 0 deletions core/taiko_genesis/preconf_devnet.json

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
@@ -44,10 +44,11 @@ import (

// EthAPIBackend implements ethapi.Backend and tracers.Backend for full nodes
type EthAPIBackend struct {
extRPCEnabled bool
allowUnprotectedTxs bool
eth *Ethereum
gpo *gasprice.Oracle
extRPCEnabled bool
allowUnprotectedTxs bool
eth *Ethereum
gpo *gasprice.Oracle
preconfirmationForwardingURL string // CHANGE(taiko): add preconfirmation forwarding URL
mask-pp marked this conversation as resolved.
Show resolved Hide resolved
}

// 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) {
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
}
5 changes: 4 additions & 1 deletion eth/backend.go
Original file line number Diff line number Diff line change
@@ -96,6 +96,8 @@ type Ethereum struct {
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

PreconfirmationForwardingURL string // CHANGE(taiko): add preconfirmation forwarding URL
}

// 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.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 {
log.Info("Unprotected transactions allowed")
}
17 changes: 15 additions & 2 deletions eth/catalyst/api.go
Original file line number Diff line number Diff line change
@@ -446,6 +446,17 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
if payloadAttributes != nil {
// CHANGE(taiko): create a L2 block by Taiko protocol.
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
// marked as required.
block, err := api.eth.Miner().SealBlockWith(
@@ -488,8 +499,10 @@ 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 its not a soft block.
davidtaikocha marked this conversation as resolved.
Show resolved Hide resolved
if l1Origin.BatchID == nil {
mask-pp marked this conversation as resolved.
Show resolved Hide resolved
rawdb.WriteHeadL1Origin(api.eth.ChainDb(), l1Origin.BlockID)
}

return valid(&id), nil
}
5 changes: 4 additions & 1 deletion eth/ethconfig/config.go
Original file line number Diff line number Diff line change
@@ -157,6 +157,9 @@ type Config struct {

// OverrideVerkle (TODO: remove after the fork)
OverrideVerkle *uint64 `toml:",omitempty"`

// CHANGE(taiko): add preconfirmation forwarding URL
PreconfirmationForwardingURL string
}

// 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) {
// 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.
10 changes: 10 additions & 0 deletions ethclient/taiko_api_test.go
Original file line number Diff line number Diff line change
@@ -69,6 +69,10 @@ func TestHeadL1Origin(t *testing.T) {
L2BlockHash: headerHash,
L1BlockHeight: randomBigInt(),
L1BlockHash: randomHash(),
BatchID: common.Big0,
EndOfBlock: false,
EndOfPreconf: false,
Preconfer: common.Address{},
}

rawdb.WriteL1Origin(db, testL1Origin.BlockID, testL1Origin)
@@ -78,6 +82,7 @@ func TestHeadL1Origin(t *testing.T) {

require.Nil(t, err)
require.Equal(t, testL1Origin, l1OriginFound)
require.False(t, l1OriginFound.IsSoftblock())
}

func TestL1OriginByID(t *testing.T) {
@@ -89,6 +94,10 @@ func TestL1OriginByID(t *testing.T) {
L2BlockHash: headerHash,
L1BlockHeight: randomBigInt(),
L1BlockHash: randomHash(),
BatchID: common.Big1,
EndOfBlock: false,
EndOfPreconf: false,
Preconfer: testAddr,
}

l1OriginFound, err := ec.L1OriginByID(context.Background(), testL1Origin.BlockID)
@@ -102,6 +111,7 @@ func TestL1OriginByID(t *testing.T) {

require.Nil(t, err)
require.Equal(t, testL1Origin, l1OriginFound)
require.True(t, l1OriginFound.IsSoftblock())
}

// randomHash generates a random blob of data and returns it as a hash.
122 changes: 119 additions & 3 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ import (
"fmt"
"maps"
"math/big"
"strconv"
"strings"
"time"

@@ -657,6 +658,22 @@ func (api *BlockChainAPI) ChainId() *hexutil.Big {

// BlockNumber returns the block number of the chain head.
func (api *BlockChainAPI) BlockNumber() hexutil.Uint64 {
// CHANGE(taiko): Forward the request to the preconf node if specified.
if forwardURL := api.b.GetPreconfirmationForwardingURL(); forwardURL != "" {
log.Info("Forwarding BlockNumber request", "forwardURL", forwardURL)

// Forward the raw transaction to the specified URL
res, err := forward[string](forwardURL, "eth_blockNumber", nil)

if err == nil && res != nil {
log.Info("Forwarded block number request", "forwardURL", forwardURL, "res", res)
i, _ := strconv.ParseUint(*res, 0, 64)

log.Info("Parsed block number", "forwardURL", forwardURL, "number", hexutil.Uint64(i))
return hexutil.Uint64(i)
}
}

header, _ := api.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available
return hexutil.Uint64(header.Number.Uint64())
}
@@ -665,6 +682,37 @@ func (api *BlockChainAPI) BlockNumber() hexutil.Uint64 {
// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
// block numbers are also allowed.
func (api *BlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) {
// CHANGE(taiko): Forward the request to the preconf node if specified.
if forwardURL := api.b.GetPreconfirmationForwardingURL(); forwardURL != "" {
if blockNr, ok := blockNrOrHash.Number(); ok {
log.Info(
"Forwarding GetBalance request",
"forwardURL", forwardURL,
"address", address.Hex(),
"blockNr", blockNr.String(),
)

bal, err := forward[string](forwardURL, "eth_getBalance", []interface{}{address.Hex(), blockNr.String()})
if err == nil && bal != nil {
return (*hexutil.Big)(hexutil.MustDecodeBig(*bal)), nil
}
}

if blockHash, ok := blockNrOrHash.Hash(); ok {
log.Info(
"Forwarding GetBalance request",
"forwardURL", forwardURL,
"address", address.Hex(),
"blockHash", blockHash.Hex(),
)

bal, err := forward[string](forwardURL, "eth_getBalance", []interface{}{address.Hex(), blockHash.Hex()})
if err == nil && bal != nil {
return (*hexutil.Big)(hexutil.MustDecodeBig(*bal)), nil
}
}
}

state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil {
return nil, err
@@ -845,7 +893,17 @@ func (api *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.Block
response[field] = nil
}
}
return response, nil
return response, err
} else {
// CHANGE(taiko): Forward the request to the preconf node if specified.
if forwardURL := api.b.GetPreconfirmationForwardingURL(); forwardURL != "" {
log.Info("Forwarding GetBlockByNumber", "forwardURL", forwardURL, "number", number.Int64(), "numberStr", number.String())
// Forward the raw transaction to the specified URL.
b, err := forward[map[string]interface{}](forwardURL, "eth_getBlockByNumber", []interface{}{number.String(), fullTx})
if err == nil && b != nil {
return *b, nil
}
}
}
return nil, err
}
@@ -856,6 +914,15 @@ func (api *BlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash,
block, err := api.b.BlockByHash(ctx, hash)
if block != nil {
return RPCMarshalBlock(block, true, fullTx, api.b.ChainConfig()), nil
} else {
// CHANGE(taiko): Forward the request to the preconf node if specified.
if forwardURL := api.b.GetPreconfirmationForwardingURL(); forwardURL != "" {
log.Info("Forwarding GetBlockByHash", "forwardURL", forwardURL, "hash", hash.Hex())
m, err := forward[map[string]interface{}](forwardURL, "eth_getBlockByHash", []interface{}{hash.Hex(), fullTx})
if err == nil && m != nil {
return *m, nil
}
}
}
return nil, err
}
@@ -1750,6 +1817,24 @@ func (api *TransactionAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Cont

// GetTransactionCount returns the number of transactions the given address has sent for the given block number
func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) {
// CHANGE(taiko): Forward the request to the preconf node if specified.
if forwardURL := api.b.GetPreconfirmationForwardingURL(); forwardURL != "" {
log.Info("Forwarding GetTransactionCount request", "forwardURL", forwardURL, "address", address.Hex())

if blockNr, ok := blockNrOrHash.Number(); ok {
txCount, err := forward[hexutil.Uint64](forwardURL, "eth_getTransactionCount", []interface{}{address.Hex(), blockNr.String()})
if err == nil && txCount != nil {
return txCount, nil
}
}

if blockHash, ok := blockNrOrHash.Hash(); ok {
txCount, err := forward[hexutil.Uint64](forwardURL, "eth_getTransactionCount", []interface{}{address.Hex(), blockHash.Hex()})
if err == nil && txCount != nil {
return txCount, nil
}
}
}
// Ask transaction pool for the nonce which includes pending transactions
if blockNr, ok := blockNrOrHash.Number(); ok && blockNr == rpc.PendingBlockNumber {
nonce, err := api.b.GetPoolNonce(ctx, address)
@@ -1807,9 +1892,28 @@ func (api *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash com
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
func (api *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
found, tx, blockHash, blockNumber, index, err := api.b.GetTransaction(ctx, hash)
if err != nil {
return nil, NewTxIndexingError() // transaction is not fully indexed
if api.b.GetPreconfirmationForwardingURL() == "" {
if err != nil {
return nil, NewTxIndexingError() // transaction is not fully indexed
}
} else {
if err != nil || !found {
// CHANGE(taiko): Forward the request to the preconf node if specified.
if forwardURL := api.b.GetPreconfirmationForwardingURL(); forwardURL != "" {
log.Info("Forwarding GetTransactionReceipt request", "forwardURL", forwardURL, "hash", hash.Hex())
// Forward the raw transaction to the specified URL
m, err := forward[map[string]interface{}](forwardURL, "eth_getTransactionReceipt", []interface{}{hash.Hex()})
if err == nil && m != nil {
return *m, err
}
}

if err != nil {
return nil, NewTxIndexingError() // transaction is not fully indexed
}
}
}

if !found {
return nil, nil // transaction is not existent or reachable
}
@@ -1974,6 +2078,18 @@ func (api *TransactionAPI) FillTransaction(ctx context.Context, args Transaction
// SendRawTransaction will add the signed transaction to the transaction pool.
// 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) {
// 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)
if err := tx.UnmarshalBinary(input); err != nil {
return common.Hash{}, err
5 changes: 5 additions & 0 deletions internal/ethapi/api_test.go
Original file line number Diff line number Diff line change
@@ -3400,3 +3400,8 @@ func testRPCResponseWithFile(t *testing.T, testid int, result interface{}, rpc s
func addressToHash(a common.Address) common.Hash {
return common.BytesToHash(a.Bytes())
}

// CHANGE(taiko): add preconfirmation forwarding URL
func (b testBackend) GetPreconfirmationForwardingURL() string {
return ""
}
3 changes: 3 additions & 0 deletions internal/ethapi/backend.go
Original file line number Diff line number Diff line change
@@ -96,6 +96,9 @@ type Backend interface {
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
BloomStatus() (uint64, uint64)
ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)

// CHANGE(taiko): add preconfirmation forwarding URL
GetPreconfirmationForwardingURL() string
}

func GetAPIs(apiBackend Backend) []rpc.API {
94 changes: 94 additions & 0 deletions internal/ethapi/taiko_preconf.go
Original file line number Diff line number Diff line change
@@ -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.Info("forwarded request result is nil", "method", method)
davidtaikocha marked this conversation as resolved.
Show resolved Hide resolved
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
}
5 changes: 5 additions & 0 deletions internal/ethapi/transaction_args_test.go
Original file line number Diff line number Diff line change
@@ -405,3 +405,8 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent)
}

func (b *backendMock) Engine() consensus.Engine { return nil }

// CHANGE(taiko): add preconfirmation forwarding URL
func (b *backendMock) GetPreconfirmationForwardingURL() string {
return ""
}
2 changes: 2 additions & 0 deletions internal/flags/categories.go
Original file line number Diff line number Diff line change
@@ -37,6 +37,8 @@ const (
MiscCategory = "MISC"
TestingCategory = "TESTING"
DeprecatedCategory = "ALIASED (deprecated)"
// CHANGE(taiko): Add a new flag category for Taiko network
TaikoCategory = "TAIKO"
)

func init() {
4 changes: 0 additions & 4 deletions miner/worker.go
Original file line number Diff line number Diff line change
@@ -165,10 +165,6 @@ func (miner *Miner) prepareWork(genParams *generateParams, witness bool) (*envir
return nil, fmt.Errorf("invalid timestamp, parent %d given %d", parent.Time, timestamp)
}
timestamp = parent.Time + 1
} else {
if parent.Time > timestamp {
davidtaikocha marked this conversation as resolved.
Show resolved Hide resolved
return nil, fmt.Errorf("invalid timestamp, parent %d given %d", parent.Time, timestamp)
}
}
}
// Construct the sealing block header.
1 change: 1 addition & 0 deletions params/config.go
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 2 additions & 0 deletions params/taiko_config.go
Original file line number Diff line number Diff line change
@@ -20,6 +20,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 +34,7 @@ var networkIDToChainConfig = map[*big.Int]*ChainConfig{
JolnirNetworkID: TaikoChainConfig,
KatlaNetworkID: TaikoChainConfig,
HeklaNetworkID: TaikoChainConfig,
PreconfDevnetNetworkID: TaikoChainConfig,
MainnetChainConfig.ChainID: MainnetChainConfig,
SepoliaChainConfig.ChainID: SepoliaChainConfig,
TestChainConfig.ChainID: TestChainConfig,