mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 18:37:26 +08:00
1264 lines
29 KiB
Go
1264 lines
29 KiB
Go
package store
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/gob"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/pkg/errors"
|
|
"go.uber.org/zap"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/hypergraph/application"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
|
)
|
|
|
|
type HypergraphStore interface {
|
|
NewTransaction(indexed bool) (Transaction, error)
|
|
LoadVertexTree(id []byte) (
|
|
*crypto.RawVectorCommitmentTree,
|
|
error,
|
|
)
|
|
LoadVertexData(id []byte) ([]application.Encrypted, error)
|
|
SaveVertexTree(
|
|
txn Transaction,
|
|
id []byte,
|
|
vertTree *crypto.RawVectorCommitmentTree,
|
|
) error
|
|
CommitAndSaveVertexData(
|
|
txn Transaction,
|
|
id []byte,
|
|
data []application.Encrypted,
|
|
) (*crypto.RawVectorCommitmentTree, []byte, error)
|
|
LoadHypergraph() (
|
|
*application.Hypergraph,
|
|
error,
|
|
)
|
|
SaveHypergraph(
|
|
txn Transaction,
|
|
hg *application.Hypergraph,
|
|
) error
|
|
GetBranchNode(id NodeID) (*StoredBranchNode, error)
|
|
GetLeafNode(id NodeID) (*StoredLeafNode, error)
|
|
BatchWrite(
|
|
txn Transaction,
|
|
branches map[NodeID]*StoredBranchNode,
|
|
leaves map[NodeID]*StoredLeafNode,
|
|
deletions map[NodeID]struct{},
|
|
) error
|
|
}
|
|
|
|
var _ HypergraphStore = (*PebbleHypergraphStore)(nil)
|
|
|
|
type PebbleHypergraphStore struct {
|
|
db KVDB
|
|
logger *zap.Logger
|
|
}
|
|
|
|
func NewPebbleHypergraphStore(
|
|
db KVDB,
|
|
logger *zap.Logger,
|
|
) *PebbleHypergraphStore {
|
|
return &PebbleHypergraphStore{
|
|
db,
|
|
logger,
|
|
}
|
|
}
|
|
|
|
const (
|
|
HYPERGRAPH_SHARD = 0x09
|
|
VERTEX_ADDS = 0x00
|
|
VERTEX_REMOVES = 0x10
|
|
VERTEX_DATA = 0xF0
|
|
HYPEREDGE_ADDS = 0x01
|
|
HYPEREDGE_REMOVES = 0x11
|
|
SET_TREE_ROOT = 0x00
|
|
SET_TREE_BRANCH = 0x01
|
|
SET_TREE_LEAF = 0x02
|
|
)
|
|
|
|
func hypergraphVertexAddsKey(shardKey application.ShardKey) []byte {
|
|
key := []byte{HYPERGRAPH_SHARD, VERTEX_ADDS}
|
|
key = append(key, shardKey.L1[:]...)
|
|
key = append(key, shardKey.L2[:]...)
|
|
return key
|
|
}
|
|
|
|
func hypergraphVertexDataKey(id []byte) []byte {
|
|
key := []byte{HYPERGRAPH_SHARD, VERTEX_DATA}
|
|
key = append(key, id...)
|
|
return key
|
|
}
|
|
|
|
func hypergraphVertexRemovesKey(shardKey application.ShardKey) []byte {
|
|
key := []byte{HYPERGRAPH_SHARD, VERTEX_REMOVES}
|
|
key = append(key, shardKey.L1[:]...)
|
|
key = append(key, shardKey.L2[:]...)
|
|
return key
|
|
}
|
|
|
|
func hypergraphHyperedgeAddsKey(shardKey application.ShardKey) []byte {
|
|
key := []byte{HYPERGRAPH_SHARD, HYPEREDGE_ADDS}
|
|
key = append(key, shardKey.L1[:]...)
|
|
key = append(key, shardKey.L2[:]...)
|
|
return key
|
|
}
|
|
|
|
func hypergraphHyperedgeRemovesKey(shardKey application.ShardKey) []byte {
|
|
key := []byte{HYPERGRAPH_SHARD, HYPEREDGE_REMOVES}
|
|
key = append(key, shardKey.L1[:]...)
|
|
key = append(key, shardKey.L2[:]...)
|
|
return key
|
|
}
|
|
|
|
func shardKeyFromKey(key []byte) application.ShardKey {
|
|
return application.ShardKey{
|
|
L1: [3]byte(key[3:6]),
|
|
L2: [32]byte(key[6:]),
|
|
}
|
|
}
|
|
|
|
func SetTreeBranchKey(
|
|
shardKey application.ShardKey,
|
|
phaseSet byte,
|
|
prefix []int,
|
|
) NodeID {
|
|
key := []byte{HYPERGRAPH_SHARD, phaseSet}
|
|
if len(prefix) == 0 {
|
|
key = append(key, SET_TREE_ROOT)
|
|
key = append(key, shardKey.L1[:]...)
|
|
key = append(key, shardKey.L2[:]...)
|
|
return NodeID(key)
|
|
}
|
|
|
|
key = append(key, SET_TREE_BRANCH)
|
|
key = append(key, shardKey.L1[:]...)
|
|
key = append(key, shardKey.L2[:]...)
|
|
key = append(key, packNibbles(prefix)...)
|
|
return NodeID(key)
|
|
}
|
|
|
|
func SetTreeLeafKey(
|
|
shardKey application.ShardKey,
|
|
phaseSet byte,
|
|
leafKey []byte,
|
|
) NodeID {
|
|
key := []byte{HYPERGRAPH_SHARD, phaseSet}
|
|
key = append(key, SET_TREE_LEAF)
|
|
key = append(key, shardKey.L1[:]...)
|
|
key = append(key, shardKey.L2[:]...)
|
|
key = append(key, leafKey...)
|
|
return NodeID(key)
|
|
}
|
|
|
|
func (p *PebbleHypergraphStore) NewTransaction(indexed bool) (
|
|
Transaction,
|
|
error,
|
|
) {
|
|
return p.db.NewBatch(indexed), nil
|
|
}
|
|
|
|
func (p *PebbleHypergraphStore) LoadVertexTree(id []byte) (
|
|
*crypto.RawVectorCommitmentTree,
|
|
error,
|
|
) {
|
|
tree := &crypto.RawVectorCommitmentTree{}
|
|
var b bytes.Buffer
|
|
vertexData, closer, err := p.db.Get(hypergraphVertexDataKey(id))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load vertex data")
|
|
}
|
|
defer closer.Close()
|
|
b.Write(vertexData)
|
|
|
|
dec := gob.NewDecoder(&b)
|
|
if err := dec.Decode(tree); err != nil {
|
|
return nil, errors.Wrap(err, "load vertex data")
|
|
}
|
|
|
|
return tree, nil
|
|
}
|
|
|
|
func (p *PebbleHypergraphStore) LoadVertexData(id []byte) (
|
|
[]application.Encrypted,
|
|
error,
|
|
) {
|
|
tree := &crypto.RawVectorCommitmentTree{}
|
|
var b bytes.Buffer
|
|
vertexData, closer, err := p.db.Get(hypergraphVertexDataKey(id))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load vertex data")
|
|
}
|
|
defer closer.Close()
|
|
b.Write(vertexData)
|
|
|
|
dec := gob.NewDecoder(&b)
|
|
if err := dec.Decode(tree); err != nil {
|
|
return nil, errors.Wrap(err, "load vertex data")
|
|
}
|
|
|
|
encData := []application.Encrypted{}
|
|
for _, d := range crypto.GetAllLeaves(tree) {
|
|
verencData := crypto.MPCitHVerEncFromBytes(d.Value)
|
|
encData = append(encData, verencData)
|
|
}
|
|
|
|
return encData, nil
|
|
}
|
|
|
|
func (p *PebbleHypergraphStore) SaveVertexTree(
|
|
txn Transaction,
|
|
id []byte,
|
|
vertTree *crypto.RawVectorCommitmentTree,
|
|
) error {
|
|
var buf bytes.Buffer
|
|
enc := gob.NewEncoder(&buf)
|
|
if err := enc.Encode(vertTree); err != nil {
|
|
return errors.Wrap(err, "save vertex tree")
|
|
}
|
|
|
|
return errors.Wrap(
|
|
txn.Set(hypergraphVertexDataKey(id), buf.Bytes()),
|
|
"save vertex tree",
|
|
)
|
|
}
|
|
|
|
func (p *PebbleHypergraphStore) CommitAndSaveVertexData(
|
|
txn Transaction,
|
|
id []byte,
|
|
data []application.Encrypted,
|
|
) (*crypto.RawVectorCommitmentTree, []byte, error) {
|
|
dataTree := application.EncryptedToVertexTree(data)
|
|
commit := dataTree.Commit(false)
|
|
|
|
var buf bytes.Buffer
|
|
enc := gob.NewEncoder(&buf)
|
|
if err := enc.Encode(dataTree); err != nil {
|
|
return nil, nil, errors.Wrap(err, "commit and save vertex data")
|
|
}
|
|
|
|
return dataTree, commit, errors.Wrap(
|
|
txn.Set(hypergraphVertexDataKey(id), buf.Bytes()),
|
|
"commit and save vertex data",
|
|
)
|
|
}
|
|
|
|
func (p *PebbleHypergraphStore) LoadHypergraph() (
|
|
*application.Hypergraph,
|
|
error,
|
|
) {
|
|
hg := application.NewHypergraph(
|
|
func(
|
|
shardKey application.ShardKey,
|
|
phaseSet protobufs.HypergraphPhaseSet,
|
|
) crypto.VectorCommitmentTree {
|
|
return NewPersistentVectorTree(p, shardKey, phaseSet)
|
|
},
|
|
)
|
|
vertexAddsIter, err := p.db.NewIter(
|
|
[]byte{HYPERGRAPH_SHARD, VERTEX_ADDS, SET_TREE_ROOT},
|
|
[]byte{HYPERGRAPH_SHARD, VERTEX_ADDS, SET_TREE_BRANCH},
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
defer vertexAddsIter.Close()
|
|
for vertexAddsIter.First(); vertexAddsIter.Valid(); vertexAddsIter.Next() {
|
|
shardKey := make([]byte, len(vertexAddsIter.Key()))
|
|
copy(shardKey, vertexAddsIter.Key())
|
|
node := &StoredBranchNode{}
|
|
var b bytes.Buffer
|
|
b.Write(vertexAddsIter.Value())
|
|
|
|
dec := gob.NewDecoder(&b)
|
|
if err := dec.Decode(node); err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
|
|
tree := NewPersistentVectorTree(
|
|
p,
|
|
shardKeyFromKey(shardKey),
|
|
protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_VERTEX_ADDS,
|
|
)
|
|
|
|
tree.tree.Root, err = tree.storedToBranch(node)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
|
|
err := hg.SetIdSet(
|
|
shardKeyFromKey(shardKey),
|
|
protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_VERTEX_ADDS,
|
|
tree,
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
}
|
|
|
|
vertexRemovesIter, err := p.db.NewIter(
|
|
[]byte{HYPERGRAPH_SHARD, VERTEX_REMOVES, SET_TREE_ROOT},
|
|
[]byte{HYPERGRAPH_SHARD, VERTEX_REMOVES, SET_TREE_BRANCH},
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
defer vertexRemovesIter.Close()
|
|
for vertexRemovesIter.First(); vertexRemovesIter.Valid(); vertexRemovesIter.Next() {
|
|
shardKey := make([]byte, len(vertexRemovesIter.Key()))
|
|
copy(shardKey, vertexRemovesIter.Key())
|
|
node := &StoredBranchNode{}
|
|
var b bytes.Buffer
|
|
b.Write(vertexRemovesIter.Value())
|
|
|
|
dec := gob.NewDecoder(&b)
|
|
if err := dec.Decode(node); err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
|
|
tree := NewPersistentVectorTree(
|
|
p,
|
|
shardKeyFromKey(shardKey),
|
|
protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_VERTEX_REMOVES,
|
|
)
|
|
|
|
tree.tree.Root, err = tree.storedToBranch(node)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
|
|
err := hg.SetIdSet(
|
|
shardKeyFromKey(shardKey),
|
|
protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_VERTEX_REMOVES,
|
|
tree,
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
}
|
|
|
|
hyperedgeAddsIter, err := p.db.NewIter(
|
|
[]byte{HYPERGRAPH_SHARD, HYPEREDGE_ADDS, SET_TREE_ROOT},
|
|
[]byte{HYPERGRAPH_SHARD, HYPEREDGE_ADDS, SET_TREE_BRANCH},
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
defer hyperedgeAddsIter.Close()
|
|
for hyperedgeAddsIter.First(); hyperedgeAddsIter.Valid(); hyperedgeAddsIter.Next() {
|
|
shardKey := make([]byte, len(hyperedgeAddsIter.Key()))
|
|
copy(shardKey, hyperedgeAddsIter.Key())
|
|
node := &StoredBranchNode{}
|
|
var b bytes.Buffer
|
|
b.Write(hyperedgeAddsIter.Value())
|
|
|
|
dec := gob.NewDecoder(&b)
|
|
if err := dec.Decode(node); err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
|
|
tree := NewPersistentVectorTree(
|
|
p,
|
|
shardKeyFromKey(shardKey),
|
|
protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_HYPEREDGE_ADDS,
|
|
)
|
|
|
|
tree.tree.Root, err = tree.storedToBranch(node)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
|
|
err := hg.SetIdSet(
|
|
shardKeyFromKey(shardKey),
|
|
protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_HYPEREDGE_ADDS,
|
|
tree,
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
}
|
|
|
|
hyperedgeRemovesIter, err := p.db.NewIter(
|
|
[]byte{HYPERGRAPH_SHARD, HYPEREDGE_REMOVES, SET_TREE_ROOT},
|
|
[]byte{HYPERGRAPH_SHARD, HYPEREDGE_REMOVES, SET_TREE_BRANCH},
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
defer hyperedgeRemovesIter.Close()
|
|
for hyperedgeRemovesIter.First(); hyperedgeRemovesIter.Valid(); hyperedgeRemovesIter.Next() {
|
|
shardKey := make([]byte, len(hyperedgeRemovesIter.Key()))
|
|
copy(shardKey, hyperedgeRemovesIter.Key())
|
|
node := &StoredBranchNode{}
|
|
var b bytes.Buffer
|
|
b.Write(hyperedgeRemovesIter.Value())
|
|
|
|
dec := gob.NewDecoder(&b)
|
|
if err := dec.Decode(node); err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
|
|
tree := NewPersistentVectorTree(
|
|
p,
|
|
shardKeyFromKey(shardKey),
|
|
protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_HYPEREDGE_REMOVES,
|
|
)
|
|
|
|
tree.tree.Root, err = tree.storedToBranch(node)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
|
|
err := hg.SetIdSet(
|
|
shardKeyFromKey(shardKey),
|
|
protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_HYPEREDGE_REMOVES,
|
|
tree,
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load hypergraph")
|
|
}
|
|
}
|
|
|
|
return hg, nil
|
|
}
|
|
|
|
func (p *PebbleHypergraphStore) SaveHypergraph(
|
|
txn Transaction,
|
|
hg *application.Hypergraph,
|
|
) error {
|
|
hg.Commit()
|
|
|
|
for _, vertexAdds := range hg.GetVertexAdds() {
|
|
if vertexAdds.IsDirty() {
|
|
err := vertexAdds.GetTree().(*PersistentVectorTree).WriteBatch(txn)
|
|
if err != nil {
|
|
return errors.Wrap(err, "save hypergraph")
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, vertexRemoves := range hg.GetVertexRemoves() {
|
|
if vertexRemoves.IsDirty() {
|
|
err := vertexRemoves.GetTree().(*PersistentVectorTree).WriteBatch(txn)
|
|
if err != nil {
|
|
return errors.Wrap(err, "save hypergraph")
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, hyperedgeAdds := range hg.GetHyperedgeAdds() {
|
|
if hyperedgeAdds.IsDirty() {
|
|
err := hyperedgeAdds.GetTree().(*PersistentVectorTree).WriteBatch(txn)
|
|
if err != nil {
|
|
return errors.Wrap(err, "save hypergraph")
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, hyperedgeRemoves := range hg.GetHyperedgeRemoves() {
|
|
if hyperedgeRemoves.IsDirty() {
|
|
err := hyperedgeRemoves.GetTree().(*PersistentVectorTree).WriteBatch(txn)
|
|
if err != nil {
|
|
return errors.Wrap(err, "save hypergraph")
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *PebbleHypergraphStore) GetBranchNode(id NodeID) (
|
|
*StoredBranchNode,
|
|
error,
|
|
) {
|
|
data, closer, err := p.db.Get([]byte(id))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "get branch node")
|
|
}
|
|
defer closer.Close()
|
|
|
|
node := &StoredBranchNode{}
|
|
var b bytes.Buffer
|
|
b.Write(data)
|
|
|
|
dec := gob.NewDecoder(&b)
|
|
if err := dec.Decode(node); err != nil {
|
|
return nil, errors.Wrap(err, "get branch node")
|
|
}
|
|
|
|
return node, nil
|
|
}
|
|
|
|
func (p *PebbleHypergraphStore) GetLeafNode(id NodeID) (
|
|
*StoredLeafNode,
|
|
error,
|
|
) {
|
|
data, closer, err := p.db.Get([]byte(id))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "get branch node")
|
|
}
|
|
defer closer.Close()
|
|
|
|
node := &StoredLeafNode{}
|
|
var b bytes.Buffer
|
|
b.Write(data)
|
|
|
|
dec := gob.NewDecoder(&b)
|
|
if err := dec.Decode(node); err != nil {
|
|
return nil, errors.Wrap(err, "get branch node")
|
|
}
|
|
|
|
return node, nil
|
|
}
|
|
|
|
func (p *PebbleHypergraphStore) BatchWrite(
|
|
txn Transaction,
|
|
branches map[NodeID]*StoredBranchNode,
|
|
leaves map[NodeID]*StoredLeafNode,
|
|
deletions map[NodeID]struct{},
|
|
) error {
|
|
for id := range deletions {
|
|
if err := txn.Delete([]byte(id)); err != nil {
|
|
return errors.Wrap(err, "batch write")
|
|
}
|
|
}
|
|
|
|
for id, node := range branches {
|
|
var buf bytes.Buffer
|
|
enc := gob.NewEncoder(&buf)
|
|
if err := enc.Encode(node); err != nil {
|
|
return errors.Wrap(err, "batch write")
|
|
}
|
|
|
|
if err := txn.Set([]byte(id), buf.Bytes()); err != nil {
|
|
return errors.Wrap(err, "batch write")
|
|
}
|
|
}
|
|
|
|
for id, node := range leaves {
|
|
var buf bytes.Buffer
|
|
enc := gob.NewEncoder(&buf)
|
|
if err := enc.Encode(node); err != nil {
|
|
return errors.Wrap(err, "batch write")
|
|
}
|
|
|
|
if err := txn.Set([]byte(id), buf.Bytes()); err != nil {
|
|
return errors.Wrap(err, "batch write")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// StoredBranchNode represents the serializable form of a branch node.
|
|
// Storage of trees necessarily includes the metadata because trees can be
|
|
// lazily loaded, and this metadata is sometimes the only information we need.
|
|
// Commitments should also be stored due to time cost of calculation.
|
|
type StoredBranchNode struct {
|
|
Prefix []int
|
|
Commitment []byte
|
|
ChildrenIDs [crypto.NodesPerBranch]NodeID
|
|
Size []byte
|
|
LeafCount int
|
|
LongestBranch int
|
|
}
|
|
|
|
// StoredLeafNode represents the serializable form of a leaf node.
|
|
// Storage of leaves necessarily includes the value of the data as we may not
|
|
// be dynamically resolving the underlying indirect reference the value
|
|
// represents with lazy loading, but the distinction between the value and hash
|
|
// target (if present) may be important.
|
|
type StoredLeafNode struct {
|
|
Commitment []byte
|
|
Key []byte
|
|
Value []byte
|
|
HashTarget []byte
|
|
Size []byte
|
|
}
|
|
|
|
type NodeID string
|
|
|
|
type PersistentVectorTree struct {
|
|
store HypergraphStore
|
|
tree *crypto.RawVectorCommitmentTree
|
|
shardKey application.ShardKey
|
|
phaseSet byte
|
|
addedBranches map[NodeID]*StoredBranchNode
|
|
addedLeaves map[NodeID]*StoredLeafNode
|
|
deletions map[NodeID]struct{}
|
|
}
|
|
|
|
var _ crypto.VectorCommitmentTree = (*PersistentVectorTree)(nil)
|
|
|
|
func NewPersistentVectorTree(
|
|
store HypergraphStore,
|
|
shardKey application.ShardKey,
|
|
phaseSet protobufs.HypergraphPhaseSet,
|
|
) *PersistentVectorTree {
|
|
phaseByte := byte(0x00)
|
|
switch phaseSet {
|
|
case protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_VERTEX_ADDS:
|
|
phaseByte = VERTEX_ADDS
|
|
case protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_VERTEX_REMOVES:
|
|
phaseByte = VERTEX_REMOVES
|
|
case protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_HYPEREDGE_ADDS:
|
|
phaseByte = HYPEREDGE_ADDS
|
|
case protobufs.HypergraphPhaseSet_HYPERGRAPH_PHASE_SET_HYPEREDGE_REMOVES:
|
|
phaseByte = HYPEREDGE_REMOVES
|
|
}
|
|
|
|
return &PersistentVectorTree{
|
|
store: store,
|
|
tree: &crypto.RawVectorCommitmentTree{},
|
|
shardKey: shardKey,
|
|
phaseSet: phaseByte,
|
|
addedBranches: make(map[NodeID]*StoredBranchNode),
|
|
addedLeaves: make(map[NodeID]*StoredLeafNode),
|
|
deletions: make(map[NodeID]struct{}),
|
|
}
|
|
}
|
|
|
|
func serializeBigInt(n *big.Int) []byte {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
return n.FillBytes(make([]byte, 32))
|
|
}
|
|
|
|
func deserializeBigInt(data []byte) *big.Int {
|
|
if len(data) == 0 {
|
|
return big.NewInt(0)
|
|
}
|
|
return new(big.Int).SetBytes(data)
|
|
}
|
|
|
|
func branchToStored(
|
|
shardKey application.ShardKey,
|
|
phaseSet byte,
|
|
prefix []int,
|
|
node *crypto.VectorCommitmentBranchNode,
|
|
) *StoredBranchNode {
|
|
stored := &StoredBranchNode{
|
|
Prefix: make([]int, len(node.Prefix)),
|
|
Commitment: node.Commitment,
|
|
ChildrenIDs: [crypto.NodesPerBranch]NodeID{},
|
|
Size: serializeBigInt(node.Size),
|
|
LeafCount: node.LeafCount,
|
|
LongestBranch: node.LongestBranch,
|
|
}
|
|
copy(stored.Prefix, node.Prefix)
|
|
for i, child := range node.Children {
|
|
if child == nil {
|
|
continue
|
|
}
|
|
switch c := child.(type) {
|
|
case *crypto.VectorCommitmentBranchNode:
|
|
stored.ChildrenIDs[i] = SetTreeBranchKey(
|
|
shardKey,
|
|
phaseSet,
|
|
append(append(append([]int{}, prefix...), node.Prefix...), i),
|
|
)
|
|
case *crypto.VectorCommitmentLeafNode:
|
|
stored.ChildrenIDs[i] = SetTreeLeafKey(
|
|
shardKey,
|
|
phaseSet,
|
|
c.Key,
|
|
)
|
|
}
|
|
}
|
|
return stored
|
|
}
|
|
|
|
func leafToStored(node *crypto.VectorCommitmentLeafNode) *StoredLeafNode {
|
|
return &StoredLeafNode{
|
|
Key: node.Key,
|
|
Commitment: node.Commitment,
|
|
Value: node.Value,
|
|
HashTarget: node.HashTarget,
|
|
Size: serializeBigInt(node.Size),
|
|
}
|
|
}
|
|
|
|
func (t *PersistentVectorTree) storedToBranch(
|
|
stored *StoredBranchNode,
|
|
) (*crypto.VectorCommitmentBranchNode, error) {
|
|
node := &crypto.VectorCommitmentBranchNode{
|
|
Prefix: stored.Prefix,
|
|
Commitment: stored.Commitment,
|
|
Children: [crypto.NodesPerBranch]crypto.VectorCommitmentNode{},
|
|
Size: deserializeBigInt(stored.Size),
|
|
LeafCount: stored.LeafCount,
|
|
LongestBranch: stored.LongestBranch,
|
|
}
|
|
|
|
for i, childID := range stored.ChildrenIDs {
|
|
if childID == "" {
|
|
continue
|
|
}
|
|
var child crypto.VectorCommitmentNode
|
|
var err error
|
|
|
|
if childID[2] == SET_TREE_BRANCH {
|
|
child, err = t.loadBranchNode(childID)
|
|
} else {
|
|
child, err = t.loadLeafNode(childID)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
node.Children[i] = child
|
|
}
|
|
|
|
return node, nil
|
|
}
|
|
|
|
func storedToLeaf(stored *StoredLeafNode) *crypto.VectorCommitmentLeafNode {
|
|
return &crypto.VectorCommitmentLeafNode{
|
|
Key: stored.Key,
|
|
Commitment: stored.Commitment,
|
|
Value: stored.Value,
|
|
HashTarget: stored.HashTarget,
|
|
Size: deserializeBigInt(stored.Size),
|
|
}
|
|
}
|
|
|
|
func (t *PersistentVectorTree) loadBranchNode(
|
|
id NodeID,
|
|
) (*crypto.VectorCommitmentBranchNode, error) {
|
|
stored, err := t.store.GetBranchNode(id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load branch node %s: %w", id, err)
|
|
}
|
|
return t.storedToBranch(stored)
|
|
}
|
|
|
|
func (t *PersistentVectorTree) loadLeafNode(id NodeID) (
|
|
*crypto.VectorCommitmentLeafNode,
|
|
error,
|
|
) {
|
|
stored, err := t.store.GetLeafNode(id)
|
|
if err != nil {
|
|
return nil, errors.Wrap(
|
|
errors.Wrap(
|
|
err,
|
|
fmt.Sprintf("failed to load leaf node %s", id),
|
|
),
|
|
"load leaf node",
|
|
)
|
|
}
|
|
return storedToLeaf(stored), nil
|
|
}
|
|
|
|
func (t *PersistentVectorTree) Load() error {
|
|
stored, err := t.store.GetBranchNode(SetTreeBranchKey(
|
|
t.shardKey,
|
|
t.phaseSet,
|
|
[]int{},
|
|
))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
root, err := t.storedToBranch(stored)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
t.tree.Root = root
|
|
return nil
|
|
}
|
|
|
|
func (t *PersistentVectorTree) trackNodeChanges(
|
|
oldPrefix, newPrefix []int,
|
|
oldNode, newNode crypto.VectorCommitmentNode,
|
|
) {
|
|
if oldNode == nil && newNode == nil {
|
|
return
|
|
}
|
|
|
|
// deletions first
|
|
if oldNode != nil {
|
|
var oldID NodeID
|
|
switch n := oldNode.(type) {
|
|
case *crypto.VectorCommitmentBranchNode:
|
|
oldID = SetTreeBranchKey(
|
|
t.shardKey,
|
|
t.phaseSet,
|
|
oldPrefix,
|
|
)
|
|
case *crypto.VectorCommitmentLeafNode:
|
|
oldID = SetTreeLeafKey(
|
|
t.shardKey,
|
|
t.phaseSet,
|
|
n.Key,
|
|
)
|
|
}
|
|
|
|
if _, ok := t.addedBranches[oldID]; ok {
|
|
delete(t.addedBranches, oldID)
|
|
} else if _, ok := t.addedLeaves[oldID]; ok {
|
|
delete(t.addedLeaves, oldID)
|
|
} else {
|
|
t.deletions[oldID] = struct{}{}
|
|
}
|
|
}
|
|
|
|
// then additions
|
|
if newNode != nil {
|
|
switch n := newNode.(type) {
|
|
case *crypto.VectorCommitmentBranchNode:
|
|
id := SetTreeBranchKey(
|
|
t.shardKey,
|
|
t.phaseSet,
|
|
newPrefix,
|
|
)
|
|
t.addedBranches[id] = branchToStored(
|
|
t.shardKey,
|
|
t.phaseSet,
|
|
newPrefix,
|
|
n,
|
|
)
|
|
case *crypto.VectorCommitmentLeafNode:
|
|
id := SetTreeLeafKey(
|
|
t.shardKey,
|
|
t.phaseSet,
|
|
n.Key,
|
|
)
|
|
t.addedLeaves[id] = leafToStored(n)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (t *PersistentVectorTree) Get(key []byte) ([]byte, error) {
|
|
if len(key) == 0 {
|
|
return nil, errors.New("empty key not allowed")
|
|
}
|
|
|
|
var get func(
|
|
prefix []int,
|
|
node crypto.VectorCommitmentNode,
|
|
depth int,
|
|
) ([]byte, error)
|
|
|
|
get = func(
|
|
prefix []int,
|
|
node crypto.VectorCommitmentNode,
|
|
depth int,
|
|
) ([]byte, error) {
|
|
if node == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
switch n := node.(type) {
|
|
case *crypto.VectorCommitmentLeafNode:
|
|
if bytes.Equal(n.Key, key) {
|
|
return n.Value, nil
|
|
}
|
|
return nil, nil
|
|
|
|
case *crypto.VectorCommitmentBranchNode:
|
|
// Check prefix match
|
|
for i, expectedNibble := range n.Prefix {
|
|
if crypto.GetNextNibble(
|
|
key,
|
|
depth+i*crypto.BranchBits,
|
|
) != expectedNibble {
|
|
return nil, nil
|
|
}
|
|
}
|
|
// Get final nibble after prefix
|
|
finalNibble := crypto.GetNextNibble(
|
|
key,
|
|
depth+len(n.Prefix)*crypto.BranchBits,
|
|
)
|
|
|
|
isLoaded := false
|
|
for _, c := range n.Children {
|
|
if c != nil {
|
|
isLoaded = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !isLoaded {
|
|
var self *crypto.VectorCommitmentBranchNode
|
|
var err error
|
|
|
|
if len(prefix) == 0 {
|
|
self, err = t.loadBranchNode(
|
|
SetTreeBranchKey(
|
|
t.shardKey,
|
|
t.phaseSet,
|
|
append([]int{}, prefix...),
|
|
),
|
|
)
|
|
} else {
|
|
self, err = t.loadBranchNode(
|
|
SetTreeBranchKey(
|
|
t.shardKey,
|
|
t.phaseSet,
|
|
append(
|
|
append([]int{}, prefix...),
|
|
n.Prefix...,
|
|
),
|
|
),
|
|
)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "get")
|
|
}
|
|
n.Children = self.Children
|
|
}
|
|
return get(
|
|
append(
|
|
append(
|
|
append([]int{}, prefix...),
|
|
n.Prefix...,
|
|
),
|
|
finalNibble,
|
|
),
|
|
n.Children[finalNibble],
|
|
depth+len(n.Prefix)*crypto.BranchBits+crypto.BranchBits,
|
|
)
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
value, err := get([]int{}, t.tree.Root, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if value == nil {
|
|
return nil, errors.New("key not found")
|
|
}
|
|
|
|
return value, nil
|
|
}
|
|
|
|
func (t *PersistentVectorTree) Insert(
|
|
key, value, hashTarget []byte,
|
|
size *big.Int,
|
|
) error {
|
|
if len(key) == 0 {
|
|
return errors.New("empty key not allowed")
|
|
}
|
|
|
|
var insert func(
|
|
prefix []int,
|
|
node crypto.VectorCommitmentNode,
|
|
depth int,
|
|
) (crypto.VectorCommitmentNode, error)
|
|
|
|
insert = func(
|
|
prefix []int,
|
|
node crypto.VectorCommitmentNode,
|
|
depth int,
|
|
) (crypto.VectorCommitmentNode, error) {
|
|
if node == nil {
|
|
t.trackNodeChanges(
|
|
nil,
|
|
prefix,
|
|
nil,
|
|
&crypto.VectorCommitmentLeafNode{
|
|
Key: key,
|
|
Value: value,
|
|
HashTarget: hashTarget,
|
|
Size: size,
|
|
},
|
|
)
|
|
return &crypto.VectorCommitmentLeafNode{
|
|
Key: key,
|
|
Value: value,
|
|
HashTarget: hashTarget,
|
|
Size: size,
|
|
}, nil
|
|
}
|
|
|
|
switch n := node.(type) {
|
|
case *crypto.VectorCommitmentLeafNode:
|
|
if bytes.Equal(n.Key, key) {
|
|
n.Value = value
|
|
n.HashTarget = hashTarget
|
|
n.Commitment = nil
|
|
n.Size = size
|
|
t.trackNodeChanges(nil, prefix, nil, n)
|
|
return n, nil
|
|
}
|
|
|
|
// Get common prefix nibbles and divergence point
|
|
sharedNibbles, divergeDepth := crypto.GetNibblesUntilDiverge(
|
|
n.Key,
|
|
key,
|
|
depth,
|
|
)
|
|
|
|
// Create single branch node with shared prefix
|
|
branch := &crypto.VectorCommitmentBranchNode{
|
|
Prefix: sharedNibbles,
|
|
LeafCount: 2,
|
|
LongestBranch: 1,
|
|
Size: new(big.Int).Add(n.Size, size),
|
|
}
|
|
|
|
// Add both leaves at their final positions
|
|
finalOldNibble := crypto.GetNextNibble(n.Key, divergeDepth)
|
|
finalNewNibble := crypto.GetNextNibble(key, divergeDepth)
|
|
branch.Children[finalOldNibble] = n
|
|
branch.Children[finalNewNibble] = &crypto.VectorCommitmentLeafNode{
|
|
Key: key,
|
|
Value: value,
|
|
HashTarget: hashTarget,
|
|
Size: size,
|
|
}
|
|
t.trackNodeChanges(
|
|
nil,
|
|
append(append(append([]int{}, prefix...), sharedNibbles...), finalOldNibble),
|
|
nil,
|
|
n,
|
|
)
|
|
t.trackNodeChanges(
|
|
nil,
|
|
append(append(append([]int{}, prefix...), sharedNibbles...), finalNewNibble),
|
|
nil,
|
|
&crypto.VectorCommitmentLeafNode{
|
|
Key: key,
|
|
Value: value,
|
|
HashTarget: hashTarget,
|
|
Size: size,
|
|
},
|
|
)
|
|
t.trackNodeChanges(
|
|
nil,
|
|
prefix,
|
|
nil,
|
|
branch,
|
|
)
|
|
return branch, nil
|
|
|
|
case *crypto.VectorCommitmentBranchNode:
|
|
isLoaded := false
|
|
for _, c := range n.Children {
|
|
if c != nil {
|
|
isLoaded = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !isLoaded {
|
|
self, err := t.loadBranchNode(
|
|
SetTreeBranchKey(
|
|
t.shardKey,
|
|
t.phaseSet,
|
|
append(
|
|
append([]int{}, prefix...),
|
|
n.Prefix...,
|
|
),
|
|
),
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "insert")
|
|
}
|
|
n.Children = self.Children
|
|
}
|
|
|
|
if len(n.Prefix) > 0 {
|
|
// Check if the new key matches the prefix
|
|
for i, expectedNibble := range n.Prefix {
|
|
actualNibble := crypto.GetNextNibble(key, depth+i*crypto.BranchBits)
|
|
if actualNibble != expectedNibble {
|
|
// Create new branch with shared prefix subset
|
|
newBranch := &crypto.VectorCommitmentBranchNode{
|
|
Prefix: n.Prefix[:i],
|
|
LeafCount: n.LeafCount + 1,
|
|
LongestBranch: n.LongestBranch + 1,
|
|
Size: new(big.Int).Add(n.Size, size),
|
|
}
|
|
// Position old branch and new leaf
|
|
newBranch.Children[expectedNibble] = n
|
|
n.Prefix = n.Prefix[i+1:] // remove shared prefix from old branch
|
|
newBranch.Children[actualNibble] = &crypto.VectorCommitmentLeafNode{
|
|
Key: key,
|
|
Value: value,
|
|
HashTarget: hashTarget,
|
|
Size: size,
|
|
}
|
|
|
|
t.trackNodeChanges(
|
|
prefix,
|
|
append(append(append([]int{}, prefix...), newBranch.Prefix...), expectedNibble),
|
|
n,
|
|
n,
|
|
)
|
|
t.trackNodeChanges(
|
|
nil,
|
|
append(append(append([]int{}, prefix...), newBranch.Prefix...), actualNibble),
|
|
nil,
|
|
&crypto.VectorCommitmentLeafNode{
|
|
Key: key,
|
|
Value: value,
|
|
HashTarget: hashTarget,
|
|
Size: size,
|
|
},
|
|
)
|
|
t.trackNodeChanges(
|
|
nil,
|
|
prefix,
|
|
nil,
|
|
newBranch,
|
|
)
|
|
return newBranch, nil
|
|
}
|
|
}
|
|
|
|
// Key matches prefix, continue with final nibble
|
|
finalNibble := crypto.GetNextNibble(
|
|
key,
|
|
depth+len(n.Prefix)*crypto.BranchBits,
|
|
)
|
|
inserted, err := insert(
|
|
append(
|
|
append(
|
|
append([]int{}, prefix...),
|
|
n.Prefix...,
|
|
),
|
|
finalNibble,
|
|
),
|
|
n.Children[finalNibble],
|
|
depth+len(n.Prefix)*crypto.BranchBits+crypto.BranchBits,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
n.Children[finalNibble] = inserted
|
|
n.Commitment = nil
|
|
n.LeafCount += 1
|
|
switch i := inserted.(type) {
|
|
case *crypto.VectorCommitmentBranchNode:
|
|
if n.LongestBranch <= i.LongestBranch {
|
|
n.LongestBranch = i.LongestBranch + 1
|
|
}
|
|
case *crypto.VectorCommitmentLeafNode:
|
|
n.LongestBranch = 1
|
|
}
|
|
n.Size = n.Size.Add(n.Size, size)
|
|
|
|
t.trackNodeChanges(
|
|
nil,
|
|
prefix,
|
|
nil,
|
|
n,
|
|
)
|
|
return n, nil
|
|
} else {
|
|
// Simple branch without prefix
|
|
nibble := crypto.GetNextNibble(key, depth)
|
|
inserted, err := insert(
|
|
append(
|
|
append(
|
|
append([]int{}, prefix...),
|
|
n.Prefix...,
|
|
),
|
|
nibble,
|
|
),
|
|
n.Children[nibble],
|
|
depth+crypto.BranchBits,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
n.Children[nibble] = inserted
|
|
n.Commitment = nil
|
|
n.LeafCount += 1
|
|
switch i := inserted.(type) {
|
|
case *crypto.VectorCommitmentBranchNode:
|
|
if n.LongestBranch <= i.LongestBranch {
|
|
n.LongestBranch = i.LongestBranch + 1
|
|
}
|
|
case *crypto.VectorCommitmentLeafNode:
|
|
n.LongestBranch = 1
|
|
}
|
|
n.Size = n.Size.Add(n.Size, size)
|
|
|
|
t.trackNodeChanges(
|
|
nil,
|
|
prefix,
|
|
nil,
|
|
n,
|
|
)
|
|
return n, nil
|
|
}
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
newRoot, err := insert([]int{}, t.tree.Root, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
t.tree.Root = newRoot
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *PersistentVectorTree) Delete(key []byte) error {
|
|
return errors.New("deletion not supported")
|
|
}
|
|
|
|
func (t *PersistentVectorTree) Commit(recalculate bool) []byte {
|
|
return t.tree.Commit(recalculate)
|
|
}
|
|
|
|
func (t *PersistentVectorTree) WriteBatch(txn Transaction) error {
|
|
err := t.store.BatchWrite(txn, t.addedBranches, t.addedLeaves, t.deletions)
|
|
if err != nil {
|
|
return errors.Wrap(err, "write batch")
|
|
}
|
|
|
|
// Reset change tracking
|
|
t.addedBranches = make(map[NodeID]*StoredBranchNode)
|
|
t.addedLeaves = make(map[NodeID]*StoredLeafNode)
|
|
t.deletions = make(map[NodeID]struct{})
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *PersistentVectorTree) GetMetadata() (
|
|
leafCount int,
|
|
longestBranch int,
|
|
) {
|
|
return t.tree.GetMetadata()
|
|
}
|
|
|
|
func (t *PersistentVectorTree) GetSize() *big.Int {
|
|
return t.tree.GetSize()
|
|
}
|
|
|
|
func (t *PersistentVectorTree) Prove(key []byte) [][]byte {
|
|
return t.tree.Prove(key)
|
|
}
|
|
|
|
func (t *PersistentVectorTree) Verify(key []byte, proofs [][]byte) bool {
|
|
return t.tree.Verify(key, proofs)
|
|
}
|
|
|
|
func (
|
|
t *PersistentVectorTree,
|
|
) GetInternalTree() *crypto.RawVectorCommitmentTree {
|
|
return t.tree
|
|
}
|
|
|
|
// this method is truncating the ints to bytes because the total size of the
|
|
// branch bits is 6. if this ever increases, this will break a lot of things.
|
|
func packNibbles(values []int) []byte {
|
|
out := []byte{}
|
|
for _, v := range values {
|
|
out = append(out, byte(v))
|
|
}
|
|
return out
|
|
}
|