mirror of
https://github.com/aljazceru/ark.git
synced 2026-02-23 12:12:49 +01:00
Add support for Out Of Round txs (#359)
* [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 * OOR scheme * fix conflicts * [sdk] OOR * update WASM wrappers * revert renaming * revert API changes * update parser.go * fix vtxosToTxsCovenantless * add settled and spent in Utxo and Transaction * Fixes (#5) * Revert unneeded changes and rename claim to settle * Revert changes to wasm and rename claim to settle --------- Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
This commit is contained in:
@@ -23,8 +23,8 @@ help:
|
||||
## intergrationtest: runs integration tests
|
||||
integrationtest:
|
||||
@echo "Running integration tests..."
|
||||
@go test -v -count 1 -timeout 400s github.com/ark-network/ark/server/test/e2e/covenant
|
||||
@go test -v -count 1 -timeout 400s github.com/ark-network/ark/server/test/e2e/covenantless
|
||||
@go test -v -count 1 -timeout 500s github.com/ark-network/ark/server/test/e2e/covenant
|
||||
@go test -v -count 1 -timeout 500s github.com/ark-network/ark/server/test/e2e/covenantless
|
||||
|
||||
## lint: lint codebase
|
||||
lint:
|
||||
|
||||
@@ -51,10 +51,7 @@ type covenantlessService struct {
|
||||
currentRoundLock sync.Mutex
|
||||
currentRound *domain.Round
|
||||
treeSigningSessions map[string]*musigSigningSession
|
||||
asyncPaymentsCache map[string]struct { // redeem txid -> receivers
|
||||
receivers []domain.Receiver
|
||||
expireAt int64
|
||||
}
|
||||
asyncPaymentsCache map[string]asyncPaymentData
|
||||
}
|
||||
|
||||
func NewCovenantlessService(
|
||||
@@ -69,11 +66,6 @@ func NewCovenantlessService(
|
||||
return nil, fmt.Errorf("failed to fetch pubkey: %s", err)
|
||||
}
|
||||
|
||||
asyncPaymentsCache := make(map[string]struct {
|
||||
receivers []domain.Receiver
|
||||
expireAt int64
|
||||
})
|
||||
|
||||
svc := &covenantlessService{
|
||||
network: network,
|
||||
pubkey: pubkey,
|
||||
@@ -90,7 +82,7 @@ func NewCovenantlessService(
|
||||
eventsCh: make(chan domain.RoundEvent),
|
||||
transactionEventsCh: make(chan TransactionEvent),
|
||||
currentRoundLock: sync.Mutex{},
|
||||
asyncPaymentsCache: asyncPaymentsCache,
|
||||
asyncPaymentsCache: make(map[string]asyncPaymentData),
|
||||
treeSigningSessions: make(map[string]*musigSigningSession),
|
||||
boardingExitDelay: boardingExitDelay,
|
||||
}
|
||||
@@ -269,19 +261,16 @@ func (s *covenantlessService) CompleteAsyncPayment(
|
||||
|
||||
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),
|
||||
},
|
||||
Pubkey: vtxoPubkey,
|
||||
Amount: uint64(out.Value),
|
||||
ExpireAt: asyncPayData.expireAt,
|
||||
RedeemTx: redeemTx,
|
||||
Pending: isPending,
|
||||
Pubkey: vtxoPubkey,
|
||||
Amount: uint64(out.Value),
|
||||
ExpireAt: asyncPayData.expireAt,
|
||||
RoundTxid: asyncPayData.roundTxid,
|
||||
RedeemTx: redeemTx,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -338,6 +327,8 @@ func (s *covenantlessService) CreateAsyncPayment(
|
||||
vtxosInputs := make([]domain.Vtxo, 0, len(inputs))
|
||||
|
||||
expiration := vtxos[0].ExpireAt
|
||||
roundTxid := vtxos[0].RoundTxid
|
||||
|
||||
for _, vtxo := range vtxos {
|
||||
if vtxo.Spent {
|
||||
return "", fmt.Errorf("all vtxos must be unspent")
|
||||
@@ -350,11 +341,9 @@ func (s *covenantlessService) CreateAsyncPayment(
|
||||
if vtxo.Swept {
|
||||
return "", fmt.Errorf("all vtxos must be swept")
|
||||
}
|
||||
if vtxo.Pending {
|
||||
return "", fmt.Errorf("all vtxos must be claimed")
|
||||
}
|
||||
|
||||
if vtxo.ExpireAt < expiration {
|
||||
roundTxid = vtxo.RoundTxid
|
||||
expiration = vtxo.ExpireAt
|
||||
}
|
||||
|
||||
@@ -373,12 +362,10 @@ func (s *covenantlessService) CreateAsyncPayment(
|
||||
return "", fmt.Errorf("failed to parse redeem tx: %s", err)
|
||||
}
|
||||
|
||||
s.asyncPaymentsCache[redeemPtx.UnsignedTx.TxID()] = struct {
|
||||
receivers []domain.Receiver
|
||||
expireAt int64
|
||||
}{
|
||||
s.asyncPaymentsCache[redeemPtx.UnsignedTx.TxID()] = asyncPaymentData{
|
||||
receivers: receivers,
|
||||
expireAt: expiration,
|
||||
roundTxid: roundTxid,
|
||||
}
|
||||
|
||||
return redeemTx, nil
|
||||
@@ -1588,6 +1575,12 @@ func findForfeitTxBitcoin(
|
||||
return "", fmt.Errorf("forfeit tx not found")
|
||||
}
|
||||
|
||||
type asyncPaymentData struct {
|
||||
receivers []domain.Receiver
|
||||
expireAt int64
|
||||
roundTxid string
|
||||
}
|
||||
|
||||
// musigSigningSession holds the state of ephemeral nonces and signatures in order to coordinate the signing of the tree
|
||||
type musigSigningSession struct {
|
||||
lock sync.Mutex
|
||||
|
||||
@@ -166,6 +166,24 @@ func (m *paymentsMap) update(payment domain.Payment) error {
|
||||
return fmt.Errorf("payment %s not found", payment.Id)
|
||||
}
|
||||
|
||||
sumOfInputs := uint64(0)
|
||||
for _, input := range payment.Inputs {
|
||||
sumOfInputs += input.Amount
|
||||
}
|
||||
|
||||
for _, boardingInput := range p.boardingInputs {
|
||||
sumOfInputs += boardingInput.Amount
|
||||
}
|
||||
|
||||
sumOfOutputs := uint64(0)
|
||||
for _, receiver := range payment.Receivers {
|
||||
sumOfOutputs += receiver.Amount
|
||||
}
|
||||
|
||||
if sumOfInputs != sumOfOutputs {
|
||||
return fmt.Errorf("sum of inputs %d does not match sum of outputs %d", sumOfInputs, sumOfOutputs)
|
||||
}
|
||||
|
||||
p.Payment = payment
|
||||
|
||||
return nil
|
||||
|
||||
@@ -123,5 +123,4 @@ type Vtxo struct {
|
||||
Swept bool
|
||||
ExpireAt int64
|
||||
RedeemTx string // empty if in-round vtxo
|
||||
Pending bool
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ 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)
|
||||
);
|
||||
|
||||
@@ -33,7 +33,6 @@ type PaymentVtxoVw struct {
|
||||
ExpireAt sql.NullInt64
|
||||
PaymentID sql.NullString
|
||||
RedeemTx sql.NullString
|
||||
Pending sql.NullBool
|
||||
}
|
||||
|
||||
type Receiver struct {
|
||||
@@ -100,5 +99,4 @@ type Vtxo struct {
|
||||
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.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
|
||||
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 FROM vtxo
|
||||
WHERE redeemed = false
|
||||
`
|
||||
|
||||
@@ -84,7 +84,6 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.Vtxo.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -100,7 +99,7 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
|
||||
}
|
||||
|
||||
const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many
|
||||
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
|
||||
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 FROM vtxo
|
||||
WHERE redeemed = false AND pubkey = ?
|
||||
`
|
||||
|
||||
@@ -130,7 +129,6 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, pubkey s
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.Vtxo.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -209,7 +207,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
|
||||
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.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
|
||||
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
|
||||
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
|
||||
@@ -275,7 +273,6 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
&i.PaymentVtxoVw.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -295,7 +292,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
|
||||
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.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
|
||||
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
|
||||
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
|
||||
@@ -361,7 +358,6 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
&i.PaymentVtxoVw.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -381,7 +377,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
|
||||
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.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
|
||||
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
|
||||
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
|
||||
@@ -447,7 +443,6 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
&i.PaymentVtxoVw.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -463,7 +458,7 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
|
||||
}
|
||||
|
||||
const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many
|
||||
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
|
||||
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 FROM vtxo
|
||||
WHERE redeemed = false AND swept = false
|
||||
`
|
||||
|
||||
@@ -493,7 +488,6 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.Vtxo.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -513,7 +507,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
|
||||
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.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
|
||||
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
|
||||
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
|
||||
@@ -579,7 +573,6 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
&i.PaymentVtxoVw.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -595,7 +588,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
|
||||
}
|
||||
|
||||
const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one
|
||||
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
|
||||
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 FROM vtxo
|
||||
WHERE txid = ? AND vout = ?
|
||||
`
|
||||
|
||||
@@ -624,13 +617,12 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.Vtxo.Pending,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const selectVtxosByPoolTxid = `-- name: SelectVtxosByPoolTxid :many
|
||||
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
|
||||
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 FROM vtxo
|
||||
WHERE pool_tx = ?
|
||||
`
|
||||
|
||||
@@ -660,7 +652,6 @@ func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]S
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.Vtxo.Pending,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -847,8 +838,8 @@ func (q *Queries) UpsertTransaction(ctx context.Context, arg UpsertTransactionPa
|
||||
}
|
||||
|
||||
const upsertVtxo = `-- name: UpsertVtxo :exec
|
||||
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
|
||||
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
|
||||
pubkey = EXCLUDED.pubkey,
|
||||
amount = EXCLUDED.amount,
|
||||
pool_tx = EXCLUDED.pool_tx,
|
||||
@@ -857,8 +848,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SE
|
||||
redeemed = EXCLUDED.redeemed,
|
||||
swept = EXCLUDED.swept,
|
||||
expire_at = EXCLUDED.expire_at,
|
||||
redeem_tx = EXCLUDED.redeem_tx,
|
||||
pending = EXCLUDED.pending
|
||||
redeem_tx = EXCLUDED.redeem_tx
|
||||
`
|
||||
|
||||
type UpsertVtxoParams struct {
|
||||
@@ -873,7 +863,6 @@ type UpsertVtxoParams struct {
|
||||
Swept bool
|
||||
ExpireAt int64
|
||||
RedeemTx sql.NullString
|
||||
Pending bool
|
||||
}
|
||||
|
||||
func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error {
|
||||
@@ -889,7 +878,6 @@ func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error {
|
||||
arg.Swept,
|
||||
arg.ExpireAt,
|
||||
arg.RedeemTx,
|
||||
arg.Pending,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ SELECT id FROM round WHERE starting_timestamp > ? AND starting_timestamp < ?;
|
||||
SELECT id FROM round;
|
||||
|
||||
-- name: UpsertVtxo :exec
|
||||
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
|
||||
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
|
||||
pubkey = EXCLUDED.pubkey,
|
||||
amount = EXCLUDED.amount,
|
||||
pool_tx = EXCLUDED.pool_tx,
|
||||
@@ -122,8 +122,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SE
|
||||
redeemed = EXCLUDED.redeemed,
|
||||
swept = EXCLUDED.swept,
|
||||
expire_at = EXCLUDED.expire_at,
|
||||
redeem_tx = EXCLUDED.redeem_tx,
|
||||
pending = EXCLUDED.pending;
|
||||
redeem_tx = EXCLUDED.redeem_tx;
|
||||
|
||||
-- name: SelectSweepableVtxos :many
|
||||
SELECT sqlc.embed(vtxo) FROM vtxo
|
||||
|
||||
@@ -51,7 +51,6 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro
|
||||
Swept: vtxo.Swept,
|
||||
ExpireAt: vtxo.ExpireAt,
|
||||
RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true},
|
||||
Pending: vtxo.Pending,
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
@@ -259,7 +258,6 @@ func rowToVtxo(row queries.Vtxo) domain.Vtxo {
|
||||
Swept: row.Swept,
|
||||
ExpireAt: row.ExpireAt,
|
||||
RedeemTx: row.RedeemTx.String,
|
||||
Pending: row.Pending,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -782,9 +782,9 @@ func (s *service) Status(ctx context.Context) (ports.WalletStatus, error) {
|
||||
|
||||
w := s.wallet.InternalWallet()
|
||||
return status{
|
||||
true,
|
||||
!w.Manager.IsLocked(),
|
||||
w.ChainSynced(),
|
||||
initialized: true,
|
||||
unlocked: !w.Manager.IsLocked(),
|
||||
synced: s.isSynced,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ func (v vtxoList) toProto() []*arkv1.Vtxo {
|
||||
SpentBy: vv.SpentBy,
|
||||
Swept: vv.Swept,
|
||||
RedeemTx: vv.RedeemTx,
|
||||
Pending: vv.Pending,
|
||||
IsOor: len(vv.RedeemTx) > 0,
|
||||
Pubkey: vv.Pubkey,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestSendOffchain(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = runArkCommand("claim", "--password", utils.Password)
|
||||
_, err = runArkCommand("settle", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
@@ -101,7 +101,7 @@ func TestUnilateralExit(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = runArkCommand("claim", "--password", utils.Password)
|
||||
_, err = runArkCommand("settle", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
@@ -142,7 +142,7 @@ func TestCollaborativeExit(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = runArkCommand("claim", "--password", utils.Password)
|
||||
_, err = runArkCommand("settle", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
@@ -164,7 +164,7 @@ func TestReactToSpentVtxosRedemption(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = client.Claim(ctx)
|
||||
_, err = client.Settle(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
@@ -216,7 +216,7 @@ func TestSweep(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = runArkCommand("claim", "--password", utils.Password)
|
||||
_, err = runArkCommand("settle", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
@@ -77,7 +77,7 @@ func TestSendOffchain(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = runClarkCommand("claim", "--password", utils.Password)
|
||||
_, err = runClarkCommand("settle", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
@@ -91,7 +91,7 @@ func TestSendOffchain(t *testing.T) {
|
||||
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
|
||||
require.NotZero(t, balance.Offchain.Total)
|
||||
|
||||
_, err = runClarkCommand("claim", "--password", utils.Password)
|
||||
_, err = runClarkCommand("settle", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
balanceStr, err = runClarkCommand("balance")
|
||||
@@ -113,7 +113,7 @@ func TestUnilateralExit(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = runClarkCommand("claim", "--password", utils.Password)
|
||||
_, err = runClarkCommand("settle", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
@@ -155,11 +155,6 @@ func TestCollaborativeExit(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = runClarkCommand("claim", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
_, err = runClarkCommand("redeem", "--amount", "1000", "--address", redeemAddress, "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -177,7 +172,7 @@ func TestReactToSpentVtxosRedemption(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = client.Claim(ctx)
|
||||
_, err = client.Settle(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = client.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)})
|
||||
@@ -217,71 +212,105 @@ func TestReactToSpentVtxosRedemption(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReactToAsyncSpentVtxosRedemption(t *testing.T) {
|
||||
t.Run("receiver claimed funds", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
sdkClient, grpcClient := setupArkSDK(t)
|
||||
defer grpcClient.Close()
|
||||
ctx := context.Background()
|
||||
sdkClient, grpcClient := setupArkSDK(t)
|
||||
defer grpcClient.Close()
|
||||
|
||||
offchainAddress, boardingAddress, err := sdkClient.Receive(ctx)
|
||||
require.NoError(t, err)
|
||||
offchainAddress, boardingAddress, err := sdkClient.Receive(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = utils.RunCommand("nigiri", "faucet", boardingAddress)
|
||||
require.NoError(t, err)
|
||||
_, err = utils.RunCommand("nigiri", "faucet", boardingAddress)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
roundId, err := sdkClient.Claim(ctx)
|
||||
require.NoError(t, err)
|
||||
roundId, err := sdkClient.Settle(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = utils.GenerateBlock()
|
||||
require.NoError(t, err)
|
||||
err = utils.GenerateBlock()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = sdkClient.SendAsync(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)})
|
||||
require.NoError(t, err)
|
||||
_, err = sdkClient.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = sdkClient.Claim(ctx)
|
||||
require.NoError(t, err)
|
||||
_, err = sdkClient.Settle(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, spentVtxos, err := sdkClient.ListVtxos(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, spentVtxos)
|
||||
_, spentVtxos, err := sdkClient.ListVtxos(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, spentVtxos)
|
||||
|
||||
var vtxo client.Vtxo
|
||||
var vtxo client.Vtxo
|
||||
|
||||
for _, v := range spentVtxos {
|
||||
if v.RoundTxid == roundId {
|
||||
vtxo = v
|
||||
break
|
||||
}
|
||||
for _, v := range spentVtxos {
|
||||
if v.RoundTxid == roundId {
|
||||
vtxo = v
|
||||
break
|
||||
}
|
||||
require.NotEmpty(t, vtxo)
|
||||
}
|
||||
require.NotEmpty(t, vtxo)
|
||||
|
||||
round, err := grpcClient.GetRound(ctx, vtxo.RoundTxid)
|
||||
round, err := grpcClient.GetRound(ctx, vtxo.RoundTxid)
|
||||
require.NoError(t, err)
|
||||
|
||||
expl := explorer.NewExplorer("http://localhost:3000", common.BitcoinRegTest)
|
||||
|
||||
branch, err := redemption.NewCovenantlessRedeemBranch(expl, round.Tree, vtxo)
|
||||
require.NoError(t, err)
|
||||
|
||||
txs, err := branch.RedeemPath()
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, tx := range txs {
|
||||
_, err := expl.Broadcast(tx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
expl := explorer.NewExplorer("http://localhost:3000", common.BitcoinRegTest)
|
||||
// give time for the ASP to detect and process the fraud
|
||||
time.Sleep(50 * time.Second)
|
||||
|
||||
branch, err := redemption.NewCovenantlessRedeemBranch(expl, round.Tree, vtxo)
|
||||
require.NoError(t, err)
|
||||
balance, err := sdkClient.Balance(ctx, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
txs, err := branch.RedeemPath()
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, balance.OnchainBalance.LockedAmount)
|
||||
}
|
||||
|
||||
for _, tx := range txs {
|
||||
_, err := expl.Broadcast(tx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
func TestChainAsyncPayments(t *testing.T) {
|
||||
var receive utils.ArkReceive
|
||||
receiveStr, err := runClarkCommand("receive")
|
||||
require.NoError(t, err)
|
||||
|
||||
// give time for the ASP to detect and process the fraud
|
||||
time.Sleep(50 * time.Second)
|
||||
err = json.Unmarshal([]byte(receiveStr), &receive)
|
||||
require.NoError(t, err)
|
||||
|
||||
balance, err := sdkClient.Balance(ctx, false)
|
||||
require.NoError(t, err)
|
||||
_, err = utils.RunCommand("nigiri", "faucet", receive.Boarding)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Empty(t, balance.OnchainBalance.LockedAmount)
|
||||
})
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = runClarkCommand("settle", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
_, err = runClarkCommand("send", "--amount", "10000", "--to", receive.Offchain, "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
var balance utils.ArkBalance
|
||||
balanceStr, err := runClarkCommand("balance")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
|
||||
require.NotZero(t, balance.Offchain.Total)
|
||||
|
||||
_, err = runClarkCommand("send", "--amount", "10000", "--to", receive.Offchain, "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
balanceStr, err = runClarkCommand("balance")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
|
||||
require.NotZero(t, balance.Offchain.Total)
|
||||
}
|
||||
|
||||
func TestAliceSeveralPaymentsToBob(t *testing.T) {
|
||||
@@ -300,9 +329,6 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = alice.Claim(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
bobAddress, _, err := bob.Receive(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -315,12 +341,6 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, bobVtxos, 1)
|
||||
|
||||
_, err = bob.Claim(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = alice.Claim(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = alice.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 10000)})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -339,7 +359,7 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, bobVtxos, 3)
|
||||
|
||||
_, err = alice.SendAsync(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 10000)})
|
||||
_, err = alice.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 10000)})
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
@@ -348,9 +368,6 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, bobVtxos, 4)
|
||||
|
||||
_, err = alice.Claim(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// bobVtxos should be unique
|
||||
uniqueVtxos := make(map[string]struct{})
|
||||
for _, v := range bobVtxos {
|
||||
@@ -358,9 +375,7 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
|
||||
}
|
||||
require.Len(t, uniqueVtxos, 4)
|
||||
|
||||
_, err = bob.Claim(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestSweep(t *testing.T) {
|
||||
@@ -376,7 +391,7 @@ func TestSweep(t *testing.T) {
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
_, err = runClarkCommand("claim", "--password", utils.Password)
|
||||
_, err = runClarkCommand("settle", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
Reference in New Issue
Block a user