mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-17 12:14:21 +01:00
Add CreatePayment and CompletePayment (#229)
Co-authored-by: Marco Argentieri <tiero@users.noreply.github.com> * Add claim command * Persist pending data in sqlite repo * Remove debug log * Return pending data at interface level * Fix unlocking btc wallet after restart * Lint & Fix whitelist permissions * Fix send command for covenant * Update client/covenantless/claim.go Signed-off-by: Marco Argentieri <3596602+tiero@users.noreply.github.com> * Fix * Pay for min relay fee instead of estimating fees for redeem and unconf forfeit txs * Add support for pending payments (coventanless) * Fixes * Fixes * Improve verbosity * Fix coin selection * Fix --------- Signed-off-by: Marco Argentieri <3596602+tiero@users.noreply.github.com> Co-authored-by: louisinger <louis@vulpem.com> Co-authored-by: Marco Argentieri <tiero@users.noreply.github.com> Co-authored-by: Marco Argentieri <3596602+tiero@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
57ce08f239
commit
72a7f29bab
@@ -25,6 +25,14 @@ const dust = 450
|
||||
|
||||
type covenantLiquidCLI struct{}
|
||||
|
||||
func (c *covenantLiquidCLI) SendAsync(ctx *cli.Context) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (c *covenantLiquidCLI) ClaimAsync(ctx *cli.Context) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (c *covenantLiquidCLI) Receive(ctx *cli.Context) error {
|
||||
offchainAddr, onchainAddr, _, err := getAddress(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -109,7 +109,7 @@ func getClient(addr string) (arkv1.ArkServiceClient, func(), error) {
|
||||
if !strings.Contains(addr, ":") {
|
||||
addr = fmt.Sprintf("%s:%d", addr, port)
|
||||
}
|
||||
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds))
|
||||
conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
98
client/covenantless/claim.go
Normal file
98
client/covenantless/claim.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package covenantless
|
||||
|
||||
import (
|
||||
"github.com/ark-network/ark-cli/utils"
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func (c *clArkBitcoinCLI) ClaimAsync(ctx *cli.Context) error {
|
||||
client, cancel, err := getClientFromState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
myselfOffchain, _, _, err := getAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vtxos, err := getVtxos(ctx, nil, client, myselfOffchain, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pendingBalance uint64
|
||||
var pendingVtxos []vtxo
|
||||
for _, vtxo := range vtxos {
|
||||
if vtxo.pending {
|
||||
pendingBalance += vtxo.amount
|
||||
pendingVtxos = append(pendingVtxos, vtxo)
|
||||
}
|
||||
}
|
||||
if pendingBalance == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
receiver := receiver{
|
||||
To: myselfOffchain,
|
||||
Amount: pendingBalance,
|
||||
}
|
||||
return selfTransferAllPendingPayments(
|
||||
ctx, client, pendingVtxos, receiver,
|
||||
)
|
||||
}
|
||||
|
||||
func selfTransferAllPendingPayments(
|
||||
ctx *cli.Context, client arkv1.ArkServiceClient,
|
||||
pendingVtxos []vtxo, myself receiver,
|
||||
) error {
|
||||
inputs := make([]*arkv1.Input, 0, len(pendingVtxos))
|
||||
|
||||
for _, coin := range pendingVtxos {
|
||||
inputs = append(inputs, &arkv1.Input{
|
||||
Txid: coin.txid,
|
||||
Vout: coin.vout,
|
||||
})
|
||||
}
|
||||
|
||||
receiversOutput := []*arkv1.Output{
|
||||
{
|
||||
Address: myself.To,
|
||||
Amount: myself.Amount,
|
||||
},
|
||||
}
|
||||
|
||||
secKey, err := utils.PrivateKeyFromPassword(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
registerResponse, err := client.RegisterPayment(
|
||||
ctx.Context, &arkv1.RegisterPaymentRequest{Inputs: inputs},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.ClaimPayment(ctx.Context, &arkv1.ClaimPaymentRequest{
|
||||
Id: registerResponse.GetId(),
|
||||
Outputs: []*arkv1.Output{{Address: myself.To, Amount: myself.Amount}},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
poolTxID, err := handleRoundStream(
|
||||
ctx, client, registerResponse.GetId(),
|
||||
pendingVtxos, secKey, receiversOutput,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utils.PrintJSON(map[string]interface{}{
|
||||
"pool_txid": poolTxID,
|
||||
})
|
||||
}
|
||||
@@ -70,15 +70,19 @@ func New() interfaces.CLI {
|
||||
return &clArkBitcoinCLI{}
|
||||
}
|
||||
|
||||
func (c *clArkBitcoinCLI) Send(ctx *cli.Context) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
type receiver struct {
|
||||
To string `json:"to"`
|
||||
Amount uint64 `json:"amount"`
|
||||
}
|
||||
|
||||
func (r *receiver) isOnchain() bool {
|
||||
_, err := btcutil.DecodeAddress(r.To, nil)
|
||||
return err == nil
|
||||
}
|
||||
// func (r *receiver) isOnchain() bool {
|
||||
// _, err := btcutil.DecodeAddress(r.To, nil)
|
||||
// return err == nil
|
||||
// }
|
||||
|
||||
func sendOnchain(ctx *cli.Context, receivers []receiver) (string, error) {
|
||||
ptx, err := psbt.New(nil, nil, 2, 0, nil)
|
||||
|
||||
@@ -27,6 +27,7 @@ type vtxo struct {
|
||||
vout uint32
|
||||
poolTxid string
|
||||
expireAt *time.Time
|
||||
pending bool
|
||||
}
|
||||
|
||||
func getVtxos(
|
||||
@@ -43,19 +44,20 @@ func getVtxos(
|
||||
vtxos := make([]vtxo, 0, len(response.GetSpendableVtxos()))
|
||||
for _, v := range response.GetSpendableVtxos() {
|
||||
var expireAt *time.Time
|
||||
if v.ExpireAt > 0 {
|
||||
if v.GetExpireAt() > 0 {
|
||||
t := time.Unix(v.ExpireAt, 0)
|
||||
expireAt = &t
|
||||
}
|
||||
if v.Swept {
|
||||
if v.GetSwept() {
|
||||
continue
|
||||
}
|
||||
vtxos = append(vtxos, vtxo{
|
||||
amount: v.Receiver.Amount,
|
||||
txid: v.Outpoint.Txid,
|
||||
vout: v.Outpoint.Vout,
|
||||
poolTxid: v.PoolTxid,
|
||||
amount: v.GetReceiver().GetAmount(),
|
||||
txid: v.GetOutpoint().GetTxid(),
|
||||
vout: v.GetOutpoint().GetVout(),
|
||||
poolTxid: v.GetPoolTxid(),
|
||||
expireAt: expireAt,
|
||||
pending: v.GetPending(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -108,7 +110,7 @@ func getClient(addr string) (arkv1.ArkServiceClient, func(), error) {
|
||||
if !strings.Contains(addr, ":") {
|
||||
addr = fmt.Sprintf("%s:%d", addr, port)
|
||||
}
|
||||
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds))
|
||||
conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -2,118 +2,63 @@ package covenantless
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/ark-network/ark-cli/utils"
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/btcsuite/btcd/btcutil/psbt"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func (c *clArkBitcoinCLI) Send(ctx *cli.Context) error {
|
||||
if !ctx.IsSet("receivers") && !ctx.IsSet("to") && !ctx.IsSet("amount") {
|
||||
return fmt.Errorf("missing destination, either use --to and --amount to send or --receivers to send to many")
|
||||
}
|
||||
receivers := ctx.String("receivers")
|
||||
to := ctx.String("to")
|
||||
func (c *clArkBitcoinCLI) SendAsync(ctx *cli.Context) error {
|
||||
receiver := ctx.String("to")
|
||||
amount := ctx.Uint64("amount")
|
||||
|
||||
var receiversJSON []receiver
|
||||
if len(receivers) > 0 {
|
||||
if err := json.Unmarshal([]byte(receivers), &receiversJSON); err != nil {
|
||||
return fmt.Errorf("invalid receivers: %s", err)
|
||||
}
|
||||
} else {
|
||||
receiversJSON = []receiver{
|
||||
{
|
||||
To: to,
|
||||
Amount: amount,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if len(receiversJSON) <= 0 {
|
||||
return fmt.Errorf("no receivers specified")
|
||||
}
|
||||
|
||||
onchainReceivers := make([]receiver, 0)
|
||||
offchainReceivers := make([]receiver, 0)
|
||||
|
||||
for _, receiver := range receiversJSON {
|
||||
if receiver.isOnchain() {
|
||||
onchainReceivers = append(onchainReceivers, receiver)
|
||||
} else {
|
||||
offchainReceivers = append(offchainReceivers, receiver)
|
||||
}
|
||||
}
|
||||
|
||||
explorer := utils.NewExplorer(ctx)
|
||||
|
||||
if len(onchainReceivers) > 0 {
|
||||
pset, err := sendOnchain(ctx, onchainReceivers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txid, err := explorer.Broadcast(pset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utils.PrintJSON(map[string]interface{}{
|
||||
"txid": txid,
|
||||
})
|
||||
}
|
||||
|
||||
if len(offchainReceivers) > 0 {
|
||||
if err := sendOffchain(ctx, offchainReceivers); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendOffchain(ctx *cli.Context, receivers []receiver) error {
|
||||
withExpiryCoinselect := ctx.Bool("enable-expiry-coinselect")
|
||||
|
||||
if amount < dust {
|
||||
return fmt.Errorf("invalid amount (%d), must be greater than dust %d", amount, dust)
|
||||
}
|
||||
|
||||
if receiver == "" {
|
||||
return fmt.Errorf("receiver address is required")
|
||||
}
|
||||
isOnchain, _, _, err := decodeReceiverAddress(receiver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isOnchain {
|
||||
return fmt.Errorf("receiver address is onchain")
|
||||
}
|
||||
|
||||
offchainAddr, _, _, err := getAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, aspPubKey, err := common.DecodeAddress(offchainAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, aspKey, err := common.DecodeAddress(receiver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid receiver address: %s", err)
|
||||
}
|
||||
if !bytes.Equal(
|
||||
aspPubKey.SerializeCompressed(), aspKey.SerializeCompressed(),
|
||||
) {
|
||||
return fmt.Errorf("invalid receiver address '%s': must be associated with the connected service provider", receiver)
|
||||
}
|
||||
|
||||
receiversOutput := make([]*arkv1.Output, 0)
|
||||
sumOfReceivers := uint64(0)
|
||||
receiversOutput = append(receiversOutput, &arkv1.Output{
|
||||
Address: receiver,
|
||||
Amount: amount,
|
||||
})
|
||||
sumOfReceivers += amount
|
||||
|
||||
for _, receiver := range receivers {
|
||||
_, _, aspKey, err := common.DecodeAddress(receiver.To)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid receiver address: %s", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(
|
||||
aspPubKey.SerializeCompressed(), aspKey.SerializeCompressed(),
|
||||
) {
|
||||
return fmt.Errorf("invalid receiver address '%s': must be associated with the connected service provider", receiver.To)
|
||||
}
|
||||
|
||||
if receiver.Amount < dust {
|
||||
return fmt.Errorf("invalid amount (%d), must be greater than dust %d", receiver.Amount, dust)
|
||||
}
|
||||
|
||||
receiversOutput = append(receiversOutput, &arkv1.Output{
|
||||
Address: receiver.To,
|
||||
Amount: uint64(receiver.Amount),
|
||||
})
|
||||
sumOfReceivers += receiver.Amount
|
||||
}
|
||||
client, close, err := getClientFromState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -148,37 +93,66 @@ func sendOffchain(ctx *cli.Context, receivers []receiver) error {
|
||||
})
|
||||
}
|
||||
|
||||
secKey, err := utils.PrivateKeyFromPassword(ctx)
|
||||
resp, err := client.CreatePayment(
|
||||
ctx.Context, &arkv1.CreatePaymentRequest{
|
||||
Inputs: inputs,
|
||||
Outputs: receiversOutput,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
registerResponse, err := client.RegisterPayment(
|
||||
ctx.Context, &arkv1.RegisterPaymentRequest{Inputs: inputs},
|
||||
)
|
||||
// TODO verify the redeem tx signature
|
||||
fmt.Println("payment created")
|
||||
fmt.Println("signing redeem and forfeit txs...")
|
||||
|
||||
seckey, err := utils.PrivateKeyFromPassword(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.ClaimPayment(ctx.Context, &arkv1.ClaimPaymentRequest{
|
||||
Id: registerResponse.GetId(),
|
||||
Outputs: receiversOutput,
|
||||
})
|
||||
signedUnconditionalForfeitTxs := make([]string, 0, len(resp.UsignedUnconditionalForfeitTxs))
|
||||
for _, tx := range resp.UsignedUnconditionalForfeitTxs {
|
||||
forfeitPtx, err := psbt.NewFromRawBytes(strings.NewReader(tx), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := signPsbt(ctx, forfeitPtx, explorer, seckey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signedForfeitTx, err := forfeitPtx.B64Encode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signedUnconditionalForfeitTxs = append(signedUnconditionalForfeitTxs, signedForfeitTx)
|
||||
}
|
||||
|
||||
redeemPtx, err := psbt.NewFromRawBytes(strings.NewReader(resp.SignedRedeemTx), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
poolTxID, err := handleRoundStream(
|
||||
ctx, client, registerResponse.GetId(),
|
||||
selectedCoins, secKey, receiversOutput,
|
||||
)
|
||||
if err := signPsbt(ctx, redeemPtx, explorer, seckey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signedRedeem, err := redeemPtx.B64Encode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utils.PrintJSON(map[string]interface{}{
|
||||
"pool_txid": poolTxID,
|
||||
})
|
||||
if _, err = client.CompletePayment(ctx.Context, &arkv1.CompletePaymentRequest{
|
||||
SignedRedeemTx: signedRedeem,
|
||||
SignedUnconditionalForfeitTxs: signedUnconditionalForfeitTxs,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("payment completed")
|
||||
return nil
|
||||
}
|
||||
|
||||
func coinSelect(vtxos []vtxo, amount uint64, sortByExpirationTime bool) ([]vtxo, uint64, error) {
|
||||
@@ -213,7 +187,7 @@ func coinSelect(vtxos []vtxo, amount uint64, sortByExpirationTime bool) ([]vtxo,
|
||||
|
||||
change := selectedAmount - amount
|
||||
|
||||
if change < dust {
|
||||
if change > 0 && change < dust {
|
||||
if len(notSelected) > 0 {
|
||||
selected = append(selected, notSelected[0])
|
||||
change += notSelected[0].amount
|
||||
|
||||
@@ -132,7 +132,7 @@ func signPsbt(
|
||||
switch c := closure.(type) {
|
||||
case *bitcointree.CSVSigClosure:
|
||||
sign = bytes.Equal(c.Pubkey.SerializeCompressed()[1:], pubkey.SerializeCompressed()[1:])
|
||||
case *bitcointree.ForfeitClosure:
|
||||
case *bitcointree.MultisigClosure:
|
||||
sign = bytes.Equal(c.Pubkey.SerializeCompressed()[1:], pubkey.SerializeCompressed()[1:])
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ func computeVtxoTaprootScript(
|
||||
Seconds: exitDelay,
|
||||
}
|
||||
|
||||
forfeitClosure := &bitcointree.ForfeitClosure{
|
||||
forfeitClosure := &bitcointree.MultisigClosure{
|
||||
Pubkey: userPubkey,
|
||||
AspPubkey: aspPubkey,
|
||||
}
|
||||
|
||||
@@ -72,18 +72,22 @@ var (
|
||||
Value: "",
|
||||
Required: false,
|
||||
}
|
||||
|
||||
AmountToRedeemFlag = cli.Uint64Flag{
|
||||
Name: "amount",
|
||||
Usage: "amount to redeem",
|
||||
Value: 0,
|
||||
Required: false,
|
||||
}
|
||||
|
||||
ForceFlag = cli.BoolFlag{
|
||||
Name: "force",
|
||||
Usage: "force redemption without collaborate with the Ark service provider",
|
||||
Value: false,
|
||||
Required: false,
|
||||
}
|
||||
AsyncPaymentFlag = cli.BoolFlag{
|
||||
Name: "async",
|
||||
Usage: "use async payment protocol",
|
||||
Value: false,
|
||||
Required: false,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -8,5 +8,7 @@ type CLI interface {
|
||||
Receive(ctx *cli.Context) error
|
||||
Redeem(ctx *cli.Context) error
|
||||
Send(ctx *cli.Context) error
|
||||
ClaimAsync(ctx *cli.Context) error
|
||||
SendAsync(ctx *cli.Context) error
|
||||
Onboard(ctx *cli.Context) error
|
||||
}
|
||||
|
||||
@@ -92,14 +92,36 @@ var (
|
||||
sendCommand = cli.Command{
|
||||
Name: "send",
|
||||
Usage: "Send your onchain or offchain funds to one or many receivers",
|
||||
Action: func(ctx *cli.Context) error {
|
||||
state, err := utils.GetState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
networkName := state[utils.NETWORK]
|
||||
cli, err := getCLI(networkName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(networkName, "liquid") {
|
||||
return cli.Send(ctx)
|
||||
}
|
||||
return cli.SendAsync(ctx)
|
||||
},
|
||||
Flags: []cli.Flag{&flags.ReceiversFlag, &flags.ToFlag, &flags.AmountFlag, &flags.PasswordFlag, &flags.EnableExpiryCoinselectFlag, &flags.AsyncPaymentFlag},
|
||||
}
|
||||
|
||||
claimCommand = cli.Command{
|
||||
Name: "claim",
|
||||
Usage: "Join round to claim pending payments",
|
||||
Action: func(ctx *cli.Context) error {
|
||||
cli, err := getCLIFromState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cli.Send(ctx)
|
||||
return cli.ClaimAsync(ctx)
|
||||
},
|
||||
Flags: []cli.Flag{&flags.ReceiversFlag, &flags.ToFlag, &flags.AmountFlag, &flags.PasswordFlag, &flags.EnableExpiryCoinselectFlag},
|
||||
Flags: []cli.Flag{&flags.PasswordFlag},
|
||||
}
|
||||
|
||||
receiveCommand = cli.Command{
|
||||
@@ -144,6 +166,7 @@ func main() {
|
||||
&receiveCommand,
|
||||
&redeemCommand,
|
||||
&sendCommand,
|
||||
&claimCommand,
|
||||
&onboardCommand,
|
||||
)
|
||||
app.Flags = []cli.Flag{
|
||||
|
||||
@@ -84,7 +84,6 @@ func (e *explorer) GetFeeRate() (float64, error) {
|
||||
fmt.Println("empty fee-estimates response, default to 2 sat/vbyte")
|
||||
return 2, nil
|
||||
}
|
||||
fmt.Println("fee rate", response["1"])
|
||||
|
||||
return response["1"], nil
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ func (l *leaf) getOutputs() ([]*wire.TxOut, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
forfeitClosure := &ForfeitClosure{
|
||||
forfeitClosure := &MultisigClosure{
|
||||
Pubkey: l.vtxoKey,
|
||||
AspPubkey: l.aspKey,
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ type CSVSigClosure struct {
|
||||
Seconds uint
|
||||
}
|
||||
|
||||
type ForfeitClosure struct {
|
||||
type MultisigClosure struct {
|
||||
Pubkey *secp256k1.PublicKey
|
||||
AspPubkey *secp256k1.PublicKey
|
||||
}
|
||||
@@ -33,7 +33,7 @@ func DecodeClosure(script []byte) (Closure, error) {
|
||||
return closure, nil
|
||||
}
|
||||
|
||||
closure = &ForfeitClosure{}
|
||||
closure = &MultisigClosure{}
|
||||
if valid, err := closure.Decode(script); err == nil && valid {
|
||||
return closure, nil
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func DecodeClosure(script []byte) (Closure, error) {
|
||||
|
||||
}
|
||||
|
||||
func (f *ForfeitClosure) Leaf() (*txscript.TapLeaf, error) {
|
||||
func (f *MultisigClosure) Leaf() (*txscript.TapLeaf, error) {
|
||||
aspKeyBytes := schnorr.SerializePubKey(f.AspPubkey)
|
||||
userKeyBytes := schnorr.SerializePubKey(f.Pubkey)
|
||||
|
||||
@@ -57,7 +57,7 @@ func (f *ForfeitClosure) Leaf() (*txscript.TapLeaf, error) {
|
||||
return &tapLeaf, nil
|
||||
}
|
||||
|
||||
func (f *ForfeitClosure) Decode(script []byte) (bool, error) {
|
||||
func (f *MultisigClosure) Decode(script []byte) (bool, error) {
|
||||
valid, aspPubKey, err := decodeChecksigScript(script)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -149,7 +149,7 @@ func ComputeVtxoTaprootScript(
|
||||
Seconds: exitDelay,
|
||||
}
|
||||
|
||||
forfeitClosure := &ForfeitClosure{
|
||||
forfeitClosure := &MultisigClosure{
|
||||
Pubkey: userPubkey,
|
||||
AspPubkey: aspPubkey,
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ services:
|
||||
- ARK_NO_MACAROONS=true
|
||||
ports:
|
||||
- "6000:6000"
|
||||
volumes:
|
||||
- clarkd:/app/data
|
||||
- clark:/app/wallet-data
|
||||
|
||||
volumes:
|
||||
clarkd:
|
||||
|
||||
@@ -14,6 +14,9 @@ services:
|
||||
- OCEAN_DB_TYPE=badger
|
||||
ports:
|
||||
- "18000:18000"
|
||||
volumes:
|
||||
- oceand:/app/data/oceand
|
||||
- ocean:/app/data/ocean
|
||||
arkd:
|
||||
container_name: arkd
|
||||
build:
|
||||
@@ -35,6 +38,9 @@ services:
|
||||
- ARK_NO_MACAROONS=true
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- arkd:/app/data
|
||||
- ark:/app/wallet-data
|
||||
|
||||
volumes:
|
||||
oceand:
|
||||
|
||||
@@ -23,6 +23,8 @@ type ArkClient interface {
|
||||
CollaborativeRedeem(
|
||||
ctx context.Context, addr string, amount uint64, withExpiryCoinselect bool,
|
||||
) (string, error)
|
||||
SendAsync(ctx context.Context, withExpiryCoinselect bool, receivers []Receiver) (string, error)
|
||||
ClaimAsync(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
type Receiver interface {
|
||||
|
||||
@@ -37,6 +37,12 @@ type ASPClient interface {
|
||||
FinalizePayment(
|
||||
ctx context.Context, signedForfeitTxs []string,
|
||||
) error
|
||||
CreatePayment(
|
||||
ctx context.Context, inputs []VtxoKey, outputs []Output,
|
||||
) (string, []string, error)
|
||||
CompletePayment(
|
||||
ctx context.Context, signedRedeemTx string, signedUnconditionalForfeitTxs []string,
|
||||
) error
|
||||
Close()
|
||||
}
|
||||
|
||||
@@ -61,9 +67,12 @@ type VtxoKey struct {
|
||||
|
||||
type Vtxo struct {
|
||||
VtxoKey
|
||||
Amount uint64
|
||||
RoundTxid string
|
||||
ExpiresAt *time.Time
|
||||
Amount uint64
|
||||
RoundTxid string
|
||||
ExpiresAt *time.Time
|
||||
RedeemTx string
|
||||
UnconditionalForfeitTxs []string
|
||||
Pending bool
|
||||
}
|
||||
|
||||
type Output struct {
|
||||
|
||||
@@ -199,6 +199,31 @@ func (a *grpcClient) FinalizePayment(
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *grpcClient) CreatePayment(
|
||||
ctx context.Context, inputs []client.VtxoKey, outputs []client.Output,
|
||||
) (string, []string, error) {
|
||||
req := &arkv1.CreatePaymentRequest{
|
||||
Inputs: ins(inputs).toProto(),
|
||||
Outputs: outs(outputs).toProto(),
|
||||
}
|
||||
resp, err := a.svc.CreatePayment(ctx, req)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return resp.SignedRedeemTx, resp.UsignedUnconditionalForfeitTxs, nil
|
||||
}
|
||||
|
||||
func (a *grpcClient) CompletePayment(
|
||||
ctx context.Context, redeemTx string, signedForfeitTxs []string,
|
||||
) error {
|
||||
req := &arkv1.CompletePaymentRequest{
|
||||
SignedRedeemTx: redeemTx,
|
||||
SignedUnconditionalForfeitTxs: signedForfeitTxs,
|
||||
}
|
||||
_, err := a.svc.CompletePayment(ctx, req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *grpcClient) GetRoundByID(
|
||||
ctx context.Context, roundID string,
|
||||
) (*client.Round, error) {
|
||||
@@ -284,14 +309,23 @@ func (v vtxo) toVtxo() client.Vtxo {
|
||||
t := time.Unix(v.GetExpireAt(), 0)
|
||||
expiresAt = &t
|
||||
}
|
||||
var redeemTx string
|
||||
var uncondForfeitTxs []string
|
||||
if v.GetPendingData() != nil {
|
||||
redeemTx = v.GetPendingData().GetRedeemTx()
|
||||
uncondForfeitTxs = v.GetPendingData().GetUnconditionalForfeitTxs()
|
||||
}
|
||||
return client.Vtxo{
|
||||
VtxoKey: client.VtxoKey{
|
||||
Txid: v.GetOutpoint().GetTxid(),
|
||||
VOut: v.GetOutpoint().GetVout(),
|
||||
},
|
||||
Amount: v.GetReceiver().GetAmount(),
|
||||
RoundTxid: v.GetPoolTxid(),
|
||||
ExpiresAt: expiresAt,
|
||||
Amount: v.GetReceiver().GetAmount(),
|
||||
RoundTxid: v.GetPoolTxid(),
|
||||
ExpiresAt: expiresAt,
|
||||
Pending: v.GetPending(),
|
||||
RedeemTx: redeemTx,
|
||||
UnconditionalForfeitTxs: uncondForfeitTxs,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -180,14 +180,24 @@ func (a *restClient) ListVtxos(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var redeemTx string
|
||||
var uncondForfeitTxs []string
|
||||
if v.PendingData != nil {
|
||||
redeemTx = v.PendingData.RedeemTx
|
||||
uncondForfeitTxs = v.PendingData.UnconditionalForfeitTxs
|
||||
}
|
||||
|
||||
spendableVtxos = append(spendableVtxos, client.Vtxo{
|
||||
VtxoKey: client.VtxoKey{
|
||||
Txid: v.Outpoint.Txid,
|
||||
VOut: uint32(v.Outpoint.Vout),
|
||||
},
|
||||
Amount: uint64(amount),
|
||||
RoundTxid: v.PoolTxid,
|
||||
ExpiresAt: expiresAt,
|
||||
Amount: uint64(amount),
|
||||
RoundTxid: v.PoolTxid,
|
||||
ExpiresAt: expiresAt,
|
||||
Pending: v.Pending,
|
||||
RedeemTx: redeemTx,
|
||||
UnconditionalForfeitTxs: uncondForfeitTxs,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -358,6 +368,53 @@ func (a *restClient) FinalizePayment(
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *restClient) CreatePayment(
|
||||
ctx context.Context, inputs []client.VtxoKey, outputs []client.Output,
|
||||
) (string, []string, error) {
|
||||
ins := make([]*models.V1Input, 0, len(inputs))
|
||||
for _, i := range inputs {
|
||||
ins = append(ins, &models.V1Input{
|
||||
Txid: i.Txid,
|
||||
Vout: int64(i.VOut),
|
||||
})
|
||||
}
|
||||
outs := make([]*models.V1Output, 0, len(outputs))
|
||||
for _, o := range outputs {
|
||||
outs = append(outs, &models.V1Output{
|
||||
Address: o.Address,
|
||||
Amount: strconv.Itoa(int(o.Amount)),
|
||||
})
|
||||
}
|
||||
body := models.V1CreatePaymentRequest{
|
||||
Inputs: ins,
|
||||
Outputs: outs,
|
||||
}
|
||||
resp, err := a.svc.ArkServiceCreatePayment(
|
||||
ark_service.NewArkServiceCreatePaymentParams().WithBody(&body),
|
||||
)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return resp.GetPayload().SignedRedeemTx, resp.GetPayload().UsignedUnconditionalForfeitTxs, nil
|
||||
}
|
||||
|
||||
func (a *restClient) CompletePayment(
|
||||
ctx context.Context, signedRedeemTx string, signedUnconditionalForfeitTxs []string,
|
||||
) error {
|
||||
req := &arkv1.CompletePaymentRequest{
|
||||
SignedRedeemTx: signedRedeemTx,
|
||||
SignedUnconditionalForfeitTxs: signedUnconditionalForfeitTxs,
|
||||
}
|
||||
body := models.V1CompletePaymentRequest{
|
||||
SignedRedeemTx: req.GetSignedRedeemTx(),
|
||||
SignedUnconditionalForfeitTxs: req.GetSignedUnconditionalForfeitTxs(),
|
||||
}
|
||||
_, err := a.svc.ArkServiceCompletePayment(
|
||||
ark_service.NewArkServiceCompletePaymentParams().WithBody(&body),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *restClient) GetRoundByID(
|
||||
ctx context.Context, roundID string,
|
||||
) (*client.Round, error) {
|
||||
|
||||
@@ -56,6 +56,10 @@ type ClientOption func(*runtime.ClientOperation)
|
||||
type ClientService interface {
|
||||
ArkServiceClaimPayment(params *ArkServiceClaimPaymentParams, opts ...ClientOption) (*ArkServiceClaimPaymentOK, error)
|
||||
|
||||
ArkServiceCompletePayment(params *ArkServiceCompletePaymentParams, opts ...ClientOption) (*ArkServiceCompletePaymentOK, error)
|
||||
|
||||
ArkServiceCreatePayment(params *ArkServiceCreatePaymentParams, opts ...ClientOption) (*ArkServiceCreatePaymentOK, error)
|
||||
|
||||
ArkServiceFinalizePayment(params *ArkServiceFinalizePaymentParams, opts ...ClientOption) (*ArkServiceFinalizePaymentOK, error)
|
||||
|
||||
ArkServiceGetEventStream(params *ArkServiceGetEventStreamParams, opts ...ClientOption) (*ArkServiceGetEventStreamOK, error)
|
||||
@@ -114,6 +118,80 @@ func (a *Client) ArkServiceClaimPayment(params *ArkServiceClaimPaymentParams, op
|
||||
return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceCompletePayment ark service complete payment API
|
||||
*/
|
||||
func (a *Client) ArkServiceCompletePayment(params *ArkServiceCompletePaymentParams, opts ...ClientOption) (*ArkServiceCompletePaymentOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
if params == nil {
|
||||
params = NewArkServiceCompletePaymentParams()
|
||||
}
|
||||
op := &runtime.ClientOperation{
|
||||
ID: "ArkService_CompletePayment",
|
||||
Method: "POST",
|
||||
PathPattern: "/v1/payment/complete",
|
||||
ProducesMediaTypes: []string{"application/json"},
|
||||
ConsumesMediaTypes: []string{"application/json"},
|
||||
Schemes: []string{"http"},
|
||||
Params: params,
|
||||
Reader: &ArkServiceCompletePaymentReader{formats: a.formats},
|
||||
Context: params.Context,
|
||||
Client: params.HTTPClient,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
|
||||
result, err := a.transport.Submit(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
success, ok := result.(*ArkServiceCompletePaymentOK)
|
||||
if ok {
|
||||
return success, nil
|
||||
}
|
||||
// unexpected success response
|
||||
unexpectedSuccess := result.(*ArkServiceCompletePaymentDefault)
|
||||
return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceCreatePayment ark service create payment API
|
||||
*/
|
||||
func (a *Client) ArkServiceCreatePayment(params *ArkServiceCreatePaymentParams, opts ...ClientOption) (*ArkServiceCreatePaymentOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
if params == nil {
|
||||
params = NewArkServiceCreatePaymentParams()
|
||||
}
|
||||
op := &runtime.ClientOperation{
|
||||
ID: "ArkService_CreatePayment",
|
||||
Method: "POST",
|
||||
PathPattern: "/v1/payment",
|
||||
ProducesMediaTypes: []string{"application/json"},
|
||||
ConsumesMediaTypes: []string{"application/json"},
|
||||
Schemes: []string{"http"},
|
||||
Params: params,
|
||||
Reader: &ArkServiceCreatePaymentReader{formats: a.formats},
|
||||
Context: params.Context,
|
||||
Client: params.HTTPClient,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
|
||||
result, err := a.transport.Submit(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
success, ok := result.(*ArkServiceCreatePaymentOK)
|
||||
if ok {
|
||||
return success, nil
|
||||
}
|
||||
// unexpected success response
|
||||
unexpectedSuccess := result.(*ArkServiceCreatePaymentDefault)
|
||||
return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceFinalizePayment ark service finalize payment API
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package ark_service
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
cr "github.com/go-openapi/runtime/client"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models"
|
||||
)
|
||||
|
||||
// NewArkServiceCompletePaymentParams creates a new ArkServiceCompletePaymentParams object,
|
||||
// with the default timeout for this client.
|
||||
//
|
||||
// Default values are not hydrated, since defaults are normally applied by the API server side.
|
||||
//
|
||||
// To enforce default values in parameter, use SetDefaults or WithDefaults.
|
||||
func NewArkServiceCompletePaymentParams() *ArkServiceCompletePaymentParams {
|
||||
return &ArkServiceCompletePaymentParams{
|
||||
timeout: cr.DefaultTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceCompletePaymentParamsWithTimeout creates a new ArkServiceCompletePaymentParams object
|
||||
// with the ability to set a timeout on a request.
|
||||
func NewArkServiceCompletePaymentParamsWithTimeout(timeout time.Duration) *ArkServiceCompletePaymentParams {
|
||||
return &ArkServiceCompletePaymentParams{
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceCompletePaymentParamsWithContext creates a new ArkServiceCompletePaymentParams object
|
||||
// with the ability to set a context for a request.
|
||||
func NewArkServiceCompletePaymentParamsWithContext(ctx context.Context) *ArkServiceCompletePaymentParams {
|
||||
return &ArkServiceCompletePaymentParams{
|
||||
Context: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceCompletePaymentParamsWithHTTPClient creates a new ArkServiceCompletePaymentParams object
|
||||
// with the ability to set a custom HTTPClient for a request.
|
||||
func NewArkServiceCompletePaymentParamsWithHTTPClient(client *http.Client) *ArkServiceCompletePaymentParams {
|
||||
return &ArkServiceCompletePaymentParams{
|
||||
HTTPClient: client,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceCompletePaymentParams contains all the parameters to send to the API endpoint
|
||||
|
||||
for the ark service complete payment operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type ArkServiceCompletePaymentParams struct {
|
||||
|
||||
// Body.
|
||||
Body *models.V1CompletePaymentRequest
|
||||
|
||||
timeout time.Duration
|
||||
Context context.Context
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// WithDefaults hydrates default values in the ark service complete payment params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *ArkServiceCompletePaymentParams) WithDefaults() *ArkServiceCompletePaymentParams {
|
||||
o.SetDefaults()
|
||||
return o
|
||||
}
|
||||
|
||||
// SetDefaults hydrates default values in the ark service complete payment params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *ArkServiceCompletePaymentParams) SetDefaults() {
|
||||
// no default values defined for this parameter
|
||||
}
|
||||
|
||||
// WithTimeout adds the timeout to the ark service complete payment params
|
||||
func (o *ArkServiceCompletePaymentParams) WithTimeout(timeout time.Duration) *ArkServiceCompletePaymentParams {
|
||||
o.SetTimeout(timeout)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetTimeout adds the timeout to the ark service complete payment params
|
||||
func (o *ArkServiceCompletePaymentParams) SetTimeout(timeout time.Duration) {
|
||||
o.timeout = timeout
|
||||
}
|
||||
|
||||
// WithContext adds the context to the ark service complete payment params
|
||||
func (o *ArkServiceCompletePaymentParams) WithContext(ctx context.Context) *ArkServiceCompletePaymentParams {
|
||||
o.SetContext(ctx)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetContext adds the context to the ark service complete payment params
|
||||
func (o *ArkServiceCompletePaymentParams) SetContext(ctx context.Context) {
|
||||
o.Context = ctx
|
||||
}
|
||||
|
||||
// WithHTTPClient adds the HTTPClient to the ark service complete payment params
|
||||
func (o *ArkServiceCompletePaymentParams) WithHTTPClient(client *http.Client) *ArkServiceCompletePaymentParams {
|
||||
o.SetHTTPClient(client)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetHTTPClient adds the HTTPClient to the ark service complete payment params
|
||||
func (o *ArkServiceCompletePaymentParams) SetHTTPClient(client *http.Client) {
|
||||
o.HTTPClient = client
|
||||
}
|
||||
|
||||
// WithBody adds the body to the ark service complete payment params
|
||||
func (o *ArkServiceCompletePaymentParams) WithBody(body *models.V1CompletePaymentRequest) *ArkServiceCompletePaymentParams {
|
||||
o.SetBody(body)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBody adds the body to the ark service complete payment params
|
||||
func (o *ArkServiceCompletePaymentParams) SetBody(body *models.V1CompletePaymentRequest) {
|
||||
o.Body = body
|
||||
}
|
||||
|
||||
// WriteToRequest writes these params to a swagger request
|
||||
func (o *ArkServiceCompletePaymentParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
|
||||
|
||||
if err := r.SetTimeout(o.timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
var res []error
|
||||
if o.Body != nil {
|
||||
if err := r.SetBodyParam(o.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package ark_service
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models"
|
||||
)
|
||||
|
||||
// ArkServiceCompletePaymentReader is a Reader for the ArkServiceCompletePayment structure.
|
||||
type ArkServiceCompletePaymentReader struct {
|
||||
formats strfmt.Registry
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *ArkServiceCompletePaymentReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewArkServiceCompletePaymentOK()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
result := NewArkServiceCompletePaymentDefault(response.Code())
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.Code()/100 == 2 {
|
||||
return result, nil
|
||||
}
|
||||
return nil, result
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceCompletePaymentOK creates a ArkServiceCompletePaymentOK with default headers values
|
||||
func NewArkServiceCompletePaymentOK() *ArkServiceCompletePaymentOK {
|
||||
return &ArkServiceCompletePaymentOK{}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceCompletePaymentOK describes a response with status code 200, with default header values.
|
||||
|
||||
A successful response.
|
||||
*/
|
||||
type ArkServiceCompletePaymentOK struct {
|
||||
Payload models.V1CompletePaymentResponse
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this ark service complete payment o k response has a 2xx status code
|
||||
func (o *ArkServiceCompletePaymentOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this ark service complete payment o k response has a 3xx status code
|
||||
func (o *ArkServiceCompletePaymentOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this ark service complete payment o k response has a 4xx status code
|
||||
func (o *ArkServiceCompletePaymentOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this ark service complete payment o k response has a 5xx status code
|
||||
func (o *ArkServiceCompletePaymentOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this ark service complete payment o k response a status code equal to that given
|
||||
func (o *ArkServiceCompletePaymentOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the ark service complete payment o k response
|
||||
func (o *ArkServiceCompletePaymentOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *ArkServiceCompletePaymentOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/complete][%d] arkServiceCompletePaymentOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceCompletePaymentOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/complete][%d] arkServiceCompletePaymentOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceCompletePaymentOK) GetPayload() models.V1CompletePaymentResponse {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *ArkServiceCompletePaymentOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
// response payload
|
||||
if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewArkServiceCompletePaymentDefault creates a ArkServiceCompletePaymentDefault with default headers values
|
||||
func NewArkServiceCompletePaymentDefault(code int) *ArkServiceCompletePaymentDefault {
|
||||
return &ArkServiceCompletePaymentDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceCompletePaymentDefault describes a response with status code -1, with default header values.
|
||||
|
||||
An unexpected error response.
|
||||
*/
|
||||
type ArkServiceCompletePaymentDefault struct {
|
||||
_statusCode int
|
||||
|
||||
Payload *models.RPCStatus
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this ark service complete payment default response has a 2xx status code
|
||||
func (o *ArkServiceCompletePaymentDefault) IsSuccess() bool {
|
||||
return o._statusCode/100 == 2
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this ark service complete payment default response has a 3xx status code
|
||||
func (o *ArkServiceCompletePaymentDefault) IsRedirect() bool {
|
||||
return o._statusCode/100 == 3
|
||||
}
|
||||
|
||||
// IsClientError returns true when this ark service complete payment default response has a 4xx status code
|
||||
func (o *ArkServiceCompletePaymentDefault) IsClientError() bool {
|
||||
return o._statusCode/100 == 4
|
||||
}
|
||||
|
||||
// IsServerError returns true when this ark service complete payment default response has a 5xx status code
|
||||
func (o *ArkServiceCompletePaymentDefault) IsServerError() bool {
|
||||
return o._statusCode/100 == 5
|
||||
}
|
||||
|
||||
// IsCode returns true when this ark service complete payment default response a status code equal to that given
|
||||
func (o *ArkServiceCompletePaymentDefault) IsCode(code int) bool {
|
||||
return o._statusCode == code
|
||||
}
|
||||
|
||||
// Code gets the status code for the ark service complete payment default response
|
||||
func (o *ArkServiceCompletePaymentDefault) Code() int {
|
||||
return o._statusCode
|
||||
}
|
||||
|
||||
func (o *ArkServiceCompletePaymentDefault) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/complete][%d] ArkService_CompletePayment default %s", o._statusCode, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceCompletePaymentDefault) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/complete][%d] ArkService_CompletePayment default %s", o._statusCode, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceCompletePaymentDefault) GetPayload() *models.RPCStatus {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *ArkServiceCompletePaymentDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
o.Payload = new(models.RPCStatus)
|
||||
|
||||
// response payload
|
||||
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package ark_service
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
cr "github.com/go-openapi/runtime/client"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models"
|
||||
)
|
||||
|
||||
// NewArkServiceCreatePaymentParams creates a new ArkServiceCreatePaymentParams object,
|
||||
// with the default timeout for this client.
|
||||
//
|
||||
// Default values are not hydrated, since defaults are normally applied by the API server side.
|
||||
//
|
||||
// To enforce default values in parameter, use SetDefaults or WithDefaults.
|
||||
func NewArkServiceCreatePaymentParams() *ArkServiceCreatePaymentParams {
|
||||
return &ArkServiceCreatePaymentParams{
|
||||
timeout: cr.DefaultTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceCreatePaymentParamsWithTimeout creates a new ArkServiceCreatePaymentParams object
|
||||
// with the ability to set a timeout on a request.
|
||||
func NewArkServiceCreatePaymentParamsWithTimeout(timeout time.Duration) *ArkServiceCreatePaymentParams {
|
||||
return &ArkServiceCreatePaymentParams{
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceCreatePaymentParamsWithContext creates a new ArkServiceCreatePaymentParams object
|
||||
// with the ability to set a context for a request.
|
||||
func NewArkServiceCreatePaymentParamsWithContext(ctx context.Context) *ArkServiceCreatePaymentParams {
|
||||
return &ArkServiceCreatePaymentParams{
|
||||
Context: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceCreatePaymentParamsWithHTTPClient creates a new ArkServiceCreatePaymentParams object
|
||||
// with the ability to set a custom HTTPClient for a request.
|
||||
func NewArkServiceCreatePaymentParamsWithHTTPClient(client *http.Client) *ArkServiceCreatePaymentParams {
|
||||
return &ArkServiceCreatePaymentParams{
|
||||
HTTPClient: client,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceCreatePaymentParams contains all the parameters to send to the API endpoint
|
||||
|
||||
for the ark service create payment operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type ArkServiceCreatePaymentParams struct {
|
||||
|
||||
// Body.
|
||||
Body *models.V1CreatePaymentRequest
|
||||
|
||||
timeout time.Duration
|
||||
Context context.Context
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// WithDefaults hydrates default values in the ark service create payment params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *ArkServiceCreatePaymentParams) WithDefaults() *ArkServiceCreatePaymentParams {
|
||||
o.SetDefaults()
|
||||
return o
|
||||
}
|
||||
|
||||
// SetDefaults hydrates default values in the ark service create payment params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *ArkServiceCreatePaymentParams) SetDefaults() {
|
||||
// no default values defined for this parameter
|
||||
}
|
||||
|
||||
// WithTimeout adds the timeout to the ark service create payment params
|
||||
func (o *ArkServiceCreatePaymentParams) WithTimeout(timeout time.Duration) *ArkServiceCreatePaymentParams {
|
||||
o.SetTimeout(timeout)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetTimeout adds the timeout to the ark service create payment params
|
||||
func (o *ArkServiceCreatePaymentParams) SetTimeout(timeout time.Duration) {
|
||||
o.timeout = timeout
|
||||
}
|
||||
|
||||
// WithContext adds the context to the ark service create payment params
|
||||
func (o *ArkServiceCreatePaymentParams) WithContext(ctx context.Context) *ArkServiceCreatePaymentParams {
|
||||
o.SetContext(ctx)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetContext adds the context to the ark service create payment params
|
||||
func (o *ArkServiceCreatePaymentParams) SetContext(ctx context.Context) {
|
||||
o.Context = ctx
|
||||
}
|
||||
|
||||
// WithHTTPClient adds the HTTPClient to the ark service create payment params
|
||||
func (o *ArkServiceCreatePaymentParams) WithHTTPClient(client *http.Client) *ArkServiceCreatePaymentParams {
|
||||
o.SetHTTPClient(client)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetHTTPClient adds the HTTPClient to the ark service create payment params
|
||||
func (o *ArkServiceCreatePaymentParams) SetHTTPClient(client *http.Client) {
|
||||
o.HTTPClient = client
|
||||
}
|
||||
|
||||
// WithBody adds the body to the ark service create payment params
|
||||
func (o *ArkServiceCreatePaymentParams) WithBody(body *models.V1CreatePaymentRequest) *ArkServiceCreatePaymentParams {
|
||||
o.SetBody(body)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBody adds the body to the ark service create payment params
|
||||
func (o *ArkServiceCreatePaymentParams) SetBody(body *models.V1CreatePaymentRequest) {
|
||||
o.Body = body
|
||||
}
|
||||
|
||||
// WriteToRequest writes these params to a swagger request
|
||||
func (o *ArkServiceCreatePaymentParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
|
||||
|
||||
if err := r.SetTimeout(o.timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
var res []error
|
||||
if o.Body != nil {
|
||||
if err := r.SetBodyParam(o.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package ark_service
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models"
|
||||
)
|
||||
|
||||
// ArkServiceCreatePaymentReader is a Reader for the ArkServiceCreatePayment structure.
|
||||
type ArkServiceCreatePaymentReader struct {
|
||||
formats strfmt.Registry
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *ArkServiceCreatePaymentReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewArkServiceCreatePaymentOK()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
result := NewArkServiceCreatePaymentDefault(response.Code())
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.Code()/100 == 2 {
|
||||
return result, nil
|
||||
}
|
||||
return nil, result
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceCreatePaymentOK creates a ArkServiceCreatePaymentOK with default headers values
|
||||
func NewArkServiceCreatePaymentOK() *ArkServiceCreatePaymentOK {
|
||||
return &ArkServiceCreatePaymentOK{}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceCreatePaymentOK describes a response with status code 200, with default header values.
|
||||
|
||||
A successful response.
|
||||
*/
|
||||
type ArkServiceCreatePaymentOK struct {
|
||||
Payload *models.V1CreatePaymentResponse
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this ark service create payment o k response has a 2xx status code
|
||||
func (o *ArkServiceCreatePaymentOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this ark service create payment o k response has a 3xx status code
|
||||
func (o *ArkServiceCreatePaymentOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this ark service create payment o k response has a 4xx status code
|
||||
func (o *ArkServiceCreatePaymentOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this ark service create payment o k response has a 5xx status code
|
||||
func (o *ArkServiceCreatePaymentOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this ark service create payment o k response a status code equal to that given
|
||||
func (o *ArkServiceCreatePaymentOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the ark service create payment o k response
|
||||
func (o *ArkServiceCreatePaymentOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *ArkServiceCreatePaymentOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment][%d] arkServiceCreatePaymentOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceCreatePaymentOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment][%d] arkServiceCreatePaymentOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceCreatePaymentOK) GetPayload() *models.V1CreatePaymentResponse {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *ArkServiceCreatePaymentOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
o.Payload = new(models.V1CreatePaymentResponse)
|
||||
|
||||
// response payload
|
||||
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewArkServiceCreatePaymentDefault creates a ArkServiceCreatePaymentDefault with default headers values
|
||||
func NewArkServiceCreatePaymentDefault(code int) *ArkServiceCreatePaymentDefault {
|
||||
return &ArkServiceCreatePaymentDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceCreatePaymentDefault describes a response with status code -1, with default header values.
|
||||
|
||||
An unexpected error response.
|
||||
*/
|
||||
type ArkServiceCreatePaymentDefault struct {
|
||||
_statusCode int
|
||||
|
||||
Payload *models.RPCStatus
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this ark service create payment default response has a 2xx status code
|
||||
func (o *ArkServiceCreatePaymentDefault) IsSuccess() bool {
|
||||
return o._statusCode/100 == 2
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this ark service create payment default response has a 3xx status code
|
||||
func (o *ArkServiceCreatePaymentDefault) IsRedirect() bool {
|
||||
return o._statusCode/100 == 3
|
||||
}
|
||||
|
||||
// IsClientError returns true when this ark service create payment default response has a 4xx status code
|
||||
func (o *ArkServiceCreatePaymentDefault) IsClientError() bool {
|
||||
return o._statusCode/100 == 4
|
||||
}
|
||||
|
||||
// IsServerError returns true when this ark service create payment default response has a 5xx status code
|
||||
func (o *ArkServiceCreatePaymentDefault) IsServerError() bool {
|
||||
return o._statusCode/100 == 5
|
||||
}
|
||||
|
||||
// IsCode returns true when this ark service create payment default response a status code equal to that given
|
||||
func (o *ArkServiceCreatePaymentDefault) IsCode(code int) bool {
|
||||
return o._statusCode == code
|
||||
}
|
||||
|
||||
// Code gets the status code for the ark service create payment default response
|
||||
func (o *ArkServiceCreatePaymentDefault) Code() int {
|
||||
return o._statusCode
|
||||
}
|
||||
|
||||
func (o *ArkServiceCreatePaymentDefault) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment][%d] ArkService_CreatePayment default %s", o._statusCode, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceCreatePaymentDefault) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment][%d] ArkService_CreatePayment default %s", o._statusCode, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceCreatePaymentDefault) GetPayload() *models.RPCStatus {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *ArkServiceCreatePaymentDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
o.Payload = new(models.RPCStatus)
|
||||
|
||||
// response payload
|
||||
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// V1CompletePaymentRequest v1 complete payment request
|
||||
//
|
||||
// swagger:model v1CompletePaymentRequest
|
||||
type V1CompletePaymentRequest struct {
|
||||
|
||||
// signed redeem tx
|
||||
SignedRedeemTx string `json:"signedRedeemTx,omitempty"`
|
||||
|
||||
// signed unconditional forfeit txs
|
||||
SignedUnconditionalForfeitTxs []string `json:"signedUnconditionalForfeitTxs"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 complete payment request
|
||||
func (m *V1CompletePaymentRequest) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this v1 complete payment request based on context it is used
|
||||
func (m *V1CompletePaymentRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1CompletePaymentRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *V1CompletePaymentRequest) UnmarshalBinary(b []byte) error {
|
||||
var res V1CompletePaymentRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// V1CompletePaymentResponse v1 complete payment response
|
||||
//
|
||||
// swagger:model v1CompletePaymentResponse
|
||||
type V1CompletePaymentResponse interface{}
|
||||
@@ -0,0 +1,183 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// V1CreatePaymentRequest v1 create payment request
|
||||
//
|
||||
// swagger:model v1CreatePaymentRequest
|
||||
type V1CreatePaymentRequest struct {
|
||||
|
||||
// inputs
|
||||
Inputs []*V1Input `json:"inputs"`
|
||||
|
||||
// outputs
|
||||
Outputs []*V1Output `json:"outputs"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 create payment request
|
||||
func (m *V1CreatePaymentRequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateInputs(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateOutputs(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1CreatePaymentRequest) validateInputs(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Inputs) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Inputs); i++ {
|
||||
if swag.IsZero(m.Inputs[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Inputs[i] != nil {
|
||||
if err := m.Inputs[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("inputs" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("inputs" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1CreatePaymentRequest) validateOutputs(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Outputs) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Outputs); i++ {
|
||||
if swag.IsZero(m.Outputs[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Outputs[i] != nil {
|
||||
if err := m.Outputs[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("outputs" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("outputs" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this v1 create payment request based on the context it is used
|
||||
func (m *V1CreatePaymentRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateInputs(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateOutputs(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1CreatePaymentRequest) contextValidateInputs(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Inputs); i++ {
|
||||
|
||||
if m.Inputs[i] != nil {
|
||||
|
||||
if swag.IsZero(m.Inputs[i]) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Inputs[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("inputs" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("inputs" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1CreatePaymentRequest) contextValidateOutputs(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Outputs); i++ {
|
||||
|
||||
if m.Outputs[i] != nil {
|
||||
|
||||
if swag.IsZero(m.Outputs[i]) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Outputs[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("outputs" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("outputs" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1CreatePaymentRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *V1CreatePaymentRequest) UnmarshalBinary(b []byte) error {
|
||||
var res V1CreatePaymentRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// V1CreatePaymentResponse v1 create payment response
|
||||
//
|
||||
// swagger:model v1CreatePaymentResponse
|
||||
type V1CreatePaymentResponse struct {
|
||||
|
||||
// signed only by the ASP
|
||||
SignedRedeemTx string `json:"signedRedeemTx,omitempty"`
|
||||
|
||||
// usigned unconditional forfeit txs
|
||||
UsignedUnconditionalForfeitTxs []string `json:"usignedUnconditionalForfeitTxs"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 create payment response
|
||||
func (m *V1CreatePaymentResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this v1 create payment response based on context it is used
|
||||
func (m *V1CreatePaymentResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1CreatePaymentResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *V1CreatePaymentResponse) UnmarshalBinary(b []byte) error {
|
||||
var res V1CreatePaymentResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// V1PendingPayment v1 pending payment
|
||||
//
|
||||
// swagger:model v1PendingPayment
|
||||
type V1PendingPayment struct {
|
||||
|
||||
// redeem tx
|
||||
RedeemTx string `json:"redeemTx,omitempty"`
|
||||
|
||||
// unconditional forfeit txs
|
||||
UnconditionalForfeitTxs []string `json:"unconditionalForfeitTxs"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 pending payment
|
||||
func (m *V1PendingPayment) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this v1 pending payment based on context it is used
|
||||
func (m *V1PendingPayment) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1PendingPayment) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *V1PendingPayment) UnmarshalBinary(b []byte) error {
|
||||
var res V1PendingPayment
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -24,6 +24,12 @@ type V1Vtxo struct {
|
||||
// outpoint
|
||||
Outpoint *V1Input `json:"outpoint,omitempty"`
|
||||
|
||||
// pending
|
||||
Pending bool `json:"pending,omitempty"`
|
||||
|
||||
// pending data
|
||||
PendingData *V1PendingPayment `json:"pendingData,omitempty"`
|
||||
|
||||
// pool txid
|
||||
PoolTxid string `json:"poolTxid,omitempty"`
|
||||
|
||||
@@ -48,6 +54,10 @@ func (m *V1Vtxo) Validate(formats strfmt.Registry) error {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validatePendingData(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateReceiver(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@@ -77,6 +87,25 @@ func (m *V1Vtxo) validateOutpoint(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1Vtxo) validatePendingData(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.PendingData) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.PendingData != nil {
|
||||
if err := m.PendingData.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("pendingData")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("pendingData")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1Vtxo) validateReceiver(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Receiver) { // not required
|
||||
return nil
|
||||
@@ -104,6 +133,10 @@ func (m *V1Vtxo) ContextValidate(ctx context.Context, formats strfmt.Registry) e
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidatePendingData(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateReceiver(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@@ -135,6 +168,27 @@ func (m *V1Vtxo) contextValidateOutpoint(ctx context.Context, formats strfmt.Reg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1Vtxo) contextValidatePendingData(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.PendingData != nil {
|
||||
|
||||
if swag.IsZero(m.PendingData) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.PendingData.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("pendingData")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("pendingData")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1Vtxo) contextValidateReceiver(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Receiver != nil {
|
||||
|
||||
@@ -547,6 +547,17 @@ func (a *covenantArkClient) CollaborativeRedeem(
|
||||
return poolTxID, nil
|
||||
}
|
||||
|
||||
func (a *covenantArkClient) SendAsync(
|
||||
ctx context.Context,
|
||||
withExpiryCoinselect bool, receivers []Receiver,
|
||||
) (string, error) {
|
||||
return "", fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (a *covenantArkClient) ClaimAsync(ctx context.Context) (string, error) {
|
||||
return "", fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (a *covenantArkClient) sendOnchain(
|
||||
ctx context.Context, receivers []Receiver,
|
||||
) (string, error) {
|
||||
|
||||
@@ -478,7 +478,7 @@ func (a *covenantlessArkClient) UnilateralRedeem(ctx context.Context) error {
|
||||
|
||||
vtxos := make([]client.Vtxo, 0)
|
||||
for _, offchainAddr := range offchainAddrs {
|
||||
spendableVtxos, err := a.getVtxos(ctx, offchainAddr, false)
|
||||
spendableVtxos, _, err := a.getVtxos(ctx, offchainAddr, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -574,7 +574,7 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
|
||||
|
||||
vtxos := make([]client.Vtxo, 0)
|
||||
for _, offchainAddr := range offchainAddrs {
|
||||
spendableVtxos, err := a.getVtxos(ctx, offchainAddr, withExpiryCoinselect)
|
||||
spendableVtxos, _, err := a.getVtxos(ctx, offchainAddr, withExpiryCoinselect)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -627,6 +627,145 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
|
||||
return poolTxID, nil
|
||||
}
|
||||
|
||||
func (a *covenantlessArkClient) SendAsync(
|
||||
ctx context.Context,
|
||||
withExpiryCoinselect bool, receivers []Receiver,
|
||||
) (string, error) {
|
||||
if len(receivers) <= 0 {
|
||||
return "", fmt.Errorf("missing receivers")
|
||||
}
|
||||
|
||||
for _, receiver := range receivers {
|
||||
isOnchain, _, _, err := utils.DecodeReceiverAddress(receiver.To())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isOnchain {
|
||||
return "", fmt.Errorf("all receiver addresses must be offchain addresses")
|
||||
}
|
||||
}
|
||||
|
||||
offchainAddrs, _, _, err := a.wallet.GetAddresses(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, _, aspPubKey, err := common.DecodeAddress(offchainAddrs[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
receiversOutput := make([]client.Output, 0)
|
||||
sumOfReceivers := uint64(0)
|
||||
|
||||
for _, receiver := range receivers {
|
||||
_, _, aspKey, err := common.DecodeAddress(receiver.To())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid receiver address: %s", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(
|
||||
aspPubKey.SerializeCompressed(), aspKey.SerializeCompressed(),
|
||||
) {
|
||||
return "", fmt.Errorf("invalid receiver address '%s': must be associated with the connected service provider", receiver)
|
||||
}
|
||||
|
||||
if receiver.Amount() < DUST {
|
||||
return "", fmt.Errorf("invalid amount (%d), must be greater than dust %d", receiver.Amount(), DUST)
|
||||
}
|
||||
|
||||
receiversOutput = append(receiversOutput, client.Output{
|
||||
Address: receiver.To(),
|
||||
Amount: receiver.Amount(),
|
||||
})
|
||||
sumOfReceivers += receiver.Amount()
|
||||
}
|
||||
|
||||
vtxos, _, err := a.getVtxos(ctx, offchainAddrs[0], withExpiryCoinselect)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
selectedCoins, changeAmount, err := utils.CoinSelect(
|
||||
vtxos, sumOfReceivers, DUST, withExpiryCoinselect,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if changeAmount > 0 {
|
||||
changeReceiver := client.Output{
|
||||
Address: offchainAddrs[0],
|
||||
Amount: changeAmount,
|
||||
}
|
||||
receiversOutput = append(receiversOutput, changeReceiver)
|
||||
}
|
||||
|
||||
inputs := make([]client.VtxoKey, 0, len(selectedCoins))
|
||||
|
||||
for _, coin := range selectedCoins {
|
||||
inputs = append(inputs, coin.VtxoKey)
|
||||
}
|
||||
|
||||
redeemTx, unconditionalForfeitTxs, err := a.client.CreatePayment(
|
||||
ctx, inputs, receiversOutput)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// TODO verify the redeem tx signature
|
||||
|
||||
signedUnconditionalForfeitTxs := make([]string, 0, len(unconditionalForfeitTxs))
|
||||
for _, tx := range unconditionalForfeitTxs {
|
||||
signedForfeitTx, err := a.wallet.SignTransaction(ctx, a.explorer, tx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
signedUnconditionalForfeitTxs = append(signedUnconditionalForfeitTxs, signedForfeitTx)
|
||||
}
|
||||
|
||||
signedRedeemTx, err := a.wallet.SignTransaction(ctx, a.explorer, redeemTx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = a.client.CompletePayment(
|
||||
ctx, signedRedeemTx, signedUnconditionalForfeitTxs,
|
||||
); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return signedRedeemTx, nil
|
||||
}
|
||||
|
||||
func (a *covenantlessArkClient) ClaimAsync(
|
||||
ctx context.Context,
|
||||
) (string, error) {
|
||||
myselfOffchain, _, err := a.wallet.NewAddress(ctx, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, pendingVtxos, err := a.getVtxos(ctx, myselfOffchain, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var pendingBalance uint64
|
||||
for _, vtxo := range pendingVtxos {
|
||||
pendingBalance += vtxo.Amount
|
||||
}
|
||||
if pendingBalance == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
receiver := client.Output{
|
||||
Address: myselfOffchain,
|
||||
Amount: pendingBalance,
|
||||
}
|
||||
return a.selfTransferAllPendingPayments(ctx, pendingVtxos, receiver)
|
||||
}
|
||||
|
||||
func (a *covenantlessArkClient) sendOnchain(
|
||||
ctx context.Context, receivers []Receiver,
|
||||
) (string, error) {
|
||||
@@ -817,7 +956,7 @@ func (a *covenantlessArkClient) sendOffchain(
|
||||
|
||||
vtxos := make([]client.Vtxo, 0)
|
||||
for _, offchainAddr := range offchainAddrs {
|
||||
spendableVtxos, err := a.getVtxos(ctx, offchainAddr, withExpiryCoinselect)
|
||||
spendableVtxos, _, err := a.getVtxos(ctx, offchainAddr, withExpiryCoinselect)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -1394,16 +1533,20 @@ func (a *covenantlessArkClient) getRedeemBranches(
|
||||
return redeemBranches, nil
|
||||
}
|
||||
|
||||
// TODO (@louisinger): return pending balance in dedicated map.
|
||||
// Currently, the returned balance is calculated from both spendable and
|
||||
// pending vtxos.
|
||||
func (a *covenantlessArkClient) getOffchainBalance(
|
||||
ctx context.Context, addr string, computeVtxoExpiration bool,
|
||||
) (uint64, map[int64]uint64, error) {
|
||||
amountByExpiration := make(map[int64]uint64, 0)
|
||||
|
||||
vtxos, err := a.getVtxos(ctx, addr, computeVtxoExpiration)
|
||||
spendableVtxos, pendingVtxos, err := a.getVtxos(ctx, addr, computeVtxoExpiration)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var balance uint64
|
||||
vtxos := append(spendableVtxos, pendingVtxos...)
|
||||
for _, vtxo := range vtxos {
|
||||
balance += vtxo.Amount
|
||||
|
||||
@@ -1423,28 +1566,38 @@ func (a *covenantlessArkClient) getOffchainBalance(
|
||||
|
||||
func (a *covenantlessArkClient) getVtxos(
|
||||
ctx context.Context, addr string, computeVtxoExpiration bool,
|
||||
) ([]client.Vtxo, error) {
|
||||
) ([]client.Vtxo, []client.Vtxo, error) {
|
||||
vtxos, _, err := a.client.ListVtxos(ctx, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pendingVtxos := make([]client.Vtxo, 0)
|
||||
spendableVtxos := make([]client.Vtxo, 0)
|
||||
for _, vtxo := range vtxos {
|
||||
if vtxo.Pending {
|
||||
pendingVtxos = append(pendingVtxos, vtxo)
|
||||
continue
|
||||
}
|
||||
spendableVtxos = append(spendableVtxos, vtxo)
|
||||
}
|
||||
|
||||
if !computeVtxoExpiration {
|
||||
return vtxos, nil
|
||||
return spendableVtxos, pendingVtxos, nil
|
||||
}
|
||||
|
||||
redeemBranches, err := a.getRedeemBranches(ctx, vtxos)
|
||||
redeemBranches, err := a.getRedeemBranches(ctx, spendableVtxos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for vtxoTxid, branch := range redeemBranches {
|
||||
expiration, err := branch.ExpiresAt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i, vtxo := range vtxos {
|
||||
for i, vtxo := range spendableVtxos {
|
||||
if vtxo.Txid == vtxoTxid {
|
||||
vtxos[i].ExpiresAt = expiration
|
||||
break
|
||||
@@ -1452,5 +1605,35 @@ func (a *covenantlessArkClient) getVtxos(
|
||||
}
|
||||
}
|
||||
|
||||
return vtxos, nil
|
||||
return spendableVtxos, pendingVtxos, nil
|
||||
}
|
||||
|
||||
func (a *covenantlessArkClient) selfTransferAllPendingPayments(
|
||||
ctx context.Context, pendingVtxos []client.Vtxo, myself client.Output,
|
||||
) (string, error) {
|
||||
inputs := make([]client.VtxoKey, 0, len(pendingVtxos))
|
||||
|
||||
for _, coin := range pendingVtxos {
|
||||
inputs = append(inputs, coin.VtxoKey)
|
||||
}
|
||||
|
||||
outputs := []client.Output{myself}
|
||||
|
||||
paymentID, err := a.client.RegisterPayment(ctx, inputs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := a.client.ClaimPayment(ctx, paymentID, outputs); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
roundTxid, err := a.handleRoundStream(
|
||||
ctx, paymentID, pendingVtxos, outputs,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return roundTxid, nil
|
||||
}
|
||||
|
||||
@@ -106,12 +106,11 @@ func main() {
|
||||
fmt.Println("")
|
||||
log.Infof("alice is sending %d sats to bob offchain...", amount)
|
||||
|
||||
txid, err = aliceArkClient.SendOffChain(ctx, false, receivers)
|
||||
if err != nil {
|
||||
if _, err = aliceArkClient.SendAsync(ctx, false, receivers); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Infof("payment completed in round tx: %s", txid)
|
||||
log.Info("payment completed out of round")
|
||||
|
||||
if err := generateBlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -135,6 +134,15 @@ func main() {
|
||||
|
||||
log.Infof("bob onchain balance: %d", bobBalance.OnchainBalance.SpendableAmount)
|
||||
log.Infof("bob offchain balance: %d", bobBalance.OffchainBalance.Total)
|
||||
|
||||
fmt.Println("")
|
||||
log.Info("bob is claiming the incoming payment...")
|
||||
roundTxid, err := bobArkClient.ClaimAsync(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Infof("bob claimed the incoming payment in round %s", roundTxid)
|
||||
}
|
||||
|
||||
func setupArkClient() (arksdk.ArkClient, error) {
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/ark-network/ark/pkg/client-sdk/internal/utils"
|
||||
"github.com/btcsuite/btcd/btcutil/psbt"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
"github.com/vulpemventures/go-elements/transaction"
|
||||
)
|
||||
@@ -97,8 +96,7 @@ func (e *explorerSvc) GetFeeRate() (float64, error) {
|
||||
}
|
||||
|
||||
if len(response) == 0 {
|
||||
log.Debug("empty fee-estimates response, default to 2 sat/vbyte")
|
||||
return 2, nil
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
return response["1"], nil
|
||||
|
||||
@@ -201,7 +201,7 @@ func (s *bitcoinWallet) SignTransaction(
|
||||
switch c := closure.(type) {
|
||||
case *bitcointree.CSVSigClosure:
|
||||
sign = bytes.Equal(c.Pubkey.SerializeCompressed()[1:], pubkey.SerializeCompressed()[1:])
|
||||
case *bitcointree.ForfeitClosure:
|
||||
case *bitcointree.MultisigClosure:
|
||||
sign = bytes.Equal(c.Pubkey.SerializeCompressed()[1:], pubkey.SerializeCompressed()[1:])
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,16 @@ lint:
|
||||
## run: run in dev mode
|
||||
run: clean
|
||||
@echo "Running arkd in dev mode..."
|
||||
@export ARK_WALLET_ADDR=localhost:18000; \
|
||||
@export ARK_NEUTRINO_PEER=localhost:18444; \
|
||||
export ARK_ROUND_INTERVAL=10; \
|
||||
export ARK_LOG_LEVEL=5; \
|
||||
export ARK_NETWORK=liquidregtest; \
|
||||
export ARK_NETWORK=regtest; \
|
||||
export ARK_PORT=8080; \
|
||||
export ARK_NO_TLS=true; \
|
||||
export ARK_NO_MACAROONS=true; \
|
||||
export ARK_TX_BUILDER_TYPE=covenantless; \
|
||||
export ARK_ESPLORA_URL=http://localhost:3000; \
|
||||
export ARK_MIN_RELAY_FEE=200; \
|
||||
go run ./cmd/arkd
|
||||
|
||||
## test: runs unit and component tests
|
||||
|
||||
@@ -101,6 +101,38 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment": {
|
||||
"post": {
|
||||
"operationId": "ArkService_CreatePayment",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1CreatePaymentResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1CreatePaymentRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/claim": {
|
||||
"post": {
|
||||
"operationId": "ArkService_ClaimPayment",
|
||||
@@ -133,6 +165,38 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/complete": {
|
||||
"post": {
|
||||
"operationId": "ArkService_CompletePayment",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1CompletePaymentResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1CompletePaymentRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/finalize": {
|
||||
"post": {
|
||||
"operationId": "ArkService_FinalizePayment",
|
||||
@@ -368,6 +432,57 @@
|
||||
"v1ClaimPaymentResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1CompletePaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"signedRedeemTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"signedUnconditionalForfeitTxs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1CompletePaymentResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1CreatePaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"inputs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Input"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Output"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1CreatePaymentResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"signedRedeemTx": {
|
||||
"type": "string",
|
||||
"title": "signed only by the ASP"
|
||||
},
|
||||
"usignedUnconditionalForfeitTxs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1FinalizePaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -517,6 +632,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1PendingPayment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"redeemTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"unconditionalForfeitTxs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1PingResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -696,6 +825,12 @@
|
||||
},
|
||||
"swept": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"pending": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"pendingData": {
|
||||
"$ref": "#/definitions/v1PendingPayment"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
820
server/api-spec/openapi/swagger/clark/v1/service.swagger.json
Normal file
820
server/api-spec/openapi/swagger/clark/v1/service.swagger.json
Normal file
@@ -0,0 +1,820 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "clark/v1/service.proto",
|
||||
"version": "version not set"
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "ArkService"
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v1/events": {
|
||||
"get": {
|
||||
"operationId": "ArkService_GetEventStream",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/v1GetEventStreamResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of v1GetEventStreamResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/info": {
|
||||
"get": {
|
||||
"operationId": "ArkService_GetInfo",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1GetInfoResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/onboard": {
|
||||
"post": {
|
||||
"operationId": "ArkService_Onboard",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1OnboardResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1OnboardRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/async": {
|
||||
"post": {
|
||||
"operationId": "ArkService_CreateAsyncPayment",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1CreateAsyncPaymentResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1CreateAsyncPaymentRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/async/complete": {
|
||||
"post": {
|
||||
"operationId": "ArkService_CompleteAsyncPayment",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1CompleteAsyncPaymentResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1CompleteAsyncPaymentRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/claim": {
|
||||
"post": {
|
||||
"operationId": "ArkService_ClaimPayment",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1ClaimPaymentResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1ClaimPaymentRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/finalize": {
|
||||
"post": {
|
||||
"operationId": "ArkService_FinalizePayment",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1FinalizePaymentResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1FinalizePaymentRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/register": {
|
||||
"post": {
|
||||
"operationId": "ArkService_RegisterPayment",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1RegisterPaymentResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1RegisterPaymentRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/ping/{paymentId}": {
|
||||
"get": {
|
||||
"operationId": "ArkService_Ping",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1PingResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "paymentId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/round/id/{id}": {
|
||||
"get": {
|
||||
"operationId": "ArkService_GetRoundById",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1GetRoundByIdResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/round/{txid}": {
|
||||
"get": {
|
||||
"summary": "TODO BTC: signTree rpc",
|
||||
"operationId": "ArkService_GetRound",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1GetRoundResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "txid",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/vtxos/{address}": {
|
||||
"get": {
|
||||
"operationId": "ArkService_ListVtxos",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1ListVtxosResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "address",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"rpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ClaimPaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Mocks wabisabi's credentials."
|
||||
},
|
||||
"outputs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Output"
|
||||
},
|
||||
"description": "List of receivers for a registered payment."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ClaimPaymentResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1CompleteAsyncPaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"signedSenderTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"signedReceiverTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"signedUnconditionalForfeitTx": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1CompleteAsyncPaymentResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"senderTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"receiverTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"unconditionalForfeitTx": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1CreateAsyncPaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"$ref": "#/definitions/v1Input"
|
||||
},
|
||||
"receiverPubkey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1CreateAsyncPaymentResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"unsignedSenderTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"unsignedReceiverTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"usignedUnconditionalForfeitTx": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1FinalizePaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"signedForfeitTxs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Forfeit txs signed by the user."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1FinalizePaymentResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1GetEventStreamResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roundFinalization": {
|
||||
"$ref": "#/definitions/v1RoundFinalizationEvent",
|
||||
"title": "TODO: BTC add \"signTree\" event"
|
||||
},
|
||||
"roundFinalized": {
|
||||
"$ref": "#/definitions/v1RoundFinalizedEvent"
|
||||
},
|
||||
"roundFailed": {
|
||||
"$ref": "#/definitions/v1RoundFailed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1GetInfoResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pubkey": {
|
||||
"type": "string"
|
||||
},
|
||||
"roundLifetime": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"unilateralExitDelay": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"roundInterval": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"network": {
|
||||
"type": "string"
|
||||
},
|
||||
"minRelayFee": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1GetRoundByIdResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"round": {
|
||||
"$ref": "#/definitions/v1Round"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1GetRoundResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"round": {
|
||||
"$ref": "#/definitions/v1Round"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string"
|
||||
},
|
||||
"vout": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ListVtxosResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"spendableVtxos": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Vtxo"
|
||||
}
|
||||
},
|
||||
"spentVtxos": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Vtxo"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Node": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string"
|
||||
},
|
||||
"tx": {
|
||||
"type": "string"
|
||||
},
|
||||
"parentTxid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1OnboardRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"boardingTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"congestionTree": {
|
||||
"$ref": "#/definitions/v1Tree"
|
||||
},
|
||||
"userPubkey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1OnboardResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1Output": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string",
|
||||
"description": "Either the offchain or onchain address."
|
||||
},
|
||||
"amount": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Amount to send in satoshis."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1PingResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"forfeitTxs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"event": {
|
||||
"$ref": "#/definitions/v1RoundFinalizationEvent"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RegisterPaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"inputs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Input"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RegisterPaymentResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Mocks wabisabi's credentials."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Round": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"start": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"end": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"poolTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"congestionTree": {
|
||||
"$ref": "#/definitions/v1Tree"
|
||||
},
|
||||
"forfeitTxs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"connectors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"stage": {
|
||||
"$ref": "#/definitions/v1RoundStage"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RoundFailed": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RoundFinalizationEvent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"poolTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"forfeitTxs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"congestionTree": {
|
||||
"$ref": "#/definitions/v1Tree"
|
||||
},
|
||||
"connectors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RoundFinalizedEvent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"poolTxid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RoundStage": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ROUND_STAGE_UNSPECIFIED",
|
||||
"ROUND_STAGE_REGISTRATION",
|
||||
"ROUND_STAGE_FINALIZATION",
|
||||
"ROUND_STAGE_FINALIZED",
|
||||
"ROUND_STAGE_FAILED"
|
||||
],
|
||||
"default": "ROUND_STAGE_UNSPECIFIED"
|
||||
},
|
||||
"v1Tree": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"levels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1TreeLevel"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1TreeLevel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nodes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Node"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Vtxo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"outpoint": {
|
||||
"$ref": "#/definitions/v1Input"
|
||||
},
|
||||
"receiver": {
|
||||
"$ref": "#/definitions/v1Output"
|
||||
},
|
||||
"spent": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"poolTxid": {
|
||||
"type": "string"
|
||||
},
|
||||
"spentBy": {
|
||||
"type": "string"
|
||||
},
|
||||
"expireAt": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"swept": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,8 +60,35 @@ service ArkService {
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc CreatePayment(CreatePaymentRequest) returns (CreatePaymentResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/payment"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc CompletePayment(CompletePaymentRequest) returns (CompletePaymentResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/payment/complete"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
message CreatePaymentRequest {
|
||||
repeated Input inputs = 1;
|
||||
repeated Output outputs = 2;
|
||||
}
|
||||
message CreatePaymentResponse {
|
||||
string signed_redeem_tx = 1; // signed only by the ASP
|
||||
repeated string usigned_unconditional_forfeit_txs = 2;
|
||||
}
|
||||
|
||||
message CompletePaymentRequest {
|
||||
string signed_redeem_tx = 1;
|
||||
repeated string signed_unconditional_forfeit_txs = 2;
|
||||
}
|
||||
message CompletePaymentResponse {}
|
||||
|
||||
message RegisterPaymentRequest {
|
||||
repeated Input inputs = 1;
|
||||
}
|
||||
@@ -217,4 +244,11 @@ message Vtxo {
|
||||
string spent_by = 5;
|
||||
int64 expire_at = 6;
|
||||
bool swept = 7;
|
||||
bool pending = 8;
|
||||
PendingPayment pending_data = 9;
|
||||
}
|
||||
|
||||
message PendingPayment {
|
||||
string redeem_tx = 1;
|
||||
repeated string unconditional_forfeit_txs =2;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -378,6 +378,58 @@ func local_request_ArkService_Onboard_0(ctx context.Context, marshaler runtime.M
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_CreatePayment_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq CreatePaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.CreatePayment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_CreatePayment_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq CreatePaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.CreatePayment(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_CompletePayment_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq CompletePaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.CompletePayment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_CompletePayment_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq CompletePaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.CompletePayment(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
// RegisterArkServiceHandlerServer registers the http handlers for service ArkService to "mux".
|
||||
// UnaryRPC :call ArkServiceServer directly.
|
||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
||||
@@ -617,6 +669,56 @@ func RegisterArkServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux,
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_CreatePayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/CreatePayment", runtime.WithHTTPPathPattern("/v1/payment"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_CreatePayment_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_CreatePayment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_CompletePayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/CompletePayment", runtime.WithHTTPPathPattern("/v1/payment/complete"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_CompletePayment_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_CompletePayment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -878,6 +980,50 @@ func RegisterArkServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux,
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_CreatePayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/CreatePayment", runtime.WithHTTPPathPattern("/v1/payment"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_CreatePayment_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_CreatePayment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_CompletePayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/CompletePayment", runtime.WithHTTPPathPattern("/v1/payment/complete"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_CompletePayment_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_CompletePayment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -901,6 +1047,10 @@ var (
|
||||
pattern_ArkService_GetInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "info"}, ""))
|
||||
|
||||
pattern_ArkService_Onboard_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "onboard"}, ""))
|
||||
|
||||
pattern_ArkService_CreatePayment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "payment"}, ""))
|
||||
|
||||
pattern_ArkService_CompletePayment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "payment", "complete"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -923,4 +1073,8 @@ var (
|
||||
forward_ArkService_GetInfo_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_Onboard_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_CreatePayment_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_CompletePayment_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
|
||||
@@ -29,6 +29,8 @@ type ArkServiceClient interface {
|
||||
ListVtxos(ctx context.Context, in *ListVtxosRequest, opts ...grpc.CallOption) (*ListVtxosResponse, error)
|
||||
GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error)
|
||||
Onboard(ctx context.Context, in *OnboardRequest, opts ...grpc.CallOption) (*OnboardResponse, error)
|
||||
CreatePayment(ctx context.Context, in *CreatePaymentRequest, opts ...grpc.CallOption) (*CreatePaymentResponse, error)
|
||||
CompletePayment(ctx context.Context, in *CompletePaymentRequest, opts ...grpc.CallOption) (*CompletePaymentResponse, error)
|
||||
}
|
||||
|
||||
type arkServiceClient struct {
|
||||
@@ -152,6 +154,24 @@ func (c *arkServiceClient) Onboard(ctx context.Context, in *OnboardRequest, opts
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) CreatePayment(ctx context.Context, in *CreatePaymentRequest, opts ...grpc.CallOption) (*CreatePaymentResponse, error) {
|
||||
out := new(CreatePaymentResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/CreatePayment", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) CompletePayment(ctx context.Context, in *CompletePaymentRequest, opts ...grpc.CallOption) (*CompletePaymentResponse, error) {
|
||||
out := new(CompletePaymentResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/CompletePayment", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ArkServiceServer is the server API for ArkService service.
|
||||
// All implementations should embed UnimplementedArkServiceServer
|
||||
// for forward compatibility
|
||||
@@ -167,6 +187,8 @@ type ArkServiceServer interface {
|
||||
ListVtxos(context.Context, *ListVtxosRequest) (*ListVtxosResponse, error)
|
||||
GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error)
|
||||
Onboard(context.Context, *OnboardRequest) (*OnboardResponse, error)
|
||||
CreatePayment(context.Context, *CreatePaymentRequest) (*CreatePaymentResponse, error)
|
||||
CompletePayment(context.Context, *CompletePaymentRequest) (*CompletePaymentResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedArkServiceServer should be embedded to have forward compatible implementations.
|
||||
@@ -203,6 +225,12 @@ func (UnimplementedArkServiceServer) GetInfo(context.Context, *GetInfoRequest) (
|
||||
func (UnimplementedArkServiceServer) Onboard(context.Context, *OnboardRequest) (*OnboardResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Onboard not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) CreatePayment(context.Context, *CreatePaymentRequest) (*CreatePaymentResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreatePayment not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) CompletePayment(context.Context, *CompletePaymentRequest) (*CompletePaymentResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CompletePayment not implemented")
|
||||
}
|
||||
|
||||
// UnsafeArkServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ArkServiceServer will
|
||||
@@ -398,6 +426,42 @@ func _ArkService_Onboard_Handler(srv interface{}, ctx context.Context, dec func(
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_CreatePayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreatePaymentRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).CreatePayment(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/CreatePayment",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).CreatePayment(ctx, req.(*CreatePaymentRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_CompletePayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CompletePaymentRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).CompletePayment(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/CompletePayment",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).CompletePayment(ctx, req.(*CompletePaymentRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ArkService_ServiceDesc is the grpc.ServiceDesc for ArkService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -441,6 +505,14 @@ var ArkService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "Onboard",
|
||||
Handler: _ArkService_Onboard_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreatePayment",
|
||||
Handler: _ArkService_CreatePayment_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CompletePayment",
|
||||
Handler: _ArkService_CompletePayment_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
|
||||
@@ -220,7 +220,7 @@ func post[T any](url, body, key, macaroon, tlsCert string) (result T, err error)
|
||||
req.Header.Add("X-Macaroon", macaroon)
|
||||
}
|
||||
client := &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
},
|
||||
@@ -267,7 +267,7 @@ func get[T any](url, key, macaroon, tlsCert string) (result T, err error) {
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
},
|
||||
@@ -331,7 +331,7 @@ func getBalance(url, macaroon, tlsCert string) (*balance, error) {
|
||||
req.Header.Add("X-Macaroon", macaroon)
|
||||
}
|
||||
client := &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
},
|
||||
@@ -384,7 +384,7 @@ func getStatus(url, tlsCert string) (*status, error) {
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
},
|
||||
|
||||
@@ -45,6 +45,8 @@ type AdminService interface {
|
||||
GetScheduledSweeps(ctx context.Context) ([]ScheduledSweep, error)
|
||||
GetRoundDetails(ctx context.Context, roundId string) (*RoundDetails, error)
|
||||
GetRounds(ctx context.Context, after int64, before int64) ([]string, error)
|
||||
GetWalletAddress(ctx context.Context) (string, error)
|
||||
GetWalletStatus(ctx context.Context) (*WalletStatus, error)
|
||||
}
|
||||
|
||||
type adminService struct {
|
||||
@@ -154,3 +156,24 @@ func (a *adminService) GetScheduledSweeps(ctx context.Context) ([]ScheduledSweep
|
||||
|
||||
return scheduledSweeps, nil
|
||||
}
|
||||
|
||||
func (a *adminService) GetWalletAddress(ctx context.Context) (string, error) {
|
||||
addresses, err := a.walletSvc.DeriveAddresses(ctx, 1)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return addresses[0], nil
|
||||
}
|
||||
|
||||
func (a *adminService) GetWalletStatus(ctx context.Context) (*WalletStatus, error) {
|
||||
status, err := a.walletSvc.Status(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &WalletStatus{
|
||||
IsInitialized: status.IsInitialized(),
|
||||
IsUnlocked: status.IsUnlocked(),
|
||||
IsSynced: status.IsSynced(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -161,6 +161,14 @@ func (s *covenantService) UpdatePaymentStatus(_ context.Context, id string) ([]s
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (s *covenantService) CompleteAsyncPayment(ctx context.Context, redeemTx string, unconditionalForfeitTxs []string) error {
|
||||
return fmt.Errorf("unimplemented")
|
||||
}
|
||||
|
||||
func (s *covenantService) CreateAsyncPayment(ctx context.Context, inputs []domain.VtxoKey, receivers []domain.Receiver) (string, []string, error) {
|
||||
return "", nil, fmt.Errorf("unimplemented")
|
||||
}
|
||||
|
||||
func (s *covenantService) SignVtxos(ctx context.Context, forfeitTxs []string) error {
|
||||
return s.forfeitTxs.sign(forfeitTxs)
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ type covenantlessService struct {
|
||||
onboardingCh chan onboarding
|
||||
|
||||
currentRound *domain.Round
|
||||
|
||||
asyncPaymentsCache map[domain.VtxoKey]struct {
|
||||
receivers []domain.Receiver
|
||||
expireAt int64
|
||||
}
|
||||
}
|
||||
|
||||
func NewCovenantlessService(
|
||||
@@ -62,13 +67,30 @@ func NewCovenantlessService(
|
||||
}
|
||||
|
||||
sweeper := newSweeper(walletSvc, repoManager, builder, scheduler)
|
||||
asyncPaymentsCache := make(map[domain.VtxoKey]struct {
|
||||
receivers []domain.Receiver
|
||||
expireAt int64
|
||||
})
|
||||
|
||||
svc := &covenantlessService{
|
||||
network, pubkey,
|
||||
roundLifetime, roundInterval, unilateralExitDelay, minRelayFee,
|
||||
walletSvc, repoManager, builder, scanner, sweeper,
|
||||
paymentRequests, forfeitTxs, eventsCh, onboardingCh, nil,
|
||||
network: network,
|
||||
pubkey: pubkey,
|
||||
roundLifetime: roundLifetime,
|
||||
roundInterval: roundInterval,
|
||||
unilateralExitDelay: unilateralExitDelay,
|
||||
minRelayFee: minRelayFee,
|
||||
wallet: walletSvc,
|
||||
repoManager: repoManager,
|
||||
builder: builder,
|
||||
scanner: scanner,
|
||||
sweeper: sweeper,
|
||||
paymentRequests: paymentRequests,
|
||||
forfeitTxs: forfeitTxs,
|
||||
eventsCh: eventsCh,
|
||||
onboardingCh: onboardingCh,
|
||||
asyncPaymentsCache: asyncPaymentsCache,
|
||||
}
|
||||
|
||||
repoManager.RegisterEventsHandler(
|
||||
func(round *domain.Round) {
|
||||
go svc.propagateEvents(round)
|
||||
@@ -115,6 +137,108 @@ func (s *covenantlessService) Stop() {
|
||||
close(s.onboardingCh)
|
||||
}
|
||||
|
||||
func (s *covenantlessService) CompleteAsyncPayment(
|
||||
ctx context.Context, redeemTx string, unconditionalForfeitTxs []string,
|
||||
) error {
|
||||
// TODO check that the user signed both transactions
|
||||
|
||||
redeemPtx, err := psbt.NewFromRawBytes(strings.NewReader(redeemTx), true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse redeem tx: %s", err)
|
||||
}
|
||||
redeemTxid := redeemPtx.UnsignedTx.TxID()
|
||||
|
||||
spentVtxos := make([]domain.VtxoKey, 0, len(unconditionalForfeitTxs))
|
||||
for _, in := range redeemPtx.UnsignedTx.TxIn {
|
||||
spentVtxos = append(spentVtxos, domain.VtxoKey{
|
||||
Txid: in.PreviousOutPoint.Hash.String(),
|
||||
VOut: in.PreviousOutPoint.Index,
|
||||
})
|
||||
}
|
||||
|
||||
asyncPayData, ok := s.asyncPaymentsCache[spentVtxos[0]]
|
||||
if !ok {
|
||||
return fmt.Errorf("async payment not found")
|
||||
}
|
||||
|
||||
vtxos := make([]domain.Vtxo, 0, len(asyncPayData.receivers))
|
||||
for i, receiver := range asyncPayData.receivers {
|
||||
vtxos = append(vtxos, domain.Vtxo{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: redeemTxid,
|
||||
VOut: uint32(i),
|
||||
},
|
||||
Receiver: receiver,
|
||||
ExpireAt: asyncPayData.expireAt,
|
||||
AsyncPayment: &domain.AsyncPaymentTxs{
|
||||
RedeemTx: redeemTx,
|
||||
UnconditionalForfeitTxs: unconditionalForfeitTxs,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if err := s.repoManager.Vtxos().AddVtxos(ctx, vtxos); err != nil {
|
||||
return fmt.Errorf("failed to add vtxos: %s", err)
|
||||
}
|
||||
log.Infof("added %d vtxos", len(vtxos))
|
||||
|
||||
if err := s.repoManager.Vtxos().SpendVtxos(ctx, spentVtxos, redeemTxid); err != nil {
|
||||
return fmt.Errorf("failed to spend vtxo: %s", err)
|
||||
}
|
||||
log.Infof("spent %d vtxos", len(spentVtxos))
|
||||
|
||||
delete(s.asyncPaymentsCache, spentVtxos[0])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *covenantlessService) CreateAsyncPayment(
|
||||
ctx context.Context, inputs []domain.VtxoKey, receivers []domain.Receiver,
|
||||
) (string, []string, error) {
|
||||
vtxos, err := s.repoManager.Vtxos().GetVtxos(ctx, inputs)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if len(vtxos) <= 0 {
|
||||
return "", nil, fmt.Errorf("vtxos not found")
|
||||
}
|
||||
|
||||
expiration := vtxos[0].ExpireAt
|
||||
for _, vtxo := range vtxos {
|
||||
if vtxo.Spent {
|
||||
return "", nil, fmt.Errorf("all vtxos must be unspent")
|
||||
}
|
||||
|
||||
if vtxo.Redeemed {
|
||||
return "", nil, fmt.Errorf("all vtxos must be redeemed")
|
||||
}
|
||||
|
||||
if vtxo.Swept {
|
||||
return "", nil, fmt.Errorf("all vtxos must be swept")
|
||||
}
|
||||
if vtxo.ExpireAt < expiration {
|
||||
expiration = vtxo.ExpireAt
|
||||
}
|
||||
}
|
||||
|
||||
res, err := s.builder.BuildAsyncPaymentTransactions(
|
||||
vtxos, s.pubkey, receivers, s.minRelayFee,
|
||||
)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("failed to build async payment txs: %s", err)
|
||||
}
|
||||
|
||||
s.asyncPaymentsCache[inputs[0]] = struct {
|
||||
receivers []domain.Receiver
|
||||
expireAt int64
|
||||
}{
|
||||
receivers: receivers,
|
||||
expireAt: expiration,
|
||||
}
|
||||
|
||||
return res.RedeemTx, res.UnconditionalForfeitTxs, nil
|
||||
}
|
||||
|
||||
func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []domain.VtxoKey) (string, error) {
|
||||
vtxos, err := s.repoManager.Vtxos().GetVtxos(ctx, inputs)
|
||||
if err != nil {
|
||||
|
||||
@@ -149,6 +149,7 @@ func (s *sweeper) createTask(
|
||||
for _, input := range inputs {
|
||||
// sweepableVtxos related to the sweep input
|
||||
sweepableVtxos := make([]domain.VtxoKey, 0)
|
||||
fmt.Println("input", input.GetHash().String(), input.GetIndex())
|
||||
|
||||
// check if input is the vtxo itself
|
||||
vtxos, _ := s.repoManager.Vtxos().GetVtxos(
|
||||
|
||||
@@ -34,6 +34,13 @@ type Service interface {
|
||||
ctx context.Context, boardingTx string,
|
||||
congestionTree tree.CongestionTree, userPubkey *secp256k1.PublicKey,
|
||||
) error
|
||||
// Async payments
|
||||
CreateAsyncPayment(
|
||||
ctx context.Context, inputs []domain.VtxoKey, receivers []domain.Receiver,
|
||||
) (string, []string, error)
|
||||
CompleteAsyncPayment(
|
||||
ctx context.Context, redeemTx string, unconditionalForfeitTxs []string,
|
||||
) error
|
||||
}
|
||||
|
||||
type ServiceInfo struct {
|
||||
|
||||
@@ -130,10 +130,16 @@ func (r Receiver) IsOnchain() bool {
|
||||
type Vtxo struct {
|
||||
VtxoKey
|
||||
Receiver
|
||||
PoolTx string
|
||||
SpentBy string
|
||||
Spent bool
|
||||
Redeemed bool
|
||||
Swept bool
|
||||
ExpireAt int64
|
||||
PoolTx string
|
||||
SpentBy string // round txid or async redeem txid
|
||||
Spent bool
|
||||
Redeemed bool
|
||||
Swept bool
|
||||
ExpireAt int64
|
||||
AsyncPayment *AsyncPaymentTxs // nil if not async vtxo
|
||||
}
|
||||
|
||||
type AsyncPaymentTxs struct {
|
||||
RedeemTx string // always signed by the ASP when created
|
||||
UnconditionalForfeitTxs []string
|
||||
}
|
||||
|
||||
@@ -29,4 +29,8 @@ type TxBuilder interface {
|
||||
FinalizeAndExtractForfeit(tx string) (txhex string, err error)
|
||||
// FindLeaves returns all the leaves txs that are reachable from the given outpoint
|
||||
FindLeaves(congestionTree tree.CongestionTree, fromtxid string, vout uint32) (leaves []tree.Node, err error)
|
||||
BuildAsyncPaymentTransactions(
|
||||
vtxosToSpend []domain.Vtxo,
|
||||
aspPubKey *secp256k1.PublicKey, receivers []domain.Receiver, minRelayFee uint64,
|
||||
) (*domain.AsyncPaymentTxs, error)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ type WalletService interface {
|
||||
SelectUtxos(ctx context.Context, asset string, amount uint64) ([]TxInput, uint64, error)
|
||||
BroadcastTransaction(ctx context.Context, txHex string) (string, error)
|
||||
WaitForSync(ctx context.Context, txid string) error
|
||||
EstimateFees(ctx context.Context, pset string) (uint64, error)
|
||||
EstimateFees(ctx context.Context, psbt string) (uint64, error)
|
||||
ListConnectorUtxos(ctx context.Context, connectorAddress string) ([]TxInput, error)
|
||||
MainAccountBalance(ctx context.Context) (uint64, uint64, error)
|
||||
ConnectorsAccountBalance(ctx context.Context) (uint64, uint64, error)
|
||||
|
||||
@@ -41,8 +41,17 @@ CREATE TABLE IF NOT EXISTS tx (
|
||||
FOREIGN KEY (round_id) REFERENCES round(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS uncond_forfeit_tx (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
tx TEXT NOT NULL,
|
||||
vtxo_txid TEXT NOT NULL,
|
||||
vtxo_vout INTEGER NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
FOREIGN KEY (vtxo_txid, vtxo_vout) REFERENCES vtxo(txid, vout)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vtxo (
|
||||
txid TEXT NOT NULL PRIMARY KEY,
|
||||
txid TEXT NOT NULL,
|
||||
vout INTEGER NOT NULL,
|
||||
pubkey TEXT NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
@@ -53,6 +62,8 @@ CREATE TABLE IF NOT EXISTS vtxo (
|
||||
swept BOOLEAN NOT NULL,
|
||||
expire_at INTEGER NOT NULL,
|
||||
payment_id TEXT,
|
||||
redeem_tx TEXT,
|
||||
PRIMARY KEY (txid, vout),
|
||||
FOREIGN KEY (payment_id) REFERENCES payment(id)
|
||||
);
|
||||
|
||||
@@ -74,4 +85,9 @@ ON payment.id=receiver.payment_id;
|
||||
CREATE VIEW payment_vtxo_vw AS SELECT vtxo.*
|
||||
FROM payment
|
||||
LEFT OUTER JOIN vtxo
|
||||
ON payment.id=vtxo.payment_id;
|
||||
ON payment.id=vtxo.payment_id;
|
||||
|
||||
CREATE VIEW uncond_forfeit_tx_vw AS SELECT uncond_forfeit_tx.*
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx
|
||||
ON vtxo.txid=uncond_forfeit_tx.vtxo_txid AND vtxo.vout=uncond_forfeit_tx.vtxo_vout;
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.26.0
|
||||
// sqlc v1.27.0
|
||||
|
||||
package queries
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.26.0
|
||||
// sqlc v1.27.0
|
||||
|
||||
package queries
|
||||
|
||||
@@ -32,6 +32,7 @@ type PaymentVtxoVw struct {
|
||||
Swept sql.NullBool
|
||||
ExpireAt sql.NullInt64
|
||||
PaymentID sql.NullString
|
||||
RedeemTx sql.NullString
|
||||
}
|
||||
|
||||
type Receiver struct {
|
||||
@@ -85,6 +86,22 @@ type Tx struct {
|
||||
IsLeaf sql.NullBool
|
||||
}
|
||||
|
||||
type UncondForfeitTx struct {
|
||||
ID int64
|
||||
Tx string
|
||||
VtxoTxid string
|
||||
VtxoVout int64
|
||||
Position int64
|
||||
}
|
||||
|
||||
type UncondForfeitTxVw struct {
|
||||
ID sql.NullInt64
|
||||
Tx sql.NullString
|
||||
VtxoTxid sql.NullString
|
||||
VtxoVout sql.NullInt64
|
||||
Position sql.NullInt64
|
||||
}
|
||||
|
||||
type Vtxo struct {
|
||||
Txid string
|
||||
Vout int64
|
||||
@@ -97,4 +114,5 @@ type Vtxo struct {
|
||||
Swept bool
|
||||
ExpireAt int64
|
||||
PaymentID sql.NullString
|
||||
RedeemTx sql.NullString
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.26.0
|
||||
// sqlc v1.27.0
|
||||
// source: query.sql
|
||||
|
||||
package queries
|
||||
@@ -54,30 +54,45 @@ func (q *Queries) MarkVtxoAsSwept(ctx context.Context, arg MarkVtxoAsSweptParams
|
||||
}
|
||||
|
||||
const selectNotRedeemedVtxos = `-- name: SelectNotRedeemedVtxos :many
|
||||
SELECT txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, payment_id FROM vtxo WHERE redeemed = false
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx,
|
||||
uncond_forfeit_tx_vw.id, uncond_forfeit_tx_vw.tx, uncond_forfeit_tx_vw.vtxo_txid, uncond_forfeit_tx_vw.vtxo_vout, uncond_forfeit_tx_vw.position
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout
|
||||
WHERE redeemed = false
|
||||
`
|
||||
|
||||
func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]Vtxo, error) {
|
||||
type SelectNotRedeemedVtxosRow struct {
|
||||
Vtxo Vtxo
|
||||
UncondForfeitTxVw UncondForfeitTxVw
|
||||
}
|
||||
|
||||
func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeemedVtxosRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, selectNotRedeemedVtxos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Vtxo
|
||||
var items []SelectNotRedeemedVtxosRow
|
||||
for rows.Next() {
|
||||
var i Vtxo
|
||||
var i SelectNotRedeemedVtxosRow
|
||||
if err := rows.Scan(
|
||||
&i.Txid,
|
||||
&i.Vout,
|
||||
&i.Pubkey,
|
||||
&i.Amount,
|
||||
&i.PoolTx,
|
||||
&i.SpentBy,
|
||||
&i.Spent,
|
||||
&i.Redeemed,
|
||||
&i.Swept,
|
||||
&i.ExpireAt,
|
||||
&i.PaymentID,
|
||||
&i.Vtxo.Txid,
|
||||
&i.Vtxo.Vout,
|
||||
&i.Vtxo.Pubkey,
|
||||
&i.Vtxo.Amount,
|
||||
&i.Vtxo.PoolTx,
|
||||
&i.Vtxo.SpentBy,
|
||||
&i.Vtxo.Spent,
|
||||
&i.Vtxo.Redeemed,
|
||||
&i.Vtxo.Swept,
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.UncondForfeitTxVw.ID,
|
||||
&i.UncondForfeitTxVw.Tx,
|
||||
&i.UncondForfeitTxVw.VtxoTxid,
|
||||
&i.UncondForfeitTxVw.VtxoVout,
|
||||
&i.UncondForfeitTxVw.Position,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -93,30 +108,45 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]Vtxo, error) {
|
||||
}
|
||||
|
||||
const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many
|
||||
SELECT txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, payment_id FROM vtxo WHERE redeemed = false AND pubkey = ?
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx,
|
||||
uncond_forfeit_tx_vw.id, uncond_forfeit_tx_vw.tx, uncond_forfeit_tx_vw.vtxo_txid, uncond_forfeit_tx_vw.vtxo_vout, uncond_forfeit_tx_vw.position
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout
|
||||
WHERE redeemed = false AND pubkey = ?
|
||||
`
|
||||
|
||||
func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, pubkey string) ([]Vtxo, error) {
|
||||
type SelectNotRedeemedVtxosWithPubkeyRow struct {
|
||||
Vtxo Vtxo
|
||||
UncondForfeitTxVw UncondForfeitTxVw
|
||||
}
|
||||
|
||||
func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, pubkey string) ([]SelectNotRedeemedVtxosWithPubkeyRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, selectNotRedeemedVtxosWithPubkey, pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Vtxo
|
||||
var items []SelectNotRedeemedVtxosWithPubkeyRow
|
||||
for rows.Next() {
|
||||
var i Vtxo
|
||||
var i SelectNotRedeemedVtxosWithPubkeyRow
|
||||
if err := rows.Scan(
|
||||
&i.Txid,
|
||||
&i.Vout,
|
||||
&i.Pubkey,
|
||||
&i.Amount,
|
||||
&i.PoolTx,
|
||||
&i.SpentBy,
|
||||
&i.Spent,
|
||||
&i.Redeemed,
|
||||
&i.Swept,
|
||||
&i.ExpireAt,
|
||||
&i.PaymentID,
|
||||
&i.Vtxo.Txid,
|
||||
&i.Vtxo.Vout,
|
||||
&i.Vtxo.Pubkey,
|
||||
&i.Vtxo.Amount,
|
||||
&i.Vtxo.PoolTx,
|
||||
&i.Vtxo.SpentBy,
|
||||
&i.Vtxo.Spent,
|
||||
&i.Vtxo.Redeemed,
|
||||
&i.Vtxo.Swept,
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.UncondForfeitTxVw.ID,
|
||||
&i.UncondForfeitTxVw.Tx,
|
||||
&i.UncondForfeitTxVw.VtxoTxid,
|
||||
&i.UncondForfeitTxVw.VtxoVout,
|
||||
&i.UncondForfeitTxVw.Position,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -195,7 +225,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
|
||||
round_payment_vw.id, round_payment_vw.round_id,
|
||||
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.amount, payment_receiver_vw.onchain_address,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx
|
||||
FROM round
|
||||
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
|
||||
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
|
||||
@@ -260,6 +290,7 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele
|
||||
&i.PaymentVtxoVw.Swept,
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -279,7 +310,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
|
||||
round_payment_vw.id, round_payment_vw.round_id,
|
||||
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.amount, payment_receiver_vw.onchain_address,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx
|
||||
FROM round
|
||||
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
|
||||
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
|
||||
@@ -344,6 +375,7 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]
|
||||
&i.PaymentVtxoVw.Swept,
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -363,7 +395,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
|
||||
round_payment_vw.id, round_payment_vw.round_id,
|
||||
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.amount, payment_receiver_vw.onchain_address,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx
|
||||
FROM round
|
||||
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
|
||||
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
|
||||
@@ -428,6 +460,7 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
|
||||
&i.PaymentVtxoVw.Swept,
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -443,30 +476,45 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
|
||||
}
|
||||
|
||||
const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many
|
||||
SELECT txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, payment_id FROM vtxo WHERE redeemed = false AND swept = false
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx,
|
||||
uncond_forfeit_tx_vw.id, uncond_forfeit_tx_vw.tx, uncond_forfeit_tx_vw.vtxo_txid, uncond_forfeit_tx_vw.vtxo_vout, uncond_forfeit_tx_vw.position
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout
|
||||
WHERE redeemed = false AND swept = false
|
||||
`
|
||||
|
||||
func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]Vtxo, error) {
|
||||
type SelectSweepableVtxosRow struct {
|
||||
Vtxo Vtxo
|
||||
UncondForfeitTxVw UncondForfeitTxVw
|
||||
}
|
||||
|
||||
func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVtxosRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, selectSweepableVtxos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Vtxo
|
||||
var items []SelectSweepableVtxosRow
|
||||
for rows.Next() {
|
||||
var i Vtxo
|
||||
var i SelectSweepableVtxosRow
|
||||
if err := rows.Scan(
|
||||
&i.Txid,
|
||||
&i.Vout,
|
||||
&i.Pubkey,
|
||||
&i.Amount,
|
||||
&i.PoolTx,
|
||||
&i.SpentBy,
|
||||
&i.Spent,
|
||||
&i.Redeemed,
|
||||
&i.Swept,
|
||||
&i.ExpireAt,
|
||||
&i.PaymentID,
|
||||
&i.Vtxo.Txid,
|
||||
&i.Vtxo.Vout,
|
||||
&i.Vtxo.Pubkey,
|
||||
&i.Vtxo.Amount,
|
||||
&i.Vtxo.PoolTx,
|
||||
&i.Vtxo.SpentBy,
|
||||
&i.Vtxo.Spent,
|
||||
&i.Vtxo.Redeemed,
|
||||
&i.Vtxo.Swept,
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.UncondForfeitTxVw.ID,
|
||||
&i.UncondForfeitTxVw.Tx,
|
||||
&i.UncondForfeitTxVw.VtxoTxid,
|
||||
&i.UncondForfeitTxVw.VtxoVout,
|
||||
&i.UncondForfeitTxVw.Position,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -486,7 +534,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
|
||||
round_payment_vw.id, round_payment_vw.round_id,
|
||||
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
|
||||
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.amount, payment_receiver_vw.onchain_address,
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id
|
||||
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx
|
||||
FROM round
|
||||
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
|
||||
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
|
||||
@@ -551,6 +599,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
|
||||
&i.PaymentVtxoVw.Swept,
|
||||
&i.PaymentVtxoVw.ExpireAt,
|
||||
&i.PaymentVtxoVw.PaymentID,
|
||||
&i.PaymentVtxoVw.RedeemTx,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -566,7 +615,11 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
|
||||
}
|
||||
|
||||
const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one
|
||||
SELECT txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, payment_id FROM vtxo WHERE txid = ? AND vout = ?
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx,
|
||||
uncond_forfeit_tx_vw.id, uncond_forfeit_tx_vw.tx, uncond_forfeit_tx_vw.vtxo_txid, uncond_forfeit_tx_vw.vtxo_vout, uncond_forfeit_tx_vw.position
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout
|
||||
WHERE txid = ? AND vout = ?
|
||||
`
|
||||
|
||||
type SelectVtxoByOutpointParams struct {
|
||||
@@ -574,50 +627,76 @@ type SelectVtxoByOutpointParams struct {
|
||||
Vout int64
|
||||
}
|
||||
|
||||
func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutpointParams) (Vtxo, error) {
|
||||
type SelectVtxoByOutpointRow struct {
|
||||
Vtxo Vtxo
|
||||
UncondForfeitTxVw UncondForfeitTxVw
|
||||
}
|
||||
|
||||
func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutpointParams) (SelectVtxoByOutpointRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, selectVtxoByOutpoint, arg.Txid, arg.Vout)
|
||||
var i Vtxo
|
||||
var i SelectVtxoByOutpointRow
|
||||
err := row.Scan(
|
||||
&i.Txid,
|
||||
&i.Vout,
|
||||
&i.Pubkey,
|
||||
&i.Amount,
|
||||
&i.PoolTx,
|
||||
&i.SpentBy,
|
||||
&i.Spent,
|
||||
&i.Redeemed,
|
||||
&i.Swept,
|
||||
&i.ExpireAt,
|
||||
&i.PaymentID,
|
||||
&i.Vtxo.Txid,
|
||||
&i.Vtxo.Vout,
|
||||
&i.Vtxo.Pubkey,
|
||||
&i.Vtxo.Amount,
|
||||
&i.Vtxo.PoolTx,
|
||||
&i.Vtxo.SpentBy,
|
||||
&i.Vtxo.Spent,
|
||||
&i.Vtxo.Redeemed,
|
||||
&i.Vtxo.Swept,
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.UncondForfeitTxVw.ID,
|
||||
&i.UncondForfeitTxVw.Tx,
|
||||
&i.UncondForfeitTxVw.VtxoTxid,
|
||||
&i.UncondForfeitTxVw.VtxoVout,
|
||||
&i.UncondForfeitTxVw.Position,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const selectVtxosByPoolTxid = `-- name: SelectVtxosByPoolTxid :many
|
||||
SELECT txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, payment_id FROM vtxo WHERE pool_tx = ?
|
||||
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx,
|
||||
uncond_forfeit_tx_vw.id, uncond_forfeit_tx_vw.tx, uncond_forfeit_tx_vw.vtxo_txid, uncond_forfeit_tx_vw.vtxo_vout, uncond_forfeit_tx_vw.position
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout
|
||||
WHERE pool_tx = ?
|
||||
`
|
||||
|
||||
func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]Vtxo, error) {
|
||||
type SelectVtxosByPoolTxidRow struct {
|
||||
Vtxo Vtxo
|
||||
UncondForfeitTxVw UncondForfeitTxVw
|
||||
}
|
||||
|
||||
func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]SelectVtxosByPoolTxidRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, selectVtxosByPoolTxid, poolTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Vtxo
|
||||
var items []SelectVtxosByPoolTxidRow
|
||||
for rows.Next() {
|
||||
var i Vtxo
|
||||
var i SelectVtxosByPoolTxidRow
|
||||
if err := rows.Scan(
|
||||
&i.Txid,
|
||||
&i.Vout,
|
||||
&i.Pubkey,
|
||||
&i.Amount,
|
||||
&i.PoolTx,
|
||||
&i.SpentBy,
|
||||
&i.Spent,
|
||||
&i.Redeemed,
|
||||
&i.Swept,
|
||||
&i.ExpireAt,
|
||||
&i.PaymentID,
|
||||
&i.Vtxo.Txid,
|
||||
&i.Vtxo.Vout,
|
||||
&i.Vtxo.Pubkey,
|
||||
&i.Vtxo.Amount,
|
||||
&i.Vtxo.PoolTx,
|
||||
&i.Vtxo.SpentBy,
|
||||
&i.Vtxo.Spent,
|
||||
&i.Vtxo.Redeemed,
|
||||
&i.Vtxo.Swept,
|
||||
&i.Vtxo.ExpireAt,
|
||||
&i.Vtxo.PaymentID,
|
||||
&i.Vtxo.RedeemTx,
|
||||
&i.UncondForfeitTxVw.ID,
|
||||
&i.UncondForfeitTxVw.Tx,
|
||||
&i.UncondForfeitTxVw.VtxoTxid,
|
||||
&i.UncondForfeitTxVw.VtxoVout,
|
||||
&i.UncondForfeitTxVw.Position,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -803,18 +882,44 @@ func (q *Queries) UpsertTransaction(ctx context.Context, arg UpsertTransactionPa
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertUnconditionalForfeitTx = `-- name: UpsertUnconditionalForfeitTx :exec
|
||||
INSERT INTO uncond_forfeit_tx (tx, vtxo_txid, vtxo_vout, position)
|
||||
VALUES (?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET
|
||||
tx = EXCLUDED.tx,
|
||||
vtxo_txid = EXCLUDED.vtxo_txid,
|
||||
vtxo_vout = EXCLUDED.vtxo_vout,
|
||||
position = EXCLUDED.position
|
||||
`
|
||||
|
||||
type UpsertUnconditionalForfeitTxParams struct {
|
||||
Tx string
|
||||
VtxoTxid string
|
||||
VtxoVout int64
|
||||
Position int64
|
||||
}
|
||||
|
||||
func (q *Queries) UpsertUnconditionalForfeitTx(ctx context.Context, arg UpsertUnconditionalForfeitTxParams) error {
|
||||
_, err := q.db.ExecContext(ctx, upsertUnconditionalForfeitTx,
|
||||
arg.Tx,
|
||||
arg.VtxoTxid,
|
||||
arg.VtxoVout,
|
||||
arg.Position,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertVtxo = `-- name: UpsertVtxo :exec
|
||||
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid) DO UPDATE SET
|
||||
vout = excluded.vout,
|
||||
pubkey = excluded.pubkey,
|
||||
amount = excluded.amount,
|
||||
pool_tx = excluded.pool_tx,
|
||||
spent_by = excluded.spent_by,
|
||||
spent = excluded.spent,
|
||||
redeemed = excluded.redeemed,
|
||||
swept = excluded.swept,
|
||||
expire_at = excluded.expire_at
|
||||
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
|
||||
pubkey = EXCLUDED.pubkey,
|
||||
amount = EXCLUDED.amount,
|
||||
pool_tx = EXCLUDED.pool_tx,
|
||||
spent_by = EXCLUDED.spent_by,
|
||||
spent = EXCLUDED.spent,
|
||||
redeemed = EXCLUDED.redeemed,
|
||||
swept = EXCLUDED.swept,
|
||||
expire_at = EXCLUDED.expire_at,
|
||||
redeem_tx = EXCLUDED.redeem_tx
|
||||
`
|
||||
|
||||
type UpsertVtxoParams struct {
|
||||
@@ -828,6 +933,7 @@ type UpsertVtxoParams struct {
|
||||
Redeemed bool
|
||||
Swept bool
|
||||
ExpireAt int64
|
||||
RedeemTx sql.NullString
|
||||
}
|
||||
|
||||
func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error {
|
||||
@@ -842,6 +948,7 @@ func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error {
|
||||
arg.Redeemed,
|
||||
arg.Swept,
|
||||
arg.ExpireAt,
|
||||
arg.RedeemTx,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -111,33 +111,61 @@ SELECT id FROM round WHERE starting_timestamp > ? AND starting_timestamp < ?;
|
||||
-- name: SelectRoundIds :many
|
||||
SELECT id FROM round;
|
||||
|
||||
-- name: UpsertUnconditionalForfeitTx :exec
|
||||
INSERT INTO uncond_forfeit_tx (tx, vtxo_txid, vtxo_vout, position)
|
||||
VALUES (?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET
|
||||
tx = EXCLUDED.tx,
|
||||
vtxo_txid = EXCLUDED.vtxo_txid,
|
||||
vtxo_vout = EXCLUDED.vtxo_vout,
|
||||
position = EXCLUDED.position;
|
||||
|
||||
-- name: UpsertVtxo :exec
|
||||
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid) DO UPDATE SET
|
||||
vout = excluded.vout,
|
||||
pubkey = excluded.pubkey,
|
||||
amount = excluded.amount,
|
||||
pool_tx = excluded.pool_tx,
|
||||
spent_by = excluded.spent_by,
|
||||
spent = excluded.spent,
|
||||
redeemed = excluded.redeemed,
|
||||
swept = excluded.swept,
|
||||
expire_at = excluded.expire_at;
|
||||
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
|
||||
pubkey = EXCLUDED.pubkey,
|
||||
amount = EXCLUDED.amount,
|
||||
pool_tx = EXCLUDED.pool_tx,
|
||||
spent_by = EXCLUDED.spent_by,
|
||||
spent = EXCLUDED.spent,
|
||||
redeemed = EXCLUDED.redeemed,
|
||||
swept = EXCLUDED.swept,
|
||||
expire_at = EXCLUDED.expire_at,
|
||||
redeem_tx = EXCLUDED.redeem_tx;
|
||||
|
||||
-- name: SelectSweepableVtxos :many
|
||||
SELECT * FROM vtxo WHERE redeemed = false AND swept = false;
|
||||
SELECT sqlc.embed(vtxo),
|
||||
sqlc.embed(uncond_forfeit_tx_vw)
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout
|
||||
WHERE redeemed = false AND swept = false;
|
||||
|
||||
-- name: SelectNotRedeemedVtxos :many
|
||||
SELECT * FROM vtxo WHERE redeemed = false;
|
||||
SELECT sqlc.embed(vtxo),
|
||||
sqlc.embed(uncond_forfeit_tx_vw)
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout
|
||||
WHERE redeemed = false;
|
||||
|
||||
-- name: SelectNotRedeemedVtxosWithPubkey :many
|
||||
SELECT * FROM vtxo WHERE redeemed = false AND pubkey = ?;
|
||||
SELECT sqlc.embed(vtxo),
|
||||
sqlc.embed(uncond_forfeit_tx_vw)
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout
|
||||
WHERE redeemed = false AND pubkey = ?;
|
||||
|
||||
-- name: SelectVtxoByOutpoint :one
|
||||
SELECT * FROM vtxo WHERE txid = ? AND vout = ?;
|
||||
SELECT sqlc.embed(vtxo),
|
||||
sqlc.embed(uncond_forfeit_tx_vw)
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout
|
||||
WHERE txid = ? AND vout = ?;
|
||||
|
||||
-- name: SelectVtxosByPoolTxid :many
|
||||
SELECT * FROM vtxo WHERE pool_tx = ?;
|
||||
SELECT sqlc.embed(vtxo),
|
||||
sqlc.embed(uncond_forfeit_tx_vw)
|
||||
FROM vtxo
|
||||
LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout
|
||||
WHERE pool_tx = ?;
|
||||
|
||||
-- name: MarkVtxoAsRedeemed :exec
|
||||
UPDATE vtxo SET redeemed = true WHERE txid = ? AND vout = ?;
|
||||
|
||||
@@ -35,10 +35,14 @@ func (v *vxtoRepository) Close() {
|
||||
|
||||
func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) error {
|
||||
txBody := func(querierWithTx *queries.Queries) error {
|
||||
for _, vtxo := range vtxos {
|
||||
for i := range vtxos {
|
||||
vtxo := vtxos[i]
|
||||
var redeemTx string
|
||||
if vtxo.AsyncPayment != nil {
|
||||
redeemTx = vtxo.AsyncPayment.RedeemTx
|
||||
}
|
||||
if err := querierWithTx.UpsertVtxo(
|
||||
ctx,
|
||||
queries.UpsertVtxoParams{
|
||||
ctx, queries.UpsertVtxoParams{
|
||||
Txid: vtxo.Txid,
|
||||
Vout: int64(vtxo.VOut),
|
||||
Pubkey: vtxo.Pubkey,
|
||||
@@ -49,10 +53,24 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro
|
||||
Redeemed: vtxo.Redeemed,
|
||||
Swept: vtxo.Swept,
|
||||
ExpireAt: vtxo.ExpireAt,
|
||||
RedeemTx: sql.NullString{String: redeemTx, Valid: true},
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if vtxo.AsyncPayment != nil {
|
||||
for i, tx := range vtxo.AsyncPayment.UnconditionalForfeitTxs {
|
||||
if err := querierWithTx.UpsertUnconditionalForfeitTx(ctx, queries.UpsertUnconditionalForfeitTxParams{
|
||||
Tx: tx,
|
||||
VtxoTxid: vtxo.Txid,
|
||||
VtxoVout: int64(vtxo.VOut),
|
||||
Position: int64(i),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -62,27 +80,49 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro
|
||||
}
|
||||
|
||||
func (v *vxtoRepository) GetAllSweepableVtxos(ctx context.Context) ([]domain.Vtxo, error) {
|
||||
rows, err := v.querier.SelectSweepableVtxos(ctx)
|
||||
res, err := v.querier.SelectSweepableVtxos(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([]vtxoWithUnconditionalForfeitTxs, 0, len(res))
|
||||
for _, row := range res {
|
||||
rows = append(rows, vtxoWithUnconditionalForfeitTxs{
|
||||
vtxo: row.Vtxo,
|
||||
tx: row.UncondForfeitTxVw,
|
||||
})
|
||||
}
|
||||
return readRows(rows)
|
||||
}
|
||||
|
||||
func (v *vxtoRepository) GetAllVtxos(ctx context.Context, pubkey string) ([]domain.Vtxo, []domain.Vtxo, error) {
|
||||
withPubkey := len(pubkey) > 0
|
||||
|
||||
var rows []queries.Vtxo
|
||||
var err error
|
||||
|
||||
var rows []vtxoWithUnconditionalForfeitTxs
|
||||
if withPubkey {
|
||||
rows, err = v.querier.SelectNotRedeemedVtxosWithPubkey(ctx, pubkey)
|
||||
res, err := v.querier.SelectNotRedeemedVtxosWithPubkey(ctx, pubkey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rows = make([]vtxoWithUnconditionalForfeitTxs, 0, len(res))
|
||||
for _, row := range res {
|
||||
rows = append(rows, vtxoWithUnconditionalForfeitTxs{
|
||||
vtxo: row.Vtxo,
|
||||
tx: row.UncondForfeitTxVw,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
rows, err = v.querier.SelectNotRedeemedVtxos(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
res, err := v.querier.SelectNotRedeemedVtxos(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rows = make([]vtxoWithUnconditionalForfeitTxs, 0, len(res))
|
||||
for _, row := range res {
|
||||
rows = append(rows, vtxoWithUnconditionalForfeitTxs{
|
||||
vtxo: row.Vtxo,
|
||||
tx: row.UncondForfeitTxVw,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
vtxos, err := readRows(rows)
|
||||
@@ -107,7 +147,7 @@ func (v *vxtoRepository) GetAllVtxos(ctx context.Context, pubkey string) ([]doma
|
||||
func (v *vxtoRepository) GetVtxos(ctx context.Context, outpoints []domain.VtxoKey) ([]domain.Vtxo, error) {
|
||||
vtxos := make([]domain.Vtxo, 0, len(outpoints))
|
||||
for _, o := range outpoints {
|
||||
vtxo, err := v.querier.SelectVtxoByOutpoint(
|
||||
res, err := v.querier.SelectVtxoByOutpoint(
|
||||
ctx,
|
||||
queries.SelectVtxoByOutpointParams{
|
||||
Txid: o.Txid,
|
||||
@@ -118,7 +158,12 @@ func (v *vxtoRepository) GetVtxos(ctx context.Context, outpoints []domain.VtxoKe
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := readRows([]queries.Vtxo{vtxo})
|
||||
result, err := readRows([]vtxoWithUnconditionalForfeitTxs{
|
||||
{
|
||||
vtxo: res.Vtxo,
|
||||
tx: res.UncondForfeitTxVw,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -134,10 +179,17 @@ func (v *vxtoRepository) GetVtxos(ctx context.Context, outpoints []domain.VtxoKe
|
||||
}
|
||||
|
||||
func (v *vxtoRepository) GetVtxosForRound(ctx context.Context, txid string) ([]domain.Vtxo, error) {
|
||||
rows, err := v.querier.SelectVtxosByPoolTxid(ctx, txid)
|
||||
res, err := v.querier.SelectVtxosByPoolTxid(ctx, txid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows := make([]vtxoWithUnconditionalForfeitTxs, 0, len(res))
|
||||
for _, row := range res {
|
||||
rows = append(rows, vtxoWithUnconditionalForfeitTxs{
|
||||
vtxo: row.Vtxo,
|
||||
tx: row.UncondForfeitTxVw,
|
||||
})
|
||||
}
|
||||
|
||||
return readRows(rows)
|
||||
}
|
||||
@@ -224,7 +276,18 @@ func (v *vxtoRepository) UpdateExpireAt(ctx context.Context, vtxos []domain.Vtxo
|
||||
return execTx(ctx, v.db, txBody)
|
||||
}
|
||||
|
||||
func rowToVtxo(row queries.Vtxo) domain.Vtxo {
|
||||
func rowToVtxo(row queries.Vtxo, uncondForfeitTxs []queries.UncondForfeitTxVw) domain.Vtxo {
|
||||
var asyncPayment *domain.AsyncPaymentTxs
|
||||
if row.RedeemTx.Valid && len(uncondForfeitTxs) > 0 {
|
||||
txs := make([]string, len(uncondForfeitTxs))
|
||||
for _, tx := range uncondForfeitTxs {
|
||||
txs[tx.Position.Int64] = tx.Tx.String
|
||||
}
|
||||
asyncPayment = &domain.AsyncPaymentTxs{
|
||||
RedeemTx: row.RedeemTx.String,
|
||||
UnconditionalForfeitTxs: txs,
|
||||
}
|
||||
}
|
||||
return domain.Vtxo{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: row.Txid,
|
||||
@@ -234,19 +297,48 @@ func rowToVtxo(row queries.Vtxo) domain.Vtxo {
|
||||
Pubkey: row.Pubkey,
|
||||
Amount: uint64(row.Amount),
|
||||
},
|
||||
PoolTx: row.PoolTx,
|
||||
SpentBy: row.SpentBy,
|
||||
Spent: row.Spent,
|
||||
Redeemed: row.Redeemed,
|
||||
Swept: row.Swept,
|
||||
ExpireAt: row.ExpireAt,
|
||||
PoolTx: row.PoolTx,
|
||||
SpentBy: row.SpentBy,
|
||||
Spent: row.Spent,
|
||||
Redeemed: row.Redeemed,
|
||||
Swept: row.Swept,
|
||||
ExpireAt: row.ExpireAt,
|
||||
AsyncPayment: asyncPayment,
|
||||
}
|
||||
}
|
||||
|
||||
func readRows(rows []queries.Vtxo) ([]domain.Vtxo, error) {
|
||||
type vtxoWithUnconditionalForfeitTxs struct {
|
||||
vtxo queries.Vtxo
|
||||
tx queries.UncondForfeitTxVw
|
||||
}
|
||||
|
||||
func readRows(rows []vtxoWithUnconditionalForfeitTxs) ([]domain.Vtxo, error) {
|
||||
uncondForfeitTxsMap := make(map[domain.VtxoKey][]queries.UncondForfeitTxVw)
|
||||
for _, row := range rows {
|
||||
if !row.vtxo.RedeemTx.Valid {
|
||||
continue
|
||||
}
|
||||
vtxoKey := domain.VtxoKey{
|
||||
Txid: row.vtxo.Txid,
|
||||
VOut: uint32(row.vtxo.Vout),
|
||||
}
|
||||
if _, ok := uncondForfeitTxsMap[vtxoKey]; !ok {
|
||||
uncondForfeitTxsMap[vtxoKey] = make([]queries.UncondForfeitTxVw, 0)
|
||||
}
|
||||
if row.tx.Tx.Valid {
|
||||
uncondForfeitTxsMap[vtxoKey] = append(
|
||||
uncondForfeitTxsMap[vtxoKey], row.tx,
|
||||
)
|
||||
}
|
||||
}
|
||||
vtxos := make([]domain.Vtxo, 0, len(rows))
|
||||
for _, v := range rows {
|
||||
vtxos = append(vtxos, rowToVtxo(v))
|
||||
for _, row := range rows {
|
||||
vtxoKey := domain.VtxoKey{
|
||||
Txid: row.vtxo.Txid,
|
||||
VOut: uint32(row.vtxo.Vout),
|
||||
}
|
||||
uncondForfeitTxs := uncondForfeitTxsMap[vtxoKey]
|
||||
vtxos = append(vtxos, rowToVtxo(row.vtxo, uncondForfeitTxs))
|
||||
}
|
||||
|
||||
return vtxos, nil
|
||||
|
||||
@@ -316,6 +316,12 @@ func (b *txBuilder) FindLeaves(
|
||||
return foundLeaves, nil
|
||||
}
|
||||
|
||||
func (b *txBuilder) BuildAsyncPaymentTransactions(
|
||||
_ []domain.Vtxo, _ *secp256k1.PublicKey, _ []domain.Receiver, _ uint64,
|
||||
) (*domain.AsyncPaymentTxs, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (b *txBuilder) getLeafScriptAndTree(
|
||||
userPubkey, aspPubkey *secp256k1.PublicKey,
|
||||
) ([]byte, *taproot.IndexedElementsTapScriptTree, error) {
|
||||
|
||||
@@ -303,6 +303,171 @@ func (b *txBuilder) FindLeaves(congestionTree tree.CongestionTree, fromtxid stri
|
||||
return foundLeaves, nil
|
||||
}
|
||||
|
||||
// TODO add locktimes to txs
|
||||
func (b *txBuilder) BuildAsyncPaymentTransactions(
|
||||
vtxos []domain.Vtxo, aspPubKey *secp256k1.PublicKey,
|
||||
receivers []domain.Receiver, minRelayFee uint64,
|
||||
) (*domain.AsyncPaymentTxs, error) {
|
||||
if len(vtxos) <= 0 {
|
||||
return nil, fmt.Errorf("missing vtxos")
|
||||
}
|
||||
|
||||
ins := make([]*wire.OutPoint, 0, len(vtxos))
|
||||
outs := make([]*wire.TxOut, 0, len(receivers))
|
||||
unconditionalForfeitTxs := make([]string, 0, len(vtxos))
|
||||
for _, vtxo := range vtxos {
|
||||
if vtxo.Spent {
|
||||
return nil, fmt.Errorf("all vtxos must be unspent")
|
||||
}
|
||||
|
||||
senderBytes, err := hex.DecodeString(vtxo.Pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sender, err := secp256k1.ParsePubKey(senderBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aspScript, err := p2trScript(aspPubKey, b.onchainNetwork())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vtxoTxID, err := chainhash.NewHashFromStr(vtxo.Txid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vtxoOutpoint := &wire.OutPoint{
|
||||
Hash: *vtxoTxID,
|
||||
Index: vtxo.VOut,
|
||||
}
|
||||
|
||||
vtxoScript, vtxoTree, err := b.getLeafScriptAndTree(sender, aspPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output := &wire.TxOut{
|
||||
PkScript: aspScript,
|
||||
Value: int64(vtxo.Amount - minRelayFee),
|
||||
}
|
||||
|
||||
forfeitClosure := &bitcointree.MultisigClosure{
|
||||
Pubkey: sender,
|
||||
AspPubkey: aspPubKey,
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
leafProof := vtxoTree.LeafMerkleProofs[vtxoTree.LeafProofIndex[forfeitLeaf.TapHash()]]
|
||||
ctrlBlock := leafProof.ToControlBlock(bitcointree.UnspendableKey())
|
||||
ctrlBlockBytes, err := ctrlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unconditionnalForfeitPtx, err := psbt.New(
|
||||
[]*wire.OutPoint{vtxoOutpoint},
|
||||
[]*wire.TxOut{output},
|
||||
2,
|
||||
0,
|
||||
[]uint32{wire.MaxTxInSequenceNum},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unconditionnalForfeitPtx.Inputs[0].WitnessUtxo = &wire.TxOut{
|
||||
Value: int64(vtxo.Amount),
|
||||
PkScript: vtxoScript,
|
||||
}
|
||||
|
||||
unconditionnalForfeitPtx.Inputs[0].TaprootInternalKey = schnorr.SerializePubKey(bitcointree.UnspendableKey())
|
||||
unconditionnalForfeitPtx.Inputs[0].TaprootLeafScript = []*psbt.TaprootTapLeafScript{
|
||||
{
|
||||
ControlBlock: ctrlBlockBytes,
|
||||
Script: forfeitLeaf.Script,
|
||||
LeafVersion: txscript.BaseLeafVersion,
|
||||
},
|
||||
}
|
||||
|
||||
forfeitTx, err := unconditionnalForfeitPtx.B64Encode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unconditionalForfeitTxs = append(unconditionalForfeitTxs, forfeitTx)
|
||||
ins = append(ins, vtxoOutpoint)
|
||||
}
|
||||
|
||||
for i, receiver := range receivers {
|
||||
// TODO (@louisinger): Add revert policy (sender+ASP)
|
||||
buf, err := hex.DecodeString(receiver.Pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receiverPk, err := secp256k1.ParsePubKey(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newVtxoScript, _, err := b.getLeafScriptAndTree(receiverPk, aspPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Deduct the min relay fee from the very last receiver which is supposed
|
||||
// to be the change in case it's not a send-all.
|
||||
value := receiver.Amount
|
||||
if i == len(receivers)-1 {
|
||||
value -= minRelayFee
|
||||
}
|
||||
outs = append(outs, &wire.TxOut{
|
||||
Value: int64(value),
|
||||
PkScript: newVtxoScript,
|
||||
})
|
||||
}
|
||||
|
||||
redeemPtx, err := psbt.New(
|
||||
ins, outs, 2, 0, []uint32{wire.MaxTxInSequenceNum},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range redeemPtx.Inputs {
|
||||
unconditionnalForfeitPsbt, _ := psbt.NewFromRawBytes(
|
||||
strings.NewReader(unconditionalForfeitTxs[i]), true,
|
||||
)
|
||||
redeemPtx.Inputs[i].WitnessUtxo = unconditionnalForfeitPsbt.Inputs[0].WitnessUtxo
|
||||
redeemPtx.Inputs[i].TaprootInternalKey = unconditionnalForfeitPsbt.Inputs[0].TaprootInternalKey
|
||||
redeemPtx.Inputs[i].TaprootLeafScript = unconditionnalForfeitPsbt.Inputs[0].TaprootLeafScript
|
||||
|
||||
}
|
||||
|
||||
redeemTx, err := redeemPtx.B64Encode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signedRedeemTx, err := b.wallet.SignTransactionTapscript(
|
||||
context.Background(), redeemTx, nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &domain.AsyncPaymentTxs{
|
||||
RedeemTx: signedRedeemTx,
|
||||
UnconditionalForfeitTxs: unconditionalForfeitTxs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *txBuilder) getLeafScriptAndTree(
|
||||
userPubkey, aspPubkey *secp256k1.PublicKey,
|
||||
) ([]byte, *txscript.IndexedTapScriptTree, error) {
|
||||
@@ -316,7 +481,7 @@ func (b *txBuilder) getLeafScriptAndTree(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
forfeitClosure := &bitcointree.ForfeitClosure{
|
||||
forfeitClosure := &bitcointree.MultisigClosure{
|
||||
Pubkey: userPubkey,
|
||||
AspPubkey: aspPubkey,
|
||||
}
|
||||
@@ -693,7 +858,9 @@ func (b *txBuilder) createConnectors(
|
||||
func (b *txBuilder) createForfeitTxs(
|
||||
aspPubkey *secp256k1.PublicKey, payments []domain.Payment, connectors []*psbt.Packet, minRelayFee uint64,
|
||||
) ([]string, error) {
|
||||
aspScript, err := p2wpkhScript(aspPubkey, b.onchainNetwork())
|
||||
// TODO (@louisinger): are we sure about this change?
|
||||
aspScript, err := p2trScript(aspPubkey, b.onchainNetwork())
|
||||
// aspScript, err := p2wpkhScript(aspPubkey, b.onchainNetwork())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -719,7 +886,7 @@ func (b *txBuilder) createForfeitTxs(
|
||||
var forfeitProof *txscript.TapscriptProof
|
||||
|
||||
for _, proof := range vtxoTaprootTree.LeafMerkleProofs {
|
||||
isForfeit, err := (&bitcointree.ForfeitClosure{}).Decode(proof.Script)
|
||||
isForfeit, err := (&bitcointree.MultisigClosure{}).Decode(proof.Script)
|
||||
if !isForfeit || err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
)
|
||||
|
||||
func p2wpkhScript(publicKey *secp256k1.PublicKey, net *chaincfg.Params) ([]byte, error) {
|
||||
func p2trScript(publicKey *secp256k1.PublicKey, net *chaincfg.Params) ([]byte, error) {
|
||||
tapKey := txscript.ComputeTaprootKeyNoScript(publicKey)
|
||||
|
||||
payment, err := btcutil.NewAddressWitnessPubKeyHash(
|
||||
|
||||
@@ -93,7 +93,7 @@ func (s *service) signPsbt(packet *psbt.Packet) ([]uint32, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: @louisinger shall we delete this code?
|
||||
// TODO (@louisinger): shall we delete this code?
|
||||
// prevOutputFetcher := wallet.PsbtPrevOutputFetcher(packet)
|
||||
// sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)
|
||||
|
||||
|
||||
@@ -177,6 +177,82 @@ func (s *service) Restore(_ context.Context, seed, password string) error {
|
||||
}
|
||||
|
||||
func (s *service) Unlock(_ context.Context, password string) error {
|
||||
if s.wallet == nil {
|
||||
pwd := []byte(password)
|
||||
opt := btcwallet.LoaderWithLocalWalletDB(s.cfg.Datadir, false, time.Minute)
|
||||
config := btcwallet.Config{
|
||||
LogDir: s.cfg.Datadir,
|
||||
PrivatePass: pwd,
|
||||
PublicPass: pwd,
|
||||
Birthday: time.Now(),
|
||||
RecoveryWindow: 0,
|
||||
NetParams: s.cfg.chainParams(),
|
||||
LoaderOptions: []btcwallet.LoaderOption{opt},
|
||||
CoinSelectionStrategy: wallet.CoinSelectionLargest,
|
||||
ChainSource: s.chainSource,
|
||||
}
|
||||
blockCache := blockcache.NewBlockCache(20 * 1024 * 1024)
|
||||
|
||||
wallet, err := btcwallet.New(config, blockCache)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setup wallet loader: %s", err)
|
||||
}
|
||||
|
||||
if err := wallet.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start wallet: %s", err)
|
||||
}
|
||||
|
||||
for {
|
||||
if !wallet.InternalWallet().ChainSynced() {
|
||||
log.Debug("waiting sync....")
|
||||
time.Sleep(3 * time.Second)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
log.Debugf("chain synced")
|
||||
|
||||
addrs, err := wallet.ListAddresses(string(aspKeyAccount), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for info, addrs := range addrs {
|
||||
if info.AccountName != string(aspKeyAccount) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if addr.Internal {
|
||||
continue
|
||||
}
|
||||
|
||||
splittedPath := strings.Split(addr.DerivationPath, "/")
|
||||
last := splittedPath[len(splittedPath)-1]
|
||||
if last == "0" {
|
||||
decoded, err := btcutil.DecodeAddress(addr.Address, s.cfg.chainParams())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
infos, err := wallet.AddressInfo(decoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
managedPubkeyAddr, ok := infos.(waddrmgr.ManagedPubKeyAddress)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to cast address to managed pubkey address")
|
||||
}
|
||||
|
||||
s.aspTaprootAddr = managedPubkeyAddr
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.wallet = wallet
|
||||
return nil
|
||||
}
|
||||
return s.wallet.InternalWallet().Unlock([]byte(password), nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,48 @@ func NewHandler(service application.Service) arkv1.ArkServiceServer {
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *handler) CompletePayment(ctx context.Context, req *arkv1.CompletePaymentRequest) (*arkv1.CompletePaymentResponse, error) {
|
||||
if req.GetSignedRedeemTx() == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing signed redeem tx")
|
||||
}
|
||||
|
||||
if len(req.GetSignedUnconditionalForfeitTxs()) <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing signed unconditional forfeit txs")
|
||||
}
|
||||
|
||||
if err := h.svc.CompleteAsyncPayment(
|
||||
ctx, req.GetSignedRedeemTx(), req.GetSignedUnconditionalForfeitTxs(),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.CompletePaymentResponse{}, nil
|
||||
}
|
||||
|
||||
func (h *handler) CreatePayment(ctx context.Context, req *arkv1.CreatePaymentRequest) (*arkv1.CreatePaymentResponse, error) {
|
||||
vtxosKeys, err := parseInputs(req.GetInputs())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
receivers, err := parseReceivers(req.GetOutputs())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
redeemTx, unconditionalForfeitTxs, err := h.svc.CreateAsyncPayment(
|
||||
ctx, vtxosKeys, receivers,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.CreatePaymentResponse{
|
||||
SignedRedeemTx: redeemTx,
|
||||
UsignedUnconditionalForfeitTxs: unconditionalForfeitTxs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *handler) Onboard(ctx context.Context, req *arkv1.OnboardRequest) (*arkv1.OnboardResponse, error) {
|
||||
if req.GetUserPubkey() == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing user pubkey")
|
||||
@@ -98,12 +140,9 @@ func (h *handler) Ping(ctx context.Context, req *arkv1.PingRequest) (*arkv1.Ping
|
||||
}
|
||||
|
||||
func (h *handler) RegisterPayment(ctx context.Context, req *arkv1.RegisterPaymentRequest) (*arkv1.RegisterPaymentResponse, error) {
|
||||
vtxosKeys := make([]domain.VtxoKey, 0, len(req.GetInputs()))
|
||||
for _, input := range req.GetInputs() {
|
||||
vtxosKeys = append(vtxosKeys, domain.VtxoKey{
|
||||
Txid: input.GetTxid(),
|
||||
VOut: input.GetVout(),
|
||||
})
|
||||
vtxosKeys, err := parseInputs(req.GetInputs())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
id, err := h.svc.SpendVtxos(ctx, vtxosKeys)
|
||||
@@ -350,6 +389,13 @@ func (v vtxoList) toProto(hrp string, aspKey *secp256k1.PublicKey) []*arkv1.Vtxo
|
||||
key, _ := secp256k1.ParsePubKey(buf)
|
||||
addr, _ = common.EncodeAddress(hrp, key, aspKey)
|
||||
}
|
||||
var pendingData *arkv1.PendingPayment
|
||||
if vv.AsyncPayment != nil {
|
||||
pendingData = &arkv1.PendingPayment{
|
||||
RedeemTx: vv.AsyncPayment.RedeemTx,
|
||||
UnconditionalForfeitTxs: vv.AsyncPayment.UnconditionalForfeitTxs,
|
||||
}
|
||||
}
|
||||
list = append(list, &arkv1.Vtxo{
|
||||
Outpoint: &arkv1.Input{
|
||||
Txid: vv.Txid,
|
||||
@@ -359,13 +405,16 @@ func (v vtxoList) toProto(hrp string, aspKey *secp256k1.PublicKey) []*arkv1.Vtxo
|
||||
Address: addr,
|
||||
Amount: vv.Amount,
|
||||
},
|
||||
PoolTxid: vv.PoolTx,
|
||||
Spent: vv.Spent,
|
||||
ExpireAt: vv.ExpireAt,
|
||||
SpentBy: vv.SpentBy,
|
||||
Swept: vv.Swept,
|
||||
PoolTxid: vv.PoolTx,
|
||||
Spent: vv.Spent,
|
||||
ExpireAt: vv.ExpireAt,
|
||||
SpentBy: vv.SpentBy,
|
||||
Swept: vv.Swept,
|
||||
PendingData: pendingData,
|
||||
Pending: pendingData != nil,
|
||||
})
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,21 @@ func parseAddress(addr string) (string, *secp256k1.PublicKey, *secp256k1.PublicK
|
||||
return common.DecodeAddress(addr)
|
||||
}
|
||||
|
||||
func parseInputs(ins []*arkv1.Input) ([]domain.VtxoKey, error) {
|
||||
if len(ins) <= 0 {
|
||||
return nil, fmt.Errorf("missing inputs")
|
||||
}
|
||||
|
||||
vtxos := make([]domain.VtxoKey, 0, len(ins))
|
||||
for _, input := range ins {
|
||||
vtxos = append(vtxos, domain.VtxoKey{
|
||||
Txid: input.GetTxid(),
|
||||
VOut: input.GetVout(),
|
||||
})
|
||||
}
|
||||
return vtxos, nil
|
||||
}
|
||||
|
||||
func parseReceivers(outs []*arkv1.Output) ([]domain.Receiver, error) {
|
||||
receivers := make([]domain.Receiver, 0, len(outs))
|
||||
for _, out := range outs {
|
||||
|
||||
@@ -149,6 +149,14 @@ func Whitelist() map[string][]bakery.Op {
|
||||
Entity: EntityArk,
|
||||
Action: "write",
|
||||
}},
|
||||
fmt.Sprintf("/%s/CreatePayment", arkv1.ArkService_ServiceDesc.ServiceName): {{
|
||||
Entity: EntityArk,
|
||||
Action: "write",
|
||||
}},
|
||||
fmt.Sprintf("/%s/CompletePayment", arkv1.ArkService_ServiceDesc.ServiceName): {{
|
||||
Entity: EntityArk,
|
||||
Action: "write",
|
||||
}},
|
||||
fmt.Sprintf("/%s/Check", grpchealth.Health_ServiceDesc.ServiceName): {{
|
||||
Entity: EntityHealth,
|
||||
Action: "read",
|
||||
|
||||
@@ -109,6 +109,14 @@ func TestSendOffchain(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
|
||||
require.NotZero(t, balance.Offchain.Total)
|
||||
|
||||
_, err = runClarkCommand("claim", "--password", utils.Password)
|
||||
require.NoError(t, err)
|
||||
|
||||
balanceStr, err = runClarkCommand("balance")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
|
||||
require.NotZero(t, balance.Offchain.Total)
|
||||
}
|
||||
|
||||
func TestUnilateralExit(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user