Skip to content

Commit

Permalink
Fix/electra proof format json marshalling (#330)
Browse files Browse the repository at this point in the history
* fix: add custom JSON serializers

* move marshallers to their own file
  • Loading branch information
gballet committed Mar 6, 2023
1 parent 0cd2007 commit e88dff5
Show file tree
Hide file tree
Showing 2 changed files with 294 additions and 0 deletions.
210 changes: 210 additions & 0 deletions proof_json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// 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 <https://unlicense.org>

package verkle

import (
"encoding/hex"
"encoding/json"
"fmt"
)

type ipaproofMarshaller struct {
CL [IPA_PROOF_DEPTH]string `json:"cl"`
CR [IPA_PROOF_DEPTH]string `json:"cr"`
FinalEvaluation string `json:"finalEvaluation"`
}

func (ipp *IPAProof) MarshalJSON() ([]byte, error) {
return json.Marshal(&ipaproofMarshaller{
CL: [IPA_PROOF_DEPTH]string{
hex.EncodeToString(ipp.CL[0][:]),
hex.EncodeToString(ipp.CL[1][:]),
hex.EncodeToString(ipp.CL[2][:]),
hex.EncodeToString(ipp.CL[3][:]),
hex.EncodeToString(ipp.CL[4][:]),
hex.EncodeToString(ipp.CL[5][:]),
hex.EncodeToString(ipp.CL[6][:]),
hex.EncodeToString(ipp.CL[7][:]),
},
CR: [IPA_PROOF_DEPTH]string{
hex.EncodeToString(ipp.CR[0][:]),
hex.EncodeToString(ipp.CR[1][:]),
hex.EncodeToString(ipp.CR[2][:]),
hex.EncodeToString(ipp.CR[3][:]),
hex.EncodeToString(ipp.CR[4][:]),
hex.EncodeToString(ipp.CR[5][:]),
hex.EncodeToString(ipp.CR[6][:]),
hex.EncodeToString(ipp.CR[7][:]),
},
FinalEvaluation: hex.EncodeToString(ipp.FinalEvaluation[:]),
})
}

func (ipp *IPAProof) UnmarshalJSON(data []byte) error {
aux := &ipaproofMarshaller{}

if err := json.Unmarshal(data, &aux); err != nil {
return err
}

if len(aux.FinalEvaluation) != 64 {
return fmt.Errorf("invalid hex string for final evaluation: %s", aux.FinalEvaluation)
}

currentValueBytes, err := hex.DecodeString(aux.FinalEvaluation)
if err != nil {
return fmt.Errorf("error decoding hex string for current value: %v", err)
}
copy(ipp.FinalEvaluation[:], currentValueBytes)

for i := range ipp.CL {
if len(aux.CL[i]) != 64 {
return fmt.Errorf("invalid hex string for CL[%d]: %s", i, aux.CL[i])
}
val, err := hex.DecodeString(aux.CL[i])
if err != nil {
return fmt.Errorf("error decoding hex string for CL[%d]: %s", i, aux.CL[i])
}
copy(ipp.CL[i][:], val[:])
if len(aux.CR[i]) != 64 {
return fmt.Errorf("invalid hex string for CR[%d]: %s", i, aux.CR[i])
}
val, err = hex.DecodeString(aux.CR[i])
if err != nil {
return fmt.Errorf("error decoding hex string for CR[%d]: %s", i, aux.CR[i])
}
copy(ipp.CR[i][:], val[:])
}
copy(ipp.FinalEvaluation[:], currentValueBytes)

return nil
}

type verkleProofMarshaller struct {
OtherStems []string `json:"otherStems"`
DepthExtensionPresent string `json:"depthExtensionPresent"`
CommitmentsByPath []string `json:"commitmentsByPath"`
D string `json:"d"`
IPAProof *IPAProof `json:"ipa_proof"`
}

func (vp *VerkleProof) MarshalJSON() ([]byte, error) {
aux := &verkleProofMarshaller{
OtherStems: make([]string, len(vp.OtherStems)),
DepthExtensionPresent: hex.EncodeToString(vp.DepthExtensionPresent[:]),
CommitmentsByPath: make([]string, len(vp.CommitmentsByPath)),
D: hex.EncodeToString(vp.D[:]),
IPAProof: vp.IPAProof,
}

for i, s := range vp.OtherStems {
aux.OtherStems[i] = hex.EncodeToString(s[:])
}
for i, c := range vp.CommitmentsByPath {
aux.CommitmentsByPath[i] = hex.EncodeToString(c[:])
}
return json.Marshal(aux)
}

func (vp *VerkleProof) UnmarshalJSON(data []byte) error {
var aux verkleProofMarshaller
err := json.Unmarshal(data, &aux)
if err != nil {
return err
}

vp.DepthExtensionPresent, err = hex.DecodeString(aux.DepthExtensionPresent)
if err != nil {
return fmt.Errorf("error decoding hex string for depth and extention present: %v", err)
}

vp.CommitmentsByPath = make([][32]byte, len(aux.CommitmentsByPath))
for i, c := range aux.CommitmentsByPath {
val, err := hex.DecodeString(c)
if err != nil {
return fmt.Errorf("error decoding hex string for commitment #%d: %w", i, err)
}
copy(vp.CommitmentsByPath[i][:], val)
}

currentValueBytes, err := hex.DecodeString(aux.D)
if err != nil {
return fmt.Errorf("error decoding hex string for D: %w", err)
}
copy(vp.D[:], currentValueBytes)

vp.OtherStems = make([][31]byte, len(aux.OtherStems))
for i, c := range aux.OtherStems {
val, err := hex.DecodeString(c)
if err != nil {
return fmt.Errorf("error decoding hex string for other stem #%d: %w", i, err)
}
copy(vp.OtherStems[i][:], val)
}

vp.IPAProof = aux.IPAProof
return nil
}

type suffixStateDiffMarshaller struct {
Suffix byte `json:"suffix"`
CurrentValue string `json:"currentValue"`
}

func (ssd SuffixStateDiff) MarshalJSON() ([]byte, error) {
return json.Marshal(&suffixStateDiffMarshaller{
Suffix: ssd.Suffix,
CurrentValue: hex.EncodeToString(ssd.CurrentValue[:]),
})
}

func (ssd *SuffixStateDiff) UnmarshalJSON(data []byte) error {
aux := &suffixStateDiffMarshaller{
CurrentValue: "",
}

if err := json.Unmarshal(data, &aux); err != nil {
return err
}

if len(aux.CurrentValue) != 64 {
return fmt.Errorf("invalid hex string for current value: %s", aux.CurrentValue)
}

currentValueBytes, err := hex.DecodeString(aux.CurrentValue)
if err != nil {
return fmt.Errorf("error decoding hex string for current value: %v", err)
}

*ssd = SuffixStateDiff{
Suffix: aux.Suffix,
CurrentValue: &[32]byte{},
}

copy(ssd.CurrentValue[:], currentValueBytes)

return nil
}
84 changes: 84 additions & 0 deletions proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import (
"bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
"reflect"
"testing"
)

Expand Down Expand Up @@ -411,3 +413,85 @@ func TestProofOfAbsenceNoneMultipleStems(t *testing.T) {
t.Fatalf("invalid number of none extension statuses: %d ≠ 1", len(proof.ExtStatus))
}
}

func TestSuffixStateDiffJSONMarshalUn(t *testing.T) {
ssd := SuffixStateDiff{
Suffix: 0x41,
CurrentValue: &[32]byte{
0x10, 0x20, 0x30, 0x40,
0x50, 0x60, 0x70, 0x80,
0x90, 0xA0, 0xB0, 0xC0,
0xD0, 0xE0, 0xF0, 0x00,
0x11, 0x22, 0x33, 0x44,
0x55, 0x66, 0x77, 0x88,
0x99, 0xAA, 0xBB, 0xCC,
0xDD, 0xEE, 0xFF, 0x00,
},
}

expectedJSON := `{"suffix":65,"currentValue":"102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00"}`
actualJSON, err := json.Marshal(ssd)
if err != nil {
t.Errorf("error marshalling SuffixStateDiff to JSON: %v", err)
}

if string(actualJSON) != expectedJSON {
t.Errorf("JSON output doesn't match expected value.\nExpected: %s\nActual: %s", expectedJSON, string(actualJSON))
}

var actualSSD SuffixStateDiff
err = json.Unmarshal([]byte(actualJSON), &actualSSD)
if err != nil {
t.Errorf("error unmarshalling JSON to SuffixStateDiff: %v", err)
}

if !reflect.DeepEqual(actualSSD, ssd) {
t.Errorf("SuffixStateDiff doesn't match expected value.\nExpected: %+v\nActual: %+v", ssd, actualSSD)
}
}

func TestIPAProofMarshalUnmarshalJSON(t *testing.T) {
ip1 := &IPAProof{
CL: [IPA_PROOF_DEPTH][32]byte{{1}, {2}, {3}},
CR: [IPA_PROOF_DEPTH][32]byte{{4}, {5}, {6}},
FinalEvaluation: [32]byte{7},
}
ipJSON, err := json.Marshal(ip1)
if err != nil {
t.Fatal(err)
}
ip2 := &IPAProof{}
err = json.Unmarshal(ipJSON, ip2)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(ip1, ip2) {
t.Errorf("expected %v, got %v", ip1, ip2)
}
}

func TestVerkleProofMarshalUnmarshalJSON(t *testing.T) {
vp1 := &VerkleProof{
OtherStems: [][31]byte{{1}, {2}, {3}},
DepthExtensionPresent: []byte{4, 5, 6},
CommitmentsByPath: [][32]byte{{7}, {8}, {9}},
D: [32]byte{10},
IPAProof: &IPAProof{
CL: [IPA_PROOF_DEPTH][32]byte{{11}, {12}, {13}},
CR: [IPA_PROOF_DEPTH][32]byte{{14}, {15}, {16}},
FinalEvaluation: [32]byte{17},
},
}
vpJSON, err := json.Marshal(vp1)
if err != nil {
t.Fatal(err)
}
vp2 := &VerkleProof{}
err = json.Unmarshal(vpJSON, vp2)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(vp1, vp2) {
t.Errorf("expected %v, got %v", vp1, vp2)
}
}

0 comments on commit e88dff5

Please sign in to comment.