diff --git a/api/sonicapi/api.go b/api/sonicapi/api.go index 967f8b5b0..73a604632 100644 --- a/api/sonicapi/api.go +++ b/api/sonicapi/api.go @@ -15,3 +15,16 @@ // along with Sonic. If not, see . package sonicapi + +import "github.com/0xsoniclabs/sonic/api/ethapi" + +type PublicBundleAPI struct { + b ethapi.Backend +} + +// NewPublicBundleAPI creates a new instance of the PublicBundleAPI with the given backend. +func NewPublicBundleAPI(b ethapi.Backend) *PublicBundleAPI { + return &PublicBundleAPI{ + b: b, + } +} diff --git a/api/sonicapi/bundle_api.go b/api/sonicapi/bundle_api.go new file mode 100644 index 000000000..4770bb9e2 --- /dev/null +++ b/api/sonicapi/bundle_api.go @@ -0,0 +1,53 @@ +// Copyright 2026 Sonic Operations Ltd +// This file is part of the Sonic Client +// +// Sonic is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Sonic is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Sonic. If not, see . + +package sonicapi + +import ( + "github.com/0xsoniclabs/sonic/gossip/blockproc/bundle" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" +) + +type RPCExecutionStep struct { + From common.Address `json:"from"` + Hash common.Hash `json:"hash"` +} + +type RPCExecutionPlan struct { + Flags bundle.ExecutionFlags `json:"flags"` + Steps []RPCExecutionStep `json:"steps"` + Earliest rpc.BlockNumber `json:"earliest"` + Latest rpc.BlockNumber `json:"latest"` +} + +// NewRPCExecutionPlan converts a bundle.ExecutionPlan to an RPCExecutionPlan for JSON-RPC responses. +func NewRPCExecutionPlan(plan bundle.ExecutionPlan) RPCExecutionPlan { + steps := make([]RPCExecutionStep, len(plan.Steps)) + for i, step := range plan.Steps { + steps[i] = RPCExecutionStep{ + From: step.From, + Hash: step.Hash, + } + } + + return RPCExecutionPlan{ + Flags: plan.Flags, + Steps: steps, + Earliest: rpc.BlockNumber(plan.Range.Earliest), + Latest: rpc.BlockNumber(plan.Range.Latest), + } +} diff --git a/api/sonicapi/bundle_api_test.go b/api/sonicapi/bundle_api_test.go new file mode 100644 index 000000000..bbeb022be --- /dev/null +++ b/api/sonicapi/bundle_api_test.go @@ -0,0 +1,57 @@ +// Copyright 2026 Sonic Operations Ltd +// This file is part of the Sonic Client +// +// Sonic is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Sonic is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Sonic. If not, see . + +package sonicapi + +import ( + "testing" + + "github.com/0xsoniclabs/sonic/gossip/blockproc/bundle" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" + "github.com/stretchr/testify/require" +) + +func TestNewRPCExecutionPlan(t *testing.T) { + plan := bundle.ExecutionPlan{ + Steps: []bundle.ExecutionStep{ + { + From: common.HexToAddress("0x1111111111111111111111111111111111111111"), + Hash: common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222"), + }, + { + From: common.HexToAddress("0x3333333333333333333333333333333333333333"), + Hash: common.HexToHash("0x4444444444444444444444444444444444444444444444444444444444444444"), + }, + }, + Flags: bundle.EF_TolerateInvalid | bundle.EF_OneOf, + Range: bundle.BlockRange{ + Earliest: 100, + Latest: 200, + }, + } + + rpcPlan := NewRPCExecutionPlan(plan) + + require.Equal(t, plan.Flags, rpcPlan.Flags) + require.Equal(t, rpc.BlockNumber(plan.Range.Earliest), rpcPlan.Earliest) + require.Equal(t, rpc.BlockNumber(plan.Range.Latest), rpcPlan.Latest) + require.Len(t, rpcPlan.Steps, len(plan.Steps)) + for i, step := range plan.Steps { + require.Equal(t, step.From, rpcPlan.Steps[i].From) + require.Equal(t, step.Hash, rpcPlan.Steps[i].Hash) + } +}