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:
Louis Singer
2024-10-24 17:43:27 +02:00
committed by GitHub
parent b536a9e652
commit bcb2b2075f
41 changed files with 1103 additions and 1390 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -123,5 +123,4 @@ type Vtxo struct {
Swept bool
ExpireAt int64
RedeemTx string // empty if in-round vtxo
Pending bool
}

View File

@@ -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)
);

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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,
}
}

View File

@@ -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
}

View File

@@ -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,
})
}

View File

@@ -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)

View File

@@ -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)