Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix proof element JSON serialization for compatibility with lighthouse #331

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 85 additions & 43 deletions proof_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ import (
"fmt"
)

// HexToPrefixedString turns a byte slice into its hex representation
// and prefixes it with `0x`.
func HexToPrefixedString(data []byte) string {
return "0x" + hex.EncodeToString(data)
}

// PrefixedHexStringToBytes does the opposite of HexToPrefixedString.
func PrefixedHexStringToBytes(input string) ([]byte, error) {
if input[0:2] == "0x" {
input = input[2:]
}
return hex.DecodeString(input)
}

type ipaproofMarshaller struct {
CL [IPA_PROOF_DEPTH]string `json:"cl"`
CR [IPA_PROOF_DEPTH]string `json:"cr"`
Expand All @@ -40,26 +54,26 @@ type ipaproofMarshaller struct {
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][:]),
HexToPrefixedString(ipp.CL[0][:]),
HexToPrefixedString(ipp.CL[1][:]),
HexToPrefixedString(ipp.CL[2][:]),
HexToPrefixedString(ipp.CL[3][:]),
HexToPrefixedString(ipp.CL[4][:]),
HexToPrefixedString(ipp.CL[5][:]),
HexToPrefixedString(ipp.CL[6][:]),
HexToPrefixedString(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][:]),
HexToPrefixedString(ipp.CR[0][:]),
HexToPrefixedString(ipp.CR[1][:]),
HexToPrefixedString(ipp.CR[2][:]),
HexToPrefixedString(ipp.CR[3][:]),
HexToPrefixedString(ipp.CR[4][:]),
HexToPrefixedString(ipp.CR[5][:]),
HexToPrefixedString(ipp.CR[6][:]),
HexToPrefixedString(ipp.CR[7][:]),
},
FinalEvaluation: hex.EncodeToString(ipp.FinalEvaluation[:]),
FinalEvaluation: HexToPrefixedString(ipp.FinalEvaluation[:]),
})
}

Expand All @@ -70,29 +84,29 @@ func (ipp *IPAProof) UnmarshalJSON(data []byte) error {
return err
}

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

currentValueBytes, err := hex.DecodeString(aux.FinalEvaluation)
currentValueBytes, err := PrefixedHexStringToBytes(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 {
if len(aux.CL[i]) != 64 && len(aux.CL[i]) != 66 {
return fmt.Errorf("invalid hex string for CL[%d]: %s", i, aux.CL[i])
}
val, err := hex.DecodeString(aux.CL[i])
val, err := PrefixedHexStringToBytes(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 {
if len(aux.CR[i]) != 64 && len(aux.CR[i]) != 66 {
return fmt.Errorf("invalid hex string for CR[%d]: %s", i, aux.CR[i])
}
val, err = hex.DecodeString(aux.CR[i])
val, err = PrefixedHexStringToBytes(aux.CR[i])
if err != nil {
return fmt.Errorf("error decoding hex string for CR[%d]: %s", i, aux.CR[i])
}
Expand All @@ -108,23 +122,23 @@ type verkleProofMarshaller struct {
DepthExtensionPresent string `json:"depthExtensionPresent"`
CommitmentsByPath []string `json:"commitmentsByPath"`
D string `json:"d"`
IPAProof *IPAProof `json:"ipa_proof"`
IPAProof *IPAProof `json:"ipaProof"`
}

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

for i, s := range vp.OtherStems {
aux.OtherStems[i] = hex.EncodeToString(s[:])
aux.OtherStems[i] = HexToPrefixedString(s[:])
}
for i, c := range vp.CommitmentsByPath {
aux.CommitmentsByPath[i] = hex.EncodeToString(c[:])
aux.CommitmentsByPath[i] = HexToPrefixedString(c[:])
}
return json.Marshal(aux)
}
Expand All @@ -136,29 +150,29 @@ func (vp *VerkleProof) UnmarshalJSON(data []byte) error {
return err
}

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

vp.CommitmentsByPath = make([][32]byte, len(aux.CommitmentsByPath))
for i, c := range aux.CommitmentsByPath {
val, err := hex.DecodeString(c)
val, err := PrefixedHexStringToBytes(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)
currentValueBytes, err := PrefixedHexStringToBytes(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)
val, err := PrefixedHexStringToBytes(c)
if err != nil {
return fmt.Errorf("error decoding hex string for other stem #%d: %w", i, err)
}
Expand All @@ -169,15 +183,45 @@ func (vp *VerkleProof) UnmarshalJSON(data []byte) error {
return nil
}

type stemStateDiffMarshaller struct {
Stem string `json:"stem"`
SuffixDiffs SuffixStateDiffs `json:"suffixDiffs"`
}

func (ssd StemStateDiff) MarshalJSON() ([]byte, error) {
return json.Marshal(&stemStateDiffMarshaller{
Stem: HexToPrefixedString(ssd.Stem[:]),
SuffixDiffs: ssd.SuffixDiffs,
})
}

func (ssd *StemStateDiff) UnmarshalJSON(data []byte) error {
var aux stemStateDiffMarshaller
if err := json.Unmarshal(data, &aux); err != nil {
return err
}

stem, err := PrefixedHexStringToBytes(aux.Stem)
if err != nil {
return fmt.Errorf("invalid hex string for stem: %w", err)
}
*ssd = StemStateDiff{
SuffixDiffs: aux.SuffixDiffs,
}
copy(ssd.Stem[:], stem)
return nil
}

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

func (ssd SuffixStateDiff) MarshalJSON() ([]byte, error) {
var cvstr string
var cvstr *string
if ssd.CurrentValue != nil {
cvstr = hex.EncodeToString(ssd.CurrentValue[:])
tempstr := HexToPrefixedString(ssd.CurrentValue[:])
cvstr = &tempstr
}
return json.Marshal(&suffixStateDiffMarshaller{
Suffix: ssd.Suffix,
Expand All @@ -186,24 +230,22 @@ func (ssd SuffixStateDiff) MarshalJSON() ([]byte, error) {
}

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

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

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

*ssd = SuffixStateDiff{
Suffix: aux.Suffix,
}

if len(aux.CurrentValue) != 0 {
currentValueBytes, err := hex.DecodeString(aux.CurrentValue)
if aux.CurrentValue != nil && len(*aux.CurrentValue) != 0 {
currentValueBytes, err := PrefixedHexStringToBytes(*aux.CurrentValue)
if err != nil {
return fmt.Errorf("error decoding hex string for current value: %v", err)
}
Expand Down
43 changes: 41 additions & 2 deletions proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ func TestSuffixStateDiffJSONMarshalUn(t *testing.T) {
},
}

expectedJSON := `{"suffix":65,"currentValue":"102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00"}`
expectedJSON := `{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00"}`
actualJSON, err := json.Marshal(ssd)
if err != nil {
t.Errorf("error marshalling SuffixStateDiff to JSON: %v", err)
Expand All @@ -446,13 +446,52 @@ func TestSuffixStateDiffJSONMarshalUn(t *testing.T) {
}
}

func TestStemStateDiffJSONMarshalUn(t *testing.T) {
ssd := StemStateDiff{
Stem: [31]byte{10},
SuffixDiffs: []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 := `{"stem":"0x0a000000000000000000000000000000000000000000000000000000000000","suffixDiffs":[{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00"}]}`
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 StemStateDiff
err = json.Unmarshal(actualJSON, &actualSSD)
if err != nil {
t.Errorf("error unmarshalling JSON to StemStateDiff: %v", err)
}

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

func TestSuffixStateDiffJSONMarshalUnCurrentValueNil(t *testing.T) {
ssd := SuffixStateDiff{
Suffix: 0x41,
CurrentValue: nil,
}

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