diff --git a/proof_ipa.go b/proof_ipa.go index 53680854..a5604e20 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -27,15 +27,30 @@ package verkle import ( "bytes" - "encoding/binary" "errors" "sort" + "unsafe" ipa "github.com/crate-crypto/go-ipa" - "github.com/crate-crypto/go-ipa/bandersnatch/fp" "github.com/crate-crypto/go-ipa/common" ) +const IPA_PROOF_DEPTH = 8 + +type IPAProof struct { + CL [IPA_PROOF_DEPTH][32]byte `json:"cl"` + CR [IPA_PROOF_DEPTH][32]byte `json:"cr"` + FinalEvaluation [32]byte `json:"finalEvaluation"` +} + +type VerkleProof struct { + OtherStems [][31]byte `json:"otherStems"` + DepthExtensionPresent []byte `json:"depthExtensionPresent"` + CommitmentsByPath [][32]byte `json:"commitmentsByPath"` + D [32]byte `json:"d"` + IPAProof *IPAProof `json:"ipa_proof"` +} + type Proof struct { Multipoint *ipa.MultiProof // multipoint argument ExtStatus []byte // the extension status of each stem @@ -45,6 +60,20 @@ type Proof struct { Values [][]byte } +type SuffixStateDiff struct { + Suffix byte `json:"suffix"` + CurrentValue *[32]byte `json:"currentValue"` +} + +type SuffixStateDiffs []SuffixStateDiff + +type StemStateDiff struct { + Stem [31]byte `json:"stem"` + SuffixDiffs SuffixStateDiffs `json:"suffixDiffs"` +} + +type StateDiff []StemStateDiff + func GetCommitmentsForMultiproof(root VerkleNode, keys [][]byte) (*ProofElements, []byte, [][]byte) { sort.Sort(keylist(keys)) return root.GetProofItems(keylist(keys)) @@ -105,130 +134,124 @@ func VerifyVerkleProof(proof *Proof, Cs []*Point, indices []uint8, ys []*Fr, tc return ipa.CheckMultiProof(tr, tc.conf, proof.Multipoint, Cs, ys, indices) } -// A structure representing a tuple -type KeyValuePair struct { - Key []byte `json:"key"` - Value []byte `json:"value"` -} - // SerializeProof serializes the proof in the rust-verkle format: // * len(Proof of absence stem) || Proof of absence stems // * len(depths) || serialize(depth || ext statusi) // * len(commitments) || serialize(commitment) // * Multipoint proof // it also returns the serialized keys and values -func SerializeProof(proof *Proof) ([]byte, []KeyValuePair, error) { - var bufProof bytes.Buffer - - binary.Write(&bufProof, binary.LittleEndian, uint32(len(proof.PoaStems))) - for _, stem := range proof.PoaStems { - _, err := bufProof.Write(stem) - if err != nil { - return nil, nil, err - } +func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { + otherstems := make([][31]byte, len(proof.PoaStems)) + for i, stem := range proof.PoaStems { + copy(otherstems[i][:], stem[:]) } - binary.Write(&bufProof, binary.LittleEndian, uint32(len(proof.ExtStatus))) - for _, daes := range proof.ExtStatus { - err := bufProof.WriteByte(daes) - if err != nil { - return nil, nil, err - } - } - - binary.Write(&bufProof, binary.LittleEndian, uint32(len(proof.Cs))) - for _, C := range proof.Cs { + cbp := make([][32]byte, len(proof.Cs)) + for i, C := range proof.Cs { serialized := C.Bytes() - _, err := bufProof.Write(serialized[:]) - if err != nil { - return nil, nil, err - } + copy(cbp[i][:], serialized[:]) } - proof.Multipoint.Write(&bufProof) + var cls, crs [IPA_PROOF_DEPTH][32]byte + for i := 0; i < IPA_PROOF_DEPTH; i++ { - keyvals := make([]KeyValuePair, 0, len(proof.Keys)) + l := proof.Multipoint.IPA.L[i].Bytes() + copy(cls[i][:], l[:]) + r := proof.Multipoint.IPA.R[i].Bytes() + copy(crs[i][:], r[:]) + } + + var stemdiff *StemStateDiff + var statediff StateDiff for i, key := range proof.Keys { - var ( - valueLen = len(proof.Values[i]) - aligned []byte - ) + if stemdiff == nil || !bytes.Equal(stemdiff.Stem[:], key[:31]) { + statediff = append(statediff, StemStateDiff{}) + stemdiff = &statediff[len(statediff)-1] + copy(stemdiff.Stem[:], key[:31]) + } + var valueLen = len(proof.Values[i]) switch valueLen { - case 0, 32: - aligned = proof.Values[i] + case 0: + stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{ + Suffix: key[31], + }) + case 32: + stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{ + Suffix: key[31], + CurrentValue: (*[32]byte)(proof.Values[i]), + }) default: - aligned = make([]byte, 32) + 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])), + }) } - keyvals = append(keyvals, KeyValuePair{key, aligned}) } - - return bufProof.Bytes(), keyvals, nil + return &VerkleProof{ + OtherStems: otherstems, + DepthExtensionPresent: proof.ExtStatus, + CommitmentsByPath: cbp, + D: proof.Multipoint.D.Bytes(), + IPAProof: &IPAProof{ + CL: cls, + CR: crs, + FinalEvaluation: proof.Multipoint.IPA.A_scalar.Bytes(), + }, + }, statediff, nil } // DeserializeProof deserializes the proof found in blocks, into a format that // can be used to rebuild a stateless version of the tree. -func DeserializeProof(proofSerialized []byte, keyvals []KeyValuePair) (*Proof, error) { +func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { var ( - numPoaStems, numExtStatus uint32 - numCommitments uint32 - poaStems, keys, values [][]byte - extStatus []byte - commitments []*Point - multipoint ipa.MultiProof + poaStems, keys, values [][]byte + extStatus []byte + commitments []*Point + multipoint ipa.MultiProof ) - reader := bytes.NewReader(proofSerialized) - - if err := binary.Read(reader, binary.LittleEndian, &numPoaStems); err != nil { - return nil, err - } - poaStems = make([][]byte, numPoaStems) - for i := 0; i < int(numPoaStems); i++ { - var poaStem [31]byte - if err := binary.Read(reader, binary.LittleEndian, &poaStem); err != nil { - return nil, err - } + poaStems = make([][]byte, len(vp.OtherStems)) + for i, poaStem := range vp.OtherStems { poaStems[i] = poaStem[:] } - if err := binary.Read(reader, binary.LittleEndian, &numExtStatus); err != nil { - return nil, err - } - extStatus = make([]byte, numExtStatus) - for i := 0; i < int(numExtStatus); i++ { - var e byte - if err := binary.Read(reader, binary.LittleEndian, &e); err != nil { - return nil, err - } - extStatus[i] = e - } + extStatus = vp.DepthExtensionPresent - if err := binary.Read(reader, binary.LittleEndian, &numCommitments); err != nil { - return nil, err - } - commitments = make([]*Point, numCommitments) - commitmentBytes := make([]byte, fp.Bytes) - for i := 0; i < int(numCommitments); i++ { + commitments = make([]*Point, len(vp.CommitmentsByPath)) + for i, commitmentBytes := range vp.CommitmentsByPath { var commitment Point - if err := binary.Read(reader, binary.LittleEndian, commitmentBytes); err != nil { - return nil, err - } - - if err := commitment.SetBytes(commitmentBytes); err != nil { + if err := commitment.SetBytesTrusted(commitmentBytes[:]); err != nil { return nil, err } - commitments[i] = &commitment } - // TODO submit PR to go-ipa to make this return an error if it fails to Read - multipoint.Read(reader) + multipoint.D.SetBytes(vp.D[:]) + multipoint.IPA.A_scalar.SetBytes(vp.IPAProof.FinalEvaluation[:]) + multipoint.IPA.L = make([]Point, IPA_PROOF_DEPTH) + for i, b := range vp.IPAProof.CL { + multipoint.IPA.L[i].SetBytes(b[:]) + } + multipoint.IPA.R = make([]Point, IPA_PROOF_DEPTH) + for i, b := range vp.IPAProof.CR { + multipoint.IPA.R[i].SetBytes(b[:]) + } - // Turn keyvals into keys and values - for _, kv := range keyvals { - keys = append(keys, kv.Key) - values = append(values, kv.Value) + // turn statediff into keys and values + for _, stemdiff := range statediff { + for _, suffixdiff := range stemdiff.SuffixDiffs { + var k [32]byte + copy(k[:31], stemdiff.Stem[:]) + k[31] = suffixdiff.Suffix + keys = append(keys, k[:]) + if suffixdiff.CurrentValue != nil { + values = append(values, suffixdiff.CurrentValue[:]) + } else { + values = append(values, nil) + } + } } proof := Proof{ @@ -239,7 +262,6 @@ func DeserializeProof(proofSerialized []byte, keyvals []KeyValuePair) (*Proof, e keys, values, } - return &proof, nil } diff --git a/proof_test.go b/proof_test.go index 60c9dd88..8e96d370 100644 --- a/proof_test.go +++ b/proof_test.go @@ -28,7 +28,6 @@ package verkle import ( "bytes" "crypto/rand" - "encoding/binary" "encoding/hex" "testing" ) @@ -265,26 +264,21 @@ func TestProofSerializationNoAbsentStem(t *testing.T) { proof, _, _, _, _ := MakeVerkleMultiProof(root, [][]byte{keys[0]}, map[string][]byte{}) - serialized, _, err := SerializeProof(proof) + vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) } - if len(serialized) == 0 { - t.Fatal("zero-length serialized proof payload") + if len(vp.OtherStems) > 0 { + t.Fatalf("first byte indicates that there are %d stems that should not be here", len(vp.OtherStems)) } - stemsize := binary.LittleEndian.Uint32(serialized[:4]) - if stemsize != 0 { - t.Fatalf("first byte indicates that there are %d stems that should not be here", stemsize) - } - extsize := binary.LittleEndian.Uint32(serialized[4:8]) + extsize := len(statediff) if extsize != 1 { t.Fatalf("second byte indicates that there are %d extension statuses, should be 1", extsize) } - // TODO keep checking the serialized values here } func TestProofSerializationWithAbsentStem(t *testing.T) { - const leafCount = 1000 + const leafCount = 256 keys := make([][]byte, leafCount) root := New() @@ -304,18 +298,15 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { proof, _, _, _, _ := MakeVerkleMultiProof(root, [][]byte{absentkey[:]}, map[string][]byte{}) - serialized, _, err := SerializeProof(proof) + vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) } - if len(serialized) == 0 { - t.Fatal("zero-length serialized proof payload") - } - stemsize := binary.LittleEndian.Uint32(serialized[:4]) + stemsize := len(vp.OtherStems) if stemsize != 1 { t.Fatalf("first byte indicates that there are %d stems that should not be here", stemsize) } - extsize := binary.LittleEndian.Uint32(serialized[4+stemsize*31 : 4+stemsize*31+4]) + extsize := len(statediff) if extsize != 1 { t.Fatalf("second byte indicates that there are %d extension statuses, should be 1", extsize) } @@ -323,20 +314,15 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { } func TestProofDeserialize(t *testing.T) { - const leafCount = 1000 + const leafCount = 256 keys := make([][]byte, leafCount) root := New() - var keyvals []KeyValuePair for i := 0; i < leafCount; i++ { key := make([]byte, 32) key[2] = byte(i) keys[i] = key root.Insert(key, fourtyKeyTest, nil) - keyvals = append(keyvals, KeyValuePair{ - Key: key, - Value: fourtyKeyTest, - }) } // Create stem 0x0000020100000.... that is not present in the tree, @@ -348,15 +334,12 @@ func TestProofDeserialize(t *testing.T) { proof, _, _, _, _ := MakeVerkleMultiProof(root, [][]byte{absentkey[:]}, map[string][]byte{}) - serialized, _, err := SerializeProof(proof) + vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) } - if len(serialized) == 0 { - t.Fatal("zero-length serialized proof payload") - } - deserialized, err := DeserializeProof(serialized, keyvals) + deserialized, err := DeserializeProof(vp, statediff) if err != nil { t.Fatalf("could not deserialize verkle proof: %v", err) } @@ -370,38 +353,7 @@ func TestProofDeserialize(t *testing.T) { } func TestProofDeserializeErrors(t *testing.T) { - - deserialized, err := DeserializeProof([]byte{0}, nil) - if err == nil { - t.Fatal("deserializing invalid proof didn't cause an error") - } - if deserialized != nil { - t.Fatalf("non-nil deserialized data returned %v", deserialized) - } - - deserialized, err = DeserializeProof([]byte{1, 0, 0, 0}, nil) - if err == nil { - t.Fatal("deserializing invalid proof didn't cause an error") - } - if deserialized != nil { - t.Fatalf("non-nil deserialized data returned %v", deserialized) - } - - deserialized, err = DeserializeProof([]byte{0, 0, 0, 0, 0}, nil) - if err == nil { - t.Fatal("deserializing invalid proof didn't cause an error") - } - if deserialized != nil { - t.Fatalf("non-nil deserialized data returned %v", deserialized) - } - - deserialized, err = DeserializeProof([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0}, nil) - if err == nil { - t.Fatal("deserializing invalid proof didn't cause an error") - } - if deserialized != nil { - t.Fatalf("non-nil deserialized data returned %v", deserialized) - } + // TODO } func TestProofOfAbsenceEdgeCase(t *testing.T) { diff --git a/stateless_test.go b/stateless_test.go index 5c4b6b2b..35bce51e 100644 --- a/stateless_test.go +++ b/stateless_test.go @@ -263,19 +263,15 @@ func TestStatelessDeserialize(t *testing.T) { for _, k := range [][]byte{zeroKeyTest, oneKeyTest, fourtyKeyTest, ffx32KeyTest} { root.Insert(k, fourtyKeyTest, nil) } - keyvals := []KeyValuePair{ - {zeroKeyTest, fourtyKeyTest}, - {fourtyKeyTest, fourtyKeyTest}, - } proof, _, _, _, _ := MakeVerkleMultiProof(root, keylist{zeroKeyTest, fourtyKeyTest}, map[string][]byte{string(zeroKeyTest): fourtyKeyTest, string(fourtyKeyTest): fourtyKeyTest}) - serialized, _, err := SerializeProof(proof) + serialized, statediff, err := SerializeProof(proof) if err != nil { t.Fatalf("could not serialize proof: %v", err) } - dproof, err := DeserializeProof(serialized, keyvals) + dproof, err := DeserializeProof(serialized, statediff) if err != nil { t.Fatalf("error deserializing proof: %v", err) } @@ -304,19 +300,15 @@ func TestStatelessDeserializeMissginChildNode(t *testing.T) { for _, k := range [][]byte{zeroKeyTest, oneKeyTest, ffx32KeyTest} { root.Insert(k, fourtyKeyTest, nil) } - keyvals := []KeyValuePair{ - {zeroKeyTest, fourtyKeyTest}, - {fourtyKeyTest, nil}, - } proof, _, _, _, _ := MakeVerkleMultiProof(root, keylist{zeroKeyTest, fourtyKeyTest}, map[string][]byte{string(zeroKeyTest): fourtyKeyTest, string(fourtyKeyTest): nil}) - serialized, _, err := SerializeProof(proof) + serialized, statediff, err := SerializeProof(proof) if err != nil { t.Fatalf("could not serialize proof: %v", err) } - dproof, err := DeserializeProof(serialized, keyvals) + dproof, err := DeserializeProof(serialized, statediff) if err != nil { t.Fatalf("error deserializing proof: %v", err) } @@ -345,19 +337,15 @@ func TestStatelessDeserializeDepth2(t *testing.T) { for _, k := range [][]byte{zeroKeyTest, key1} { root.Insert(k, fourtyKeyTest, nil) } - keyvals := []KeyValuePair{ - {zeroKeyTest, fourtyKeyTest}, - {key1, nil}, - } proof, _, _, _, _ := MakeVerkleMultiProof(root, keylist{zeroKeyTest, key1}, map[string][]byte{string(zeroKeyTest): fourtyKeyTest, string(key1): nil}) - serialized, _, err := SerializeProof(proof) + serialized, statediff, err := SerializeProof(proof) if err != nil { t.Fatalf("could not serialize proof: %v", err) } - dproof, err := DeserializeProof(serialized, keyvals) + dproof, err := DeserializeProof(serialized, statediff) if err != nil { t.Fatalf("error deserializing proof: %v", err) } @@ -384,19 +372,15 @@ func TestStatelessGetProofItems(t *testing.T) { for _, k := range insertedKeys { root.Insert(k, fourtyKeyTest, nil) } - keyvals := []KeyValuePair{ - {zeroKeyTest, fourtyKeyTest}, - {fourtyKeyTest, nil}, - } proof, _, _, _, _ := MakeVerkleMultiProof(root, keylist(provenKeys), map[string][]byte{string(zeroKeyTest): fourtyKeyTest, string(fourtyKeyTest): nil}) - serialized, _, err := SerializeProof(proof) + serialized, statediff, err := SerializeProof(proof) if err != nil { t.Fatalf("could not serialize proof: %v", err) } - dproof, err := DeserializeProof(serialized, keyvals) + dproof, err := DeserializeProof(serialized, statediff) if err != nil { t.Fatalf("error deserializing proof: %v", err) } diff --git a/tree_test.go b/tree_test.go index 2d080502..fed03931 100644 --- a/tree_test.go +++ b/tree_test.go @@ -1147,22 +1147,18 @@ func TestRustBanderwagonBlock48(t *testing.T) { r := tree.Commit() proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, keys, initialVals) - serialized, _, err := SerializeProof(proof) + vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) } - t.Logf("serialized proof=%x", serialized) + t.Logf("serialized proof=%v", vp) cfg := GetConfig() if !VerifyVerkleProof(proof, cis, zis, yis, cfg) { t.Fatal("proof didn't verify") } - var kvp []KeyValuePair - for i := range keys { - kvp = append(kvp, KeyValuePair{Key: keys[i], Value: vals[i]}) - } - dproof, err := DeserializeProof(serialized, kvp) + dproof, err := DeserializeProof(vp, statediff) if err != nil { t.Fatal(err) }