From 61854098d27d0f85e392d3014282786e4bdbcee2 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Thu, 23 May 2024 13:23:55 +0800 Subject: [PATCH] Ashraf Basic snap algo .. Did something + #1925 --- blockchain/blockchain.go | 67 +- blockchain/blockchain_test.go | 19 + blockchain/snap_server_interface.go | 164 +++ core/contract.go | 31 +- core/state.go | 292 ++++- core/trie/key.go | 58 + core/trie/key_test.go | 118 ++ core/trie/proof.go | 8 + core/trie/snap_support.go | 162 +++ core/trie/snap_support_test.go | 278 +++++ core/trie/trie.go | 69 ++ core/trie/trie_test.go | 152 +++ db/db.go | 2 + db/pebble/db.go | 12 + db/pebble/transaction.go | 7 + db/remote/db.go | 5 + go.mod | 2 +- migration/migration_pkg_test.go | 2 + node/node_test.go | 1 + p2p/starknet/handlers.go | 3 +- p2p/starknet/p2p/proto/class.proto | 7 +- p2p/starknet/p2p/proto/snapshot.proto | 112 ++ p2p/starknet/p2p/proto/state.proto | 2 +- p2p/starknet/spec/class.pb.go | 118 +- p2p/starknet/spec/common.pb.go | 2 +- p2p/starknet/spec/event.pb.go | 2 +- p2p/starknet/spec/header.pb.go | 2 +- p2p/starknet/spec/receipt.pb.go | 2 +- p2p/starknet/spec/snapshot.pb.go | 1530 +++++++++++++++++++++++++ p2p/starknet/spec/state.pb.go | 2 +- p2p/starknet/spec/transaction.pb.go | 2 +- rpc/events_test.go | 3 + sync/snap_server.go | 447 ++++++++ sync/snap_server_test.go | 206 ++++ sync/snapsyncer.go | 1107 ++++++++++++++++++ sync/snapsyncer_test.go | 384 +++++++ sync/sync_test.go | 5 + 37 files changed, 5345 insertions(+), 40 deletions(-) create mode 100644 blockchain/snap_server_interface.go create mode 100644 core/trie/snap_support.go create mode 100644 core/trie/snap_support_test.go create mode 100644 p2p/starknet/p2p/proto/snapshot.proto create mode 100644 p2p/starknet/spec/snapshot.pb.go create mode 100644 sync/snap_server.go create mode 100644 sync/snap_server_test.go create mode 100644 sync/snapsyncer.go create mode 100644 sync/snapsyncer_test.go diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index ee2411c471..36c98946c4 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -74,6 +74,8 @@ type Blockchain struct { network *utils.Network database db.DB + snapshots []*snapshotRecord + listener EventListener cachedPending atomic.Pointer[Pending] @@ -81,11 +83,19 @@ type Blockchain struct { func New(database db.DB, network *utils.Network) *Blockchain { RegisterCoreTypesToEncoder() - return &Blockchain{ + bc := &Blockchain{ database: database, network: network, listener: &SelectiveListener{}, } + + // TODO: Used only for testing though... + err := bc.seedSnapshot() + if err != nil { + fmt.Printf("Error seeding snapshot %s", err) + } + + return bc } func (b *Blockchain) WithListener(listener EventListener) *Blockchain { @@ -336,7 +346,7 @@ func (b *Blockchain) SetL1Head(update *core.L1Head) error { func (b *Blockchain) Store(block *core.Block, blockCommitments *core.BlockCommitments, stateUpdate *core.StateUpdate, newClasses map[felt.Felt]core.Class, ) error { - return b.database.Update(func(txn db.Transaction) error { + err := b.database.Update(func(txn db.Transaction) error { if err := verifyBlock(txn, block); err != nil { return err } @@ -372,6 +382,49 @@ func (b *Blockchain) Store(block *core.Block, blockCommitments *core.BlockCommit heightBin := core.MarshalBlockNumber(block.Number) return txn.Set(db.ChainHeight.Key(), heightBin) }) + if err != nil { + return err + } + + err = b.seedSnapshot() + if err != nil { + return err + } + + return nil +} + +func (b *Blockchain) StoreRaw(blockNumber uint64, stateDiff *core.StateDiff) error { + return b.database.Update(func(txn db.Transaction) error { + return core.NewState(txn).UpdateNoVerify(blockNumber, stateDiff, make(map[felt.Felt]core.Class)) + }) +} + +func (b *Blockchain) PutClasses(blockNumber uint64, classHashes map[felt.Felt]*felt.Felt, newClasses map[felt.Felt]core.Class) error { + return b.database.Update(func(txn db.Transaction) error { + v1ClassHashes := map[felt.Felt]*felt.Felt{} + for ch, class := range newClasses { + if class.Version() == 1 { + v1ClassHashes[ch] = classHashes[ch] + } + } + + return core.NewState(txn).UpdateNoVerify(blockNumber, &core.StateDiff{ + DeclaredV1Classes: v1ClassHashes, + }, newClasses) + }) +} + +func (b *Blockchain) PutContracts(address, nonces, classHash []*felt.Felt) error { + return b.database.Update(func(txn db.Transaction) error { + return core.NewState(txn).UpdateContractNoLog(address, nonces, classHash) + }) +} + +func (b *Blockchain) PutStorage(storage map[felt.Felt]map[felt.Felt]*felt.Felt) error { + return b.database.Update(func(txn db.Transaction) error { + return core.NewState(txn).UpdateContractStorages(storage) + }) } // VerifyBlock assumes the block has already been sanity-checked. @@ -769,6 +822,16 @@ func (b *Blockchain) HeadState() (core.StateReader, StateCloser, error) { return core.NewState(txn), txn.Discard, nil } +func (b *Blockchain) HeadStateFreakingState() (*core.State, StateCloser, error) { + b.listener.OnRead("HeadState") + txn, err := b.database.NewTransaction(false) + if err != nil { + return nil, nil, err + } + + return core.NewState(txn), txn.Discard, nil +} + // StateAtBlockNumber returns a StateReader that provides a stable view to the state at the given block number func (b *Blockchain) StateAtBlockNumber(blockNumber uint64) (core.StateReader, StateCloser, error) { b.listener.OnRead("StateAtBlockNumber") diff --git a/blockchain/blockchain_test.go b/blockchain/blockchain_test.go index c0c3666a91..abb70e01b7 100644 --- a/blockchain/blockchain_test.go +++ b/blockchain/blockchain_test.go @@ -27,6 +27,7 @@ func TestNew(t *testing.T) { gw := adaptfeeder.New(client) t.Run("empty blockchain's head is nil", func(t *testing.T) { chain := blockchain.New(pebble.NewMemTest(t), &utils.Mainnet) + defer chain.Close() assert.Equal(t, &utils.Mainnet, chain.Network()) b, err := chain.Head() assert.Nil(t, b) @@ -42,8 +43,10 @@ func TestNew(t *testing.T) { testDB := pebble.NewMemTest(t) chain := blockchain.New(testDB, &utils.Mainnet) assert.NoError(t, chain.Store(block0, &emptyCommitments, stateUpdate0, nil)) + chain.Close() chain = blockchain.New(testDB, &utils.Mainnet) + defer chain.Close() b, err := chain.Head() require.NoError(t, err) assert.Equal(t, block0, b) @@ -55,6 +58,7 @@ func TestHeight(t *testing.T) { gw := adaptfeeder.New(client) t.Run("return nil if blockchain is empty", func(t *testing.T) { chain := blockchain.New(pebble.NewMemTest(t), &utils.Sepolia) + defer chain.Close() _, err := chain.Height() assert.Error(t, err) }) @@ -68,16 +72,19 @@ func TestHeight(t *testing.T) { testDB := pebble.NewMemTest(t) chain := blockchain.New(testDB, &utils.Mainnet) assert.NoError(t, chain.Store(block0, &emptyCommitments, stateUpdate0, nil)) + chain.Close() chain = blockchain.New(testDB, &utils.Mainnet) height, err := chain.Height() require.NoError(t, err) assert.Equal(t, block0.Number, height) + chain.Close() }) } func TestBlockByNumberAndHash(t *testing.T) { chain := blockchain.New(pebble.NewMemTest(t), &utils.Sepolia) + defer chain.Close() t.Run("same block is returned for both GetBlockByNumber and GetBlockByHash", func(t *testing.T) { client := feeder.NewTestClient(t, &utils.Mainnet) gw := adaptfeeder.New(client) @@ -114,6 +121,7 @@ func TestVerifyBlock(t *testing.T) { require.NoError(t, err) chain := blockchain.New(pebble.NewMemTest(t), &utils.Mainnet) + defer chain.Close() t.Run("error if chain is empty and incoming block number is not 0", func(t *testing.T) { block := &core.Block{Header: &core.Header{Number: 10}} @@ -176,6 +184,7 @@ func TestSanityCheckNewHeight(t *testing.T) { require.NoError(t, err) chain := blockchain.New(pebble.NewMemTest(t), &utils.Mainnet) + defer chain.Close() client := feeder.NewTestClient(t, &utils.Mainnet) @@ -221,6 +230,7 @@ func TestStore(t *testing.T) { t.Run("add block to empty blockchain", func(t *testing.T) { chain := blockchain.New(pebble.NewMemTest(t), &utils.Mainnet) + defer chain.Close() require.NoError(t, chain.Store(block0, &emptyCommitments, stateUpdate0, nil)) headBlock, err := chain.Head() @@ -247,6 +257,7 @@ func TestStore(t *testing.T) { require.NoError(t, err) chain := blockchain.New(pebble.NewMemTest(t), &utils.Mainnet) + defer chain.Close() require.NoError(t, chain.Store(block0, &emptyCommitments, stateUpdate0, nil)) require.NoError(t, chain.Store(block1, &emptyCommitments, stateUpdate1, nil)) @@ -270,6 +281,7 @@ func TestStore(t *testing.T) { func TestBlockCommitments(t *testing.T) { chain := blockchain.New(pebble.NewMemTest(t), &utils.Mainnet) + defer chain.Close() client := feeder.NewTestClient(t, &utils.Mainnet) gw := adaptfeeder.New(client) @@ -295,6 +307,7 @@ func TestBlockCommitments(t *testing.T) { func TestTransactionAndReceipt(t *testing.T) { chain := blockchain.New(pebble.NewMemTest(t), &utils.Mainnet) + defer chain.Close() client := feeder.NewTestClient(t, &utils.Mainnet) gw := adaptfeeder.New(client) @@ -383,6 +396,7 @@ func TestTransactionAndReceipt(t *testing.T) { func TestState(t *testing.T) { testDB := pebble.NewMemTest(t) chain := blockchain.New(testDB, &utils.Mainnet) + defer chain.Close() client := feeder.NewTestClient(t, &utils.Mainnet) gw := adaptfeeder.New(client) @@ -446,6 +460,7 @@ func TestState(t *testing.T) { func TestEvents(t *testing.T) { testDB := pebble.NewMemTest(t) chain := blockchain.New(testDB, &utils.Goerli2) + defer chain.Close() client := feeder.NewTestClient(t, &utils.Goerli2) gw := adaptfeeder.New(client) @@ -565,6 +580,7 @@ func TestEvents(t *testing.T) { func TestRevert(t *testing.T) { testdb := pebble.NewMemTest(t) chain := blockchain.New(testdb, &utils.Mainnet) + defer chain.Close() client := feeder.NewTestClient(t, &utils.Mainnet) gw := adaptfeeder.New(client) @@ -653,6 +669,7 @@ func TestL1Update(t *testing.T) { got, err := chain.L1Head() require.NoError(t, err) assert.Equal(t, head, got) + chain.Close() }) } } @@ -660,6 +677,7 @@ func TestL1Update(t *testing.T) { func TestPending(t *testing.T) { testDB := pebble.NewMemTest(t) chain := blockchain.New(testDB, &utils.Mainnet) + defer chain.Close() client := feeder.NewTestClient(t, &utils.Mainnet) gw := adaptfeeder.New(client) @@ -801,6 +819,7 @@ func TestPending(t *testing.T) { func TestStorePendingIncludesNumber(t *testing.T) { network := utils.Mainnet chain := blockchain.New(pebble.NewMemTest(t), &network) + defer chain.Close() // Store pending genesis. require.NoError(t, chain.StorePending(&blockchain.Pending{ diff --git a/blockchain/snap_server_interface.go b/blockchain/snap_server_interface.go new file mode 100644 index 0000000000..e06e3b6c7a --- /dev/null +++ b/blockchain/snap_server_interface.go @@ -0,0 +1,164 @@ +package blockchain + +import ( + "errors" + "fmt" + + "github.com/NethermindEth/juno/core" + "github.com/NethermindEth/juno/core/felt" + "github.com/NethermindEth/juno/db" +) + +const MaxSnapshots = 128 + +type snapshotRecord struct { + stateRoot *felt.Felt + contractsRoot *felt.Felt + classRoot *felt.Felt + blockHash *felt.Felt + txn db.Transaction + closer func() error +} + +var ErrMissingSnapshot = errors.New("missing snapshot") + +func (b *Blockchain) GetStateForStateRoot(stateRoot *felt.Felt) (*core.State, error) { + snapshot, err := b.findSnapshotMatching(func(record *snapshotRecord) bool { + return record.stateRoot.Equal(stateRoot) + }) + if err != nil { + return nil, err + } + + s := core.NewState(snapshot.txn) + + return s, nil +} + +func (b *Blockchain) findSnapshotMatching(filter func(record *snapshotRecord) bool) (*snapshotRecord, error) { + var snapshot *snapshotRecord + for _, record := range b.snapshots { + if filter(record) { + snapshot = record + break + } + } + + if snapshot == nil { + return nil, ErrMissingSnapshot + } + + return snapshot, nil +} + +func (b *Blockchain) GetClasses(felts []*felt.Felt) ([]core.Class, error) { + classes := make([]core.Class, len(felts)) + err := b.database.View(func(txn db.Transaction) error { + state := core.NewState(txn) + for i, f := range felts { + d, err := state.Class(f) + if err != nil && !errors.Is(err, db.ErrKeyNotFound) { + return err + } else if errors.Is(err, db.ErrKeyNotFound) { + classes[i] = nil + } else { + classes[i] = d.Class + } + } + + return nil + }) + if err != nil { + return nil, err + } + + return classes, nil +} + +func (b *Blockchain) GetDClasses(felts []*felt.Felt) ([]*core.DeclaredClass, error) { + classes := make([]*core.DeclaredClass, len(felts)) + err := b.database.View(func(txn db.Transaction) error { + state := core.NewState(txn) + for i, f := range felts { + d, err := state.Class(f) + if err != nil && !errors.Is(err, db.ErrKeyNotFound) { + return err + } else if errors.Is(err, db.ErrKeyNotFound) { + classes[i] = nil + } else { + classes[i] = d + } + } + + return nil + }) + if err != nil { + return nil, err + } + + return classes, nil +} + +func (b *Blockchain) seedSnapshot() error { + headheader, err := b.HeadsHeader() + if err != nil { + return err + } + + stateR, srCloser, err := b.HeadState() + if err != nil { + return err + } + + defer func() { _ = srCloser() }() + + state := stateR.(*core.State) + contractsRoot, classRoot, err := state.StateAndClassRoot() + if err != nil { + return err + } + + stateRoot, err := state.Root() + if err != nil { + return err + } + + txn, closer, err := b.database.PersistedView() + if err != nil { + return err + } + + dbsnap := snapshotRecord{ + stateRoot: stateRoot, + contractsRoot: contractsRoot, + classRoot: classRoot, + blockHash: headheader.Hash, + txn: txn, + closer: closer, + } + + fmt.Printf("Snapshot %d %s %s\n", headheader.Number, headheader.GlobalStateRoot, stateRoot) + + // TODO: Reorgs + b.snapshots = append(b.snapshots, &dbsnap) + if len(b.snapshots) > MaxSnapshots { + toremove := b.snapshots[0] + err = toremove.closer() + if err != nil { + return err + } + + // TODO: I think internally, it keep the old array. + // maybe the append copy it to a new array, who knows... + b.snapshots = b.snapshots[1:] + } + + return nil +} + +func (b *Blockchain) Close() { + for _, snapshot := range b.snapshots { + // ignore the errors here as it's most likely called on shutdown + _ = snapshot.closer() + } +} diff --git a/core/contract.go b/core/contract.go index 2af1fd8c4c..69f3e23e5f 100644 --- a/core/contract.go +++ b/core/contract.go @@ -10,7 +10,10 @@ import ( ) // contract storage has fixed height at 251 -const ContractStorageTrieHeight = 251 +const ( + GlobalTrieHeight = 251 + ContractStorageTrieHeight = 251 +) var ( ErrContractNotDeployed = errors.New("contract not deployed") @@ -168,6 +171,32 @@ func (c *ContractUpdater) UpdateStorage(diff map[felt.Felt]*felt.Felt, cb OnValu return cStorage.Commit() } +// UpdateStorage applies a change-set to the contract storage. +func (c *ContractUpdater) UpdateStorageKV(diff []FeltKV, cb OnValueChanged) error { + cStorage, err := storage(c.Address, c.txn) + if err != nil { + return err + } + + // apply the diff + for _, kv := range diff { + key := kv.Key + value := kv.Value + oldValue, pErr := cStorage.Put(key, value) + if pErr != nil { + return pErr + } + + if oldValue != nil { + if err = cb(key, oldValue); err != nil { + return err + } + } + } + + return cStorage.Commit() +} + func ContractStorage(addr, key *felt.Felt, txn db.Transaction) (*felt.Felt, error) { cStorage, err := storage(addr, txn) if err != nil { diff --git a/core/state.go b/core/state.go index e1dc7bf292..340630b8c3 100644 --- a/core/state.go +++ b/core/state.go @@ -123,6 +123,38 @@ func (s *State) Root() (*felt.Felt, error) { return crypto.PoseidonArray(stateVersion, storageRoot, classesRoot), nil } +func (s *State) StateAndClassRoot() (*felt.Felt, *felt.Felt, error) { + var storageRoot, classesRoot *felt.Felt + + sStorage, closer, err := s.storage() + if err != nil { + return nil, nil, err + } + + if storageRoot, err = sStorage.Root(); err != nil { + return nil, nil, err + } + + if err = closer(); err != nil { + return nil, nil, err + } + + classes, closer, err := s.classesTrie() + if err != nil { + return nil, nil, err + } + + if classesRoot, err = classes.Root(); err != nil { + return nil, nil, err + } + + if err = closer(); err != nil { + return nil, nil, err + } + + return storageRoot, classesRoot, nil +} + // storage returns a [core.Trie] that represents the Starknet global state in the given Txn context. func (s *State) storage() (*trie.Trie, func() error, error) { return s.globalTrie(db.StateTrie, trie.NewTriePedersen) @@ -132,6 +164,19 @@ func (s *State) classesTrie() (*trie.Trie, func() error, error) { return s.globalTrie(db.ClassesTrie, trie.NewTriePoseidon) } +// storage returns a [core.Trie] that represents the Starknet global state in the given Txn context. +func (s *State) StorageTrie() (*trie.Trie, func() error, error) { + return s.storage() +} + +func (s *State) ClassTrie() (*trie.Trie, func() error, error) { + return s.classesTrie() +} + +func (s *State) StorageTrieForAddr(addr *felt.Felt) (*trie.Trie, error) { + return storage(addr, s.txn) +} + func (s *State) globalTrie(bucket db.Bucket, newTrie trie.NewTrieFunc) (*trie.Trie, func() error, error) { dbPrefix := bucket.Key() tTxn := trie.NewStorage(s.txn, dbPrefix) @@ -203,14 +248,22 @@ func (s *State) Update(blockNumber uint64, update *StateUpdate, declaredClasses return err } + if err = s.UpdateNoVerify(blockNumber, update.StateDiff, declaredClasses); err != nil { + return err + } + + return s.verifyStateUpdateRoot(update.NewRoot) +} + +func (s *State) UpdateNoVerify(blockNumber uint64, update *StateDiff, declaredClasses map[felt.Felt]Class) error { // register declared classes mentioned in stateDiff.deployedContracts and stateDiff.declaredClasses for cHash, class := range declaredClasses { - if err = s.putClass(&cHash, class, blockNumber); err != nil { + if err := s.putClass(&cHash, class, blockNumber); err != nil { return err } } - if err = s.updateDeclaredClassesTrie(update.StateDiff.DeclaredV1Classes, declaredClasses); err != nil { + if err := s.updateDeclaredClassesTrie(update.DeclaredV1Classes, declaredClasses); err != nil { return err } @@ -220,13 +273,13 @@ func (s *State) Update(blockNumber uint64, update *StateUpdate, declaredClasses } // register deployed contracts - for addr, classHash := range update.StateDiff.DeployedContracts { + for addr, classHash := range update.DeployedContracts { if err = s.putNewContract(stateTrie, &addr, classHash, blockNumber); err != nil { return err } } - if err = s.updateContracts(stateTrie, blockNumber, update.StateDiff, true); err != nil { + if err = s.updateContracts(stateTrie, blockNumber, update, true); err != nil { return err } @@ -234,7 +287,7 @@ func (s *State) Update(blockNumber uint64, update *StateUpdate, declaredClasses return err } - return s.verifyStateUpdateRoot(update.NewRoot) + return nil } var ( @@ -278,6 +331,77 @@ func (s *State) updateContracts(stateTrie *trie.Trie, blockNumber uint64, diff * return s.updateContractStorages(stateTrie, diff.StorageDiffs, blockNumber, logChanges) } +func (s *State) UpdateContractNoLog(paths, nonces, classes []*felt.Felt) error { + stateTrie, storageCloser, err := s.storage() + if err != nil { + return err + } + + for i, path := range paths { + nonce := nonces[i] + class := classes[i] + + contract, err := NewContractUpdater(path, s.txn) + if err != nil && !errors.Is(err, ErrContractNotDeployed) { + return err + } + if errors.Is(err, ErrContractNotDeployed) { + contract, err = DeployContract(path, class, s.txn) + if err != nil { + return err + } + } + + err = contract.Replace(class) + if err != nil { + return err + } + + err = contract.UpdateNonce(nonce) + if err != nil { + return err + } + + err = s.updateContractCommitment(stateTrie, contract) + if err != nil { + return err + } + } + + if err = storageCloser(); err != nil { + return err + } + return nil +} + +func (s *State) UpdateContractStorages(storages map[felt.Felt]map[felt.Felt]*felt.Felt) error { + stateTrie, storageCloser, err := s.storage() + if err != nil { + return err + } + + err = s.updateContractStorages(stateTrie, storages, 0, false) + if err != nil { + return err + } + + return storageCloser() +} + +func (s *State) UpdateContractStoragesKV(storages map[felt.Felt][]FeltKV) error { + stateTrie, storageCloser, err := s.storage() + if err != nil { + return err + } + + err = s.updateContractStoragesKV(stateTrie, storages, 0, false) + if err != nil { + return err + } + + return storageCloser() +} + // replaceContract replaces the class that a contract at a given address instantiates func (s *State) replaceContract(stateTrie *trie.Trie, addr, classHash *felt.Felt) (*felt.Felt, error) { contract, err := NewContractUpdater(addr, s.txn) @@ -341,6 +465,64 @@ func (s *State) Class(classHash *felt.Felt) (*DeclaredClass, error) { return &class, nil } +// StartsWith checks if the byte array 'a' starts with the byte array 'b' +func StartsWith(a, b []byte) bool { + // If b is longer than a, it can't be a prefix + if len(b) > len(a) { + return false + } + + // Compare the elements of a and b + for i := 0; i < len(b); i++ { + if a[i] != b[i] { + return false + } + } + + return true +} + +func (s *State) PrintIt() (*DeclaredClass, error) { + classKey := db.Class.Key(nil) + + it, err := s.txn.NewIterator() + if err != nil { + return nil, err + } + + it.Seek(classKey) + idx := 0 + printed := 0 + for it.Valid() && StartsWith(it.Key(), classKey) { + value, err := it.Value() + if err != nil { + return nil, err + } + + var class DeclaredClass + err = encoder.Unmarshal(value, &class) + if err != nil { + return nil, err + } + + if class.Class.Version() == 0 { + fmt.Printf("%d %x %d %d\n", idx, it.Key(), class.Class.Version(), len(value)) + if class.Class.Version() == 0 && len(value) < 20000 { + fmt.Printf("%x\n", value) + printed++ + if printed >= 2 { + return nil, nil + } + } + } + + idx++ + it.Next() + } + + return nil, nil +} + func (s *State) updateStorageBuffered(contractAddr *felt.Felt, updateDiff map[felt.Felt]*felt.Felt, blockNumber uint64, logChanges bool) ( *db.BufferedTransaction, error, ) { @@ -366,6 +548,106 @@ func (s *State) updateStorageBuffered(contractAddr *felt.Felt, updateDiff map[fe return bufferedTxn, nil } +func (s *State) updateStorageBufferedKV(contractAddr *felt.Felt, updateDiff []FeltKV, blockNumber uint64, logChanges bool) ( + *db.BufferedTransaction, error, +) { + // to avoid multiple transactions writing to s.txn, create a buffered transaction and use that in the worker goroutine + bufferedTxn := db.NewBufferedTransaction(s.txn) + bufferedState := NewState(bufferedTxn) + bufferedContract, err := NewContractUpdater(contractAddr, bufferedTxn) + if err != nil { + return nil, err + } + + onValueChanged := func(location, oldValue *felt.Felt) error { + if logChanges { + return bufferedState.LogContractStorage(contractAddr, location, oldValue, blockNumber) + } + return nil + } + + if err = bufferedContract.UpdateStorageKV(updateDiff, onValueChanged); err != nil { + return nil, err + } + + return bufferedTxn, nil +} + +type FeltKV struct { + Key *felt.Felt + Value *felt.Felt +} + +// updateContractStorage applies the diff set to the Trie of the +// contract at the given address in the given Txn context. +func (s *State) updateContractStoragesKV(stateTrie *trie.Trie, diffs map[felt.Felt][]FeltKV, + blockNumber uint64, logChanges bool, +) error { + // make sure all noClassContracts are deployed + for addr := range diffs { + if _, ok := noClassContracts[addr]; !ok { + continue + } + + _, err := NewContractUpdater(&addr, s.txn) + if err != nil { + if !errors.Is(err, ErrContractNotDeployed) { + return err + } + // Deploy noClassContract + err = s.putNewContract(stateTrie, &addr, noClassContractsClassHash, blockNumber) + if err != nil { + return err + } + } + } + + // sort the contracts in decending diff size order + // so we start with the heaviest update first + keys := make([]felt.Felt, 0, len(diffs)) + for key := range diffs { + keys = append(keys, key) + } + sort.SliceStable(keys, func(i, j int) bool { + return len(diffs[keys[i]]) > len(diffs[keys[j]]) + }) + + // update per-contract storage Tries concurrently + contractUpdaters := pool.NewWithResults[*db.BufferedTransaction]().WithErrors().WithMaxGoroutines(runtime.GOMAXPROCS(0)) + for _, key := range keys { + conractAddr := key + updateDiff := diffs[conractAddr] + contractUpdaters.Go(func() (*db.BufferedTransaction, error) { + return s.updateStorageBufferedKV(&conractAddr, updateDiff, blockNumber, logChanges) + }) + } + + bufferedTxns, err := contractUpdaters.Wait() + if err != nil { + return err + } + + // flush buffered txns + for _, bufferedTxn := range bufferedTxns { + if err = bufferedTxn.Flush(); err != nil { + return err + } + } + + for addr := range diffs { + contract, err := NewContractUpdater(&addr, s.txn) + if err != nil { + return err + } + + if err = s.updateContractCommitment(stateTrie, contract); err != nil { + return err + } + } + + return nil +} + // updateContractStorage applies the diff set to the Trie of the // contract at the given address in the given Txn context. func (s *State) updateContractStorages(stateTrie *trie.Trie, diffs map[felt.Felt]map[felt.Felt]*felt.Felt, diff --git a/core/trie/key.go b/core/trie/key.go index 7f0e6af609..a451cb25ee 100644 --- a/core/trie/key.go +++ b/core/trie/key.go @@ -153,3 +153,61 @@ func (k *Key) RemoveLastBit() { inUseBytes[0] = (inUseBytes[0] << unusedBitsCount) >> unusedBitsCount } } + +// CmpAligned is Cmp as if the value is bigendian bytes of key of the same length +func (k Key) CmpAligned(other *Key) int { + // No its not aligned, so need to convert to bigint then left shift it so that the MSB is of the same index + height := k.len + if other.len > height { + height = other.len + } + + b1i := k.alignedBitInt(height) + b2i := other.alignedBitInt(height) + return b1i.Cmp(b2i) +} + +func (k Key) alignedBitInt(height uint8) *big.Int { + theint := &big.Int{} + theint = theint.SetBytes(k.bitset[:]) + if k.len < height { + theint = theint.Lsh(theint, uint(height-k.len)) + } + + return theint +} + +func (k *Key) AppendBitMut(flag bool) { + const LSB = uint8(0x1) + bit := k.len + byteIdx := bit / 8 + byteAtIdx := k.bitset[len(k.bitset)-int(byteIdx)-1] + bitIdx := bit % 8 + + // I'm sure someone will make this nicer + if flag { + byteAtIdx |= LSB << bitIdx + } else { + byteAtIdx &= ^(LSB << bitIdx) + } + + k.len++ + k.bitset[len(k.bitset)-int(byteIdx)-1] = byteAtIdx +} + +func (k Key) Append(otherKey *Key) Key { + result := NewKey(otherKey.len, otherKey.bitset[:]) + + // I'm sure someone will make this faster + for i := uint8(0); i < k.len; i++ { + result.AppendBitMut(k.Test(i)) + } + + return result +} + +func (k Key) AppendBit(flag bool) Key { + result := NewKey(0, []byte{}) + result.AppendBitMut(flag) + return k.Append(&result) +} diff --git a/core/trie/key_test.go b/core/trie/key_test.go index 8d56a31e0c..a16299a019 100644 --- a/core/trie/key_test.go +++ b/core/trie/key_test.go @@ -2,6 +2,7 @@ package trie_test import ( "bytes" + "fmt" "testing" "github.com/NethermindEth/juno/core/felt" @@ -153,3 +154,120 @@ func TestTruncate(t *testing.T) { }) } } + +func Test_cmp(t *testing.T) { + tests := []struct { + n1 int + n2 int + isHigher bool + }{ + { + n1: 10, + n2: 0, + isHigher: true, + }, + { + n1: 5, + n2: 0, + isHigher: true, + }, + { + n1: 5, + n2: 4, + isHigher: true, + }, + { + n1: 5, + n2: 5, + isHigher: false, + }, + { + n1: 4, + n2: 5, + isHigher: false, + }, + { + n1: 0, + n2: 5, + isHigher: false, + }, + { + n1: 300, + n2: 1, + isHigher: true, + }, + { + n1: 1, + n2: 300, + isHigher: false, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%d %d %v", test.n1, test.n2, test.isHigher), func(t *testing.T) { + k1 := numToKey(test.n1) + k2 := numToKey(test.n2) + + assert.Equal(t, + k1.CmpAligned(&k2) > 0, + test.isHigher) + }) + } +} + +func numToKey(num int) trie.Key { + return trie.NewKey(8, []byte{byte(num)}) +} + +func TestKeyAppend(t *testing.T) { + tests := map[string]struct { + Key1 trie.Key + Key2 trie.Key + ExpectedKey trie.Key + }{ + "no append": { + Key1: trie.NewKey(1, []byte{0x01}), + Key2: trie.NewKey(0, []byte{0x00}), + ExpectedKey: trie.NewKey(1, []byte{0x01}), + }, + "from zero append": { + Key1: trie.NewKey(0, []byte{0x00}), + Key2: trie.NewKey(1, []byte{0x01}), + ExpectedKey: trie.NewKey(1, []byte{0x01}), + }, + "append shift": { + Key1: trie.NewKey(1, []byte{0x01}), + Key2: trie.NewKey(7, []byte{0x00}), + ExpectedKey: trie.NewKey(8, []byte{0x80}), + }, + "append to a new byte": { + Key1: trie.NewKey(8, []byte{0xff}), + Key2: trie.NewKey(1, []byte{0x01}), + ExpectedKey: trie.NewKey(9, []byte{0x01, 0xff}), + }, + "append multi byte": { + Key1: trie.NewKey(11, []byte{0x00, 0xff}), // 000 1111 1111 + Key2: trie.NewKey(12, []byte{0x00, 0xff}), // 0000 1111 1111 + ExpectedKey: trie.NewKey(23, []byte{0x0f, 0xf0, 0xff}), // 000 1111 1111 0000 1111 1111 + }, + } + + for desc, test := range tests { + t.Run(desc, func(t *testing.T) { + appended := test.Key1.Append(&test.Key2) + assert.Equal(t, test.ExpectedKey, appended) + }) + } +} + +func TestKeyAppendBit(t *testing.T) { + k1 := trie.NewKey(1, []byte{0x01}) + k2 := k1.AppendBit(true) + expected := trie.NewKey(2, []byte{0x03}) + assert.Equal(t, k2, expected) + + k1 = trie.NewKey(1, []byte{0x00}) + k2 = k1.AppendBit(true) + expected = trie.NewKey(2, []byte{0x01}) + assert.Equal(t, k2, expected) +} diff --git a/core/trie/proof.go b/core/trie/proof.go index f8f0e325ad..de8431d6d6 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -515,3 +515,11 @@ func BuildTrie(leftProofPath, rightProofPath []StorageNode, keys, values []*felt } return tempTrie, nil } + +func (t *Trie) RangeProof(startPath, endPath *felt.Felt) ([]ProofNode, error) { + // TODO: Do this properly + const trieHeight = 251 + bts := startPath.Bytes() + k := NewKey(trieHeight, bts[:]) + return GetProof(&k, t) +} diff --git a/core/trie/snap_support.go b/core/trie/snap_support.go new file mode 100644 index 0000000000..84f3f1e2c4 --- /dev/null +++ b/core/trie/snap_support.go @@ -0,0 +1,162 @@ +package trie + +import ( + "github.com/NethermindEth/juno/core/felt" +) + +func (t *Trie) IterateAndGenerateProof(startValue *felt.Felt, consumer func(key, value *felt.Felt) (bool, error), +) ([]ProofNode, bool, error) { + var lastKey *felt.Felt + + finished, err := t.Iterate(startValue, func(key, value *felt.Felt) (bool, error) { + lastKey = key + + return consumer(key, value) + }) + if err != nil { + return nil, finished, err + } + + proofset := map[felt.Felt]ProofNode{} + + // If start value is null && finished, you dont need to provide any proof at all + if !finished || startValue != nil { + feltBts := startValue.Bytes() + startKey := NewKey(t.height, feltBts[:]) + // Yes, the left proof is actually for the start query, not the actual leaf. Very confusing, yea I know. Need to + // actually check that the server did not skip leafs. + leftProof, err := GetProof(&startKey, t) + if err != nil { + return nil, finished, err + } + for _, proof := range leftProof { + // Well.. using the trie hash here is kinda slow... but I just need it to work right now. + proofset[*proof.Hash(t.hash)] = proof + } + } + + if !finished && lastKey != nil { + feltBts := lastKey.Bytes() + lastKey := NewKey(t.height, feltBts[:]) + rightProof, err := GetProof(&lastKey, t) + if err != nil { + return nil, finished, err + } + + for _, proof := range rightProof { + proofset[*proof.Hash(t.hash)] = proof + } + } + + proofs := make([]ProofNode, 0, len(proofset)) + for _, node := range proofset { + proofs = append(proofs, node) + } + + return proofs, finished, nil +} + +func VerifyRange(root, startKey *felt.Felt, keys, values []*felt.Felt, proofs []ProofNode, hash hashFunc, + treeHeight uint8, +) (hasMore, valid bool, oerr error) { + proofMap := map[felt.Felt]ProofNode{} + for _, proof := range proofs { + proofHash := proof.Hash(hash) + proofMap[*proofHash] = proof + } + + if len(proofMap) == 0 && startKey == nil { + // Special case where the whole trie is sent in one go. + // We just need to completely reconstruct the trie. + + tempTrie, err := newTrie(newMemStorage(), treeHeight, hash) + if err != nil { + return false, false, err + } + + for i, key := range keys { + _, err = tempTrie.Put(key, values[i]) + if err != nil { + return false, false, err + } + } + + recalculatedRoot, err := tempTrie.Root() + if err != nil { + return false, false, err + } + + if !root.Equal(recalculatedRoot) { + return false, false, nil + } + + return false, true, nil + } + + if _, ok := proofMap[*root]; !ok { + // Verification failure, root not included in proof. + return false, false, nil + } + + proofKeys := map[felt.Felt]Key{} + err := buildKeys(NewKey(0, []byte{}), root, proofMap, proofKeys, 0) + if err != nil { + return false, false, err + } + + // TODO: Verify here proof here + + hasMoreKeyCheck := startKey + if len(keys) > 0 { + hasMoreKeyCheck = keys[len(keys)-1] + } + + feltBytes := hasMoreKeyCheck.Bytes() + hasMoreKeyCheckKey := NewKey(treeHeight, feltBytes[:]) + + // does this actually work on all case? + hasMore = false + for _, key := range proofKeys { + comparison := key.CmpAligned(&hasMoreKeyCheckKey) + if comparison > 0 { + hasMore = true + } + } + + return hasMore, true, nil +} + +func buildKeys(currentKey Key, currentNode *felt.Felt, proofMap map[felt.Felt]ProofNode, keys map[felt.Felt]Key, depth int) error { + keys[*currentNode] = currentKey + proofNode, ok := proofMap[*currentNode] + if !ok { + return nil + } + + if proofNode.Edge != nil { + chKey := currentKey.Append(proofNode.Edge.Path) + ch := proofNode.Edge.Child + err := buildKeys(chKey, ch, proofMap, keys, depth+1) + if err != nil { + return err + } + } else { + binary := proofNode.Binary + + chKey := currentKey.AppendBit(false) + ch := binary.LeftHash + err := buildKeys(chKey, ch, proofMap, keys, depth+1) + if err != nil { + return err + } + + chKey = currentKey.AppendBit(true) + ch = binary.RightHash + err = buildKeys(chKey, ch, proofMap, keys, depth+1) + if err != nil { + return err + } + } + + return nil +} diff --git a/core/trie/snap_support_test.go b/core/trie/snap_support_test.go new file mode 100644 index 0000000000..9a3501bb6d --- /dev/null +++ b/core/trie/snap_support_test.go @@ -0,0 +1,278 @@ +package trie_test + +import ( + "testing" + + "github.com/NethermindEth/juno/core/crypto" + "github.com/NethermindEth/juno/core/felt" + "github.com/NethermindEth/juno/core/trie" + "github.com/NethermindEth/juno/db" + "github.com/stretchr/testify/assert" +) + +const trieHeight = 251 + +func TestRangeAndVerify(t *testing.T) { + scenarios := []struct { + name string + startQuery *felt.Felt + limitQuery *felt.Felt + maxNode int + expectedKeyCount int + hasMore bool + noProof bool + }{ + { + name: "all", + startQuery: numToFelt(0), + expectedKeyCount: 10, + hasMore: false, + }, + { + name: "all without start query", + expectedKeyCount: 10, + hasMore: false, + noProof: true, + }, + { + name: "start in the middle", + startQuery: numToFelt(500), + expectedKeyCount: 5, + hasMore: false, + }, + { + name: "start with limit query", + startQuery: numToFelt(100), + limitQuery: numToFelt(500), + expectedKeyCount: 5, + hasMore: true, + }, + { + name: "start with limit query and node count limit", + startQuery: numToFelt(100), + limitQuery: numToFelt(500), + maxNode: 3, + expectedKeyCount: 3, + hasMore: true, + }, + { + name: "finished before limit query", + startQuery: numToFelt(100), + limitQuery: numToFelt(20000), + expectedKeyCount: 9, + hasMore: false, + }, + { + name: "last one right after limit query", + startQuery: numToFelt(100), + limitQuery: numToFelt(900), + expectedKeyCount: 9, + hasMore: false, + }, + { + name: "two leaf after limit query, last leaf skipped", + startQuery: numToFelt(100), + limitQuery: numToFelt(800), + expectedKeyCount: 8, + hasMore: true, + }, + { + name: "no node between start and limit", + startQuery: numToFelt(450), + limitQuery: numToFelt(451), + expectedKeyCount: 1, + hasMore: true, + }, + { + name: "start query after last node", + startQuery: numToFelt(10000), + expectedKeyCount: 0, + hasMore: false, + }, + } + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + storage := trie.NewStorage(db.NewMemTransaction(), []byte{}) + testTrie, err := trie.NewTriePedersen(storage, 251) + assert.NoError(t, err) + + for i := 0; i < 10; i++ { + _, err = testTrie.Put(numToFelt(i*100+1), numToFelt(i*100+2)) + assert.NoError(t, err) + } + + expectedRoot, err := testTrie.Root() + assert.NoError(t, err) + + startQuery := scenario.startQuery + var keys []*felt.Felt + var values []*felt.Felt + + proofs, _, err := testTrie.IterateAndGenerateProof(startQuery, func(key, value *felt.Felt) (bool, error) { + keys = append(keys, key) + values = append(values, value) + if scenario.maxNode > 0 && len(keys) >= scenario.maxNode { + return false, nil + } + if scenario.limitQuery != nil && key.Cmp(scenario.limitQuery) > 0 { + // Last (one after limit) is included. + return false, nil + } + return true, nil + }) + assert.NoError(t, err) + + assert.Equal(t, scenario.expectedKeyCount, len(keys)) + if scenario.noProof { + assert.Empty(t, proofs) + } + + hasMore, valid, err := trie.VerifyRange(expectedRoot, startQuery, keys, values, proofs, crypto.Pedersen, trieHeight) + assert.NoError(t, err) + assert.True(t, valid) + + assert.Equal(t, scenario.hasMore, hasMore) + }) + } +} + +func TestRangeAndVerifyReject(t *testing.T) { + scenarios := []struct { + name string + startQuery *felt.Felt + skip bool + maxNode int + mutator func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) + }{ + { + name: "missing proofs", + startQuery: numToFelt(500), + mutator: func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) { + return keys, values, nil + }, + }, + { + name: "missing leaf when all node requested", + mutator: func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) { + return keys[1:], values[1:], nil + }, + }, + { + skip: true, + name: "missing part of keys at start", + startQuery: numToFelt(500), + mutator: func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) { + return keys[1:], values[1:], proofs + }, + }, + { + skip: true, + name: "missing part of keys at end", + startQuery: numToFelt(500), + mutator: func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) { + return keys[:len(keys)-1], values[:len(keys)-1], proofs + }, + }, + { + skip: true, + name: "missing part of keys in the middle", + startQuery: numToFelt(500), + mutator: func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) { + newkeys := []*felt.Felt{} + newvalues := []*felt.Felt{} + newkeys = append(newkeys, keys[:2]...) + newvalues = append(newvalues, values[:2]...) + newkeys = append(newkeys, keys[3:]...) + newvalues = append(newvalues, values[3:]...) + + return newkeys, newvalues, proofs + }, + }, + { + name: "missing part of keys in the middle when whole trie is sent", + mutator: func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) { + newkeys := []*felt.Felt{} + newvalues := []*felt.Felt{} + newkeys = append(newkeys, keys[:2]...) + newvalues = append(newvalues, values[:2]...) + newkeys = append(newkeys, keys[3:]...) + newvalues = append(newvalues, values[3:]...) + + return newkeys, newvalues, proofs + }, + }, + { + name: "value changed", + mutator: func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) { + values[3] = numToFelt(10000) + return keys, values, proofs + }, + }, + { + skip: true, + startQuery: numToFelt(500), + name: "value changed when whole trie is sent", + mutator: func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) { + values[3] = numToFelt(10000) + return keys, values, proofs + }, + }, + { + name: "key changed", + mutator: func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) { + keys[3] = numToFelt(10000) + return keys, values, proofs + }, + }, + { + skip: true, + startQuery: numToFelt(500), + name: "key changed when whole trie is sent", + mutator: func(keys, values []*felt.Felt, proofs []trie.ProofNode) ([]*felt.Felt, []*felt.Felt, []trie.ProofNode) { + keys[3] = numToFelt(10000) + return keys, values, proofs + }, + }, + } + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + if scenario.skip { + t.Skip() + } + + storage := trie.NewStorage(db.NewMemTransaction(), []byte{}) + testTrie, err := trie.NewTriePedersen(storage, 251) + assert.NoError(t, err) + + for i := 0; i < 10; i++ { + _, err = testTrie.Put(numToFelt(i*100+1), numToFelt(i*100+2)) + assert.NoError(t, err) + } + + expectedRoot, err := testTrie.Root() + assert.NoError(t, err) + + startQuery := scenario.startQuery + var keys []*felt.Felt + var values []*felt.Felt + + proofs, _, err := testTrie.IterateAndGenerateProof(startQuery, func(key, value *felt.Felt) (bool, error) { + keys = append(keys, key) + values = append(values, value) + if scenario.maxNode > 0 && len(keys) >= scenario.maxNode { + return false, nil + } + return true, nil + }) + assert.NoError(t, err) + + keys, values, proofs = scenario.mutator(keys, values, proofs) + + _, valid, err := trie.VerifyRange(expectedRoot, startQuery, keys, values, proofs, crypto.Pedersen, trieHeight) + assert.NoError(t, err) + assert.False(t, valid) + }) + } +} diff --git a/core/trie/trie.go b/core/trie/trie.go index c03357d3af..5ab0f2c481 100644 --- a/core/trie/trie.go +++ b/core/trie/trie.go @@ -11,10 +11,18 @@ import ( "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/juno/db" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" ) type hashFunc func(*felt.Felt, *felt.Felt) *felt.Felt +type IterableStorage interface { + IterateLeaf(startKey *Key, consumer func(key, value *felt.Felt) (bool, error)) (bool, error) +} + +type HashFunc func(*felt.Felt, *felt.Felt) *felt.Felt + // Trie is a dense Merkle Patricia Trie (i.e., all internal nodes have two children). // // This implementation allows for a "flat" storage by keying nodes on their path rather than @@ -43,6 +51,10 @@ type Trie struct { rootKeyIsDirty bool } +func (t *Trie) GetRootKey() *Key { + return t.rootKey +} + type NewTrieFunc func(*Storage, uint8) (*Trie, error) func NewTriePedersen(storage *Storage, height uint8) (*Trie, error) { @@ -53,6 +65,10 @@ func NewTriePoseidon(storage *Storage, height uint8) (*Trie, error) { return newTrie(storage, height, crypto.Poseidon) } +func NewTrie(storage *Storage, height uint8, hash HashFunc) (*Trie, error) { + return newTrie(storage, height, hashFunc(hash)) +} + func newTrie(storage *Storage, height uint8, hash hashFunc) (*Trie, error) { if height > felt.Bits { return nil, fmt.Errorf("max trie height is %d, got: %d", felt.Bits, height) @@ -321,8 +337,14 @@ func (t *Trie) insertOrUpdateValue(nodeKey *Key, node *Node, nodes []StorageNode return nil } +var triePut = promauto.NewCounter(prometheus.CounterOpts{ + Name: "juno_trie_put", + Help: "trie put", +}) + // Put updates the corresponding `value` for a `key` func (t *Trie) Put(key, value *felt.Felt) (*felt.Felt, error) { + triePut.Inc() if key.Cmp(t.maxKey) > 0 { return nil, fmt.Errorf("key %s exceeds trie height %d", key, t.height) } @@ -715,3 +737,50 @@ func (t *Trie) dump(level int, parentP *Key) { storage: t.storage, }).dump(level+1, t.rootKey) } + +// Iterate the trie from startValue in ascending order until the consumer returned false or an error occur or end of +// trie was reached. Return true if end of trie is reached. +// TODO: its much more efficient to iterate from the txn level. But even without that, if the leaf are ordered correctly, +// block cache should have a pretty good hit rate. +func (t *Trie) Iterate(startValue *felt.Felt, consumer func(key, value *felt.Felt) (bool, error)) (bool, error) { + if startValue == nil { + startValue = &felt.Zero + } + startKey := t.feltToKey(startValue) + + return t.doIterate(&startKey, t.rootKey, consumer) +} + +// doIterate returns false if the end of the trie is reached, true otherwise +func (t *Trie) doIterate(startKey, key *Key, consumer func(key, value *felt.Felt) (bool, error)) (bool, error) { + if key == nil { + return false, nil + } + + node, err := t.storage.Get(key) + if err != nil { + return false, err + } + + if key.Len() == t.height { + if startKey.CmpAligned(key) > 0 { + return true, nil + } + keyAsFelt := key.Felt() + return consumer(&keyAsFelt, node.Value) + } + + // If the startKey is higher than the right node, no point in going to left at all + if startKey.CmpAligned(node.Right) < 0 { + next, err := t.doIterate(startKey, node.Left, consumer) + if err != nil { + return false, err + } + + if !next { + return false, nil + } + } + + return t.doIterate(startKey, node.Right, consumer) +} diff --git a/core/trie/trie_test.go b/core/trie/trie_test.go index fb5460739d..6ed50be639 100644 --- a/core/trie/trie_test.go +++ b/core/trie/trie_test.go @@ -1,6 +1,7 @@ package trie_test import ( + "math/big" "strconv" "testing" @@ -375,3 +376,154 @@ func BenchmarkTriePut(b *testing.B) { return t.Commit() })) } + +func TestTrieIterate(t *testing.T) { + t.Run("iterate standard", func(t *testing.T) { + require.NoError(t, trie.RunOnTempTriePedersen(251, func(tempTrie *trie.Trie) error { + expectedKeys := []*felt.Felt{} + expectedValues := []*felt.Felt{} + for i := 0; i < 2; i++ { + key := new(felt.Felt).SetUint64(uint64(i*10 + 1)) + val := new(felt.Felt).SetUint64(uint64(i + 1)) + + expectedKeys = append(expectedKeys, key) + expectedValues = append(expectedValues, val) + + _, err := tempTrie.Put(key, val) + require.NoError(t, err) + } + + startAddr := new(felt.Felt).SetUint64(0) + keys := []*felt.Felt{} + values := []*felt.Felt{} + finished, err := tempTrie.Iterate(startAddr, func(key, value *felt.Felt) (bool, error) { + keys = append(keys, key) + values = append(values, value) + return true, nil + }) + + assert.Nil(t, err) + assert.True(t, finished) + + assert.Equal(t, expectedKeys, keys) + assert.Equal(t, expectedValues, values) + + return nil + })) + }) +} + +func numToFelt(num int) *felt.Felt { + return numToFeltBigInt(big.NewInt(int64(num))) +} + +func numToFeltBigInt(num *big.Int) *felt.Felt { + f := felt.Zero + return f.SetBigInt(num) +} + +func TestTrie_Iterate(t *testing.T) { + tr, err := trie.NewTriePedersen(trie.NewStorage(db.NewMemTransaction(), []byte{1}), 251) + assert.Nil(t, err) + + for i := 0; i < 10; i++ { + _, err = tr.Put(numToFelt(i*10), numToFelt(i+10)) + assert.Nil(t, err) + } + err = tr.Commit() + assert.Nil(t, err) + + tests := []struct { + name string + startKey *felt.Felt + count int + expectedKeys []*felt.Felt + expectedValues []*felt.Felt + }{ + { + name: "all", + startKey: numToFelt(0), + count: 10, + expectedKeys: []*felt.Felt{ + numToFelt(0), + numToFelt(10), + numToFelt(20), + numToFelt(30), + numToFelt(40), + numToFelt(50), + numToFelt(60), + numToFelt(70), + numToFelt(80), + numToFelt(90), + }, + expectedValues: []*felt.Felt{ + numToFelt(10), + numToFelt(11), + numToFelt(12), + numToFelt(13), + numToFelt(14), + numToFelt(15), + numToFelt(16), + numToFelt(17), + numToFelt(18), + numToFelt(19), + }, + }, + { + name: "limited", + startKey: numToFelt(0), + count: 2, + expectedKeys: []*felt.Felt{ + numToFelt(0), + numToFelt(10), + }, + expectedValues: []*felt.Felt{ + numToFelt(10), + numToFelt(11), + }, + }, + { + name: "limited with offset", + startKey: numToFelt(30), + count: 2, + expectedKeys: []*felt.Felt{ + numToFelt(30), + numToFelt(40), + }, + expectedValues: []*felt.Felt{ + numToFelt(13), + numToFelt(14), + }, + }, + { + name: "limited with offset that does not match a leaf", + startKey: numToFelt(25), + count: 2, + expectedKeys: []*felt.Felt{ + numToFelt(30), + numToFelt(40), + }, + expectedValues: []*felt.Felt{ + numToFelt(13), + numToFelt(14), + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + keys := make([]*felt.Felt, 0) + values := make([]*felt.Felt, 0) + + _, err := tr.Iterate(test.startKey, func(key *felt.Felt, value *felt.Felt) (bool, error) { + keys = append(keys, key) + values = append(values, value) + return len(keys) < test.count, nil + }) + assert.Nil(t, err) + + assert.Equal(t, test.expectedKeys, keys) + assert.Equal(t, test.expectedValues, values) + }) + } +} diff --git a/db/db.go b/db/db.go index 3fc16526d1..4e60e7b339 100644 --- a/db/db.go +++ b/db/db.go @@ -33,6 +33,8 @@ type DB interface { // WithListener registers an EventListener WithListener(listener EventListener) DB + + PersistedView() (Transaction, func() error, error) } // Iterator is an iterator over a DB's key/value pairs. diff --git a/db/pebble/db.go b/db/pebble/db.go index 6592a602f0..6025e7f0df 100644 --- a/db/pebble/db.go +++ b/db/pebble/db.go @@ -94,6 +94,7 @@ func (d *DB) NewTransaction(update bool) (db.Transaction, error) { } else { txn.snapshot = d.pebble.NewSnapshot() } + txn.rwlock = &sync.RWMutex{} return txn, nil } @@ -158,3 +159,14 @@ func CalculatePrefixSize(ctx context.Context, pDB *DB, prefix []byte) (*Item, er return item, utils.RunAndWrapOnError(it.Close, err) } + +// View : see db.DB.View +func (d *DB) PersistedView() (db.Transaction, func() error, error) { + txn, err := d.NewTransaction(false) + if err != nil { + return nil, nil, err + } + return txn, func() error { + return txn.Discard() + }, nil +} diff --git a/db/pebble/transaction.go b/db/pebble/transaction.go index e1b74e089b..1963694c28 100644 --- a/db/pebble/transaction.go +++ b/db/pebble/transaction.go @@ -19,6 +19,7 @@ type Transaction struct { batch *pebble.Batch snapshot *pebble.Snapshot lock *sync.Mutex + rwlock *sync.RWMutex listener db.EventListener } @@ -56,6 +57,8 @@ func (t *Transaction) Commit() error { // Set : see db.Transaction.Set func (t *Transaction) Set(key, val []byte) error { + t.rwlock.Lock() + defer t.rwlock.Unlock() start := time.Now() if t.batch == nil { return errors.New("read only transaction") @@ -70,6 +73,8 @@ func (t *Transaction) Set(key, val []byte) error { // Delete : see db.Transaction.Delete func (t *Transaction) Delete(key []byte) error { + t.rwlock.Lock() + defer t.rwlock.Unlock() start := time.Now() if t.batch == nil { return errors.New("read only transaction") @@ -81,6 +86,8 @@ func (t *Transaction) Delete(key []byte) error { // Get : see db.Transaction.Get func (t *Transaction) Get(key []byte, cb func([]byte) error) error { + t.rwlock.RLock() + defer t.rwlock.RUnlock() start := time.Now() var val []byte var closer io.Closer diff --git a/db/remote/db.go b/db/remote/db.go index 734722adec..6e7984f771 100644 --- a/db/remote/db.go +++ b/db/remote/db.go @@ -2,6 +2,7 @@ package remote import ( "context" + "errors" "math" "github.com/NethermindEth/juno/db" @@ -62,3 +63,7 @@ func (d *DB) Close() error { func (d *DB) Impl() any { return d.kvClient } + +func (d *DB) PersistedView() (db.Transaction, func() error, error) { + return nil, nil, errors.New("persisted view not supported") +} diff --git a/go.mod b/go.mod index 6b096308b8..5e064920fd 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( go.uber.org/mock v0.4.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.26.0 + golang.org/x/sync v0.8.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 @@ -187,7 +188,6 @@ require ( golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect diff --git a/migration/migration_pkg_test.go b/migration/migration_pkg_test.go index fbcc168a90..960541fc44 100644 --- a/migration/migration_pkg_test.go +++ b/migration/migration_pkg_test.go @@ -81,6 +81,7 @@ func TestRelocateContractStorageRootKeys(t *testing.T) { func TestRecalculateBloomFilters(t *testing.T) { testdb := pebble.NewMemTest(t) chain := blockchain.New(testdb, &utils.Mainnet) + defer chain.Close() client := feeder.NewTestClient(t, &utils.Mainnet) gw := adaptfeeder.New(client) @@ -165,6 +166,7 @@ func TestChangeTrieNodeEncoding(t *testing.T) { func TestCalculateBlockCommitments(t *testing.T) { testdb := pebble.NewMemTest(t) chain := blockchain.New(testdb, &utils.Mainnet) + defer chain.Close() client := feeder.NewTestClient(t, &utils.Mainnet) gw := adaptfeeder.New(client) diff --git a/node/node_test.go b/node/node_test.go index a9287e0e4e..e9f718cba6 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -74,6 +74,7 @@ func TestNetworkVerificationOnNonEmptyDB(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond) require.NoError(t, syncer.Run(ctx)) cancel() + chain.Close() require.NoError(t, database.Close()) _, err = node.New(&node.Config{ diff --git a/p2p/starknet/handlers.go b/p2p/starknet/handlers.go index cf09e6ba86..e527b6cd8e 100644 --- a/p2p/starknet/handlers.go +++ b/p2p/starknet/handlers.go @@ -1,4 +1,5 @@ -//go:generate protoc --go_out=./ --proto_path=./ --go_opt=Mp2p/proto/transaction.proto=./spec --go_opt=Mp2p/proto/state.proto=./spec --go_opt=Mp2p/proto/snapshot.proto=./spec --go_opt=Mp2p/proto/receipt.proto=./spec --go_opt=Mp2p/proto/mempool.proto=./spec --go_opt=Mp2p/proto/event.proto=./spec --go_opt=Mp2p/proto/block.proto=./spec --go_opt=Mp2p/proto/common.proto=./spec p2p/proto/transaction.proto p2p/proto/state.proto p2p/proto/snapshot.proto p2p/proto/common.proto p2p/proto/block.proto p2p/proto/event.proto p2p/proto/receipt.proto +//go:generate protoc --go_out=./ --proto_path=./ --go_opt=Mp2p/proto/common.proto=./spec --go_opt=Mp2p/proto/header.proto=./spec --go_opt=Mp2p/proto/event.proto=./spec --go_opt=Mp2p/proto/receipt.proto=./spec --go_opt=Mp2p/proto/state.proto=./spec --go_opt=Mp2p/proto/transaction.proto=./spec --go_opt=Mp2p/proto/class.proto=./spec --go_opt=Mp2p/proto/snapshot.proto=./spec p2p/proto/common.proto p2p/proto/event.proto p2p/proto/header.proto p2p/proto/receipt.proto p2p/proto/state.proto p2p/proto/transaction.proto p2p/proto/class.proto p2p/proto/snapshot.proto + package starknet import ( diff --git a/p2p/starknet/p2p/proto/class.proto b/p2p/starknet/p2p/proto/class.proto index 2cd3ed5265..f68235350c 100644 --- a/p2p/starknet/p2p/proto/class.proto +++ b/p2p/starknet/p2p/proto/class.proto @@ -55,4 +55,9 @@ message ClassesResponse { Class class = 1; Fin fin = 2; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its classes. } -} \ No newline at end of file +} + +message Classes { + uint32 domain = 1; + repeated Class classes = 2; +} diff --git a/p2p/starknet/p2p/proto/snapshot.proto b/p2p/starknet/p2p/proto/snapshot.proto new file mode 100644 index 0000000000..47b5ae12e0 --- /dev/null +++ b/p2p/starknet/p2p/proto/snapshot.proto @@ -0,0 +1,112 @@ +syntax = "proto3"; + +import "p2p/proto/common.proto"; +import "p2p/proto/state.proto"; +import "p2p/proto/class.proto"; + +message PatriciaNode { + message Edge { + uint32 length = 1; + Felt252 path = 2; // as bits of left/right + Felt252 value = 3; + } + message Binary { + Felt252 left = 1; + Felt252 right = 2; + } + + oneof node { + Edge edge = 1; + Binary binary = 2; + } +} + +// non leaf nodes required to build the trie given the range (leaves) +message PatriciaRangeProof { + repeated PatriciaNode nodes = 1; +} + +// leafs of the contract state tree +message ContractState { + Address address = 1; // the key + Hash class = 2; + Hash storage = 3; // patricia + uint64 nonce = 4; +} + +// request a range from the contract state tree that matches the given root (block) +// starts at 'start' and ends no more than 'end'. +// the result is (ContractRange+, PatriciaRangeProof)* +message ContractRangeRequest { + uint32 domain = 1; // volition + Hash state_root = 2; + Address start = 3; + Address end = 4; + uint32 chunks_per_proof = 5; // how many ContractRange items to send before sending a proof +} + +// stream of leaves in the contracts tree +message ContractRange { + repeated ContractState state = 1; +} + +message ContractRangeResponse { + optional Hash root = 1; // may not appear if Fin is sent to end the whole response + optional Hash contracts_root = 2;// may not appear if Fin is sent to end the whole response + optional Hash classes_root = 3;// may not appear if Fin is sent to end the whole response + oneof responses { + ContractRange range = 4; + Fin fin = 5; + } +} + +// duplicate of GetContractRange. Can introduce a 'type' instead. +// result is (Classes+, PatriciaRangeProof)* +message ClassRangeRequest { + Hash root = 1; + Hash start = 2; + Hash end = 3; + uint32 chunks_per_proof = 4; +} + +message ClassRangeResponse { + optional Hash root = 1; // may not appear if Fin is sent to end the whole response + optional Hash contracts_root = 2;// may not appear if Fin is sent to end the whole response + optional Hash classes_root = 3;// may not appear if Fin is sent to end the whole response + oneof responses { + Classes classes = 4; + Fin fin = 5; + } +} + +// A position in some contract's state tree is identified by the state tree's root and the key in it +message StorageLeafQuery { + Hash contract_storage_root = 1; + Felt252 key = 2; +} + +message StorageRangeQuery { + Address address = 3; + StorageLeafQuery start = 1; + StorageLeafQuery end = 2; +} + +// result is (ContractStorageRange+, PatriciaRangeProof)* +message ContractStorageRequest { + uint32 domain = 1; // volition + Hash state_root = 2; + uint32 chunks_per_proof = 5; // how many ContractRange items to send before sending a proof + repeated StorageRangeQuery query = 3; +} + +message ContractStorage { + repeated ContractStoredValue keyValue = 2; +} + +message ContractStorageResponse { + optional Hash state_root = 1; // may not appear if Fin is sent to end the whole response + oneof responses { + ContractStorage storage = 2; + Fin fin = 3; + } +} diff --git a/p2p/starknet/p2p/proto/state.proto b/p2p/starknet/p2p/proto/state.proto index 79f33fcafe..228897c242 100644 --- a/p2p/starknet/p2p/proto/state.proto +++ b/p2p/starknet/p2p/proto/state.proto @@ -34,4 +34,4 @@ message StateDiffsResponse { DeclaredClass declared_class = 2; Fin fin = 3; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its state diff. } -} \ No newline at end of file +} diff --git a/p2p/starknet/spec/class.pb.go b/p2p/starknet/spec/class.pb.go index c0d591d4ca..53389258a0 100644 --- a/p2p/starknet/spec/class.pb.go +++ b/p2p/starknet/spec/class.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc v3.17.3 // source: p2p/proto/class.proto package spec @@ -570,6 +570,61 @@ func (*ClassesResponse_Class) isClassesResponse_ClassMessage() {} func (*ClassesResponse_Fin) isClassesResponse_ClassMessage() {} +type Classes struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Domain uint32 `protobuf:"varint,1,opt,name=domain,proto3" json:"domain,omitempty"` + Classes []*Class `protobuf:"bytes,2,rep,name=classes,proto3" json:"classes,omitempty"` +} + +func (x *Classes) Reset() { + *x = Classes{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_class_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Classes) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Classes) ProtoMessage() {} + +func (x *Classes) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_class_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Classes.ProtoReflect.Descriptor instead. +func (*Classes) Descriptor() ([]byte, []int) { + return file_p2p_proto_class_proto_rawDescGZIP(), []int{8} +} + +func (x *Classes) GetDomain() uint32 { + if x != nil { + return x.Domain + } + return 0 +} + +func (x *Classes) GetClasses() []*Class { + if x != nil { + return x.Classes + } + return nil +} + var File_p2p_proto_class_proto protoreflect.FileDescriptor var file_p2p_proto_class_proto_rawDesc = []byte{ @@ -640,10 +695,15 @@ var file_p2p_proto_class_proto_rawDesc = []byte{ 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x03, 0x66, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x46, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x03, 0x66, 0x69, 0x6e, 0x42, 0x0f, 0x0a, 0x0d, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x4e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x64, 0x45, 0x74, 0x68, 0x2f, 0x6a, 0x75, - 0x6e, 0x6f, 0x2f, 0x70, 0x32, 0x70, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x6b, 0x6e, 0x65, 0x74, 0x2f, - 0x73, 0x70, 0x65, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x22, 0x43, 0x0a, 0x07, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x64, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x52, 0x07, 0x63, + 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x64, 0x45, + 0x74, 0x68, 0x2f, 0x6a, 0x75, 0x6e, 0x6f, 0x2f, 0x70, 0x32, 0x70, 0x2f, 0x73, 0x74, 0x61, 0x72, + 0x6b, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x65, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -658,7 +718,7 @@ func file_p2p_proto_class_proto_rawDescGZIP() []byte { return file_p2p_proto_class_proto_rawDescData } -var file_p2p_proto_class_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_p2p_proto_class_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_p2p_proto_class_proto_goTypes = []any{ (*EntryPoint)(nil), // 0: EntryPoint (*Cairo0Class)(nil), // 1: Cairo0Class @@ -668,33 +728,35 @@ var file_p2p_proto_class_proto_goTypes = []any{ (*Class)(nil), // 5: Class (*ClassesRequest)(nil), // 6: ClassesRequest (*ClassesResponse)(nil), // 7: ClassesResponse - (*Felt252)(nil), // 8: Felt252 - (*Hash)(nil), // 9: Hash - (*Iteration)(nil), // 10: Iteration - (*Fin)(nil), // 11: Fin + (*Classes)(nil), // 8: Classes + (*Felt252)(nil), // 9: Felt252 + (*Hash)(nil), // 10: Hash + (*Iteration)(nil), // 11: Iteration + (*Fin)(nil), // 12: Fin } var file_p2p_proto_class_proto_depIdxs = []int32{ - 8, // 0: EntryPoint.selector:type_name -> Felt252 + 9, // 0: EntryPoint.selector:type_name -> Felt252 0, // 1: Cairo0Class.externals:type_name -> EntryPoint 0, // 2: Cairo0Class.l1_handlers:type_name -> EntryPoint 0, // 3: Cairo0Class.constructors:type_name -> EntryPoint - 8, // 4: SierraEntryPoint.selector:type_name -> Felt252 + 9, // 4: SierraEntryPoint.selector:type_name -> Felt252 2, // 5: Cairo1EntryPoints.externals:type_name -> SierraEntryPoint 2, // 6: Cairo1EntryPoints.l1_handlers:type_name -> SierraEntryPoint 2, // 7: Cairo1EntryPoints.constructors:type_name -> SierraEntryPoint 3, // 8: Cairo1Class.entry_points:type_name -> Cairo1EntryPoints - 8, // 9: Cairo1Class.program:type_name -> Felt252 + 9, // 9: Cairo1Class.program:type_name -> Felt252 1, // 10: Class.cairo0:type_name -> Cairo0Class 4, // 11: Class.cairo1:type_name -> Cairo1Class - 9, // 12: Class.class_hash:type_name -> Hash - 10, // 13: ClassesRequest.iteration:type_name -> Iteration + 10, // 12: Class.class_hash:type_name -> Hash + 11, // 13: ClassesRequest.iteration:type_name -> Iteration 5, // 14: ClassesResponse.class:type_name -> Class - 11, // 15: ClassesResponse.fin:type_name -> Fin - 16, // [16:16] is the sub-list for method output_type - 16, // [16:16] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name + 12, // 15: ClassesResponse.fin:type_name -> Fin + 5, // 16: Classes.classes:type_name -> Class + 17, // [17:17] is the sub-list for method output_type + 17, // [17:17] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_p2p_proto_class_proto_init() } @@ -800,6 +862,18 @@ func file_p2p_proto_class_proto_init() { return nil } } + file_p2p_proto_class_proto_msgTypes[8].Exporter = func(v any, i int) any { + switch v := v.(*Classes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_p2p_proto_class_proto_msgTypes[5].OneofWrappers = []any{ (*Class_Cairo0)(nil), @@ -815,7 +889,7 @@ func file_p2p_proto_class_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_p2p_proto_class_proto_rawDesc, NumEnums: 0, - NumMessages: 8, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, diff --git a/p2p/starknet/spec/common.pb.go b/p2p/starknet/spec/common.pb.go index db3915436a..b8c2fee32f 100644 --- a/p2p/starknet/spec/common.pb.go +++ b/p2p/starknet/spec/common.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc v3.17.3 // source: p2p/proto/common.proto package spec diff --git a/p2p/starknet/spec/event.pb.go b/p2p/starknet/spec/event.pb.go index 8543465c35..5b559e259c 100644 --- a/p2p/starknet/spec/event.pb.go +++ b/p2p/starknet/spec/event.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc v3.17.3 // source: p2p/proto/event.proto package spec diff --git a/p2p/starknet/spec/header.pb.go b/p2p/starknet/spec/header.pb.go index 5d94c6211b..49a8280b36 100644 --- a/p2p/starknet/spec/header.pb.go +++ b/p2p/starknet/spec/header.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc v3.17.3 // source: p2p/proto/header.proto package spec diff --git a/p2p/starknet/spec/receipt.pb.go b/p2p/starknet/spec/receipt.pb.go index 67d993d488..66f4772d24 100644 --- a/p2p/starknet/spec/receipt.pb.go +++ b/p2p/starknet/spec/receipt.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc v3.17.3 // source: p2p/proto/receipt.proto package spec diff --git a/p2p/starknet/spec/snapshot.pb.go b/p2p/starknet/spec/snapshot.pb.go new file mode 100644 index 0000000000..8c89f0bdc2 --- /dev/null +++ b/p2p/starknet/spec/snapshot.pb.go @@ -0,0 +1,1530 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc v3.17.3 +// source: p2p/proto/snapshot.proto + +package spec + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type PatriciaNode struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Node: + // + // *PatriciaNode_Edge_ + // *PatriciaNode_Binary_ + Node isPatriciaNode_Node `protobuf_oneof:"node"` +} + +func (x *PatriciaNode) Reset() { + *x = PatriciaNode{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PatriciaNode) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PatriciaNode) ProtoMessage() {} + +func (x *PatriciaNode) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PatriciaNode.ProtoReflect.Descriptor instead. +func (*PatriciaNode) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{0} +} + +func (m *PatriciaNode) GetNode() isPatriciaNode_Node { + if m != nil { + return m.Node + } + return nil +} + +func (x *PatriciaNode) GetEdge() *PatriciaNode_Edge { + if x, ok := x.GetNode().(*PatriciaNode_Edge_); ok { + return x.Edge + } + return nil +} + +func (x *PatriciaNode) GetBinary() *PatriciaNode_Binary { + if x, ok := x.GetNode().(*PatriciaNode_Binary_); ok { + return x.Binary + } + return nil +} + +type isPatriciaNode_Node interface { + isPatriciaNode_Node() +} + +type PatriciaNode_Edge_ struct { + Edge *PatriciaNode_Edge `protobuf:"bytes,1,opt,name=edge,proto3,oneof"` +} + +type PatriciaNode_Binary_ struct { + Binary *PatriciaNode_Binary `protobuf:"bytes,2,opt,name=binary,proto3,oneof"` +} + +func (*PatriciaNode_Edge_) isPatriciaNode_Node() {} + +func (*PatriciaNode_Binary_) isPatriciaNode_Node() {} + +// non leaf nodes required to build the trie given the range (leaves) +type PatriciaRangeProof struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Nodes []*PatriciaNode `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"` +} + +func (x *PatriciaRangeProof) Reset() { + *x = PatriciaRangeProof{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PatriciaRangeProof) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PatriciaRangeProof) ProtoMessage() {} + +func (x *PatriciaRangeProof) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PatriciaRangeProof.ProtoReflect.Descriptor instead. +func (*PatriciaRangeProof) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{1} +} + +func (x *PatriciaRangeProof) GetNodes() []*PatriciaNode { + if x != nil { + return x.Nodes + } + return nil +} + +// leafs of the contract state tree +type ContractState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address *Address `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // the key + Class *Hash `protobuf:"bytes,2,opt,name=class,proto3" json:"class,omitempty"` + Storage *Hash `protobuf:"bytes,3,opt,name=storage,proto3" json:"storage,omitempty"` // patricia + Nonce uint64 `protobuf:"varint,4,opt,name=nonce,proto3" json:"nonce,omitempty"` +} + +func (x *ContractState) Reset() { + *x = ContractState{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContractState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContractState) ProtoMessage() {} + +func (x *ContractState) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContractState.ProtoReflect.Descriptor instead. +func (*ContractState) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{2} +} + +func (x *ContractState) GetAddress() *Address { + if x != nil { + return x.Address + } + return nil +} + +func (x *ContractState) GetClass() *Hash { + if x != nil { + return x.Class + } + return nil +} + +func (x *ContractState) GetStorage() *Hash { + if x != nil { + return x.Storage + } + return nil +} + +func (x *ContractState) GetNonce() uint64 { + if x != nil { + return x.Nonce + } + return 0 +} + +// request a range from the contract state tree that matches the given root (block) +// starts at 'start' and ends no more than 'end'. +// the result is (ContractRange+, PatriciaRangeProof)* +type ContractRangeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Domain uint32 `protobuf:"varint,1,opt,name=domain,proto3" json:"domain,omitempty"` // volition + StateRoot *Hash `protobuf:"bytes,2,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"` + Start *Address `protobuf:"bytes,3,opt,name=start,proto3" json:"start,omitempty"` + End *Address `protobuf:"bytes,4,opt,name=end,proto3" json:"end,omitempty"` + ChunksPerProof uint32 `protobuf:"varint,5,opt,name=chunks_per_proof,json=chunksPerProof,proto3" json:"chunks_per_proof,omitempty"` // how many ContractRange items to send before sending a proof +} + +func (x *ContractRangeRequest) Reset() { + *x = ContractRangeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContractRangeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContractRangeRequest) ProtoMessage() {} + +func (x *ContractRangeRequest) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContractRangeRequest.ProtoReflect.Descriptor instead. +func (*ContractRangeRequest) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{3} +} + +func (x *ContractRangeRequest) GetDomain() uint32 { + if x != nil { + return x.Domain + } + return 0 +} + +func (x *ContractRangeRequest) GetStateRoot() *Hash { + if x != nil { + return x.StateRoot + } + return nil +} + +func (x *ContractRangeRequest) GetStart() *Address { + if x != nil { + return x.Start + } + return nil +} + +func (x *ContractRangeRequest) GetEnd() *Address { + if x != nil { + return x.End + } + return nil +} + +func (x *ContractRangeRequest) GetChunksPerProof() uint32 { + if x != nil { + return x.ChunksPerProof + } + return 0 +} + +// stream of leaves in the contracts tree +type ContractRange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + State []*ContractState `protobuf:"bytes,1,rep,name=state,proto3" json:"state,omitempty"` +} + +func (x *ContractRange) Reset() { + *x = ContractRange{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContractRange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContractRange) ProtoMessage() {} + +func (x *ContractRange) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContractRange.ProtoReflect.Descriptor instead. +func (*ContractRange) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{4} +} + +func (x *ContractRange) GetState() []*ContractState { + if x != nil { + return x.State + } + return nil +} + +type ContractRangeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Root *Hash `protobuf:"bytes,1,opt,name=root,proto3,oneof" json:"root,omitempty"` // may not appear if Fin is sent to end the whole response + ContractsRoot *Hash `protobuf:"bytes,2,opt,name=contracts_root,json=contractsRoot,proto3,oneof" json:"contracts_root,omitempty"` // may not appear if Fin is sent to end the whole response + ClassesRoot *Hash `protobuf:"bytes,3,opt,name=classes_root,json=classesRoot,proto3,oneof" json:"classes_root,omitempty"` // may not appear if Fin is sent to end the whole response + // Types that are assignable to Responses: + // + // *ContractRangeResponse_Range + // *ContractRangeResponse_Fin + Responses isContractRangeResponse_Responses `protobuf_oneof:"responses"` +} + +func (x *ContractRangeResponse) Reset() { + *x = ContractRangeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContractRangeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContractRangeResponse) ProtoMessage() {} + +func (x *ContractRangeResponse) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContractRangeResponse.ProtoReflect.Descriptor instead. +func (*ContractRangeResponse) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{5} +} + +func (x *ContractRangeResponse) GetRoot() *Hash { + if x != nil { + return x.Root + } + return nil +} + +func (x *ContractRangeResponse) GetContractsRoot() *Hash { + if x != nil { + return x.ContractsRoot + } + return nil +} + +func (x *ContractRangeResponse) GetClassesRoot() *Hash { + if x != nil { + return x.ClassesRoot + } + return nil +} + +func (m *ContractRangeResponse) GetResponses() isContractRangeResponse_Responses { + if m != nil { + return m.Responses + } + return nil +} + +func (x *ContractRangeResponse) GetRange() *ContractRange { + if x, ok := x.GetResponses().(*ContractRangeResponse_Range); ok { + return x.Range + } + return nil +} + +func (x *ContractRangeResponse) GetFin() *Fin { + if x, ok := x.GetResponses().(*ContractRangeResponse_Fin); ok { + return x.Fin + } + return nil +} + +type isContractRangeResponse_Responses interface { + isContractRangeResponse_Responses() +} + +type ContractRangeResponse_Range struct { + Range *ContractRange `protobuf:"bytes,4,opt,name=range,proto3,oneof"` +} + +type ContractRangeResponse_Fin struct { + Fin *Fin `protobuf:"bytes,5,opt,name=fin,proto3,oneof"` +} + +func (*ContractRangeResponse_Range) isContractRangeResponse_Responses() {} + +func (*ContractRangeResponse_Fin) isContractRangeResponse_Responses() {} + +// duplicate of GetContractRange. Can introduce a 'type' instead. +// result is (Classes+, PatriciaRangeProof)* +type ClassRangeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Root *Hash `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"` + Start *Hash `protobuf:"bytes,2,opt,name=start,proto3" json:"start,omitempty"` + End *Hash `protobuf:"bytes,3,opt,name=end,proto3" json:"end,omitempty"` + ChunksPerProof uint32 `protobuf:"varint,4,opt,name=chunks_per_proof,json=chunksPerProof,proto3" json:"chunks_per_proof,omitempty"` +} + +func (x *ClassRangeRequest) Reset() { + *x = ClassRangeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClassRangeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClassRangeRequest) ProtoMessage() {} + +func (x *ClassRangeRequest) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClassRangeRequest.ProtoReflect.Descriptor instead. +func (*ClassRangeRequest) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{6} +} + +func (x *ClassRangeRequest) GetRoot() *Hash { + if x != nil { + return x.Root + } + return nil +} + +func (x *ClassRangeRequest) GetStart() *Hash { + if x != nil { + return x.Start + } + return nil +} + +func (x *ClassRangeRequest) GetEnd() *Hash { + if x != nil { + return x.End + } + return nil +} + +func (x *ClassRangeRequest) GetChunksPerProof() uint32 { + if x != nil { + return x.ChunksPerProof + } + return 0 +} + +type ClassRangeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Root *Hash `protobuf:"bytes,1,opt,name=root,proto3,oneof" json:"root,omitempty"` // may not appear if Fin is sent to end the whole response + ContractsRoot *Hash `protobuf:"bytes,2,opt,name=contracts_root,json=contractsRoot,proto3,oneof" json:"contracts_root,omitempty"` // may not appear if Fin is sent to end the whole response + ClassesRoot *Hash `protobuf:"bytes,3,opt,name=classes_root,json=classesRoot,proto3,oneof" json:"classes_root,omitempty"` // may not appear if Fin is sent to end the whole response + // Types that are assignable to Responses: + // + // *ClassRangeResponse_Classes + // *ClassRangeResponse_Fin + Responses isClassRangeResponse_Responses `protobuf_oneof:"responses"` +} + +func (x *ClassRangeResponse) Reset() { + *x = ClassRangeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClassRangeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClassRangeResponse) ProtoMessage() {} + +func (x *ClassRangeResponse) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClassRangeResponse.ProtoReflect.Descriptor instead. +func (*ClassRangeResponse) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{7} +} + +func (x *ClassRangeResponse) GetRoot() *Hash { + if x != nil { + return x.Root + } + return nil +} + +func (x *ClassRangeResponse) GetContractsRoot() *Hash { + if x != nil { + return x.ContractsRoot + } + return nil +} + +func (x *ClassRangeResponse) GetClassesRoot() *Hash { + if x != nil { + return x.ClassesRoot + } + return nil +} + +func (m *ClassRangeResponse) GetResponses() isClassRangeResponse_Responses { + if m != nil { + return m.Responses + } + return nil +} + +func (x *ClassRangeResponse) GetClasses() *Classes { + if x, ok := x.GetResponses().(*ClassRangeResponse_Classes); ok { + return x.Classes + } + return nil +} + +func (x *ClassRangeResponse) GetFin() *Fin { + if x, ok := x.GetResponses().(*ClassRangeResponse_Fin); ok { + return x.Fin + } + return nil +} + +type isClassRangeResponse_Responses interface { + isClassRangeResponse_Responses() +} + +type ClassRangeResponse_Classes struct { + Classes *Classes `protobuf:"bytes,4,opt,name=classes,proto3,oneof"` +} + +type ClassRangeResponse_Fin struct { + Fin *Fin `protobuf:"bytes,5,opt,name=fin,proto3,oneof"` +} + +func (*ClassRangeResponse_Classes) isClassRangeResponse_Responses() {} + +func (*ClassRangeResponse_Fin) isClassRangeResponse_Responses() {} + +// A position in some contract's state tree is identified by the state tree's root and the key in it +type StorageLeafQuery struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContractStorageRoot *Hash `protobuf:"bytes,1,opt,name=contract_storage_root,json=contractStorageRoot,proto3" json:"contract_storage_root,omitempty"` + Key *Felt252 `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *StorageLeafQuery) Reset() { + *x = StorageLeafQuery{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StorageLeafQuery) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StorageLeafQuery) ProtoMessage() {} + +func (x *StorageLeafQuery) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StorageLeafQuery.ProtoReflect.Descriptor instead. +func (*StorageLeafQuery) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{8} +} + +func (x *StorageLeafQuery) GetContractStorageRoot() *Hash { + if x != nil { + return x.ContractStorageRoot + } + return nil +} + +func (x *StorageLeafQuery) GetKey() *Felt252 { + if x != nil { + return x.Key + } + return nil +} + +type StorageRangeQuery struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address *Address `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` + Start *StorageLeafQuery `protobuf:"bytes,1,opt,name=start,proto3" json:"start,omitempty"` + End *StorageLeafQuery `protobuf:"bytes,2,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *StorageRangeQuery) Reset() { + *x = StorageRangeQuery{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StorageRangeQuery) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StorageRangeQuery) ProtoMessage() {} + +func (x *StorageRangeQuery) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StorageRangeQuery.ProtoReflect.Descriptor instead. +func (*StorageRangeQuery) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{9} +} + +func (x *StorageRangeQuery) GetAddress() *Address { + if x != nil { + return x.Address + } + return nil +} + +func (x *StorageRangeQuery) GetStart() *StorageLeafQuery { + if x != nil { + return x.Start + } + return nil +} + +func (x *StorageRangeQuery) GetEnd() *StorageLeafQuery { + if x != nil { + return x.End + } + return nil +} + +// result is (ContractStorageRange+, PatriciaRangeProof)* +type ContractStorageRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Domain uint32 `protobuf:"varint,1,opt,name=domain,proto3" json:"domain,omitempty"` // volition + StateRoot *Hash `protobuf:"bytes,2,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"` + ChunksPerProof uint32 `protobuf:"varint,5,opt,name=chunks_per_proof,json=chunksPerProof,proto3" json:"chunks_per_proof,omitempty"` // how many ContractRange items to send before sending a proof + Query []*StorageRangeQuery `protobuf:"bytes,3,rep,name=query,proto3" json:"query,omitempty"` +} + +func (x *ContractStorageRequest) Reset() { + *x = ContractStorageRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContractStorageRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContractStorageRequest) ProtoMessage() {} + +func (x *ContractStorageRequest) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContractStorageRequest.ProtoReflect.Descriptor instead. +func (*ContractStorageRequest) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{10} +} + +func (x *ContractStorageRequest) GetDomain() uint32 { + if x != nil { + return x.Domain + } + return 0 +} + +func (x *ContractStorageRequest) GetStateRoot() *Hash { + if x != nil { + return x.StateRoot + } + return nil +} + +func (x *ContractStorageRequest) GetChunksPerProof() uint32 { + if x != nil { + return x.ChunksPerProof + } + return 0 +} + +func (x *ContractStorageRequest) GetQuery() []*StorageRangeQuery { + if x != nil { + return x.Query + } + return nil +} + +type ContractStorage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + KeyValue []*ContractStoredValue `protobuf:"bytes,2,rep,name=keyValue,proto3" json:"keyValue,omitempty"` +} + +func (x *ContractStorage) Reset() { + *x = ContractStorage{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContractStorage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContractStorage) ProtoMessage() {} + +func (x *ContractStorage) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContractStorage.ProtoReflect.Descriptor instead. +func (*ContractStorage) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{11} +} + +func (x *ContractStorage) GetKeyValue() []*ContractStoredValue { + if x != nil { + return x.KeyValue + } + return nil +} + +type ContractStorageResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StateRoot *Hash `protobuf:"bytes,1,opt,name=state_root,json=stateRoot,proto3,oneof" json:"state_root,omitempty"` // may not appear if Fin is sent to end the whole response + // Types that are assignable to Responses: + // + // *ContractStorageResponse_Storage + // *ContractStorageResponse_Fin + Responses isContractStorageResponse_Responses `protobuf_oneof:"responses"` +} + +func (x *ContractStorageResponse) Reset() { + *x = ContractStorageResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContractStorageResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContractStorageResponse) ProtoMessage() {} + +func (x *ContractStorageResponse) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContractStorageResponse.ProtoReflect.Descriptor instead. +func (*ContractStorageResponse) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{12} +} + +func (x *ContractStorageResponse) GetStateRoot() *Hash { + if x != nil { + return x.StateRoot + } + return nil +} + +func (m *ContractStorageResponse) GetResponses() isContractStorageResponse_Responses { + if m != nil { + return m.Responses + } + return nil +} + +func (x *ContractStorageResponse) GetStorage() *ContractStorage { + if x, ok := x.GetResponses().(*ContractStorageResponse_Storage); ok { + return x.Storage + } + return nil +} + +func (x *ContractStorageResponse) GetFin() *Fin { + if x, ok := x.GetResponses().(*ContractStorageResponse_Fin); ok { + return x.Fin + } + return nil +} + +type isContractStorageResponse_Responses interface { + isContractStorageResponse_Responses() +} + +type ContractStorageResponse_Storage struct { + Storage *ContractStorage `protobuf:"bytes,2,opt,name=storage,proto3,oneof"` +} + +type ContractStorageResponse_Fin struct { + Fin *Fin `protobuf:"bytes,3,opt,name=fin,proto3,oneof"` +} + +func (*ContractStorageResponse_Storage) isContractStorageResponse_Responses() {} + +func (*ContractStorageResponse_Fin) isContractStorageResponse_Responses() {} + +type PatriciaNode_Edge struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Length uint32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` + Path *Felt252 `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` // as bits of left/right + Value *Felt252 `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *PatriciaNode_Edge) Reset() { + *x = PatriciaNode_Edge{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PatriciaNode_Edge) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PatriciaNode_Edge) ProtoMessage() {} + +func (x *PatriciaNode_Edge) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PatriciaNode_Edge.ProtoReflect.Descriptor instead. +func (*PatriciaNode_Edge) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *PatriciaNode_Edge) GetLength() uint32 { + if x != nil { + return x.Length + } + return 0 +} + +func (x *PatriciaNode_Edge) GetPath() *Felt252 { + if x != nil { + return x.Path + } + return nil +} + +func (x *PatriciaNode_Edge) GetValue() *Felt252 { + if x != nil { + return x.Value + } + return nil +} + +type PatriciaNode_Binary struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Left *Felt252 `protobuf:"bytes,1,opt,name=left,proto3" json:"left,omitempty"` + Right *Felt252 `protobuf:"bytes,2,opt,name=right,proto3" json:"right,omitempty"` +} + +func (x *PatriciaNode_Binary) Reset() { + *x = PatriciaNode_Binary{} + if protoimpl.UnsafeEnabled { + mi := &file_p2p_proto_snapshot_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PatriciaNode_Binary) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PatriciaNode_Binary) ProtoMessage() {} + +func (x *PatriciaNode_Binary) ProtoReflect() protoreflect.Message { + mi := &file_p2p_proto_snapshot_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PatriciaNode_Binary.ProtoReflect.Descriptor instead. +func (*PatriciaNode_Binary) Descriptor() ([]byte, []int) { + return file_p2p_proto_snapshot_proto_rawDescGZIP(), []int{0, 1} +} + +func (x *PatriciaNode_Binary) GetLeft() *Felt252 { + if x != nil { + return x.Left + } + return nil +} + +func (x *PatriciaNode_Binary) GetRight() *Felt252 { + if x != nil { + return x.Right + } + return nil +} + +var File_p2p_proto_snapshot_proto protoreflect.FileDescriptor + +var file_p2p_proto_snapshot_proto_rawDesc = []byte{ + 0x0a, 0x18, 0x70, 0x32, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x16, 0x70, 0x32, 0x70, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x15, 0x70, 0x32, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x70, 0x32, 0x70, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x96, 0x02, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x69, 0x61, 0x4e, 0x6f, 0x64, + 0x65, 0x12, 0x28, 0x0a, 0x04, 0x65, 0x64, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x69, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x45, + 0x64, 0x67, 0x65, 0x48, 0x00, 0x52, 0x04, 0x65, 0x64, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x50, 0x61, + 0x74, 0x72, 0x69, 0x63, 0x69, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x48, 0x00, 0x52, 0x06, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x1a, 0x5c, 0x0a, 0x04, 0x45, + 0x64, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x46, 0x65, 0x6c, 0x74, + 0x32, 0x35, 0x32, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1e, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x46, 0x65, 0x6c, 0x74, 0x32, + 0x35, 0x32, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x46, 0x0a, 0x06, 0x42, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x04, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x08, 0x2e, 0x46, 0x65, 0x6c, 0x74, 0x32, 0x35, 0x32, 0x52, 0x04, 0x6c, 0x65, 0x66, + 0x74, 0x12, 0x1e, 0x0a, 0x05, 0x72, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x08, 0x2e, 0x46, 0x65, 0x6c, 0x74, 0x32, 0x35, 0x32, 0x52, 0x05, 0x72, 0x69, 0x67, 0x68, + 0x74, 0x42, 0x06, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x39, 0x0a, 0x12, 0x50, 0x61, 0x74, + 0x72, 0x69, 0x63, 0x69, 0x61, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, + 0x23, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, + 0x2e, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x69, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, + 0x6f, 0x64, 0x65, 0x73, 0x22, 0x87, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x05, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, 0x68, + 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, + 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0xba, + 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, + 0x24, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1a, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x03, 0x65, 0x6e, + 0x64, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x68, 0x75, + 0x6e, 0x6b, 0x73, 0x50, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x35, 0x0a, 0x0d, 0x43, + 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x22, 0x95, 0x02, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x04, + 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, + 0x68, 0x48, 0x01, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x0e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x48, 0x02, 0x52, 0x0d, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x88, 0x01, 0x01, 0x12, + 0x2d, 0x0a, 0x0c, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x48, 0x03, 0x52, 0x0b, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x88, 0x01, 0x01, 0x12, 0x26, + 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, + 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x03, 0x66, 0x69, 0x6e, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x46, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x03, 0x66, 0x69, 0x6e, + 0x42, 0x0b, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x42, 0x07, 0x0a, + 0x05, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x61, 0x63, 0x74, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x22, 0x8e, 0x01, 0x0a, 0x11, 0x43, + 0x6c, 0x61, 0x73, 0x73, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x19, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, + 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x12, 0x1b, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, + 0x68, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x03, 0x65, 0x6e, + 0x64, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x68, 0x75, + 0x6e, 0x6b, 0x73, 0x50, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x90, 0x02, 0x0a, 0x12, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x48, 0x01, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x88, + 0x01, 0x01, 0x12, 0x31, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x5f, + 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, + 0x68, 0x48, 0x02, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x52, 0x6f, + 0x6f, 0x74, 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x0c, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, + 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, + 0x73, 0x68, 0x48, 0x03, 0x52, 0x0b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x52, 0x6f, 0x6f, + 0x74, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x48, + 0x00, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x03, 0x66, 0x69, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x46, 0x69, 0x6e, 0x48, 0x00, 0x52, + 0x03, 0x66, 0x69, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x42, 0x0f, 0x0a, + 0x0d, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x22, 0x69, + 0x0a, 0x10, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x65, 0x61, 0x66, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x39, 0x0a, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, + 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1a, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x46, 0x65, 0x6c, + 0x74, 0x32, 0x35, 0x32, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x85, 0x01, 0x0a, 0x11, 0x53, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, + 0x22, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x08, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x65, 0x61, 0x66, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x23, 0x0a, 0x03, + 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x4c, 0x65, 0x61, 0x66, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x03, 0x65, 0x6e, + 0x64, 0x22, 0xaa, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x53, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x64, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x24, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, + 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, + 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x68, + 0x75, 0x6e, 0x6b, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x50, 0x65, 0x72, 0x50, + 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x28, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x43, + 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x12, 0x30, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x53, 0x74, + 0x6f, 0x72, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0xa8, 0x01, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x29, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x48, 0x01, 0x52, 0x09, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x07, 0x73, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, + 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x03, 0x66, 0x69, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x46, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x03, 0x66, + 0x69, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x42, + 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_p2p_proto_snapshot_proto_rawDescOnce sync.Once + file_p2p_proto_snapshot_proto_rawDescData = file_p2p_proto_snapshot_proto_rawDesc +) + +func file_p2p_proto_snapshot_proto_rawDescGZIP() []byte { + file_p2p_proto_snapshot_proto_rawDescOnce.Do(func() { + file_p2p_proto_snapshot_proto_rawDescData = protoimpl.X.CompressGZIP(file_p2p_proto_snapshot_proto_rawDescData) + }) + return file_p2p_proto_snapshot_proto_rawDescData +} + +var file_p2p_proto_snapshot_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_p2p_proto_snapshot_proto_goTypes = []any{ + (*PatriciaNode)(nil), // 0: PatriciaNode + (*PatriciaRangeProof)(nil), // 1: PatriciaRangeProof + (*ContractState)(nil), // 2: ContractState + (*ContractRangeRequest)(nil), // 3: ContractRangeRequest + (*ContractRange)(nil), // 4: ContractRange + (*ContractRangeResponse)(nil), // 5: ContractRangeResponse + (*ClassRangeRequest)(nil), // 6: ClassRangeRequest + (*ClassRangeResponse)(nil), // 7: ClassRangeResponse + (*StorageLeafQuery)(nil), // 8: StorageLeafQuery + (*StorageRangeQuery)(nil), // 9: StorageRangeQuery + (*ContractStorageRequest)(nil), // 10: ContractStorageRequest + (*ContractStorage)(nil), // 11: ContractStorage + (*ContractStorageResponse)(nil), // 12: ContractStorageResponse + (*PatriciaNode_Edge)(nil), // 13: PatriciaNode.Edge + (*PatriciaNode_Binary)(nil), // 14: PatriciaNode.Binary + (*Address)(nil), // 15: Address + (*Hash)(nil), // 16: Hash + (*Fin)(nil), // 17: Fin + (*Classes)(nil), // 18: Classes + (*Felt252)(nil), // 19: Felt252 + (*ContractStoredValue)(nil), // 20: ContractStoredValue +} +var file_p2p_proto_snapshot_proto_depIdxs = []int32{ + 13, // 0: PatriciaNode.edge:type_name -> PatriciaNode.Edge + 14, // 1: PatriciaNode.binary:type_name -> PatriciaNode.Binary + 0, // 2: PatriciaRangeProof.nodes:type_name -> PatriciaNode + 15, // 3: ContractState.address:type_name -> Address + 16, // 4: ContractState.class:type_name -> Hash + 16, // 5: ContractState.storage:type_name -> Hash + 16, // 6: ContractRangeRequest.state_root:type_name -> Hash + 15, // 7: ContractRangeRequest.start:type_name -> Address + 15, // 8: ContractRangeRequest.end:type_name -> Address + 2, // 9: ContractRange.state:type_name -> ContractState + 16, // 10: ContractRangeResponse.root:type_name -> Hash + 16, // 11: ContractRangeResponse.contracts_root:type_name -> Hash + 16, // 12: ContractRangeResponse.classes_root:type_name -> Hash + 4, // 13: ContractRangeResponse.range:type_name -> ContractRange + 17, // 14: ContractRangeResponse.fin:type_name -> Fin + 16, // 15: ClassRangeRequest.root:type_name -> Hash + 16, // 16: ClassRangeRequest.start:type_name -> Hash + 16, // 17: ClassRangeRequest.end:type_name -> Hash + 16, // 18: ClassRangeResponse.root:type_name -> Hash + 16, // 19: ClassRangeResponse.contracts_root:type_name -> Hash + 16, // 20: ClassRangeResponse.classes_root:type_name -> Hash + 18, // 21: ClassRangeResponse.classes:type_name -> Classes + 17, // 22: ClassRangeResponse.fin:type_name -> Fin + 16, // 23: StorageLeafQuery.contract_storage_root:type_name -> Hash + 19, // 24: StorageLeafQuery.key:type_name -> Felt252 + 15, // 25: StorageRangeQuery.address:type_name -> Address + 8, // 26: StorageRangeQuery.start:type_name -> StorageLeafQuery + 8, // 27: StorageRangeQuery.end:type_name -> StorageLeafQuery + 16, // 28: ContractStorageRequest.state_root:type_name -> Hash + 9, // 29: ContractStorageRequest.query:type_name -> StorageRangeQuery + 20, // 30: ContractStorage.keyValue:type_name -> ContractStoredValue + 16, // 31: ContractStorageResponse.state_root:type_name -> Hash + 11, // 32: ContractStorageResponse.storage:type_name -> ContractStorage + 17, // 33: ContractStorageResponse.fin:type_name -> Fin + 19, // 34: PatriciaNode.Edge.path:type_name -> Felt252 + 19, // 35: PatriciaNode.Edge.value:type_name -> Felt252 + 19, // 36: PatriciaNode.Binary.left:type_name -> Felt252 + 19, // 37: PatriciaNode.Binary.right:type_name -> Felt252 + 38, // [38:38] is the sub-list for method output_type + 38, // [38:38] is the sub-list for method input_type + 38, // [38:38] is the sub-list for extension type_name + 38, // [38:38] is the sub-list for extension extendee + 0, // [0:38] is the sub-list for field type_name +} + +func init() { file_p2p_proto_snapshot_proto_init() } +func file_p2p_proto_snapshot_proto_init() { + if File_p2p_proto_snapshot_proto != nil { + return + } + file_p2p_proto_common_proto_init() + file_p2p_proto_state_proto_init() + file_p2p_proto_class_proto_init() + if !protoimpl.UnsafeEnabled { + file_p2p_proto_snapshot_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*PatriciaNode); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*PatriciaRangeProof); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*ContractState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*ContractRangeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*ContractRange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*ContractRangeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[6].Exporter = func(v any, i int) any { + switch v := v.(*ClassRangeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[7].Exporter = func(v any, i int) any { + switch v := v.(*ClassRangeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[8].Exporter = func(v any, i int) any { + switch v := v.(*StorageLeafQuery); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[9].Exporter = func(v any, i int) any { + switch v := v.(*StorageRangeQuery); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[10].Exporter = func(v any, i int) any { + switch v := v.(*ContractStorageRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[11].Exporter = func(v any, i int) any { + switch v := v.(*ContractStorage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[12].Exporter = func(v any, i int) any { + switch v := v.(*ContractStorageResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[13].Exporter = func(v any, i int) any { + switch v := v.(*PatriciaNode_Edge); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_p2p_proto_snapshot_proto_msgTypes[14].Exporter = func(v any, i int) any { + switch v := v.(*PatriciaNode_Binary); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_p2p_proto_snapshot_proto_msgTypes[0].OneofWrappers = []any{ + (*PatriciaNode_Edge_)(nil), + (*PatriciaNode_Binary_)(nil), + } + file_p2p_proto_snapshot_proto_msgTypes[5].OneofWrappers = []any{ + (*ContractRangeResponse_Range)(nil), + (*ContractRangeResponse_Fin)(nil), + } + file_p2p_proto_snapshot_proto_msgTypes[7].OneofWrappers = []any{ + (*ClassRangeResponse_Classes)(nil), + (*ClassRangeResponse_Fin)(nil), + } + file_p2p_proto_snapshot_proto_msgTypes[12].OneofWrappers = []any{ + (*ContractStorageResponse_Storage)(nil), + (*ContractStorageResponse_Fin)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_p2p_proto_snapshot_proto_rawDesc, + NumEnums: 0, + NumMessages: 15, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_p2p_proto_snapshot_proto_goTypes, + DependencyIndexes: file_p2p_proto_snapshot_proto_depIdxs, + MessageInfos: file_p2p_proto_snapshot_proto_msgTypes, + }.Build() + File_p2p_proto_snapshot_proto = out.File + file_p2p_proto_snapshot_proto_rawDesc = nil + file_p2p_proto_snapshot_proto_goTypes = nil + file_p2p_proto_snapshot_proto_depIdxs = nil +} diff --git a/p2p/starknet/spec/state.pb.go b/p2p/starknet/spec/state.pb.go index c230a042b3..a165d3d680 100644 --- a/p2p/starknet/spec/state.pb.go +++ b/p2p/starknet/spec/state.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc v3.17.3 // source: p2p/proto/state.proto package spec diff --git a/p2p/starknet/spec/transaction.pb.go b/p2p/starknet/spec/transaction.pb.go index ecd18338b3..3aa5519f2a 100644 --- a/p2p/starknet/spec/transaction.pb.go +++ b/p2p/starknet/spec/transaction.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc v3.17.3 // source: p2p/proto/transaction.proto package spec diff --git a/rpc/events_test.go b/rpc/events_test.go index 5ed4973306..736e888c19 100644 --- a/rpc/events_test.go +++ b/rpc/events_test.go @@ -28,6 +28,7 @@ func TestEvents(t *testing.T) { testDB := pebble.NewMemTest(t) n := utils.Ptr(utils.Sepolia) chain := blockchain.New(testDB, n) + defer chain.Close() client := feeder.NewTestClient(t, n) gw := adaptfeeder.New(client) @@ -238,6 +239,7 @@ func TestSubscribeNewHeadsAndUnsubscribe(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) chain := blockchain.New(pebble.NewMemTest(t), n) + defer chain.Close() syncer := sync.New(chain, gw, log, 0, false) handler := rpc.New(chain, syncer, nil, "", log) @@ -319,6 +321,7 @@ func TestMultipleSubscribeNewHeadsAndUnsubscribe(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) chain := blockchain.New(pebble.NewMemTest(t), n) + defer chain.Close() syncer := sync.New(chain, gw, log, 0, false) handler := rpc.New(chain, syncer, nil, "", log) go func() { diff --git a/sync/snap_server.go b/sync/snap_server.go new file mode 100644 index 0000000000..b1f3e03c7d --- /dev/null +++ b/sync/snap_server.go @@ -0,0 +1,447 @@ +package sync + +import ( + "context" + "errors" + "math/big" + + "github.com/NethermindEth/juno/adapters/core2p2p" + "github.com/NethermindEth/juno/adapters/p2p2core" + "github.com/NethermindEth/juno/blockchain" + "github.com/NethermindEth/juno/core" + "github.com/NethermindEth/juno/core/felt" + "github.com/NethermindEth/juno/core/trie" + "github.com/NethermindEth/juno/p2p/starknet/spec" + "github.com/NethermindEth/juno/utils/iter" +) + +type ContractRangeStreamingResult struct { + ContractsRoot *felt.Felt + ClassesRoot *felt.Felt + Range []*spec.ContractState + RangeProof *spec.PatriciaRangeProof +} + +type StorageRangeRequest struct { + StateRoot *felt.Felt + ChunkPerProof uint64 // Missing in spec + Queries []*spec.StorageRangeQuery +} + +type StorageRangeStreamingResult struct { + ContractsRoot *felt.Felt + ClassesRoot *felt.Felt + StorageAddr *felt.Felt + Range []*spec.ContractStoredValue + RangeProof *spec.PatriciaRangeProof +} + +type ClassRangeStreamingResult struct { + ContractsRoot *felt.Felt + ClassesRoot *felt.Felt + Range *spec.Classes + RangeProof *spec.PatriciaRangeProof +} + +type SnapServer interface { + GetContractRange(ctx context.Context, request *spec.ContractRangeRequest) iter.Seq2[*ContractRangeStreamingResult, error] + GetStorageRange(ctx context.Context, request *StorageRangeRequest) iter.Seq2[*StorageRangeStreamingResult, error] + GetClassRange(ctx context.Context, request *spec.ClassRangeRequest) iter.Seq2[*ClassRangeStreamingResult, error] + GetClasses(ctx context.Context, classHashes []*felt.Felt) ([]*spec.Class, error) +} + +type SnapServerBlockchain interface { + GetStateForStateRoot(stateRoot *felt.Felt) (*core.State, error) + GetClasses(felts []*felt.Felt) ([]core.Class, error) +} + +type snapServer struct { + blockchain SnapServerBlockchain +} + +var _ SnapServerBlockchain = &blockchain.Blockchain{} + +func determineMaxNodes(specifiedMaxNodes uint64) uint64 { + const ( + defaultMaxNodes = 1024 * 16 + maxNodePerRequest = 1024 * 1024 // I just want it to process faster + ) + + if specifiedMaxNodes == 0 { + return defaultMaxNodes + } + + if specifiedMaxNodes < maxNodePerRequest { + return specifiedMaxNodes + } + + return maxNodePerRequest +} + +func (b *snapServer) GetClassRange(ctx context.Context, request *spec.ClassRangeRequest) iter.Seq2[*ClassRangeStreamingResult, error] { + return func(yield func(*ClassRangeStreamingResult, error) bool) { + stateRoot := p2p2core.AdaptHash(request.Root) + + s, err := b.blockchain.GetStateForStateRoot(stateRoot) + if err != nil { + yield(nil, err) + return + } + + contractRoot, classRoot, err := s.StateAndClassRoot() + if err != nil { + yield(nil, err) + return + } + + ctrie, classCloser, err := s.ClassTrie() + if err != nil { + yield(nil, err) + return + } + defer func() { _ = classCloser() }() + + startAddr := p2p2core.AdaptHash(request.Start) + limitAddr := p2p2core.AdaptHash(request.End) + if limitAddr.IsZero() { + limitAddr = nil + } + + for { + response := &spec.Classes{ + Classes: make([]*spec.Class, 0), + } + + classkeys := []*felt.Felt{} + proofs, finished, err := iterateWithLimit(ctrie, startAddr, limitAddr, determineMaxNodes(uint64(request.ChunksPerProof)), + func(key, value *felt.Felt) error { + classkeys = append(classkeys, key) + return nil + }) + if err != nil { + yield(nil, err) + return + } + + coreClasses, err := b.blockchain.GetClasses(classkeys) + if err != nil { + yield(nil, err) + return + } + + for _, coreclass := range coreClasses { + if coreclass == nil { + yield(nil, errors.New("class is nil")) + return + } + response.Classes = append(response.Classes, core2p2p.AdaptClass(coreclass)) + } + + if err != nil { + yield(nil, err) + return + } + + shouldContinue := yield(&ClassRangeStreamingResult{ + ContractsRoot: contractRoot, + ClassesRoot: classRoot, + Range: response, + RangeProof: Core2P2pProof(proofs), + }, err) + + if finished || !shouldContinue { + break + } + startAddr = classkeys[len(classkeys)-1] + } + + // TODO: not needed? - just stop the loop + yield(nil, nil) + } +} + +func (b *snapServer) GetContractRange( + ctx context.Context, + request *spec.ContractRangeRequest, +) iter.Seq2[*ContractRangeStreamingResult, error] { + return func(yield func(*ContractRangeStreamingResult, error) bool) { + stateRoot := p2p2core.AdaptHash(request.StateRoot) + + s, err := b.blockchain.GetStateForStateRoot(stateRoot) + if err != nil { + yield(nil, err) + return + } + + contractRoot, classRoot, err := s.StateAndClassRoot() + if err != nil { + yield(nil, err) + return + } + + strie, scloser, err := s.StorageTrie() + if err != nil { + yield(nil, err) + return + } + defer func() { _ = scloser() }() + + startAddr := p2p2core.AdaptAddress(request.Start) + limitAddr := p2p2core.AdaptAddress(request.End) + states := []*spec.ContractState{} + + for { + proofs, finished, err := iterateWithLimit(strie, startAddr, limitAddr, determineMaxNodes(uint64(request.ChunksPerProof)), + func(key, value *felt.Felt) error { + classHash, err := s.ContractClassHash(key) + if err != nil { + return err + } + + nonce, err := s.ContractNonce(key) + if err != nil { + return err + } + + ctr, err := s.StorageTrieForAddr(key) + if err != nil { + return err + } + + croot, err := ctr.Root() + if err != nil { + return err + } + + startAddr = key + states = append(states, &spec.ContractState{ + Address: core2p2p.AdaptAddress(key), + Class: core2p2p.AdaptHash(classHash), + Storage: core2p2p.AdaptHash(croot), + Nonce: nonce.Uint64(), + }) + return nil + }) + if err != nil { + yield(nil, err) + return + } + + shouldContinue := yield(&ContractRangeStreamingResult{ + ContractsRoot: contractRoot, + ClassesRoot: classRoot, + Range: states, + RangeProof: Core2P2pProof(proofs), + }, nil) + + if finished || !shouldContinue { + break + } + } + + yield(nil, nil) + } +} + +func (b *snapServer) GetStorageRange(ctx context.Context, request *StorageRangeRequest) iter.Seq2[*StorageRangeStreamingResult, error] { + return func(yield func(*StorageRangeStreamingResult, error) bool) { + stateRoot := request.StateRoot + + s, err := b.blockchain.GetStateForStateRoot(stateRoot) + if err != nil { + yield(nil, err) + return + } + + contractRoot, classRoot, err := s.StateAndClassRoot() + if err != nil { + yield(nil, err) + return + } + + var curNodeLimit int64 = 1000000 + + for _, query := range request.Queries { + if ctxerr := ctx.Err(); ctxerr != nil { + break + } + + contractLimit := uint64(curNodeLimit) + + strie, err := s.StorageTrieForAddr(p2p2core.AdaptAddress(query.Address)) + if err != nil { + yield(nil, err) + return + } + + handled, err := b.handleStorageRangeRequest(ctx, strie, query, request.ChunkPerProof, contractLimit, + func(values []*spec.ContractStoredValue, proofs []trie.ProofNode) { + yield(&StorageRangeStreamingResult{ + ContractsRoot: contractRoot, + ClassesRoot: classRoot, + StorageAddr: p2p2core.AdaptAddress(query.Address), + Range: values, + RangeProof: Core2P2pProof(proofs), + }, nil) + }) + if err != nil { + yield(nil, err) + return + } + + curNodeLimit -= handled + + if curNodeLimit <= 0 { + break + } + } + } +} + +func (b *snapServer) GetClasses(ctx context.Context, felts []*felt.Felt) ([]*spec.Class, error) { + classes := make([]*spec.Class, len(felts)) + coreClasses, err := b.blockchain.GetClasses(felts) + if err != nil { + return nil, err + } + + for i, class := range coreClasses { + classes[i] = core2p2p.AdaptClass(class) + } + + return classes, nil +} + +func (b *snapServer) handleStorageRangeRequest( + ctx context.Context, + stTrie *trie.Trie, + request *spec.StorageRangeQuery, + maxChunkPerProof uint64, + nodeLimit uint64, + yield func([]*spec.ContractStoredValue, []trie.ProofNode), +) (int64, error) { + totalSent := int64(0) + finished := false + startAddr := p2p2core.AdaptFelt(request.Start.Key) + var endAddr *felt.Felt = nil + if request.End != nil { + endAddr = p2p2core.AdaptFelt(request.End.Key) + } + + for !finished { + if ctxErr := ctx.Err(); ctxErr != nil { + return totalSent, ctxErr + } + + response := []*spec.ContractStoredValue{} + + limit := maxChunkPerProof + if nodeLimit < limit { + limit = nodeLimit + } + + proofs, finish, err := iterateWithLimit(stTrie, startAddr, endAddr, limit, func(key, value *felt.Felt) error { + response = append(response, &spec.ContractStoredValue{ + Key: core2p2p.AdaptFelt(key), + Value: core2p2p.AdaptFelt(value), + }) + + startAddr = key + return nil + }) + finished = finish + + if err != nil { + return 0, err + } + + if len(response) == 0 { + finished = true + } + + yield(response, proofs) + + totalSent += int64(len(response)) + nodeLimit -= limit + + asBint := startAddr.BigInt(big.NewInt(0)) + asBint = asBint.Add(asBint, big.NewInt(1)) + startAddr = startAddr.SetBigInt(asBint) + } + + return totalSent, nil +} + +func iterateWithLimit( + srcTrie *trie.Trie, + startAddr *felt.Felt, + limitAddr *felt.Felt, + maxNodes uint64, + consumer func(key, value *felt.Felt) error, +) ([]trie.ProofNode, bool, error) { + pathes := make([]*felt.Felt, 0) + hashes := make([]*felt.Felt, 0) + + // TODO: Verify class trie + count := uint64(0) + proof, finished, err := srcTrie.IterateAndGenerateProof(startAddr, func(key *felt.Felt, value *felt.Felt) (bool, error) { + // Need at least one. + if limitAddr != nil && key.Cmp(limitAddr) > 1 && count > 0 { + return false, nil + } + + pathes = append(pathes, key) + hashes = append(hashes, value) + + err := consumer(key, value) + if err != nil { + return false, err + } + + count++ + if count >= maxNodes { + return false, nil + } + return true, nil + }) + if err != nil { + return nil, finished, err + } + + return proof, finished, err +} + +func Core2P2pProof(proofs []trie.ProofNode) *spec.PatriciaRangeProof { + nodes := make([]*spec.PatriciaNode, len(proofs)) + + for i := range proofs { + if proofs[i].Binary != nil { + binary := proofs[i].Binary + nodes[i] = &spec.PatriciaNode{ + Node: &spec.PatriciaNode_Binary_{ + Binary: &spec.PatriciaNode_Binary{ + Left: core2p2p.AdaptFelt(binary.LeftHash), + Right: core2p2p.AdaptFelt(binary.RightHash), + }, + }, + } + } + if proofs[i].Edge != nil { + edge := proofs[i].Edge + pathfeld := edge.Path.Felt() + nodes[i] = &spec.PatriciaNode{ + Node: &spec.PatriciaNode_Edge_{ + Edge: &spec.PatriciaNode_Edge{ + Length: uint32(edge.Path.Len()), + Path: core2p2p.AdaptFelt(&pathfeld), + Value: core2p2p.AdaptFelt(edge.Child), + }, + }, + } + } + } + + return &spec.PatriciaRangeProof{ + Nodes: nodes, + } +} diff --git a/sync/snap_server_test.go b/sync/snap_server_test.go new file mode 100644 index 0000000000..bd0bf74683 --- /dev/null +++ b/sync/snap_server_test.go @@ -0,0 +1,206 @@ +package sync + +import ( + "context" + "fmt" + "testing" + + "github.com/NethermindEth/juno/adapters/core2p2p" + "github.com/NethermindEth/juno/adapters/p2p2core" + "github.com/NethermindEth/juno/blockchain" + "github.com/NethermindEth/juno/core" + "github.com/NethermindEth/juno/core/crypto" + "github.com/NethermindEth/juno/core/felt" + "github.com/NethermindEth/juno/db" + "github.com/NethermindEth/juno/db/pebble" + "github.com/NethermindEth/juno/p2p/starknet/spec" + "github.com/NethermindEth/juno/utils" + "github.com/stretchr/testify/assert" +) + +func TestClassRange(t *testing.T) { + var d db.DB + t.Skip("DB snapshot is needed for this test") + d, _ = pebble.NewWithOptions("/Users/pnowosie/juno/snapshots/juno-sepolia", 128000000, 128, false) + defer func() { _ = d.Close() }() + bc := blockchain.New(d, &utils.Sepolia) // Needed because class loader need encoder to be registered + + b, err := bc.Head() + assert.NoError(t, err) + + fmt.Printf("headblock %d\n", b.Number) + + stateRoot := b.GlobalStateRoot + + server := &snapServer{ + blockchain: bc, + } + + startRange := (&felt.Felt{}).SetUint64(0) + + chunksPerProof := 150 + var classResult *ClassRangeStreamingResult + server.GetClassRange(context.Background(), + &spec.ClassRangeRequest{ + Root: core2p2p.AdaptHash(stateRoot), + Start: core2p2p.AdaptHash(startRange), + ChunksPerProof: uint32(chunksPerProof), + })(func(result *ClassRangeStreamingResult, err error) bool { + if err != nil { + fmt.Printf("err %s\n", err) + t.Fatal(err) + } + + if result != nil { + classResult = result + } + + return false + }) + + assert.NotNil(t, classResult) + assert.Equal(t, chunksPerProof, len(classResult.Range.Classes)) + verifyErr := VerifyGlobalStateRoot(stateRoot, classResult.ClassesRoot, classResult.ContractsRoot) + assert.NoError(t, verifyErr) +} + +func TestContractRange(t *testing.T) { + var d db.DB + t.Skip("DB snapshot is needed for this test") + d, _ = pebble.NewWithOptions("/Users/pnowosie/juno/snapshots/juno-sepolia", 128000000, 128, false) + defer func() { _ = d.Close() }() + bc := blockchain.New(d, &utils.Sepolia) // Needed because class loader need encoder to be registered + + b, err := bc.Head() + assert.NoError(t, err) + + fmt.Printf("headblock %d\n", b.Number) + + stateRoot := b.GlobalStateRoot + + server := &snapServer{ + blockchain: bc, + } + + startRange := (&felt.Felt{}).SetUint64(0) + + chunksPerProof := 150 + var contractResult *ContractRangeStreamingResult + server.GetContractRange(context.Background(), + &spec.ContractRangeRequest{ + StateRoot: core2p2p.AdaptHash(stateRoot), + Start: core2p2p.AdaptAddress(startRange), + ChunksPerProof: uint32(chunksPerProof), + })(func(result *ContractRangeStreamingResult, err error) bool { + if err != nil { + fmt.Printf("err %s\n", err) + t.Fatal(err) + } + + if result != nil { + contractResult = result + } + + return false + }) + + assert.NotNil(t, contractResult) + assert.Equal(t, chunksPerProof, len(contractResult.Range)) + verifyErr := VerifyGlobalStateRoot(stateRoot, contractResult.ClassesRoot, contractResult.ContractsRoot) + assert.NoError(t, verifyErr) +} + +func TestContractStorageRange(t *testing.T) { + var d db.DB + t.Skip("DB snapshot is needed for this test") + d, _ = pebble.NewWithOptions("/Users/pnowosie/juno/snapshots/juno-sepolia", 128000000, 128, false) + defer func() { _ = d.Close() }() + bc := blockchain.New(d, &utils.Sepolia) // Needed because class loader need encoder to be registered + + b, err := bc.Head() + assert.NoError(t, err) + + fmt.Printf("headblock %d\n", b.Number) + + stateRoot := b.GlobalStateRoot + + server := &snapServer{ + blockchain: bc, + } + + startRange := (&felt.Felt{}).SetUint64(0) + + tests := []struct { + address *felt.Felt + storageRoot *felt.Felt + expectedLeaves int + }{ + { + address: feltFromString("0x3deecdb26a60e4c062d5bd98ab37f72ea2acc37f28dae6923359627ebde9"), + storageRoot: feltFromString("0x276edbc91a945d11645ba0b8298c7d657e554d06ab2bb765cbc44d61fa01fd5"), + expectedLeaves: 1, + }, + { + address: feltFromString("0x5de00d3720421ab00fdbc47d33d253605c1ac226ab1a0d267f7d57e23305"), + storageRoot: feltFromString("0x5eebb2c6722d321469cb662260c5171c9f6f67b9a625c9c9ab56b0a4631b0fe"), + expectedLeaves: 2, + }, + { + address: feltFromString("0x1ee60ed3c5abd9a08c61de5e8cbcf32b49646e681ee6e84da9d52f5c3099"), + storageRoot: feltFromString("0x60dccd54f4956147c6a499b71579820d181e22d5e9c430fd5953f861ca7727e"), + expectedLeaves: 4, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%.7s...", test.address), func(t *testing.T) { + request := &StorageRangeRequest{ + StateRoot: stateRoot, + ChunkPerProof: 100, + Queries: []*spec.StorageRangeQuery{ + { + Address: core2p2p.AdaptAddress(test.address), + Start: &spec.StorageLeafQuery{ + ContractStorageRoot: core2p2p.AdaptHash(test.storageRoot), + Key: core2p2p.AdaptFelt(startRange), + }, + End: nil, + }, + }, + } + + keys := make([]*felt.Felt, 0, test.expectedLeaves) + vals := make([]*felt.Felt, 0, test.expectedLeaves) + server.GetStorageRange(context.Background(), request)(func(result *StorageRangeStreamingResult, err error) bool { + if err != nil { + fmt.Printf("err %s\n", err) + t.Fatal(err) + } + + if result != nil { + for _, r := range result.Range { + keys = append(keys, p2p2core.AdaptFelt(r.Key)) + vals = append(vals, p2p2core.AdaptFelt(r.Value)) + } + } + + return true + }) + + fmt.Println("Address:", test.address, "storage length:", len(keys)) + assert.Equal(t, test.expectedLeaves, len(keys)) + + hasMore, err := VerifyTrie(test.storageRoot, keys, vals, nil, core.ContractStorageTrieHeight, crypto.Pedersen) + assert.NoError(t, err) + assert.False(t, hasMore) + }) + } +} + +func feltFromString(str string) *felt.Felt { + f, err := (&felt.Felt{}).SetString(str) + if err != nil { + panic(err) + } + return f +} diff --git a/sync/snapsyncer.go b/sync/snapsyncer.go new file mode 100644 index 0000000000..d7bad6f7cf --- /dev/null +++ b/sync/snapsyncer.go @@ -0,0 +1,1107 @@ +package sync + +import ( + "context" + "errors" + "fmt" + big "math/big" + "sync" + "sync/atomic" + "time" + + "github.com/NethermindEth/juno/adapters/core2p2p" + "github.com/NethermindEth/juno/adapters/p2p2core" + "github.com/NethermindEth/juno/blockchain" + "github.com/NethermindEth/juno/core" + "github.com/NethermindEth/juno/core/crypto" + "github.com/NethermindEth/juno/core/felt" + "github.com/NethermindEth/juno/core/trie" + "github.com/NethermindEth/juno/p2p/starknet/spec" + "github.com/NethermindEth/juno/service" + "github.com/NethermindEth/juno/starknetdata" + "github.com/NethermindEth/juno/utils" + "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "golang.org/x/sync/errgroup" +) + +const JobDuration = time.Second * 10 + +type Blockchain interface { + GetClasses(felts []*felt.Felt) ([]core.Class, error) + PutClasses(blockNumber uint64, v1CompiledHashes map[felt.Felt]*felt.Felt, newClasses map[felt.Felt]core.Class) error + PutContracts(address, nonces, classHash []*felt.Felt) error + PutStorage(storage map[felt.Felt]map[felt.Felt]*felt.Felt) error +} + +type SnapSyncher struct { + baseSync service.Service + starknetData starknetdata.StarknetData + snapServer SnapServer + blockchain Blockchain + log utils.Logger + + startingBlock *core.Header + lastBlock *core.Header + currentGlobalStateRoot *felt.Felt + + contractRangeDone chan interface{} + storageRangeDone chan interface{} + + storageRangeJobCount int32 + storageRangeJobQueue chan *storageRangeJob + storageRefreshJob chan *storageRangeJob + + classesJob chan *felt.Felt + + // Three lock priority lock + mtxM *sync.Mutex + mtxN *sync.Mutex + mtxL *sync.Mutex +} + +type storageRangeJob struct { + path *felt.Felt + storageRoot *felt.Felt + startAddress *felt.Felt + classHash *felt.Felt + nonce uint64 +} + +func NewSnapSyncer( + baseSyncher service.Service, + consensus starknetdata.StarknetData, + server SnapServer, + bc *blockchain.Blockchain, + log utils.Logger, +) *SnapSyncher { + return &SnapSyncher{ + baseSync: baseSyncher, + starknetData: consensus, + snapServer: server, + blockchain: bc, + log: log, + } +} + +var ( + // magic values linter does not like + start = 1.0 + factor = 1.5 + count = 30 + + rangeProgress = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "juno_range_progress", + Help: "Time in address get", + }) + + pivotUpdates = promauto.NewCounter(prometheus.CounterOpts{ + Name: "juno_pivot_update", + Help: "Time in address get", + }) + + storageAddressCount = promauto.NewHistogram(prometheus.HistogramOpts{ + Name: "juno_storage_address_count", + Help: "Time in address get", + Buckets: prometheus.ExponentialBuckets(start, factor, count), + }) +) + +var ( + storageJobWorker = 8 + storageBatchSize = 500 + storageJobQueueSize = storageJobWorker * storageBatchSize // Too high and the progress from address range would be inaccurate. + + // For some reason, the trie throughput is higher if the batch size is small. + classRangeChunksPerProof = 500 + contractRangeChunkPerProof = 500 + storageRangeChunkPerProof = 500 + maxStorageBatchSize = 500 + maxMaxPerStorageSize = 500 + + fetchClassWorkerCount = 8 // Fairly parallelizable. But this is brute force... + classesJobQueueSize = 128 + + maxPivotDistance = 32 // Set to 1 to test updated storage. + newPivotHeadDistance = uint64(0) // This should be the reorg depth + +) + +func (s *SnapSyncher) Run(ctx context.Context) error { + s.log.Infow("starting snap sync") + // 1. Get the current head + // 2. Start the snap sync with pivot set to that head + // 3. If at any moment, if: + // a. The current head is too new (more than 64 block let say) + // b. Too many missing node + // then reset the pivot. + // 4. Once finished, replay state update from starting pivot to the latest pivot. + // 5. Then do some cleanup, mark things and complete and such. + // 6. Probably download old state updato/bodies too + // 7. Send back control to base sync. + + err := s.runPhase1(ctx) + if err != nil { + return err + } + + s.log.Infow("delegating to standard synchronizer") + return s.baseSync.Run(ctx) +} + +func VerifyTrie( + expectedRoot *felt.Felt, + paths, hashes []*felt.Felt, + proofs []trie.ProofNode, + height uint8, + hash func(*felt.Felt, *felt.Felt) *felt.Felt, +) (bool, error) { + hasMore, valid, err := trie.VerifyRange(expectedRoot, nil, paths, hashes, proofs, hash, height) + if err != nil { + return false, err + } + if !valid { + return false, errors.New("invalid proof") + } + + return hasMore, nil +} + +//nolint:gocyclo,nolintlint +func (s *SnapSyncher) runPhase1(ctx context.Context) error { + starttime := time.Now() + + err := s.initState(ctx) + if err != nil { + return errors.Join(err, errors.New("error initialising snap syncer state")) + } + + eg, ectx := errgroup.WithContext(ctx) + + eg.Go(func() error { + defer func() { + s.log.Infow("pool latest block done") + if err := recover(); err != nil { + s.log.Errorw("latest block pool panicked", "err", err) + } + }() + + return s.poolLatestBlock(ectx) + }) + + eg.Go(func() error { + defer func() { + if err := recover(); err != nil { + s.log.Errorw("class range panicked", "err", err) + } + }() + + err := s.runClassRangeWorker(ectx) + if err != nil { + s.log.Errorw("error in class range worker", "err", err) + } + return err + }) + + eg.Go(func() error { + defer func() { + if err := recover(); err != nil { + s.log.Errorw("address range paniced", "err", err) + } + }() + + err := s.runContractRangeWorker(ectx) + if err != nil { + s.log.Errorw("error in address range worker", "err", err) + } + + s.log.Infow("contract range done") + close(s.contractRangeDone) + close(s.classesJob) + + return err + }) + + storageEg, sctx := errgroup.WithContext(ectx) + for i := 0; i < storageJobWorker; i++ { + i := i + storageEg.Go(func() error { + defer func() { + if err := recover(); err != nil { + s.log.Errorw("storage worker paniced", "err", err) + } + }() + + err := s.runStorageRangeWorker(sctx, i) + if err != nil { + s.log.Errorw("error in storage range worker", "err", err) + } + s.log.Infow("Storage worker completed", "workerId", i) + + return err + }) + } + + // For notifying that storage range is done + eg.Go(func() error { + err := storageEg.Wait() + if err != nil { + return err + } + + s.log.Infow("Storage range range completed") + close(s.storageRangeDone) + return nil + }) + + eg.Go(func() error { + defer func() { + if err := recover(); err != nil { + s.log.Errorw("storage refresh paniced", "err", err) + } + }() + + err := s.runStorageRefreshWorker(ectx) + if err != nil { + s.log.Errorw("error in storage refresh worker", "err", err) + } + + return err + }) + + for i := 0; i < fetchClassWorkerCount; i++ { + i := i + eg.Go(func() error { + err := s.runFetchClassJob(ectx) + if err != nil { + s.log.Errorw("fetch class failed", "err", err) + } + s.log.Infow("fetch class completed", "workerId", i) + return err + }) + } + + err = eg.Wait() + if err != nil { + return err + } + + s.log.Infow("first phase completed", "duration", time.Since(starttime)) + + return nil +} + +func (s *SnapSyncher) getNextStartingBlock(ctx context.Context) (*core.Block, error) { + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + head, err := s.starknetData.BlockLatest(ctx) + if err != nil { + s.log.Warnw("error getting current head", "error", err) + continue + } + startingBlock, err := s.starknetData.BlockByNumber(ctx, head.Number-newPivotHeadDistance) + if err != nil { + s.log.Warnw("error getting starting block", "error", err) + continue + } + + return startingBlock, nil + } +} + +func (s *SnapSyncher) initState(ctx context.Context) error { + startingBlock, err := s.getNextStartingBlock(ctx) + if err != nil { + return errors.Join(err, errors.New("error getting current head")) + } + + s.startingBlock = startingBlock.Header + s.lastBlock = startingBlock.Header + + fmt.Printf("Start state root is %s\n", s.startingBlock.GlobalStateRoot) + s.currentGlobalStateRoot = s.startingBlock.GlobalStateRoot.Clone() + s.storageRangeJobCount = 0 + s.storageRangeJobQueue = make(chan *storageRangeJob, storageJobQueueSize) + s.classesJob = make(chan *felt.Felt, classesJobQueueSize) + + s.contractRangeDone = make(chan interface{}) + s.storageRangeDone = make(chan interface{}) + + s.mtxM = &sync.Mutex{} + s.mtxN = &sync.Mutex{} + s.mtxL = &sync.Mutex{} + + return nil +} + +func calculatePercentage(f *felt.Felt) uint64 { + const maxPercent = 100 + maxint := big.NewInt(1) + maxint.Lsh(maxint, core.GlobalTrieHeight) + + percent := f.BigInt(big.NewInt(0)) + percent.Mul(percent, big.NewInt(maxPercent)) + percent.Div(percent, maxint) + + return percent.Uint64() +} + +//nolint:gocyclo,nolintlint +func (s *SnapSyncher) runClassRangeWorker(ctx context.Context) error { + totaladded := 0 + completed := false + startAddr := &felt.Zero + for !completed { + s.log.Infow("class range progress", "progress", calculatePercentage(startAddr)) + + stateRoot := s.currentGlobalStateRoot + + var err error + + s.log.Infow("class range state root", "stateroot", stateRoot) + + // TODO: Maybe timeout + s.snapServer.GetClassRange(ctx, &spec.ClassRangeRequest{ + Root: core2p2p.AdaptHash(stateRoot), + Start: core2p2p.AdaptHash(startAddr), + ChunksPerProof: uint32(classRangeChunksPerProof), + })(func(response *ClassRangeStreamingResult, reqErr error) bool { + if reqErr != nil { + fmt.Printf("%s\n", errors.Join(reqErr, errors.New("error get address range"))) + return false + } + + if response == nil || (response.Range == nil && response.RangeProof == nil) { + // State root missing. + return false + } + s.log.Infow("got", "res", len(response.Range.Classes), "err", reqErr, "startAdr", startAddr) + + err = VerifyGlobalStateRoot(stateRoot, response.ClassesRoot, response.ContractsRoot) + if err != nil { + s.log.Infow("global state root verification failure") + // Root verification failed + // TODO: Ban peer + return false + } + + if response.ClassesRoot.Equal(&felt.Zero) { + // Special case, no V1 at all + completed = true + return false + } + + paths := make([]*felt.Felt, len(response.Range.Classes)) + values := make([]*felt.Felt, len(response.Range.Classes)) + coreClasses := make([]core.Class, len(response.Range.Classes)) + + egrp := errgroup.Group{} + + for i, cls := range response.Range.Classes { + coreClass := p2p2core.AdaptClass(cls) + i := i + egrp.Go(func() error { + coreClasses[i] = coreClass + paths[i] = CalculateClassHash(coreClass) + values[i] = CalculateCompiledClassHash(coreClass) + + // For verification, should be + // leafValue := crypto.Poseidon(leafVersion, compiledClassHash) + return nil + }) + } + + err = egrp.Wait() + if err != nil { + return false + } + + proofs := P2pProofToTrieProofs(response.RangeProof) + hasNext, err := VerifyTrie(response.ClassesRoot, paths, values, proofs, core.GlobalTrieHeight, crypto.Poseidon) + if err != nil { + // Root verification failed + // TODO: Ban peer + return false + } + + // Ingest + coreClassesMap := map[felt.Felt]core.Class{} + coreClassesHashMap := map[felt.Felt]*felt.Felt{} + for i, coreClass := range coreClasses { + coreClassesMap[*paths[i]] = coreClass + coreClassesHashMap[*paths[i]] = values[i] + } + + err = s.blockchain.PutClasses(s.lastBlock.Number, coreClassesHashMap, coreClassesMap) + if err != nil { + return false + } + + if !hasNext { + s.log.Infow("class range completed", "totalClass", totaladded) + completed = true + return false + } + + // Increment addr, start loop again + startAddr = paths[len(paths)-1] + + return true + }) + + if err != nil { + return err + } + } + + return nil +} + +func CalculateCompiledClassHash(cls core.Class) *felt.Felt { + return cls.(*core.Cairo1Class).Compiled.Hash() +} + +func P2pProofToTrieProofs(proof *spec.PatriciaRangeProof) []trie.ProofNode { + // TODO: Move to adapter + + proofs := make([]trie.ProofNode, len(proof.Nodes)) + for i, node := range proof.Nodes { + if node.GetBinary() != nil { + binary := node.GetBinary() + proofs[i] = trie.ProofNode{ + Binary: &trie.Binary{ + LeftHash: p2p2core.AdaptFelt(binary.Left), + RightHash: p2p2core.AdaptFelt(binary.Right), + }, + } + } else { + edge := node.GetEdge() + // TODO. What if edge is nil too? + key := trie.NewKey(uint8(edge.Length), edge.Path.Elements) + proofs[i] = trie.ProofNode{ + Edge: &trie.Edge{ + Child: p2p2core.AdaptFelt(edge.Value), + Path: &key, + }, + } + } + } + + return proofs +} + +var stateVersion = new(felt.Felt).SetBytes([]byte(`STARKNET_STATE_V0`)) + +func VerifyGlobalStateRoot(globalStateRoot, classRoot, storageRoot *felt.Felt) error { + if classRoot.IsZero() { + if globalStateRoot.Equal(storageRoot) { + return nil + } else { + return errors.New("invalid global state root") + } + } + + if !crypto.PoseidonArray(stateVersion, storageRoot, classRoot).Equal(globalStateRoot) { + return errors.New("invalid global state root") + } + return nil +} + +func CalculateClassHash(cls core.Class) *felt.Felt { + hash, err := cls.Hash() + if err != nil { + panic(err) + } + + return hash +} + +//nolint:gocyclo +func (s *SnapSyncher) runContractRangeWorker(ctx context.Context) error { + startAddr := &felt.Zero + completed := false + + for !completed { + var outherErr error + + stateRoot := s.currentGlobalStateRoot + s.snapServer.GetContractRange(ctx, &spec.ContractRangeRequest{ + Domain: 0, // What do this do? + StateRoot: core2p2p.AdaptHash(stateRoot), + Start: core2p2p.AdaptAddress(startAddr), + End: nil, // No need for now. + ChunksPerProof: uint32(contractRangeChunkPerProof), + })(func(response *ContractRangeStreamingResult, _err error) bool { + s.log.Infow("snap range progress", "progress", calculatePercentage(startAddr), "addr", startAddr) + rangeProgress.Set(float64(calculatePercentage(startAddr))) + + if response == nil || (response.Range == nil && response.RangeProof == nil) { + // State root missing. + return false + } + + err := VerifyGlobalStateRoot(stateRoot, response.ClassesRoot, response.ContractsRoot) + outherErr = err + if err != nil { + // Root verification failed + // TODO: Ban peer + return false + } + + paths := make([]*felt.Felt, len(response.Range)) + values := make([]*felt.Felt, len(response.Range)) + + for i, rangeValue := range response.Range { + paths[i] = p2p2core.AdaptAddress(rangeValue.Address) + values[i] = CalculateRangeValueHash(rangeValue) + } + + proofs := P2pProofToTrieProofs(response.RangeProof) + hasNext, err := VerifyTrie(response.ContractsRoot, paths, values, proofs, core.GlobalTrieHeight, crypto.Pedersen) + outherErr = err + if err != nil { + // The peer should get penalised in this case + return false + } + + classes := []*felt.Felt{} + nonces := []*felt.Felt{} + for _, r := range response.Range { + classHash := p2p2core.AdaptHash(r.Class) + classes = append(classes, classHash) + nonces = append(nonces, (&felt.Felt{}).SetUint64(r.Nonce)) + } + + err = s.blockchain.PutContracts(paths, nonces, classes) + outherErr = err + if err != nil { + fmt.Printf("%s\n", err) + panic(err) + } + + // We don't actually store it directly here... only put it as part of job. + // Can't remember why. Could be because it would be some wasted work. + for _, r := range response.Range { + path := p2p2core.AdaptAddress(r.Address) + storageRoot := p2p2core.AdaptHash(r.Storage) + classHash := p2p2core.AdaptHash(r.Class) + nonce := r.Nonce + + err = s.queueClassJob(ctx, classHash) + outherErr = err + if err != nil { + return false + } + + err = s.queueStorageRangeJob(ctx, path, storageRoot, classHash, nonce) + outherErr = err + if err != nil { + return false + } + } + + if !hasNext { + s.log.Infow("address range completed") + completed = true + return false + } + + if len(paths) == 0 { + return false + } + + startAddr = paths[len(paths)-1] + return true + }) + + if outherErr != nil { + s.log.Errorw("Error with contract range", "err", outherErr) + // TODO: address the error properly + // Well... need to figure out how to determine if its a temporary error or not. + // For sure, the state root can be outdated, so this need to restart + return outherErr + } + } + + return nil +} + +func CalculateRangeValueHash(value *spec.ContractState) *felt.Felt { + nonce := fp.NewElement(value.Nonce) + return calculateContractCommitment( + p2p2core.AdaptHash(value.Storage), + p2p2core.AdaptHash(value.Class), + felt.NewFelt(&nonce), + ) +} + +func calculateContractCommitment(storageRoot, classHash, nonce *felt.Felt) *felt.Felt { + return crypto.Pedersen(crypto.Pedersen(crypto.Pedersen(classHash, storageRoot), nonce), &felt.Zero) +} + +func (s *SnapSyncher) queueClassJob(ctx context.Context, classHash *felt.Felt) error { + queued := false + for !queued { + select { + case s.classesJob <- classHash: + queued = true + case <-ctx.Done(): + return ctx.Err() + case <-time.After(time.Second): + s.log.Infow("class queue stall on class") + } + } + return nil +} + +func (s *SnapSyncher) queueStorageRangeJob(ctx context.Context, path, storageRoot, classHash *felt.Felt, nonce uint64) error { + return s.queueStorageRangeJobJob(ctx, &storageRangeJob{ + path: path, + storageRoot: storageRoot, + startAddress: &felt.Zero, + classHash: classHash, + nonce: nonce, + }) +} + +func (s *SnapSyncher) queueStorageRangeJobJob(ctx context.Context, job *storageRangeJob) error { + queued := false + for !queued { + select { + case s.storageRangeJobQueue <- job: + queued = true + atomic.AddInt32(&s.storageRangeJobCount, 1) + case <-ctx.Done(): + return ctx.Err() + case <-time.After(JobDuration): + s.log.Infow("queue storage range stall") + } + } + return nil +} + +func (s *SnapSyncher) queueStorageRefreshJob(ctx context.Context, job *storageRangeJob) error { + queued := false + for !queued { + select { + case s.storageRefreshJob <- job: + queued = true + atomic.AddInt32(&s.storageRangeJobCount, 1) + case <-ctx.Done(): + return ctx.Err() + case <-time.After(time.Second): + s.log.Infow("storage refresh queue stall") + } + } + return nil +} + +//nolint:funlen,gocyclo +func (s *SnapSyncher) runStorageRangeWorker(ctx context.Context, workerIdx int) error { + nextjobs := make([]*storageRangeJob, 0) + for { + jobs := nextjobs + + requestloop: + for len(jobs) < storageBatchSize { + contractDoneChecker := s.contractRangeDone + if s.storageRangeJobCount > 0 { + contractDoneChecker = nil // So that it never complete as there are job to be done + } + + select { + case job := <-s.storageRangeJobQueue: + jobs = append(jobs, job) + case <-ctx.Done(): + return ctx.Err() + case <-time.After(JobDuration): + if len(jobs) > 0 { + break requestloop + } + s.log.Infow("waiting for more storage job", "count", s.storageRangeJobCount) + case <-contractDoneChecker: + // Its done... + return nil + } + } + + s.log.Infow("Pending jobs count", "pending", s.storageRangeJobCount) + + requests := make([]*spec.StorageRangeQuery, 0) + for _, job := range jobs { + requests = append(requests, &spec.StorageRangeQuery{ + Address: core2p2p.AdaptAddress(job.path), + Start: &spec.StorageLeafQuery{ + ContractStorageRoot: core2p2p.AdaptHash(job.storageRoot), + + // TODO: Should be address + Key: core2p2p.AdaptFelt(job.startAddress), + }, + }) + } + + var err error + + stateRoot := s.currentGlobalStateRoot + processedJobs := 0 + storage := map[felt.Felt]map[felt.Felt]*felt.Felt{} + totalPath := 0 + maxPerStorageSize := 0 + + s.log.Infow("storage range", + "rootDistance", s.lastBlock.Number-s.startingBlock.Number, + "root", stateRoot.String(), + "requestcount", len(requests), + ) + s.snapServer.GetStorageRange(ctx, &StorageRangeRequest{ + StateRoot: stateRoot, + ChunkPerProof: uint64(storageRangeChunkPerProof), + Queries: requests, + })(func(response *StorageRangeStreamingResult, _err error) bool { + job := jobs[processedJobs] + if !job.path.Equal(response.StorageAddr) { + s.log.Errorw(fmt.Sprintf( + "storage addr differ %s %s %d\n", job.path, response.StorageAddr, workerIdx)) + return false + } + + if response.Range == nil && response.RangeProof == nil { + // State root missing. + return false + } + + // Wait.. why is this needed here? + err = VerifyGlobalStateRoot(stateRoot, response.ClassesRoot, response.ContractsRoot) + if err != nil { + // Root verification failed + return false + } + + // Validate response + paths := make([]*felt.Felt, len(response.Range)) + values := make([]*felt.Felt, len(response.Range)) + + for i, v := range response.Range { + paths[i] = p2p2core.AdaptFelt(v.Key) + values[i] = p2p2core.AdaptFelt(v.Value) + } + + proofs := P2pProofToTrieProofs(response.RangeProof) + hasNext, err := VerifyTrie(job.storageRoot, paths, values, proofs, core.ContractStorageTrieHeight, crypto.Pedersen) + if err != nil { + // It is unclear how to distinguish if the peer is malicious/broken/non-bizantine or the contracts root is outdated. + err = s.queueStorageRefreshJob(ctx, job) + if err != nil { + return false + } + + // Go to next contract + processedJobs++ + return true + } + + if storage[*job.path] == nil { + storage[*job.path] = map[felt.Felt]*felt.Felt{} + } + for i, path := range paths { + storage[*job.path][*path] = values[i] + } + + totalPath += len(paths) + if maxPerStorageSize < len(storage[*job.path]) { + maxPerStorageSize = len(storage[*job.path]) + } + + if totalPath > maxStorageBatchSize || maxPerStorageSize > maxMaxPerStorageSize { + // Only after a certain amount of path, we store it + // so that the storing part is more efficient + storageAddressCount.Observe(float64(len(storage))) + err = s.blockchain.PutStorage(storage) + if err != nil { + s.log.Errorw("error store", "err", err) + return false + } + + totalPath = 0 + maxPerStorageSize = 0 + storage = map[felt.Felt]map[felt.Felt]*felt.Felt{} + } + + if hasNext { + job.startAddress = paths[len(paths)-1] + } else { + processedJobs++ + atomic.AddInt32(&s.storageRangeJobCount, -1) // its... done? + } + + return true + }) + + if err != nil { + s.log.Errorw("Error with storage range", "err", err) + // Well... need to figure out how to determine if its a temporary error or not. + // For sure, the state root can be outdated, so this need to restart + continue + } + + storageAddressCount.Observe(float64(len(storage))) + err = s.blockchain.PutStorage(storage) + if err != nil { + s.log.Errorw("store raw err", "err", err) + return err + } + + // TODO: assign to nil to clear memory + nextjobs = make([]*storageRangeJob, 0) + for i := processedJobs; i < len(jobs); i++ { + unprocessedRequest := jobs[i] + nextjobs = append(nextjobs, unprocessedRequest) + } + } +} + +func (s *SnapSyncher) poolLatestBlock(ctx context.Context) error { + for { + select { + case <-ctx.Done(): + return nil + case <-time.After(JobDuration): + break + case <-s.storageRangeDone: + return nil + } + + newTarget, err := s.getNextStartingBlock(ctx) + if err != nil { + return errors.Join(err, errors.New("error getting current head")) + } + + // TODO: Race issue + if newTarget.Number-s.lastBlock.Number < uint64(maxPivotDistance) { + s.log.Infow("Not updating pivot yet", "lastblock", s.lastBlock.Number, + "newTarget", newTarget.Number, "diff", newTarget.Number-s.lastBlock.Number) + continue + } + + pivotUpdates.Inc() + + s.log.Infow("Switching snap pivot", "hash", newTarget.Hash, "number", newTarget.Number) + s.lastBlock = newTarget.Header + + fmt.Printf("Current state root is %s", s.lastBlock.GlobalStateRoot) + s.currentGlobalStateRoot = s.lastBlock.GlobalStateRoot + } +} + +func (s *SnapSyncher) ApplyStateUpdate(blockNumber uint64) error { + return errors.New("unimplemented") +} + +//nolint:gocyclo +func (s *SnapSyncher) runFetchClassJob(ctx context.Context) error { + keyBatches := make([]*felt.Felt, 0) + for { + requestloop: + for len(keyBatches) < 100 { + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(JobDuration): + // Just request whatever we have + if len(keyBatches) > 0 { + break requestloop + } + s.log.Infow("waiting for more class job", "count", s.storageRangeJobCount) + case key := <-s.classesJob: + if key == nil { + // channel finished. + if len(keyBatches) > 0 { + break requestloop + } else { + // Worker finished + return nil + } + } else { + if key.Equal(&felt.Zero) { + continue + } + + // TODO: Can be done in batches + cls, err := s.blockchain.GetClasses([]*felt.Felt{key}) + if err != nil { + s.log.Errorw("error getting class", "err", err) + return err + } + + if cls[0] == nil { + keyBatches = append(keyBatches, key) + } + } + } + } + + classes, err := s.snapServer.GetClasses(ctx, keyBatches) + if err != nil { + s.log.Errorw("error getting class from outside", "err", err) + return err + } + + processedClasses := map[felt.Felt]bool{} + newClasses := map[felt.Felt]core.Class{} + classHashes := map[felt.Felt]*felt.Felt{} + for i, class := range classes { + if class == nil { + s.log.Infow("class empty", "key", keyBatches[i]) + continue + } + + coreClass := p2p2core.AdaptClass(class) + newClasses[*keyBatches[i]] = coreClass + h, err := coreClass.Hash() + if err != nil { + s.log.Errorw("error hashing class", "err", err) + return err + } + + if !h.Equal(keyBatches[i]) { + s.log.Warnw("invalid classhash", "got", h, "expected", keyBatches[i]) + return errors.New("invalid class hash") + } + + if coreClass.Version() == 1 { + classHashes[*keyBatches[i]] = coreClass.(*core.Cairo1Class).Compiled.Hash() + } + + processedClasses[*keyBatches[i]] = true + } + + if len(newClasses) != 0 { + err = s.blockchain.PutClasses(s.lastBlock.Number, classHashes, newClasses) + if err != nil { + s.log.Errorw("error storing class", "err", err) + return err + } + } else { + s.log.Errorw("Unable to fetch any class from peer") + // TODO: Penalise peer? + } + + newBatch := make([]*felt.Felt, 0) + for _, classHash := range keyBatches { + if _, ok := processedClasses[*classHash]; !ok { + newBatch = append(newBatch, classHash) + } + } + + keyBatches = newBatch + } +} + +//nolint:gocyclo +func (s *SnapSyncher) runStorageRefreshWorker(ctx context.Context) error { + // In ethereum, this is normally done with get tries, but since we don't have that here, we'll have to be + // creative. This does mean that this is impressively inefficient. + var job *storageRangeJob + + for { + if job == nil { + requestloop: + for { + contractDoneChecker := s.contractRangeDone + if s.storageRangeJobCount > 0 { + contractDoneChecker = nil // So that it never complete as there are job to be done + } + + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(JobDuration): + s.log.Infow("waiting for more refresh job", "count", s.storageRangeJobCount) + case <-contractDoneChecker: + // Its done... + return nil + case job = <-s.storageRefreshJob: + break requestloop + } + } + } + + bigIntAdd := job.startAddress.BigInt(&big.Int{}) + bigIntAdd = (&big.Int{}).Add(bigIntAdd, big.NewInt(1)) + elem := fp.NewElement(0) + limitAddr := felt.NewFelt((&elem).SetBigInt(bigIntAdd)) + var err error + + stateRoot := s.currentGlobalStateRoot + s.snapServer.GetContractRange(ctx, &spec.ContractRangeRequest{ + Domain: 0, // What do this do? + StateRoot: core2p2p.AdaptHash(stateRoot), + Start: core2p2p.AdaptAddress(job.startAddress), + End: core2p2p.AdaptAddress(limitAddr), + ChunksPerProof: 10000, + })(func(response *ContractRangeStreamingResult, _err error) bool { + if response.Range == nil && response.RangeProof == nil { + // State root missing. + return false + } + + if len(response.Range) == 0 { + // Unexpected behaviour + return false + } + + err = VerifyGlobalStateRoot(stateRoot, response.ClassesRoot, response.ContractsRoot) + if err != nil { + // Root verification failed + // TODO: Ban peer + return false + } + + paths := make([]*felt.Felt, len(response.Range)) + values := make([]*felt.Felt, len(response.Range)) + + for i, rangeValue := range response.Range { + paths[i] = p2p2core.AdaptAddress(rangeValue.Address) + values[i] = CalculateRangeValueHash(rangeValue) + } + + proofs := P2pProofToTrieProofs(response.RangeProof) + _, err = VerifyTrie(response.ContractsRoot, paths, values, proofs, core.GlobalTrieHeight, crypto.Pedersen) + if err != nil { + // The peer should get penalised in this case + return false + } + + job.storageRoot = p2p2core.AdaptHash(response.Range[0].Storage) + newClass := p2p2core.AdaptHash(response.Range[0].Storage) + if newClass != job.classHash { + err := s.queueClassJob(ctx, newClass) + if err != nil { + return false + } + } + + err = s.queueStorageRangeJobJob(ctx, job) + if err != nil { + return false + } + + job = nil + + return true + }) + + if err != nil { + s.log.Errorw("Error with contract range", "err", err) + // Well... need to figure out how to determine if its a temporary error or not. + // For sure, the state root can be outdated, so this need to restart + continue + } + } +} diff --git a/sync/snapsyncer_test.go b/sync/snapsyncer_test.go new file mode 100644 index 0000000000..b4d5495bd0 --- /dev/null +++ b/sync/snapsyncer_test.go @@ -0,0 +1,384 @@ +package sync + +import ( + "context" + "encoding/hex" + "errors" + "math/big" + "net/http" + "os" + "testing" + "time" + + "github.com/NethermindEth/juno/blockchain" + "github.com/NethermindEth/juno/core" + "github.com/NethermindEth/juno/core/felt" + "github.com/NethermindEth/juno/db" + "github.com/NethermindEth/juno/db/pebble" + "github.com/NethermindEth/juno/encoder" + "github.com/NethermindEth/juno/starknetdata" + "github.com/NethermindEth/juno/utils" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/stretchr/testify/assert" +) + +func hexToFelt(str string) *felt.Felt { + flt := felt.Zero.Clone() + + bint, ok := big.NewInt(0).SetString(str, 16) + if !ok { + panic("set fail") + } + + return flt.SetBigInt(bint) +} + +func V0ClassFromString(key, bytes string) (*felt.Felt, core.Class, error) { + clsHash := hexToFelt(key) + cls := &core.DeclaredClass{} + clsBytes, err := hex.DecodeString(bytes) + if err != nil { + return nil, nil, err + } + err = encoder.Unmarshal(clsBytes, cls) + if err != nil { + return nil, nil, err + } + + h, err := cls.Class.Hash() + if err != nil { + return nil, nil, err + } + + if !h.Equal(clsHash) { + return nil, nil, errors.New("recalculated hash missed") + } + + return clsHash, cls.Class, nil +} + +func V1ClassFromString(key, bytes string) (*felt.Felt, *felt.Felt, core.Class, error) { + clsHash := hexToFelt(key) + cls := &core.DeclaredClass{} + clsBytes, err := hex.DecodeString(bytes) + if err != nil { + return nil, nil, nil, err + } + err = encoder.Unmarshal(clsBytes, cls) + if err != nil { + return nil, nil, nil, err + } + compiledClsHash := cls.Class.(*core.Cairo1Class).Compiled.Hash() + + return clsHash, compiledClsHash, cls.Class, nil +} + +func TestSnapOfflineCopy(t *testing.T) { + scenarios := []struct { + name string + scenario func(t *testing.T, bc *blockchain.Blockchain) error + }{ + { + name: "basic contract", + scenario: func(t *testing.T, bc *blockchain.Blockchain) error { + expectedRoot := hexToFelt("07391bf3b040e9df8ad739f0b0be3aa2d83f6c5260f7b745549be2432122d369") + + key := new(felt.Felt).SetUint64(uint64(12)) + nonce := new(felt.Felt).SetUint64(uint64(1)) + classHash, cls, err := V0ClassFromString("000118cec7463a59c23203e8e4dd7769a914da30c68d7b5ff404531b1e85de9a", "a262417419d22165436c617373da00010005a5634162695908355b7b226d656d62657273223a205b7b226e616d65223a2022746f222c20226f6666736574223a20302c202274797065223a202266656c74227d2c207b226e616d65223a202273656c6563746f72222c20226f6666736574223a20312c202274797065223a202266656c74227d2c207b226e616d65223a2022646174615f6f6666736574222c20226f6666736574223a20322c202274797065223a202266656c74227d2c207b226e616d65223a2022646174615f6c656e222c20226f6666736574223a20332c202274797065223a202266656c74227d5d2c20226e616d65223a20224163636f756e7443616c6c4172726179222c202273697a65223a20342c202274797065223a2022737472756374227d2c207b22696e70757473223a205b7b226e616d65223a20227075626c69634b6579222c202274797065223a202266656c74227d5d2c20226e616d65223a2022636f6e7374727563746f72222c20226f757470757473223a205b5d2c202274797065223a2022636f6e7374727563746f72227d2c207b22696e70757473223a205b5d2c20226e616d65223a20226765745075626c69634b6579222c20226f757470757473223a205b7b226e616d65223a20227075626c69634b6579222c202274797065223a202266656c74227d5d2c202273746174654d75746162696c697479223a202276696577222c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a2022696e746572666163654964222c202274797065223a202266656c74227d5d2c20226e616d65223a2022737570706f727473496e74657266616365222c20226f757470757473223a205b7b226e616d65223a202273756363657373222c202274797065223a202266656c74227d5d2c202273746174654d75746162696c697479223a202276696577222c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a20226e65775075626c69634b6579222c202274797065223a202266656c74227d5d2c20226e616d65223a20227365745075626c69634b6579222c20226f757470757473223a205b5d2c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a202268617368222c202274797065223a202266656c74227d2c207b226e616d65223a20227369676e61747572655f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a20227369676e6174757265222c202274797065223a202266656c742a227d5d2c20226e616d65223a2022697356616c69645369676e6174757265222c20226f757470757473223a205b7b226e616d65223a2022697356616c6964222c202274797065223a202266656c74227d5d2c202273746174654d75746162696c697479223a202276696577222c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a202263616c6c5f61727261795f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a202263616c6c5f6172726179222c202274797065223a20224163636f756e7443616c6c41727261792a227d2c207b226e616d65223a202263616c6c646174615f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a202263616c6c64617461222c202274797065223a202266656c742a227d5d2c20226e616d65223a20225f5f76616c69646174655f5f222c20226f757470757473223a205b5d2c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a2022636c6173735f68617368222c202274797065223a202266656c74227d5d2c20226e616d65223a20225f5f76616c69646174655f6465636c6172655f5f222c20226f757470757473223a205b5d2c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a2022636c6173735f68617368222c202274797065223a202266656c74227d2c207b226e616d65223a202273616c74222c202274797065223a202266656c74227d2c207b226e616d65223a20227075626c69634b6579222c202274797065223a202266656c74227d5d2c20226e616d65223a20225f5f76616c69646174655f6465706c6f795f5f222c20226f757470757473223a205b5d2c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a202263616c6c5f61727261795f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a202263616c6c5f6172726179222c202274797065223a20224163636f756e7443616c6c41727261792a227d2c207b226e616d65223a202263616c6c646174615f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a202263616c6c64617461222c202274797065223a202266656c742a227d5d2c20226e616d65223a20225f5f657865637574655f5f222c20226f757470757473223a205b7b226e616d65223a2022726573706f6e73655f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a2022726573706f6e7365222c202274797065223a202266656c742a227d5d2c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b5d2c20226e616d65223a20224465636c617265642066726f6d20737461726b6e65742d7273207465737420636173652e2054696d657374616d7020286d73293a2031373131353832303039313838222c20226f757470757473223a205b5d2c202274797065223a202266756e6374696f6e227d5d6750726f6772616d793c284834734941414141414141412f2b783943322f634e72726f583948317863576d5864654870455253436b347534447254316c6a487a725764377535744130476a346468437870705a5363356a692f7a334137316d394b416b6b714c473432344b6e4c4f78527678652f4637382b496e383432675442512f73364b5678424437626f504966684544755033683062427774764d5137656d6e386467512b5734446141454336584337542f30742f425a39682f6a3849325050794237706373764a706463516966326a326a624273344e486c4d76736c706237323147383942656c666a6164672b33537849394f796b5657517372414c31726245392f376f41564c686f53437a4c6c69776c507476586c4b4b537134716c48624b5a55744768566554514f4954337a4c4a6b6a4271555551674d616d316c5151714a4f46554a4147415755424b6e337131703668344f7138394e59756e66753270565478646249565955756a734b4b5262386c4e536350334834696d70503555534143575957715559434b624967735169715341774e616e5a456b52644a55686c316b76734e644b61503671515670755a677945506d39516979347773536a424742424e49326f726a637858483530363776344e7531736845395239486b456b7049673631434734524f7563534f6d39704c53716574736d66743869586e3235735564747943434f454c5063317556734a7441774c4e703961567074773546582b6d774e632f342b55513031516576455747735246626f6f6774326f42783759354161674c75636562686434704768634e743377677531546e7173715a7062585974566c3253714972697369642b793276754f53565663574553792b4f7555705546585366756e43463575394c6149754730477754466554355656347a556161526b56546678656d3772506d754a726c597749534668645a446d6c4f5a3630574e6c42625a75485244744b6f586e4a514a715a504a56536855793143574e546757514c546974503247736b6b6c57313770777970614330466d3163582f635856474d6a76792f66364a36736c6551434e373457596b534e6b2b65484e4a446c6953383057665966636d377233384c4553346e4941666876723447517154776a5052495336544f703450434c4c6d6a756e34624f347332494935434547346f4973355a67785361386d51505763554951765067516339354d30584e7651646579693366306f313252705278357a443076464f5334624e2b68415754374845303459387836383370314470525132684a794c3776536d476264664949467a6969495355572b77646f4e4e6369766a4636636b672f70374e6a35544f465542553464767049304e4c4357632f386c7a676762794b6b354b72496d51416d41523750636d5a7264457335454f37526c367834316a49732f73533061666c6452716b437335754370326d423278776335475568334b4a73376c507035656e353963514f6d4a616b7a6e47646771354c667077717a4b4c61514d337252466e63556e6d503230786b6a303178317369622f466b3169534c4a3539676332694375376d713744564d4f3366554536476d487153334d6f5a6d317851757073396c345a5934586869594443396a7978723779344a52574e4e67766e4f55435179635653396e66726a736d586736356a4758656453334575706d6e6c4f677a5a3869455459396756564c6178456846776673396e5a666a6f335044424c4b5633746355333044733667726b514578366b6b594b6a484535386151396c5045665772576e3036676841756e706f544e6d6566625a64315a49586c62342b7644424f7a3571452b702b4758634f6b4c31736a6c6e623833612f756a744a53497871392b756b563251775959737257354e586f4d64586b79573276547132476e6a626f57425853725765726535637757327874562b7437583378642f53324d4b74512b426d4964733539365a54702f61475139647967626459744b6254746637692f2f4d7049316c3250794e39573442355738696973667375746448627379383776634e596d6a324246665a6e4a6a4a502b64764d32564f72396251374b4266675254335034656d61796154546d3132716e7532623264774e36764b587873547849335165426370493042656875386845383472574c3352702f52524f716e656e59734b61744d4a3676713270456b62447377734661336b4f2f687231742f773845787636553463563172752b6e4e374f76356e644244556a357a39586e2f2f38615a4a4d655a39666258384f4f6777474f73756553656a6f7359586d38712b436959357061613030647461614858734d75397459526332395a516e62336e2b664a323368665a78652f7166592f6474336877417a6177686265304939542f6d625a713143327154686b4578654a3053397a524a616643496f50714b597435366139594a5238645371397a354f5552786443737a30694b634b3771707072684c7561734475755333734c56386d3069472f706168464a327a5261665852755130657a3847746f6e37783973394979616e4a78594a6151366277756549706d347a4b38784d352f6e375178436d624d375758585052585437546b6659666e4a52337261627a6b50745a502f4b58466b31554a37442b3769644c4a546453334255783033444c7538457955734d4d7855664e77544c527273625062453533576e4d6d66335a786248792f6f364a4e357a675556746d75646c75346b72426455366c2b786c34304275362f767164317a496b425044515a6b582b4756516b4d69512f6f6b4f702f636f5675566c674e65497731795371727272546932384f59645a3034734257667a62616b323650636e574b7278336479576d6370544c517334784d554357304f6d2b4c68303935574c796647522b64505352395937315a726d6e6a38744c6270654739764456374b307a307137374c7972465a6258315a5939355a667a3149707837342b4e6f2f6c6a734571434d4d354f6c4e6d7742597469467162765231353478317a2f6e766b66306a2b5a7634697a4b4441506b6b39426e412b2b4438496b48666e4845556a482f33486b7278665a515463503747456466666e4e3237773358686b787533746759524b6665497646692b3953474a3776737a674f3569766d7876353677334c7363654a46487a353545547678765342616e2f6a7268346431654f4b745675757373747233652f4657537452797466376b4a70486e66776a434f3763344c65655049322b7a665a6a3966526574487a64484c773177624279746c3875594a656b665831504f325a4a464c50535a477977793772352b2f5a6f434a6e5565507a36344c4578596c4c5077346f2b2f684839356151534c2b47544677712f796244367742332f7a705966502f4958795051564f3454436e51726854426a4d4975566967565a644c61507a77796f432f68366b6f2f485759424f456a632f3331356b735133686d764447674553794d302f713842444c614b6d51454f58564a34684b53612f4b657753374852746a703944704a436d77356566306966705342633579332b4576766561755865652b466978614b54374939554e70486e4a79394b2f2f43712f4d6578555137594a4e4772564938716677744a4a767458794a4b5339324a3858426450313174312b6c524568617169676b4d4b4a455a4856516770696b4c5770746b76367a7557754f6b4446726e65596847784f4435416762654a564a473671556671625749616f6a634c3056744151505446374232363842746b716f6a6630696a2b426a6d4e4362444b436244374a79424f3170463378397949655973444648795650425742597a304372354c5245445175424932706d4b412f52554843446c6a53475830716f695a36525a3352305a41314b644e4d4159656566486144634c6b2b51456c5871464f524d39586e51776f71476c4b6d705a516247683038624e5a525967514a69354c316568582f487634654c7150316739475654586e4a76667559424b76594b495a366363796978413343684e3278365065772f76654c6443363837376950352b6c6a343157326550434d2f324f387654352f4d2f73396e426550357274482b5744444d2f37376c54452f4e705a2f53596639345830316774674931346d78596e46734a506465614b776a672f33723056735a79647049416630782f3372796c3553702f3233384649514c7777734e4c2f4972417a494d786e2b6c5164544c666c386e39797a697649424f666739584c4c784c376d505843786475454334436e3858474b2b4f3346393678416234374e6c374d6a52384d3739694136622f7a635438593050676870526c39393534372f69526552386d4c7259414d7a69752f6766652f67666370377755742f325759476247386432487258665237794437377138634657786976754750512b392f672b31524978666f316c58356c485a79716b5046584137342f4e6e7066414f6d71393858766f57455978694c342b4c4265764f6a6b356a6962346f7847392b72583262567275722b632f2f7a4c6439384e4547454f4559484569494263496c424268507771784576752b395967586e4a2f556c6a4169726c4c746c4a4b722b30524b396b324253646541564a6979507a6f7065484944576c4d55657266796955307054336c6b327a42764658632f2f5671784d4a352f354e6a3979305448536a444e6e7847624474396245505169504346783973357031634765673563777435794759536f7a755a6577326b75306865316b50716438642f356b3231452f6537592b503333334563577362513267424e58352b564c57786866542f34793657516c7970506c61484b547963354e516968466465596e59566d71674b69682b466c523253314b30436665597548477756336f4a593852792b59702f333254524366464f7441397a6964772b356f623562467239794257694672623054317a736e336e35434f4c677555584e7964754e31536c6f695a516646616c5a796538464246575a367771374253557151565579687530746f7068393051436b4561436c69572f61707079486836676b6932362f734e6d774237545630364365497a7a68474d7138473053636f4d6b556d546e426c6b75676b794b4e657a62754f3644463453756d3735522f2f656e794e7473574253332f7a3778313247635249392b736f3655536e3031797a48354e66715353776339465a64334c486e374f4638462f742f59463565464b514675784a4c484b46526947676e7656566e4e45767a2b654934664e326b416a382f446845564c7a326361474366696a464d64573546716a46636d57366c2b57744e70334d396d622b34384b5a74422f4b7533436859335731632b656e6f746362334731486b71766c3333593871336c7a445856654b795671366c7448657275626b372b4352734c70692f38694a56646d756c50474c32737475734c7a3852753576562b6f7369743756636d2f54474932492f6d5974795866615a2b592b70436f38335779786f74756b502f767068453678593548356b55527973772b773270424f4954724a384c614d2b347931395875556d574c4177435a5942692f4c7371507a74354e5433313439686b6a31637344674a51693870344b34334c50773332327a594b6c3155354f2b6472494a353545566674754f4f6a61506b5334375057775665665053314973667972544e767454714e49752b4c4b706f6467483538502b5a4e4e542f6d4b794565746f34554c2f756a5845484654546a3953482f7834767652474b74412b74467441385a6f6e4331492f59677253702f683350697069524c37324468614d48386465636b36796d324b66553559464871727a42704b694d7648304d2f6f36774a36636872643564713566467974334e4237714b6c782b39586a7a4f546e7055356e75784e6571695675336c6e7a523857715574764a754e2b536b79342b4d7050614471735067613068557072362f525a3436674461464b462b69725a655935656363776438662f5131485249482f326246316e483561373473364a62322b634e6d466668424969483132704347394f766251344f697a3856544e7371315237576c4c3231484f597271387231582f476f326b794d706d766e61614e715470754b4163695474776e63466b6457685468586c77424c4b635a314873397973716c426652437a65724d4f5970527239306b69524842766c732f7a7637372b724f704c306639304657775a6830472f394e2b662f663362316b33747864585a36635a4e682f756974486c6d6851535734624c336242464a4a4b4c654f435a74777047506151523332544d313376376b6d656464556b614777622b6f613838303543666f4e2f7453707a31793334354478436857414f747a43626747326377384f304f55657474416c3345526a544e4e64724c773464752b392b46374d5656516d43347050316f3447655850724750764e374a37453748617a6f646e38646f44316d474652474e69744874445931554d4c754a5152566f654d744d4630347278564d7153783235633332784b6e554f79744b4959706f78674668796f577a6876367a634366794d434c79644275337756635a664f75376a39747a647245547475736d7a74562f5a5a64655876496f6c75764e70533077394930424e4d715a6b454c36787a7954437972562b6b314f4b2b716648516f6578576573704a5874783872576d365474705a2f444e676e4166577551687a53372f6137645632705342634d534c6347536c426a7538643855396d32674c7031647575473869714a7541725834492f523457314465572f46574b676c76623953334e7a61334e6d4d5a584f714d6f4932303451365a4466383978744b4b35666762666471577a57546e6b7776726f6c6851504f2b6c314b39466f75434e74302f37706e59395a38697957764e524c63444b56365664523874444d6f754a4f6147515174626f395a777355516f624c2f62304e575166586f3757626f584b305450376a4850784d716d6a70367857505155312f6459533768736456395646423579696861434d617746646c446a2b514d6179684f55763534763947743969774a52316538662b45332f4f36545562515478593962434968734232696a6b7a594c662f464b7035446d634a623949464f4144376a534c7674666c6c30556430495955584753594e6d4a363147466e4d73584f363746527459695858562b4a394f6a38736446512b484b506435747469554a7435565048526d57625842524b597950383245696c754e743354685a65346f6c764f336549754e73634c436c7a4f436c2f574c69706c765057576478756d6f37566c464444326334433766614f6c7254643159464c57694676634d504c5279787853396e322b6e6978506f4e4a486251516b3271756f672b494c736652774e4854306345332b4c49336f50723357457472304e52746436615533545841466b667a394659353668393431493730456246476273654a7062695a3151465a7a5072364f302b6b3155656943554e6f6e4435792f7178784d4165724c36794e3670336f67695156324372474957644c54326a486e4259524171684f657835754578456170387567784c732f704d62724a2b2b62335576612f5a696d6a534749616e35675a3178714e766d6b6671485673354a39674b54524c517a307259674d30326431677630694d734f31452f664e49306837424f55326a774741717636674d436f6c553979334e2b43337542444f3572396769777366746f6744474768316b6251746d6659566b57486169446b384138386654476974342f70554f67424a574764566461564d59742f57794f2f46735942435830454856424644314e47543077564f77685246653350476b504d666149776a4f32363649456d595930334c35597969717a35734f6b69785074774e58645a5574466149577731566739586772696164696e6b6753664e514c77654c5174466d79516454454f3468717476596f4a7978375473736462653859634435306b67694e416d337651324f4761564a3071316d776d50316b6e563434656f51463463616d734c366f456c4575706268794e74615638537a6e485a5371475a7a386c465042494a793547743041753472376733774e4d49685442622f4268413958517763494778554842794150586b73374f67544a596f6635486441467248436f5835525357325361674156477165506e4d4f4c65354d7630305a32656e5a426b676865736477794c583743424c57766f64586b6448424c5a4b6a69546133446738595a68485372717668677a5954394a357172336a62554e6a675a7732315a6734494664656164714c31447147684a386f6d6e45416a6c7a4c5052676279767a484f497154466d506c6e754f595470365a4c50496370475a5a3944774b654964454a48466159597936477061735362314f3546683366596e76685157563053677479762b5049674a694679784663395176426c7734675130473133365371496b306f566e625052314f6e623552474e3054494f494d34785a4c4830435754786b4576767065374d5736324b4c326d4b376d3252446d32687a324156704b4c42586e6f416a76366b366576675a316b4b4a4d716335616648494f76345a627538785641736f2f5744577a6c584c316c6e66315573315a5449776b6268484757307654436630544743676c624e39796e7444366c46664d725877632b7378303242446d6368425074672f45595874564e48384537456b345432346d366464626a3634735a7374647a3544476a7239686c4e58474e3842522f574a436c6243355547572b694865536732304b4a7961743176495a78453534754976464e3178446d32595a797146796a476148674e78444d4b666b393768713655614455594d6866554d2f6d632f3974783441504867557470774c5448684575524d736b713434346c6274356d356e366f376d56435237667a72474d6134304e356b43624a455271494e446957506f6a50784c394d39445736697669476a714a4c58394b35366d2b676e79534e79596a795673472f5765566244456a62483136507338594b6d6a476d32414c54554f4b6d62394677624938305852724d746850634e3575566b7433554b343071726d6d734d38362f316e4c6a64687373306c364f61324d625a6173643050344d35305771436b4348627869412b6b786378484d2b61314a316e76704f6f73786631706b39634169597845664648536b39517530504f7366357031686253732b443144373438716d7969566a374f714150346a4e7847452b5a55385369367741396c687676496538766d3072636f4e322b69446766324979303342613255646262416133724445383330486149707970394f6d7834414f6f334f3159526f61367a51565878543248633964746870545774646a647356615753745869697a6c5a736537364453493665466632334c3472562f666b624264506d64633263514c332f545538717335664b5a42584a6669376e6c615671764838567a492f304333546962474c71544b494b33317373716c56457a76474e6f394b494a69704e5a724d444e6656555a356a30716d6f625a504f7970733651336e62665534523039595a314a564830627142706a4f5574784650623172305833794d4a6a35752b58777a53346d4844646653516c5472646c463057782f3230744336395364615264386334594c5251467a467655664538324a7a4f3836536f4e486d654861697050552b4753612f6e6159503874674b526c742b65747852622b4b6632576f585a753176376c4c386c71775a4369374d6f4958364b67714b425335327148495957736e626b3545364d637943654e69655734644c6b785371774768354136424d346e6157584e6c6c3666523448356a656e4a792f4150533535636f5261335679396430783945647941492b39447a7372504d3852314f68757978374c4f4d2b6a6e2f4f6e30346d596d4d59767a39587056444a4b6673674f6f6e7079666e70316476627538646339663830696f6a58314d676c57634830377068556e4d6861464177657a3644424938696f416443486e387a633169395a6c6f515a496e3576623639504c6d394f7a322f4f72532f58563266584e2b64616b734652347346597265536474444e6b5965316273675442416d45746765387848626b664934766456717a54334c6f414e6a396e3478536835626c677a34367a434a504439527a4458724d4f527057415a687675542b77487a662b794374376a487a4e7965374c6f496d4f486d43376c69536659334549735669517655473351596752576f4b3665716770776c4b6a614c6c7876584368627552556457493351567877714b344155474e676f6b764e2b37464838547569726c5a5269444f2f6f4f58334c762b7736593657683731336f3954694465724946486874547053487531484667584c4c323765694656766b524d6a596563524f6b437030355463373843346a394a4267757532656d45506b396f5663312f50666a7039643348726e72352b63333770586c39647a4754584633314a31757a6d35757a713876623636714c4d6c6b7241434a6945517475303644674d3156527743357336434e69576a51572b394f6a4e304344475464676d77596841697a6a386d77446c594c2b5a335a362b50723039625446676d545a463143456163467a507a6d626e763836756d7a6967435947446f47574e6d654a364a7279566b596b74674b457a446a4246304a3164766e737a757a373938574c573168396f49575253694d646a61594b47324b6255786e536b2b715367753259595968505a44674a4541343675475461784361684a7942677475767a31394f4c3864524f7968527a4c495251354934522f64656d574b76726a3665335a4c7955627239326232635873375062717573344d784a6a61784c62354a77374a6f7578485a6746694f6841544f4d49356453324d2b6b394f45674c3937767a79316e62666e50366a727261595078734336344a74305661696357345962456439566e5367374a3653414e7a2b61716f7367416b49374e6c50325a372b4e31686a464d416a57314473576a766e4a6343543034734c392b7079566f66304177534551704236435141784e5446466a6f3354774977416443787341684d35674a725541734243474750627472464e4b48535179512b75413154737337413558467854462b7132494448734c705159456c583851624655713743666859757767392f6462392f386e43304176776758586a2b376e396452382f3275667272732f6662723668394b69306c78356c3974526b35454655537a5730617852596235376d5964684d6e4a7a482b622f6d2f47304c2b47424338443745464d7a4d664755614d366a3958525675614d6a70757a484b433452326e5249754e456573726c69687254714c68506262635269783958676e32777733743059697a2b4c53766d6a5a52544855697248332f7a6d436a6156313571644f504553316764793033364b452f3948704d57416c736267756f474e64456d364279347546304d6b546e43544e3675597859733175464946576943306167456d774a30775834445537386943423855496f536b6f67796a6449454c58567762524767646f524863665449466c6544746b74574337754e632f427537624777636533644d563539704c784f3577525632642f4c6a7864585a333977302b5a664f6a67734947382f2f77425a7547353738504456492b2b6674374d59397633542f666e56644c7a625938706c76412f5454727774345a503174646e5a322b6a66333750547436646e353754394c336d2f304d6c39672b656e647859563766586f375339466b736d3455412f6c466f704749327678305646546b38647a63706a685331584e2f6d6c3363336a524b49574f78544c537a4b304742396e316547647a3545585268364b356b2f486d3271564d624f356151387543737a3058506a754357666c45517149346553306d527558694c52625a684e624444796b7436386d6f6537343864554931554676716f6e644174334c4730547246524b59652b2f4668504748334d6b6e4c6357505331514e703550345638534f6241485533712b704e5555766370477a45576136466e6273512b7369686d4c67735867536354765575483341466f4e483168484e7946624f4575676f39757842356b2f57527276444a4264515634326c7173574c4c3463764130506758774235626c31616d372b65583870306153596c6f413263676b784548414d57324c6d4a594e6251524e44444841467157556d4b426a44314b656743644a4a2b6f6b374b5574614c696c596837634257467938754f70564e747342634275517936444d5a4b4d344f343854457a4a74536b50516d4e567567444343394946464337744c644265796e70562f71375033422b76336c31796d3232484f716d325938644e306c4f73426d723477346774486e3232474b38744c56422f567256704d497231795177666c737a535630336850612b46745a663972717234746974433561586b53504e35777556736c59774479426835386175616b314271556f6777736a41325459494968546145446b5a4549516c73347072646e745a786a515a35436571314a57416847396870466f7363514e4a38466d426745384276524a5043424c7646424169314562574a4137464b6461754a43645635636b774c32644345304c524e4378424348597763322b356f6535504239426230546a327962594951635279467a6f4d6d70683770565a564d49646474597471623947356d5a322f647439666e623262754c2b632f2f7a4b553252505470425942314449684a546143304a4c723168716934654c713738496b4947705245316b6d744d634c50435068657661473078356f49355846537761662b62573857447044623266474b6e36532b62576466386e636f544a61647374646d6b484f39727a6b7a514a6371434f334355737850466e326e4f4c6d5a6336712b73544c6e525870436d4c33337977612b44696c612f7779594b7646466f513645654536584c44457a626b624a356f474b485761486b735a75772b50717a486971515053515644384c356c4e69423643556b447142425566676f78566e796f5952574a795145396453436d6f655070346b524e537046556a685045576a4b51416a7159416a7152413571536e726b52754641573176475145485673346f366735794369556b2f6255612b4f63696f4f4c524c76763774546457375851504a364d63766b387873756d4d4d5a5438765365746b724d67657a364e4f695362786e755844714d492b52796444693656413548465370476836524c355a42556f574a30574c7055446b7556617853656344396a523857544c636f714a427869564e795274393864307962794a36755a633268346f74536754636b366b5633323149677068342b6d4a396335395256716f624d564d4c70495374626a7a68756f553161424e7072412f47485a565659306b615670476773486a6c626b774b363334765343486b3034383131764955396947636d4c34547249554e61336e41777465735a384e325233636e332b4c556f4b434b4f4a475873795546324e684134476b694876365974384f316f4f65495756422b347855696f676a4b616b6242564d316756336f2b5455686a616577494f716a764c6f4f6f5169615a57757739687972787a6a34363243685a6377742b517a3439746c4b2f6241565057744b4f6a32517835502b6450576e5265426e3768656471664e796576415430367a667772767a48554d62327a4e79587a4774596e595235647a5248425053302f4950764647614f2b4779676f4939652f4d6c616f696a532f4e6865657173584f3668322b6d6d5839796b2f3579396868397a4b56624471336662434d792f4f5430347530763956345671523649426a435a73305934517a57644d6c4b48334772472b634769784945416d686851597072556f52695a4671474f36546732784a6851684b434673474e5347324948453873424e7258544879317145644e474e734359514e4f47556c2b5331636e366558627031672b3173616c46546366434546416241456f4a73544331694f5859447157576952437754574935694e6f4555477961794849734730494d5452745a4e67515532635332415345416a5350726e33563977496751516830454b4d5451746b3167457874546a444731495849733238516d734b434667415653796c4d6562476861794c4653455347454d63493267526151366869716b3656306d6b305069456b30376572363961782b754e4950446a477a646851434d51594f67734347446f41494f7a6243304d514f784a62745348333457456335346e68754966696144732f4a7577442b3965677433496a4677654a5275736a534843376e4d427665566c7942326d353630674d6c7671702b415a316449624f664536733777417764647941716334466a453271482b497633493866692f636978654439794c4e3650484176324936657634767172375a4e6864712b532b71756b353156616635554f713645747059626458315565774f5a564e3348373353446f706d4f363432576c53546d6b5478497a446e57662f642b48532b2b7836674c6c2b756f4279746e33596f5332546963382b684375503457757433483965792b385930657978785679454d706c374a3041476a375a4533624a38346e44496f39697056527945464244424c3133754b686672434e425864347266665872374e704637625a7462454c69514f6a594a6a5164516b786932525a4d4d335762554949524a6852595567764241524a4d54756334746977437147506246447555596b6f77535663344e7161325a574a67645234384c4547432f767959683056546c7377443759315355652b497232725a7757564c467248515a356c7a716271646b76616a33337776546c34734e385a666a52632f574e386446326479766b2b486578733369547a2f517844657564734c594f366939654f6d5342703232763731613956546266484b536d492b53684a7a665a49776e316f53504f65694b70636d4c47315377694f6b64467947515671446e34482f6251762f766648584e4958653372676e6a734c574e4248786832446a73732f2b366e48425847385876577666456132384f56754e676a783348344c774d613569674d425552704530636730493063544a526a49323255696558624b5236456f326549414f4a396c4939684a6d6b2b6e43624449717a435a71595461337550467831744561585a4a5263545a5269374e396f70414a7446704555653837465033476350655a6f395232774259685a77306f332f445665346d4f4b415671745966476143554b47706349356171423270665361346c4c44597a7963596b4c344b446a55704e6935626a55432b6a4a34314b5475733634314e525579636a55784b4d784d6a56427930576d396d684a64357a5a33506a49424b453266317a6852693430745566726b34564d614a7047467545364b587556633270726c78734e72776871427778756f55686378693843574e4b31746f633254352b4a42772b6b336c33564845652b6947664e5878612b394661355561584f6e5a6f443767616861554f376a75466976643738464f55307156433447372b66655651506b5457794a316a4e314f4472696859316f50343654494c776b626e2b65764d6c434f38557036774652744a6e6772624c3944595376724c714b6b305656316e6a7072527165546d6b492b56594e2f5545693947786f7337476572335a655864624a6b4b496e4667764b6c7642302b38505a324e663263747631703932537a4c6c745a7a55496970466563635331397449344b73664e4f6c746c44412b31516d584c654946304b2b383843346476776c574c44705a42664e784975686951774d6c616a4b70666f2f37684f3277597a344c4874555675304f387030364248634a395831492f54464339505668552b745678616f6a33306a6778346e4d3174572f55526e795970765931576731682f4469585270694f55554d59784f356e6478323676727a32314965716f592b59762f3749797076716846487668716d6837626c67757967466a463859443247545779754c5157766b564e7a6265506f2b2b33696372774b2f6665465058344b3176613837456d2b683349364a78586f704b396d5a4a5a57644455704e61566b75423755784a2f6d374175565274516a792f59694b366942662b68664b67796731725a30483864536e52597357374542716d397675306d58336170522b6432776f34684e6177494b755a67365958565845372b61675754634855715a737541476b525a6630796e70776770752b543476575649484b31712b376c5543716c72395079576e30756c57672b69516e5663775a4c626d796935703730386b455a2b2b572b4136675862386b355a665469352f633745365247765051746978434c5174516b77494859306a6b767359727762636854374458332f7863514654484f5a385a56484f48565846626b55677564782f6333552b3830567853573553786774444e646f4156713244623453704b732b635358415074666f385261694a2f67704f4453684c4b572b673871584e756d6e6659655a4a48325453786a377041542f4c2b764362752f64376556324c66622f6d33697657705373416c44646d4f754b5371373762536c56424f6630646369576b6633344e6c2f77705a736b32726b6e586b3362475430396576727a6d4e634f713371316e32304e3055586153384f66324865334e3764583336383877397635323961562b73686762446352667377757368444e78356f4e7236566f36574666575832506457712f6a6b4c4533767a713475623639507a3237646d396e46374f7a327176346c4f5149324e6845314c596f5241685a466c6b326741366c743257686f3564754e313175747a745a686d734432666851744271445a2f63582b39636947392f6b6c73567758554c2f6d642b3976316d48637147653150355756526c474172575a43644367546b714a2b724b784c4f4d303955375a6966724957374c51377a72516c672b643669305655486a456a5575597243353475483256507353396c4a6c306f75595667785436644c6b6656422f412f6f503565376b596e4f5a305950334d466f4a61314a423153365a6d2b5974414533544764764c79655863782b50723264755a6e503472737143416e466a675549704c614a414c47524352314349616e3371456d3571693365432b6a2b636e72352b6d4a3233594864524e5143454e73414f52545a466b5957785a6a59466e4a73596c4948517073514374485147536b396c4c793975506f6e487a6e464343434c324468645167353950396d4e675731573679394b716c594d6e637756352f43486e5844374541494a34427a333636677262493169645a6c7163376b724c343764657938575849317a764c516265367445334f476d7170644b624232356973365841304851455765375a716e3433475730667469745377635074716a4d2f654239504b494b4e5762792b5636375033373242393674534c74646637394f4e414749614d54336371314e33574b706e306f6e6c6a4a336e30756e6c44585033707a6675724e665a3563644b544e454e7144414d514845474a676d70705a447261477a6c7271785051544a37435072507a3149595052592f2f474266556c5872714777707151444246556a74645a55453176672b79375a33484e57397650733173327631373538392b6248726977415770594e624163435970735949784d41614a6b32426369304d45434132744c727854622b322f4d3373357662307a64764f784b5237476f345946495445555349685731694934695269536d3171594d6f70685244694a55546b5a53534e41756258627670596e313263384d6e4a4b55697a622b6f44553348424e53324c4174687977596d674a526747774a45724b4872362f71704b4a6575765851514242334c775359424e7343574179304b55336b3446454154554543514252316b49774147372b72724a655a6d39762f657a5337506871514373594f6741327a5478445a3048456942436242704132776a427a735767656c504b545534565a7778424e332b777a322f2f4f6d7167777754556f41633077454957424169434579415276462f2b772f3335767a6e793950626439657a66754e414e73484974414367466f5555456f516f425941366733635a64754e6e79592b7274662f68386a48316345716573674669736b79326a6d63346f3231375741556b6e4d785766536e47353243387a4c566b756a4a4e4a374c693038456950352b6270362b34345535373938766e6266444134735237324978696351646c6375505a6f707259666970344a6a47684668396135483934687453576f795a472b38777071576e313368672b3831597246703157466d594b664e614254476c504e557a546d564d446a573572346e4b68512f59485a55743849657068733650516b4c306b56326251786d315235426872536730776b7870544864654535745245704e32672b4a7a6f6d5950444d716f4f5565706956576346547866504e2b6b456850376f4d4e57434d3656784e5a464e5a313174544c724e7134735854664e77554162574b5531747a504a4e4c43376665786f62752f313848693758716b77576f36653070787a4664465a55777464744f33573652386e336f4f796b49612b526a50467449766e73427156576a6c4f6748492f6b5a7a3739684e2f55506f4254346e3048596c724c32654b5a306e777153446732704c364a78756467764d77507a4a726134745042596b65733258377a30647244366a74416f716277652b716b75546a2f386672302b703935493831515634746c456d5361466b5955573943784961595559476f526841473148597770686735434e72497341676b6d796e744b4e614c3475306e49704151675168334c5242524153436c794d4352457562586e497068485876516c5851637261555a312f475465706f4c6b2b625567636f67664b65676e3749623554326739764a3639765467396d376c6e463664642b3563495977417359434a494c637330715930524a51425332795245656276796d6d31576e732f4f306c6c5255704561674830726878613366444f37664f322b6d6433636e50343863322b7655732f63345a464e414b6b44624570734535695736546941516f66593245475753536b463674756f5a62503739657a30646363574c6743324134686a6d68536b73303441734243773051673358434c392b2f5835626366474d6349327367476b47434362576743446c46644343484b6f7163347343786476386f4d4a62746358384f5a4c7242774a4f6b434e31634e6b4c6430667666472b724e626567754e70657678544f616831474144664f3330766478424374395479547947756d6264516b33706c2f47547874344a6b4f5036325a53794c515775657a364639704a7831685638707464626a586a6c534869304d2f6c7141632f7673704975646771532f5230482f7958686941505936745a5776335455666a6a6451743143534537386d393546466364352f4b6967673331382f686f6d722f41334d672f665a58544a783939367a4e7532374e4a432f4e753372754534694c34793950456c755a30383956776e3639313451757347695071446e5173467748666f4e776e54634b646735382b6e2f33303559686a632f72364639554c7a4136557869574161505a5a49426f3674396647414e314e633333723047456c673537544d74715974503947416d4658424e5635735045543279586c6670745535673939464b31633846586d5a486b52776278624f5878636b6b346f637643564b6a6675715349494b6d7a4d664e626856617835783148763353666559374c55392b45546e3642585764676a523443424939727379694f41346f6472704d70775476574f4a79756b7a796f34324256762f61527158755a4c7467715a2b484c6f4e7174466361676e6b59726f6c445a62642f71722f34636e632f32306950784346694172664577544c574e7732416c4852514a756545396562525643494f79757879554b625a3661444d387051325151646c616e645176495170703770395965356f463956414e744a4a6361464e3561616179505134716c366f422b53716d6e54324f4b764771357264565a4f5171527857453438576c395548564e5a704364796d4b754b307243366e5a51467454737653376253717652373572616e3630366b43787a675856514d796b5763716357687853467867682b4f4853764b3633552f78786b7444754f716c5a5846584a5734696c315343312b474a654c416b485244526c4458524c6764453947564e564b63444b73363563714e79647964336c355a57443152466f753643326c4361567972757658323052744a6f6c39554e37544238566f322b6271655661626d2b524b6d4764514a7656494d2f316831314170503052356b4244687a4e4c4f4b50634764435a4866364979767a5236617750384a542b4b4e50323132796a467a63766f4a636830504b73497a33534255775931775366356474307533574f6766615042674833474735734a78414c666354434f4b613048506c43485335726a593053642b4675322f73786a4b2b693354354c6b773766526557394631457a48643972664c71506e696864316363414c4448712f35324679566f766b5478324e6a583573623078636e4a43776c54422b624a7257663670553733484d68634b324572774a63784c78583457416438434c756e57455a416a676f4347516c3149356a7135684168444b4e7532424443384952583445783076626f51686e47586c6d633955306b5342665048704979475a6672426f6d676475655831596c554354764d757135644776674e6d424c455272684d6a755139696f2b6a414f6971796f53512f525234364b57705748696f506e56545a6c3676314a77363146526271314e666a5071724839324352317a534c4c6a41577838463878647a5958323979786f37574778622b6d323032624257454a77575a4a36763838346555334c37665430353362496d38567834697667355858397959725a5a48373750706c4a4474676d3069356e734a57786a4a5a364e73684b754c4657476e4b6c5a4548486d786f7070534f4163745666615a2b59384a6b78646d78466959524636595a4372626c434a464e536c5361367755726563697876665a436e332b654666755a6f5350713958582f776b4141502f2f304551477335726f4151413d6945787465726e616c7388a2664f6666736574841bffffffffffffc6611bffffffffffffffff1bffffffffffffffff1b07fffffffffc2c706853656c6563746f72841b30b9224cd18a4e7c1bc71f0c828480ecb01b8de0747c8cfdb4821b04c9700ad999c431a2664f6666736574841bffffffffffffa8a11bffffffffffffffff1bffffffffffffffff1b07fffffffffa32b06853656c6563746f72841ba6951e73bb6dfe241b05e5ff539dbeebe71bdd254888370955b41b043ef1349908885ea2664f6666736574841bffffffffffffbb811bffffffffffffffff1bffffffffffffffff1b07fffffffffb73906853656c6563746f72841bb22ea4840c873b8b1baa7fdf9303e1d7731b704e592f223c3de61b00bda65049efea59a2664f6666736574841bffffffffffffcde11bffffffffffffffff1bffffffffffffffff1b07fffffffffcabf06853656c6563746f72841b279f5f4dc8b4c7b31b6ac884a41a1b5bd71bc1307f4321f00b881b04bc8012967069c6a2664f6666736574841bffffffffffffc1811bffffffffffffffff1bffffffffffffffff1b07fffffffffbd9906853656c6563746f72841bc735e82ed1e23e011b9ee8c8991bbc1d151bacb56cf03bbaac5a1b00186ed02a71bd65a2664f6666736574841bffffffffffffb4211bffffffffffffffff1bffffffffffffffff1b07fffffffffaf6306853656c6563746f72841b04b9244ab52a4b811bffdbe496a2c48be11b8a5d0118067bf13c1b07102404b615fdd5a2664f6666736574841bffffffffffffc9c11bffffffffffffffff1bffffffffffffffff1b07fffffffffc65d06853656c6563746f72841bf6c53d175ebbcd9f1bdd07ee6aca2299561bd457faeea1e288671b0754ebc0b261e0b1a2664f6666736574841bffffffffffffaf611bffffffffffffffff1bffffffffffffffff1b07fffffffffaa5706853656c6563746f72841b4298606763e030411ba7525ed75dc70c951b01c0a0b4d48ac5061b0624e8d006b62cda6a4c3148616e646c657273806c436f6e7374727563746f727381a2664f6666736574841bffffffffffffd2411bffffffffffffffff1bffffffffffffffff1b07fffffffffcf6506853656c6563746f72841bd1c59afcd3f9742d1b083befda3709a3ed1b3142bfd1003b62e21b041f80cbf54be289") + assert.NoError(t, err) + + return bc.Store( + &core.Block{ + Header: &core.Header{ + Hash: &felt.Zero, + ParentHash: &felt.Zero, + GlobalStateRoot: expectedRoot, + }, + }, + nil, + &core.StateUpdate{ + NewRoot: expectedRoot, + OldRoot: &felt.Zero, + StateDiff: &core.StateDiff{ + StorageDiffs: nil, + Nonces: map[felt.Felt]*felt.Felt{ + *key: nonce, + }, + DeployedContracts: map[felt.Felt]*felt.Felt{ + *key: classHash, + }, + DeclaredV0Classes: nil, + DeclaredV1Classes: nil, + ReplacedClasses: nil, + }, + }, + map[felt.Felt]core.Class{ + *classHash: cls, + }) + }, + }, + { + name: "basic with storage", + scenario: func(t *testing.T, bc *blockchain.Blockchain) error { + expectedStateRoot := hexToFelt("05e69846cb070d495023a6700872cce39a9be8457b2243fcf8d87d8edf7c8784") + + key := new(felt.Felt).SetUint64(uint64(12)) + nonce := new(felt.Felt).SetUint64(uint64(1)) + classHash, cls, err := V0ClassFromString("000118cec7463a59c23203e8e4dd7769a914da30c68d7b5ff404531b1e85de9a", "a262417419d22165436c617373da00010005a5634162695908355b7b226d656d62657273223a205b7b226e616d65223a2022746f222c20226f6666736574223a20302c202274797065223a202266656c74227d2c207b226e616d65223a202273656c6563746f72222c20226f6666736574223a20312c202274797065223a202266656c74227d2c207b226e616d65223a2022646174615f6f6666736574222c20226f6666736574223a20322c202274797065223a202266656c74227d2c207b226e616d65223a2022646174615f6c656e222c20226f6666736574223a20332c202274797065223a202266656c74227d5d2c20226e616d65223a20224163636f756e7443616c6c4172726179222c202273697a65223a20342c202274797065223a2022737472756374227d2c207b22696e70757473223a205b7b226e616d65223a20227075626c69634b6579222c202274797065223a202266656c74227d5d2c20226e616d65223a2022636f6e7374727563746f72222c20226f757470757473223a205b5d2c202274797065223a2022636f6e7374727563746f72227d2c207b22696e70757473223a205b5d2c20226e616d65223a20226765745075626c69634b6579222c20226f757470757473223a205b7b226e616d65223a20227075626c69634b6579222c202274797065223a202266656c74227d5d2c202273746174654d75746162696c697479223a202276696577222c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a2022696e746572666163654964222c202274797065223a202266656c74227d5d2c20226e616d65223a2022737570706f727473496e74657266616365222c20226f757470757473223a205b7b226e616d65223a202273756363657373222c202274797065223a202266656c74227d5d2c202273746174654d75746162696c697479223a202276696577222c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a20226e65775075626c69634b6579222c202274797065223a202266656c74227d5d2c20226e616d65223a20227365745075626c69634b6579222c20226f757470757473223a205b5d2c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a202268617368222c202274797065223a202266656c74227d2c207b226e616d65223a20227369676e61747572655f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a20227369676e6174757265222c202274797065223a202266656c742a227d5d2c20226e616d65223a2022697356616c69645369676e6174757265222c20226f757470757473223a205b7b226e616d65223a2022697356616c6964222c202274797065223a202266656c74227d5d2c202273746174654d75746162696c697479223a202276696577222c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a202263616c6c5f61727261795f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a202263616c6c5f6172726179222c202274797065223a20224163636f756e7443616c6c41727261792a227d2c207b226e616d65223a202263616c6c646174615f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a202263616c6c64617461222c202274797065223a202266656c742a227d5d2c20226e616d65223a20225f5f76616c69646174655f5f222c20226f757470757473223a205b5d2c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a2022636c6173735f68617368222c202274797065223a202266656c74227d5d2c20226e616d65223a20225f5f76616c69646174655f6465636c6172655f5f222c20226f757470757473223a205b5d2c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a2022636c6173735f68617368222c202274797065223a202266656c74227d2c207b226e616d65223a202273616c74222c202274797065223a202266656c74227d2c207b226e616d65223a20227075626c69634b6579222c202274797065223a202266656c74227d5d2c20226e616d65223a20225f5f76616c69646174655f6465706c6f795f5f222c20226f757470757473223a205b5d2c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b7b226e616d65223a202263616c6c5f61727261795f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a202263616c6c5f6172726179222c202274797065223a20224163636f756e7443616c6c41727261792a227d2c207b226e616d65223a202263616c6c646174615f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a202263616c6c64617461222c202274797065223a202266656c742a227d5d2c20226e616d65223a20225f5f657865637574655f5f222c20226f757470757473223a205b7b226e616d65223a2022726573706f6e73655f6c656e222c202274797065223a202266656c74227d2c207b226e616d65223a2022726573706f6e7365222c202274797065223a202266656c742a227d5d2c202274797065223a202266756e6374696f6e227d2c207b22696e70757473223a205b5d2c20226e616d65223a20224465636c617265642066726f6d20737461726b6e65742d7273207465737420636173652e2054696d657374616d7020286d73293a2031373131353832303039313838222c20226f757470757473223a205b5d2c202274797065223a202266756e6374696f6e227d5d6750726f6772616d793c284834734941414141414141412f2b783943322f634e72726f583948317863576d5864654870455253436b347534447254316c6a487a725764377535744130476a346468437870705a5363356a692f7a334137316d394b416b6b714c473432344b6e4c4f78527678652f4637382b496e383432675442512f73364b5678424437626f504966684544755033683062427774764d5137656d6e386467512b5734446141454336584337542f30742f425a39682f6a3849325050794237706373764a706463516966326a326a624273344e486c4d76736c706237323147383942656c666a6164672b33537849394f796b5657517372414c31726245392f376f41564c686f53437a4c6c69776c507476586c4b4b537134716c48624b5a55744768566554514f4954337a4c4a6b6a4271555551674d616d316c5151714a4f46554a4147415755424b6e337131703668344f7138394e59756e66753270565478646249565955756a734b4b5262386c4e536350334834696d70503555534143575957715559434b624967735169715341774e616e5a456b52644a55686c316b76734e644b61503671515670755a677945506d39516979347773536a424742424e49326f726a637858483530363776344e7531736845395239486b456b7049673631434734524f7563534f6d39704c53716574736d66743869586e3235735564747943434f454c5063317556734a7441774c4e703961567074773546582b6d774e632f342b55513031516576455747735246626f6f6774326f42783759354161674c75636562686434704768634e743377677531546e7173715a7062585974566c3253714972697369642b793276754f53565663574553792b4f7555705546585366756e43463575394c6149754730477754466554355656347a556161526b56546678656d3772506d754a726c597749534668645a446d6c4f5a3630574e6c42625a75485244744b6f586e4a514a715a504a56536855793143574e546757514c546974503247736b6b6c57313770777970614330466d3163582f635856474d6a76792f66364a36736c6551434e373457596b534e6b2b65484e4a446c6953383057665966636d377233384c4553346e4941666876723447517154776a5052495336544f703450434c4c6d6a756e34624f347332494935434547346f4973355a67785361386d51505763554951765067516339354d30584e7651646579693366306f313252705278357a443076464f5334624e2b68415754374845303459387836383370314470525132684a794c3776536d476264664949467a6969495355572b77646f4e4e6369766a4636636b672f70374e6a35544f465542553464767049304e4c4357632f386c7a676762794b6b354b72496d51416d41523750636d5a7264457335454f37526c367834316a49732f73533061666c6452716b437335754370326d423278776335475568334b4a73376c507035656e353963514f6d4a616b7a6e47646771354c667077717a4b4c61514d337252466e63556e6d503230786b6a303178317369622f466b3169534c4a3539676332694375376d713744564d4f3366554536476d487153334d6f5a6d317851757073396c345a5934586869594443396a7978723779344a52574e4e67766e4f55435179635653396e66726a736d586736356a4758656453334575706d6e6c4f677a5a3869455459396756564c6178456846776673396e5a666a6f335044424c4b5633746355333044733667726b514578366b6b594b6a484535386151396c5045665772576e3036676841756e706f544e6d6566625a64315a49586c62342b7644424f7a3571452b702b4758634f6b4c31736a6c6e623833612f756a744a53497871392b756b563251775959737257354e586f4d64586b79573276547132476e6a626f57425853725765726535637757327874562b7437583378642f53324d4b74512b426d4964733539365a54702f61475139647967626459744b6254746637692f2f4d7049316c3250794e39573442355738696973667375746448627379383776634e596d6a324246665a6e4a6a4a502b64764d32564f72396251374b4266675254335034656d61796154546d3132716e7532623264774e36764b587873547849335165426370493042656875386845383472574c3352702f52524f716e656e59734b61744d4a3676713270456b62447377734661336b4f2f687231742f773845787636553463563172752b6e4e374f76356e644244556a357a39586e2f2f38615a4a4d655a39666258384f4f6777474f73756553656a6f7359586d38712b436959357061613030647461614858734d75397459526332395a516e62336e2b664a323368665a78652f7166592f6474336877417a6177686265304939542f6d625a713143327154686b4578654a3053397a524a616643496f50714b597435366139594a5238645371397a354f5552786443737a30694b634b3771707072684c7561734475755333734c56386d3069472f706168464a327a5261665852755130657a3847746f6e37783973394979616e4a78594a6151366277756549706d347a4b38784d352f6e375178436d624d375758585052585437546b6659666e4a52337261627a6b50745a502f4b58466b31554a37442b3769644c4a546453334255783033444c7538457955734d4d7855664e77544c527273625062453533576e4d6d66335a786248792f6f364a4e357a675556746d75646c75346b72426455366c2b786c34304275362f767164317a496b425044515a6b582b4756516b4d69512f6f6b4f702f636f5675566c674e65497731795371727272546932384f59645a3034734257667a62616b323650636e574b7278336479576d6370544c517334784d554357304f6d2b4c68303935574c796647522b64505352395937315a726d6e6a38744c6270654739764456374b307a307137374c7972465a6258315a5939355a667a3149707837342b4e6f2f6c6a734571434d4d354f6c4e6d7742597469467162765231353478317a2f6e766b66306a2b5a7634697a4b4441506b6b39426e412b2b4438496b48666e4845556a482f33486b7278665a515463503747456466666e4e3237773358686b787533746759524b6665497646692b3953474a3776737a674f3569766d7876353677334c7363654a46487a353545547678765342616e2f6a7268346431654f4b745675757373747233652f4657537452797466376b4a70486e66776a434f3763344c65655049322b7a665a6a3966526574487a64484c773177624279746c3875594a656b665831504f325a4a464c50535a477977793772352b2f5a6f434a6e5565507a36344c4578596c4c5077346f2b2f684839356151534c2b47544677712f796244367742332f7a705966502f4958795051564f3454436e51726854426a4d4975566967565a644c61507a77796f432f68366b6f2f485759424f456a632f3331356b735133686d764447674553794d302f713842444c614b6d51454f58564a34684b53612f4b657753374852746a703944704a436d77356566306966705342633579332b4576766561755865652b466978614b54374939554e70486e4a79394b2f2f43712f4d6578555137594a4e4772564938716677744a4a767458794a4b5339324a3858426450313174312b6c524568617169676b4d4b4a455a4856516770696b4c5770746b76367a7557754f6b4446726e65596847784f4435416762654a564a473671556671625749616f6a634c3056744151505446374232363842746b716f6a6630696a2b426a6d4e4362444b436244374a79424f3170463378397949655973444648795650425742597a304372354c5245445175424932706d4b412f52554843446c6a53475830716f695a36525a3352305a41314b644e4d4159656566486144634c6b2b51456c5871464f524d39586e51776f71476c4b6d705a516247683038624e5a525967514a69354c316568582f487634654c7150316739475654586e4a76667559424b76594b495a366363796978413343684e3278365065772f76654c6443363837376950352b6c6a343157326550434d2f324f387654352f4d2f73396e426550357274482b5744444d2f37376c54452f4e705a2f53596639345830316774674931346d78596e46734a506465614b776a672f33723056735a79647049416630782f3372796c3553702f3233384649514c7777734e4c2f4972417a494d786e2b6c5164544c666c386e39797a697649424f666739584c4c784c376d505843786475454334436e3858474b2b4f3346393678416234374e6c374d6a52384d3739694136622f7a635438593050676870526c39393534372f69526552386d4c7259414d7a69752f6766652f67666370377755742f325759476247386432487258665237794437377138634657786976754750512b392f672b31524978666f316c58356c485a79716b5046584137342f4e6e7066414f6d71393858766f57455978694c342b4c4265764f6a6b356a6962346f7847392b72583262567275722b632f2f7a4c6439384e4547454f4559484569494263496c424268507771784576752b395967586e4a2f556c6a4169726c4c746c4a4b722b30524b396b324253646541564a6979507a6f7065484944576c4d55657266796955307054336c6b327a42764658632f2f5671784d4a352f354e6a3979305448536a444e6e7847624474396245505169504346783973357031634765673563777435794759536f7a755a6577326b75306865316b50716438642f356b3231452f6537592b503333334563577362513267424e58352b564c57786866542f34793657516c7970506c61484b547963354e516968466465596e59566d71674b69682b466c523253314b30436665597548477756336f4a593852792b59702f333254524366464f7441397a6964772b356f623562467239794257694672623054317a736e336e35434f4c677555584e7964754e31536c6f695a516646616c5a796538464246575a367771374253557151565579687530746f7068393051436b4561436c69572f61707079486836676b6932362f734e6d774237545630364365497a7a68474d7138473053636f4d6b556d546e426c6b75676b794b4e657a62754f3644463453756d3735522f2f656e794e7473574253332f7a3778313247635249392b736f3655536e3031797a48354e66715353776339465a64334c486e374f4638462f742f59463565464b514675784a4c484b46526947676e7656566e4e45767a2b654934664e326b416a382f446845564c7a326361474366696a464d64573546716a46636d57366c2b57744e70334d396d622b34384b5a74422f4b7533436859335731632b656e6f746362334731486b71766c3333593871336c7a445856654b795671366c7448657275626b372b4352734c70692f38694a56646d756c50474c32737475734c7a3852753576562b6f7369743756636d2f54474932492f6d5974795866615a2b592b70436f38335779786f74756b502f767068453678593548356b55527973772b773270424f4954724a384c614d2b347931395875556d574c4177435a5942692f4c7371507a74354e5433313439686b6a31637344674a51693870344b34334c50773332327a594b6c3155354f2b6472494a353545566674754f4f6a61506b5334375057775665665053314973667972544e767454714e49752b4c4b706f6467483538502b5a4e4e542f6d4b794565746f34554c2f756a5845484654546a3953482f7834767652474b74412b74467441385a6f6e4331492f59677253702f683350697069524c37324468614d48386465636b36796d324b66553559464871727a42704b694d7648304d2f6f36774a36636872643564713566467974334e4237714b6c782b39586a7a4f546e7055356e75784e6571695675336c6e7a523857715574764a754e2b536b79342b4d7050614471735067613068557072362f525a3436674461464b462b69725a655935656363776438662f5131485249482f326246316e483561373473364a62322b634e6d466668424969483132704347394f766251344f697a3856544e7371315237576c4c3231484f597271387231582f476f326b794d706d766e61614e715470754b4163695474776e63466b6457685468586c77424c4b635a314873397973716c426652437a65724d4f5970527239306b69524842766c732f7a7637372b724f704c306639304657775a6830472f394e2b662f663362316b33747864585a36635a4e682f756974486c6d6851535734624c336242464a4a4b4c654f435a74777047506151523332544d313376376b6d656464556b614777622b6f613838303543666f4e2f7453707a31793334354478436857414f747a43626747326377384f304f55657474416c3345526a544e4e64724c773464752b392b46374d5656516d43347050316f3447655850724750764e374a37453748617a6f646e38646f44316d474652474e69744874445931554d4c754a5152566f654d744d4630347278564d7153783235633332784b6e554f79744b4959706f78674668796f577a6876367a634366794d434c79644275337756635a664f75376a39747a647245547475736d7a74562f5a5a64655876496f6c75764e70533077394930424e4d715a6b454c36787a7954437972562b6b314f4b2b716648516f6578576573704a5874783872576d365474705a2f444e676e4166577551687a53372f6137645632705342634d534c6347536c426a7538643855396d32674c7031647575473869714a7541725834492f523457314465572f46574b676c76623953334e7a61334e6d4d5a584f714d6f4932303451365a4466383978744b4b35666762666471577a57546e6b7776726f6c6851504f2b6c314b39466f75434e74302f37706e59395a38697957764e524c63444b56365664523874444d6f754a4f6147515174626f395a777355516f624c2f62304e575166586f3757626f584b305450376a4850784d716d6a70367857505155312f6459533768736456395646423579696861434d617746646c446a2b514d6179684f55763534763947743969774a52316538662b45332f4f36545562515478593962434968734232696a6b7a594c662f464b7035446d634a623949464f4144376a534c7674666c6c30556430495955584753594e6d4a363147466e4d73584f363746527459695858562b4a394f6a38736446512b484b506435747469554a7435565048526d57625842524b597950383245696c754e743354685a65346f6c764f336549754e73634c436c7a4f436c2f574c69706c765057576478756d6f37566c464444326334433766614f6c7254643159464c57694676634d504c5279787853396e322b6e6978506f4e4a486251516b3271756f672b494c736652774e4854306345332b4c49336f50723357457472304e52746436615533545841466b667a394659353668393431493730456246476273654a7062695a3151465a7a5072364f302b6b3155656943554e6f6e4435792f7178784d4165724c36794e3670336f67695156324372474957644c54326a486e4259524171684f657835754578456170387567784c732f704d62724a2b2b62335576612f5a696d6a534749616e35675a3178714e766d6b6671485673354a39674b54524c517a307259674d30326431677630694d734f31452f664e49306837424f55326a774741717636674d436f6c553979334e2b43337542444f3572396769777366746f6744474768316b6251746d6659566b57486169446b384138386654476974342f70554f67424a574764566461564d59742f57794f2f46735942435830454856424644314e47543077564f77685246653350476b504d666149776a4f32363649456d595930334c35597969717a35734f6b69785074774e58645a5574466149577731566739586772696164696e6b6753664e514c77654c5174466d79516454454f3468717476596f4a7978375473736462653859634435306b67694e416d337651324f4761564a3071316d776d50316b6e563434656f51463463616d734c366f456c4575706268794e74615638537a6e485a5371475a7a386c465042494a793547743041753472376733774e4d49685442622f4268413958517763494778554842794150586b73374f67544a596f6635486441467248436f5835525357325361674156477165506e4d4f4c65354d7630305a32656e5a426b676865736477794c583743424c57766f64586b6448424c5a4b6a69546133446738595a68485372717668677a5954394a357172336a62554e6a675a7732315a6734494664656164714c31447147684a386f6d6e45416a6c7a4c5052676279767a484f497154466d506c6e754f595470365a4c50496370475a5a3944774b654964454a48466159597936477061735362314f3546683366596e76685157563053677479762b5049674a694679784663395176426c7734675130473133365371496b306f566e625052314f6e623552474e3054494f494d34785a4c4830435754786b4576767065374d5736324b4c326d4b376d3252446d32687a324156704b4c42586e6f416a76366b366576675a316b4b4a4d716335616648494f76345a627538785641736f2f5744577a6c584c316c6e66315573315a5449776b6268484757307654436630544743676c624e39796e7444366c46664d725877632b7378303242446d6368425074672f45595874564e48384537456b345432346d366464626a3634735a7374647a3544476a7239686c4e58474e3842522f574a436c6243355547572b694865536732304b4a7961743176495a78453534754976464e3178446d32595a797146796a476148674e78444d4b666b393768713655614455594d6866554d2f6d632f3974783441504867557470774c5448684575524d736b713434346c6274356d356e366f376d56435237667a72474d6134304e356b43624a455271494e446957506f6a50784c394d39445736697669476a714a4c58394b35366d2b676e79534e79596a795673472f5765566244456a62483136507338594b6d6a476d32414c54554f4b6d62394677624938305852724d746850634e3575566b7433554b343071726d6d734d38362f316e4c6a64687373306c364f61324d625a6173643050344d35305771436b4348627869412b6b786378484d2b61314a316e76704f6f73786631706b39634169597845664648536b39517530504f7366357031686253732b443144373438716d7969566a374f714150346a4e7847452b5a55385369367741396c687676496538766d3072636f4e322b69446766324979303342613255646262416133724445383330486149707970394f6d7834414f6f334f3159526f61367a51565878543248633964746870545774646a647356615753745869697a6c5a736537364453493665466632334c3472562f666b624264506d64633263514c332f545538717335664b5a42584a6669376e6c615671764838567a492f304333546962474c71544b494b33317373716c56457a76474e6f394b494a69704e5a724d444e6656555a356a30716d6f625a504f7970733651336e62665534523039595a314a564830627142706a4f5574784650623172305833794d4a6a35752b58777a53346d4844646653516c5472646c463057782f3230744336395364615264386334594c5251467a467655664538324a7a4f3836536f4e486d654861697050552b4753612f6e6159503874674b526c742b65747852622b4b6632576f585a753176376c4c386c71775a4369374d6f4958364b67714b425335327148495957736e626b3545364d637943654e69655734644c6b785371774768354136424d346e6157584e6c6c3666523448356a656e4a792f4150533535636f5261335679396430783945647941492b39447a7372504d3852314f68757978374c4f4d2b6a6e2f4f6e30346d596d4d59767a39587056444a4b6673674f6f6e7079666e70316476627538646339663830696f6a58314d676c57634830377068556e4d6861464177657a3644424938696f416443486e387a633169395a6c6f515a496e3576623639504c6d394f7a322f4f72532f58563266584e2b64616b734652347346597265536474444e6b5965316273675442416d45746765387848626b664934766456717a54334c6f414e6a396e3478536835626c677a34367a434a504439527a4458724d4f527057415a687675542b77487a662b794374376a487a4e7965374c6f496d4f486d43376c69536659334549735669517655473351596752576f4b3665716770776c4b6a614c6c7876584368627552556457493351567877714b344155474e676f6b764e2b37464838547569726c5a5269444f2f6f4f58334c762b7736593657683731336f3954694465724946486874547053487531484667584c4c323765694656766b524d6a596563524f6b437030355463373843346a394a4267757532656d45506b396f5663312f50666a7039643348726e72352b63333770586c39647a4754584633314a31757a6d35757a713876623636714c4d6c6b7241434a6945517475303644674d3156527743357336434e69576a51572b394f6a4e304344475464676d77596841697a6a386d77446c594c2b5a335a362b50723039625446676d545a463143456163467a507a6d626e763836756d7a6967435947446f47574e6d654a364a7279566b596b74674b457a446a4246304a3164766e737a757a373938574c573168396f49575253694d646a61594b47324b6255786e536b2b715367753259595968505a44674a4541343675475461784361684a7942677475767a31394f4c3864524f7968527a4c495251354934522f64656d574b76726a3665335a4c7955627239326232635873375062717573344d784a6a61784c62354a77374a6f7578485a6746694f6841544f4d49356453324d2b6b394f45674c3937767a79316e62666e50366a727261595078734336344a74305661696357345962456439566e5367374a3653414e7a2b61716f7367416b49374e6c50325a372b4e31686a464d416a57314473576a766e4a6343543034734c392b7079566f66304177534551704236435141784e5446466a6f3354774977416443787341684d35674a725541734243474750627472464e4b48535179512b75413154737337413558467854462b7132494448734c705159456c583851624655713743666859757767392f6462392f386e43304176776758586a2b376e396452382f3275667272732f6662723668394b69306c78356c3974526b35454655537a5730617852596235376d5964684d6e4a7a482b622f6d2f47304c2b47424338443745464d7a4d664755614d366a3958525675614d6a70757a484b433452326e5249754e456573726c69687254714c68506262635269783958676e32777733743059697a2b4c53766d6a5a52544855697248332f7a6d436a6156313571644f504553316764793033364b452f3948704d57416c736267756f474e64456d364279347546304d6b546e43544e3675597859733175464946576943306167456d774a30775834445537386943423855496f536b6f67796a6449454c58567762524767646f524863665449466c6544746b74574337754e632f427537624777636533644d563539704c784f3577525632642f4c6a7864585a333977302b5a664f6a67734947382f2f77425a7547353738504456492b2b6674374d59397633542f666e56644c7a625938706c76412f5454727774345a503174646e5a322b6a66333750547436646e353754394c336d2f304d6c39672b656e647859563766586f375339466b736d3455412f6c466f704749327678305646546b38647a63706a685331584e2f6d6c3363336a524b49574f78544c537a4b304742396e316547647a3545585268364b356b2f486d3271564d624f356151387543737a3058506a754357666c45517149346553306d527558694c52625a684e624444796b7436386d6f6537343864554931554676716f6e644174334c4730547246524b59652b2f4668504748334d6b6e4c6357505331514e703550345638534f6241485533712b704e5555766370477a45576136466e6273512b7369686d4c67735867536354765575483341466f4e483168484e7946624f4575676f39757842356b2f57527276444a4264515634326c7173574c4c3463764130506758774235626c31616d372b65583870306153596c6f413263676b784548414d57324c6d4a594e6251524e44444841467157556d4b426a44314b656743644a4a2b6f6b374b5574614c696c596837634257467938754f70564e747342634275517936444d5a4b4d344f343854457a4a74536b50516d4e567567444343394946464337744c644265796e70562f71375033422b76336c31796d3232484f716d325938644e306c4f73426d723477346774486e3232474b38744c56422f567256704d497231795177666c737a535630336850612b46745a663972717234746974433561586b53504e35777556736c59774479426835386175616b314271556f6777736a41325459494968546145446b5a4549516c73347072646e745a786a515a35436571314a57416847396870466f7363514e4a38466d426745384276524a5043424c7646424169314562574a4137464b6461754a43645635636b774c32644345304c524e4378424348597763322b356f6535504239426230546a327962594951635279467a6f4d6d70683770565a564d49646474597471623947356d5a322f647439666e623262754c2b632f2f7a4b553252505470425942314449684a546143304a4c723168716934654c713738496b4947705245316b6d744d634c50435068657661473078356f49355846537761662b62573857447044623266474b6e36532b62576466386e636f544a61647374646d6b484f39727a6b7a514a6371434f334355737850466e326e4f4c6d5a6336712b73544c6e525870436d4c33337977612b44696c612f7779594b7646466f513645654536584c44457a626b624a356f474b485761486b735a75772b50717a486971515053515644384c356c4e69423643556b447142425566676f78566e796f5952574a795145396453436d6f655070346b524e537046556a685045576a4b51416a7159416a7152413571536e726b52754641573176475145485673346f366735794369556b2f6255612b4f63696f4f4c524c76763774546457375851504a364d63766b387873756d4d4d5a5438765365746b724d67657a364e4f695362786e755844714d492b52796444693656413548465370476836524c355a42556f574a30574c7055446b7556617853656344396a523857544c636f714a427869564e795274393864307962794a36755a633268346f74536754636b366b5633323149677068342b6d4a396335395256716f624d564d4c70495374626a7a68756f553161424e7072412f47485a565659306b615670476773486a6c626b774b363334765343486b3034383131764955396947636d4c34547249554e61336e41777465735a384e325233636e332b4c556f4b434b4f4a475873795546324e684134476b694876365974384f316f4f65495756422b347855696f676a4b616b6242564d316756336f2b5455686a616577494f716a764c6f4f6f5169615a57757739687972787a6a34363243685a6377742b517a3439746c4b2f6241565057744b4f6a32517835502b6450576e5265426e3768656471664e796576415430367a667772767a48554d62327a4e79587a4774596e595235647a5248425053302f4950764647614f2b4779676f4939652f4d6c616f696a532f4e6865657173584f3668322b6d6d5839796b2f3579396868397a4b56624471336662434d792f4f5430347530763956345671523649426a435a73305934517a57644d6c4b48334772472b634769784945416d686851597072556f52695a4671474f36546732784a6851684b434673474e5347324948453873424e7258544879317145644e474e734359514e4f47556c2b5331636e366558627031672b3173616c46546366434546416241456f4a73544331694f5859447157576952437754574935694e6f4555477961794849734730494d5452745a4e67515532635332415345416a5350726e33563977496751516830454b4d5451746b3167457874546a444731495849733238516d734b434667415653796c4d6562476861794c4653455347454d63493267526151366869716b3656306d6b305069456b30376572363961782b754e4950446a477a646851434d51594f67734347446f41494f7a6243304d514f784a62745348333457456335346e68754966696144732f4a7577442b3965677433496a4677654a5275736a534843376e4d427665566c7942326d353630674d6c7671702b415a316449624f664536733777417764647941716334466a453271482b497633493866692f636978654439794c4e3650484176324936657634767172375a4e6864712b532b71756b353156616635554f713645747059626458315565774f5a564e3348373353446f706d4f363432576c53546d6b5478497a446e57662f642b48532b2b7836674c6c2b756f4279746e33596f5332546963382b684375503457757433483965792b385930657978785679454d706c374a3041476a375a4533624a38346e44496f39697056527945464244424c3133754b686672434e425864347266665872374e704637625a7462454c69514f6a594a6a5164516b786932525a4d4d335762554949524a6852595567764241524a4d54756334746977437147506246447555596b6f77535663344e7161325a574a67645234384c4547432f767959683056546c7377443759315355652b497232725a7757564c467248515a356c7a716271646b76616a33337776546c34734e385a666a52632f574e386446326479766b2b486578733369547a2f517844657564734c594f366939654f6d5342703232763731613956546266484b536d492b53684a7a665a49776e316f53504f65694b70636d4c47315377694f6b64467947515671446e34482f6251762f766648584e4958653372676e6a734c574e4248786832446a73732f2b366e48425847385876577666456132384f56754e676a783348344c774d613569674d425552704530636730493063544a526a49323255696558624b5236456f326549414f4a396c4939684a6d6b2b6e43624449717a435a71595461337550467831744561585a4a5263545a5269374e396f70414a7446704555653837465033476350655a6f395232774259685a77306f332f445665346d4f4b415671745966476143554b47706349356171423270665361346c4c44597a7963596b4c344b446a55704e6935626a55432b6a4a34314b5475733634314e525579636a55784b4d784d6a56427930576d396d684a64357a5a33506a49424b453266317a6852693430745566726b34564d614a7047467545364b587556633270726c78734e72776871427778756f55686378693843574e4b31746f633254352b4a42772b6b336c33564845652b6947664e5878612b394661355561584f6e5a6f443767616861554f376a75466976643738464f55307156433447372b66655651506b5457794a316a4e314f4472696859316f50343654494c776b626e2b65764d6c434f38557036774652744a6e6772624c3944595376724c714b6b305656316e6a7072527165546d6b492b56594e2f5545693947786f7337476572335a655864624a6b4b496e4667764b6c7642302b38505a324e663263747631703932537a4c6c745a7a55496970466563635331397449344b73664e4f6c746c44412b31516d584c654946304b2b383843346476776c574c44705a42664e784975686951774d6c616a4b70666f2f37684f3277597a344c4874555675304f387030364248634a395831492f54464339505668552b745678616f6a33306a6778346e4d3174572f55526e795970765931576731682f4469585270694f55554d59784f356e6478323676727a32314965716f592b59762f3749797076716846487668716d6837626c67757967466a463859443247545779754c5157766b564e7a6265506f2b2b33696372774b2f6665465058344b3176613837456d2b683349364a78586f704b396d5a4a5a57644455704e61566b75423755784a2f6d374175565274516a792f59694b366942662b68664b67796731725a30483864536e52597357374542716d397675306d58336170522b6432776f34684e6177494b755a67365958565845372b61675754634855715a737541476b525a6630796e70776770752b543476575649484b31712b376c5543716c72395079576e30756c57672b69516e5663775a4c626d796935703730386b455a2b2b572b4136675862386b355a665469352f633745365247765051746978434c5174516b77494859306a6b767359727762636854374458332f7863514654484f5a385a56484f48565846626b55677564782f6333552b3830567853573553786774444e646f4156713244623453704b732b635358415074666f385261694a2f67704f4453684c4b572b673871584e756d6e6659655a4a48325453786a377041542f4c2b764362752f64376556324c66622f6d33697657705373416c44646d4f754b5371373762536c56424f6630646369576b6633344e6c2f77705a736b32726b6e586b3362475430396576727a6d4e634f713371316e32304e3055586153384f66324865334e3764583336383877397635323961562b73686762446352667377757368444e78356f4e7236566f36574666575832506457712f6a6b4c4533767a713475623639507a3237646d396e46374f7a327176346c4f5149324e6845314c596f5241685a466c6b326741366c743257686f3564754e313175747a745a686d734432666851744271445a2f63582b39636947392f6b6c73567758554c2f6d642b3976316d48637147653150355756526c474172575a43644367546b714a2b724b784c4f4d303955375a6966724957374c51377a72516c672b643669305655486a456a5575597243353475483256507353396c4a6c306f75595667785436644c6b6656422f412f6f503565376b596e4f5a305950334d466f4a61314a423153365a6d2b5974414533544764764c79655863782b50723264755a6e503472737143416e466a675549704c614a414c47524352314349616e3371456d3571693365432b6a2b636e72352b6d4a3233594864524e5143454e73414f52545a466b5957785a6a59466e4a73596c4948517073514374485147536b396c4c793975506f6e487a6e464343434c324468645167353950396d4e675731573679394b716c594d6e637756352f43486e5844374541494a34427a333636677262493169645a6c7163376b724c343764657938575849317a764c516265367445334f476d7170644b624232356973365841304851455765375a716e3433475730667469745377635074716a4d2f654239504b494b4e5762792b5636375033373242393674534c74646637394f4e414749614d54336371314e33574b706e306f6e6c6a4a336e30756e6c44585033707a6675724e665a3563644b544e454e7144414d514845474a676d70705a447261477a6c7271785051544a37435072507a3149595052592f2f474266556c5872714777707151444246556a74645a55453176672b79375a33484e57397650733173327631373538392b6248726977415770594e624163435970735949784d41614a6b32426369304d45434132744c727854622b322f4d3373357662307a64764f784b5237476f345946495445555349685731694934695269536d3171594d6f70685244694a55546b5a53534e41756258627670596e313263384d6e4a4b55697a622b6f44553348424e53324c4174687977596d674a526747774a45724b4872362f71704b4a6575765851514242334c775359424e7343574179304b55336b3446454154554543514252316b49774147372b72724a655a6d39762f657a5337506871514373594f6741327a5478445a3048456942436242704132776a427a735767656c504b545534565a7778424e332b777a322f2f4f6d7167777754556f41633077454957424169434579415276462f2b772f3335767a6e793950626439657a66754e414e73484974414367466f5555456f516f425941366733635a64754e6e79592b7274662f68386a48316345716573674669736b79326a6d63346f3231375741556b6e4d785766536e47353243387a4c566b756a4a4e4a374c693038456950352b6270362b34345535373938766e6266444134735237324978696351646c6375505a6f707259666970344a6a47684668396135483934687453576f795a472b38777071576e313368672b3831597246703157466d594b664e614254476c504e557a546d564d446a573572346e4b68512f59485a55743849657068733650516b4c306b56326251786d315235426872536730776b7870544864654535745245704e32672b4a7a6f6d5950444d716f4f5565706956576346547866504e2b6b456850376f4d4e57434d3656784e5a464e5a313174544c724e7134735854664e77554162574b5531747a504a4e4c43376665786f62752f313848693758716b77576f36653070787a4664465a55777464744f33573652386e336f4f796b49612b526a50467449766e73427156576a6c4f6748492f6b5a7a3739684e2f55506f4254346e3048596c724c32654b5a306e777153446732704c364a78756467764d77507a4a726134745042596b65733258377a30647244366a74416f716277652b716b75546a2f386672302b703935493831515634746c456d5361466b5955573943784961595559476f526841473148597770686735434e72497341676b6d796e744b4e614c3475306e49704151675168334c5242524153436c794d4352457562586e497068485876516c5851637261555a312f475465706f4c6b2b625567636f67664b65676e3749623554326739764a3639765467396d376c6e463664642b3563495977417359434a494c637330715930524a51425332795245656276796d6d31576e732f4f306c6c5255704561674830726878613366444f37664f322b6d6433636e50343863322b7655732f63345a464e414b6b44624570734535695736546941516f66593245475753536b463674756f5a62503739657a30646363574c6743324134686a6d68536b73303441734243773051673358434c392b2f5835626366474d6349327367476b47434362576743446c46644343484b6f7163347343786476386f4d4a62746358384f5a4c7242774a4f6b434e31634e6b4c6430667666472b724e626567754e70657678544f616831474144664f3330766478424374395479547947756d6264516b33706c2f47547874344a6b4f5036325a53794c515775657a364639704a7831685638707464626a586a6c534869304d2f6c7141632f7673704975646771532f5230482f7958686941505936745a5776335455666a6a6451743143534537386d393546466364352f4b6967673331382f686f6d722f41334d672f665a58544a783939367a4e7532374e4a432f4e753372754534694c34793950456c755a30383956776e3639313451757347695071446e5173467748666f4e776e54634b646735382b6e2f33303559686a632f72364639554c7a4136557869574161505a5a49426f3674396647414e314e633333723047456c673537544d74715974503947416d4658424e5635735045543279586c6670745535673939464b31633846586d5a486b52776278624f5878636b6b346f637643564b6a6675715349494b6d7a4d664e626856617835783148763353666559374c55392b45546e3642585764676a523443424939727379694f41346f6472704d70775476574f4a79756b7a796f34324256762f61527158755a4c7467715a2b484c6f4e7174466361676e6b59726f6c445a62642f71722f34636e632f32306950784346694172664577544c574e7732416c4852514a756545396562525643494f79757879554b625a3661444d387051325151646c616e645176495170703770395965356f463956414e744a4a6361464e3561616179505134716c366f422b53716d6e54324f4b764771357264565a4f5171527857453438576c395548564e5a704364796d4b754b307243366e5a51467454737653376253717652373572616e3630366b43787a675856514d796b5763716357687853467867682b4f4853764b3633552f78786b7444754f716c5a5846584a5734696c315343312b474a654c416b485244526c4458524c6764453947564e564b63444b73363563714e79647964336c355a57443152466f753643326c4361567972757658323052744a6f6c39554e37544238566f322b6271655661626d2b524b6d4764514a7656494d2f316831314170503052356b4244687a4e4c4f4b50634764435a4866364979767a5236617750384a542b4b4e50323132796a467a63766f4a636830504b73497a33534255775931775366356474307533574f6766615042674833474735734a78414c666354434f4b613048506c43485335726a593053642b4675322f73786a4b2b693354354c6b773766526557394631457a48643972664c71506e696864316363414c4448712f35324679566f766b5478324e6a583573623078636e4a43776c54422b624a7257663670553733484d68634b324572774a63784c78583457416438434c756e57455a416a676f4347516c3149356a7135684168444b4e7532424443384952583445783076626f51686e47586c6d633955306b5342665048704979475a6672426f6d676475655831596c554354764d757135644776674e6d424c455272684d6a755139696f2b6a414f6971796f53512f525234364b57705748696f506e56545a6c3676314a77363146526271314e666a5071724839324352317a534c4c6a41577838463878647a5958323979786f37574778622b6d323032624257454a77575a4a36763838346555334c37665430353362496d38567834697667355858397959725a5a48373750706c4a4474676d3069356e734a57786a4a5a364e73684b754c4657476e4b6c5a4548486d786f7070534f4163745666615a2b59384a6b78646d78466959524636595a4372626c434a464e536c5361367755726563697876665a436e332b654666755a6f5350713958582f776b4141502f2f304551477335726f4151413d6945787465726e616c7388a2664f6666736574841bffffffffffffc6611bffffffffffffffff1bffffffffffffffff1b07fffffffffc2c706853656c6563746f72841b30b9224cd18a4e7c1bc71f0c828480ecb01b8de0747c8cfdb4821b04c9700ad999c431a2664f6666736574841bffffffffffffa8a11bffffffffffffffff1bffffffffffffffff1b07fffffffffa32b06853656c6563746f72841ba6951e73bb6dfe241b05e5ff539dbeebe71bdd254888370955b41b043ef1349908885ea2664f6666736574841bffffffffffffbb811bffffffffffffffff1bffffffffffffffff1b07fffffffffb73906853656c6563746f72841bb22ea4840c873b8b1baa7fdf9303e1d7731b704e592f223c3de61b00bda65049efea59a2664f6666736574841bffffffffffffcde11bffffffffffffffff1bffffffffffffffff1b07fffffffffcabf06853656c6563746f72841b279f5f4dc8b4c7b31b6ac884a41a1b5bd71bc1307f4321f00b881b04bc8012967069c6a2664f6666736574841bffffffffffffc1811bffffffffffffffff1bffffffffffffffff1b07fffffffffbd9906853656c6563746f72841bc735e82ed1e23e011b9ee8c8991bbc1d151bacb56cf03bbaac5a1b00186ed02a71bd65a2664f6666736574841bffffffffffffb4211bffffffffffffffff1bffffffffffffffff1b07fffffffffaf6306853656c6563746f72841b04b9244ab52a4b811bffdbe496a2c48be11b8a5d0118067bf13c1b07102404b615fdd5a2664f6666736574841bffffffffffffc9c11bffffffffffffffff1bffffffffffffffff1b07fffffffffc65d06853656c6563746f72841bf6c53d175ebbcd9f1bdd07ee6aca2299561bd457faeea1e288671b0754ebc0b261e0b1a2664f6666736574841bffffffffffffaf611bffffffffffffffff1bffffffffffffffff1b07fffffffffaa5706853656c6563746f72841b4298606763e030411ba7525ed75dc70c951b01c0a0b4d48ac5061b0624e8d006b62cda6a4c3148616e646c657273806c436f6e7374727563746f727381a2664f6666736574841bffffffffffffd2411bffffffffffffffff1bffffffffffffffff1b07fffffffffcf6506853656c6563746f72841bd1c59afcd3f9742d1b083befda3709a3ed1b3142bfd1003b62e21b041f80cbf54be289") + assert.NoError(t, err) + + stateDiff := map[felt.Felt]*felt.Felt{} + + for i := 0; i < 10; i++ { + stateDiff[*new(felt.Felt).SetUint64(uint64(i*10 + 1))] = new(felt.Felt).SetUint64(uint64(i*10 + 2)) + } + + return bc.Store( + &core.Block{ + Header: &core.Header{ + Hash: &felt.Zero, + ParentHash: &felt.Zero, + GlobalStateRoot: expectedStateRoot, + }, + }, + nil, + &core.StateUpdate{ + NewRoot: expectedStateRoot, + OldRoot: &felt.Zero, + StateDiff: &core.StateDiff{ + Nonces: map[felt.Felt]*felt.Felt{ + *key: nonce, + }, + DeployedContracts: map[felt.Felt]*felt.Felt{ + *key: classHash, + }, + StorageDiffs: map[felt.Felt]map[felt.Felt]*felt.Felt{ + *key: stateDiff, + }, + DeclaredV0Classes: nil, + DeclaredV1Classes: nil, + ReplacedClasses: nil, + }, + }, + map[felt.Felt]core.Class{ + *classHash: cls, + }) + }, + }, + { + name: "basic with v1 classes", + scenario: func(t *testing.T, bc *blockchain.Blockchain) error { + t.Skip("new test data needed - why?") + expectedStateRoot := hexToFelt("051099998c4c482b6ab11ed287d9fd1bc1b2e0dafca099cfc23e55c8754048bd") + + key := new(felt.Felt).SetUint64(uint64(12)) + nonce := new(felt.Felt).SetUint64(uint64(1)) + + clsHash, compiledClsHash, cls, err := V1ClassFromString("01b5126eb60b96de5b8e45c1334ad44d23dacaa76f748ab08b23dac43f3958", "a262417419aba565436c617373da00010006a763416269785f5b7b2274797065223a20226576656e74222c20226e616d65223a2022627974655f61727261793a3a53696d706c6553746f726167653a3a4576656e74222c20226b696e64223a2022656e756d222c202276617269616e7473223a205b5d7d5d6741626948617368841b194b356d26f0cd761bd738e24f884338411bc2749e3d7f8913051b0459fa72a5e413526750726f6772616d8b841bffffffffffffffe11bffffffffffffffff1bffffffffffffffff1b07fffffffffffdf0841bffffffffffffff811bffffffffffffffff1bffffffffffffffff1b07fffffffffff7908400000000841bffffffffffffffc11bffffffffffffffff1bffffffffffffffff1b07fffffffffffbd0841bffffffffffffff611bffffffffffffffff1bffffffffffffffff1b07fffffffffff570841bffffffffffffff811bffffffffffffffff1bffffffffffffffff1b07fffffffffff790841bffffffffffffffe11bffffffffffffffff1bffffffffffffffff1b07fffffffffffdf0841bffffffffffffe0211bffffffffffffffff1bffffffffffffffff1b07fffffffffde2308400000000841bffffffffffffff811bffffffffffffffff1bffffffffffffffff1b07fffffffffff790840000000068436f6d70696c6564a96548696e7473425b5d655072696d65c2582008000000000000110000000000000000000000000000000000000000000000016842797465636f6465806845787465726e616c80694c3148616e646c6572806b436f6e7374727563746f72806d507974686f6e696348696e7473425b5d6f436f6d70696c657256657273696f6e65322e362e307642797465636f64655365676d656e744c656e67746873a2664c656e67746800684368696c6472656ef66b456e747279506f696e7473a36845787465726e616c80694c3148616e646c6572806b436f6e7374727563746f72806b50726f6772616d48617368841bb3799734fc0a22761b73a1705363df43691b78153b3e72b8b9ff1b0735af66c53fce8d6f53656d616e74696356657273696f6e65302e312e30") + assert.NoError(t, err) + clsHash2, compiledClsHash2, cls2, err := V1ClassFromString("0cc7d5b4b3f7f112209f743ed739f97570d83d7b42c3516bd955d4b053237dd", "a262417419eee165436c617373da00010006a76341626978615b7b2274797065223a20226576656e74222c20226e616d65223a2022746f746f3a3a636f6e7472616374733a3a546f746f3a3a546f746f3a3a4576656e74222c20226b696e64223a2022656e756d222c202276617269616e7473223a205b5d7d5d6741626948617368841b12a4a837ffa449ba1b6eeb23f3ef6440e31b453422a17e9d29381b02cbf391fe0a8c9d6750726f6772616d8b841bffffffffffffffe11bffffffffffffffff1bffffffffffffffff1b07fffffffffffdf0841bffffffffffffff811bffffffffffffffff1bffffffffffffffff1b07fffffffffff7908400000000841bffffffffffffffc11bffffffffffffffff1bffffffffffffffff1b07fffffffffffbd0841bffffffffffffff611bffffffffffffffff1bffffffffffffffff1b07fffffffffff570841bffffffffffffffa11bffffffffffffffff1bffffffffffffffff1b07fffffffffff9b0841bffffffffffffffe11bffffffffffffffff1bffffffffffffffff1b07fffffffffffdf0841bffffffffffffe0211bffffffffffffffff1bffffffffffffffff1b07fffffffffde2308400000000841bffffffffffffff811bffffffffffffffff1bffffffffffffffff1b07fffffffffff790840000000068436f6d70696c6564a96548696e7473425b5d655072696d65c2582008000000000000110000000000000000000000000000000000000000000000016842797465636f6465806845787465726e616c80694c3148616e646c6572806b436f6e7374727563746f72806d507974686f6e696348696e7473425b5d6f436f6d70696c657256657273696f6e65322e362e307642797465636f64655365676d656e744c656e67746873a2664c656e67746800684368696c6472656ef66b456e747279506f696e7473a36845787465726e616c80694c3148616e646c6572806b436f6e7374727563746f72806b50726f6772616d48617368841ba97fcaac59fda9281bcb5930ad34825d5c1bf25090cae1f11f781b008272c3d5d2ae0b6f53656d616e74696356657273696f6e65302e312e30") + assert.NoError(t, err) + + return bc.Store( + &core.Block{ + Header: &core.Header{ + Hash: &felt.Zero, + ParentHash: &felt.Zero, + GlobalStateRoot: expectedStateRoot, + }, + }, + nil, + &core.StateUpdate{ + NewRoot: expectedStateRoot, + OldRoot: &felt.Zero, + StateDiff: &core.StateDiff{ + Nonces: map[felt.Felt]*felt.Felt{ + *key: nonce, + }, + DeployedContracts: map[felt.Felt]*felt.Felt{ + *key: clsHash, + }, + StorageDiffs: map[felt.Felt]map[felt.Felt]*felt.Felt{}, + DeclaredV0Classes: nil, + DeclaredV1Classes: map[felt.Felt]*felt.Felt{ + *clsHash: compiledClsHash, + *clsHash2: compiledClsHash2, + }, + ReplacedClasses: nil, + }, + }, + map[felt.Felt]core.Class{ + *clsHash: cls, + *clsHash2: cls2, + }) + }, + }, + } + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + d, err := pebble.NewMem() + assert.NoError(t, err) + bc := blockchain.New(d, &utils.Sepolia) + + err = scenario.scenario(t, bc) + assert.NoError(t, err) + + d2, err := pebble.NewMem() + assert.NoError(t, err) + bc2 := blockchain.New(d2, &utils.Sepolia) + + logger, err := utils.NewZapLogger(utils.DEBUG, false) + assert.NoError(t, err) + + syncer := NewSnapSyncer( + &NoopService{ + blockchain: bc, + }, + &localStarknetData{bc}, + &snapServer{ + blockchain: bc, + }, + bc2, + logger, + ) + + err = syncer.Run(context.Background()) + assert.NoError(t, err) + + state1, closer, err := bc.HeadStateFreakingState() + assert.NoError(t, err) + defer func() { _ = closer() }() + + state2, closer, err := bc2.HeadStateFreakingState() + assert.NoError(t, err) + defer func() { _ = closer() }() + + sr, cr, err := state1.StateAndClassRoot() + assert.NoError(t, err) + + sr2, cr2, err := state2.StateAndClassRoot() + assert.NoError(t, err) + + assert.Equal(t, sr, sr2) + assert.Equal(t, cr, cr2) + }) + } +} + +func TestSnapCopyTrie(t *testing.T) { + t.Skip("DB snapshot is needed for this test") + var d db.DB + d, err := pebble.NewWithOptions("/home/amirul/fastworkscratch3/juno_db/juno_mainnet/", 1280, 128, false) + defer func() { _ = d.Close() }() + assert.NoError(t, err) + + bc := blockchain.New(d, &utils.Sepolia) // Needed because class loader need encoder to be registered + + targetdir := "/home/amirul/fastworkscratch3/targetjuno" + os.RemoveAll(targetdir) + + go func() { + http.Handle("/metrics", promhttp.Handler()) + _ = http.ListenAndServe(":9201", nil) + }() + + var d2 db.DB + d2, _ = pebble.NewWithOptions(targetdir, 128000000, 128, false) + defer func() { _ = d2.Close() }() + bc2 := blockchain.New(d2, &utils.Mainnet) // Needed because class loader need encoder to be registered + + logger, err := utils.NewZapLogger(utils.DEBUG, false) + assert.NoError(t, err) + + syncer := NewSnapSyncer( + &NoopService{ + blockchain: bc, + }, + &localStarknetData{bc}, + &snapServer{ + blockchain: bc, + }, + bc2, + logger, + ) + + err = syncer.Run(context.Background()) + assert.NoError(t, err) + + state1, closer, err := bc.HeadStateFreakingState() + assert.NoError(t, err) + defer func() { _ = closer() }() + + state2, closer, err := bc2.HeadStateFreakingState() + assert.NoError(t, err) + defer func() { _ = closer() }() + + sr, cr, err := state1.StateAndClassRoot() + assert.NoError(t, err) + + sr2, cr2, err := state2.StateAndClassRoot() + assert.NoError(t, err) + + assert.Equal(t, sr, sr2) + assert.Equal(t, cr, cr2) +} + +type NoopService struct { + blockchain *blockchain.Blockchain +} + +func (n NoopService) Run(ctx context.Context) error { + return nil +} + +type localStarknetData struct { + blockchain *blockchain.Blockchain +} + +func (l localStarknetData) BlockByNumber(ctx context.Context, blockNumber uint64) (*core.Block, error) { + return l.blockchain.BlockByNumber(blockNumber) +} + +func (l localStarknetData) BlockLatest(ctx context.Context) (*core.Block, error) { + time.Sleep(time.Second) + return l.blockchain.Head() +} + +func (l localStarknetData) BlockPending(ctx context.Context) (*core.Block, error) { + // TODO implement me + panic("implement me") +} + +func (l localStarknetData) Transaction(ctx context.Context, transactionHash *felt.Felt) (core.Transaction, error) { + // TODO implement me + panic("implement me") +} + +func (l localStarknetData) Class(ctx context.Context, classHash *felt.Felt) (core.Class, error) { + // TODO implement me + panic("implement me") +} + +func (l localStarknetData) StateUpdate(ctx context.Context, blockNumber uint64) (*core.StateUpdate, error) { + // TODO implement me + panic("implement me") +} + +func (l localStarknetData) StateUpdatePending(ctx context.Context) (*core.StateUpdate, error) { + // TODO implement me + panic("implement me") +} + +func (l localStarknetData) StateUpdateWithBlock(ctx context.Context, blockNumber uint64) (*core.StateUpdate, *core.Block, error) { + // TODO implement me + panic("implement me") +} + +func (l localStarknetData) StateUpdatePendingWithBlock(ctx context.Context) (*core.StateUpdate, *core.Block, error) { + // TODO implement me + panic("implement me") +} + +var _ starknetdata.StarknetData = &localStarknetData{} diff --git a/sync/sync_test.go b/sync/sync_test.go index 4f1d8a096a..f579840843 100644 --- a/sync/sync_test.go +++ b/sync/sync_test.go @@ -49,6 +49,7 @@ func TestSyncBlocks(t *testing.T) { assert.Equal(t, b, block) height-- } + bc.Close() return nil }()) } @@ -147,6 +148,7 @@ func TestReorg(t *testing.T) { // sync to integration for 2 blocks bc := blockchain.New(testDB, &utils.Integration) + defer bc.Close() synchronizer := sync.New(bc, integGw, utils.NewNopZapLogger(), 0, false) ctx, cancel := context.WithTimeout(context.Background(), timeout) @@ -155,6 +157,7 @@ func TestReorg(t *testing.T) { t.Run("resync to mainnet with the same db", func(t *testing.T) { bc := blockchain.New(testDB, &utils.Mainnet) + defer bc.Close() // Ensure current head is Integration head head, err := bc.HeadsHeader() @@ -182,6 +185,7 @@ func TestPending(t *testing.T) { testDB := pebble.NewMemTest(t) log := utils.NewNopZapLogger() bc := blockchain.New(testDB, &utils.Mainnet) + defer bc.Close() synchronizer := sync.New(bc, gw, log, time.Millisecond*100, false) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) @@ -201,6 +205,7 @@ func TestSubscribeNewHeads(t *testing.T) { log := utils.NewNopZapLogger() integration := utils.Integration chain := blockchain.New(testDB, &integration) + defer chain.Close() integrationClient := feeder.NewTestClient(t, &integration) gw := adaptfeeder.New(integrationClient) syncer := sync.New(chain, gw, log, 0, false)