ceremonyclient/node/store/coin.go

330 lines
7.0 KiB
Go
Raw Normal View History

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
}