mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-17 12:14:21 +01:00
* v0 unilateral redemption * add fee outputs to congestion tree * unilateral exit * rework unilateral exit verbosity * substract fee from vtxo amount * remove unused functions and variables * fix after reviews * Update noah/explorer.go Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com> Signed-off-by: Louis Singer <41042567+louisinger@users.noreply.github.com> * remove bufferutils --------- Signed-off-by: Louis Singer <41042567+louisinger@users.noreply.github.com> Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
246 lines
5.4 KiB
Go
246 lines
5.4 KiB
Go
package txbuilder
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
|
|
"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
|
|
)
|
|
|
|
type txBuilder struct {
|
|
net network.Network
|
|
}
|
|
|
|
func NewTxBuilder(net network.Network) ports.TxBuilder {
|
|
return &txBuilder{net}
|
|
}
|
|
|
|
// 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 := numberOfVTXOs(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, wallet ports.WalletService, payments []domain.Payment,
|
|
minRelayFee uint64,
|
|
) (poolTx string, congestionTree domain.CongestionTree, err error) {
|
|
aspScriptBytes, err := p2wpkhScript(aspPubkey, b.net)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
aspScript := hex.EncodeToString(aspScriptBytes)
|
|
|
|
offchainReceivers, onchainReceivers := receiversFromPayments(payments)
|
|
sharedOutputAmount := sumReceivers(offchainReceivers)
|
|
|
|
numberOfConnectors := numberOfVTXOs(payments)
|
|
connectorOutputAmount := connectorAmount * numberOfConnectors
|
|
|
|
poolTxOuts := []ports.TxOutput{
|
|
newOutput(aspScript, sharedOutputAmount, b.net.AssetID),
|
|
newOutput(aspScript, connectorOutputAmount, b.net.AssetID),
|
|
}
|
|
for _, receiver := range onchainReceivers {
|
|
buf, _ := address.ToOutputScript(receiver.OnchainAddress)
|
|
script := hex.EncodeToString(buf)
|
|
poolTxOuts = append(poolTxOuts, newOutput(script, receiver.Amount, b.net.AssetID))
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
poolTx, err = wallet.Transfer(ctx, poolTxOuts)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
poolTxID, err := getTxid(poolTx)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
congestionTree, err = buildCongestionTree(
|
|
newOutputScriptFactory(aspPubkey, b.net),
|
|
b.net,
|
|
poolTxID,
|
|
offchainReceivers,
|
|
)
|
|
|
|
return poolTx, congestionTree, err
|
|
}
|
|
|
|
func (b *txBuilder) GetLeafOutputScript(userPubkey, _ *secp256k1.PublicKey) ([]byte, error) {
|
|
p2wpkh := payment.FromPublicKey(userPubkey, &b.net, nil)
|
|
addr, _ := p2wpkh.WitnessPubKeyHash()
|
|
return address.ToOutputScript(addr)
|
|
}
|
|
|
|
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 numberOfVTXOs(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
|
|
}
|
|
|
|
type output struct {
|
|
script string
|
|
amount uint64
|
|
asset string
|
|
}
|
|
|
|
func newOutput(script string, amount uint64, asset string) ports.TxOutput {
|
|
return &output{
|
|
script: script,
|
|
amount: amount,
|
|
asset: asset,
|
|
}
|
|
}
|
|
|
|
func (o *output) GetAmount() uint64 {
|
|
return o.amount
|
|
}
|
|
|
|
func (o *output) GetAsset() string {
|
|
return o.asset
|
|
}
|
|
|
|
func (o *output) GetScript() string {
|
|
return o.script
|
|
}
|