Prevent getting cheated by broadcasting forfeit transactions (#123)

* broadcast forfeit transaction in case the user is trying the cheat the ASP

* fix connector input + --cheat flag in CLI

* WIP

* cleaning and fixes

* add TODO

* sweeper.go: mark round swept if vtxo are redeemed

* fixes after reviews

* revert "--cheat" flag in client

* revert redeem.go

* optimization

* update account.go according to ocean ListUtxos new spec

* WaitForSync implementation

* ocean-wallet/service.go: remove go rountine while writing to notification channel
This commit is contained in:
Louis Singer
2024-03-04 13:58:36 +01:00
committed by GitHub
parent 6d0d03e316
commit 066e8eeabb
21 changed files with 537 additions and 120 deletions

View File

@@ -11,6 +11,7 @@ import (
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/vulpemventures/go-elements/address"
"github.com/vulpemventures/go-elements/network"
"github.com/vulpemventures/go-elements/payment"
"github.com/vulpemventures/go-elements/psetv2"
"github.com/vulpemventures/go-elements/taproot"
)
@@ -41,9 +42,9 @@ func (b *txBuilder) GetVtxoScript(userPubkey, aspPubkey *secp256k1.PublicKey) ([
return outputScript, nil
}
func (b *txBuilder) BuildSweepTx(wallet ports.WalletService, inputs []ports.SweepInput) (signedSweepTx string, err error) {
func (b *txBuilder) BuildSweepTx(inputs []ports.SweepInput) (signedSweepTx string, err error) {
sweepPset, err := sweepTransaction(
wallet,
b.wallet,
inputs,
b.net.AssetID,
)
@@ -57,7 +58,7 @@ func (b *txBuilder) BuildSweepTx(wallet ports.WalletService, inputs []ports.Swee
}
ctx := context.Background()
signedSweepPsetB64, err := wallet.SignPsetWithKey(ctx, sweepPsetBase64, nil)
signedSweepPsetB64, err := b.wallet.SignPsetWithKey(ctx, sweepPsetBase64, nil)
if err != nil {
return "", err
}
@@ -80,9 +81,14 @@ func (b *txBuilder) BuildSweepTx(wallet ports.WalletService, inputs []ports.Swee
}
func (b *txBuilder) BuildForfeitTxs(
aspPubkey *secp256k1.PublicKey, poolTx string, payments []domain.Payment,
aspPubkey *secp256k1.PublicKey, poolTx string, payments []domain.Payment, minRelayFee uint64,
) (connectors []string, forfeitTxs []string, err error) {
connectorTxs, err := b.createConnectors(poolTx, payments, aspPubkey)
connectorAddress, err := b.getConnectorAddress(poolTx)
if err != nil {
return nil, nil, err
}
connectorTxs, err := b.createConnectors(poolTx, payments, connectorAddress, minRelayFee)
if err != nil {
return nil, nil, err
}
@@ -101,7 +107,7 @@ func (b *txBuilder) BuildForfeitTxs(
func (b *txBuilder) BuildPoolTx(
aspPubkey *secp256k1.PublicKey, payments []domain.Payment, minRelayFee uint64,
) (poolTx string, congestionTree tree.CongestionTree, err error) {
) (poolTx string, congestionTree tree.CongestionTree, connectorAddress string, err error) {
// The creation of the tree and the pool tx are tightly coupled:
// - building the tree requires knowing the shared outpoint (txid:vout)
// - building the pool tx requires knowing the shared output script and amount
@@ -120,8 +126,13 @@ func (b *txBuilder) BuildPoolTx(
return
}
connectorAddress, err = b.wallet.DeriveConnectorAddress(context.Background())
if err != nil {
return
}
ptx, err := b.createPoolTx(
sharedOutputAmount, sharedOutputScript, payments, aspPubkey,
sharedOutputAmount, sharedOutputScript, payments, aspPubkey, connectorAddress, minRelayFee,
)
if err != nil {
return
@@ -190,15 +201,24 @@ func (b *txBuilder) getLeafScriptAndTree(
func (b *txBuilder) createPoolTx(
sharedOutputAmount uint64, sharedOutputScript []byte,
payments []domain.Payment, aspPubKey *secp256k1.PublicKey,
payments []domain.Payment, aspPubKey *secp256k1.PublicKey, connectorAddress string, minRelayFee uint64,
) (*psetv2.Pset, error) {
aspScript, err := p2wpkhScript(aspPubKey, b.net)
if err != nil {
return nil, err
}
connectorScript, err := address.ToOutputScript(connectorAddress)
if err != nil {
return nil, err
}
receivers := getOnchainReceivers(payments)
connectorsAmount := connectorAmount * countSpentVtxos(payments)
nbOfInputs := countSpentVtxos(payments)
connectorsAmount := (connectorAmount + minRelayFee) * nbOfInputs
if nbOfInputs > 1 {
connectorsAmount -= minRelayFee
}
targetAmount := sharedOutputAmount + connectorsAmount
outputs := []psetv2.OutputArgs{
@@ -210,7 +230,7 @@ func (b *txBuilder) createPoolTx(
{
Asset: b.net.AssetID,
Amount: connectorsAmount,
Script: aspScript,
Script: connectorScript,
},
}
@@ -354,11 +374,11 @@ func (b *txBuilder) createPoolTx(
}
func (b *txBuilder) createConnectors(
poolTx string, payments []domain.Payment, aspPubkey *secp256k1.PublicKey,
poolTx string, payments []domain.Payment, connectorAddress string, minRelayFee uint64,
) ([]*psetv2.Pset, error) {
txid, _ := getTxid(poolTx)
aspScript, err := p2wpkhScript(aspPubkey, b.net)
aspScript, err := address.ToOutputScript(connectorAddress)
if err != nil {
return nil, err
}
@@ -378,7 +398,7 @@ func (b *txBuilder) createConnectors(
if numberOfConnectors == 1 {
outputs := []psetv2.OutputArgs{connectorOutput}
connectorTx, err := craftConnectorTx(previousInput, outputs)
connectorTx, err := craftConnectorTx(previousInput, aspScript, outputs, minRelayFee)
if err != nil {
return nil, err
}
@@ -386,12 +406,16 @@ func (b *txBuilder) createConnectors(
return []*psetv2.Pset{connectorTx}, nil
}
totalConnectorAmount := connectorAmount * numberOfConnectors
totalConnectorAmount := (connectorAmount + minRelayFee) * numberOfConnectors
if numberOfConnectors > 1 {
totalConnectorAmount -= minRelayFee
}
connectors := make([]*psetv2.Pset, 0, numberOfConnectors-1)
for i := uint64(0); i < numberOfConnectors-1; i++ {
outputs := []psetv2.OutputArgs{connectorOutput}
totalConnectorAmount -= connectorAmount
totalConnectorAmount -= minRelayFee
if totalConnectorAmount > 0 {
outputs = append(outputs, psetv2.OutputArgs{
Asset: b.net.AssetID,
@@ -399,7 +423,7 @@ func (b *txBuilder) createConnectors(
Amount: totalConnectorAmount,
})
}
connectorTx, err := craftConnectorTx(previousInput, outputs)
connectorTx, err := craftConnectorTx(previousInput, aspScript, outputs, minRelayFee)
if err != nil {
return nil, err
}
@@ -473,3 +497,23 @@ func (b *txBuilder) createForfeitTxs(
}
return forfeitTxs, nil
}
func (b *txBuilder) getConnectorAddress(poolTx string) (string, error) {
pset, err := psetv2.NewPsetFromBase64(poolTx)
if err != nil {
return "", err
}
if len(pset.Outputs) < 1 {
return "", fmt.Errorf("connector output not found in pool tx")
}
connectorOutput := pset.Outputs[1]
pay, err := payment.FromScript(connectorOutput.Script, b.net, nil)
if err != nil {
return "", err
}
return pay.WitnessPubKeyHash()
}