mirror of https://github.com/lightninglabs/loop
loopdb: static address store
parent
bb498f42d0
commit
c5756001ec
@ -0,0 +1,58 @@
|
|||||||
|
package staticaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrAddressAlreadyExists = fmt.Errorf("address already exists")
|
||||||
|
ErrAddressNotFound = fmt.Errorf("address not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddressStore is the database interface that is used to store and retrieve
|
||||||
|
// static addresses.
|
||||||
|
type AddressStore interface {
|
||||||
|
// CreateStaticAddress inserts a new static address with its parameters
|
||||||
|
// into the store.
|
||||||
|
CreateStaticAddress(ctx context.Context,
|
||||||
|
addrParams *AddressParameters) error
|
||||||
|
|
||||||
|
// GetStaticAddress fetches static address parameters for a given
|
||||||
|
// address ID.
|
||||||
|
GetStaticAddress(ctx context.Context,
|
||||||
|
pkScript []byte) (*AddressParameters, error)
|
||||||
|
|
||||||
|
// GetAllStaticAddresses retrieves all static addresses from the store.
|
||||||
|
GetAllStaticAddresses(ctx context.Context) (
|
||||||
|
[]*AddressParameters, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressParameters holds all the necessary information for the 2-of-2 multisig
|
||||||
|
// address.
|
||||||
|
type AddressParameters struct {
|
||||||
|
// ClientPubkey is the client's pubkey for the static address. It is
|
||||||
|
// used for the 2-of-2 funding output as well as for the client's
|
||||||
|
// timeout path.
|
||||||
|
ClientPubkey *btcec.PublicKey
|
||||||
|
|
||||||
|
// ClientPubkey is the client's pubkey for the static address. It is
|
||||||
|
// used for the 2-of-2 funding output.
|
||||||
|
ServerPubkey *btcec.PublicKey
|
||||||
|
|
||||||
|
// Expiry is the CSV timout value at which the client can claim the
|
||||||
|
// static address's timout path.
|
||||||
|
Expiry uint32
|
||||||
|
|
||||||
|
// PkScript is the unique static address's output script.
|
||||||
|
PkScript []byte
|
||||||
|
|
||||||
|
// KeyLocator is the locator of the client's key.
|
||||||
|
KeyLocator keychain.KeyLocator
|
||||||
|
|
||||||
|
// ProtocolVersion is the protocol version of the static address.
|
||||||
|
ProtocolVersion AddressProtocolVersion
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
package staticaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
|
"github.com/jackc/pgx/v4"
|
||||||
|
"github.com/lightninglabs/loop/loopdb"
|
||||||
|
"github.com/lightninglabs/loop/loopdb/sqlc"
|
||||||
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SqlStore is the backing store for static addresses.
|
||||||
|
type SqlStore struct {
|
||||||
|
baseDB *loopdb.BaseDB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSqlStore constructs a new SQLStore from a BaseDB. The BaseDB is agnostic
|
||||||
|
// to the underlying driver which can be postgres or sqlite.
|
||||||
|
func NewSqlStore(db *loopdb.BaseDB) *SqlStore {
|
||||||
|
return &SqlStore{
|
||||||
|
baseDB: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecTx is a wrapper for txBody to abstract the creation and commit of a db
|
||||||
|
// transaction. The db transaction is embedded in a `*sqlc.Queries` that txBody
|
||||||
|
// needs to use when executing each one of the queries that need to be applied
|
||||||
|
// atomically.
|
||||||
|
func (s *SqlStore) ExecTx(ctx context.Context, txOptions loopdb.TxOptions,
|
||||||
|
txBody func(queries *sqlc.Queries) error) error {
|
||||||
|
|
||||||
|
// Create the db transaction.
|
||||||
|
tx, err := s.baseDB.BeginTx(ctx, txOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rollback is safe to call even if the tx is already closed, so if the
|
||||||
|
// tx commits successfully, this is a no-op.
|
||||||
|
defer func() {
|
||||||
|
err := tx.Rollback()
|
||||||
|
switch {
|
||||||
|
// If the tx was already closed (it was successfully executed)
|
||||||
|
// we do not need to log that error.
|
||||||
|
case errors.Is(err, pgx.ErrTxClosed):
|
||||||
|
return
|
||||||
|
|
||||||
|
// If this is an unexpected error, log it.
|
||||||
|
case err != nil:
|
||||||
|
log.Errorf("unable to rollback db tx: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := txBody(s.baseDB.Queries.WithTx(tx)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit transaction.
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateStaticAddress creates a static address record in the database.
|
||||||
|
func (s *SqlStore) CreateStaticAddress(ctx context.Context,
|
||||||
|
addrParams *AddressParameters) error {
|
||||||
|
|
||||||
|
createArgs := sqlc.CreateStaticAddressParams{
|
||||||
|
ClientPubkey: addrParams.ClientPubkey.SerializeCompressed(),
|
||||||
|
ServerPubkey: addrParams.ServerPubkey.SerializeCompressed(),
|
||||||
|
Expiry: int32(addrParams.Expiry),
|
||||||
|
ClientKeyFamily: int32(addrParams.KeyLocator.Family),
|
||||||
|
ClientKeyIndex: int32(addrParams.KeyLocator.Index),
|
||||||
|
Pkscript: addrParams.PkScript,
|
||||||
|
ProtocolVersion: int32(addrParams.ProtocolVersion),
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.baseDB.Queries.CreateStaticAddress(ctx, createArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStaticAddress retrieves static address parameters for a given pkScript.
|
||||||
|
func (s *SqlStore) GetStaticAddress(ctx context.Context,
|
||||||
|
pkScript []byte) (*AddressParameters, error) {
|
||||||
|
|
||||||
|
staticAddress, err := s.baseDB.Queries.GetStaticAddress(ctx, pkScript)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.toAddressParameters(staticAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllStaticAddresses returns all address known to the server.
|
||||||
|
func (s *SqlStore) GetAllStaticAddresses(ctx context.Context) (
|
||||||
|
[]*AddressParameters, error) {
|
||||||
|
|
||||||
|
staticAddresses, err := s.baseDB.Queries.AllStaticAddresses(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []*AddressParameters
|
||||||
|
for _, address := range staticAddresses {
|
||||||
|
res, err := s.toAddressParameters(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the database connection.
|
||||||
|
func (s *SqlStore) Close() {
|
||||||
|
s.baseDB.DB.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// toAddressParameters transforms a database representation of a static address
|
||||||
|
// to an AddressParameters struct.
|
||||||
|
func (s *SqlStore) toAddressParameters(row sqlc.StaticAddress) (
|
||||||
|
*AddressParameters, error) {
|
||||||
|
|
||||||
|
clientPubkey, err := btcec.ParsePubKey(row.ClientPubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serverPubkey, err := btcec.ParsePubKey(row.ServerPubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AddressParameters{
|
||||||
|
ClientPubkey: clientPubkey,
|
||||||
|
ServerPubkey: serverPubkey,
|
||||||
|
PkScript: row.Pkscript,
|
||||||
|
Expiry: uint32(row.Expiry),
|
||||||
|
KeyLocator: keychain.KeyLocator{
|
||||||
|
Family: keychain.KeyFamily(row.ClientKeyFamily),
|
||||||
|
Index: uint32(row.ClientKeyIndex),
|
||||||
|
},
|
||||||
|
ProtocolVersion: AddressProtocolVersion(row.ProtocolVersion),
|
||||||
|
}, nil
|
||||||
|
}
|
Loading…
Reference in New Issue