Skip to content

Commit

Permalink
implement utxo keeper
Browse files Browse the repository at this point in the history
  • Loading branch information
keithsue committed Jun 13, 2024
1 parent fff5b81 commit c7ee688
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 46 deletions.
26 changes: 9 additions & 17 deletions x/btclightclient/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (

type (
Keeper struct {
BaseUTXOKeeper

cdc codec.BinaryCodec
storeKey storetypes.StoreKey
memKey storetypes.StoreKey
Expand All @@ -37,10 +39,11 @@ func NewKeeper(
bankKeeper types.BankKeeper,
) *Keeper {
return &Keeper{
cdc: cdc,
storeKey: storeKey,
memKey: memKey,
bankKeeper: bankKeeper,
cdc: cdc,
storeKey: storeKey,
memKey: memKey,
bankKeeper: bankKeeper,
BaseUTXOKeeper: *NewBaseUTXOKeeper(cdc, storeKey),
}
}

Expand Down Expand Up @@ -272,8 +275,8 @@ func (k Keeper) ProcessBitcoinDepositTransaction(ctx sdk.Context, msg *types.Msg
IsLocked: false,
}

k.SetUtxo(ctx, utxo)
k.SetOwnerUtxo(ctx, utxo)
k.SetUTXO(ctx, &utxo)
k.SetOwnerUTXO(ctx, &utxo)

ctx.Logger().Info("Minted Bitcoin Voucher", "index", i, "address", addr.EncodeAddress(), "amount", out.Value, "sender", sender.EncodeAddress(), "senderAddr", senderAddr.String(), "coins", coins.String())

Expand All @@ -284,17 +287,6 @@ func (k Keeper) ProcessBitcoinDepositTransaction(ctx sdk.Context, msg *types.Msg
return nil
}

func (k Keeper) SetUtxo(ctx sdk.Context, utxo types.UTXO) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshal(&utxo)
store.Set(types.BtcUtxoKey(utxo.Txid, utxo.Vout), bz)
}

func (k Keeper) SetOwnerUtxo(ctx sdk.Context, utxo types.UTXO) {
store := ctx.KVStore(k.storeKey)
store.Set(types.BtcOwnerUtxoKey(utxo.Address, utxo.Txid, utxo.Vout), nil)
}

func (k Keeper) GetBlockHeader(ctx sdk.Context, hash string) *types.BlockHeader {
store := ctx.KVStore(k.storeKey)
var blockHeader types.BlockHeader
Expand Down
248 changes: 224 additions & 24 deletions x/btclightclient/keeper/utxo.go
Original file line number Diff line number Diff line change
@@ -1,77 +1,277 @@
package keeper

import (
"math/big"
"sort"

"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/sideprotocol/side/x/btclightclient/types"
)

type UTXOViewKeeper interface {
HasUTXO(ctx sdk.Context, hash string, vout uint64) bool
IsUTXOLocked(ctx sdk.Context, hash string, vout uint64) bool
IsUTXOLocked(ctx sdk.Context, hash string, vout uint64) (bool, error)

GetUTXO(ctx sdk.Context, hash string, vout uint64) *types.UTXO
GetAllUTXOs(ctx sdk.Context) []*types.UTXO

GetUTXOsByAddr(ctx sdk.Context, addr string) []*types.UTXO
GetUnlockedUTXOsByAddr(ctx sdk.Context, addr string) []*types.UTXO
GetOrderedUTXOsByAddr(ctx sdk.Context, addr string) []*types.UTXO

IterateAllUTXOs(ctx sdk.Context, cb func(utxo *types.UTXO) (stop bool))
IterateUTXOsByAddr(ctx sdk.Context, addr string, cb func(addr string, utxo *types.UTXO) (stop bool))
}

type UTXOKeeper interface {
UTXOViewKeeper

LockUTXO(ctx sdk.Context, hash string, vout uint64)
SpendUTXO(ctx sdk.Context, hash string, vout uint64)
LockUTXO(ctx sdk.Context, hash string, vout uint64) error
LockUTXOs(ctx sdk.Context, utxos []*types.UTXO) error

UnlockUTXO(ctx sdk.Context, hash string, vout uint64) error
UnlockUTXOs(ctx sdk.Context, utxos []*types.UTXO) error

SpendUTXO(ctx sdk.Context, hash string, vout uint64) error
SpendUTXOs(ctx sdk.Context, utxos []*types.UTXO) error
}

var _ UTXOKeeper = (*BaseUTXOKeeper)(nil)

type BaseUTXOViewKeeper struct {
k Keeper
cdc codec.BinaryCodec
storeKey storetypes.StoreKey
}

func NewBaseUTXOViewKeeper(k Keeper) *BaseUTXOViewKeeper {
return &BaseUTXOViewKeeper{k}
func NewBaseUTXOViewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey) *BaseUTXOViewKeeper {
return &BaseUTXOViewKeeper{
cdc,
storeKey,
}
}

func (bvk *BaseUTXOViewKeeper) HasUTXO(ctx sdk.Context, hash string, vout uint64) bool {
// TODO
return true
store := ctx.KVStore(bvk.storeKey)
return store.Has(types.BtcUtxoKey(hash, vout))
}

func (bvk *BaseUTXOViewKeeper) IsUTXOLocked(ctx sdk.Context, hash string, vout uint64) bool {
// TODO
return true
func (bvk *BaseUTXOViewKeeper) IsUTXOLocked(ctx sdk.Context, hash string, vout uint64) (bool, error) {
if !bvk.HasUTXO(ctx, hash, vout) {
return false, types.ErrUTXODoesNotExist
}

utxo := bvk.GetUTXO(ctx, hash, vout)

return utxo.IsLocked, nil
}

func (bvk *BaseUTXOViewKeeper) GetUTXO(ctx sdk.Context, hash string, vout uint64) *types.UTXO {
store := ctx.KVStore(bvk.storeKey)

var utxo types.UTXO
bz := store.Get(types.BtcUtxoKey(hash, vout))
bvk.cdc.MustUnmarshal(bz, &utxo)

return &utxo
}

func (bvk *BaseUTXOViewKeeper) GetAllUTXOs(ctx sdk.Context) []*types.UTXO {
// TODO
return nil
utxos := make([]*types.UTXO, 0)

bvk.IterateAllUTXOs(ctx, func(utxo *types.UTXO) (stop bool) {
utxos = append(utxos, utxo)
return false
})

return utxos
}

func (bvk *BaseUTXOViewKeeper) GetUTXOsByAddr(ctx sdk.Context, addr string) []*types.UTXO {
// TODO
return nil
utxos := make([]*types.UTXO, 0)

bvk.IterateUTXOsByAddr(ctx, addr, func(addr string, utxo *types.UTXO) (stop bool) {
utxos = append(utxos, utxo)
return false
})

return utxos
}

func (bvk *BaseUTXOViewKeeper) GetUnlockedUTXOsByAddr(ctx sdk.Context, addr string) []*types.UTXO {
utxos := make([]*types.UTXO, 0)

bvk.IterateUTXOsByAddr(ctx, addr, func(addr string, utxo *types.UTXO) (stop bool) {
if !utxo.IsLocked {
utxos = append(utxos, utxo)
}

return false
})

return utxos
}

// GetOrderedUTXOsByAddr gets all unlocked utxos of the given address in the descending order by amount
func (bvk *BaseUTXOViewKeeper) GetOrderedUTXOsByAddr(ctx sdk.Context, addr string) []*types.UTXO {
// TODO
return nil
utxos := make([]*types.UTXO, 0)

bvk.IterateUTXOsByAddr(ctx, addr, func(addr string, utxo *types.UTXO) (stop bool) {
if !utxo.IsLocked {
utxos = append(utxos, utxo)
}

return false
})

// sort utoxs in the descending order
sort.SliceStable(utxos, func(i int, j int) bool {
return utxos[i].Amount > utxos[j].Amount
})

return utxos
}

func (bvk *BaseUTXOViewKeeper) IterateAllUTXOs(ctx sdk.Context, cb func(utxo *types.UTXO) (stop bool)) {
store := ctx.KVStore(bvk.storeKey)

iterator := sdk.KVStorePrefixIterator(store, types.BtcUtxoKeyPrefix)
defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
var utxo types.UTXO
bvk.cdc.MustUnmarshal(iterator.Value(), &utxo)

if cb(&utxo) {
break
}
}
}

func (bvk *BaseUTXOViewKeeper) IterateUTXOsByAddr(ctx sdk.Context, addr string, cb func(addr string, utxo *types.UTXO) (stop bool)) {
store := ctx.KVStore(bvk.storeKey)

keyPrefix := append(types.BtcOwnerUtxoKeyPrefix, []byte(addr)...)
iterator := sdk.KVStorePrefixIterator(store, keyPrefix)
defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
key := iterator.Key()

hash := key[1+len(addr) : 1+len(addr)+64]
vout := key[1+len(addr)+64:]

utxo := bvk.GetUTXO(ctx, string(hash), new(big.Int).SetBytes(vout).Uint64())
if cb(addr, utxo) {
break
}
}
}

type BaseUTXOKeeper struct {
BaseUTXOViewKeeper

k Keeper
cdc codec.BinaryCodec
storeKey storetypes.StoreKey
}

func NewBaseUTXOKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey) *BaseUTXOKeeper {
return &BaseUTXOKeeper{
BaseUTXOViewKeeper: *NewBaseUTXOViewKeeper(cdc, storeKey),
cdc: cdc,
storeKey: storeKey,
}
}

func (bk *BaseUTXOKeeper) SetUTXO(ctx sdk.Context, utxo *types.UTXO) {
store := ctx.KVStore(bk.storeKey)

bz := bk.cdc.MustMarshal(utxo)
store.Set(types.BtcUtxoKey(utxo.Txid, utxo.Vout), bz)
}

func (bk *BaseUTXOKeeper) SetOwnerUTXO(ctx sdk.Context, utxo *types.UTXO) {
store := ctx.KVStore(bk.storeKey)

store.Set(types.BtcOwnerUtxoKey(utxo.Address, utxo.Txid, utxo.Vout), nil)
}

func NewBaseUTXOKeeper(k Keeper) *BaseUTXOKeeper {
return &BaseUTXOKeeper{BaseUTXOViewKeeper: *NewBaseUTXOViewKeeper(k), k: k}
func (bk *BaseUTXOKeeper) LockUTXO(ctx sdk.Context, hash string, vout uint64) error {
if !bk.HasUTXO(ctx, hash, vout) {
return types.ErrUTXODoesNotExist
}

utxo := bk.GetUTXO(ctx, hash, vout)
if utxo.IsLocked {
return types.ErrUTXOLocked
}

utxo.IsLocked = true
bk.SetUTXO(ctx, utxo)

return nil
}

func (bk *BaseUTXOKeeper) LockUTXO(ctx sdk.Context, hash string, vout uint64) {
// TODO
func (bk *BaseUTXOKeeper) LockUTXOs(ctx sdk.Context, utxos []*types.UTXO) error {
for _, utxo := range utxos {
if err := bk.LockUTXO(ctx, utxo.Txid, utxo.Vout); err != nil {
return err
}
}

return nil
}

func (bk *BaseUTXOKeeper) SpendUTXO(ctx sdk.Context, hash string, vout uint64) {
// TODO
func (bk *BaseUTXOKeeper) UnlockUTXO(ctx sdk.Context, hash string, vout uint64) error {
if !bk.HasUTXO(ctx, hash, vout) {
return types.ErrUTXODoesNotExist
}

utxo := bk.GetUTXO(ctx, hash, vout)
if !utxo.IsLocked {
return types.ErrUTXOUnlocked
}

utxo.IsLocked = false
bk.SetUTXO(ctx, utxo)

return nil
}

func (bk *BaseUTXOKeeper) UnlockUTXOs(ctx sdk.Context, utxos []*types.UTXO) error {
for _, utxo := range utxos {
if err := bk.UnlockUTXO(ctx, utxo.Txid, utxo.Vout); err != nil {
return err
}
}

return nil
}

func (bk *BaseUTXOKeeper) SpendUTXO(ctx sdk.Context, hash string, vout uint64) error {
if !bk.HasUTXO(ctx, hash, vout) {
return types.ErrUTXODoesNotExist
}

bk.removeUTXO(ctx, hash, vout)

return nil
}

func (bk *BaseUTXOKeeper) SpendUTXOs(ctx sdk.Context, utxos []*types.UTXO) error {
for _, utxo := range utxos {
if err := bk.SpendUTXO(ctx, utxo.Txid, utxo.Vout); err != nil {
return err
}
}

return nil
}

func (bk *BaseUTXOKeeper) removeUTXO(ctx sdk.Context, hash string, vout uint64) {
store := ctx.KVStore(bk.storeKey)

store.Delete(types.BtcUtxoKey(hash, vout))
}
4 changes: 4 additions & 0 deletions x/btclightclient/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ var (

ErrInvalidSignatures = errorsmod.Register(ModuleName, 4200, "invalid signatures")
ErrInsufficientBalance = errorsmod.Register(ModuleName, 4201, "insufficient balance")

ErrUTXODoesNotExist = errorsmod.Register(ModuleName, 5100, "utxo does not exist")
ErrUTXOLocked = errorsmod.Register(ModuleName, 5101, "utxo locked")
ErrUTXOUnlocked = errorsmod.Register(ModuleName, 5102, "utxo unlocked")
)
5 changes: 0 additions & 5 deletions x/btclightclient/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ func KeyPrefix(p string) []byte {
}

var (
// key separator
// only used when the separated keys do not contain the separator
KeySeparator = []byte{0x0}

ParamsStoreKey = []byte{0x1}
SequenceKey = []byte{0x2}

Expand Down Expand Up @@ -53,7 +49,6 @@ func BtcUtxoKey(hash string, vout uint64) []byte {

func BtcOwnerUtxoKey(owner string, hash string, vout uint64) []byte {
key := append(BtcOwnerUtxoKeyPrefix, []byte(owner)...)
key = append(key, KeySeparator...)
key = append(key, []byte(hash)...)

return append(key, Int64ToBytes(vout)...)
Expand Down

0 comments on commit c7ee688

Please sign in to comment.