Transfer struct tracks owned outputs with key image, amount, block height, and spend status. Storage helpers use go-store JSON serialisation keyed by key image hex. IsSpendable checks coinbase maturity and unlock time. Co-Authored-By: Charon <charon@lethean.io>
114 lines
3.5 KiB
Go
114 lines
3.5 KiB
Go
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
|
//
|
|
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
|
// You may obtain a copy of the licence at:
|
|
//
|
|
// https://joinup.ec.europa.eu/software/page/eupl/licence-eupl
|
|
//
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
package wallet
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
store "forge.lthn.ai/core/go-store"
|
|
|
|
"forge.lthn.ai/core/go-blockchain/config"
|
|
"forge.lthn.ai/core/go-blockchain/types"
|
|
)
|
|
|
|
// groupTransfers is the go-store group name for wallet transfer records.
|
|
const groupTransfers = "transfers"
|
|
|
|
// KeyPair holds an ephemeral public/secret key pair for an owned output.
|
|
type KeyPair struct {
|
|
Public types.PublicKey `json:"public"`
|
|
Secret types.SecretKey `json:"secret"`
|
|
}
|
|
|
|
// Transfer represents an owned transaction output tracked by the wallet.
|
|
// Each transfer is keyed by its unique key image for double-spend detection.
|
|
type Transfer struct {
|
|
TxHash types.Hash `json:"tx_hash"`
|
|
OutputIndex uint32 `json:"output_index"`
|
|
Amount uint64 `json:"amount"`
|
|
GlobalIndex uint64 `json:"global_index"`
|
|
BlockHeight uint64 `json:"block_height"`
|
|
EphemeralKey KeyPair `json:"ephemeral_key"`
|
|
KeyImage types.KeyImage `json:"key_image"`
|
|
Spent bool `json:"spent"`
|
|
SpentHeight uint64 `json:"spent_height"`
|
|
Coinbase bool `json:"coinbase"`
|
|
UnlockTime uint64 `json:"unlock_time"`
|
|
}
|
|
|
|
// IsSpendable reports whether the transfer can be used as an input at the
|
|
// given chain height. A transfer is not spendable if it has already been
|
|
// spent, if it is a coinbase output that has not yet matured, or if its
|
|
// unlock time has not been reached.
|
|
func (t *Transfer) IsSpendable(chainHeight uint64, _ bool) bool {
|
|
if t.Spent {
|
|
return false
|
|
}
|
|
if t.Coinbase && t.BlockHeight+config.MinedMoneyUnlockWindow > chainHeight {
|
|
return false
|
|
}
|
|
if t.UnlockTime > 0 && t.UnlockTime > chainHeight {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// putTransfer serialises a transfer as JSON and stores it in the given store,
|
|
// keyed by the transfer's key image hex string.
|
|
func putTransfer(s *store.Store, tr *Transfer) error {
|
|
val, err := json.Marshal(tr)
|
|
if err != nil {
|
|
return fmt.Errorf("wallet: marshal transfer: %w", err)
|
|
}
|
|
return s.Set(groupTransfers, tr.KeyImage.String(), string(val))
|
|
}
|
|
|
|
// getTransfer retrieves and deserialises a transfer by its key image.
|
|
func getTransfer(s *store.Store, ki types.KeyImage) (*Transfer, error) {
|
|
val, err := s.Get(groupTransfers, ki.String())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("wallet: get transfer %s: %w", ki, err)
|
|
}
|
|
var tr Transfer
|
|
if err := json.Unmarshal([]byte(val), &tr); err != nil {
|
|
return nil, fmt.Errorf("wallet: unmarshal transfer: %w", err)
|
|
}
|
|
return &tr, nil
|
|
}
|
|
|
|
// markTransferSpent sets the spent flag and records the height at which the
|
|
// transfer was consumed.
|
|
func markTransferSpent(s *store.Store, ki types.KeyImage, height uint64) error {
|
|
tr, err := getTransfer(s, ki)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tr.Spent = true
|
|
tr.SpentHeight = height
|
|
return putTransfer(s, tr)
|
|
}
|
|
|
|
// listTransfers returns all transfers stored in the given store.
|
|
func listTransfers(s *store.Store) ([]Transfer, error) {
|
|
pairs, err := s.GetAll(groupTransfers)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("wallet: list transfers: %w", err)
|
|
}
|
|
transfers := make([]Transfer, 0, len(pairs))
|
|
for _, val := range pairs {
|
|
var tr Transfer
|
|
if err := json.Unmarshal([]byte(val), &tr); err != nil {
|
|
continue
|
|
}
|
|
transfers = append(transfers, tr)
|
|
}
|
|
return transfers, nil
|
|
}
|