Skip to content

Commit

Permalink
Compact Precise Proofs (#743)
Browse files Browse the repository at this point in the history
* latest precise proofs

* latest precise proofs

* using compact properties

* added compact prefix

* fix client test

* fix client test
  • Loading branch information
mikiquantum committed Feb 13, 2019
1 parent 2feca77 commit 0956e7a
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 69 deletions.
66 changes: 51 additions & 15 deletions coredocument/coredocument.go
Expand Up @@ -16,6 +16,48 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
)

const (
// CDRootField represents the coredocument root property of a tree
CDRootField = "cd_root"
// DataRootField represents the data root property of a tree
DataRootField = "data_root"
// DocumentTypeField represents the doc type property of a tree
DocumentTypeField = "document_type"
// SignaturesField represents the signatures property of a tree
SignaturesField = "signatures"
// SigningRootField represents the signature root property of a tree
SigningRootField = "signing_root"
)

var compactProperties = map[string][]byte{
CDRootField: {0, 0, 0, 7},
DataRootField: {0, 0, 0, 5},
DocumentTypeField: {0, 0, 0, 100},
SignaturesField: {0, 0, 0, 6},
SigningRootField: {0, 0, 0, 10},
}

// NewDefaultTree returns a DocumentTree with default opts
func NewDefaultTree(salts *proofs.Salts) *proofs.DocumentTree {
return NewDefaultTreeWithPrefix(salts, "")
}

// NewDefaultTreeWithPrefix returns a DocumentTree with default opts passing a prefix to the tree leaves
func NewDefaultTreeWithPrefix(salts *proofs.Salts, prefix string) *proofs.DocumentTree {
var prop proofs.Property
if prefix != "" {
prop = proofs.NewProperty(prefix)
}

t := proofs.NewDocumentTree(proofs.TreeOptions{EnableHashSorting: true, Hash: sha256.New(), ParentPrefix: prop, Salts: salts})
return &t
}

// NewLeafProperty returns a proof property with the literal and the compact
func NewLeafProperty(literal string, compact []byte) proofs.Property {
return proofs.NewProperty(literal, compact...)
}

// getDataProofHashes returns the hashes needed to create a proof from DataRoot to SigningRoot. This method is used
// to create field proofs
func getDataProofHashes(document *coredocumentpb.CoreDocument, dataRoot []byte) (hashes [][]byte, err error) {
Expand Down Expand Up @@ -80,16 +122,15 @@ func CalculateDocumentRoot(document *coredocumentpb.CoreDocument) error {
// GetDocumentRootTree returns the merkle tree for the document root
func GetDocumentRootTree(document *coredocumentpb.CoreDocument) (tree *proofs.DocumentTree, err error) {
h := sha256.New()
t := proofs.NewDocumentTree(proofs.TreeOptions{EnableHashSorting: true, Hash: h, Salts: ConvertToProofSalts(document.CoredocumentSalts)})
tree = &t
tree = NewDefaultTree(ConvertToProofSalts(document.CoredocumentSalts))

// The first leave added is the signing_root
err = tree.AddLeaf(proofs.LeafNode{Hash: document.SigningRoot, Hashed: true, Property: proofs.NewProperty("signing_root")})
err = tree.AddLeaf(proofs.LeafNode{Hash: document.SigningRoot, Hashed: true, Property: NewLeafProperty(SigningRootField, compactProperties[SigningRootField])})
if err != nil {
return nil, err
}
// For every signature we create a LeafNode
sigProperty := proofs.NewProperty("signatures")
sigProperty := NewLeafProperty(SignaturesField, compactProperties[SignaturesField])
sigLeafList := make([]proofs.LeafNode, len(document.Signatures)+1)
sigLengthNode := proofs.LeafNode{
Property: sigProperty.LengthProp(proofs.DefaultSaltsLengthSuffix),
Expand Down Expand Up @@ -128,8 +169,7 @@ func GetDocumentRootTree(document *coredocumentpb.CoreDocument) (tree *proofs.Do
// GetCoreDocTree returns the merkle tree for the coredoc root
func GetCoreDocTree(document *coredocumentpb.CoreDocument) (tree *proofs.DocumentTree, err error) {
h := sha256.New()
t := proofs.NewDocumentTree(proofs.TreeOptions{EnableHashSorting: true, Hash: h, Salts: ConvertToProofSalts(document.CoredocumentSalts)})
tree = &t
tree = NewDefaultTree(ConvertToProofSalts(document.CoredocumentSalts))
err = tree.AddLeavesFromDocument(document)
if err != nil {
return nil, err
Expand All @@ -140,7 +180,7 @@ func GetCoreDocTree(document *coredocumentpb.CoreDocument) (tree *proofs.Documen
}
// Adding document type as it is an excluded field in the tree
documentTypeNode := proofs.LeafNode{
Property: proofs.NewProperty("document_type"),
Property: NewLeafProperty(DocumentTypeField, compactProperties[DocumentTypeField]),
Salt: make([]byte, 32),
Value: []byte(document.EmbeddedData.TypeUrl),
}
Expand All @@ -164,25 +204,22 @@ func GetCoreDocTree(document *coredocumentpb.CoreDocument) (tree *proofs.Documen

// GetDocumentSigningTree returns the merkle tree for the signing root
func GetDocumentSigningTree(document *coredocumentpb.CoreDocument, dataRoot []byte) (tree *proofs.DocumentTree, err error) {
h := sha256.New()

// coredoc tree
coreDocTree, err := GetCoreDocTree(document)
if err != nil {
return nil, err
}

// create the signing tree with data root and coredoc root as siblings
t2 := proofs.NewDocumentTree(proofs.TreeOptions{EnableHashSorting: true, Hash: h, Salts: ConvertToProofSalts(document.CoredocumentSalts)})
tree = &t2
tree = NewDefaultTree(ConvertToProofSalts(document.CoredocumentSalts))
err = tree.AddLeaves([]proofs.LeafNode{
{
Property: proofs.NewProperty("data_root"),
Property: NewLeafProperty(DataRootField, compactProperties[DataRootField]),
Hash: dataRoot,
Hashed: true,
},
{
Property: proofs.NewProperty("cd_root"),
Property: NewLeafProperty(CDRootField, compactProperties[CDRootField]),
Hash: coreDocTree.RootHash(),
Hashed: true,
},
Expand Down Expand Up @@ -309,8 +346,7 @@ func GetExternalCollaborators(selfCentID identity.CentID, doc *coredocumentpb.Co
// GenerateNewSalts generates salts for new document
func GenerateNewSalts(document proto.Message, prefix string) (*proofs.Salts, error) {
docSalts := &proofs.Salts{}
prop := proofs.NewProperty(prefix)
t := proofs.NewDocumentTree(proofs.TreeOptions{EnableHashSorting: true, Hash: sha256.New(), ParentPrefix: prop, Salts: docSalts})
t := NewDefaultTreeWithPrefix(docSalts, prefix)
err := t.AddLeavesFromDocument(document)
if err != nil {
return nil, err
Expand Down
9 changes: 5 additions & 4 deletions coredocument/coredocument_test.go
Expand Up @@ -215,10 +215,11 @@ func TestNewWithCollaborators(t *testing.T) {

func TestCreateProofs(t *testing.T) {
h := sha256.New()
testTree := proofs.NewDocumentTree(proofs.TreeOptions{EnableHashSorting: true, Hash: sha256.New()})
err := testTree.AddLeaf(proofs.LeafNode{Hash: utils.RandomSlice(32), Hashed: true, Property: proofs.NewProperty("sample_field")})
testTree := NewDefaultTree(nil)
props := []proofs.Property{NewLeafProperty("sample_field", []byte{0, 0, 0, 200}), NewLeafProperty("sample_field2", []byte{0, 0, 0, 202})}
err := testTree.AddLeaf(proofs.LeafNode{Hash: utils.RandomSlice(32), Hashed: true, Property: props[0]})
assert.NoError(t, err)
err = testTree.AddLeaf(proofs.LeafNode{Hash: utils.RandomSlice(32), Hashed: true, Property: proofs.NewProperty("sample_field2")})
err = testTree.AddLeaf(proofs.LeafNode{Hash: utils.RandomSlice(32), Hashed: true, Property: props[1]})
assert.NoError(t, err)
err = testTree.Generate()
assert.NoError(t, err)
Expand Down Expand Up @@ -267,7 +268,7 @@ func TestCreateProofs(t *testing.T) {
}
for _, test := range tests {
t.Run(test.fieldName, func(t *testing.T) {
p, err := CreateProofs(&testTree, cd, []string{test.fieldName})
p, err := CreateProofs(testTree, cd, []string{test.fieldName})
assert.NoError(t, err)
assert.Equal(t, test.proofLength, len(p[0].SortedHashes))
var l *proofs.LeafNode
Expand Down
4 changes: 2 additions & 2 deletions documents/documents_test/service_test.go
Expand Up @@ -205,7 +205,7 @@ func TestService_CreateProofs(t *testing.T) {
assert.Equal(t, i.CoreDocument.DocumentIdentifier, proof.DocumentID)
assert.Equal(t, i.CoreDocument.DocumentIdentifier, proof.VersionID)
assert.Equal(t, len(proof.FieldProofs), 1)
assert.Equal(t, proof.FieldProofs[0].GetReadableName(), "invoice.invoice_number")
assert.Equal(t, proof.FieldProofs[0].GetCompactName(), []byte{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1})
}
func TestService_CreateProofsValidationFails(t *testing.T) {
service, idService := getServiceWithMockedLayers()
Expand Down Expand Up @@ -254,7 +254,7 @@ func TestService_CreateProofsForVersion(t *testing.T) {
assert.Equal(t, i.CoreDocument.DocumentIdentifier, proof.DocumentID)
assert.Equal(t, olderVersion, proof.VersionID)
assert.Equal(t, len(proof.FieldProofs), 1)
assert.Equal(t, proof.FieldProofs[0].GetReadableName(), "invoice.invoice_number")
assert.Equal(t, proof.FieldProofs[0].GetCompactName(), []byte{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1})
}

func TestService_RequestDocumentSignature_SigningRootNil(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion documents/handler.go
Expand Up @@ -104,7 +104,7 @@ func ConvertProofsToClientFormat(proofs []*proofspb.Proof) []*documentpb.Proof {
// ConvertProofToClientFormat converts a proof in precise proof format in to a client protobuf proof
func ConvertProofToClientFormat(proof *proofspb.Proof) *documentpb.Proof {
return &documentpb.Proof{
Property: proof.GetReadableName(),
Property: hexutil.Encode(proof.GetCompactName()),
Value: hexutil.Encode(proof.Value),
Salt: hexutil.Encode(proof.Salt),
Hash: hexutil.Encode(proof.Hash),
Expand Down
4 changes: 2 additions & 2 deletions documents/handler_test.go
Expand Up @@ -156,7 +156,7 @@ func TestConvertDocProofToClientFormat(t *testing.T) {
State: "state",
FieldProofs: []*proofspb.Proof{
{
Property: proofs.ReadableName("prop1"),
Property: proofs.CompactName([]byte{0, 0, 1}...),
Value: v1,
Salt: []byte{1, 2, 3},
Hash: []byte{1, 2, 4},
Expand All @@ -176,7 +176,7 @@ func TestConvertDocProofToClientFormat(t *testing.T) {
},
FieldProofs: []*documentpb.Proof{
{
Property: "prop1",
Property: "0x000001",
Value: "0x76616c756531",
Salt: "0x010203",
Hash: "0x010204",
Expand Down
10 changes: 5 additions & 5 deletions documents/invoice/model.go
@@ -1,7 +1,6 @@
package invoice

import (
"crypto/sha256"
"encoding/json"
"reflect"

Expand All @@ -24,6 +23,8 @@ import (

const prefix string = "invoice"

var compactPrefix = []byte{1, 0, 0, 0}

// Invoice implements the documents.Model keeps track of invoice related fields and state
type Invoice struct {
InvoiceNumber string // invoice number or reference number
Expand Down Expand Up @@ -250,7 +251,7 @@ func (i *Invoice) loadFromP2PProtobuf(invoiceData *invoicepb.InvoiceData) {
// getInvoiceSalts returns the invoice salts. Initialises if not present
func (i *Invoice) getInvoiceSalts(invoiceData *invoicepb.InvoiceData) (*proofs.Salts, error) {
if i.InvoiceSalts == nil {
invoiceSalts, err := documents.GenerateNewSalts(invoiceData, prefix)
invoiceSalts, err := documents.GenerateNewSalts(invoiceData, prefix, compactPrefix)
if err != nil {
return nil, errors.New("getInvoiceSalts error %v", err)
}
Expand Down Expand Up @@ -357,13 +358,12 @@ func (i *Invoice) CalculateDataRoot() ([]byte, error) {

// getDocumentDataTree creates precise-proofs data tree for the model
func (i *Invoice) getDocumentDataTree() (tree *proofs.DocumentTree, err error) {
prop := proofs.NewProperty(prefix)
invProto := i.createP2PProtobuf()
salts, err := i.getInvoiceSalts(invProto)
if err != nil {
return nil, err
}
t := proofs.NewDocumentTree(proofs.TreeOptions{EnableHashSorting: true, Hash: sha256.New(), ParentPrefix: prop, Salts: salts})
t := documents.NewDefaultTreeWithPrefix(salts, prefix, compactPrefix)
err = t.AddLeavesFromDocument(invProto)
if err != nil {
return nil, errors.New("getDocumentDataTree error %v", err)
Expand All @@ -372,7 +372,7 @@ func (i *Invoice) getDocumentDataTree() (tree *proofs.DocumentTree, err error) {
if err != nil {
return nil, errors.New("getDocumentDataTree error %v", err)
}
return &t, nil
return t, nil
}

// CreateProofs generates proofs for given fields
Expand Down

0 comments on commit 0956e7a

Please sign in to comment.