diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index b2b63c2ac7..58dd0d4aa8 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -381,7 +381,6 @@ 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 } diff --git a/blockchain/snap_server_interface.go b/blockchain/snap_server_interface.go index ee4fe5f1b8..df9b939a3d 100644 --- a/blockchain/snap_server_interface.go +++ b/blockchain/snap_server_interface.go @@ -3,6 +3,7 @@ package blockchain import ( "errors" "fmt" + "github.com/NethermindEth/juno/core" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/juno/db" @@ -23,7 +24,6 @@ func (b *Blockchain) GetStateForStateRoot(stateRoot *felt.Felt) (*core.State, er snapshot, err := b.findSnapshotMatching(func(record *snapshotRecord) bool { return record.stateRoot.Equal(stateRoot) }) - if err != nil { return nil, err } @@ -66,7 +66,6 @@ func (b *Blockchain) GetClasses(felts []*felt.Felt) ([]core.Class, error) { return nil }) - if err != nil { return nil, err } @@ -91,7 +90,6 @@ func (b *Blockchain) GetDClasses(felts []*felt.Felt) ([]*core.DeclaredClass, err return nil }) - if err != nil { return nil, err } diff --git a/core/contract.go b/core/contract.go index 8c8994d8e1..cb6c69415f 100644 --- a/core/contract.go +++ b/core/contract.go @@ -2,6 +2,7 @@ package core import ( "errors" + "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/juno/core/trie" @@ -9,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") diff --git a/core/crypto/poseidon_hash.go b/core/crypto/poseidon_hash.go index cf7dcc0fb3..4290bf2105 100644 --- a/core/crypto/poseidon_hash.go +++ b/core/crypto/poseidon_hash.go @@ -1,9 +1,6 @@ package crypto import ( - lru "github.com/hashicorp/golang-lru" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" "sync" "github.com/NethermindEth/juno/core/felt" @@ -45,7 +42,7 @@ func round(state []felt.Felt, full bool, index int) { mixLayer(state) } -func HadesPermutation(state []felt.Felt) { +func hadesPermutation(state []felt.Felt) { initialiseRoundKeys.Do(setRoundKeys) totalRounds := fullRounds + partialRounds for i := 0; i < totalRounds; i++ { @@ -56,37 +53,13 @@ func HadesPermutation(state []felt.Felt) { var two = new(felt.Felt).SetUint64(2) -var lruPoseidon, _ = lru.New(10000000) -var poseidonCache = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "juno_poseidon", - Help: "pederson", -}, []string{"hit"}) - -type lruKey struct { - x felt.Felt - y felt.Felt -} - // Poseidon implements the [Poseidon hash]. // // [Poseidon hash]: https://docs.starknet.io/documentation/architecture_and_concepts/Hashing/hash-functions/#poseidon_hash func Poseidon(x, y *felt.Felt) *felt.Felt { - key := lruKey{ - x: *x, y: *y, - } - - res, ok := lruPoseidon.Get(key) - if ok { - poseidonCache.WithLabelValues("true").Inc() - return res.(*felt.Felt).Clone() - } - state := []felt.Felt{*x, *y, *two} - HadesPermutation(state) - result := new(felt.Felt).Set(&state[0]) - lruPoseidon.Add(key, result.Clone()) - poseidonCache.WithLabelValues("false").Inc() - return result + hadesPermutation(state) + return new(felt.Felt).Set(&state[0]) } var one = new(felt.Felt).SetUint64(1) @@ -104,7 +77,7 @@ func PoseidonArray(elems ...*felt.Felt) *felt.Felt { for i := 0; i < len(elems)/2; i++ { state[0].Add(&state[0], elems[2*i]) state[1].Add(&state[1], elems[2*i+1]) - HadesPermutation(state) + hadesPermutation(state) } rem := len(elems) % 2 @@ -112,7 +85,7 @@ func PoseidonArray(elems ...*felt.Felt) *felt.Felt { state[0].Add(&state[0], elems[len(elems)-1]) } state[rem].Add(&state[rem], one) - HadesPermutation(state) + hadesPermutation(state) return new(felt.Felt).Set(&state[0]) } @@ -154,7 +127,7 @@ func (d *PoseidonDigest) Update(elems ...*felt.Felt) Digest { } else { d.state[0].Add(&d.state[0], d.lastElem) d.state[1].Add(&d.state[1], elems[idx]) - HadesPermutation(d.state[:]) + hadesPermutation(d.state[:]) d.lastElem = nil } } @@ -168,7 +141,7 @@ func (d *PoseidonDigest) Finish() *felt.Felt { d.state[0].Add(&d.state[0], d.lastElem) d.state[1].Add(&d.state[1], one) } - HadesPermutation(d.state[:]) + hadesPermutation(d.state[:]) return &d.state[0] } diff --git a/core/state.go b/core/state.go index a9acdea5b9..41db641ad8 100644 --- a/core/state.go +++ b/core/state.go @@ -5,14 +5,15 @@ import ( "encoding/binary" "errors" "fmt" + "runtime" + "sort" + "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/NethermindEth/juno/encoder" "github.com/sourcegraph/conc/pool" - "runtime" - "sort" ) const globalTrieHeight = 251 diff --git a/core/trie/trie.go b/core/trie/trie.go index fc6b2eeee6..09215b476a 100644 --- a/core/trie/trie.go +++ b/core/trie/trie.go @@ -4,12 +4,13 @@ package trie import ( "errors" "fmt" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" "math/big" "strings" "sync" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/juno/db" @@ -752,6 +753,7 @@ func (t *Trie) Iterate(startValue *felt.Felt, consumer func(key, value *felt.Fel func (t *Trie) doIterate(startKey, key *Key, consumer func(key, value *felt.Felt) (bool, error)) (bool, error) { if key == nil { + // shouldn't it be finished == true here? return false, nil } diff --git a/sync/snap_server.go b/sync/snap_server.go index 171dfd4383..e24b226c38 100644 --- a/sync/snap_server.go +++ b/sync/snap_server.go @@ -3,6 +3,8 @@ package sync import ( "context" "errors" + "math/big" + "github.com/NethermindEth/juno/adapters/core2p2p" "github.com/NethermindEth/juno/adapters/p2p2core" "github.com/NethermindEth/juno/blockchain" @@ -11,7 +13,6 @@ import ( "github.com/NethermindEth/juno/core/trie" "github.com/NethermindEth/juno/p2p/starknet/spec" "github.com/NethermindEth/juno/utils/iter" - "math/big" ) type ContractRangeStreamingResult struct { @@ -58,9 +59,7 @@ type snapServer struct { blockchain SnapServerBlockchain } -var ( - _ SnapServerBlockchain = &blockchain.Blockchain{} -) +var _ SnapServerBlockchain = &blockchain.Blockchain{} const maxNodePerRequest = 1024 * 1024 // I just want it to process faster func determineMaxNodes(specifiedMaxNodes uint64) uint64 { @@ -74,46 +73,6 @@ func determineMaxNodes(specifiedMaxNodes uint64) uint64 { return maxNodePerRequest } -func iterateWithLimit( - srcTrie *trie.Trie, - startAddr *felt.Felt, - limitAddr *felt.Felt, - maxNode 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 >= maxNode { - return false, nil - } - return true, nil - }) - - if err != nil { - return nil, finished, err - } - - return proof, finished, err -} - 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) @@ -130,7 +89,6 @@ func (b *snapServer) GetClassRange(ctx context.Context, request *spec.ClassRange return } - // TODO: Verify class trie ctrie, classCloser, err := s.ClassTrie() if err != nil { yield(nil, err) @@ -138,83 +96,57 @@ func (b *snapServer) GetClassRange(ctx context.Context, request *spec.ClassRange } defer classCloser() - response := &spec.Classes{ - Classes: make([]*spec.Class, 0), - } - - classkeys := []*felt.Felt{} startAddr := p2p2core.AdaptHash(request.Start) limitAddr := p2p2core.AdaptHash(request.End) if limitAddr.IsZero() { limitAddr = nil } - // TODO: loop this - proofs, _, err := iterateWithLimit(ctrie, startAddr, limitAddr, determineMaxNodes(uint64(request.ChunksPerProof)), func(key, value *felt.Felt) error { - classkeys = append(classkeys, key) - return nil - }) + for { + response := &spec.Classes{ + Classes: make([]*spec.Class, 0), + } - coreclasses, err := b.blockchain.GetClasses(classkeys) - if err != nil { - yield(nil, err) - return - } + 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 + }) - for _, coreclass := range coreclasses { - if coreclass == nil { - yield(nil, errors.New("class is nil")) + coreclasses, err := b.blockchain.GetClasses(classkeys) + if err != nil { + yield(nil, err) return } - response.Classes = append(response.Classes, core2p2p.AdaptClass(coreclass)) - } - 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)) + } - yield(&ClassRangeStreamingResult{ - ContractsRoot: contractRoot, - ClassesRoot: classRoot, - Range: response, - RangeProof: Core2P2pProof(proofs), - }, err) - } -} + if err != nil { + yield(nil, err) + return + } -func Core2P2pProof(proofs []trie.ProofNode) *spec.PatriciaRangeProof { - nodes := make([]*spec.PatriciaNode, len(proofs)) + shouldContinue := yield(&ClassRangeStreamingResult{ + ContractsRoot: contractRoot, + ClassesRoot: classRoot, + Range: response, + RangeProof: Core2P2pProof(proofs), + }, err) - 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), - }, - }, + if finished || !shouldContinue { + break } + startAddr = classkeys[len(classkeys)-1] } - } - return &spec.PatriciaRangeProof{ - Nodes: nodes, + // will this send a `Fin` as in https://github.com/starknet-io/starknet-p2p-specs/blob/e335372d39b728372c0ff393bef78763deeb3fcb/p2p/proto/snapshot.proto#L77 + yield(nil, nil) } } @@ -234,7 +166,6 @@ func (b *snapServer) GetContractRange(ctx context.Context, request *spec.Contrac return } - // TODO: Verify class trie strie, scloser, err := s.StorageTrie() if err != nil { yield(nil, err) @@ -246,59 +177,58 @@ func (b *snapServer) GetContractRange(ctx context.Context, request *spec.Contrac limitAddr := p2p2core.AdaptAddress(request.End) states := []*spec.ContractState{} - proofs, _, 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) + 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 { - return err + yield(nil, err) + return } - ctr, err := s.StorageTrieForAddr(key) - if err != nil { - return err - } + shouldContinue := yield(&ContractRangeStreamingResult{ + ContractsRoot: contractRoot, + ClassesRoot: classRoot, + Range: states, + RangeProof: Core2P2pProof(proofs), + }, nil) - croot, err := ctr.Root() - if err != nil { - return err + if finished || !shouldContinue { + break } + } - states = append(states, &spec.ContractState{ - Address: core2p2p.AdaptAddress(key), - Class: core2p2p.AdaptHash(classHash), - Storage: core2p2p.AdaptHash(croot), - Nonce: nonce.Uint64(), - }) - return nil - }) - - yield(&ContractRangeStreamingResult{ - ContractsRoot: contractRoot, - ClassesRoot: classRoot, - Range: states, - RangeProof: Core2P2pProof(proofs), - }, nil) + yield(nil, nil) } } -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) GetStorageRange(ctx context.Context, request *StorageRangeRequest) iter.Seq2[*StorageRangeStreamingResult, error] { return func(yield func(*StorageRangeStreamingResult, error) bool) { stateRoot := request.StateRoot @@ -339,7 +269,6 @@ func (b *snapServer) GetStorageRange(ctx context.Context, request *StorageRangeR RangeProof: Core2P2pProof(proofs), }, nil) }) - if err != nil { yield(nil, err) return @@ -354,14 +283,28 @@ func (b *snapServer) GetStorageRange(ctx context.Context, request *StorageRangeR } } +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, trie *trie.Trie, request *spec.StorageRangeQuery, maxChunkPerProof uint64, nodeLimit uint64, - yield func([]*spec.ContractStoredValue, []trie.ProofNode)) (int64, error) { - + yield func([]*spec.ContractStoredValue, []trie.ProofNode), +) (int64, error) { totalSent := int64(0) finished := false startAddr := p2p2core.AdaptFelt(request.Start.Key) @@ -382,7 +325,7 @@ func (b *snapServer) handleStorageRangeRequest( limit = nodeLimit } - proofs, finished, err := iterateWithLimit(trie, startAddr, endAddr, limit, func(key, value *felt.Felt) error { + proofs, finish, err := iterateWithLimit(trie, startAddr, endAddr, limit, func(key, value *felt.Felt) error { response = append(response, &spec.ContractStoredValue{ Key: core2p2p.AdaptFelt(key), Value: core2p2p.AdaptFelt(value), @@ -391,6 +334,7 @@ func (b *snapServer) handleStorageRangeRequest( startAddr = key return nil }) + finished = finish if err != nil { return 0, err @@ -415,3 +359,77 @@ func (b *snapServer) handleStorageRangeRequest( 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 index bf7be48886..4d928ae6b7 100644 --- a/sync/snap_server_test.go +++ b/sync/snap_server_test.go @@ -3,23 +3,27 @@ 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" - "testing" ) func TestClassRange(t *testing.T) { - var d db.DB - d, _ = pebble.New("/home/amirul/fastworkscratch3/juno_db/juno_mainnet", 128000000, 128, utils.NewNopZapLogger()) - bc := blockchain.New(d, &utils.Mainnet) // Needed because class loader need encoder to be registered - + t.Skip("You need to provide a valid path to the snapshot") + d, _ = pebble.New("/Users/pnowosie/juno/snapshots/juno-sepolia", 128000000, 128, utils.NewNopZapLogger()) + defer func() { _ = d.Close() }() + bc := blockchain.New(d, &utils.Sepolia) // Needed because class loader need encoder to be registered _, err := utils.NewZapLogger(utils.DEBUG, false) assert.NoError(t, err) @@ -34,22 +38,183 @@ func TestClassRange(t *testing.T) { blockchain: bc, } - // err = syncer.Run(context.Background()) - assert.NoError(t, err) - 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: 100, + 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("You need to provide a valid path to the snapshot") + d, _ = pebble.New("/Users/pnowosie/juno/snapshots/juno-sepolia", 128000000, 128, utils.NewNopZapLogger()) + defer func() { _ = d.Close() }() + bc := blockchain.New(d, &utils.Sepolia) // Needed because class loader need encoder to be registered + + _, err := utils.NewZapLogger(utils.DEBUG, false) + assert.NoError(t, err) + + 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 true + 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) { + 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, + }, + // Note: long root calc - comment when root is verified + //{ + // address: feltFromString("0x000000000000000000000000000000000000000000000000000000000001"), + // storageRoot: feltFromString("0x241ce4b3da62e79caf008c66ca5f3232e9628af90ba3fcb70974d0b8b30cd8b"), + // expectedLeaves: 66468, + //}, + } + + var d db.DB + t.Skip("You need to provide a valid path to the snapshot") + d, _ = pebble.New("/Users/pnowosie/juno/snapshots/juno-sepolia", 128000000, 128, utils.NewNopZapLogger()) + defer func() { _ = d.Close() }() + bc := blockchain.New(d, &utils.Sepolia) // Needed because class loader need encoder to be registered + + _, err := utils.NewZapLogger(utils.DEBUG, false) + assert.NoError(t, err) + + 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) + + 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 index 8ffce19b07..f13752a885 100644 --- a/sync/snapsyncer.go +++ b/sync/snapsyncer.go @@ -4,26 +4,26 @@ import ( "context" "errors" "fmt" - "github.com/NethermindEth/juno/adapters/core2p2p" - "github.com/NethermindEth/juno/adapters/p2p2core" - "github.com/NethermindEth/juno/core/crypto" - "github.com/NethermindEth/juno/core/trie" - "github.com/NethermindEth/juno/p2p/starknet/spec" - "github.com/NethermindEth/juno/starknetdata" - "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" 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" ) type Blockchain interface { @@ -457,7 +457,7 @@ func (s *SnapSyncher) runClassRangeWorker(ctx context.Context) error { } proofs := P2pProofToTrieProofs(response.RangeProof) - hasNext, err := VerifyTrie(response.ClassesRoot, paths, values, proofs, ClassTrieHeight, crypto.Poseidon) + hasNext, err := VerifyTrie(response.ClassesRoot, paths, values, proofs, core.GlobalTrieHeight, crypto.Poseidon) if err != nil { // Root verification failed // TODO: Ban peer @@ -547,9 +547,6 @@ func VerifyGlobalStateRoot(globalStateRoot *felt.Felt, classRoot *felt.Felt, sto return nil } -const ClassTrieHeight = 251 -const ContractTrieDepth = 251 - func CalculateClassHash(cls core.Class) *felt.Felt { hash, err := cls.Hash() if err != nil { @@ -598,7 +595,7 @@ func (s *SnapSyncher) runContractRangeWorker(ctx context.Context) error { } proofs := P2pProofToTrieProofs(response.RangeProof) - hasNext, ierr := VerifyTrie(response.ContractsRoot, paths, values, proofs, ContractTrieDepth, crypto.Pedersen) + hasNext, ierr := VerifyTrie(response.ContractsRoot, paths, values, proofs, core.GlobalTrieHeight, crypto.Pedersen) if ierr != nil { err = ierr // The peer should get penalized in this case @@ -829,7 +826,7 @@ func (s *SnapSyncher) runStorageRangeWorker(ctx context.Context, workerIdx int) } proofs := P2pProofToTrieProofs(response.RangeProof) - hasNext, err := VerifyTrie(job.storageRoot, paths, values, proofs, ContractTrieDepth, crypto.Pedersen) + 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) @@ -939,7 +936,6 @@ func (s *SnapSyncher) ApplyStateUpdate(blockNumber uint64) error { } func (s *SnapSyncher) runFetchClassJob(ctx context.Context) error { - keyBatches := make([]*felt.Felt, 0) for { @@ -1108,7 +1104,7 @@ func (s *SnapSyncher) runStorageRefreshWorker(ctx context.Context) error { } proofs := P2pProofToTrieProofs(response.RangeProof) - _, err = VerifyTrie(response.ContractsRoot, paths, values, proofs, ContractTrieDepth, crypto.Pedersen) + _, err = VerifyTrie(response.ContractsRoot, paths, values, proofs, core.GlobalTrieHeight, crypto.Pedersen) if err != nil { // The peer should get penalized in this case return false diff --git a/sync/snapsyncer_test.go b/sync/snapsyncer_test.go index b792d40202..acb36e4ca2 100644 --- a/sync/snapsyncer_test.go +++ b/sync/snapsyncer_test.go @@ -4,14 +4,15 @@ import ( "context" "encoding/hex" "errors" - "github.com/NethermindEth/juno/encoder" - "github.com/prometheus/client_golang/prometheus/promhttp" "math/big" "net/http" "os" "testing" "time" + "github.com/NethermindEth/juno/encoder" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/NethermindEth/juno/blockchain" "github.com/NethermindEth/juno/core" "github.com/NethermindEth/juno/core/felt" @@ -74,7 +75,6 @@ func V1ClassFromString(key, bytes string) (*felt.Felt, *felt.Felt, core.Class, e } func TestSnapOfflineCopy(t *testing.T) { - scenarios := []struct { name string scenario func(t *testing.T, bc *blockchain.Blockchain) error @@ -343,37 +343,37 @@ func (l localStarknetData) BlockLatest(ctx context.Context) (*core.Block, error) } func (l localStarknetData) BlockPending(ctx context.Context) (*core.Block, error) { - //TODO implement me + // TODO implement me panic("implement me") } func (l localStarknetData) Transaction(ctx context.Context, transactionHash *felt.Felt) (core.Transaction, error) { - //TODO implement me + // TODO implement me panic("implement me") } func (l localStarknetData) Class(ctx context.Context, classHash *felt.Felt) (core.Class, error) { - //TODO implement me + // TODO implement me panic("implement me") } func (l localStarknetData) StateUpdate(ctx context.Context, blockNumber uint64) (*core.StateUpdate, error) { - //TODO implement me + // TODO implement me panic("implement me") } func (l localStarknetData) StateUpdatePending(ctx context.Context) (*core.StateUpdate, error) { - //TODO implement me + // TODO implement me panic("implement me") } func (l localStarknetData) StateUpdateWithBlock(ctx context.Context, blockNumber uint64) (*core.StateUpdate, *core.Block, error) { - //TODO implement me + // TODO implement me panic("implement me") } func (l localStarknetData) StateUpdatePendingWithBlock(ctx context.Context) (*core.StateUpdate, *core.Block, error) { - //TODO implement me + // TODO implement me panic("implement me") }