|
| 1 | +package store |
| 2 | + |
| 3 | +import ( |
| 4 | + "crypto/sha256" |
| 5 | + |
| 6 | + ics23 "github.com/cosmos/ics23/go" |
| 7 | + coreTypes "github.com/pokt-network/pocket/shared/core/types" |
| 8 | + "github.com/pokt-network/smt" |
| 9 | +) |
| 10 | + |
| 11 | +// position refers to whether the node is either the left or right child of its parent |
| 12 | +// for the binary SMT |
| 13 | +// Ref: https://github.com/pokt-network/smt/blob/main/types.go |
| 14 | +const ( |
| 15 | + left int = iota // 0 |
| 16 | + right // 1 |
| 17 | + hashSize = 32 |
| 18 | +) |
| 19 | + |
| 20 | +var ( |
| 21 | + // Custom SMT spec as the store does not hash values |
| 22 | + smtSpec *ics23.ProofSpec = &ics23.ProofSpec{ |
| 23 | + LeafSpec: &ics23.LeafOp{ |
| 24 | + Hash: ics23.HashOp_SHA256, |
| 25 | + PrehashKey: ics23.HashOp_SHA256, |
| 26 | + PrehashValue: ics23.HashOp_NO_HASH, |
| 27 | + Length: ics23.LengthOp_NO_PREFIX, |
| 28 | + Prefix: []byte{0}, |
| 29 | + }, |
| 30 | + InnerSpec: &ics23.InnerSpec{ |
| 31 | + ChildOrder: []int32{0, 1}, |
| 32 | + ChildSize: hashSize, |
| 33 | + MinPrefixLength: 1, |
| 34 | + MaxPrefixLength: 1, |
| 35 | + EmptyChild: make([]byte, hashSize), |
| 36 | + Hash: ics23.HashOp_SHA256, |
| 37 | + }, |
| 38 | + MaxDepth: 256, |
| 39 | + PrehashKeyBeforeComparison: true, |
| 40 | + } |
| 41 | + innerPrefix = []byte{1} |
| 42 | + |
| 43 | + // defaultValue is the default placeholder value in a SparseMerkleTree |
| 44 | + defaultValue = make([]byte, hashSize) |
| 45 | +) |
| 46 | + |
| 47 | +// VerifyMembership verifies the CommitmentProof provided, checking whether it produces the same |
| 48 | +// root as the one given. If it does, the key-value pair is a member of the tree |
| 49 | +func VerifyMembership(root ics23.CommitmentRoot, proof *ics23.CommitmentProof, key, value []byte) bool { |
| 50 | + // verify the proof |
| 51 | + return ics23.VerifyMembership(smtSpec, root, proof, key, value) |
| 52 | +} |
| 53 | + |
| 54 | +// VerifyNonMembership verifies the CommitmentProof provided, checking whether it produces the same |
| 55 | +// root as the one given. If it does, the key-value pair is not a member of the tree as the proof's |
| 56 | +// value is either the default nil value for the SMT or an unrelated value at the path |
| 57 | +func VerifyNonMembership(root ics23.CommitmentRoot, proof *ics23.CommitmentProof, key []byte) bool { |
| 58 | + // verify the proof |
| 59 | + return ics23.VerifyNonMembership(smtSpec, root, proof, key) |
| 60 | +} |
| 61 | + |
| 62 | +// createMembershipProof generates a CommitmentProof object verifying the membership of a key-value pair |
| 63 | +// in the SMT provided |
| 64 | +func createMembershipProof(tree *smt.SMT, key, value []byte) (*ics23.CommitmentProof, error) { |
| 65 | + proof, err := tree.Prove(key) |
| 66 | + if err != nil { |
| 67 | + return nil, coreTypes.ErrCreatingProof(err) |
| 68 | + } |
| 69 | + return convertSMPToExistenceProof(proof, key, value), nil |
| 70 | +} |
| 71 | + |
| 72 | +// createNonMembershipProof generates a CommitmentProof object verifying the membership of an unrealted key at the given key in the SMT provided |
| 73 | +func createNonMembershipProof(tree *smt.SMT, key []byte) (*ics23.CommitmentProof, error) { |
| 74 | + proof, err := tree.Prove(key) |
| 75 | + if err != nil { |
| 76 | + return nil, coreTypes.ErrCreatingProof(err) |
| 77 | + } |
| 78 | + |
| 79 | + return convertSMPToExclusionProof(proof, key), nil |
| 80 | +} |
| 81 | + |
| 82 | +// convertSMPToExistenceProof converts a SparseMerkleProof to an ics23 |
| 83 | +// ExistenceProof to verify membership of an element |
| 84 | +func convertSMPToExistenceProof(proof *smt.SparseMerkleProof, key, value []byte) *ics23.CommitmentProof { |
| 85 | + path := sha256.Sum256(key) |
| 86 | + steps := convertSideNodesToSteps(proof.SideNodes, path[:]) |
| 87 | + return &ics23.CommitmentProof{ |
| 88 | + Proof: &ics23.CommitmentProof_Exist{ |
| 89 | + Exist: &ics23.ExistenceProof{ |
| 90 | + Key: key, |
| 91 | + Value: value, |
| 92 | + Leaf: smtSpec.LeafSpec, |
| 93 | + Path: steps, |
| 94 | + }, |
| 95 | + }, |
| 96 | + } |
| 97 | +} |
| 98 | + |
| 99 | +// convertSMPToExclusionProof converts a SparseMerkleProof to an ics23 |
| 100 | +// ExclusionProof to verify non-membership of an element |
| 101 | +func convertSMPToExclusionProof(proof *smt.SparseMerkleProof, key []byte) *ics23.CommitmentProof { |
| 102 | + path := sha256.Sum256(key) |
| 103 | + steps := convertSideNodesToSteps(proof.SideNodes, path[:]) |
| 104 | + leaf := &ics23.LeafOp{ |
| 105 | + Hash: ics23.HashOp_SHA256, |
| 106 | + // Do not re-hash already hashed fields from NonMembershipLeafData |
| 107 | + PrehashKey: ics23.HashOp_NO_HASH, |
| 108 | + PrehashValue: ics23.HashOp_NO_HASH, |
| 109 | + Length: ics23.LengthOp_NO_PREFIX, |
| 110 | + Prefix: []byte{0}, |
| 111 | + } |
| 112 | + actualPath := path[:] |
| 113 | + actualValue := defaultValue |
| 114 | + if proof.NonMembershipLeafData != nil { |
| 115 | + actualPath = proof.NonMembershipLeafData[1 : 1+hashSize] // len(prefix): len(prefix) + hashSize |
| 116 | + actualValue = proof.NonMembershipLeafData[1+hashSize:] |
| 117 | + } |
| 118 | + return &ics23.CommitmentProof{ |
| 119 | + Proof: &ics23.CommitmentProof_Exclusion{ |
| 120 | + Exclusion: &ics23.ExclusionProof{ |
| 121 | + Key: key, |
| 122 | + ActualPath: actualPath, |
| 123 | + ActualValueHash: actualValue, |
| 124 | + Leaf: leaf, |
| 125 | + Path: steps, |
| 126 | + }, |
| 127 | + }, |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +// convertSideNodesToSteps converts the SideNodes field in the SparseMerkleProof |
| 132 | +// into a list of InnerOps for the ics23 CommitmentProof |
| 133 | +func convertSideNodesToSteps(sideNodes [][]byte, path []byte) []*ics23.InnerOp { |
| 134 | + steps := make([]*ics23.InnerOp, 0, len(sideNodes)) |
| 135 | + for i := 0; i < len(sideNodes); i++ { |
| 136 | + var prefix, suffix []byte |
| 137 | + prefix = append(prefix, innerPrefix...) |
| 138 | + if isLeft(path, len(sideNodes)-1-i) { |
| 139 | + // path is on the left so sidenode must be on the right |
| 140 | + suffix = make([]byte, 0, len(sideNodes[i])) |
| 141 | + suffix = append(suffix, sideNodes[i]...) |
| 142 | + } else { |
| 143 | + // path is on the right so sidenode must be on the left |
| 144 | + prefix = append(prefix, sideNodes[i]...) |
| 145 | + } |
| 146 | + op := &ics23.InnerOp{ |
| 147 | + Hash: ics23.HashOp_SHA256, |
| 148 | + Prefix: prefix, |
| 149 | + Suffix: suffix, |
| 150 | + } |
| 151 | + steps = append(steps, op) |
| 152 | + } |
| 153 | + return steps |
| 154 | +} |
| 155 | + |
| 156 | +// isLeft returns true is the i-th bit of path is a left child in the SMT |
| 157 | +func isLeft(path []byte, i int) bool { |
| 158 | + return smt.GetPathBit(path, i) == left |
| 159 | +} |
0 commit comments