From 9d22de935d750c5af847be633a32d576867d36ca Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Wed, 19 Jul 2023 17:07:15 +0300 Subject: [PATCH] feat(proof): create a proto representation of the proof (#220) ## Overview Added and generated a proto representation of the Proof ## Checklist - [ ] New and updated code has appropriate documentation - [ ] New and updated code has new and/or updated testing - [ ] Required CI checks are passing - [ ] Visual proof for any user facing features like CLI or documentation updates - [ ] Linked issues closed with keywords --------- Co-authored-by: Rootul P --- go.mod | 3 +- go.sum | 31 ++++ pb/proof.pb.go | 493 +++++++++++++++++++++++++++++++++++++++++++++++++ pb/proof.proto | 20 ++ proof.go | 25 +++ proof_test.go | 64 +++++++ 6 files changed, 635 insertions(+), 1 deletion(-) create mode 100644 pb/proof.pb.go create mode 100644 pb/proof.proto diff --git a/go.mod b/go.mod index 54d7bab..b3b469b 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,15 @@ module github.com/celestiaorg/nmt go 1.19 require ( + github.com/gogo/protobuf v1.3.2 github.com/google/gofuzz v1.2.0 github.com/stretchr/testify v1.8.1 + github.com/tidwall/gjson v1.14.4 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index f8a5bba..5bf4373 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,12 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -19,6 +23,33 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pb/proof.pb.go b/pb/proof.pb.go new file mode 100644 index 0000000..ce0ab54 --- /dev/null +++ b/pb/proof.pb.go @@ -0,0 +1,493 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pb/proof.proto + +package proof_pb + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Proof struct { + // Start index of the leaves that match the queried namespace.ID. + Start int64 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"` + // End index (non-inclusive) of the leaves that match the queried + // namespace.ID. + End int64 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"` + // Nodes hold the tree nodes necessary for the Merkle range proof. + Nodes [][]byte `protobuf:"bytes,3,rep,name=nodes,proto3" json:"nodes,omitempty"` + // LeafHash contains the namespace.ID if NMT does not have it and + // it should be proven. leafHash is necessary to prove the Absence Proof. + // This field will be empty in case of Inclusion Proof. + LeafHash []byte `protobuf:"bytes,4,opt,name=leafHash,proto3" json:"leafHash,omitempty"` + // The isMaxNamespaceIgnored flag influences the calculation of the + // namespace ID range for intermediate nodes in the tree + IsMaxNamespaceIgnored bool `protobuf:"varint,5,opt,name=isMaxNamespaceIgnored,proto3" json:"isMaxNamespaceIgnored,omitempty"` +} + +func (m *Proof) Reset() { *m = Proof{} } +func (m *Proof) String() string { return proto.CompactTextString(m) } +func (*Proof) ProtoMessage() {} +func (*Proof) Descriptor() ([]byte, []int) { + return fileDescriptor_2e2daa763cd7daf3, []int{0} +} +func (m *Proof) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Proof) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Proof.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Proof) XXX_Merge(src proto.Message) { + xxx_messageInfo_Proof.Merge(m, src) +} +func (m *Proof) XXX_Size() int { + return m.Size() +} +func (m *Proof) XXX_DiscardUnknown() { + xxx_messageInfo_Proof.DiscardUnknown(m) +} + +var xxx_messageInfo_Proof proto.InternalMessageInfo + +func (m *Proof) GetStart() int64 { + if m != nil { + return m.Start + } + return 0 +} + +func (m *Proof) GetEnd() int64 { + if m != nil { + return m.End + } + return 0 +} + +func (m *Proof) GetNodes() [][]byte { + if m != nil { + return m.Nodes + } + return nil +} + +func (m *Proof) GetLeafHash() []byte { + if m != nil { + return m.LeafHash + } + return nil +} + +func (m *Proof) GetIsMaxNamespaceIgnored() bool { + if m != nil { + return m.IsMaxNamespaceIgnored + } + return false +} + +func init() { + proto.RegisterType((*Proof)(nil), "proof.pb.Proof") +} + +func init() { proto.RegisterFile("pb/proof.proto", fileDescriptor_2e2daa763cd7daf3) } + +var fileDescriptor_2e2daa763cd7daf3 = []byte{ + // 186 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2b, 0x48, 0xd2, 0x2f, + 0x28, 0xca, 0xcf, 0x4f, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x80, 0x72, 0x92, 0x94, + 0xa6, 0x33, 0x72, 0xb1, 0x06, 0x80, 0x38, 0x42, 0x22, 0x5c, 0xac, 0xc5, 0x25, 0x89, 0x45, 0x25, + 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x10, 0x8e, 0x90, 0x00, 0x17, 0x73, 0x6a, 0x5e, 0x8a, + 0x04, 0x13, 0x58, 0x0c, 0xc4, 0x04, 0xa9, 0xcb, 0xcb, 0x4f, 0x49, 0x2d, 0x96, 0x60, 0x56, 0x60, + 0xd6, 0xe0, 0x09, 0x82, 0x70, 0x84, 0xa4, 0xb8, 0x38, 0x72, 0x52, 0x13, 0xd3, 0x3c, 0x12, 0x8b, + 0x33, 0x24, 0x58, 0x14, 0x18, 0x35, 0x78, 0x82, 0xe0, 0x7c, 0x21, 0x13, 0x2e, 0xd1, 0xcc, 0x62, + 0xdf, 0xc4, 0x0a, 0xbf, 0xc4, 0xdc, 0xd4, 0xe2, 0x82, 0xc4, 0xe4, 0x54, 0xcf, 0xf4, 0xbc, 0xfc, + 0xa2, 0xd4, 0x14, 0x09, 0x56, 0x05, 0x46, 0x0d, 0x8e, 0x20, 0xec, 0x92, 0x4e, 0x12, 0x27, 0x1e, + 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, + 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x90, 0xc4, 0x06, 0xf6, 0x84, 0x31, 0x20, 0x00, 0x00, + 0xff, 0xff, 0x1b, 0x93, 0x09, 0xff, 0xd6, 0x00, 0x00, 0x00, +} + +func (m *Proof) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Proof) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Proof) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IsMaxNamespaceIgnored { + i-- + if m.IsMaxNamespaceIgnored { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.LeafHash) > 0 { + i -= len(m.LeafHash) + copy(dAtA[i:], m.LeafHash) + i = encodeVarintProof(dAtA, i, uint64(len(m.LeafHash))) + i-- + dAtA[i] = 0x22 + } + if len(m.Nodes) > 0 { + for iNdEx := len(m.Nodes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Nodes[iNdEx]) + copy(dAtA[i:], m.Nodes[iNdEx]) + i = encodeVarintProof(dAtA, i, uint64(len(m.Nodes[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.End != 0 { + i = encodeVarintProof(dAtA, i, uint64(m.End)) + i-- + dAtA[i] = 0x10 + } + if m.Start != 0 { + i = encodeVarintProof(dAtA, i, uint64(m.Start)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintProof(dAtA []byte, offset int, v uint64) int { + offset -= sovProof(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Proof) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Start != 0 { + n += 1 + sovProof(uint64(m.Start)) + } + if m.End != 0 { + n += 1 + sovProof(uint64(m.End)) + } + if len(m.Nodes) > 0 { + for _, b := range m.Nodes { + l = len(b) + n += 1 + l + sovProof(uint64(l)) + } + } + l = len(m.LeafHash) + if l > 0 { + n += 1 + l + sovProof(uint64(l)) + } + if m.IsMaxNamespaceIgnored { + n += 2 + } + return n +} + +func sovProof(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozProof(x uint64) (n int) { + return sovProof(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Proof) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Proof: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Proof: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Start", wireType) + } + m.Start = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Start |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field End", wireType) + } + m.End = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.End |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nodes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthProof + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthProof + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Nodes = append(m.Nodes, make([]byte, postIndex-iNdEx)) + copy(m.Nodes[len(m.Nodes)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LeafHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthProof + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthProof + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LeafHash = append(m.LeafHash[:0], dAtA[iNdEx:postIndex]...) + if m.LeafHash == nil { + m.LeafHash = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsMaxNamespaceIgnored", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsMaxNamespaceIgnored = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipProof(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProof + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipProof(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowProof + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowProof + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowProof + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthProof + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupProof + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthProof + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthProof = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowProof = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupProof = fmt.Errorf("proto: unexpected end of group") +) diff --git a/pb/proof.proto b/pb/proof.proto new file mode 100644 index 0000000..e1db55d --- /dev/null +++ b/pb/proof.proto @@ -0,0 +1,20 @@ +syntax="proto3"; + +package proof.pb; + +message Proof { + // Start index of the leaves that match the queried namespace.ID. + int64 start = 1; + // End index (non-inclusive) of the leaves that match the queried + // namespace.ID. + int64 end = 2; + // Nodes hold the tree nodes necessary for the Merkle range proof. + repeated bytes nodes = 3; + // LeafHash contains the namespace.ID if NMT does not have it and + // it should be proven. LeafHash is necessary to prove the Absence Proof. + // This field will be empty in case of Inclusion Proof. + bytes leafHash = 4; + // The isMaxNamespaceIgnored flag influences the calculation of the + // namespace ID range for intermediate nodes in the tree. + bool isMaxNamespaceIgnored=5; +} \ No newline at end of file diff --git a/proof.go b/proof.go index eea0730..a6a6085 100644 --- a/proof.go +++ b/proof.go @@ -9,6 +9,7 @@ import ( "math/bits" "github.com/celestiaorg/nmt/namespace" + pb "github.com/celestiaorg/nmt/pb" ) // ErrFailedCompletenessCheck indicates that the verification of a namespace proof failed due to the lack of completeness property. @@ -453,6 +454,30 @@ func (proof Proof) VerifyInclusion(h hash.Hash, nid namespace.ID, leavesWithoutN return res } +// ProtoToProof creates a proof from its proto representation. +func ProtoToProof(protoProof pb.Proof) Proof { + if protoProof.Start == 0 && protoProof.End == 0 { + return NewEmptyRangeProof(protoProof.IsMaxNamespaceIgnored) + } + + if len(protoProof.LeafHash) > 0 { + return NewAbsenceProof( + int(protoProof.Start), + int(protoProof.End), + protoProof.Nodes, + protoProof.LeafHash, + protoProof.IsMaxNamespaceIgnored, + ) + } + + return NewInclusionProof( + int(protoProof.Start), + int(protoProof.End), + protoProof.Nodes, + protoProof.IsMaxNamespaceIgnored, + ) +} + // nextSubtreeSize returns the number of leaves of the subtree adjacent to start // that does not overlap end. func nextSubtreeSize(start, end uint64) int { diff --git a/proof_test.go b/proof_test.go index 9d308c3..2dba96f 100644 --- a/proof_test.go +++ b/proof_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/celestiaorg/nmt/namespace" + pb "github.com/celestiaorg/nmt/pb" ) func TestJsonMarshal_Proof(t *testing.T) { @@ -893,3 +894,66 @@ func TestVerifyNamespace_ShortAbsenceProof_Invalid(t *testing.T) { }) } } + +func Test_ProtoToProof(t *testing.T) { + verifier := func(t *testing.T, proof Proof, protoProof pb.Proof) { + require.Equal(t, int64(proof.Start()), protoProof.Start) + require.Equal(t, int64(proof.End()), protoProof.End) + require.Equal(t, proof.Nodes(), protoProof.Nodes) + require.Equal(t, proof.LeafHash(), protoProof.LeafHash) + require.Equal(t, proof.IsMaxNamespaceIDIgnored(), protoProof.IsMaxNamespaceIgnored) + } + + tests := []struct { + name string + protoProof pb.Proof + verifyFn func(t *testing.T, proof Proof, protoProof pb.Proof) + }{ + { + name: "Inclusion proof", + protoProof: pb.Proof{ + Start: 0, + End: 1, + Nodes: [][]byte{bytes.Repeat([]byte{1}, 10)}, + LeafHash: nil, + IsMaxNamespaceIgnored: true, + }, + verifyFn: verifier, + }, + { + name: "Absence Proof", + protoProof: pb.Proof{ + Start: 0, + End: 1, + Nodes: [][]byte{bytes.Repeat([]byte{1}, 10)}, + LeafHash: bytes.Repeat([]byte{1}, 10), + IsMaxNamespaceIgnored: true, + }, + verifyFn: verifier, + }, + { + name: "Empty Proof", + protoProof: pb.Proof{ + Start: 0, + End: 0, + Nodes: [][]byte{bytes.Repeat([]byte{1}, 10)}, + LeafHash: nil, + IsMaxNamespaceIgnored: true, + }, + verifyFn: func(t *testing.T, proof Proof, protoProof pb.Proof) { + require.Equal(t, proof.Start(), 0) + require.Equal(t, proof.End(), 0) + require.Nil(t, proof.Nodes()) + require.Nil(t, proof.LeafHash()) + require.Equal(t, proof.IsMaxNamespaceIDIgnored(), protoProof.IsMaxNamespaceIgnored) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + proof := ProtoToProof(tt.protoProof) + tt.verifyFn(t, proof, tt.protoProof) + }) + } +}