Skip to content

Commit

Permalink
implement logic to let sequencer shadow the sepolia network
Browse files Browse the repository at this point in the history
  • Loading branch information
rian committed Aug 3, 2024
1 parent 6571a1c commit fe14d54
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 12 deletions.
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,20 @@ sequencer-with-accounts:
--seq-genesis-file "./genesis/genesis_prefund_accounts.json" \
--rpc-call-max-steps=4123000


sequencer-shadow-sepolia:
./build/juno \
--http \
--http-port=6060 \
--http-host=0.0.0.0 \
--db-path=./seq-db \
--log-level=debug \
--seq-enable \
--seq-shadow-mode \
--seq-block-time=5 \
--network sepolia \
--rpc-call-max-steps=4123000

pathfinder: juno-cached
./build/juno \
--network=sepolia \
Expand Down
210 changes: 203 additions & 7 deletions builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package builder
import (
"context"
"errors"
"fmt"
stdsync "sync"
"time"

Expand All @@ -15,6 +16,7 @@ import (
"github.com/NethermindEth/juno/feed"
"github.com/NethermindEth/juno/mempool"
"github.com/NethermindEth/juno/service"
"github.com/NethermindEth/juno/starknetdata"
"github.com/NethermindEth/juno/sync"
"github.com/NethermindEth/juno/utils"
"github.com/NethermindEth/juno/vm"
Expand Down Expand Up @@ -42,17 +44,27 @@ type Builder struct {
pendingBlock blockchain.Pending
headState core.StateReader
headCloser blockchain.StateCloser

shadowMode bool
starknetData starknetdata.StarknetData
chanNumTxnsToShadow chan int
chanFinaliseShadow chan struct{}

chanFinalise chan struct{}
chanFinalised chan struct{}
}

func New(privKey *ecdsa.PrivateKey, ownAddr *felt.Felt, bc *blockchain.Blockchain, builderVM vm.VM,
blockTime time.Duration, pool *mempool.Pool, log utils.Logger,
) *Builder {
return &Builder{
ownAddress: *ownAddr,
privKey: privKey,
blockTime: blockTime,
log: log,
listener: &SelectiveListener{},
ownAddress: *ownAddr,
privKey: privKey,
blockTime: blockTime,
log: log,
listener: &SelectiveListener{},
chanFinalise: make(chan struct{}),
chanFinalised: make(chan struct{}, 1),

bc: bc,
pool: pool,
Expand All @@ -61,12 +73,42 @@ func New(privKey *ecdsa.PrivateKey, ownAddr *felt.Felt, bc *blockchain.Blockchai
}
}

func NewShadow(privKey *ecdsa.PrivateKey, ownAddr *felt.Felt, bc *blockchain.Blockchain, builderVM vm.VM,
blockTime time.Duration, pool *mempool.Pool, log utils.Logger, starknetData starknetdata.StarknetData,
) *Builder {
return &Builder{
ownAddress: *ownAddr,
privKey: privKey,
blockTime: blockTime,
log: log,
listener: &SelectiveListener{},
chanFinalise: make(chan struct{}, 1),
chanFinalised: make(chan struct{}, 1),

bc: bc,
pool: pool,
vm: builderVM,
newHeads: feed.New[*core.Header](),

shadowMode: true,
starknetData: starknetData,
chanNumTxnsToShadow: make(chan int, 1),
chanFinaliseShadow: make(chan struct{}, 1),
}
}

func (b *Builder) WithEventListener(l EventListener) *Builder {
b.listener = l
return b
}

func (b *Builder) Run(ctx context.Context) error {

Check failure on line 105 in builder/builder.go

View workflow job for this annotation

GitHub Actions / lint

cyclomatic complexity 19 of func `(*Builder).Run` is high (> 15) (gocyclo)
if b.shadowMode {
if err := b.syncStore(1); err != nil {
return err
}
}

if err := b.InitPendingBlock(); err != nil {
return err
}
Expand All @@ -84,17 +126,45 @@ func (b *Builder) Run(ctx context.Context) error {
}
close(doneListen)
}()
if b.shadowMode {
go func() {
if pErr := b.shadowTxns(ctx); pErr != nil {
b.log.Errorw("shadowTxns", "err", pErr)
}
}()
}

go func() {
if b.shadowMode {
for {
select {
case <-b.chanFinaliseShadow:
b.chanFinalise <- struct{}{}
case <-ctx.Done():
return
}
}
}
for {
select {
case <-time.After(b.blockTime):
b.chanFinalise <- struct{}{}
case <-ctx.Done():
return
}
}
}()
for {
select {
case <-ctx.Done():
<-doneListen
return nil
case <-time.After(b.blockTime):
case <-b.chanFinalise:
b.log.Debugw("Finalising new block")
if err := b.Finalise(); err != nil {
return err
}
<-b.chanFinalised
}
}
}
Expand Down Expand Up @@ -314,6 +384,17 @@ func (b *Builder) depletePool(ctx context.Context) error {
b.log.Debugw("failed txn", "hash", userTxn.Transaction.Hash().String(), "err", err.Error())
}

if b.shadowMode {
fmt.Println("<-chanNumTxnsToShadow ")
numTxnsToExecute := <-b.chanNumTxnsToShadow
fmt.Println("chanNumTxnsToShadow <- <- ")
b.chanNumTxnsToShadow <- numTxnsToExecute - 1
if numTxnsToExecute-1 == 0 {
b.chanFinaliseShadow <- struct{}{}
<-b.chanNumTxnsToShadow
}
}

select {
case <-ctx.Done():
return nil
Expand All @@ -322,6 +403,26 @@ func (b *Builder) depletePool(ctx context.Context) error {
}
}

func getPaidOnL1Fees(txn *mempool.BroadcastedTransaction) ([]*felt.Felt, error) {
if tx, ok := (txn.Transaction).(*core.L1HandlerTransaction); ok {
handleDepositEPS, err := new(felt.Felt).SetString("0x2d757788a8d8d6f21d1cd40bce38a8222d70654214e96ff95d8086e684fbee5")
if err != nil {
return nil, err
}
handleTokenDepositEPS, err := new(felt.Felt).SetString("0x1b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb19")
if err != nil {
return nil, err
}
if tx.EntryPointSelector.Equal(handleDepositEPS) {
return []*felt.Felt{tx.CallData[2]}, nil
} else if tx.EntryPointSelector.Equal(handleTokenDepositEPS) {
return []*felt.Felt{tx.CallData[4]}, nil
}
return nil, fmt.Errorf("failed to get fees_paid_on_l1, unkmown entry point selector")
}
return []*felt.Felt{}, nil
}

func (b *Builder) runTxn(txn *mempool.BroadcastedTransaction) error {
b.pendingLock.Lock()
defer b.pendingLock.Unlock()
Expand All @@ -331,6 +432,10 @@ func (b *Builder) runTxn(txn *mempool.BroadcastedTransaction) error {
classes = append(classes, txn.DeclaredClass)
}

feesPaidOnL1, err := getPaidOnL1Fees(txn)
if err != nil {
return err
}
blockInfo := &vm.BlockInfo{
Header: &core.Header{
Number: b.pendingBlock.Block.Number,
Expand All @@ -341,7 +446,7 @@ func (b *Builder) runTxn(txn *mempool.BroadcastedTransaction) error {
},
}

fee, _, trace, err := b.vm.Execute([]core.Transaction{txn.Transaction}, classes, []*felt.Felt{}, blockInfo, state,
fee, _, trace, err := b.vm.Execute([]core.Transaction{txn.Transaction}, classes, feesPaidOnL1, blockInfo, state,
b.bc.Network(), false, false, false, false)
if err != nil {
return err
Expand Down Expand Up @@ -400,3 +505,94 @@ func mergeStateDiffs(oldStateDiff, newStateDiff *core.StateDiff) *core.StateDiff

return oldStateDiff
}

func (b *Builder) shadowTxns(ctx context.Context) error {
for {
b.chanFinalised <- struct{}{}
builderHeadBlock, err := b.bc.Head()
if err != nil {
return err
}
snHeadBlock, err := b.starknetData.BlockLatest(ctx)
if err != nil {
return err
}
b.log.Debugw(fmt.Sprintf("Juno head at block %d, Sepolia at block %d, attempting to sequence next block", builderHeadBlock.Number, snHeadBlock.Number))

Check failure on line 520 in builder/builder.go

View workflow job for this annotation

GitHub Actions / lint

line is 153 characters (lll)
if builderHeadBlock.Number < snHeadBlock.Number {
block, _, classes, err := b.getSyncData(builderHeadBlock.Number + 1) // todo: don't need state updates here
if err != nil {
return err
}
fmt.Println("chanNumTxnsToShadow <- ")
b.chanNumTxnsToShadow <- int(block.TransactionCount)
fmt.Println(" not blocking chanNumTxnsToShadow <- ")
for i, txn := range block.Transactions {
var declaredClass core.Class
declareTxn, ok := txn.(*core.DeclareTransaction)
if ok {
declaredClass = classes[*declareTxn.ClassHash]
}
err = b.pool.Push(
&mempool.BroadcastedTransaction{
Transaction: txn,
DeclaredClass: declaredClass,
})
if err != nil {
return err
}
qwe := declaredClass == nil
b.log.Debugw(fmt.Sprintf("Pushed txn number %d, %v", i, qwe)) // Todo : remove
}

} else {

Check failure on line 547 in builder/builder.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary trailing newline (whitespace)
var sleepTime uint = 1
b.log.Debugw("Juno Sequencer is at Sepolia chain head. Sleeping for %ds before querying for a new block.", sleepTime)
time.Sleep(time.Duration(sleepTime))
}
}
}

func (b *Builder) syncStore(toBlockNum uint64) error {
var i uint64
for i = 0; i < toBlockNum; i++ {
b.log.Infow("Sequencer, syncing block", "blockNumber", i)
block, su, classes, err := b.getSyncData(i)
if err != nil {
return err
}
commitments, err := b.bc.SanityCheckNewHeight(block, su, classes)
if err != nil {
return err
}
err = b.bc.Store(block, commitments, su, classes)
if err != nil {
return err
}
}
return nil
}

func (b *Builder) getSyncData(blockNumber uint64) (*core.Block, *core.StateUpdate,
map[felt.Felt]core.Class, error,
) {
block, err := b.starknetData.BlockByNumber(context.Background(), blockNumber)
if err != nil {
return nil, nil, nil, err
}
su, err := b.starknetData.StateUpdate(context.Background(), blockNumber)
if err != nil {
return nil, nil, nil, err
}
txns := block.Transactions
classes := make(map[felt.Felt]core.Class)
for _, txn := range txns {
if t, ok := txn.(*core.DeclareTransaction); ok {
class, err := b.starknetData.Class(context.Background(), t.ClassHash)
if err != nil {
return nil, nil, nil, err
}
classes[*t.ClassHash] = class
}
}
return block, su, classes, nil
}
62 changes: 62 additions & 0 deletions builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,3 +487,65 @@ func TestPrefundedAccounts(t *testing.T) {
require.Equal(t, len(expectedExnsInBlock), numExpectedBalance, "Accounts don't have the expected balance")
require.True(t, foundExpectedBalance)
}

func TestShadowSepolia(t *testing.T) {
mockCtrl := gomock.NewController(t)
snData := mocks.NewMockStarknetData(mockCtrl)
network := &utils.Sepolia
bc := blockchain.New(pebble.NewMemTest(t), network)
p := mempool.New(pebble.NewMemTest(t))
log := utils.NewNopZapLogger()
vmm := vm.New(false, log)
seqAddr := utils.HexToFelt(t, "0xDEADBEEF")
privKey, err := ecdsa.GenerateKey(rand.Reader)
require.NoError(t, err)

blockTime := time.Second
testBuilder := builder.NewShadow(privKey, seqAddr, bc, vmm, blockTime, p, log, snData)
gw := adaptfeeder.New(feeder.NewTestClient(t, network))

const numTestBlocks = 3 // Note: depends on the number of blocks that the buidler syncStores (see Run())
var blocks [numTestBlocks]*core.Block
for i := 0; i < numTestBlocks; i++ {
block, err2 := gw.BlockByNumber(context.Background(), uint64(i))
require.NoError(t, err2)
blocks[i] = block
su, err2 := gw.StateUpdate(context.Background(), uint64(i))
require.NoError(t, err2)
snData.EXPECT().BlockByNumber(context.Background(), uint64(i)).Return(block, nil)
snData.EXPECT().StateUpdate(context.Background(), uint64(i)).Return(su, nil)
}
ctx, cancel := context.WithTimeout(context.Background(), numTestBlocks*blockTime)
defer cancel()
// We sync store block 0, then sequence blocks 1 and 2
snData.EXPECT().BlockLatest(ctx).Return(blocks[1], nil)
snData.EXPECT().BlockLatest(ctx).Return(blocks[2], nil)
snData.EXPECT().BlockLatest(ctx).Return(nil, errors.New("only sequence up to block 2"))
classHashes := []string{
"0x5c478ee27f2112411f86f207605b2e2c58cdb647bac0df27f660ef2252359c6",
"0xd0e183745e9dae3e4e78a8ffedcce0903fc4900beace4e0abf192d4c202da3",
"0x1b661756bf7d16210fc611626e1af4569baa1781ffc964bd018f4585ae241c1",
"0x4f23a756b221f8ce46b72e6a6b10ee7ee6cf3b59790e76e02433104f9a8c5d1",
}
for _, hash := range classHashes {
classHash := utils.HexToFelt(t, hash)
class, err2 := gw.Class(context.Background(), classHash)
require.NoError(t, err2)
snData.EXPECT().Class(context.Background(), classHash).Return(class, nil)
}
err = testBuilder.Run(ctx)
require.NoError(t, err)
runTest := func(t *testing.T, wantBlockNum uint64, wantBlock *core.Block) {
gotBlock, err := bc.BlockByNumber(wantBlockNum)
require.NoError(t, err)
require.Equal(t, wantBlock.Number, gotBlock.Number)
require.Equal(t, wantBlock.TransactionCount, gotBlock.TransactionCount, "TransactionCount diff")
require.Equal(t, wantBlock.GlobalStateRoot.String(), gotBlock.GlobalStateRoot.String(), "GlobalStateRoot diff")
}
for i := range numTestBlocks {
runTest(t, uint64(i), blocks[i])
}
head, err := bc.Head()
require.NoError(t, err)
require.Equal(t, uint64(2), head.Number)
}
Loading

0 comments on commit fe14d54

Please sign in to comment.