Skip to content

Commit

Permalink
Implement hyper verification pruner
Browse files Browse the repository at this point in the history
Co-authored-by: Gabriel Díaz <[email protected]>
  • Loading branch information
aalda and gdiazlo committed Feb 25, 2019
1 parent ad84405 commit 427e071
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 12 deletions.
3 changes: 1 addition & 2 deletions balloon/hyper2/navigation/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@
package navigation

import (
"github.com/bbva/qed/balloon/navigator"
"github.com/bbva/qed/hashing"
)

type AuditPath map[string]hashing.Digest

func (p AuditPath) Get(pos navigator.Position) (hashing.Digest, bool) {
func (p AuditPath) Get(pos Position) (hashing.Digest, bool) {
digest, ok := p[pos.StringId()]
return digest, ok
}
Expand Down
23 changes: 13 additions & 10 deletions balloon/hyper2/pruning2/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/bbva/qed/balloon/cache"
"github.com/bbva/qed/balloon/hyper2/navigation"
"github.com/bbva/qed/hashing"
"github.com/bbva/qed/log"
"github.com/bbva/qed/storage"
)

Expand All @@ -42,8 +43,8 @@ const (
GetProvidedHashCode
PutInCacheCode
MutateBatchCode
CollectCode
CollectHashCode
GetFromPathCode
NoOpCode
)

Expand Down Expand Up @@ -145,25 +146,27 @@ func mutateBatch(pos navigation.Position, batch *BatchNode) *Operation {
}
}

func collect(pos navigation.Position) *Operation {
func collectHash(pos navigation.Position) *Operation {
return &Operation{
Code: CollectCode,
Code: CollectHashCode,
Pos: pos,
Interpret: func(ops *OperationsStack, c *Context) hashing.Digest {
leftHash := ops.Pop().Interpret(ops, c)
rightHash := ops.Pop().Interpret(ops, c)
return c.Hasher.Salted(pos.Bytes(), leftHash, rightHash)
hash := ops.Pop().Interpret(ops, c)
c.AuditPath[pos.StringId()] = hash
return hash
},
}
}

func collectHash(pos navigation.Position) *Operation {
func getFromPath(pos navigation.Position) *Operation {
return &Operation{
Code: CollectHashCode,
Code: GetFromPathCode,
Pos: pos,
Interpret: func(ops *OperationsStack, c *Context) hashing.Digest {
hash := ops.Pop().Interpret(ops, c)
c.AuditPath[pos.StringId()] = hash
hash, ok := c.AuditPath.Get(pos)
if !ok {
log.Fatalf("Oops, something went wrong. Invalid position in audit path")
}
return hash
},
}
Expand Down
53 changes: 53 additions & 0 deletions balloon/hyper2/pruning2/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Copyright 2018 Banco Bilbao Vizcaya Argentaria, S.A.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package pruning2

import (
"bytes"

"github.com/bbva/qed/balloon/hyper2/navigation"
)

func PruneToVerify(index, value []byte, auditPathHeight uint16) *OperationsStack {

var traverse func(pos navigation.Position, ops *OperationsStack)

traverse = func(pos navigation.Position, ops *OperationsStack) {

if pos.Height <= auditPathHeight {
ops.Push(leafHash(pos, value))
return
}

rightPos := pos.Right()
if bytes.Compare(index, rightPos.Index) < 0 { // go to left
traverse(pos.Left(), ops)
ops.Push(getFromPath(rightPos))
} else { // go to right
ops.Push(getFromPath(pos.Left()))
traverse(rightPos, ops)
}

ops.Push(innerHash(pos))

}

ops := NewOperationsStack()
traverse(navigation.NewRootPosition(uint16(len(index)*8)), ops)
return ops

}
120 changes: 120 additions & 0 deletions balloon/hyper2/pruning2/verify_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
Copyright 2018 Banco Bilbao Vizcaya Argentaria, S.A.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package pruning2

import (
"testing"

"github.com/bbva/qed/balloon/hyper2/navigation"
"github.com/bbva/qed/hashing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestPruneToVerify(t *testing.T) {

testCases := []struct {
index, value []byte
auditPath navigation.AuditPath
expectedOps []op
}{
{
// verify index=0 with empty audit path
index: []byte{0},
value: []byte{0},
auditPath: navigation.AuditPath{},
expectedOps: []op{
{LeafHashCode, pos(0, 8)},
},
},
{
// verify index=0
index: []byte{0},
value: []byte{0},
auditPath: navigation.AuditPath{
pos(128, 7).StringId(): []byte{0x0},
pos(64, 6).StringId(): []byte{0x0},
pos(32, 5).StringId(): []byte{0x0},
pos(16, 4).StringId(): []byte{0x0},
},
expectedOps: []op{
{InnerHashCode, pos(0, 8)},
{GetFromPathCode, pos(128, 7)},
{InnerHashCode, pos(0, 7)},
{GetFromPathCode, pos(64, 6)},
{InnerHashCode, pos(0, 6)},
{GetFromPathCode, pos(32, 5)},
{InnerHashCode, pos(0, 5)},
{GetFromPathCode, pos(16, 4)},
{LeafHashCode, pos(0, 4)},
},
},
}

for i, c := range testCases {
prunedOps := PruneToVerify(c.index, c.value, uint16(8-len(c.auditPath))).List()
require.Truef(t, len(c.expectedOps) == len(prunedOps), "The size of the pruned ops should match the expected for test case %d", i)
for j := 0; j < len(prunedOps); j++ {
assert.Equalf(t, c.expectedOps[j].Code, prunedOps[j].Code, "The pruned operation's code should match for test case %d", i)
assert.Equalf(t, c.expectedOps[j].Pos, prunedOps[j].Pos, "The pruned operation's position should match for test case %d", i)
}
}
}

func TestVerifyInterpretation(t *testing.T) {

testCases := []struct {
index, value []byte
auditPath navigation.AuditPath
expectedRootHash hashing.Digest
}{
{
// verify index=0 with empty audit path
index: []byte{0},
value: []byte{0},
auditPath: navigation.AuditPath{},
expectedRootHash: []byte{0},
},
{
// verify index=0
index: []byte{0},
value: []byte{0},
auditPath: navigation.AuditPath{
pos(128, 7).StringId(): []byte{0x0},
pos(64, 6).StringId(): []byte{0x1},
pos(32, 5).StringId(): []byte{0x2},
pos(16, 4).StringId(): []byte{0x3},
},
expectedRootHash: []byte{0},
},
}

for i, c := range testCases {

ops := PruneToVerify(c.index, c.value, uint16(8-len(c.auditPath)))
ctx := &Context{
Hasher: hashing.NewFakeXorHasher(),
Cache: nil,
DefaultHashes: nil,
AuditPath: c.auditPath,
}

rootHash := ops.Pop().Interpret(ops, ctx)
assert.Equalf(t, c.expectedRootHash, rootHash, "The recomputed root hash should match for test case %d", i)

}
}

0 comments on commit 427e071

Please sign in to comment.