[Server] Validate forfeit txs without re-building them (#382)

* compute forfeit partial tx client-side first

* fix conflict

* go work sync

* move verify sig in VerifyForfeits

* move check after len(inputs)
This commit is contained in:
Louis Singer
2024-11-18 19:08:10 +01:00
committed by GitHub
parent 6ed4e30b6d
commit 0d2db92173
14 changed files with 712 additions and 729 deletions

View File

@@ -463,7 +463,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 {
@@ -520,7 +520,24 @@ 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
vtxoScript, err := bitcointree.ParseVtxoScript(input.Descriptor)
if err != nil {
return "", fmt.Errorf("failed to parse boarding descriptor: %s", err)
}
tapKey, _, err := vtxoScript.TapTree()
if err != nil {
return "", fmt.Errorf("failed to get taproot key: %s", err)
}
expectedTapKey, err := vtxo.TapKey()
if err != nil {
return "", fmt.Errorf("failed to get taproot key: %s", err)
}
if !bytes.Equal(schnorr.SerializePubKey(tapKey), schnorr.SerializePubKey(expectedTapKey)) {
return "", fmt.Errorf("descriptor does not match vtxo pubkey")
}
vtxosInputs = append(vtxosInputs, vtxo)
}
@@ -529,7 +546,7 @@ func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Inp
if err != nil {
return "", err
}
if err := s.paymentRequests.push(*payment, boardingInputs, descriptors); err != nil {
if err := s.paymentRequests.push(*payment, boardingInputs); err != nil {
return "", err
}
return payment.Id, nil
@@ -872,7 +889,7 @@ func (s *covenantlessService) startFinalization() {
if num > paymentsThreshold {
num = paymentsThreshold
}
payments, boardingInputs, descriptors, cosigners, paymentsNotes := s.paymentRequests.pop(num)
payments, boardingInputs, cosigners, paymentsNotes := 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))
@@ -904,13 +921,21 @@ func (s *covenantlessService) startFinalization() {
cosigners = append(cosigners, ephemeralKey.PubKey())
unsignedRoundTx, tree, connectorAddress, err := s.builder.BuildRoundTx(s.pubkey, payments, boardingInputs, sweptRounds, cosigners...)
unsignedRoundTx, tree, connectorAddress, connectors, err := s.builder.BuildRoundTx(
s.pubkey,
payments,
boardingInputs,
sweptRounds,
cosigners...,
)
if err != nil {
round.Fail(fmt.Errorf("failed to create pool tx: %s", err))
log.WithError(err).Warn("failed to create pool tx")
round.Fail(fmt.Errorf("failed to create round tx: %s", err))
log.WithError(err).Warn("failed to create round tx")
return
}
log.Debugf("pool tx created for round %s", round.Id)
log.Debugf("round tx created for round %s", round.Id)
s.forfeitTxs.init(connectors, payments)
if len(tree) > 0 {
log.Debugf("signing congestion tree for round %s", round.Id)
@@ -1063,34 +1088,6 @@ func (s *covenantlessService) startFinalization() {
tree = signedTree
}
needForfeits := false
for _, pay := range payments {
if len(pay.Inputs) > 0 {
needForfeits = true
break
}
}
var forfeitTxs, connectors []string
minRelayFeeRate := s.wallet.MinRelayFeeRate(ctx)
if needForfeits {
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")
return
}
log.Debugf("forfeit transactions created for round %s", round.Id)
if err := s.forfeitTxs.push(forfeitTxs); err != nil {
round.Fail(fmt.Errorf("failed to store forfeit txs: %s", err))
log.WithError(err).Warn("failed to store forfeit txs")
return
}
}
if _, err := round.StartFinalization(
connectorAddress, connectors, tree, unsignedRoundTx,
); err != nil {
@@ -1143,9 +1140,8 @@ func (s *covenantlessService) finalizeRound(notes []note.Note) {
}
}()
forfeitTxs, leftUnsigned := s.forfeitTxs.pop()
if len(leftUnsigned) > 0 {
err := fmt.Errorf("%d forfeit txs left to sign", len(leftUnsigned))
forfeitTxs, err := s.forfeitTxs.pop()
if err != nil {
changes = round.Fail(fmt.Errorf("failed to finalize round: %s", err))
log.WithError(err).Warn("failed to finalize round")
return