Skip to content

Commit

Permalink
fix: dont try to broadcast empty transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
jackstar12 committed Apr 26, 2024
1 parent b04d533 commit ba60a73
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 84 deletions.
178 changes: 96 additions & 82 deletions boltz/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ type OutputResult struct {
Fee uint64
}

func ConstructTransaction(network *Network, currency Currency, outputs []OutputDetails, satPerVbyte float64, boltzApi *Boltz) (Transaction, []OutputResult, error) {
func ConstructTransaction(network *Network, currency Currency, outputs []OutputDetails, satPerVbyte float64, boltzApi *Boltz) (Transaction, map[string]OutputResult, error) {
construct := constructBtcTransaction
if currency == CurrencyLiquid {
construct = constructLiquidTransaction
}
results := make([]OutputResult, len(outputs))
results := make(map[string]OutputResult, len(outputs))

getOutValues := func(fee uint64) map[string]uint64 {
outValues := make(map[string]uint64)
Expand All @@ -83,20 +83,21 @@ func ConstructTransaction(network *Network, currency Currency, outputs []OutputD
for i := range outputs {
output := &outputs[i]
output.Fee = feePerOutput + feeRemainder
results[i].Fee = output.Fee
feeRemainder = 0

value, err := output.LockupTransaction.VoutValue(output.Vout)
if err != nil {
results[i].Err = err
continue
if err == nil {
if value < output.Fee {
err = fmt.Errorf("value less than fee: %d < %d", value, output.Fee)
}
}

if value < output.Fee {
results[i].Err = fmt.Errorf("value than fee: %d < %d", value, output.Fee)
if err != nil {
results[output.SwapId] = OutputResult{Err: err}
continue
}

results[output.SwapId] = OutputResult{Fee: output.Fee}
outValues[output.Address] += value - output.Fee
}
return outValues
Expand All @@ -114,94 +115,107 @@ func ConstructTransaction(network *Network, currency Currency, outputs []OutputD
return nil, nil, err
}

var retry []OutputDetails
var valid []OutputDetails

for i, output := range outputs {
if output.Cooperative {
err = func() error {
if output.Cooperative {
return nil
}
serialized, err := transaction.Serialize()
if err != nil {
return nil, nil, fmt.Errorf("could not serialize transaction: %w", err)
return fmt.Errorf("could not serialize transaction: %w", err)
}

err = func() error {
if boltzApi == nil {
return errors.New("boltzApi is required for cooperative transactions")
}

session, err := NewSigningSession(outputs[i].SwapTree)
if err != nil {
return fmt.Errorf("could not initialize signing session: %w", err)
}
if boltzApi == nil {
return errors.New("boltzApi is required for cooperative transactions")
}

pubNonce := session.PublicNonce()
refundRequest := &RefundRequest{
Transaction: serialized,
PubNonce: pubNonce[:],
Index: i,
}
claimRequest := &ClaimRequest{
Transaction: serialized,
PubNonce: pubNonce[:],
Index: i,
Preimage: output.Preimage,
}
var signature *PartialSignature
if output.SwapType == ReverseSwap {
signature, err = boltzApi.ClaimReverseSwap(output.SwapId, claimRequest)
} else if output.SwapType == NormalSwap {
signature, err = boltzApi.RefundSwap(output.SwapId, refundRequest)
} else {
signature, err = func() (*PartialSignature, error) {
if output.IsRefund() {
return boltzApi.RefundChainSwap(output.SwapId, refundRequest)
}
if output.RefundSwapTree == nil {
return nil, errors.New("RefundSwapTree is required for cooperatively claiming chain swap")
}
boltzSession, err := NewSigningSession(output.RefundSwapTree)
if err != nil {
return nil, fmt.Errorf("could not initialize signing session: %w", err)
}
details, err := boltzApi.GetChainSwapClaimDetails(output.SwapId)
if err != nil {
return nil, err
}
boltzSignature, err := boltzSession.Sign(details.TransactionHash, details.PubNonce)
if err != nil {
return nil, fmt.Errorf("could not sign transaction: %w", err)
}
return boltzApi.ExchangeChainSwapClaimSignature(output.SwapId, &ChainSwapSigningRequest{
Preimage: output.Preimage,
Signature: boltzSignature,
ToSign: claimRequest,
})
}()
}
if err != nil {
return fmt.Errorf("could not get partial signature from boltz: %w", err)
}
session, err := NewSigningSession(outputs[i].SwapTree)
if err != nil {
return fmt.Errorf("could not initialize signing session: %w", err)
}

if err := session.Finalize(transaction, outputs, network, signature); err != nil {
return fmt.Errorf("could not finalize signing session: %w", err)
}
pubNonce := session.PublicNonce()
refundRequest := &RefundRequest{
Transaction: serialized,
PubNonce: pubNonce[:],
Index: i,
}
claimRequest := &ClaimRequest{
Transaction: serialized,
PubNonce: pubNonce[:],
Index: i,
Preimage: output.Preimage,
}
var signature *PartialSignature
if output.SwapType == ReverseSwap {
signature, err = boltzApi.ClaimReverseSwap(output.SwapId, claimRequest)
} else if output.SwapType == NormalSwap {
signature, err = boltzApi.RefundSwap(output.SwapId, refundRequest)
} else {
signature, err = func() (*PartialSignature, error) {
if output.IsRefund() {
return boltzApi.RefundChainSwap(output.SwapId, refundRequest)
}
if output.RefundSwapTree == nil {
return nil, errors.New("RefundSwapTree is required for cooperatively claiming chain swap")
}
boltzSession, err := NewSigningSession(output.RefundSwapTree)
if err != nil {
return nil, fmt.Errorf("could not initialize signing session: %w", err)
}
details, err := boltzApi.GetChainSwapClaimDetails(output.SwapId)
if err != nil {
return nil, err
}
boltzSignature, err := boltzSession.Sign(details.TransactionHash, details.PubNonce)
if err != nil {
return nil, fmt.Errorf("could not sign transaction: %w", err)
}
return boltzApi.ExchangeChainSwapClaimSignature(output.SwapId, &ChainSwapSigningRequest{
Preimage: output.Preimage,
Signature: boltzSignature,
ToSign: claimRequest,
})
}()
}
if err != nil {
return fmt.Errorf("could not get partial signature from boltz: %w", err)
}

return nil
}()
if err := session.Finalize(transaction, outputs, network, signature); err != nil {
return fmt.Errorf("could not finalize signing session: %w", err)
}

if err != nil {
if output.IsRefund() {
results[i].Err = err
} else {
nonCoop := outputs[i]
nonCoop.Cooperative = false
retry = append(retry, nonCoop)
}
return nil
}()
if err != nil {
if output.IsRefund() {
results[output.SwapId] = OutputResult{Err: err}
} else {
nonCoop := outputs[i]
nonCoop.Cooperative = false
valid = append(valid, nonCoop)
}
} else {
valid = append(valid, output)
}
}

if len(retry) > 0 {
return ConstructTransaction(network, currency, retry, satPerVbyte, boltzApi)
if len(valid) == 0 {
return nil, results, fmt.Errorf("all outputs invalid")
}

if len(valid) > 0 && len(valid) < len(outputs) {
transaction, newResults, err := ConstructTransaction(network, currency, valid, satPerVbyte, boltzApi)
if err != nil {
return nil, nil, err
}
for id, result := range newResults {
results[id] = result
}
return transaction, results, nil
}

return transaction, results, err
Expand Down
10 changes: 8 additions & 2 deletions nursery/nursery.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@ func (nursery *Nursery) createTransaction(currency boltz.Currency, outputs []*Ou
}

transaction, results, err := boltz.ConstructTransaction(nursery.network, currency, details, feeSatPerVbyte, nursery.boltz)
for _, output := range valid {
result := results[output.SwapId]
if result.Err != nil {
logger.Errorf("Could not spend output for %s swap %s: %s", output.SwapType, output.SwapId, result.Err)
}
}
if err != nil {
return "", fmt.Errorf("construct transaction: %v", err)
}
Expand All @@ -286,9 +292,9 @@ func (nursery *Nursery) createTransaction(currency boltz.Currency, outputs []*Ou

id := response.TransactionId

for i, result := range results {
for _, output := range valid {
result := results[output.SwapId]
if result.Err == nil {
output := valid[i]
if err := output.setTransaction(id, result.Fee); err != nil {
logger.Errorf("Could not set transaction id for %s swap %s: %s", output.SwapType, output.SwapId, err)
continue
Expand Down

0 comments on commit ba60a73

Please sign in to comment.