From fb555050ba624a1828e461b9cadf6a5ed11f822c Mon Sep 17 00:00:00 2001 From: defonsen Date: Sun, 20 Apr 2025 23:17:10 +0530 Subject: [PATCH] Fix error handling, typos, and optimize performance --- doc.go | 3 +++ empty.go | 2 +- hashednode.go | 2 +- proof_ipa.go | 41 ++++++++++++++++++++++++----------------- unknown.go | 8 +++----- 5 files changed, 32 insertions(+), 24 deletions(-) diff --git a/doc.go b/doc.go index 258ec2c1..7d947623 100644 --- a/doc.go +++ b/doc.go @@ -32,12 +32,15 @@ var ( errDeleteHash = errors.New("trying to delete from a hashed subtree") errDeleteMissing = errors.New("trying to delete a missing group") errDeleteUnknown = errors.New("trying to delete an out-of-view node") + errDeleteEmpty = errors.New("can't delete an empty node") errReadFromInvalid = errors.New("trying to read from an invalid child") errSerializeHashedNode = errors.New("trying to serialize a hashed internal node") + errSerializeUnknownNode = errors.New("trying to serialize a subtree missing from the stateless view") errInsertIntoOtherStem = errors.New("insert splits a stem where it should not happen") errUnknownNodeType = errors.New("unknown node type detected") errMissingNodeInStateless = errors.New("trying to access a node that is missing from the stateless view") errIsPOAStub = errors.New("trying to read/write a proof of absence leaf node") + errGetProofItemsUnknownNode = errors.New("can't generate proof items for unknown node") ) const ( diff --git a/empty.go b/empty.go index bc6d8bb1..d0a3d34f 100644 --- a/empty.go +++ b/empty.go @@ -36,7 +36,7 @@ func (Empty) Insert([]byte, []byte, NodeResolverFn) error { } func (Empty) Delete([]byte, NodeResolverFn) (bool, error) { - return false, errors.New("cant delete an empty node") + return false, errDeleteEmpty } func (Empty) Get([]byte, NodeResolverFn) ([]byte, error) { diff --git a/hashednode.go b/hashednode.go index c0f75c44..2bb30024 100644 --- a/hashednode.go +++ b/hashednode.go @@ -37,7 +37,7 @@ func (HashedNode) Insert([]byte, []byte, NodeResolverFn) error { } func (HashedNode) Delete([]byte, NodeResolverFn) (bool, error) { - return false, errors.New("cant delete a hashed node in-place") + return false, errDeleteHash } func (HashedNode) Get([]byte, NodeResolverFn) ([]byte, error) { diff --git a/proof_ipa.go b/proof_ipa.go index f6947279..3eb15a23 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -294,22 +294,24 @@ func verifyVerkleProof(proof *Proof, Cs []*Point, indices []uint8, ys []*Fr, tc // * Multipoint proof // it also returns the serialized keys and values func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { + // SerializeProof serializes a proof into a format that can be stored in a block. + // it also returns the serialized keys and values otherstems := make([][StemSize]byte, len(proof.PoaStems)) - for i, stem := range proof.PoaStems { - copy(otherstems[i][:], stem) + for i, poaStem := range proof.PoaStems { + copy(otherstems[i][:], poaStem) } cbp := make([][32]byte, len(proof.Cs)) - for i, C := range proof.Cs { - serialized := C.Bytes() - copy(cbp[i][:], serialized[:]) + for i, c := range proof.Cs { + serializedC := c.Bytes() + copy(cbp[i][:], serializedC[:]) } var cls, crs [IPA_PROOF_DEPTH][32]byte - for i := 0; i < IPA_PROOF_DEPTH; i++ { - + for i := range proof.Multipoint.IPA.L { l := proof.Multipoint.IPA.L[i].Bytes() copy(cls[i][:], l[:]) + r := proof.Multipoint.IPA.R[i].Bytes() copy(crs[i][:], r[:]) } @@ -492,6 +494,13 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // i++ } + // Build an index of keys by stem to avoid O(N²) lookups + keyStemIndex := make(map[string][]int) + for j, k := range proof.Keys { + stem := KeyToStem(k) + keyStemIndex[string(stem)] = append(keyStemIndex[string(stem)], j) + } + // assign one or more stem to each stem info for i, es := range proof.ExtStatus { si := stemInfo{ @@ -503,16 +512,16 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // case extStatusAbsentEmpty: // All keys that are part of a proof of absence, must contain empty // prestate values. If that isn't the case, the proof is invalid. - for j := range proof.Keys { // TODO: DoS risk, use map or binary search. - if bytes.HasPrefix(proof.Keys[j], stems[i]) && proof.PreValues[j] != nil { + for _, j := range keyStemIndex[string(stems[i])] { + if proof.PreValues[j] != nil { return nil, fmt.Errorf("proof of absence (empty) stem %x has a value", si.stem) } } case extStatusAbsentOther: // All keys that are part of a proof of absence, must contain empty // prestate values. If that isn't the case, the proof is invalid. - for j := range proof.Keys { // TODO: DoS risk, use map or binary search. - if bytes.HasPrefix(proof.Keys[j], stems[i]) && proof.PreValues[j] != nil { + for _, j := range keyStemIndex[string(stems[i])] { + if proof.PreValues[j] != nil { return nil, fmt.Errorf("proof of absence (other) stem %x has a value", si.stem) } } @@ -537,12 +546,10 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // case extStatusPresent: si.values = map[byte][]byte{} si.stem = stems[i] - for j, k := range proof.Keys { // TODO: DoS risk, use map or binary search. - if bytes.Equal(KeyToStem(k), si.stem) { - si.values[k[StemSize]] = proof.PreValues[j] - si.has_c1 = si.has_c1 || (k[StemSize] < 128) - si.has_c2 = si.has_c2 || (k[StemSize] >= 128) - } + for _, j := range keyStemIndex[string(si.stem)] { + si.values[proof.Keys[j][StemSize]] = proof.PreValues[j] + si.has_c1 = si.has_c1 || (proof.Keys[j][StemSize] < 128) + si.has_c2 = si.has_c2 || (proof.Keys[j][StemSize] >= 128) } default: return nil, fmt.Errorf("invalid extension status: %d", si.stemType) diff --git a/unknown.go b/unknown.go index a84dcaa5..44ed1d11 100644 --- a/unknown.go +++ b/unknown.go @@ -25,8 +25,6 @@ package verkle -import "errors" - type UnknownNode struct{} func (UnknownNode) Insert([]byte, []byte, NodeResolverFn) error { @@ -34,7 +32,7 @@ func (UnknownNode) Insert([]byte, []byte, NodeResolverFn) error { } func (UnknownNode) Delete([]byte, NodeResolverFn) (bool, error) { - return false, errors.New("cant delete in a subtree missing form a stateless view") + return false, errDeleteUnknown } func (UnknownNode) Get([]byte, NodeResolverFn) ([]byte, error) { @@ -52,11 +50,11 @@ func (UnknownNode) Commitment() *Point { } func (UnknownNode) GetProofItems(Keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { - return nil, nil, nil, errors.New("can't generate proof items for unknown node") + return nil, nil, nil, errGetProofItemsUnknownNode } func (UnknownNode) Serialize() ([]byte, error) { - return nil, errors.New("trying to serialize a subtree missing from the statless view") + return nil, errSerializeUnknownNode } func (UnknownNode) Copy() VerkleNode {