Add Proof generation
arnaucube root@arnaucube.com
Tue, 30 Jun 2020 21:58:15 +0200
1 files changed,
130 insertions(+),
0 deletions(-)
jump to
M
merkletree.go
→
merkletree.go
@@ -300,3 +300,133 @@ path[n] = common.TestBit(k[:], uint(n))
} return path } + +// NodeAux contains the auxiliary node used in a non-existence proof. +type NodeAux struct { + Key *Hash + Value *Hash +} + +// Proof defines the required elements for a MT proof of existence or non-existence. +type Proof struct { + // existence indicates wether this is a proof of existence or non-existence. + Existence bool + // depth indicates how deep in the tree the proof goes. + depth uint + // notempties is a bitmap of non-empty Siblings found in Siblings. + notempties [ElemBytesLen - proofFlagsLen]byte + // Siblings is a list of non-empty sibling keys. + Siblings []*Hash + NodeAux *NodeAux +} + +// NewProofFromBytes parses a byte array into a Proof. +func NewProofFromBytes(bs []byte) (*Proof, error) { + if len(bs) < ElemBytesLen { + return nil, ErrInvalidProofBytes + } + p := &Proof{} + if (bs[0] & 0x01) == 0 { + p.Existence = true + } + p.depth = uint(bs[1]) + copy(p.notempties[:], bs[proofFlagsLen:ElemBytesLen]) + siblingBytes := bs[ElemBytesLen:] + sibIdx := 0 + for i := uint(0); i < p.depth; i++ { + if common.TestBitBigEndian(p.notempties[:], i) { + if len(siblingBytes) < (sibIdx+1)*ElemBytesLen { + return nil, ErrInvalidProofBytes + } + var sib Hash + copy(sib[:], siblingBytes[sibIdx*ElemBytesLen:(sibIdx+1)*ElemBytesLen]) + p.Siblings = append(p.Siblings, &sib) + sibIdx++ + } + } + + if !p.Existence && ((bs[0] & 0x02) != 0) { + p.NodeAux = &NodeAux{Key: &Hash{}, Value: &Hash{}} + nodeAuxBytes := siblingBytes[len(p.Siblings)*ElemBytesLen:] + if len(nodeAuxBytes) != 2*ElemBytesLen { + return nil, ErrInvalidProofBytes + } + copy(p.NodeAux.Key[:], nodeAuxBytes[:ElemBytesLen]) + copy(p.NodeAux.Value[:], nodeAuxBytes[ElemBytesLen:2*ElemBytesLen]) + } + return p, nil +} + +// Bytes serializes a Proof into a byte array. +func (p *Proof) Bytes() []byte { + bsLen := proofFlagsLen + len(p.notempties) + ElemBytesLen*len(p.Siblings) + if p.NodeAux != nil { + bsLen += 2 * ElemBytesLen + } + bs := make([]byte, bsLen) + + if !p.Existence { + bs[0] |= 0x01 + } + bs[1] = byte(p.depth) + copy(bs[proofFlagsLen:len(p.notempties)+proofFlagsLen], p.notempties[:]) + siblingsBytes := bs[len(p.notempties)+proofFlagsLen:] + for i, k := range p.Siblings { + copy(siblingsBytes[i*ElemBytesLen:(i+1)*ElemBytesLen], k[:]) + } + if p.NodeAux != nil { + bs[0] |= 0x02 + copy(bs[len(bs)-2*ElemBytesLen:], p.NodeAux.Key[:]) + copy(bs[len(bs)-1*ElemBytesLen:], p.NodeAux.Value[:]) + } + return bs +} + +// GenerateProof generates the proof of existence (or non-existence) of an +// Entry's hash Index for a Merkle Tree given the root. +// If the rootKey is nil, the current merkletree root is used +func (mt *MerkleTree) GenerateProof(k *big.Int, rootKey *Hash) (*Proof, error) { + p := &Proof{} + var siblingKey *Hash + + kHash := NewHashFromBigInt(k) + path := getPath(mt.maxLevels, kHash[:]) + if rootKey == nil { + rootKey = mt.Root() + } + nextKey := rootKey + for p.depth = 0; p.depth < uint(mt.maxLevels); p.depth++ { + n, err := mt.GetNode(nextKey) + if err != nil { + return nil, err + } + switch n.Type { + case NodeTypeEmpty: + return p, nil + case NodeTypeLeaf: + if bytes.Equal(kHash[:], n.Entry[0][:]) { + p.Existence = true + return p, nil + } else { + // We found a leaf whose entry didn't match hIndex + p.NodeAux = &NodeAux{Key: n.Entry[0], Value: n.Entry[1]} + return p, nil + } + case NodeTypeMiddle: + if path[p.depth] { + nextKey = n.ChildR + siblingKey = n.ChildL + } else { + nextKey = n.ChildL + siblingKey = n.ChildR + } + default: + return nil, ErrInvalidNodeFound + } + if !bytes.Equal(siblingKey[:], HashZero[:]) { + common.SetBitBigEndian(p.notempties[:], uint(p.depth)) + p.Siblings = append(p.Siblings, siblingKey) + } + } + return nil, ErrEntryIndexNotFound +}