mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-27 09:05:18 +00:00
330 lines
7.0 KiB
Go
330 lines
7.0 KiB
Go
|
package store
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
|
||
|
"github.com/cockroachdb/pebble"
|
||
|
"github.com/pkg/errors"
|
||
|
"go.uber.org/zap"
|
||
|
"google.golang.org/protobuf/proto"
|
||
|
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
||
|
)
|
||
|
|
||
|
type CoinStore interface {
|
||
|
NewTransaction() (Transaction, error)
|
||
|
GetCoinsForOwner(owner []byte) ([]uint64, []*protobufs.Coin, error)
|
||
|
GetPreCoinProofsForOwner(owner []byte) (
|
||
|
[]uint64,
|
||
|
[]*protobufs.PreCoinProof,
|
||
|
error,
|
||
|
)
|
||
|
GetCoinByAddress(address []byte) (*protobufs.Coin, error)
|
||
|
GetPreCoinProofByAddress(address []byte) (*protobufs.PreCoinProof, error)
|
||
|
PutCoin(
|
||
|
txn Transaction,
|
||
|
frameNumber uint64,
|
||
|
address []byte,
|
||
|
coin *protobufs.Coin,
|
||
|
) error
|
||
|
DeleteCoin(
|
||
|
txn Transaction,
|
||
|
address []byte,
|
||
|
coin *protobufs.Coin,
|
||
|
) error
|
||
|
PutPreCoinProof(
|
||
|
txn Transaction,
|
||
|
frameNumber uint64,
|
||
|
address []byte,
|
||
|
preCoinProof *protobufs.PreCoinProof,
|
||
|
) error
|
||
|
DeletePreCoinProof(
|
||
|
txn Transaction,
|
||
|
address []byte,
|
||
|
preCoinProof *protobufs.PreCoinProof,
|
||
|
) error
|
||
|
}
|
||
|
|
||
|
var _ CoinStore = (*PebbleCoinStore)(nil)
|
||
|
|
||
|
type PebbleCoinStore struct {
|
||
|
db KVDB
|
||
|
logger *zap.Logger
|
||
|
}
|
||
|
|
||
|
func NewPebbleCoinStore(
|
||
|
db KVDB,
|
||
|
logger *zap.Logger,
|
||
|
) *PebbleCoinStore {
|
||
|
return &PebbleCoinStore{
|
||
|
db,
|
||
|
logger,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
COIN = 0x05
|
||
|
PROOF = 0x06
|
||
|
COIN_BY_ADDRESS = 0x00
|
||
|
COIN_BY_OWNER = 0x01
|
||
|
)
|
||
|
|
||
|
func coinKey(address []byte) []byte {
|
||
|
key := []byte{COIN, COIN_BY_ADDRESS}
|
||
|
key = append(key, address...)
|
||
|
return key
|
||
|
}
|
||
|
|
||
|
func coinByOwnerKey(owner []byte, address []byte) []byte {
|
||
|
key := []byte{COIN, COIN_BY_OWNER}
|
||
|
key = append(key, owner...)
|
||
|
key = append(key, address...)
|
||
|
return key
|
||
|
}
|
||
|
|
||
|
func proofKey(address []byte) []byte {
|
||
|
key := []byte{PROOF, COIN_BY_ADDRESS}
|
||
|
key = append(key, address...)
|
||
|
return key
|
||
|
}
|
||
|
|
||
|
func proofByOwnerKey(owner []byte, address []byte) []byte {
|
||
|
key := []byte{PROOF, COIN_BY_OWNER}
|
||
|
key = append(key, owner...)
|
||
|
key = append(key, address...)
|
||
|
return key
|
||
|
}
|
||
|
|
||
|
func (p *PebbleCoinStore) NewTransaction() (Transaction, error) {
|
||
|
return p.db.NewBatch(), nil
|
||
|
}
|
||
|
|
||
|
func (p *PebbleCoinStore) GetCoinsForOwner(
|
||
|
owner []byte,
|
||
|
) ([]uint64, []*protobufs.Coin, error) {
|
||
|
iter, err := p.db.NewIter(
|
||
|
coinByOwnerKey(owner, bytes.Repeat([]byte{0x00}, 32)),
|
||
|
coinByOwnerKey(owner, bytes.Repeat([]byte{0xff}, 32)),
|
||
|
)
|
||
|
if err != nil {
|
||
|
if errors.Is(err, pebble.ErrNotFound) {
|
||
|
err = ErrNotFound
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
err = errors.Wrap(err, "get coins for owner")
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
|
||
|
defer iter.Close()
|
||
|
frameNumbers := []uint64{}
|
||
|
coins := []*protobufs.Coin{}
|
||
|
for iter.First(); iter.Valid(); iter.Next() {
|
||
|
coinBytes := iter.Value()
|
||
|
frameNumber := binary.BigEndian.Uint64(coinBytes[:8])
|
||
|
coin := &protobufs.Coin{}
|
||
|
err := proto.Unmarshal(coinBytes[8:], coin)
|
||
|
if err != nil {
|
||
|
return nil, nil, errors.Wrap(err, "get coins for owner")
|
||
|
}
|
||
|
frameNumbers = append(frameNumbers, frameNumber)
|
||
|
coins = append(coins, coin)
|
||
|
}
|
||
|
|
||
|
return frameNumbers, coins, nil
|
||
|
}
|
||
|
|
||
|
func (p *PebbleCoinStore) GetPreCoinProofsForOwner(owner []byte) (
|
||
|
[]uint64,
|
||
|
[]*protobufs.PreCoinProof,
|
||
|
error,
|
||
|
) {
|
||
|
iter, err := p.db.NewIter(
|
||
|
proofByOwnerKey(owner, bytes.Repeat([]byte{0x00}, 32)),
|
||
|
proofByOwnerKey(owner, bytes.Repeat([]byte{0xff}, 32)),
|
||
|
)
|
||
|
if err != nil {
|
||
|
if errors.Is(err, pebble.ErrNotFound) {
|
||
|
err = ErrNotFound
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
err = errors.Wrap(err, "get pre coin proofs for owner")
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
|
||
|
defer iter.Close()
|
||
|
frameNumbers := []uint64{}
|
||
|
proofs := []*protobufs.PreCoinProof{}
|
||
|
for iter.First(); iter.Valid(); iter.Next() {
|
||
|
proofBytes := iter.Value()
|
||
|
frameNumber := binary.BigEndian.Uint64(proofBytes[:8])
|
||
|
proof := &protobufs.PreCoinProof{}
|
||
|
err := proto.Unmarshal(proofBytes[8:], proof)
|
||
|
if err != nil {
|
||
|
return nil, nil, errors.Wrap(err, "get pre coin proofs for owner")
|
||
|
}
|
||
|
frameNumbers = append(frameNumbers, frameNumber)
|
||
|
proofs = append(proofs, proof)
|
||
|
}
|
||
|
|
||
|
return frameNumbers, proofs, nil
|
||
|
}
|
||
|
|
||
|
func (p *PebbleCoinStore) GetCoinByAddress(address []byte) (
|
||
|
*protobufs.Coin,
|
||
|
error,
|
||
|
) {
|
||
|
coinBytes, closer, err := p.db.Get(coinKey(address))
|
||
|
if err != nil {
|
||
|
if errors.Is(err, pebble.ErrNotFound) {
|
||
|
err = ErrNotFound
|
||
|
return nil, err
|
||
|
}
|
||
|
err = errors.Wrap(err, "get coin by address")
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
defer closer.Close()
|
||
|
|
||
|
coin := &protobufs.Coin{}
|
||
|
err = proto.Unmarshal(coinBytes[:8], coin)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "get coin by address")
|
||
|
}
|
||
|
|
||
|
return coin, nil
|
||
|
}
|
||
|
|
||
|
func (p *PebbleCoinStore) GetPreCoinProofByAddress(address []byte) (
|
||
|
*protobufs.PreCoinProof,
|
||
|
error,
|
||
|
) {
|
||
|
preCoinProofBytes, closer, err := p.db.Get(proofKey(address))
|
||
|
if err != nil {
|
||
|
if errors.Is(err, pebble.ErrNotFound) {
|
||
|
err = ErrNotFound
|
||
|
return nil, err
|
||
|
}
|
||
|
err = errors.Wrap(err, "get pre coin proof by address")
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
defer closer.Close()
|
||
|
|
||
|
proof := &protobufs.PreCoinProof{}
|
||
|
err = proto.Unmarshal(preCoinProofBytes[:8], proof)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "get pre coin proof by address")
|
||
|
}
|
||
|
|
||
|
return proof, nil
|
||
|
}
|
||
|
|
||
|
func (p *PebbleCoinStore) PutCoin(
|
||
|
txn Transaction,
|
||
|
frameNumber uint64,
|
||
|
address []byte,
|
||
|
coin *protobufs.Coin,
|
||
|
) error {
|
||
|
coinBytes, err := proto.Marshal(coin)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "put coin")
|
||
|
}
|
||
|
|
||
|
data := []byte{}
|
||
|
data = binary.BigEndian.AppendUint64(data, frameNumber)
|
||
|
data = append(data, coinBytes...)
|
||
|
err = txn.Set(
|
||
|
coinByOwnerKey(coin.Owner.GetImplicitAccount().Address, address),
|
||
|
data,
|
||
|
)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "put coin")
|
||
|
}
|
||
|
|
||
|
err = txn.Set(
|
||
|
coinKey(coin.Owner.GetImplicitAccount().Address),
|
||
|
data,
|
||
|
)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "put coin")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (p *PebbleCoinStore) DeleteCoin(
|
||
|
txn Transaction,
|
||
|
address []byte,
|
||
|
coin *protobufs.Coin,
|
||
|
) error {
|
||
|
err := txn.Delete(coinKey(address))
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "delete coin")
|
||
|
}
|
||
|
|
||
|
err = txn.Delete(
|
||
|
coinByOwnerKey(coin.Owner.GetImplicitAccount().GetAddress(), address),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "delete coin")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (p *PebbleCoinStore) PutPreCoinProof(
|
||
|
txn Transaction,
|
||
|
frameNumber uint64,
|
||
|
address []byte,
|
||
|
preCoinProof *protobufs.PreCoinProof,
|
||
|
) error {
|
||
|
proofBytes, err := proto.Marshal(preCoinProof)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "put pre coin proof")
|
||
|
}
|
||
|
|
||
|
data := []byte{}
|
||
|
data = binary.BigEndian.AppendUint64(data, frameNumber)
|
||
|
data = append(data, proofBytes...)
|
||
|
err = txn.Set(
|
||
|
proofByOwnerKey(preCoinProof.Owner.GetImplicitAccount().Address, address),
|
||
|
data,
|
||
|
)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "put pre coin proof")
|
||
|
}
|
||
|
|
||
|
err = txn.Set(
|
||
|
proofKey(preCoinProof.Owner.GetImplicitAccount().Address),
|
||
|
data,
|
||
|
)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "put pre coin proof")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (p *PebbleCoinStore) DeletePreCoinProof(
|
||
|
txn Transaction,
|
||
|
address []byte,
|
||
|
preCoinProof *protobufs.PreCoinProof,
|
||
|
) error {
|
||
|
err := txn.Delete(proofKey(address))
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "delete pre coin proof")
|
||
|
}
|
||
|
|
||
|
err = txn.Delete(
|
||
|
proofByOwnerKey(
|
||
|
preCoinProof.Owner.GetImplicitAccount().GetAddress(),
|
||
|
address,
|
||
|
),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "delete pre coin proof")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|