You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
loop/instantout/reservation/reservation.go

138 lines
3.1 KiB
Go

package reservation
import (
"bytes"
"errors"
"fmt"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/fsm"
reservation_script "github.com/lightninglabs/loop/instantout/reservation/script"
"github.com/lightningnetwork/lnd/keychain"
)
// ID is a unique identifier for a reservation.
type ID [IdLength]byte
// FromByteSlice creates a reservation id from a byte slice.
func (r *ID) FromByteSlice(b []byte) error {
if len(b) != IdLength {
return fmt.Errorf("reservation id must be 32 bytes, got %d, %x",
len(b), b)
}
copy(r[:], b)
return nil
}
// Reservation holds all the necessary information for the 2-of-2 multisig
// reservation utxo.
type Reservation struct {
// ID is the unique identifier of the reservation.
ID ID
// State is the current state of the reservation.
State fsm.StateType
// ClientPubkey is the client's pubkey.
ClientPubkey *btcec.PublicKey
// ServerPubkey is the server's pubkey.
ServerPubkey *btcec.PublicKey
// Value is the amount of the reservation.
Value btcutil.Amount
// Expiry is the absolute block height at which the reservation expires.
Expiry uint32
// KeyLocator is the key locator of the client's key.
KeyLocator keychain.KeyLocator
// Outpoint is the outpoint of the reservation.
Outpoint *wire.OutPoint
// InitiationHeight is the height at which the reservation was
// initiated.
InitiationHeight int32
// ConfirmationHeight is the height at which the reservation was
// confirmed.
ConfirmationHeight uint32
}
func NewReservation(id ID, serverPubkey, clientPubkey *btcec.PublicKey,
value btcutil.Amount, expiry, heightHint uint32,
keyLocator keychain.KeyLocator) (*Reservation,
error) {
if id == [32]byte{} {
return nil, errors.New("id is empty")
}
if clientPubkey == nil {
return nil, errors.New("client pubkey is nil")
}
if serverPubkey == nil {
return nil, errors.New("server pubkey is nil")
}
if expiry == 0 {
return nil, errors.New("expiry is 0")
}
if value == 0 {
return nil, errors.New("value is 0")
}
if keyLocator.Family == 0 {
return nil, errors.New("key locator family is 0")
}
return &Reservation{
ID: id,
Value: value,
ClientPubkey: clientPubkey,
ServerPubkey: serverPubkey,
KeyLocator: keyLocator,
Expiry: expiry,
InitiationHeight: int32(heightHint),
}, nil
}
// GetPkScript returns the pk script of the reservation.
func (r *Reservation) GetPkScript() ([]byte, error) {
// Now that we have all the required data, we can create the pk script.
pkScript, err := reservation_script.ReservationScript(
r.Expiry, r.ServerPubkey, r.ClientPubkey,
)
if err != nil {
return nil, err
}
return pkScript, nil
}
func (r *Reservation) findReservationOutput(tx *wire.MsgTx) (*wire.OutPoint,
error) {
pkScript, err := r.GetPkScript()
if err != nil {
return nil, err
}
for i, txOut := range tx.TxOut {
if bytes.Equal(txOut.PkScript, pkScript) {
return &wire.OutPoint{
Hash: tx.TxHash(),
Index: uint32(i),
}, nil
}
}
return nil, errors.New("reservation output not found")
}