package store import ( "bytes" "encoding/gob" "github.com/pkg/errors" "go.uber.org/zap" "source.quilibrium.com/quilibrium/monorepo/node/crypto" "source.quilibrium.com/quilibrium/monorepo/node/hypergraph/application" ) type HypergraphStore interface { NewTransaction(indexed bool) (Transaction, error) LoadVertexTree(id []byte) ( *crypto.VectorCommitmentTree, error, ) LoadVertexData(id []byte) ([]application.Encrypted, error) SaveVertexTree( txn Transaction, id []byte, vertTree *crypto.VectorCommitmentTree, ) error CommitAndSaveVertexData( txn Transaction, id []byte, data []application.Encrypted, ) (*crypto.VectorCommitmentTree, []byte, error) LoadHypergraph() ( *application.Hypergraph, error, ) SaveHypergraph( txn Transaction, hg *application.Hypergraph, ) 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 ) 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[2:5]), L2: [32]byte(key[5:]), } } func (p *PebbleHypergraphStore) NewTransaction(indexed bool) ( Transaction, error, ) { return p.db.NewBatch(indexed), nil } func (p *PebbleHypergraphStore) LoadVertexTree(id []byte) ( *crypto.VectorCommitmentTree, error, ) { tree := &crypto.VectorCommitmentTree{} 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.VectorCommitmentTree{} 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.VectorCommitmentTree, ) 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.VectorCommitmentTree, []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() vertexAddsIter, err := p.db.NewIter( []byte{HYPERGRAPH_SHARD, VERTEX_ADDS}, []byte{HYPERGRAPH_SHARD, VERTEX_REMOVES}, ) 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()) err := hg.ImportFromBytes( application.VertexAtomType, application.AddsPhaseType, shardKeyFromKey(shardKey), vertexAddsIter.Value(), ) if err != nil { return nil, errors.Wrap(err, "load hypergraph") } } vertexRemovesIter, err := p.db.NewIter( []byte{HYPERGRAPH_SHARD, VERTEX_REMOVES}, []byte{HYPERGRAPH_SHARD, VERTEX_REMOVES + 1}, ) 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()) err := hg.ImportFromBytes( application.VertexAtomType, application.RemovesPhaseType, shardKeyFromKey(shardKey), vertexRemovesIter.Value(), ) if err != nil { return nil, errors.Wrap(err, "load hypergraph") } } hyperedgeAddsIter, err := p.db.NewIter( []byte{HYPERGRAPH_SHARD, HYPEREDGE_ADDS}, []byte{HYPERGRAPH_SHARD, HYPEREDGE_REMOVES}, ) 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()) err := hg.ImportFromBytes( application.HyperedgeAtomType, application.AddsPhaseType, shardKeyFromKey(shardKey), hyperedgeAddsIter.Value(), ) if err != nil { return nil, errors.Wrap(err, "load hypergraph") } } hyperedgeRemovesIter, err := p.db.NewIter( []byte{HYPERGRAPH_SHARD, HYPEREDGE_REMOVES}, []byte{HYPERGRAPH_SHARD, HYPEREDGE_REMOVES + 1}, ) 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()) err := hg.ImportFromBytes( application.HyperedgeAtomType, application.RemovesPhaseType, shardKeyFromKey(shardKey), hyperedgeRemovesIter.Value(), ) if err != nil { return nil, errors.Wrap(err, "load hypergraph") } } return hg, nil } func (p *PebbleHypergraphStore) SaveHypergraph( txn Transaction, hg *application.Hypergraph, ) error { for shardKey, vertexAdds := range hg.GetVertexAdds() { if vertexAdds.IsDirty() { err := txn.Set(hypergraphVertexAddsKey(shardKey), vertexAdds.ToBytes()) if err != nil { return errors.Wrap(err, "save hypergraph") } } } for shardKey, vertexRemoves := range hg.GetVertexRemoves() { if vertexRemoves.IsDirty() { err := txn.Set( hypergraphVertexRemovesKey(shardKey), vertexRemoves.ToBytes(), ) if err != nil { return errors.Wrap(err, "save hypergraph") } } } for shardKey, hyperedgeAdds := range hg.GetHyperedgeAdds() { if hyperedgeAdds.IsDirty() { err := txn.Set( hypergraphHyperedgeAddsKey(shardKey), hyperedgeAdds.ToBytes(), ) if err != nil { return errors.Wrap(err, "save hypergraph") } } } for shardKey, hyperedgeRemoves := range hg.GetHyperedgeRemoves() { if hyperedgeRemoves.IsDirty() { err := txn.Set( hypergraphHyperedgeRemovesKey(shardKey), hyperedgeRemoves.ToBytes(), ) if err != nil { return errors.Wrap(err, "save hypergraph") } } } return nil }