diff --git a/proof_ipa.go b/proof_ipa.go index 5df38b1f..3609b1d6 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -58,12 +58,14 @@ type Proof struct { Cs []*Point // commitments, sorted by their path in the tree PoaStems [][]byte // stems proving another stem is absent Keys [][]byte - Values [][]byte + PreValues [][]byte + PostValues [][]byte } type SuffixStateDiff struct { Suffix byte `json:"suffix"` CurrentValue *[32]byte `json:"currentValue"` + NewValue *[32]byte `json:"newValue"` } type SuffixStateDiffs []SuffixStateDiff @@ -80,38 +82,78 @@ func GetCommitmentsForMultiproof(root VerkleNode, keys [][]byte, resolver NodeRe return root.GetProofItems(keylist(keys), resolver) } -func MakeVerkleMultiProof(root VerkleNode, keys [][]byte, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { +// getProofElementsFromTree factors the logic that is used both in the proving and verification methods. It takes a pre-state +// tree and an optional post-state tree, extracts the proof data from them and returns all the items required to build/verify +// a proof. +func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, resolver NodeResolverFn) (*ProofElements, []byte, [][]byte, [][]byte, []*Point, [][]Fr, []*Fr, []byte, error) { // go-ipa won't accept no key as an input, catch this corner case // and return an empty result. if len(keys) == 0 { - return nil, nil, nil, nil, errors.New("no key provided for proof") + return nil, nil, nil, nil, nil, nil, nil, nil, errors.New("no key provided for proof") } - tr := common.NewTranscript("vt") - root.Commit() + pe, es, poas, err := GetCommitmentsForMultiproof(preroot, keys, resolver) + if err != nil { + return nil, nil, nil, nil, nil, nil, nil, nil, fmt.Errorf("error getting pre-state proof data: %w", err) + } + + // List of points and vectors and indices used to generate the IPA proof + // If only a pre-state root is available, it's a simple copy, but if both + // pre- and post-state root are present, it's merging the two lists. + var ( + proof_cis []*Point + proof_fs [][]Fr + proof_zs []uint8 + proof_ys []*Fr + postvals = make([][]byte, len(keys)) + ) + for i := range pe.Cis { + proof_cis = append(proof_cis, pe.Cis[i]) + proof_fs = append(proof_fs, pe.Fis[i]) + proof_ys = append(proof_ys, pe.Yis[i]) + proof_zs = append(proof_zs, pe.Zis[i]) + } - pe, es, poas, err := GetCommitmentsForMultiproof(root, keys, resolver) + // if a post-state tree is present, merge its proof elements with + // those of the pre-state tree, so that they can be proved together. + if postroot != nil { + pe_post, _, _, err := GetCommitmentsForMultiproof(postroot, keys, resolver) + if err != nil { + return nil, nil, nil, nil, nil, nil, nil, nil, fmt.Errorf("error getting post-state proof data: %w", err) + } + + for i := range pe_post.Cis { + proof_cis = append(proof_cis, pe_post.Cis[i]) + proof_fs = append(proof_fs, pe_post.Fis[i]) + proof_ys = append(proof_ys, pe_post.Yis[i]) + proof_zs = append(proof_zs, pe_post.Zis[i]) + } + + // Set the post values, if they are untouched, leave them `nil` + for i, v := range pe.Vals { + if !bytes.Equal(v, pe_post.Vals[i]) { + postvals[i] = pe_post.Vals[i] + } + } + } + + // [0:3]: proof elements of the pre-state trie for serialization, + // 3: values to be inserted in the post-state trie for serialization + // [4:8]: aggregated pre+post elements for proving + return pe, es, poas, postvals, proof_cis, proof_fs, proof_ys, proof_zs, nil +} + +func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { + pe, es, poas, postvals, proof_cis, proof_fs, _, proof_zs, err := getProofElementsFromTree(preroot, postroot, keys, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("get commitments for multiproof: %s", err) } - // NOTE this is leftover code from the time the proof was - // made against the POST state. Since proofs are expected - // to prove PRE and POST state in the future, I'm leaving - // this for reference - eventhough it's unlikely that the - // final version will look like this, but you never know. - // var vals [][]byte - // for _, k := range keys { - // // TODO at the moment, do not include the post-data - // // val, _ := root.Get(k, nil) - // // vals = append(vals, val) - // vals = append(vals, keyvals[string(k)]) - // } - cfg := GetConfig() - mpArg, err := ipa.CreateMultiProof(tr, cfg.conf, pe.Cis, pe.Fis, pe.Zis) + tr := common.NewTranscript("vt") + mpArg, err := ipa.CreateMultiProof(tr, cfg.conf, proof_cis, proof_fs, proof_zs) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("creating multiproof: %s", err) + return nil, nil, nil, nil, fmt.Errorf("creating multiproof: %w", err) } // It's wheel-reinvention time again 🎉: reimplement a basic @@ -130,17 +172,34 @@ func MakeVerkleMultiProof(root VerkleNode, keys [][]byte, resolver NodeResolverF for i, path := range paths { cis[i] = pe.ByPath[path] } + proof := &Proof{ Multipoint: mpArg, Cs: cis, ExtStatus: es, PoaStems: poas, Keys: keys, - Values: pe.Vals, + PreValues: pe.Vals, + PostValues: postvals, } return proof, pe.Cis, pe.Zis, pe.Yis, nil } +// VerifyVerkleProofWithPreAndPostTrie takes a pre-state trie, a post-state trie and a list of keys, and verifies that +// the provided proof verifies. If the post-state trie is `nil`, the behavior is the same as `VerifyVerkleProof`. +func VerifyVerkleProofWithPreAndPostTrie(proof *Proof, preroot, postroot VerkleNode) error { + _, _, _, _, proof_cis, _, proof_ys, proof_zs, err := getProofElementsFromTree(preroot, postroot, proof.Keys, nil) + if err != nil { + return fmt.Errorf("error getting proof elements: %w", err) + } + + if ok, err := VerifyVerkleProof(proof, proof_cis, proof_zs, proof_ys, GetConfig()); !ok || err != nil { + return fmt.Errorf("error verifying proof: verifies=%v, error=%w", ok, err) + } + + return nil +} + func VerifyVerkleProof(proof *Proof, Cs []*Point, indices []uint8, ys []*Fr, tc *Config) (bool, error) { tr := common.NewTranscript("vt") return ipa.CheckMultiProof(tr, tc.conf, proof.Multipoint, Cs, ys, indices) @@ -181,26 +240,35 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { stemdiff = &statediff[len(statediff)-1] copy(stemdiff.Stem[:], key[:31]) } - var valueLen = len(proof.Values[i]) + stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{Suffix: key[31]}) + newsd := &stemdiff.SuffixDiffs[len(stemdiff.SuffixDiffs)-1] + + var valueLen = len(proof.PreValues[i]) + switch valueLen { + case 0: + // null value + case 32: + newsd.CurrentValue = (*[32]byte)(proof.PreValues[i]) + default: + var aligned [32]byte + copy(aligned[:valueLen], proof.PreValues[i]) + newsd.CurrentValue = (*[32]byte)(unsafe.Pointer(&aligned[0])) + } + + valueLen = len(proof.PostValues[i]) switch valueLen { case 0: - stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{ - Suffix: key[31], - }) + // null value case 32: - stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{ - Suffix: key[31], - CurrentValue: (*[32]byte)(proof.Values[i]), - }) + newsd.NewValue = (*[32]byte)(proof.PostValues[i]) default: + // TODO remove usage of unsafe var aligned [32]byte - copy(aligned[:valueLen], proof.Values[i]) - stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{ - Suffix: key[31], - CurrentValue: (*[32]byte)(unsafe.Pointer(&aligned[0])), - }) + copy(aligned[:valueLen], proof.PostValues[i]) + newsd.NewValue = (*[32]byte)(unsafe.Pointer(&aligned[0])) } } + return &VerkleProof{ OtherStems: otherstems, DepthExtensionPresent: proof.ExtStatus, @@ -218,10 +286,11 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { // can be used to rebuild a stateless version of the tree. func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { var ( - poaStems, keys, values [][]byte - extStatus []byte - commitments []*Point - multipoint ipa.MultiProof + poaStems, keys [][]byte + prevalues, postvalues [][]byte + extStatus []byte + commitments []*Point + multipoint ipa.MultiProof ) poaStems = make([][]byte, len(vp.OtherStems)) @@ -259,9 +328,15 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { k[31] = suffixdiff.Suffix keys = append(keys, k[:]) if suffixdiff.CurrentValue != nil { - values = append(values, suffixdiff.CurrentValue[:]) + prevalues = append(prevalues, suffixdiff.CurrentValue[:]) } else { - values = append(values, nil) + prevalues = append(prevalues, nil) + } + + if suffixdiff.NewValue != nil { + postvalues = append(postvalues, suffixdiff.NewValue[:]) + } else { + postvalues = append(postvalues, nil) } } } @@ -272,7 +347,8 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { commitments, poaStems, keys, - values, + prevalues, + postvalues, } return &proof, nil } @@ -285,8 +361,8 @@ type stemInfo struct { stem []byte } -// TreeFromProof builds a stateless tree from the proof -func TreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // skipcq: GO-R1005 +// PreStateTreeFromProof builds a stateless prestate tree from the proof. +func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // skipcq: GO-R1005 stems := make([][]byte, 0, len(proof.Keys)) for _, k := range proof.Keys { if len(stems) == 0 || !bytes.Equal(stems[len(stems)-1], k[:31]) { @@ -322,8 +398,8 @@ func TreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // skipcq: stemPath := stems[stemIndex][:len(path)] si.values = map[byte][]byte{} for i, k := range proof.Keys { - if bytes.Equal(k[:len(path)], stemPath) && proof.Values[i] != nil { - si.values[k[31]] = proof.Values[i] + if bytes.Equal(k[:len(path)], stemPath) && proof.PreValues[i] != nil { + si.values[k[31]] = proof.PreValues[i] si.has_c1 = si.has_c1 || (k[31] < 128) si.has_c2 = si.has_c2 || (k[31] >= 128) // This key has values, its stem is the one that @@ -356,14 +432,14 @@ func TreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // skipcq: // but not for block validation. values := make([][]byte, NodeWidth) for i, k := range proof.Keys { - if len(proof.Values[i]) == 0 { + if len(proof.PreValues[i]) == 0 { // Skip the nil keys, they are here to prove // an absence. continue } if bytes.Equal(k[:31], info[string(p)].stem) { - values[k[31]] = proof.Values[i] + values[k[31]] = proof.PreValues[i] } } comms, err = root.CreatePath(p, info[string(p)], comms, values) @@ -374,3 +450,36 @@ func TreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // skipcq: return root, nil } + +// PostStateTreeFromProof uses the pre-state trie and the list of updated values +// to produce the stateless post-state trie. +func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff) (VerkleNode, error) { + postroot := preroot.Copy() + + for _, stemstatediff := range statediff { + var ( + values = make([][]byte, NodeWidth) + overwrites bool + ) + + for _, suffixdiff := range stemstatediff.SuffixDiffs { + if /* len(suffixdiff.NewValue) > 0 - this only works for a slice */ suffixdiff.NewValue != nil { + // if this value is non-nil, it means InsertStem should be + // called, otherwise, skip updating the tree. + overwrites = true + values[suffixdiff.Suffix] = suffixdiff.NewValue[:] + } + } + + if overwrites { + var stem [31]byte + copy(stem[:31], stemstatediff.Stem[:]) + if err := postroot.(*InternalNode).InsertStem(stem[:], values, nil); err != nil { + return nil, fmt.Errorf("error overwriting value in post state: %w", err) + } + } + } + postroot.Commit() + + return postroot, nil +} diff --git a/proof_json.go b/proof_json.go index 197d3aa5..8d775ee0 100644 --- a/proof_json.go +++ b/proof_json.go @@ -147,7 +147,7 @@ func (vp *VerkleProof) UnmarshalJSON(data []byte) error { var aux verkleProofMarshaller err := json.Unmarshal(data, &aux) if err != nil { - return err + return fmt.Errorf("verkle proof unmarshal error: %w", err) } vp.DepthExtensionPresent, err = PrefixedHexStringToBytes(aux.DepthExtensionPresent) @@ -198,7 +198,7 @@ func (ssd StemStateDiff) MarshalJSON() ([]byte, error) { func (ssd *StemStateDiff) UnmarshalJSON(data []byte) error { var aux stemStateDiffMarshaller if err := json.Unmarshal(data, &aux); err != nil { - return err + return fmt.Errorf("stemdiff unmarshal error: %w", err) } stem, err := PrefixedHexStringToBytes(aux.Stem) @@ -215,17 +215,23 @@ func (ssd *StemStateDiff) UnmarshalJSON(data []byte) error { type suffixStateDiffMarshaller struct { Suffix byte `json:"suffix"` CurrentValue *string `json:"currentValue"` + NewValue *string `json:"newValue"` } func (ssd SuffixStateDiff) MarshalJSON() ([]byte, error) { - var cvstr *string + var cvstr, nvstr *string if ssd.CurrentValue != nil { tempstr := HexToPrefixedString(ssd.CurrentValue[:]) cvstr = &tempstr } + if ssd.NewValue != nil { + tempstr := HexToPrefixedString(ssd.NewValue[:]) + nvstr = &tempstr + } return json.Marshal(&suffixStateDiffMarshaller{ Suffix: ssd.Suffix, CurrentValue: cvstr, + NewValue: nvstr, }) } @@ -233,7 +239,7 @@ func (ssd *SuffixStateDiff) UnmarshalJSON(data []byte) error { aux := &suffixStateDiffMarshaller{} if err := json.Unmarshal(data, &aux); err != nil { - return err + return fmt.Errorf("suffix diff unmarshal error: %w", err) } if aux.CurrentValue != nil && len(*aux.CurrentValue) != 64 && len(*aux.CurrentValue) != 0 && len(*aux.CurrentValue) != 66 { @@ -254,5 +260,15 @@ func (ssd *SuffixStateDiff) UnmarshalJSON(data []byte) error { copy(ssd.CurrentValue[:], currentValueBytes) } + if aux.NewValue != nil && len(*aux.NewValue) != 0 { + newValueBytes, err := PrefixedHexStringToBytes(*aux.NewValue) + if err != nil { + return fmt.Errorf("error decoding hex string for current value: %v", err) + } + + ssd.NewValue = &[32]byte{} + copy(ssd.NewValue[:], newValueBytes) + } + return nil } diff --git a/proof_json_test.go b/proof_json_test.go new file mode 100644 index 00000000..743b0f3b --- /dev/null +++ b/proof_json_test.go @@ -0,0 +1,24 @@ +package verkle + +import ( + "encoding/json" + "testing" +) + +func TestJSONDeserialization(t *testing.T) { + str := `{ + "stem": "0x97233a822ee74c294ccec8e4e0c65106b374d4423d5d09236d0f6c6647e185", + "suffixDiffs": [ + { "suffix": 0, "currentValue": null, "newValue": null }, + { "suffix": 1, "currentValue": null, "newValue": null }, + { "suffix": 2, "currentValue": null, "newValue": null }, + { "suffix": 3, "currentValue": null, "newValue": null }, + { "suffix": 4, "currentValue": null, "newValue": null } + ] + }` + var statediff StemStateDiff + err := json.Unmarshal([]byte(str), &statediff) + if err != nil { + t.Fatal(err) + } +} diff --git a/proof_test.go b/proof_test.go index d111078b..5e87e71b 100644 --- a/proof_test.go +++ b/proof_test.go @@ -20,7 +20,6 @@ // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. -// // For more information, please refer to package verkle @@ -30,6 +29,7 @@ import ( "crypto/rand" "encoding/hex" "encoding/json" + "fmt" "reflect" "testing" ) @@ -43,7 +43,7 @@ func TestProofVerifyTwoLeaves(t *testing.T) { root.Insert(ffx32KeyTest, zeroKeyTest, nil) root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, [][]byte{ffx32KeyTest}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, nil) cfg := GetConfig() if ok, err := VerifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -64,8 +64,9 @@ func TestProofVerifyMultipleLeaves(t *testing.T) { keys[i] = key root.Insert(key, fourtyKeyTest, nil) } + root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, [][]byte{keys[0]}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, nil) cfg := GetConfig() if ok, err := VerifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -86,8 +87,9 @@ func TestMultiProofVerifyMultipleLeaves(t *testing.T) { keys[i] = key root.Insert(key, fourtyKeyTest, nil) } + root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, keys[0:2], nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], nil) pe, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil) if err != nil { @@ -118,12 +120,14 @@ func TestMultiProofVerifyMultipleLeavesWithAbsentStem(t *testing.T) { copy(absentstem[:], key[:31]) } } + root.Commit() + absent := make([]byte, 32) absent[2] = 3 // not in the proof, but leads to a stem absent[3] = 1 // and the stem differs keys = append(keys, absent) - proof, _, _, _, _ := MakeVerkleMultiProof(root, keys, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, nil) pe, _, isabsent, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { @@ -151,8 +155,9 @@ func TestMultiProofVerifyMultipleLeavesCommitmentRedundancy(t *testing.T) { root.Insert(keys[0], fourtyKeyTest, nil) keys[1] = oneKeyTest root.Insert(keys[1], fourtyKeyTest, nil) + root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, keys, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, nil) pe, _, _, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { @@ -170,8 +175,9 @@ func TestProofOfAbsenceInternalVerify(t *testing.T) { root := New() root.Insert(zeroKeyTest, zeroKeyTest, nil) root.Insert(oneKeyTest, zeroKeyTest, nil) + root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, [][]byte{ffx32KeyTest}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, nil) cfg := GetConfig() if ok, err := VerifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -185,8 +191,9 @@ func TestProofOfAbsenceLeafVerify(t *testing.T) { root := New() root.Insert(zeroKeyTest, zeroKeyTest, nil) root.Insert(ffx32KeyTest, zeroKeyTest, nil) + root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, [][]byte{oneKeyTest}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, nil) cfg := GetConfig() if ok, err := VerifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -199,13 +206,14 @@ func TestProofOfAbsenceLeafVerifyOtherSuffix(t *testing.T) { root := New() root.Insert(zeroKeyTest, zeroKeyTest, nil) root.Insert(ffx32KeyTest, zeroKeyTest, nil) + root.Commit() key := func() []byte { ret, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000080") return ret }() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, [][]byte{key}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, nil) cfg := GetConfig() if ok, err := VerifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -224,7 +232,8 @@ func TestProofOfAbsenceStemVerify(t *testing.T) { return ret }() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, [][]byte{key}, nil) + root.Commit() + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, nil) cfg := GetConfig() if ok, err := VerifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -246,7 +255,7 @@ func BenchmarkProofCalculation(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - MakeVerkleMultiProof(root, [][]byte{keys[len(keys)/2]}, nil) + MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, nil) } } @@ -261,7 +270,7 @@ func BenchmarkProofVerification(b *testing.B) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, [][]byte{keys[len(keys)/2]}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, nil) b.ResetTimer() b.ReportAllocs() @@ -286,7 +295,7 @@ func TestProofSerializationNoAbsentStem(t *testing.T) { root.Insert(key, fourtyKeyTest, nil) } - proof, _, _, _, _ := MakeVerkleMultiProof(root, [][]byte{keys[0]}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -314,6 +323,7 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { keys[i] = key root.Insert(key, fourtyKeyTest, nil) } + root.Commit() // Create stem 0x0000020100000.... that is not present in the tree, // however stem 0x0000020000000.... is present and will be returned @@ -322,7 +332,7 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, [][]byte{absentkey[:]}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -352,6 +362,7 @@ func TestProofDeserialize(t *testing.T) { keys[i] = key root.Insert(key, fourtyKeyTest, nil) } + root.Commit() // Create stem 0x0000020100000.... that is not present in the tree, // however stem 0x0000020000000.... is present and will be returned @@ -360,7 +371,7 @@ func TestProofDeserialize(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, [][]byte{absentkey[:]}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -390,7 +401,7 @@ func TestProofOfAbsenceEdgeCase(t *testing.T) { root.Commit() ret, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030303") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, [][]byte{ret}, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, nil) cfg := GetConfig() if ok, err := VerifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -409,7 +420,7 @@ func TestProofOfAbsenceOtherMultipleLeaves(t *testing.T) { ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030301") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, [][]byte{ret1, ret2}, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, nil) cfg := GetConfig() if ok, err := VerifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -430,7 +441,7 @@ func TestProofOfAbsenceNoneMultipleStems(t *testing.T) { ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030200") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, [][]byte{ret1, ret2}, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, nil) cfg := GetConfig() if ok, err := VerifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -462,7 +473,7 @@ func TestSuffixStateDiffJSONMarshalUn(t *testing.T) { }, } - expectedJSON := `{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00"}` + expectedJSON := `{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00","newValue":null}` actualJSON, err := json.Marshal(ssd) if err != nil { t.Errorf("error marshalling SuffixStateDiff to JSON: %v", err) @@ -503,7 +514,7 @@ func TestStemStateDiffJSONMarshalUn(t *testing.T) { }}, } - expectedJSON := `{"stem":"0x0a000000000000000000000000000000000000000000000000000000000000","suffixDiffs":[{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00"}]}` + expectedJSON := `{"stem":"0x0a000000000000000000000000000000000000000000000000000000000000","suffixDiffs":[{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00","newValue":null}]}` actualJSON, err := json.Marshal(ssd) if err != nil { t.Errorf("error marshalling SuffixStateDiff to JSON: %v", err) @@ -532,7 +543,7 @@ func TestSuffixStateDiffJSONMarshalUnCurrentValueNil(t *testing.T) { CurrentValue: nil, } - expectedJSON := `{"suffix":65,"currentValue":null}` + expectedJSON := `{"suffix":65,"currentValue":null,"newValue":null}` actualJSON, err := json.Marshal(ssd) if err != nil { t.Errorf("error marshalling SuffixStateDiff to JSON: %v", err) @@ -610,8 +621,9 @@ func TestStatelessDeserialize(t *testing.T) { for _, k := range [][]byte{zeroKeyTest, oneKeyTest, fourtyKeyTest, ffx32KeyTest} { root.Insert(k, fourtyKeyTest, nil) } + root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, keylist{zeroKeyTest, fourtyKeyTest}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{zeroKeyTest, fourtyKeyTest}, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -623,7 +635,7 @@ func TestStatelessDeserialize(t *testing.T) { t.Fatalf("error deserializing proof: %v", err) } - droot, err := TreeFromProof(dproof, root.Commit()) + droot, err := PreStateTreeFromProof(dproof, root.Commit()) if err != nil { t.Fatal(err) } @@ -650,7 +662,7 @@ func TestStatelessDeserializeMissingChildNode(t *testing.T) { root.Insert(k, fourtyKeyTest, nil) } - proof, _, _, _, _ := MakeVerkleMultiProof(root, keylist{zeroKeyTest, fourtyKeyTest}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{zeroKeyTest, fourtyKeyTest}, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -662,7 +674,7 @@ func TestStatelessDeserializeMissingChildNode(t *testing.T) { t.Fatalf("error deserializing proof: %v", err) } - droot, err := TreeFromProof(dproof, root.Commit()) + droot, err := PreStateTreeFromProof(dproof, root.Commit()) if err != nil { t.Fatal(err) } @@ -687,8 +699,9 @@ func TestStatelessDeserializeDepth2(t *testing.T) { for _, k := range [][]byte{zeroKeyTest, key1} { root.Insert(k, fourtyKeyTest, nil) } + root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, keylist{zeroKeyTest, key1}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{zeroKeyTest, key1}, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -700,7 +713,7 @@ func TestStatelessDeserializeDepth2(t *testing.T) { t.Fatalf("error deserializing proof: %v", err) } - droot, err := TreeFromProof(dproof, root.Commit()) + droot, err := PreStateTreeFromProof(dproof, root.Commit()) if err != nil { t.Fatal(err) } @@ -713,3 +726,105 @@ func TestStatelessDeserializeDepth2(t *testing.T) { t.Fatal("differing commitment for child #0") } } + +func TestProofVerificationWithPostState(t *testing.T) { + t.Parallel() + + testlist := []struct { + name string + keys, values, keystoprove, updatekeys, updatevalues [][]byte + }{ + { + // overwrite a key + name: "update_in_leaf_node", + keys: [][]byte{zeroKeyTest, oneKeyTest, ffx32KeyTest}, + values: [][]byte{zeroKeyTest, zeroKeyTest, zeroKeyTest}, + keystoprove: [][]byte{zeroKeyTest}, + updatekeys: [][]byte{zeroKeyTest}, + updatevalues: [][]byte{fourtyKeyTest}, + }, + { + // check for a key present at the root level + name: "new_key_in_internal_node", + keys: [][]byte{zeroKeyTest, oneKeyTest, ffx32KeyTest}, + values: [][]byte{zeroKeyTest, zeroKeyTest, zeroKeyTest}, + keystoprove: [][]byte{ffx32KeyTest, zeroKeyTest, fourtyKeyTest}, // all modified values must be proven + updatekeys: [][]byte{zeroKeyTest, fourtyKeyTest}, + updatevalues: [][]byte{fourtyKeyTest, fourtyKeyTest}, + }, + { + // prove an absent key at the root level + name: "absent_in_internal_node", + keys: [][]byte{zeroKeyTest, oneKeyTest, ffx32KeyTest}, + values: [][]byte{zeroKeyTest, zeroKeyTest, zeroKeyTest}, + keystoprove: [][]byte{zeroKeyTest, fourtyKeyTest}, + updatekeys: [][]byte{zeroKeyTest, fourtyKeyTest}, + updatevalues: [][]byte{fourtyKeyTest, fourtyKeyTest}, + }, + { + // prove an absent key at the leaf level + name: "absent_in_leaf_node", + keys: [][]byte{zeroKeyTest, fourtyKeyTest, ffx32KeyTest}, + values: [][]byte{zeroKeyTest, zeroKeyTest, zeroKeyTest}, + keystoprove: [][]byte{oneKeyTest, zeroKeyTest, fourtyKeyTest}, // all modified values must be proven + updatekeys: [][]byte{zeroKeyTest, fourtyKeyTest}, + updatevalues: [][]byte{oneKeyTest, fourtyKeyTest}, + }, + } + for _, data := range testlist { + data := data // make linter happy by not capturing the loop variable + + t.Run(fmt.Sprintf("verification_with_post_state/%s", data.name), func(t *testing.T) { + t.Parallel() + + if len(data.keys) != len(data.values) { + t.Fatalf("incompatible number of keys and values: %d != %d", len(data.keys), len(data.values)) + } + + if len(data.updatekeys) != len(data.updatevalues) { + t.Fatalf("incompatible number of post-state keys and values: %d != %d", len(data.updatekeys), len(data.updatevalues)) + } + + root := New() + for i := range data.keys { + root.Insert(data.keys[i], data.values[i], nil) + } + rootC := root.Commit() + + postroot := root.Copy() + for i := range data.updatekeys { + postroot.Insert(data.updatekeys[i], data.updatevalues[i], nil) + } + postroot.Commit() + + proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, nil) + + p, diff, err := SerializeProof(proof) + if err != nil { + t.Fatalf("error serializing proof: %v", err) + } + + dproof, err := DeserializeProof(p, diff) + if err != nil { + t.Fatalf("error deserializing proof: %v", err) + } + + if err = VerifyVerkleProofWithPreAndPostTrie(dproof, root, postroot); err != nil { + t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(root), ToDot(postroot)) + } + + dpreroot, err := PreStateTreeFromProof(dproof, rootC) + if err != nil { + t.Fatalf("error recreating pre tree: %v", err) + } + + dpostroot, err := PostStateTreeFromStateDiff(dpreroot, diff) + if err != nil { + t.Fatalf("error recreating post tree: %v", err) + } + if err = VerifyVerkleProofWithPreAndPostTrie(dproof, dpreroot, dpostroot); err != nil { + t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(dpreroot), ToDot(dpostroot)) + } + }) + } +} diff --git a/tree.go b/tree.go index 35330ddd..0ed5bff7 100644 --- a/tree.go +++ b/tree.go @@ -822,12 +822,15 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*Pr if child != nil { var c VerkleNode if _, ok := child.(HashedNode); ok { + childpath := make([]byte, n.depth+1) + copy(childpath[:n.depth+1], keys[0][:n.depth]) + childpath[n.depth] = byte(i) if resolver == nil { - return nil, nil, nil, fmt.Errorf("no resolver for path %x", keys[0][:n.depth+1]) + return nil, nil, nil, fmt.Errorf("no resolver for path %x", childpath) } - serialized, err := resolver(keys[0][:n.depth+1]) + serialized, err := resolver(childpath) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, fmt.Errorf("error resolving for path %x: %w", childpath, err) } c, err = ParseNode(serialized, n.depth+1) if err != nil { @@ -1321,7 +1324,7 @@ func leafToComms(poly []Fr, val []byte) error { return nil } -func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements, []byte, [][]byte, error) { +func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements, []byte, [][]byte, error) { // skipcq: GO-R1005 var ( poly [NodeWidth]Fr // top-level polynomial pe = &ProofElements{ @@ -1340,7 +1343,7 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements // Initialize the top-level polynomial with 1 + stem + C1 + C2 poly[0].SetUint64(1) if err := StemFromBytes(&poly[1], n.stem); err != nil { - return nil, nil, nil, err + return nil, nil, nil, fmt.Errorf("error serializing stem '%x': %w", n.stem, err) } banderwagon.BatchMapToScalarField([]*Fr{&poly[2], &poly[3]}, []*Point{n.c1, n.c2}) @@ -1517,7 +1520,7 @@ func (n *LeafNode) toDot(parent, path string) string { n.Commitment().MapToScalarField(&hash) ret := fmt.Sprintf("leaf%s [label=\"L: %x\nC: %x\nC₁: %x\nC₂:%x\"]\n%s -> leaf%s\n", path, hash.Bytes(), n.commitment.Bytes(), n.c1.Bytes(), n.c2.Bytes(), parent, path) for i, v := range n.values { - if v != nil { + if len(v) != 0 { ret = fmt.Sprintf("%sval%s%02x [label=\"%x\"]\nleaf%s -> val%s%02x\n", ret, path, i, v, path, path, i) } } diff --git a/tree_test.go b/tree_test.go index f335eb73..61b4fb4f 100644 --- a/tree_test.go +++ b/tree_test.go @@ -1180,7 +1180,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { r := tree.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, keys, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, nil) vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) @@ -1197,7 +1197,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { t.Fatal(err) } - droot, err := TreeFromProof(dproof, r) + droot, err := PreStateTreeFromProof(dproof, r) if err != nil { t.Fatal(err) }