Skip to content

Commit 77df88e

Browse files
authored
Subtree consistency tests for subtrees starting at 0 (transparency-dev#247)
* verify: implement tests verify: modify test fields * generate subtree consistency probes
1 parent b0ba6a7 commit 77df88e

109 files changed

Lines changed: 1612 additions & 7 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/proofgen/main.go

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -531,8 +531,10 @@ func consistencyProbes(rootDir string) error {
531531
return err
532532
}
533533

534-
if err := staticConsistencyProbes(staticDir); err != nil {
535-
return err
534+
for _, p := range staticConsistencyProbes() {
535+
if err := writeConsistencyProbe(staticDir, p); err != nil {
536+
return err
537+
}
536538
}
537539

538540
return nil
@@ -604,13 +606,13 @@ func corruptedConsistencyProbes(dir string, size1, size2 uint64, proof [][]byte,
604606
return nil
605607
}
606608

607-
func staticConsistencyProbes(dir string) error {
609+
func staticConsistencyProbes() []consistencyProbe {
608610
root1 := []byte("don't care 1")
609611
root2 := []byte("don't care 2")
610612
proof1 := [][]byte{}
611613
proof2 := [][]byte{sha256EmptyTreeHash}
612614

613-
tests := []consistencyProbe{
615+
return []consistencyProbe{
614616
{0, 0, root1, root2, proof1, "sizes are equal (zero) but roots are not", true},
615617
{1, 1, root1, root2, proof1, "sizes are equal (one) but roots are not", true},
616618
{0, 0, root1, root1, proof1, "sizes are equal (zero) and proof is empty", true},
@@ -632,17 +634,135 @@ func staticConsistencyProbes(dir string) error {
632634
{0, 1, sha256EmptyTreeHash, root2, proof1, "size1 is zero and size2 is not zero", true},
633635
{0, 1, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, "consistency check on empty tree (size1 is zero) is useless", true},
634636
}
637+
}
635638

636-
for _, p := range tests {
637-
if err := writeConsistencyProbe(dir, p); err != nil {
639+
func writeConsistencyProbe(dir string, probe consistencyProbe) error {
640+
fn := fileName(probe.Desc)
641+
642+
probeJson, err := json.MarshalIndent(probe, "", " ")
643+
if err != nil {
644+
return fmt.Errorf("marshaling probe: %s", err)
645+
}
646+
647+
fileLocation := filepath.Join(dir, fn)
648+
if err := os.WriteFile(fileLocation, probeJson, 0644); err != nil {
649+
return fmt.Errorf("writing probe: %s: %s", fn, err)
650+
}
651+
652+
return nil
653+
}
654+
655+
// =============================================================================
656+
// Subtree Consistency Proofs
657+
// =============================================================================
658+
659+
// subtreeConsistencyProbe is a parameter set for subtree consistency proof
660+
// verification.
661+
type subtreeConsistencyProbe struct {
662+
Start uint64 `json:"start"`
663+
End uint64 `json:"end"`
664+
Size uint64 `json:"size"`
665+
Root1 []byte `json:"root1"`
666+
Root2 []byte `json:"root2"`
667+
Proof [][]byte `json:"proof"`
668+
669+
Desc string `json:"desc"`
670+
WantError bool `json:"wantErr"`
671+
}
672+
673+
func subtreeConsistencyProbes(rootDir string) error {
674+
for i, p := range consistencyProofs {
675+
dir := filepath.Join(rootDir, strconv.Itoa(i))
676+
if err := os.MkdirAll(dir, 0755); err != nil {
677+
return err
678+
}
679+
680+
if err := corruptedSubtreeConsistencyProbes(dir, p.size1, p.size2, p.proof,
681+
roots[p.size1-1], roots[p.size2-1]); err != nil {
682+
return fmt.Errorf("write subtree consistency test data: %s", err)
683+
}
684+
}
685+
686+
staticDir := filepath.Join(rootDir, "additional")
687+
if err := os.MkdirAll(staticDir, 0755); err != nil {
688+
return err
689+
}
690+
691+
if err := staticSubtreeConsistencyProbes(staticDir); err != nil {
692+
return err
693+
}
694+
695+
return nil
696+
}
697+
698+
func corruptedSubtreeConsistencyProbes(dir string, size1, size2 uint64, proof [][]byte, root1, root2 []byte) error {
699+
happyPath := subtreeConsistencyProbe{0, size1, size2, root1, root2, proof, "happy path", false}
700+
if err := writeSubtreeConsistencyProbe(dir, happyPath); err != nil {
701+
return err
702+
}
703+
704+
if len(proof) == 0 {
705+
return nil
706+
}
707+
708+
probes := invalidSubtreeConsistencyProof(size1, size2, root1, root2, proof)
709+
for _, p := range probes {
710+
if err := writeSubtreeConsistencyProbe(dir, p); err != nil {
638711
return err
639712
}
640713
}
641714

642715
return nil
643716
}
644717

645-
func writeConsistencyProbe(dir string, probe consistencyProbe) error {
718+
func invalidSubtreeConsistencyProof(size1, size2 uint64, root1, root2 []byte, proof [][]byte) []subtreeConsistencyProbe {
719+
cProbes := invalidConsistencyProof(size1, size2, root1, root2, proof)
720+
ret := make([]subtreeConsistencyProbe, 0, len(cProbes)+1)
721+
for _, p := range cProbes {
722+
ret = append(ret, subtreeConsistencyProbe{
723+
Start: 0,
724+
End: p.Size1,
725+
Size: p.Size2,
726+
Root1: p.Root1,
727+
Root2: p.Root2,
728+
Proof: p.Proof,
729+
Desc: p.Desc,
730+
WantError: p.WantError,
731+
})
732+
}
733+
ret = append(ret, subtreeConsistencyProbe{
734+
Start: 1,
735+
End: 15,
736+
Size: 15,
737+
Root1: root1,
738+
Root2: root2,
739+
Proof: proof,
740+
Desc: "invalid subtree",
741+
WantError: true,
742+
})
743+
return ret
744+
}
745+
746+
func staticSubtreeConsistencyProbes(dir string) error {
747+
for _, p := range staticConsistencyProbes() {
748+
sp := subtreeConsistencyProbe{
749+
Start: 0,
750+
End: p.Size1,
751+
Size: p.Size2,
752+
Root1: p.Root1,
753+
Root2: p.Root2,
754+
Proof: p.Proof,
755+
Desc: p.Desc,
756+
WantError: p.WantError,
757+
}
758+
if err := writeSubtreeConsistencyProbe(dir, sp); err != nil {
759+
return err
760+
}
761+
}
762+
return nil
763+
}
764+
765+
func writeSubtreeConsistencyProbe(dir string, probe subtreeConsistencyProbe) error {
646766
fn := fileName(probe.Desc)
647767

648768
probeJson, err := json.MarshalIndent(probe, "", " ")
@@ -710,4 +830,9 @@ func main() {
710830
if err := consistencyProbes(consistencyDir); err != nil {
711831
log.Fatalf("writing consistency test data: %s", err)
712832
}
833+
834+
subtreeConsistencyDir := "testdata/subtreeconsistency"
835+
if err := subtreeConsistencyProbes(subtreeConsistencyDir); err != nil {
836+
log.Fatalf("writing subtree consistency test data: %s", err)
837+
}
713838
}

proof/verify_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ type consistencyProbe struct {
6464
WantError bool `json:"wantErr"`
6565
}
6666

67+
// subtreeConsistencyProbe is a parameter set for subtree consistency proof
68+
// verification.
69+
type subtreeConsistencyProbe struct {
70+
Start uint64 `json:"start"`
71+
End uint64 `json:"end"`
72+
Size uint64 `json:"size"`
73+
Root1 []byte `json:"root1"`
74+
Root2 []byte `json:"root2"`
75+
Proof [][]byte `json:"proof"`
76+
77+
Desc string `json:"desc"`
78+
WantError bool `json:"wantErr"`
79+
}
80+
6781
func TestVerifyInclusionProbes(t *testing.T) {
6882
var probes []inclusionProbe
6983

@@ -219,3 +233,55 @@ func TestVerifyConsistencyProbes(t *testing.T) {
219233
t.Errorf("errors verifying consistency probes: \n%d out of %d failures \nError messages: \n%s", len(wrong), len(probes), strings.Join(wrong, "\n"))
220234
}
221235
}
236+
237+
func TestVerifySubtreeConsistencyProbes(t *testing.T) {
238+
var probes []subtreeConsistencyProbe
239+
240+
if err := filepath.WalkDir("../testdata/subtreeconsistency", func(path string, d fs.DirEntry, err error) error {
241+
if err != nil {
242+
return err
243+
}
244+
245+
if d.IsDir() {
246+
return nil
247+
}
248+
249+
if filepath.Ext(d.Name()) != ".json" {
250+
return nil
251+
}
252+
253+
data, err := os.ReadFile(path)
254+
if err != nil {
255+
return err
256+
}
257+
258+
var probe subtreeConsistencyProbe
259+
if err := json.Unmarshal(data, &probe); err != nil {
260+
return fmt.Errorf("failed to parse subtree consistency probe json: %s", err)
261+
}
262+
263+
probes = append(probes, probe)
264+
265+
return nil
266+
}); err != nil {
267+
t.Errorf("failed to read subtree consistency probes: %s", err)
268+
}
269+
270+
var wrong []string
271+
for _, p := range probes {
272+
err := VerifySubtreeConsistency(rfc6962.DefaultHasher, p.Start, p.End, p.Size, p.Proof, p.Root1, p.Root2)
273+
if p.WantError && err == nil {
274+
wrong = append(wrong, fmt.Sprintf("expected error but didn't get one: %s", p.Desc))
275+
continue
276+
}
277+
278+
if !p.WantError && err != nil {
279+
wrong = append(wrong, fmt.Sprintf("unexpected error: %s, %s", p.Desc, err))
280+
continue
281+
}
282+
}
283+
284+
if len(wrong) > 0 {
285+
t.Errorf("errors verifying subtree consistency probes: \n%d out of %d failures \nError messages: \n%s", len(wrong), len(probes), strings.Join(wrong, "\n"))
286+
}
287+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"start": 0,
3+
"end": 1,
4+
"size": 1,
5+
"root1": "bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0=",
6+
"root2": "bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0=",
7+
"proof": null,
8+
"desc": "happy path",
9+
"wantErr": false
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"start": 0,
3+
"end": 1,
4+
"size": 8,
5+
"root1": "bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0=",
6+
"root2": "XcnaeacGWamtVZy3Ad7ZoqudgjqtL0lgz+Nw7/RgQyg=",
7+
"proof": [],
8+
"desc": "empty proof",
9+
"wantErr": true
10+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"start": 0,
3+
"end": 1,
4+
"size": 8,
5+
"root1": "bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0=",
6+
"root2": "XcnaeacGWamtVZy3Ad7ZoqudgjqtL0lgz+Nw7/RgQyg=",
7+
"proof": [
8+
"lqKW0iTyhcZ77pPDD4owkVfw2qNdxbh+QQt4YwoJz8c=",
9+
"Xwg/ChozygdqlSeYMlgNs+DvRYS9/x9UyKNg9Q3jAx4=",
10+
"a0eq8p7jwq+a+Im8H7klTavTEXfxYjLdaqsDXKOb9uQ="
11+
],
12+
"desc": "happy path",
13+
"wantErr": false
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"start": 1,
3+
"end": 15,
4+
"size": 15,
5+
"root1": "bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0=",
6+
"root2": "XcnaeacGWamtVZy3Ad7ZoqudgjqtL0lgz+Nw7/RgQyg=",
7+
"proof": [
8+
"lqKW0iTyhcZ77pPDD4owkVfw2qNdxbh+QQt4YwoJz8c=",
9+
"Xwg/ChozygdqlSeYMlgNs+DvRYS9/x9UyKNg9Q3jAx4=",
10+
"a0eq8p7jwq+a+Im8H7klTavTEXfxYjLdaqsDXKOb9uQ="
11+
],
12+
"desc": "invalid subtree",
13+
"wantErr": true
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"start": 0,
3+
"end": 1,
4+
"size": 8,
5+
"root1": "bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0=",
6+
"root2": "XcnaeacGWamtVZy3Ad7ZoqudgjqtL0lgz+Nw7/RgQyg=",
7+
"proof": [
8+
"hqKW0iTyhcZ77pPDD4owkVfw2qNdxbh+QQt4YwoJz8c=",
9+
"Xwg/ChozygdqlSeYMlgNs+DvRYS9/x9UyKNg9Q3jAx4=",
10+
"a0eq8p7jwq+a+Im8H7klTavTEXfxYjLdaqsDXKOb9uQ="
11+
],
12+
"desc": "modified proof@0 bit @4",
13+
"wantErr": true
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"start": 0,
3+
"end": 1,
4+
"size": 8,
5+
"root1": "bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0=",
6+
"root2": "XcnaeacGWamtVZy3Ad7ZoqudgjqtL0lgz+Nw7/RgQyg=",
7+
"proof": [
8+
"lqKW0iTyhcZ77pPDD4owkVfw2qNdxbh+QQt4YwoJz8c=",
9+
"Twg/ChozygdqlSeYMlgNs+DvRYS9/x9UyKNg9Q3jAx4=",
10+
"a0eq8p7jwq+a+Im8H7klTavTEXfxYjLdaqsDXKOb9uQ="
11+
],
12+
"desc": "modified proof@1 bit @4",
13+
"wantErr": true
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"start": 0,
3+
"end": 1,
4+
"size": 8,
5+
"root1": "bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0=",
6+
"root2": "XcnaeacGWamtVZy3Ad7ZoqudgjqtL0lgz+Nw7/RgQyg=",
7+
"proof": [
8+
"lqKW0iTyhcZ77pPDD4owkVfw2qNdxbh+QQt4YwoJz8c=",
9+
"Xwg/ChozygdqlSeYMlgNs+DvRYS9/x9UyKNg9Q3jAx4=",
10+
"e0eq8p7jwq+a+Im8H7klTavTEXfxYjLdaqsDXKOb9uQ="
11+
],
12+
"desc": "modified proof@2 bit @4",
13+
"wantErr": true
14+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"start": 0,
3+
"end": 1,
4+
"size": 8,
5+
"root1": "bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0=",
6+
"root2": "XcnaeacGWamtVZy3Ad7ZoqudgjqtL0lgz+Nw7/RgQyg=",
7+
"proof": [
8+
"",
9+
"lqKW0iTyhcZ77pPDD4owkVfw2qNdxbh+QQt4YwoJz8c=",
10+
"Xwg/ChozygdqlSeYMlgNs+DvRYS9/x9UyKNg9Q3jAx4=",
11+
"a0eq8p7jwq+a+Im8H7klTavTEXfxYjLdaqsDXKOb9uQ="
12+
],
13+
"desc": "preceding garbage",
14+
"wantErr": true
15+
}

0 commit comments

Comments
 (0)