Files
ark/server/internal/infrastructure/tx-builder/dummy/builder.go
Pietralberto Mazza dc00d60585 Rename folders (#97)
* Rename arkd folder & drop cli

* Rename ark cli folder & update docs

* Update readme

* Fix

* scripts: add build-all

* Add target to build cli for all platforms

* Update build scripts

---------

Co-authored-by: tiero <3596602+tiero@users.noreply.github.com>
2024-02-09 19:32:58 +01:00

286 lines
6.2 KiB
Go

package txbuilder
import (
"context"
"github.com/ark-network/ark/common/tree"
"github.com/ark-network/ark/internal/core/domain"
"github.com/ark-network/ark/internal/core/ports"
"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/transaction"
)
const (
connectorAmount = 450
sevenDays = 7 * 24 * 60 * 60
)
type txBuilder struct {
wallet ports.WalletService
net network.Network
}
func NewTxBuilder(
wallet ports.WalletService, net network.Network,
) ports.TxBuilder {
return &txBuilder{wallet, net}
}
// BuildSweepTx implements ports.TxBuilder.
func (*txBuilder) BuildSweepTx(wallet ports.WalletService, inputs []ports.SweepInput) (signedSweepTx string, err error) {
panic("unimplemented")
}
// BuildForfeitTxs implements ports.TxBuilder.
func (b *txBuilder) BuildForfeitTxs(
aspPubkey *secp256k1.PublicKey, poolTx string, payments []domain.Payment,
) (connectors []string, forfeitTxs []string, err error) {
poolTxID, err := getTxid(poolTx)
if err != nil {
return nil, nil, err
}
aspScript, err := p2wpkhScript(aspPubkey, b.net)
if err != nil {
return nil, nil, err
}
numberOfConnectors := countSpentVtxos(payments)
connectors, err = createConnectors(
poolTxID,
1,
psetv2.OutputArgs{
Asset: b.net.AssetID,
Amount: connectorAmount,
Script: aspScript,
},
aspScript,
numberOfConnectors,
)
if err != nil {
return nil, nil, err
}
connectorsAsInputs, err := connectorsToInputArgs(connectors)
if err != nil {
return nil, nil, err
}
forfeitTxs = make([]string, 0)
for _, payment := range payments {
for _, vtxo := range payment.Inputs {
for _, connector := range connectorsAsInputs {
forfeitTx, err := createForfeitTx(
connector,
psetv2.InputArgs{
Txid: vtxo.Txid,
TxIndex: vtxo.VOut,
},
vtxo.Amount,
aspScript,
b.net,
)
if err != nil {
return nil, nil, err
}
forfeitTxs = append(forfeitTxs, forfeitTx)
}
}
}
return connectors, forfeitTxs, nil
}
// BuildPoolTx implements ports.TxBuilder.
func (b *txBuilder) BuildPoolTx(
aspPubkey *secp256k1.PublicKey, payments []domain.Payment, minRelayFee uint64,
) (poolTx string, congestionTree tree.CongestionTree, err error) {
aspScriptBytes, err := p2wpkhScript(aspPubkey, b.net)
if err != nil {
return "", nil, err
}
offchainReceivers, onchainReceivers := receiversFromPayments(payments)
sharedOutputAmount := sumReceivers(offchainReceivers)
numberOfConnectors := countSpentVtxos(payments)
connectorOutputAmount := connectorAmount * numberOfConnectors
ctx := context.Background()
outputs := []psetv2.OutputArgs{
{
Asset: b.net.AssetID,
Amount: sharedOutputAmount,
Script: aspScriptBytes,
},
{
Asset: b.net.AssetID,
Amount: connectorOutputAmount,
Script: aspScriptBytes,
},
}
amountToSelect := sharedOutputAmount + connectorOutputAmount
for _, receiver := range onchainReceivers {
amountToSelect += receiver.Amount
receiverScript, err := address.ToOutputScript(receiver.OnchainAddress)
if err != nil {
return "", nil, err
}
outputs = append(outputs, psetv2.OutputArgs{
Asset: b.net.AssetID,
Amount: receiver.Amount,
Script: receiverScript,
})
}
utxos, change, err := b.wallet.SelectUtxos(ctx, b.net.AssetID, amountToSelect)
if err != nil {
return
}
if change > 0 {
outputs = append(outputs, psetv2.OutputArgs{
Asset: b.net.AssetID,
Amount: change,
Script: aspScriptBytes,
})
}
ptx, err := psetv2.New(toInputArgs(utxos), outputs, nil)
if err != nil {
return
}
utx, err := ptx.UnsignedTx()
if err != nil {
return
}
congestionTree, err = buildCongestionTree(
newOutputScriptFactory(aspPubkey, b.net),
b.net,
utx.TxHash().String(),
offchainReceivers,
)
if err != nil {
return
}
poolTx, err = ptx.ToBase64()
if err != nil {
return
}
return poolTx, congestionTree, err
}
func (b *txBuilder) GetVtxoScript(userPubkey, _ *secp256k1.PublicKey) ([]byte, error) {
p2wpkh := payment.FromPublicKey(userPubkey, &b.net, nil)
addr, _ := p2wpkh.WitnessPubKeyHash()
return address.ToOutputScript(addr)
}
func (b *txBuilder) GetLeafSweepClosure(
node tree.Node, userPubKey *secp256k1.PublicKey,
) (*psetv2.TapLeafScript, int64, error) {
panic("unimplemented")
}
func connectorsToInputArgs(connectors []string) ([]psetv2.InputArgs, error) {
inputs := make([]psetv2.InputArgs, 0, len(connectors)+1)
for i, psetb64 := range connectors {
tx, err := psetv2.NewPsetFromBase64(psetb64)
if err != nil {
return nil, err
}
utx, err := tx.UnsignedTx()
if err != nil {
return nil, err
}
txid := utx.TxHash().String()
for j := range tx.Outputs {
inputs = append(inputs, psetv2.InputArgs{
Txid: txid,
TxIndex: uint32(j),
})
if i != len(connectors)-1 {
break
}
}
}
return inputs, nil
}
func getTxid(txStr string) (string, error) {
pset, err := psetv2.NewPsetFromBase64(txStr)
if err != nil {
tx, err := transaction.NewTxFromHex(txStr)
if err != nil {
return "", err
}
return tx.TxHash().String(), nil
}
utx, err := pset.UnsignedTx()
if err != nil {
return "", err
}
return utx.TxHash().String(), nil
}
func countSpentVtxos(payments []domain.Payment) uint64 {
var sum uint64
for _, payment := range payments {
sum += uint64(len(payment.Inputs))
}
return sum
}
func receiversFromPayments(
payments []domain.Payment,
) (offchainReceivers, onchainReceivers []domain.Receiver) {
for _, payment := range payments {
for _, receiver := range payment.Receivers {
if receiver.IsOnchain() {
onchainReceivers = append(onchainReceivers, receiver)
} else {
offchainReceivers = append(offchainReceivers, receiver)
}
}
}
return
}
func sumReceivers(receivers []domain.Receiver) uint64 {
var sum uint64
for _, r := range receivers {
sum += r.Amount
}
return sum
}
func toInputArgs(
ins []ports.TxInput,
) []psetv2.InputArgs {
inputs := make([]psetv2.InputArgs, 0, len(ins))
for _, in := range ins {
inputs = append(inputs, psetv2.InputArgs{
Txid: in.GetTxid(),
TxIndex: in.GetIndex(),
})
}
return inputs
}