mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-19 05:04:21 +01:00
New address encoding (#356)
* [common] rework address encoding * new address encoding * replace offchain address by vtxo output key in DB * merge migrations files into init one * fix txbuilder fixtures * fix transaction events
This commit is contained in:
@@ -156,6 +156,7 @@ func (s *covenantService) GetBoardingAddress(ctx context.Context, userPubkey *se
|
||||
func (s *covenantService) SpendVtxos(ctx context.Context, inputs []ports.Input) (string, error) {
|
||||
vtxosInputs := make([]domain.Vtxo, 0)
|
||||
boardingInputs := make([]ports.BoardingInput, 0)
|
||||
descriptors := make(map[domain.VtxoKey]string)
|
||||
|
||||
now := time.Now().Unix()
|
||||
|
||||
@@ -218,13 +219,14 @@ func (s *covenantService) SpendVtxos(ctx context.Context, inputs []ports.Input)
|
||||
}
|
||||
|
||||
vtxosInputs = append(vtxosInputs, vtxo)
|
||||
descriptors[vtxo.VtxoKey] = input.Descriptor
|
||||
}
|
||||
|
||||
payment, err := domain.NewPayment(vtxosInputs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := s.paymentRequests.push(*payment, boardingInputs); err != nil {
|
||||
if err := s.paymentRequests.push(*payment, boardingInputs, descriptors); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return payment.Id, nil
|
||||
@@ -324,7 +326,7 @@ func (s *covenantService) CompleteAsyncPayment(ctx context.Context, redeemTx str
|
||||
return fmt.Errorf("unimplemented")
|
||||
}
|
||||
|
||||
func (s *covenantService) CreateAsyncPayment(ctx context.Context, inputs []ports.Input, receivers []domain.Receiver) (string, error) {
|
||||
func (s *covenantService) CreateAsyncPayment(_ context.Context, _ []AsyncPaymentInput, _ []domain.Receiver) (string, error) {
|
||||
return "", fmt.Errorf("unimplemented")
|
||||
}
|
||||
|
||||
@@ -345,9 +347,14 @@ func (s *covenantService) SignRoundTx(ctx context.Context, signedRoundTx string)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *covenantService) ListVtxos(ctx context.Context, pubkey *secp256k1.PublicKey) ([]domain.Vtxo, []domain.Vtxo, error) {
|
||||
pk := hex.EncodeToString(pubkey.SerializeCompressed())
|
||||
return s.repoManager.Vtxos().GetAllVtxos(ctx, pk)
|
||||
func (s *covenantService) ListVtxos(ctx context.Context, address string) ([]domain.Vtxo, []domain.Vtxo, error) {
|
||||
decodedAddress, err := common.DecodeAddress(address)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode address: %s", err)
|
||||
}
|
||||
pubkey := hex.EncodeToString(schnorr.SerializePubKey(decodedAddress.VtxoTapKey))
|
||||
|
||||
return s.repoManager.Vtxos().GetAllVtxos(ctx, pubkey)
|
||||
}
|
||||
|
||||
func (s *covenantService) GetEventsChannel(ctx context.Context) <-chan domain.RoundEvent {
|
||||
@@ -482,7 +489,7 @@ func (s *covenantService) startFinalization() {
|
||||
if num > paymentsThreshold {
|
||||
num = paymentsThreshold
|
||||
}
|
||||
payments, boardingInputs, _ := s.paymentRequests.pop(num)
|
||||
payments, boardingInputs, descriptors, _ := s.paymentRequests.pop(num)
|
||||
if _, err := round.RegisterPayments(payments); err != nil {
|
||||
round.Fail(fmt.Errorf("failed to register payments: %s", err))
|
||||
log.WithError(err).Warn("failed to register payments")
|
||||
@@ -517,7 +524,7 @@ func (s *covenantService) startFinalization() {
|
||||
minRelayFeeRate := s.wallet.MinRelayFeeRate(ctx)
|
||||
|
||||
if needForfeits {
|
||||
connectors, forfeitTxs, err = s.builder.BuildForfeitTxs(unsignedPoolTx, payments, minRelayFeeRate)
|
||||
connectors, forfeitTxs, err = s.builder.BuildForfeitTxs(unsignedPoolTx, payments, descriptors, minRelayFeeRate)
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to create connectors and forfeit txs: %s", err))
|
||||
log.WithError(err).Warn("failed to create connectors and forfeit txs")
|
||||
@@ -933,53 +940,20 @@ func (s *covenantService) getNewVtxos(round *domain.Round) []domain.Vtxo {
|
||||
continue // skip fee outputs
|
||||
}
|
||||
|
||||
desc := ""
|
||||
found := false
|
||||
|
||||
for _, p := range round.Payments {
|
||||
if found {
|
||||
break
|
||||
}
|
||||
|
||||
for _, r := range p.Receivers {
|
||||
if r.IsOnchain() {
|
||||
continue
|
||||
}
|
||||
|
||||
vtxoScript, err := tree.ParseVtxoScript(r.Descriptor)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to parse vtxo descriptor")
|
||||
continue
|
||||
}
|
||||
|
||||
tapKey, _, err := vtxoScript.TapTree()
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to compute vtxo tap key")
|
||||
continue
|
||||
}
|
||||
|
||||
script, err := common.P2TRScript(tapKey)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to create vtxo scriptpubkey")
|
||||
continue
|
||||
}
|
||||
|
||||
if bytes.Equal(script, out.Script) {
|
||||
found = true
|
||||
desc = r.Descriptor
|
||||
break
|
||||
}
|
||||
}
|
||||
vtxoTapKey, err := schnorr.ParsePubKey(out.Script[2:])
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to parse vtxo tap key")
|
||||
continue
|
||||
}
|
||||
|
||||
if found {
|
||||
vtxos = append(vtxos, domain.Vtxo{
|
||||
VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)},
|
||||
Receiver: domain.Receiver{Descriptor: desc, Amount: uint64(out.Value)},
|
||||
RoundTxid: round.Txid,
|
||||
})
|
||||
break
|
||||
}
|
||||
vtxoPubkey := hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey))
|
||||
|
||||
vtxos = append(vtxos, domain.Vtxo{
|
||||
VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)},
|
||||
Pubkey: vtxoPubkey,
|
||||
Amount: uint64(out.Value),
|
||||
RoundTxid: round.Txid,
|
||||
})
|
||||
}
|
||||
}
|
||||
return vtxos
|
||||
@@ -1050,17 +1024,17 @@ func (s *covenantService) restoreWatchingVtxos() error {
|
||||
func (s *covenantService) extractVtxosScripts(vtxos []domain.Vtxo) ([]string, error) {
|
||||
indexedScripts := make(map[string]struct{})
|
||||
for _, vtxo := range vtxos {
|
||||
vtxoScript, err := tree.ParseVtxoScript(vtxo.Receiver.Descriptor)
|
||||
vtxoTapKeyBytes, err := hex.DecodeString(vtxo.Pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tapKey, _, err := vtxoScript.TapTree()
|
||||
vtxoTapKey, err := schnorr.ParsePubKey(vtxoTapKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
script, err := common.P2TRScript(tapKey)
|
||||
script, err := common.P2TRScript(vtxoTapKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/btcutil/psbt"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
@@ -177,37 +178,28 @@ func (s *covenantlessService) CompleteAsyncPayment(
|
||||
|
||||
// verify that the vtxo is spendable
|
||||
|
||||
vtxo, err := vtxoRepo.GetVtxos(ctx, []domain.VtxoKey{{Txid: vtxoOutpoint.Hash.String(), VOut: vtxoOutpoint.Index}})
|
||||
vtxos, err := vtxoRepo.GetVtxos(ctx, []domain.VtxoKey{{Txid: vtxoOutpoint.Hash.String(), VOut: vtxoOutpoint.Index}})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get vtxo: %s", err)
|
||||
}
|
||||
|
||||
if len(vtxo) == 0 {
|
||||
if len(vtxos) == 0 {
|
||||
return fmt.Errorf("vtxo not found")
|
||||
}
|
||||
|
||||
if vtxo[0].Spent {
|
||||
vtxo := vtxos[0]
|
||||
if vtxo.Spent {
|
||||
return fmt.Errorf("vtxo already spent")
|
||||
}
|
||||
|
||||
if vtxo[0].Redeemed {
|
||||
if vtxo.Redeemed {
|
||||
return fmt.Errorf("vtxo already redeemed")
|
||||
}
|
||||
|
||||
if vtxo[0].Swept {
|
||||
if vtxo.Swept {
|
||||
return fmt.Errorf("vtxo already swept")
|
||||
}
|
||||
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(vtxo[0].Descriptor)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse vtxo script: %s", err)
|
||||
}
|
||||
|
||||
vtxoTapKey, _, err := vtxoScript.TapTree()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get taproot key: %s", err)
|
||||
}
|
||||
|
||||
// verify that the user signs a forfeit closure
|
||||
var userPubKey *secp256k1.PublicKey
|
||||
|
||||
@@ -228,6 +220,16 @@ func (s *covenantlessService) CompleteAsyncPayment(
|
||||
return fmt.Errorf("redeem transaction is not signed")
|
||||
}
|
||||
|
||||
vtxoPublicKeyBytes, err := hex.DecodeString(vtxo.Pubkey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode vtxo pubkey: %s", err)
|
||||
}
|
||||
|
||||
vtxoTapKey, err := schnorr.ParsePubKey(vtxoPublicKeyBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse vtxo pubkey: %s", err)
|
||||
}
|
||||
|
||||
// verify witness utxo
|
||||
pkscript, err := common.P2TRScript(vtxoTapKey)
|
||||
if err != nil {
|
||||
@@ -238,7 +240,7 @@ func (s *covenantlessService) CompleteAsyncPayment(
|
||||
return fmt.Errorf("witness utxo script mismatch")
|
||||
}
|
||||
|
||||
if input.WitnessUtxo.Value != int64(vtxo[0].Amount) {
|
||||
if input.WitnessUtxo.Value != int64(vtxo.Amount) {
|
||||
return fmt.Errorf("witness utxo value mismatch")
|
||||
}
|
||||
}
|
||||
@@ -260,19 +262,23 @@ func (s *covenantlessService) CompleteAsyncPayment(
|
||||
vtxos := make([]domain.Vtxo, 0, len(asyncPayData.receivers))
|
||||
|
||||
for outIndex, out := range redeemPtx.UnsignedTx.TxOut {
|
||||
desc := asyncPayData.receivers[outIndex].Descriptor
|
||||
_, _, _, _, err := descriptor.ParseReversibleVtxoDescriptor(desc)
|
||||
isPending := err == nil
|
||||
vtxoTapKey, err := schnorr.ParsePubKey(out.PkScript[2:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse vtxo taproot key: %s", err)
|
||||
}
|
||||
|
||||
vtxoPubkey := hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey))
|
||||
|
||||
// all pending except the last one
|
||||
isPending := outIndex < len(asyncPayData.receivers)-1
|
||||
|
||||
vtxos = append(vtxos, domain.Vtxo{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: redeemTxid,
|
||||
VOut: uint32(outIndex),
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: desc,
|
||||
Amount: uint64(out.Value),
|
||||
},
|
||||
Pubkey: vtxoPubkey,
|
||||
Amount: uint64(out.Value),
|
||||
ExpireAt: asyncPayData.expireAt,
|
||||
RedeemTx: redeemTx,
|
||||
Pending: isPending,
|
||||
@@ -309,11 +315,16 @@ func (s *covenantlessService) CompleteAsyncPayment(
|
||||
}
|
||||
|
||||
func (s *covenantlessService) CreateAsyncPayment(
|
||||
ctx context.Context, inputs []ports.Input, receivers []domain.Receiver,
|
||||
ctx context.Context, inputs []AsyncPaymentInput, receivers []domain.Receiver,
|
||||
) (string, error) {
|
||||
vtxosKeys := make([]domain.VtxoKey, 0, len(inputs))
|
||||
descriptors := make(map[domain.VtxoKey]string)
|
||||
forfeitLeaves := make(map[domain.VtxoKey]chainhash.Hash)
|
||||
|
||||
for _, in := range inputs {
|
||||
vtxosKeys = append(vtxosKeys, in.VtxoKey)
|
||||
descriptors[in.VtxoKey] = in.Descriptor
|
||||
forfeitLeaves[in.VtxoKey] = in.ForfeitLeafHash
|
||||
}
|
||||
|
||||
vtxos, err := s.repoManager.Vtxos().GetVtxos(ctx, vtxosKeys)
|
||||
@@ -351,7 +362,7 @@ func (s *covenantlessService) CreateAsyncPayment(
|
||||
}
|
||||
|
||||
redeemTx, err := s.builder.BuildAsyncPaymentTransactions(
|
||||
vtxosInputs, s.pubkey, receivers,
|
||||
vtxosInputs, descriptors, forfeitLeaves, receivers,
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to build async payment txs: %s", err)
|
||||
@@ -404,7 +415,7 @@ func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Inp
|
||||
now := time.Now().Unix()
|
||||
|
||||
boardingTxs := make(map[string]wire.MsgTx, 0) // txid -> txhex
|
||||
|
||||
descriptors := make(map[domain.VtxoKey]string)
|
||||
for _, input := range inputs {
|
||||
vtxosResult, err := s.repoManager.Vtxos().GetVtxos(ctx, []domain.VtxoKey{input.VtxoKey})
|
||||
if err != nil || len(vtxosResult) == 0 {
|
||||
@@ -461,6 +472,8 @@ func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Inp
|
||||
return "", fmt.Errorf("input %s:%d already swept", vtxo.Txid, vtxo.VOut)
|
||||
}
|
||||
|
||||
descriptors[vtxo.VtxoKey] = input.Descriptor
|
||||
|
||||
vtxosInputs = append(vtxosInputs, vtxo)
|
||||
}
|
||||
|
||||
@@ -468,7 +481,7 @@ func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Inp
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := s.paymentRequests.push(*payment, boardingInputs); err != nil {
|
||||
if err := s.paymentRequests.push(*payment, boardingInputs, descriptors); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return payment.Id, nil
|
||||
@@ -572,9 +585,14 @@ func (s *covenantlessService) SignRoundTx(ctx context.Context, signedRoundTx str
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *covenantlessService) ListVtxos(ctx context.Context, pubkey *secp256k1.PublicKey) ([]domain.Vtxo, []domain.Vtxo, error) {
|
||||
pk := hex.EncodeToString(pubkey.SerializeCompressed())
|
||||
return s.repoManager.Vtxos().GetAllVtxos(ctx, pk)
|
||||
func (s *covenantlessService) ListVtxos(ctx context.Context, address string) ([]domain.Vtxo, []domain.Vtxo, error) {
|
||||
decodedAddress, err := common.DecodeAddress(address)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode address: %s", err)
|
||||
}
|
||||
pubkey := hex.EncodeToString(schnorr.SerializePubKey(decodedAddress.VtxoTapKey))
|
||||
|
||||
return s.repoManager.Vtxos().GetAllVtxos(ctx, pubkey)
|
||||
}
|
||||
|
||||
func (s *covenantlessService) GetEventsChannel(ctx context.Context) <-chan domain.RoundEvent {
|
||||
@@ -771,7 +789,7 @@ func (s *covenantlessService) startFinalization() {
|
||||
if num > paymentsThreshold {
|
||||
num = paymentsThreshold
|
||||
}
|
||||
payments, boardingInputs, cosigners := s.paymentRequests.pop(num)
|
||||
payments, boardingInputs, descriptors, cosigners := s.paymentRequests.pop(num)
|
||||
if len(payments) > len(cosigners) {
|
||||
err := fmt.Errorf("missing ephemeral key for payments")
|
||||
round.Fail(fmt.Errorf("round aborted: %s", err))
|
||||
@@ -973,7 +991,7 @@ func (s *covenantlessService) startFinalization() {
|
||||
minRelayFeeRate := s.wallet.MinRelayFeeRate(ctx)
|
||||
|
||||
if needForfeits {
|
||||
connectors, forfeitTxs, err = s.builder.BuildForfeitTxs(unsignedRoundTx, payments, minRelayFeeRate)
|
||||
connectors, forfeitTxs, err = s.builder.BuildForfeitTxs(unsignedRoundTx, payments, descriptors, minRelayFeeRate)
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to create connectors and forfeit txs: %s", err))
|
||||
log.WithError(err).Warn("failed to create connectors and forfeit txs")
|
||||
@@ -1332,53 +1350,18 @@ func (s *covenantlessService) getNewVtxos(round *domain.Round) []domain.Vtxo {
|
||||
continue
|
||||
}
|
||||
for i, out := range tx.UnsignedTx.TxOut {
|
||||
desc := ""
|
||||
found := false
|
||||
|
||||
for _, p := range round.Payments {
|
||||
if found {
|
||||
break
|
||||
}
|
||||
|
||||
for _, r := range p.Receivers {
|
||||
if r.IsOnchain() {
|
||||
continue
|
||||
}
|
||||
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(r.Descriptor)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to parse vtxo descriptor")
|
||||
continue
|
||||
}
|
||||
|
||||
tapKey, _, err := vtxoScript.TapTree()
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to compute vtxo tap key")
|
||||
continue
|
||||
}
|
||||
|
||||
script, err := common.P2TRScript(tapKey)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to create vtxo scriptpubkey")
|
||||
continue
|
||||
}
|
||||
|
||||
if bytes.Equal(script, out.PkScript) {
|
||||
found = true
|
||||
desc = r.Descriptor
|
||||
break
|
||||
}
|
||||
}
|
||||
vtxoTapKey, err := schnorr.ParsePubKey(out.PkScript[2:])
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to parse vtxo tap key")
|
||||
continue
|
||||
}
|
||||
|
||||
if found {
|
||||
vtxos = append(vtxos, domain.Vtxo{
|
||||
VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)},
|
||||
Receiver: domain.Receiver{Descriptor: desc, Amount: uint64(out.Value)},
|
||||
RoundTxid: round.Txid,
|
||||
})
|
||||
break
|
||||
}
|
||||
vtxos = append(vtxos, domain.Vtxo{
|
||||
VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)},
|
||||
Pubkey: hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey)),
|
||||
Amount: uint64(out.Value),
|
||||
RoundTxid: round.Txid,
|
||||
})
|
||||
}
|
||||
}
|
||||
return vtxos
|
||||
@@ -1450,17 +1433,17 @@ func (s *covenantlessService) extractVtxosScripts(vtxos []domain.Vtxo) ([]string
|
||||
indexedScripts := make(map[string]struct{})
|
||||
|
||||
for _, vtxo := range vtxos {
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(vtxo.Receiver.Descriptor)
|
||||
vtxoTapKeyBytes, err := hex.DecodeString(vtxo.Pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tapKey, _, err := vtxoScript.TapTree()
|
||||
vtxoTapKey, err := schnorr.ParsePubKey(vtxoTapKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
script, err := common.P2TRScript(tapKey)
|
||||
script, err := common.P2TRScript(vtxoTapKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/ark-network/ark/server/internal/core/domain"
|
||||
"github.com/ark-network/ark/server/internal/core/ports"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
)
|
||||
|
||||
@@ -12,6 +13,11 @@ var (
|
||||
paymentsThreshold = int64(128)
|
||||
)
|
||||
|
||||
type AsyncPaymentInput struct {
|
||||
ports.Input
|
||||
ForfeitLeafHash chainhash.Hash
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
Start() error
|
||||
Stop()
|
||||
@@ -27,12 +33,12 @@ type Service interface {
|
||||
ctx context.Context, paymentId string,
|
||||
) (lastEvent domain.RoundEvent, err error)
|
||||
ListVtxos(
|
||||
ctx context.Context, pubkey *secp256k1.PublicKey,
|
||||
ctx context.Context, address string,
|
||||
) (spendableVtxos, spentVtxos []domain.Vtxo, err error)
|
||||
GetInfo(ctx context.Context) (*ServiceInfo, error)
|
||||
// Async payments
|
||||
CreateAsyncPayment(
|
||||
ctx context.Context, inputs []ports.Input, receivers []domain.Receiver,
|
||||
ctx context.Context, inputs []AsyncPaymentInput, receivers []domain.Receiver,
|
||||
) (string, error)
|
||||
CompleteAsyncPayment(
|
||||
ctx context.Context, redeemTx string,
|
||||
|
||||
@@ -24,13 +24,14 @@ type timedPayment struct {
|
||||
type paymentsMap struct {
|
||||
lock *sync.RWMutex
|
||||
payments map[string]*timedPayment
|
||||
descriptors map[domain.VtxoKey]string
|
||||
ephemeralKeys map[string]*secp256k1.PublicKey
|
||||
}
|
||||
|
||||
func newPaymentsMap() *paymentsMap {
|
||||
paymentsById := make(map[string]*timedPayment)
|
||||
lock := &sync.RWMutex{}
|
||||
return &paymentsMap{lock, paymentsById, make(map[string]*secp256k1.PublicKey)}
|
||||
return &paymentsMap{lock, paymentsById, make(map[domain.VtxoKey]string), make(map[string]*secp256k1.PublicKey)}
|
||||
}
|
||||
|
||||
func (m *paymentsMap) len() int64 {
|
||||
@@ -58,7 +59,11 @@ func (m *paymentsMap) delete(id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *paymentsMap) push(payment domain.Payment, boardingInputs []ports.BoardingInput) error {
|
||||
func (m *paymentsMap) push(
|
||||
payment domain.Payment,
|
||||
boardingInputs []ports.BoardingInput,
|
||||
descriptors map[domain.VtxoKey]string,
|
||||
) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
@@ -86,6 +91,10 @@ func (m *paymentsMap) push(payment domain.Payment, boardingInputs []ports.Boardi
|
||||
}
|
||||
}
|
||||
|
||||
for key, desc := range descriptors {
|
||||
m.descriptors[key] = desc
|
||||
}
|
||||
|
||||
m.payments[payment.Id] = &timedPayment{payment, boardingInputs, time.Now(), time.Time{}}
|
||||
return nil
|
||||
}
|
||||
@@ -102,7 +111,7 @@ func (m *paymentsMap) pushEphemeralKey(paymentId string, pubkey *secp256k1.Publi
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *paymentsMap) pop(num int64) ([]domain.Payment, []ports.BoardingInput, []*secp256k1.PublicKey) {
|
||||
func (m *paymentsMap) pop(num int64) ([]domain.Payment, []ports.BoardingInput, map[domain.VtxoKey]string, []*secp256k1.PublicKey) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
@@ -129,6 +138,7 @@ func (m *paymentsMap) pop(num int64) ([]domain.Payment, []ports.BoardingInput, [
|
||||
payments := make([]domain.Payment, 0, num)
|
||||
boardingInputs := make([]ports.BoardingInput, 0)
|
||||
cosigners := make([]*secp256k1.PublicKey, 0, num)
|
||||
descriptors := make(map[domain.VtxoKey]string)
|
||||
for _, p := range paymentsByTime[:num] {
|
||||
boardingInputs = append(boardingInputs, p.boardingInputs...)
|
||||
payments = append(payments, p.Payment)
|
||||
@@ -136,9 +146,15 @@ func (m *paymentsMap) pop(num int64) ([]domain.Payment, []ports.BoardingInput, [
|
||||
cosigners = append(cosigners, pubkey)
|
||||
delete(m.ephemeralKeys, p.Payment.Id)
|
||||
}
|
||||
for _, input := range payments {
|
||||
for _, vtxo := range input.Inputs {
|
||||
descriptors[vtxo.VtxoKey] = m.descriptors[vtxo.VtxoKey]
|
||||
delete(m.descriptors, vtxo.VtxoKey)
|
||||
}
|
||||
}
|
||||
delete(m.payments, p.Id)
|
||||
}
|
||||
return payments, boardingInputs, cosigners
|
||||
return payments, boardingInputs, descriptors, cosigners
|
||||
}
|
||||
|
||||
func (m *paymentsMap) update(payment domain.Payment) error {
|
||||
|
||||
@@ -68,9 +68,12 @@ func (p Payment) validate(ignoreOuts bool) error {
|
||||
return fmt.Errorf("missing outputs")
|
||||
}
|
||||
for _, r := range p.Receivers {
|
||||
if len(r.OnchainAddress) <= 0 && len(r.Descriptor) <= 0 {
|
||||
if len(r.OnchainAddress) <= 0 && len(r.Pubkey) <= 0 {
|
||||
return fmt.Errorf("missing receiver destination")
|
||||
}
|
||||
if r.Amount == 0 {
|
||||
return fmt.Errorf("missing receiver amount")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -80,6 +83,10 @@ type VtxoKey struct {
|
||||
VOut uint32
|
||||
}
|
||||
|
||||
func (k VtxoKey) String() string {
|
||||
return fmt.Sprintf("%s:%d", k.Txid, k.VOut)
|
||||
}
|
||||
|
||||
func (k VtxoKey) Hash() string {
|
||||
calcHash := func(buf []byte, hasher hash.Hash) []byte {
|
||||
_, _ = hasher.Write(buf)
|
||||
@@ -96,9 +103,9 @@ func (k VtxoKey) Hash() string {
|
||||
}
|
||||
|
||||
type Receiver struct {
|
||||
Descriptor string
|
||||
Amount uint64
|
||||
OnchainAddress string
|
||||
OnchainAddress string // onchain
|
||||
Pubkey string // offchain
|
||||
}
|
||||
|
||||
func (r Receiver) IsOnchain() bool {
|
||||
@@ -107,7 +114,8 @@ func (r Receiver) IsOnchain() bool {
|
||||
|
||||
type Vtxo struct {
|
||||
VtxoKey
|
||||
Receiver
|
||||
Amount uint64
|
||||
Pubkey string
|
||||
RoundTxid string
|
||||
SpentBy string // round txid or async redeem txid
|
||||
Spent bool
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
package domain_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ark-network/ark/common/descriptor"
|
||||
"github.com/ark-network/ark/server/internal/core/domain"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var desc = fmt.Sprintf(
|
||||
descriptor.DefaultVtxoDescriptorTemplate,
|
||||
"030000000000000000000000000000000000000000000000000000000000000001",
|
||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
512,
|
||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
)
|
||||
// x-only pubkey
|
||||
const pubkey = "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967"
|
||||
|
||||
var inputs = []domain.Vtxo{
|
||||
{
|
||||
@@ -24,10 +16,8 @@ var inputs = []domain.Vtxo{
|
||||
Txid: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: desc,
|
||||
Amount: 1000,
|
||||
},
|
||||
Pubkey: pubkey,
|
||||
Amount: 1000,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -51,12 +41,12 @@ func TestPayment(t *testing.T) {
|
||||
|
||||
err = payment.AddReceivers([]domain.Receiver{
|
||||
{
|
||||
Descriptor: desc,
|
||||
Amount: 450,
|
||||
Pubkey: pubkey,
|
||||
Amount: 450,
|
||||
},
|
||||
{
|
||||
Descriptor: desc,
|
||||
Amount: 550,
|
||||
Pubkey: pubkey,
|
||||
Amount: 550,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -20,24 +20,22 @@ var (
|
||||
Txid: txid,
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: desc,
|
||||
Amount: 2000,
|
||||
},
|
||||
Pubkey: pubkey,
|
||||
Amount: 2000,
|
||||
},
|
||||
},
|
||||
Receivers: []domain.Receiver{
|
||||
{
|
||||
Descriptor: desc,
|
||||
Amount: 700,
|
||||
Pubkey: pubkey,
|
||||
Amount: 700,
|
||||
},
|
||||
{
|
||||
Descriptor: desc,
|
||||
Amount: 700,
|
||||
Pubkey: pubkey,
|
||||
Amount: 700,
|
||||
},
|
||||
{
|
||||
Descriptor: desc,
|
||||
Amount: 600,
|
||||
Pubkey: pubkey,
|
||||
Amount: 600,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -49,25 +47,21 @@ var (
|
||||
Txid: txid,
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: desc,
|
||||
Amount: 1000,
|
||||
},
|
||||
Pubkey: pubkey,
|
||||
Amount: 1000,
|
||||
},
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: txid,
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: desc,
|
||||
Amount: 1000,
|
||||
},
|
||||
Pubkey: pubkey,
|
||||
Amount: 1000,
|
||||
},
|
||||
},
|
||||
Receivers: []domain.Receiver{{
|
||||
Descriptor: desc,
|
||||
Amount: 2000,
|
||||
Pubkey: pubkey,
|
||||
Amount: 2000,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -32,7 +32,12 @@ type TxBuilder interface {
|
||||
aspPubkey *secp256k1.PublicKey, payments []domain.Payment, boardingInputs []BoardingInput, sweptRounds []domain.Round,
|
||||
cosigners ...*secp256k1.PublicKey,
|
||||
) (roundTx string, congestionTree tree.CongestionTree, connectorAddress string, err error)
|
||||
BuildForfeitTxs(poolTx string, payments []domain.Payment, minRelayFeeRate chainfee.SatPerKVByte) (connectors []string, forfeitTxs []string, err error)
|
||||
BuildForfeitTxs(
|
||||
roundTx string,
|
||||
payments []domain.Payment,
|
||||
descriptors map[domain.VtxoKey]string,
|
||||
minRelayFeeRate chainfee.SatPerKVByte,
|
||||
) (connectors []string, forfeitTxs []string, err error)
|
||||
BuildSweepTx(inputs []SweepInput) (signedSweepTx string, err error)
|
||||
GetSweepInput(node tree.Node) (lifetime int64, sweepInput SweepInput, err error)
|
||||
FinalizeAndExtract(tx string) (txhex string, err error)
|
||||
@@ -41,7 +46,9 @@ type TxBuilder interface {
|
||||
FindLeaves(congestionTree tree.CongestionTree, fromtxid string, vout uint32) (leaves []tree.Node, err error)
|
||||
BuildAsyncPaymentTransactions(
|
||||
vtxosToSpend []domain.Vtxo,
|
||||
aspPubKey *secp256k1.PublicKey, receivers []domain.Receiver,
|
||||
descriptors map[domain.VtxoKey]string,
|
||||
forfeitsLeaves map[domain.VtxoKey]chainhash.Hash,
|
||||
receivers []domain.Receiver,
|
||||
) (string, error)
|
||||
VerifyAndCombinePartialTx(dest string, src string) (string, error)
|
||||
GetTxID(tx string) (string, error)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/ark-network/ark/server/internal/core/domain"
|
||||
@@ -101,13 +100,7 @@ func (r *vtxoRepository) GetAllVtxos(
|
||||
) ([]domain.Vtxo, []domain.Vtxo, error) {
|
||||
query := badgerhold.Where("Redeemed").Eq(false)
|
||||
if len(pubkey) > 0 {
|
||||
if len(pubkey) == 66 {
|
||||
pubkey = pubkey[2:]
|
||||
}
|
||||
|
||||
query = query.And("Descriptor").RegExp(
|
||||
regexp.MustCompile(fmt.Sprintf(".*%s.*", pubkey)),
|
||||
)
|
||||
query = query.And("Pubkey").Eq(pubkey)
|
||||
}
|
||||
vtxos, err := r.findVtxos(ctx, query)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,14 +4,12 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark/common/descriptor"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/server/internal/core/domain"
|
||||
"github.com/ark-network/ark/server/internal/core/ports"
|
||||
@@ -24,26 +22,8 @@ import (
|
||||
const (
|
||||
emptyPtx = "cHNldP8BAgQCAAAAAQQBAAEFAQABBgEDAfsEAgAAAAA="
|
||||
emptyTx = "0200000000000000000000"
|
||||
pubkey1 = "00000000000000000000000000000000000000000000000000000000000000001"
|
||||
pubkey2 = "00000000000000000000000000000000000000000000000000000000000000002"
|
||||
)
|
||||
|
||||
var desc1 = fmt.Sprintf(
|
||||
descriptor.DefaultVtxoDescriptorTemplate,
|
||||
randomString(66),
|
||||
pubkey1,
|
||||
pubkey1,
|
||||
512,
|
||||
pubkey1,
|
||||
)
|
||||
|
||||
var desc2 = fmt.Sprintf(
|
||||
descriptor.DefaultVtxoDescriptorTemplate,
|
||||
randomString(66),
|
||||
pubkey2,
|
||||
pubkey2,
|
||||
512,
|
||||
pubkey2,
|
||||
pubkey = "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967"
|
||||
pubkey2 = "33ffb3dee353b1a9ebe4ced64b946238d0a4ac364f275d771da6ad2445d07ae0"
|
||||
)
|
||||
|
||||
var congestionTree = [][]tree.Node{
|
||||
@@ -270,15 +250,13 @@ func testRoundRepository(t *testing.T, svc ports.RepoManager) {
|
||||
},
|
||||
RoundTxid: randomString(32),
|
||||
ExpireAt: 7980322,
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: randomString(120),
|
||||
Amount: 300,
|
||||
},
|
||||
Pubkey: randomString(32),
|
||||
Amount: 300,
|
||||
},
|
||||
},
|
||||
Receivers: []domain.Receiver{{
|
||||
Descriptor: randomString(120),
|
||||
Amount: 300,
|
||||
Pubkey: randomString(32),
|
||||
Amount: 300,
|
||||
}},
|
||||
},
|
||||
{
|
||||
@@ -292,20 +270,18 @@ func testRoundRepository(t *testing.T, svc ports.RepoManager) {
|
||||
},
|
||||
RoundTxid: randomString(32),
|
||||
ExpireAt: 7980322,
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: randomString(120),
|
||||
Amount: 600,
|
||||
},
|
||||
Pubkey: randomString(32),
|
||||
Amount: 600,
|
||||
},
|
||||
},
|
||||
Receivers: []domain.Receiver{
|
||||
{
|
||||
Descriptor: randomString(120),
|
||||
Amount: 400,
|
||||
Pubkey: randomString(32),
|
||||
Amount: 400,
|
||||
},
|
||||
{
|
||||
Descriptor: randomString(120),
|
||||
Amount: 200,
|
||||
Pubkey: randomString(32),
|
||||
Amount: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -370,20 +346,16 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) {
|
||||
Txid: randomString(32),
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: desc1,
|
||||
Amount: 1000,
|
||||
},
|
||||
Pubkey: pubkey,
|
||||
Amount: 1000,
|
||||
},
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: randomString(32),
|
||||
VOut: 1,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: desc1,
|
||||
Amount: 2000,
|
||||
},
|
||||
Pubkey: pubkey,
|
||||
Amount: 2000,
|
||||
},
|
||||
}
|
||||
newVtxos := append(userVtxos, domain.Vtxo{
|
||||
@@ -391,10 +363,8 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) {
|
||||
Txid: randomString(32),
|
||||
VOut: 1,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: desc2,
|
||||
Amount: 2000,
|
||||
},
|
||||
Pubkey: pubkey2,
|
||||
Amount: 2000,
|
||||
})
|
||||
|
||||
vtxoKeys := make([]domain.VtxoKey, 0, len(userVtxos))
|
||||
@@ -406,7 +376,7 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) {
|
||||
require.Error(t, err)
|
||||
require.Empty(t, vtxos)
|
||||
|
||||
spendableVtxos, spentVtxos, err := svc.Vtxos().GetAllVtxos(ctx, pubkey1)
|
||||
spendableVtxos, spentVtxos, err := svc.Vtxos().GetAllVtxos(ctx, pubkey)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, spendableVtxos)
|
||||
require.Empty(t, spentVtxos)
|
||||
@@ -423,7 +393,7 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) {
|
||||
require.NoError(t, err)
|
||||
require.Exactly(t, userVtxos, vtxos)
|
||||
|
||||
spendableVtxos, spentVtxos, err = svc.Vtxos().GetAllVtxos(ctx, pubkey1)
|
||||
spendableVtxos, spentVtxos, err = svc.Vtxos().GetAllVtxos(ctx, pubkey)
|
||||
require.NoError(t, err)
|
||||
|
||||
sortedVtxos := sortVtxos(userVtxos)
|
||||
@@ -449,7 +419,7 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) {
|
||||
require.True(t, v.Spent)
|
||||
}
|
||||
|
||||
spendableVtxos, spentVtxos, err = svc.Vtxos().GetAllVtxos(ctx, pubkey1)
|
||||
spendableVtxos, spentVtxos, err = svc.Vtxos().GetAllVtxos(ctx, pubkey)
|
||||
require.NoError(t, err)
|
||||
require.Exactly(t, vtxos[1:], spendableVtxos)
|
||||
require.Len(t, spentVtxos, len(vtxoKeys[:1]))
|
||||
|
||||
@@ -21,11 +21,11 @@ CREATE TABLE IF NOT EXISTS payment (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS receiver (
|
||||
payment_id TEXT NOT NULL,
|
||||
pubkey TEXT NOT NULL,
|
||||
pubkey TEXT,
|
||||
onchain_address TEXT,
|
||||
amount INTEGER NOT NULL,
|
||||
onchain_address TEXT NOT NULL,
|
||||
FOREIGN KEY (payment_id) REFERENCES payment(id),
|
||||
PRIMARY KEY (payment_id, pubkey)
|
||||
PRIMARY KEY (payment_id, pubkey, onchain_address)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tx (
|
||||
@@ -54,6 +54,7 @@ CREATE TABLE IF NOT EXISTS vtxo (
|
||||
expire_at INTEGER NOT NULL,
|
||||
payment_id TEXT,
|
||||
redeem_tx TEXT,
|
||||
pending BOOLEAN NOT NULL,
|
||||
PRIMARY KEY (txid, vout),
|
||||
FOREIGN KEY (payment_id) REFERENCES payment(id)
|
||||
);
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS old_receiver (
|
||||
payment_id TEXT NOT NULL,
|
||||
pubkey TEXT NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
onchain_address TEXT NOT NULL,
|
||||
FOREIGN KEY (payment_id) REFERENCES payment(id),
|
||||
PRIMARY KEY (payment_id, pubkey)
|
||||
);
|
||||
|
||||
INSERT INTO old_receiver SELECT * FROM receiver;
|
||||
|
||||
DROP TABLE receiver;
|
||||
|
||||
ALTER TABLE old_receiver RENAME TO receiver;
|
||||
|
||||
ALTER TABLE vtxo DROP COLUMN descriptor;
|
||||
ALTER TABLE vtxo ADD COLUMN pubkey TEXT NOT NULL;
|
||||
@@ -1,28 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS new_receiver (
|
||||
payment_id TEXT NOT NULL,
|
||||
descriptor TEXT NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
onchain_address TEXT NOT NULL,
|
||||
FOREIGN KEY (payment_id) REFERENCES payment(id),
|
||||
PRIMARY KEY (payment_id, descriptor)
|
||||
);
|
||||
|
||||
INSERT INTO new_receiver SELECT * FROM receiver;
|
||||
|
||||
DROP VIEW payment_vtxo_vw;
|
||||
DROP VIEW payment_receiver_vw;
|
||||
DROP TABLE receiver;
|
||||
ALTER TABLE new_receiver RENAME TO receiver;
|
||||
|
||||
ALTER TABLE vtxo ADD COLUMN descriptor TEXT;
|
||||
ALTER TABLE vtxo DROP COLUMN pubkey;
|
||||
|
||||
CREATE VIEW payment_vtxo_vw AS SELECT vtxo.*
|
||||
FROM payment
|
||||
LEFT OUTER JOIN vtxo
|
||||
ON payment.id=vtxo.payment_id;
|
||||
|
||||
CREATE VIEW payment_receiver_vw AS SELECT receiver.*
|
||||
FROM payment
|
||||
LEFT OUTER JOIN receiver
|
||||
ON payment.id=receiver.payment_id;
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE vtxo DROP COLUMN pending;
|
||||
@@ -1,14 +0,0 @@
|
||||
ALTER TABLE vtxo ADD COLUMN pending BOOLEAN NOT NULL;
|
||||
|
||||
DROP VIEW payment_vtxo_vw;
|
||||
DROP VIEW payment_receiver_vw;
|
||||
|
||||
CREATE VIEW payment_vtxo_vw AS SELECT vtxo.*
|
||||
FROM payment
|
||||
LEFT OUTER JOIN vtxo
|
||||
ON payment.id=vtxo.payment_id;
|
||||
|
||||
CREATE VIEW payment_receiver_vw AS SELECT receiver.*
|
||||
FROM payment
|
||||
LEFT OUTER JOIN receiver
|
||||
ON payment.id=receiver.payment_id;
|
||||
@@ -164,10 +164,16 @@ func (r *roundRepository) AddOrUpdateRound(ctx context.Context, round domain.Rou
|
||||
if err := querierWithTx.UpsertReceiver(
|
||||
ctx,
|
||||
queries.UpsertReceiverParams{
|
||||
PaymentID: payment.Id,
|
||||
Descriptor: receiver.Descriptor,
|
||||
Amount: int64(receiver.Amount),
|
||||
OnchainAddress: receiver.OnchainAddress,
|
||||
PaymentID: payment.Id,
|
||||
Amount: int64(receiver.Amount),
|
||||
Pubkey: sql.NullString{
|
||||
String: receiver.Pubkey,
|
||||
Valid: len(receiver.Pubkey) > 0,
|
||||
},
|
||||
OnchainAddress: sql.NullString{
|
||||
String: receiver.OnchainAddress,
|
||||
Valid: len(receiver.OnchainAddress) > 0,
|
||||
},
|
||||
},
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to upsert receiver: %w", err)
|
||||
@@ -320,8 +326,8 @@ func (r *roundRepository) GetSweptRounds(ctx context.Context) ([]domain.Round, e
|
||||
|
||||
func rowToReceiver(row queries.PaymentReceiverVw) domain.Receiver {
|
||||
return domain.Receiver{
|
||||
Descriptor: row.Descriptor.String,
|
||||
Amount: uint64(row.Amount.Int64),
|
||||
Pubkey: row.Pubkey.String,
|
||||
OnchainAddress: row.OnchainAddress.String,
|
||||
}
|
||||
}
|
||||
@@ -413,8 +419,8 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error
|
||||
|
||||
found := false
|
||||
for _, rcv := range payment.Receivers {
|
||||
if v.receiver.Descriptor.Valid && v.receiver.Amount.Valid {
|
||||
if rcv.Descriptor == v.receiver.Descriptor.String && int64(rcv.Amount) == v.receiver.Amount.Int64 {
|
||||
if (v.receiver.Pubkey.Valid || v.receiver.OnchainAddress.Valid) && v.receiver.Amount.Valid {
|
||||
if rcv.Pubkey == v.receiver.Pubkey.String && rcv.OnchainAddress == v.receiver.OnchainAddress.String && int64(rcv.Amount) == v.receiver.Amount.Int64 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
@@ -469,10 +475,8 @@ func rowToPaymentVtxoVw(row queries.PaymentVtxoVw) domain.Vtxo {
|
||||
Txid: row.Txid.String,
|
||||
VOut: uint32(row.Vout.Int64),
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: row.Descriptor.String,
|
||||
Amount: uint64(row.Amount.Int64),
|
||||
},
|
||||
Amount: uint64(row.Amount.Int64),
|
||||
Pubkey: row.Pubkey.String,
|
||||
RoundTxid: row.PoolTx.String,
|
||||
SpentBy: row.SpentBy.String,
|
||||
Spent: row.Spent.Bool,
|
||||
|
||||
@@ -15,32 +15,32 @@ type Payment struct {
|
||||
|
||||
type PaymentReceiverVw struct {
|
||||
PaymentID sql.NullString
|
||||
Descriptor sql.NullString
|
||||
Amount sql.NullInt64
|
||||
Pubkey sql.NullString
|
||||
OnchainAddress sql.NullString
|
||||
Amount sql.NullInt64
|
||||
}
|
||||
|
||||
type PaymentVtxoVw struct {
|
||||
Txid sql.NullString
|
||||
Vout sql.NullInt64
|
||||
Amount sql.NullInt64
|
||||
PoolTx sql.NullString
|
||||
SpentBy sql.NullString
|
||||
Spent sql.NullBool
|
||||
Redeemed sql.NullBool
|
||||
Swept sql.NullBool
|
||||
ExpireAt sql.NullInt64
|
||||
PaymentID sql.NullString
|
||||
RedeemTx sql.NullString
|
||||
Descriptor sql.NullString
|
||||
Pending sql.NullBool
|
||||
Txid sql.NullString
|
||||
Vout sql.NullInt64
|
||||
Pubkey sql.NullString
|
||||
Amount sql.NullInt64
|
||||
PoolTx sql.NullString
|
||||
SpentBy sql.NullString
|
||||
Spent sql.NullBool
|
||||
Redeemed sql.NullBool
|
||||
Swept sql.NullBool
|
||||
ExpireAt sql.NullInt64
|
||||
PaymentID sql.NullString
|
||||
RedeemTx sql.NullString
|
||||
Pending sql.NullBool
|
||||
}
|
||||
|
||||
type Receiver struct {
|
||||
PaymentID string
|
||||
Descriptor string
|
||||
Pubkey sql.NullString
|
||||
OnchainAddress sql.NullString
|
||||
Amount int64
|
||||
OnchainAddress string
|
||||
}
|
||||
|
||||
type Round struct {
|
||||
@@ -88,17 +88,17 @@ type Tx struct {
|
||||
}
|
||||
|
||||
type Vtxo struct {
|
||||
Txid string
|
||||
Vout int64
|
||||
Amount int64
|
||||
PoolTx string
|
||||
SpentBy string
|
||||
Spent bool
|
||||
Redeemed bool
|
||||
Swept bool
|
||||
ExpireAt int64
|
||||
PaymentID sql.NullString
|
||||
RedeemTx sql.NullString
|
||||
Descriptor sql.NullString
|
||||
Pending bool
|
||||
Txid string
|
||||
Vout int64
|
||||
Pubkey string
|
||||
Amount int64
|
||||
PoolTx string
|
||||
SpentBy string
|
||||
Spent bool
|
||||
Redeemed bool
|
||||
Swept bool
|
||||
ExpireAt int64
|
||||
PaymentID sql.NullString
|
||||
RedeemTx sql.NullString
|
||||
Pending bool
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func (q *Queries) MarkVtxoAsSwept(ctx context.Context, arg MarkVtxoAsSweptParams
|
||||
}
|
||||
|
||||
const selectNotRedeemedVtxos = `-- name: SelectNotRedeemedVtxos :many
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo
|
||||
WHERE redeemed = false
|
||||
`
|
||||
|
||||
@@ -74,6 +74,7 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
|
||||
if err := rows.Scan(
|
||||
&i.Vtxo.Txid,
|
||||
&i.Vtxo.Vout,
|
||||
&i.Vtxo.Pubkey,
|
||||
&i.Vtxo.Amount,
|
||||
&i.Vtxo.PoolTx,
|
||||
&i.Vtxo.SpentBy,
|
||||
@@ -83,7 +84,6 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.Vtxo.Descriptor,
|
||||
&i.Vtxo.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -100,16 +100,16 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
|
||||
}
|
||||
|
||||
const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo
|
||||
WHERE redeemed = false AND INSTR(descriptor, ?) > 0
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo
|
||||
WHERE redeemed = false AND pubkey = ?
|
||||
`
|
||||
|
||||
type SelectNotRedeemedVtxosWithPubkeyRow struct {
|
||||
Vtxo Vtxo
|
||||
}
|
||||
|
||||
func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, instr string) ([]SelectNotRedeemedVtxosWithPubkeyRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, selectNotRedeemedVtxosWithPubkey, instr)
|
||||
func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, pubkey string) ([]SelectNotRedeemedVtxosWithPubkeyRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, selectNotRedeemedVtxosWithPubkey, pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -120,6 +120,7 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, instr st
|
||||
if err := rows.Scan(
|
||||
&i.Vtxo.Txid,
|
||||
&i.Vtxo.Vout,
|
||||
&i.Vtxo.Pubkey,
|
||||
&i.Vtxo.Amount,
|
||||
&i.Vtxo.PoolTx,
|
||||
&i.Vtxo.SpentBy,
|
||||
@@ -129,7 +130,6 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, instr st
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.Vtxo.Descriptor,
|
||||
&i.Vtxo.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -208,8 +208,8 @@ const selectRoundWithRoundId = `-- name: SelectRoundWithRoundId :many
|
||||
SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept,
|
||||
round_payment_vw.id, round_payment_vw.round_id,
|
||||
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.descriptor, payment_receiver_vw.amount, payment_receiver_vw.onchain_address,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.descriptor, payment_vtxo_vw.pending
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending
|
||||
FROM round
|
||||
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
|
||||
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
|
||||
@@ -260,11 +260,12 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele
|
||||
&i.RoundTxVw.ParentTxid,
|
||||
&i.RoundTxVw.IsLeaf,
|
||||
&i.PaymentReceiverVw.PaymentID,
|
||||
&i.PaymentReceiverVw.Descriptor,
|
||||
&i.PaymentReceiverVw.Amount,
|
||||
&i.PaymentReceiverVw.Pubkey,
|
||||
&i.PaymentReceiverVw.OnchainAddress,
|
||||
&i.PaymentReceiverVw.Amount,
|
||||
&i.PaymentVtxoVw.Txid,
|
||||
&i.PaymentVtxoVw.Vout,
|
||||
&i.PaymentVtxoVw.Pubkey,
|
||||
&i.PaymentVtxoVw.Amount,
|
||||
&i.PaymentVtxoVw.PoolTx,
|
||||
&i.PaymentVtxoVw.SpentBy,
|
||||
@@ -274,7 +275,6 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
&i.PaymentVtxoVw.Descriptor,
|
||||
&i.PaymentVtxoVw.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -294,8 +294,8 @@ const selectRoundWithRoundTxId = `-- name: SelectRoundWithRoundTxId :many
|
||||
SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept,
|
||||
round_payment_vw.id, round_payment_vw.round_id,
|
||||
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.descriptor, payment_receiver_vw.amount, payment_receiver_vw.onchain_address,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.descriptor, payment_vtxo_vw.pending
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending
|
||||
FROM round
|
||||
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
|
||||
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
|
||||
@@ -346,11 +346,12 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]
|
||||
&i.RoundTxVw.ParentTxid,
|
||||
&i.RoundTxVw.IsLeaf,
|
||||
&i.PaymentReceiverVw.PaymentID,
|
||||
&i.PaymentReceiverVw.Descriptor,
|
||||
&i.PaymentReceiverVw.Amount,
|
||||
&i.PaymentReceiverVw.Pubkey,
|
||||
&i.PaymentReceiverVw.OnchainAddress,
|
||||
&i.PaymentReceiverVw.Amount,
|
||||
&i.PaymentVtxoVw.Txid,
|
||||
&i.PaymentVtxoVw.Vout,
|
||||
&i.PaymentVtxoVw.Pubkey,
|
||||
&i.PaymentVtxoVw.Amount,
|
||||
&i.PaymentVtxoVw.PoolTx,
|
||||
&i.PaymentVtxoVw.SpentBy,
|
||||
@@ -360,7 +361,6 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
&i.PaymentVtxoVw.Descriptor,
|
||||
&i.PaymentVtxoVw.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -380,8 +380,8 @@ const selectSweepableRounds = `-- name: SelectSweepableRounds :many
|
||||
SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept,
|
||||
round_payment_vw.id, round_payment_vw.round_id,
|
||||
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.descriptor, payment_receiver_vw.amount, payment_receiver_vw.onchain_address,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.descriptor, payment_vtxo_vw.pending
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending
|
||||
FROM round
|
||||
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
|
||||
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
|
||||
@@ -432,11 +432,12 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
|
||||
&i.RoundTxVw.ParentTxid,
|
||||
&i.RoundTxVw.IsLeaf,
|
||||
&i.PaymentReceiverVw.PaymentID,
|
||||
&i.PaymentReceiverVw.Descriptor,
|
||||
&i.PaymentReceiverVw.Amount,
|
||||
&i.PaymentReceiverVw.Pubkey,
|
||||
&i.PaymentReceiverVw.OnchainAddress,
|
||||
&i.PaymentReceiverVw.Amount,
|
||||
&i.PaymentVtxoVw.Txid,
|
||||
&i.PaymentVtxoVw.Vout,
|
||||
&i.PaymentVtxoVw.Pubkey,
|
||||
&i.PaymentVtxoVw.Amount,
|
||||
&i.PaymentVtxoVw.PoolTx,
|
||||
&i.PaymentVtxoVw.SpentBy,
|
||||
@@ -446,7 +447,6 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
&i.PaymentVtxoVw.Descriptor,
|
||||
&i.PaymentVtxoVw.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -463,7 +463,7 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
|
||||
}
|
||||
|
||||
const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo
|
||||
WHERE redeemed = false AND swept = false
|
||||
`
|
||||
|
||||
@@ -483,6 +483,7 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt
|
||||
if err := rows.Scan(
|
||||
&i.Vtxo.Txid,
|
||||
&i.Vtxo.Vout,
|
||||
&i.Vtxo.Pubkey,
|
||||
&i.Vtxo.Amount,
|
||||
&i.Vtxo.PoolTx,
|
||||
&i.Vtxo.SpentBy,
|
||||
@@ -492,7 +493,6 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.Vtxo.Descriptor,
|
||||
&i.Vtxo.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -512,8 +512,8 @@ const selectSweptRounds = `-- name: SelectSweptRounds :many
|
||||
SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept,
|
||||
round_payment_vw.id, round_payment_vw.round_id,
|
||||
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.descriptor, payment_receiver_vw.amount, payment_receiver_vw.onchain_address,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.descriptor, payment_vtxo_vw.pending
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending
|
||||
FROM round
|
||||
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
|
||||
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
|
||||
@@ -564,11 +564,12 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
|
||||
&i.RoundTxVw.ParentTxid,
|
||||
&i.RoundTxVw.IsLeaf,
|
||||
&i.PaymentReceiverVw.PaymentID,
|
||||
&i.PaymentReceiverVw.Descriptor,
|
||||
&i.PaymentReceiverVw.Amount,
|
||||
&i.PaymentReceiverVw.Pubkey,
|
||||
&i.PaymentReceiverVw.OnchainAddress,
|
||||
&i.PaymentReceiverVw.Amount,
|
||||
&i.PaymentVtxoVw.Txid,
|
||||
&i.PaymentVtxoVw.Vout,
|
||||
&i.PaymentVtxoVw.Pubkey,
|
||||
&i.PaymentVtxoVw.Amount,
|
||||
&i.PaymentVtxoVw.PoolTx,
|
||||
&i.PaymentVtxoVw.SpentBy,
|
||||
@@ -578,7 +579,6 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
&i.PaymentVtxoVw.Descriptor,
|
||||
&i.PaymentVtxoVw.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -595,7 +595,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
|
||||
}
|
||||
|
||||
const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo
|
||||
WHERE txid = ? AND vout = ?
|
||||
`
|
||||
|
||||
@@ -614,6 +614,7 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp
|
||||
err := row.Scan(
|
||||
&i.Vtxo.Txid,
|
||||
&i.Vtxo.Vout,
|
||||
&i.Vtxo.Pubkey,
|
||||
&i.Vtxo.Amount,
|
||||
&i.Vtxo.PoolTx,
|
||||
&i.Vtxo.SpentBy,
|
||||
@@ -623,14 +624,13 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.Vtxo.Descriptor,
|
||||
&i.Vtxo.Pending,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const selectVtxosByPoolTxid = `-- name: SelectVtxosByPoolTxid :many
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo
|
||||
WHERE pool_tx = ?
|
||||
`
|
||||
|
||||
@@ -650,6 +650,7 @@ func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]S
|
||||
if err := rows.Scan(
|
||||
&i.Vtxo.Txid,
|
||||
&i.Vtxo.Vout,
|
||||
&i.Vtxo.Pubkey,
|
||||
&i.Vtxo.Amount,
|
||||
&i.Vtxo.PoolTx,
|
||||
&i.Vtxo.SpentBy,
|
||||
@@ -659,7 +660,6 @@ func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]S
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.Vtxo.Descriptor,
|
||||
&i.Vtxo.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -721,26 +721,26 @@ func (q *Queries) UpsertPayment(ctx context.Context, arg UpsertPaymentParams) er
|
||||
}
|
||||
|
||||
const upsertReceiver = `-- name: UpsertReceiver :exec
|
||||
INSERT INTO receiver (payment_id, descriptor, amount, onchain_address) VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(payment_id, descriptor) DO UPDATE SET
|
||||
INSERT INTO receiver (payment_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(payment_id, pubkey, onchain_address) DO UPDATE SET
|
||||
amount = EXCLUDED.amount,
|
||||
onchain_address = EXCLUDED.onchain_address,
|
||||
descriptor = EXCLUDED.descriptor
|
||||
pubkey = EXCLUDED.pubkey,
|
||||
onchain_address = EXCLUDED.onchain_address
|
||||
`
|
||||
|
||||
type UpsertReceiverParams struct {
|
||||
PaymentID string
|
||||
Descriptor string
|
||||
Pubkey sql.NullString
|
||||
OnchainAddress sql.NullString
|
||||
Amount int64
|
||||
OnchainAddress string
|
||||
}
|
||||
|
||||
func (q *Queries) UpsertReceiver(ctx context.Context, arg UpsertReceiverParams) error {
|
||||
_, err := q.db.ExecContext(ctx, upsertReceiver,
|
||||
arg.PaymentID,
|
||||
arg.Descriptor,
|
||||
arg.Amount,
|
||||
arg.Pubkey,
|
||||
arg.OnchainAddress,
|
||||
arg.Amount,
|
||||
)
|
||||
return err
|
||||
}
|
||||
@@ -847,9 +847,9 @@ func (q *Queries) UpsertTransaction(ctx context.Context, arg UpsertTransactionPa
|
||||
}
|
||||
|
||||
const upsertVtxo = `-- name: UpsertVtxo :exec
|
||||
INSERT INTO vtxo (txid, vout, descriptor, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending)
|
||||
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
|
||||
descriptor = EXCLUDED.descriptor,
|
||||
pubkey = EXCLUDED.pubkey,
|
||||
amount = EXCLUDED.amount,
|
||||
pool_tx = EXCLUDED.pool_tx,
|
||||
spent_by = EXCLUDED.spent_by,
|
||||
@@ -862,25 +862,25 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SE
|
||||
`
|
||||
|
||||
type UpsertVtxoParams struct {
|
||||
Txid string
|
||||
Vout int64
|
||||
Descriptor sql.NullString
|
||||
Amount int64
|
||||
PoolTx string
|
||||
SpentBy string
|
||||
Spent bool
|
||||
Redeemed bool
|
||||
Swept bool
|
||||
ExpireAt int64
|
||||
RedeemTx sql.NullString
|
||||
Pending bool
|
||||
Txid string
|
||||
Vout int64
|
||||
Pubkey string
|
||||
Amount int64
|
||||
PoolTx string
|
||||
SpentBy string
|
||||
Spent bool
|
||||
Redeemed bool
|
||||
Swept bool
|
||||
ExpireAt int64
|
||||
RedeemTx sql.NullString
|
||||
Pending bool
|
||||
}
|
||||
|
||||
func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error {
|
||||
_, err := q.db.ExecContext(ctx, upsertVtxo,
|
||||
arg.Txid,
|
||||
arg.Vout,
|
||||
arg.Descriptor,
|
||||
arg.Pubkey,
|
||||
arg.Amount,
|
||||
arg.PoolTx,
|
||||
arg.SpentBy,
|
||||
|
||||
@@ -44,11 +44,11 @@ INSERT INTO payment (id, round_id) VALUES (?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET round_id = EXCLUDED.round_id;
|
||||
|
||||
-- name: UpsertReceiver :exec
|
||||
INSERT INTO receiver (payment_id, descriptor, amount, onchain_address) VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(payment_id, descriptor) DO UPDATE SET
|
||||
INSERT INTO receiver (payment_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(payment_id, pubkey, onchain_address) DO UPDATE SET
|
||||
amount = EXCLUDED.amount,
|
||||
onchain_address = EXCLUDED.onchain_address,
|
||||
descriptor = EXCLUDED.descriptor;
|
||||
pubkey = EXCLUDED.pubkey,
|
||||
onchain_address = EXCLUDED.onchain_address;
|
||||
|
||||
-- name: UpdateVtxoPaymentId :exec
|
||||
UPDATE vtxo SET payment_id = ? WHERE txid = ? AND vout = ?;
|
||||
@@ -112,9 +112,9 @@ SELECT id FROM round WHERE starting_timestamp > ? AND starting_timestamp < ?;
|
||||
SELECT id FROM round;
|
||||
|
||||
-- name: UpsertVtxo :exec
|
||||
INSERT INTO vtxo (txid, vout, descriptor, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending)
|
||||
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
|
||||
descriptor = EXCLUDED.descriptor,
|
||||
pubkey = EXCLUDED.pubkey,
|
||||
amount = EXCLUDED.amount,
|
||||
pool_tx = EXCLUDED.pool_tx,
|
||||
spent_by = EXCLUDED.spent_by,
|
||||
@@ -135,7 +135,7 @@ WHERE redeemed = false;
|
||||
|
||||
-- name: SelectNotRedeemedVtxosWithPubkey :many
|
||||
SELECT sqlc.embed(vtxo) FROM vtxo
|
||||
WHERE redeemed = false AND INSTR(descriptor, ?) > 0;
|
||||
WHERE redeemed = false AND pubkey = ?;
|
||||
|
||||
-- name: SelectVtxoByOutpoint :one
|
||||
SELECT sqlc.embed(vtxo) FROM vtxo
|
||||
|
||||
@@ -37,20 +37,21 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro
|
||||
txBody := func(querierWithTx *queries.Queries) error {
|
||||
for i := range vtxos {
|
||||
vtxo := vtxos[i]
|
||||
|
||||
if err := querierWithTx.UpsertVtxo(
|
||||
ctx, queries.UpsertVtxoParams{
|
||||
Txid: vtxo.Txid,
|
||||
Vout: int64(vtxo.VOut),
|
||||
Descriptor: sql.NullString{String: vtxo.Descriptor, Valid: true},
|
||||
Amount: int64(vtxo.Amount),
|
||||
PoolTx: vtxo.RoundTxid,
|
||||
SpentBy: vtxo.SpentBy,
|
||||
Spent: vtxo.Spent,
|
||||
Redeemed: vtxo.Redeemed,
|
||||
Swept: vtxo.Swept,
|
||||
ExpireAt: vtxo.ExpireAt,
|
||||
RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true},
|
||||
Pending: vtxo.Pending,
|
||||
Txid: vtxo.Txid,
|
||||
Vout: int64(vtxo.VOut),
|
||||
Pubkey: vtxo.Pubkey,
|
||||
Amount: int64(vtxo.Amount),
|
||||
PoolTx: vtxo.RoundTxid,
|
||||
SpentBy: vtxo.SpentBy,
|
||||
Spent: vtxo.Spent,
|
||||
Redeemed: vtxo.Redeemed,
|
||||
Swept: vtxo.Swept,
|
||||
ExpireAt: vtxo.ExpireAt,
|
||||
RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true},
|
||||
Pending: vtxo.Pending,
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
@@ -81,10 +82,6 @@ func (v *vxtoRepository) GetAllVtxos(ctx context.Context, pubkey string) ([]doma
|
||||
|
||||
var rows []queries.Vtxo
|
||||
if withPubkey {
|
||||
if len(pubkey) == 66 {
|
||||
pubkey = pubkey[2:]
|
||||
}
|
||||
|
||||
res, err := v.querier.SelectNotRedeemedVtxosWithPubkey(ctx, pubkey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -253,10 +250,8 @@ func rowToVtxo(row queries.Vtxo) domain.Vtxo {
|
||||
Txid: row.Txid,
|
||||
VOut: uint32(row.Vout),
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Descriptor: row.Descriptor.String,
|
||||
Amount: uint64(row.Amount),
|
||||
},
|
||||
Amount: uint64(row.Amount),
|
||||
Pubkey: row.Pubkey,
|
||||
RoundTxid: row.PoolTx,
|
||||
SpentBy: row.SpentBy,
|
||||
Spent: row.Spent,
|
||||
|
||||
@@ -84,6 +84,7 @@ func (b *txBuilder) BuildSweepTx(inputs []ports.SweepInput) (signedSweepTx strin
|
||||
func (b *txBuilder) BuildForfeitTxs(
|
||||
poolTx string,
|
||||
payments []domain.Payment,
|
||||
descriptors map[domain.VtxoKey]string,
|
||||
minRelayFeeRate chainfee.SatPerKVByte,
|
||||
) (connectors []string, forfeitTxs []string, err error) {
|
||||
connectorAddress, err := b.getConnectorAddress(poolTx)
|
||||
@@ -106,7 +107,7 @@ func (b *txBuilder) BuildForfeitTxs(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
forfeitTxs, err = b.createForfeitTxs(payments, connectorTxs, connectorAmount, minRelayFeeRate)
|
||||
forfeitTxs, err = b.createForfeitTxs(payments, descriptors, connectorTxs, connectorAmount, minRelayFeeRate)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -147,13 +148,13 @@ func (b *txBuilder) BuildRoundTx(
|
||||
return "", nil, "", err
|
||||
}
|
||||
|
||||
receivers, err := getOffchainReceivers(payments)
|
||||
vtxosLeaves, err := getOutputVtxosLeaves(payments)
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
}
|
||||
|
||||
treeFactoryFn, sharedOutputScript, sharedOutputAmount, err = tree.CraftCongestionTree(
|
||||
b.onchainNetwork().AssetID, aspPubkey, receivers, feeSatsPerNode, b.roundLifetime,
|
||||
b.onchainNetwork().AssetID, aspPubkey, vtxosLeaves, feeSatsPerNode, b.roundLifetime,
|
||||
)
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
@@ -362,7 +363,10 @@ func (b *txBuilder) FindLeaves(
|
||||
}
|
||||
|
||||
func (b *txBuilder) BuildAsyncPaymentTransactions(
|
||||
_ []domain.Vtxo, _ *secp256k1.PublicKey, _ []domain.Receiver,
|
||||
_ []domain.Vtxo,
|
||||
_ map[domain.VtxoKey]string,
|
||||
_ map[domain.VtxoKey]chainhash.Hash,
|
||||
_ []domain.Receiver,
|
||||
) (string, error) {
|
||||
return "", fmt.Errorf("not implemented")
|
||||
}
|
||||
@@ -396,7 +400,6 @@ func (b *txBuilder) createPoolTx(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receivers := getOnchainReceivers(payments)
|
||||
nbOfInputs := countSpentVtxos(payments)
|
||||
connectorsAmount := (dustAmount + connectorMinRelayFee) * nbOfInputs
|
||||
if nbOfInputs > 1 {
|
||||
@@ -424,21 +427,17 @@ func (b *txBuilder) createPoolTx(
|
||||
})
|
||||
}
|
||||
|
||||
for _, receiver := range receivers {
|
||||
targetAmount += receiver.Amount
|
||||
|
||||
receiverScript, err := address.ToOutputScript(receiver.OnchainAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputs = append(outputs, psetv2.OutputArgs{
|
||||
Asset: b.onchainNetwork().AssetID,
|
||||
Amount: receiver.Amount,
|
||||
Script: receiverScript,
|
||||
})
|
||||
onchainOutputs, err := getOnchainOutputs(payments, b.onchainNetwork())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, out := range onchainOutputs {
|
||||
targetAmount += out.Amount
|
||||
}
|
||||
|
||||
outputs = append(outputs, onchainOutputs...)
|
||||
|
||||
for _, in := range boardingInputs {
|
||||
targetAmount -= in.Amount
|
||||
}
|
||||
@@ -786,6 +785,7 @@ func (b *txBuilder) createConnectors(
|
||||
|
||||
func (b *txBuilder) createForfeitTxs(
|
||||
payments []domain.Payment,
|
||||
descriptors map[domain.VtxoKey]string,
|
||||
connectors []*psetv2.Pset,
|
||||
connectorAmount uint64,
|
||||
minRelayFeeRate chainfee.SatPerKVByte,
|
||||
@@ -803,7 +803,12 @@ func (b *txBuilder) createForfeitTxs(
|
||||
forfeitTxs := make([]string, 0)
|
||||
for _, payment := range payments {
|
||||
for _, vtxo := range payment.Inputs {
|
||||
offchainScript, err := tree.ParseVtxoScript(vtxo.Descriptor)
|
||||
desc, ok := descriptors[vtxo.VtxoKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("descriptor not found for vtxo %s:%d", vtxo.VtxoKey.Txid, vtxo.VtxoKey.VOut)
|
||||
}
|
||||
|
||||
offchainScript, err := tree.ParseVtxoScript(desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func TestBuildForfeitTxs(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
for _, f := range fixtures.Valid {
|
||||
connectors, forfeitTxs, err := builder.BuildForfeitTxs(
|
||||
f.PoolTx, f.Payments, minRelayFeeRate,
|
||||
f.PoolTx, f.Payments, f.Descriptors, minRelayFeeRate,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, connectors, f.ExpectedNumOfConnectors)
|
||||
@@ -151,7 +151,7 @@ func TestBuildForfeitTxs(t *testing.T) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
for _, f := range fixtures.Invalid {
|
||||
connectors, forfeitTxs, err := builder.BuildForfeitTxs(
|
||||
f.PoolTx, f.Payments, minRelayFeeRate,
|
||||
f.PoolTx, f.Payments, f.Descriptors, minRelayFeeRate,
|
||||
)
|
||||
require.EqualError(t, err, f.ExpectedErr)
|
||||
require.Empty(t, connectors)
|
||||
@@ -215,6 +215,7 @@ func parsePoolTxFixtures() (*poolTxFixtures, error) {
|
||||
type forfeitTxsFixtures struct {
|
||||
Valid []struct {
|
||||
Payments []domain.Payment
|
||||
Descriptors map[domain.VtxoKey]string
|
||||
ExpectedNumOfConnectors int
|
||||
ExpectedNumOfForfeitTxs int
|
||||
PoolTx string
|
||||
@@ -222,6 +223,7 @@ type forfeitTxsFixtures struct {
|
||||
}
|
||||
Invalid []struct {
|
||||
Payments []domain.Payment
|
||||
Descriptors map[domain.VtxoKey]string
|
||||
ExpectedErr string
|
||||
PoolTx string
|
||||
}
|
||||
@@ -244,6 +246,42 @@ func parseForfeitTxsFixtures() (*forfeitTxsFixtures, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
valid := vv["valid"].([]interface{})
|
||||
for i, v := range valid {
|
||||
val := v.(map[string]interface{})
|
||||
payments := val["payments"].([]interface{})
|
||||
descriptors := make(map[domain.VtxoKey]string)
|
||||
for _, p := range payments {
|
||||
inputs := p.(map[string]interface{})["inputs"].([]interface{})
|
||||
for _, in := range inputs {
|
||||
inMap := in.(map[string]interface{})
|
||||
descriptors[domain.VtxoKey{
|
||||
Txid: inMap["txid"].(string),
|
||||
VOut: uint32(inMap["vout"].(float64)),
|
||||
}] = inMap["descriptor"].(string)
|
||||
}
|
||||
}
|
||||
fixtures.Valid[i].Descriptors = descriptors
|
||||
}
|
||||
|
||||
invalid := vv["invalid"].([]interface{})
|
||||
for i, v := range invalid {
|
||||
val := v.(map[string]interface{})
|
||||
payments := val["payments"].([]interface{})
|
||||
descriptors := make(map[domain.VtxoKey]string)
|
||||
for _, p := range payments {
|
||||
inputs := p.(map[string]interface{})["inputs"].([]interface{})
|
||||
for _, in := range inputs {
|
||||
inMap := in.(map[string]interface{})
|
||||
descriptors[domain.VtxoKey{
|
||||
Txid: inMap["txid"].(string),
|
||||
VOut: uint32(inMap["vout"].(float64)),
|
||||
}] = inMap["descriptor"].(string)
|
||||
}
|
||||
}
|
||||
fixtures.Invalid[i].Descriptors = descriptors
|
||||
}
|
||||
|
||||
return &fixtures, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1100
|
||||
}
|
||||
]
|
||||
@@ -33,18 +33,18 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -61,18 +61,18 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -83,18 +83,18 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -105,18 +105,18 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -133,58 +133,58 @@
|
||||
{
|
||||
"txid": "755c820771284d85ea4bbcc246565b4eddadc44237a7e57a0f9cb78a840d1d41",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "66a0df86fcdeb84b8877adfe0b2c556dba30305d72ddbd4c49355f6930355357",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "9913159bc7aa493ca53cbb9cbc88f97ba01137c814009dc7ef520c3fafc67909",
|
||||
"vout": 1,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 500
|
||||
},
|
||||
{
|
||||
"txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60",
|
||||
"vout": 0,
|
||||
"pubkey":"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60",
|
||||
"vout": 1,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 1000
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -206,25 +206,25 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 1,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001",
|
||||
"amount": 500
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
|
||||
@@ -45,34 +45,38 @@ func getPsetId(pset *psetv2.Pset) (string, error) {
|
||||
return utx.TxHash().String(), nil
|
||||
}
|
||||
|
||||
func getOnchainReceivers(
|
||||
payments []domain.Payment,
|
||||
) []domain.Receiver {
|
||||
receivers := make([]domain.Receiver, 0)
|
||||
func getOnchainOutputs(
|
||||
payments []domain.Payment, net *network.Network,
|
||||
) ([]psetv2.OutputArgs, error) {
|
||||
outputs := make([]psetv2.OutputArgs, 0)
|
||||
for _, payment := range payments {
|
||||
for _, receiver := range payment.Receivers {
|
||||
if receiver.IsOnchain() {
|
||||
receivers = append(receivers, receiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
return receivers
|
||||
}
|
||||
|
||||
func getOffchainReceivers(
|
||||
payments []domain.Payment,
|
||||
) ([]tree.Receiver, error) {
|
||||
receivers := make([]tree.Receiver, 0)
|
||||
for _, payment := range payments {
|
||||
for _, receiver := range payment.Receivers {
|
||||
if !receiver.IsOnchain() {
|
||||
vtxoScript, err := tree.ParseVtxoScript(receiver.Descriptor)
|
||||
receiverScript, err := address.ToOutputScript(receiver.OnchainAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receivers = append(receivers, tree.Receiver{
|
||||
Script: vtxoScript,
|
||||
outputs = append(outputs, psetv2.OutputArgs{
|
||||
Script: receiverScript,
|
||||
Amount: receiver.Amount,
|
||||
Asset: net.AssetID,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return outputs, nil
|
||||
}
|
||||
|
||||
func getOutputVtxosLeaves(
|
||||
payments []domain.Payment,
|
||||
) ([]tree.VtxoLeaf, error) {
|
||||
receivers := make([]tree.VtxoLeaf, 0)
|
||||
for _, payment := range payments {
|
||||
for _, receiver := range payment.Receivers {
|
||||
if !receiver.IsOnchain() {
|
||||
receivers = append(receivers, tree.VtxoLeaf{
|
||||
Pubkey: receiver.Pubkey,
|
||||
Amount: receiver.Amount,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -228,7 +228,10 @@ func (b *txBuilder) BuildSweepTx(inputs []ports.SweepInput) (signedSweepTx strin
|
||||
}
|
||||
|
||||
func (b *txBuilder) BuildForfeitTxs(
|
||||
poolTx string, payments []domain.Payment, minRelayFeeRate chainfee.SatPerKVByte,
|
||||
poolTx string,
|
||||
payments []domain.Payment,
|
||||
descriptors map[domain.VtxoKey]string,
|
||||
minRelayFeeRate chainfee.SatPerKVByte,
|
||||
) (connectors []string, forfeitTxs []string, err error) {
|
||||
connectorPkScript, err := b.getConnectorPkScript(poolTx)
|
||||
if err != nil {
|
||||
@@ -245,7 +248,7 @@ func (b *txBuilder) BuildForfeitTxs(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
forfeitTxs, err = b.createForfeitTxs(payments, connectorTxs, minRelayFeeRate)
|
||||
forfeitTxs, err = b.createForfeitTxs(payments, descriptors, connectorTxs, minRelayFeeRate)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -271,7 +274,7 @@ func (b *txBuilder) BuildRoundTx(
|
||||
return "", nil, "", fmt.Errorf("missing cosigners")
|
||||
}
|
||||
|
||||
receivers, err := getOffchainReceivers(payments)
|
||||
receivers, err := getOutputVtxosLeaves(payments)
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
}
|
||||
@@ -399,7 +402,10 @@ func (b *txBuilder) FindLeaves(congestionTree tree.CongestionTree, fromtxid stri
|
||||
}
|
||||
|
||||
func (b *txBuilder) BuildAsyncPaymentTransactions(
|
||||
vtxos []domain.Vtxo, aspPubKey *secp256k1.PublicKey, receivers []domain.Receiver,
|
||||
vtxos []domain.Vtxo,
|
||||
descriptors map[domain.VtxoKey]string,
|
||||
forfeitsLeaves map[domain.VtxoKey]chainhash.Hash,
|
||||
receivers []domain.Receiver,
|
||||
) (string, error) {
|
||||
if len(vtxos) <= 0 {
|
||||
return "", fmt.Errorf("missing vtxos")
|
||||
@@ -412,6 +418,16 @@ func (b *txBuilder) BuildAsyncPaymentTransactions(
|
||||
|
||||
redeemTxWeightEstimator := &input.TxWeightEstimator{}
|
||||
for index, vtxo := range vtxos {
|
||||
desc, ok := descriptors[vtxo.VtxoKey]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("missing descriptor for vtxo %s", vtxo.VtxoKey)
|
||||
}
|
||||
|
||||
forfeitLeafHash, ok := forfeitsLeaves[vtxo.VtxoKey]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("missing forfeit leaf hash for vtxo %s", vtxo.VtxoKey)
|
||||
}
|
||||
|
||||
if vtxo.Spent || vtxo.Redeemed || vtxo.Swept {
|
||||
return "", fmt.Errorf("all vtxos must be unspent")
|
||||
}
|
||||
@@ -426,7 +442,7 @@ func (b *txBuilder) BuildAsyncPaymentTransactions(
|
||||
Index: vtxo.VOut,
|
||||
}
|
||||
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(vtxo.Descriptor)
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(desc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -446,41 +462,27 @@ func (b *txBuilder) BuildAsyncPaymentTransactions(
|
||||
PkScript: vtxoOutputScript,
|
||||
}
|
||||
|
||||
if defaultVtxoScript, ok := vtxoScript.(*bitcointree.DefaultVtxoScript); ok {
|
||||
forfeitLeaf := bitcointree.MultisigClosure{
|
||||
Pubkey: defaultVtxoScript.Owner,
|
||||
AspPubkey: defaultVtxoScript.Asp,
|
||||
}
|
||||
|
||||
tapLeaf, err := forfeitLeaf.Leaf()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
leafProof, err := vtxoTree.GetTaprootMerkleProof(tapLeaf.TapHash())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tapscripts[index] = &psbt.TaprootTapLeafScript{
|
||||
ControlBlock: leafProof.ControlBlock,
|
||||
Script: leafProof.Script,
|
||||
LeafVersion: txscript.BaseLeafVersion,
|
||||
}
|
||||
|
||||
ctrlBlock, err := txscript.ParseControlBlock(leafProof.ControlBlock)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
redeemTxWeightEstimator.AddTapscriptInput(64*2, &waddrmgr.Tapscript{
|
||||
RevealedScript: leafProof.Script,
|
||||
ControlBlock: ctrlBlock,
|
||||
})
|
||||
} else {
|
||||
return "", fmt.Errorf("vtxo %s:%d script is not default script, can't be async spent", vtxo.Txid, vtxo.VOut)
|
||||
leafProof, err := vtxoTree.GetTaprootMerkleProof(forfeitLeafHash)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tapscripts[index] = &psbt.TaprootTapLeafScript{
|
||||
ControlBlock: leafProof.ControlBlock,
|
||||
Script: leafProof.Script,
|
||||
LeafVersion: txscript.BaseLeafVersion,
|
||||
}
|
||||
|
||||
ctrlBlock, err := txscript.ParseControlBlock(leafProof.ControlBlock)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
redeemTxWeightEstimator.AddTapscriptInput(64*2+40, &waddrmgr.Tapscript{
|
||||
RevealedScript: leafProof.Script,
|
||||
ControlBlock: ctrlBlock,
|
||||
})
|
||||
|
||||
ins = append(ins, vtxoOutpoint)
|
||||
}
|
||||
|
||||
@@ -498,17 +500,21 @@ func (b *txBuilder) BuildAsyncPaymentTransactions(
|
||||
}
|
||||
|
||||
for i, receiver := range receivers {
|
||||
offchainScript, err := bitcointree.ParseVtxoScript(receiver.Descriptor)
|
||||
if receiver.IsOnchain() {
|
||||
return "", fmt.Errorf("receiver %d is onchain", i)
|
||||
}
|
||||
|
||||
pubkeyBytes, err := hex.DecodeString(receiver.Pubkey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
receiverVtxoTaprootKey, _, err := offchainScript.TapTree()
|
||||
pubkey, err := schnorr.ParsePubKey(pubkeyBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
newVtxoScript, err := common.P2TRScript(receiverVtxoTaprootKey)
|
||||
newVtxoScript, err := common.P2TRScript(pubkey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -588,7 +594,6 @@ func (b *txBuilder) createRoundTx(
|
||||
|
||||
connectorAmount := dustLimit
|
||||
|
||||
receivers := getOnchainReceivers(payments)
|
||||
nbOfInputs := countSpentVtxos(payments)
|
||||
connectorsAmount := (connectorAmount + connectorMinRelayFee) * nbOfInputs
|
||||
if nbOfInputs > 1 {
|
||||
@@ -614,25 +619,17 @@ func (b *txBuilder) createRoundTx(
|
||||
})
|
||||
}
|
||||
|
||||
for _, receiver := range receivers {
|
||||
targetAmount += receiver.Amount
|
||||
|
||||
receiverAddr, err := btcutil.DecodeAddress(receiver.OnchainAddress, b.onchainNetwork())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receiverScript, err := txscript.PayToAddrScript(receiverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputs = append(outputs, &wire.TxOut{
|
||||
Value: int64(receiver.Amount),
|
||||
PkScript: receiverScript,
|
||||
})
|
||||
onchainOutputs, err := getOnchainOutputs(payments, b.onchainNetwork())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, output := range onchainOutputs {
|
||||
targetAmount += uint64(output.Value)
|
||||
}
|
||||
|
||||
outputs = append(outputs, onchainOutputs...)
|
||||
|
||||
for _, input := range boardingInputs {
|
||||
targetAmount -= input.Amount
|
||||
}
|
||||
@@ -1016,6 +1013,7 @@ func (b *txBuilder) minRelayFeeTreeTx() (uint64, error) {
|
||||
|
||||
func (b *txBuilder) createForfeitTxs(
|
||||
payments []domain.Payment,
|
||||
descriptors map[domain.VtxoKey]string,
|
||||
connectors []*psbt.Packet,
|
||||
minRelayFeeRate chainfee.SatPerKVByte,
|
||||
) ([]string, error) {
|
||||
@@ -1042,7 +1040,12 @@ func (b *txBuilder) createForfeitTxs(
|
||||
forfeitTxs := make([]string, 0)
|
||||
for _, payment := range payments {
|
||||
for _, vtxo := range payment.Inputs {
|
||||
offchainscript, err := bitcointree.ParseVtxoScript(vtxo.Descriptor)
|
||||
desc, ok := descriptors[vtxo.VtxoKey]
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
offchainscript, err := bitcointree.ParseVtxoScript(desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ func TestBuildForfeitTxs(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
for _, f := range fixtures.Valid {
|
||||
connectors, forfeitTxs, err := builder.BuildForfeitTxs(
|
||||
f.PoolTx, f.Payments, minRelayFeeRate,
|
||||
f.PoolTx, f.Payments, f.Descriptors, minRelayFeeRate,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, connectors, f.ExpectedNumOfConnectors)
|
||||
@@ -161,7 +161,7 @@ func TestBuildForfeitTxs(t *testing.T) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
for _, f := range fixtures.Invalid {
|
||||
connectors, forfeitTxs, err := builder.BuildForfeitTxs(
|
||||
f.PoolTx, f.Payments, minRelayFeeRate,
|
||||
f.PoolTx, f.Payments, f.Descriptors, minRelayFeeRate,
|
||||
)
|
||||
require.EqualError(t, err, f.ExpectedErr)
|
||||
require.Empty(t, connectors)
|
||||
@@ -225,6 +225,7 @@ func parsePoolTxFixtures() (*poolTxFixtures, error) {
|
||||
type forfeitTxsFixtures struct {
|
||||
Valid []struct {
|
||||
Payments []domain.Payment
|
||||
Descriptors map[domain.VtxoKey]string
|
||||
ExpectedNumOfConnectors int
|
||||
ExpectedNumOfForfeitTxs int
|
||||
PoolTx string
|
||||
@@ -232,6 +233,7 @@ type forfeitTxsFixtures struct {
|
||||
}
|
||||
Invalid []struct {
|
||||
Payments []domain.Payment
|
||||
Descriptors map[domain.VtxoKey]string
|
||||
ExpectedErr string
|
||||
PoolTx string
|
||||
}
|
||||
@@ -254,5 +256,41 @@ func parseForfeitTxsFixtures() (*forfeitTxsFixtures, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
valid := vv["valid"].([]interface{})
|
||||
for i, v := range valid {
|
||||
val := v.(map[string]interface{})
|
||||
payments := val["payments"].([]interface{})
|
||||
descriptors := make(map[domain.VtxoKey]string)
|
||||
for _, p := range payments {
|
||||
inputs := p.(map[string]interface{})["inputs"].([]interface{})
|
||||
for _, in := range inputs {
|
||||
inMap := in.(map[string]interface{})
|
||||
descriptors[domain.VtxoKey{
|
||||
Txid: inMap["txid"].(string),
|
||||
VOut: uint32(inMap["vout"].(float64)),
|
||||
}] = inMap["descriptor"].(string)
|
||||
}
|
||||
}
|
||||
fixtures.Valid[i].Descriptors = descriptors
|
||||
}
|
||||
|
||||
invalid := vv["invalid"].([]interface{})
|
||||
for i, v := range invalid {
|
||||
val := v.(map[string]interface{})
|
||||
payments := val["payments"].([]interface{})
|
||||
descriptors := make(map[domain.VtxoKey]string)
|
||||
for _, p := range payments {
|
||||
inputs := p.(map[string]interface{})["inputs"].([]interface{})
|
||||
for _, in := range inputs {
|
||||
inMap := in.(map[string]interface{})
|
||||
descriptors[domain.VtxoKey{
|
||||
Txid: inMap["txid"].(string),
|
||||
VOut: uint32(inMap["vout"].(float64)),
|
||||
}] = inMap["descriptor"].(string)
|
||||
}
|
||||
}
|
||||
fixtures.Invalid[i].Descriptors = descriptors
|
||||
}
|
||||
|
||||
return &fixtures, nil
|
||||
}
|
||||
|
||||
@@ -9,13 +9,14 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1100
|
||||
}
|
||||
]
|
||||
@@ -32,17 +33,18 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -59,17 +61,18 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -80,17 +83,18 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -101,17 +105,18 @@
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -128,54 +133,58 @@
|
||||
{
|
||||
"txid": "755c820771284d85ea4bbcc246565b4eddadc44237a7e57a0f9cb78a840d1d41",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "66a0df86fcdeb84b8877adfe0b2c556dba30305d72ddbd4c49355f6930355357",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "9913159bc7aa493ca53cbb9cbc88f97ba01137c814009dc7ef520c3fafc67909",
|
||||
"vout": 1,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 500
|
||||
},
|
||||
{
|
||||
"txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60",
|
||||
"vout": 1,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1000
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -197,53 +206,58 @@
|
||||
{
|
||||
"txid": "755c820771284d85ea4bbcc246565b4eddadc44237a7e57a0f9cb78a840d1d41",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "66a0df86fcdeb84b8877adfe0b2c556dba30305d72ddbd4c49355f6930355357",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "9913159bc7aa493ca53cbb9cbc88f97ba01137c814009dc7ef520c3fafc67909",
|
||||
"vout": 1,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 500
|
||||
},
|
||||
{
|
||||
"txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60",
|
||||
"vout": 0,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60",
|
||||
"vout": 1,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"amount": 1000
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })",
|
||||
"pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
@@ -252,7 +266,7 @@
|
||||
"poolTx": "cHNidP8BALICAAAAAnonOnsJBkHUUaKf/7fdS0/sVyBCgDPusYzGSZZiXPbtAAAAAAD/////VLtr81ZII3QJnXgrIwgcnbsq3aa4L3qdHOAn2evlFtEAAAAAAP////8CohIAAAAAAAAiUSBZarBUuSIHnlkuIoel9MmvexqTGK8jCZaRjt8L+Pb3s+gDAAAAAAAAIlEgI95L4kHEn2fAA+vysD+RIR4eD3AIQwc+FyCInJ8HivYAAAAAAAEBIOgDAAAAAAAAF6kU6p9IboLvs92Dpp/Zbj8BE3V9oDyHAAEBIOgDAAAAAAAAF6kU6p9IboLvs92Dpp/Zbj8BE3V9oDyHAAAA",
|
||||
"poolTxid": "7c0c10756cdb9ab8e605f1c82e25989761308cf4c60e6a6f42b72d46144c4ce0",
|
||||
"expectedNumOfForfeitTxs": 25,
|
||||
"expectedNumOfConnectors": 4
|
||||
"expectedNumOfConnectors": 4
|
||||
}
|
||||
],
|
||||
"invalid": []
|
||||
|
||||
@@ -1,47 +1,58 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"github.com/ark-network/ark/common/bitcointree"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/server/internal/core/domain"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
)
|
||||
|
||||
func getOnchainReceivers(
|
||||
payments []domain.Payment,
|
||||
) []domain.Receiver {
|
||||
receivers := make([]domain.Receiver, 0)
|
||||
func getOnchainOutputs(
|
||||
payments []domain.Payment, network *chaincfg.Params,
|
||||
) ([]*wire.TxOut, error) {
|
||||
outputs := make([]*wire.TxOut, 0)
|
||||
for _, payment := range payments {
|
||||
for _, receiver := range payment.Receivers {
|
||||
if receiver.IsOnchain() {
|
||||
receivers = append(receivers, receiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
return receivers
|
||||
}
|
||||
|
||||
func getOffchainReceivers(
|
||||
payments []domain.Payment,
|
||||
) ([]bitcointree.Receiver, error) {
|
||||
receivers := make([]bitcointree.Receiver, 0)
|
||||
for _, payment := range payments {
|
||||
for _, receiver := range payment.Receivers {
|
||||
if !receiver.IsOnchain() {
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(receiver.Descriptor)
|
||||
receiverAddr, err := btcutil.DecodeAddress(receiver.OnchainAddress, network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receivers = append(receivers, bitcointree.Receiver{
|
||||
Script: vtxoScript,
|
||||
receiverScript, err := txscript.PayToAddrScript(receiverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputs = append(outputs, &wire.TxOut{
|
||||
Value: int64(receiver.Amount),
|
||||
PkScript: receiverScript,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return outputs, nil
|
||||
}
|
||||
|
||||
func getOutputVtxosLeaves(
|
||||
payments []domain.Payment,
|
||||
) ([]tree.VtxoLeaf, error) {
|
||||
leaves := make([]tree.VtxoLeaf, 0)
|
||||
for _, payment := range payments {
|
||||
for _, receiver := range payment.Receivers {
|
||||
if !receiver.IsOnchain() {
|
||||
leaves = append(leaves, tree.VtxoLeaf{
|
||||
Pubkey: receiver.Pubkey,
|
||||
Amount: receiver.Amount,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return receivers, nil
|
||||
return leaves, nil
|
||||
}
|
||||
|
||||
func countSpentVtxos(payments []domain.Payment) uint64 {
|
||||
|
||||
@@ -338,7 +338,7 @@ func (h *handler) Ping(
|
||||
func (h *handler) CreatePayment(
|
||||
ctx context.Context, req *arkv1.CreatePaymentRequest,
|
||||
) (*arkv1.CreatePaymentResponse, error) {
|
||||
inputs, err := parseInputs(req.GetInputs())
|
||||
inputs, err := parseAsyncPaymentInputs(req.GetInputs())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
@@ -353,12 +353,12 @@ func (h *handler) CreatePayment(
|
||||
return nil, status.Error(codes.InvalidArgument, "output amount must be greater than 0")
|
||||
}
|
||||
|
||||
if len(receiver.OnchainAddress) > 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "onchain address is not supported as async payment destination")
|
||||
if len(receiver.OnchainAddress) <= 0 && len(receiver.Pubkey) <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing address")
|
||||
}
|
||||
|
||||
if len(receiver.Descriptor) <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing output descriptor")
|
||||
if receiver.IsOnchain() {
|
||||
return nil, status.Error(codes.InvalidArgument, "onchain outputs are not supported as async payment destination")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,12 +462,12 @@ func (h *handler) GetRoundById(
|
||||
func (h *handler) ListVtxos(
|
||||
ctx context.Context, req *arkv1.ListVtxosRequest,
|
||||
) (*arkv1.ListVtxosResponse, error) {
|
||||
_, userPubkey, _, err := parseAddress(req.GetAddress())
|
||||
_, err := parseAddress(req.GetAddress())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
spendableVtxos, spentVtxos, err := h.svc.ListVtxos(ctx, userPubkey)
|
||||
spendableVtxos, spentVtxos, err := h.svc.ListVtxos(ctx, req.GetAddress())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,25 +1,55 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/server/internal/core/application"
|
||||
"github.com/ark-network/ark/server/internal/core/domain"
|
||||
"github.com/ark-network/ark/server/internal/core/ports"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// From interface type to app type
|
||||
|
||||
func parseAddress(addr string) (string, *secp256k1.PublicKey, *secp256k1.PublicKey, error) {
|
||||
func parseAddress(addr string) (*common.Address, error) {
|
||||
if len(addr) <= 0 {
|
||||
return "", nil, nil, fmt.Errorf("missing address")
|
||||
return nil, fmt.Errorf("missing address")
|
||||
}
|
||||
return common.DecodeAddress(addr)
|
||||
}
|
||||
|
||||
func parseAsyncPaymentInputs(ins []*arkv1.AsyncPaymentInput) ([]application.AsyncPaymentInput, error) {
|
||||
if len(ins) <= 0 {
|
||||
return nil, fmt.Errorf("missing inputs")
|
||||
}
|
||||
|
||||
inputs := make([]application.AsyncPaymentInput, 0, len(ins))
|
||||
for _, input := range ins {
|
||||
forfeitLeafHash, err := chainhash.NewHashFromStr(input.GetForfeitLeafHash())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid forfeit leaf hash: %s", err)
|
||||
}
|
||||
|
||||
inputs = append(inputs, application.AsyncPaymentInput{
|
||||
Input: ports.Input{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: input.GetInput().GetOutpoint().GetTxid(),
|
||||
VOut: input.GetInput().GetOutpoint().GetVout(),
|
||||
},
|
||||
Descriptor: input.GetInput().GetDescriptor_(),
|
||||
},
|
||||
ForfeitLeafHash: *forfeitLeafHash,
|
||||
})
|
||||
}
|
||||
|
||||
return inputs, nil
|
||||
}
|
||||
|
||||
func parseInputs(ins []*arkv1.Input) ([]ports.Input, error) {
|
||||
if len(ins) <= 0 {
|
||||
return nil, fmt.Errorf("missing inputs")
|
||||
@@ -39,26 +69,43 @@ func parseInputs(ins []*arkv1.Input) ([]ports.Input, error) {
|
||||
return inputs, nil
|
||||
}
|
||||
|
||||
func parseReceiver(out *arkv1.Output) (domain.Receiver, error) {
|
||||
decodedAddr, err := common.DecodeAddress(out.GetAddress())
|
||||
if err != nil {
|
||||
// onchain address
|
||||
return domain.Receiver{
|
||||
Amount: out.GetAmount(),
|
||||
OnchainAddress: out.GetAddress(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return domain.Receiver{
|
||||
Amount: out.GetAmount(),
|
||||
Pubkey: hex.EncodeToString(schnorr.SerializePubKey(decodedAddr.VtxoTapKey)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseReceivers(outs []*arkv1.Output) ([]domain.Receiver, error) {
|
||||
receivers := make([]domain.Receiver, 0, len(outs))
|
||||
for _, out := range outs {
|
||||
if out.GetAmount() == 0 {
|
||||
return nil, fmt.Errorf("missing output amount")
|
||||
}
|
||||
if len(out.GetAddress()) <= 0 && len(out.GetDescriptor_()) <= 0 {
|
||||
if len(out.GetAddress()) <= 0 {
|
||||
return nil, fmt.Errorf("missing output destination")
|
||||
}
|
||||
|
||||
receivers = append(receivers, domain.Receiver{
|
||||
Descriptor: out.GetDescriptor_(),
|
||||
Amount: out.GetAmount(),
|
||||
OnchainAddress: out.GetAddress(),
|
||||
})
|
||||
rcv, err := parseReceiver(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receivers = append(receivers, rcv)
|
||||
}
|
||||
return receivers, nil
|
||||
}
|
||||
|
||||
// From app typeto interface type
|
||||
// From app type to interface type
|
||||
|
||||
type vtxoList []domain.Vtxo
|
||||
|
||||
@@ -70,15 +117,15 @@ func (v vtxoList) toProto() []*arkv1.Vtxo {
|
||||
Txid: vv.Txid,
|
||||
Vout: vv.VOut,
|
||||
},
|
||||
Descriptor_: vv.Descriptor,
|
||||
Amount: vv.Amount,
|
||||
RoundTxid: vv.RoundTxid,
|
||||
Spent: vv.Spent,
|
||||
ExpireAt: vv.ExpireAt,
|
||||
SpentBy: vv.SpentBy,
|
||||
Swept: vv.Swept,
|
||||
RedeemTx: vv.RedeemTx,
|
||||
Pending: vv.Pending,
|
||||
Amount: vv.Amount,
|
||||
RoundTxid: vv.RoundTxid,
|
||||
Spent: vv.Spent,
|
||||
ExpireAt: vv.ExpireAt,
|
||||
SpentBy: vv.SpentBy,
|
||||
Swept: vv.Swept,
|
||||
RedeemTx: vv.RedeemTx,
|
||||
Pending: vv.Pending,
|
||||
Pubkey: vv.Pubkey,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user