Drop unconditional forfeits txs in offline payment (#344)

* remove unconditionnal forfeit tx

* fix sqlite vtxo repo

* remove pendingData struct

* delete uncond_forfeits_tx table
This commit is contained in:
Louis Singer
2024-10-04 18:06:00 +02:00
committed by GitHub
parent 1d40892196
commit 7606b4cd00
33 changed files with 1654 additions and 1041 deletions

View File

@@ -511,12 +511,6 @@
"properties": {
"signedRedeemTx": {
"type": "string"
},
"signedUnconditionalForfeitTxs": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
@@ -548,12 +542,6 @@
"signedRedeemTx": {
"type": "string",
"title": "signed only by the ASP"
},
"usignedUnconditionalForfeitTxs": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
@@ -735,20 +723,6 @@
}
}
},
"v1PendingPayment": {
"type": "object",
"properties": {
"redeemTx": {
"type": "string"
},
"unconditionalForfeitTxs": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"v1PingResponse": {
"type": "object",
"properties": {
@@ -1094,8 +1068,8 @@
"pending": {
"type": "boolean"
},
"pendingData": {
"$ref": "#/definitions/v1PendingPayment"
"redeemTx": {
"type": "string"
},
"amount": {
"type": "string",

View File

@@ -193,12 +193,10 @@ message CreatePaymentRequest {
}
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 {}
@@ -318,14 +316,10 @@ message Vtxo {
int64 expire_at = 6;
bool swept = 7;
bool pending = 8;
PendingPayment pending_data = 9;
string redeem_tx = 9;
uint64 amount = 10;
}
message PendingPayment {
string redeem_tx = 1;
repeated string unconditional_forfeit_txs =2;
}
message GetTransactionsStreamRequest {}
message GetTransactionsStreamResponse {

File diff suppressed because it is too large Load Diff

View File

@@ -42,9 +42,9 @@ type ASPClient interface {
Ping(ctx context.Context, paymentID string) (RoundEvent, error)
CreatePayment(
ctx context.Context, inputs []Input, outputs []Output,
) (string, []string, error)
) (string, error)
CompletePayment(
ctx context.Context, signedRedeemTx string, signedUnconditionalForfeitTxs []string,
ctx context.Context, signedRedeemTx string,
) error
ListVtxos(ctx context.Context, addr string) ([]Vtxo, []Vtxo, error)
GetRound(ctx context.Context, txID string) (*Round, error)
@@ -80,14 +80,13 @@ type Input struct {
type Vtxo struct {
Outpoint
Descriptor string
Amount uint64
RoundTxid string
ExpiresAt *time.Time
RedeemTx string
UnconditionalForfeitTxs []string
Pending bool
SpentBy string
Descriptor string
Amount uint64
RoundTxid string
ExpiresAt *time.Time
RedeemTx string
Pending bool
SpentBy string
}
type Output struct {

View File

@@ -238,24 +238,23 @@ func (a *grpcClient) Ping(
func (a *grpcClient) CreatePayment(
ctx context.Context, inputs []client.Input, outputs []client.Output,
) (string, []string, error) {
) (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 "", err
}
return resp.SignedRedeemTx, resp.UsignedUnconditionalForfeitTxs, nil
return resp.SignedRedeemTx, nil
}
func (a *grpcClient) CompletePayment(
ctx context.Context, redeemTx string, signedForfeitTxs []string,
ctx context.Context, redeemTx string,
) error {
req := &arkv1.CompletePaymentRequest{
SignedRedeemTx: redeemTx,
SignedUnconditionalForfeitTxs: signedForfeitTxs,
SignedRedeemTx: redeemTx,
}
_, err := a.svc.CompletePayment(ctx, req)
return err

View File

@@ -118,25 +118,18 @@ 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{
Outpoint: client.Outpoint{
Txid: v.GetOutpoint().GetTxid(),
VOut: v.GetOutpoint().GetVout(),
},
Amount: v.GetAmount(),
RoundTxid: v.GetRoundTxid(),
ExpiresAt: expiresAt,
Pending: v.GetPending(),
RedeemTx: redeemTx,
UnconditionalForfeitTxs: uncondForfeitTxs,
SpentBy: v.GetSpentBy(),
Descriptor: v.GetDescriptor_(),
Amount: v.GetAmount(),
RoundTxid: v.GetRoundTxid(),
ExpiresAt: expiresAt,
Pending: v.GetPending(),
RedeemTx: v.GetRedeemTx(),
SpentBy: v.GetSpentBy(),
Descriptor: v.GetDescriptor_(),
}
}

View File

@@ -338,7 +338,7 @@ func (a *restClient) Ping(
func (a *restClient) CreatePayment(
ctx context.Context, inputs []client.Input, outputs []client.Output,
) (string, []string, error) {
) (string, error) {
ins := make([]*models.V1Input, 0, len(inputs))
for _, i := range inputs {
ins = append(ins, &models.V1Input{
@@ -365,21 +365,19 @@ func (a *restClient) CreatePayment(
ark_service.NewArkServiceCreatePaymentParams().WithBody(&body),
)
if err != nil {
return "", nil, err
return "", err
}
return resp.GetPayload().SignedRedeemTx, resp.GetPayload().UsignedUnconditionalForfeitTxs, nil
return resp.GetPayload().SignedRedeemTx, nil
}
func (a *restClient) CompletePayment(
ctx context.Context, signedRedeemTx string, signedUncondForfeitTxs []string,
ctx context.Context, signedRedeemTx string,
) error {
req := &arkv1.CompletePaymentRequest{
SignedRedeemTx: signedRedeemTx,
SignedUnconditionalForfeitTxs: signedUncondForfeitTxs,
SignedRedeemTx: signedRedeemTx,
}
body := models.V1CompletePaymentRequest{
SignedRedeemTx: req.GetSignedRedeemTx(),
SignedUnconditionalForfeitTxs: req.GetSignedUnconditionalForfeitTxs(),
SignedRedeemTx: req.GetSignedRedeemTx(),
}
_, err := a.svc.ArkServiceCompletePayment(
ark_service.NewArkServiceCompletePaymentParams().WithBody(&body),
@@ -492,26 +490,18 @@ 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{
Outpoint: client.Outpoint{
Txid: v.Outpoint.Txid,
VOut: uint32(v.Outpoint.Vout),
},
Amount: uint64(amount),
RoundTxid: v.RoundTxid,
ExpiresAt: expiresAt,
Pending: v.Pending,
RedeemTx: redeemTx,
UnconditionalForfeitTxs: uncondForfeitTxs,
SpentBy: v.SpentBy,
Descriptor: v.Descriptor,
Amount: uint64(amount),
RoundTxid: v.RoundTxid,
ExpiresAt: expiresAt,
Pending: v.Pending,
RedeemTx: v.RedeemTx,
SpentBy: v.SpentBy,
Descriptor: v.Descriptor,
})
}

View File

@@ -68,6 +68,8 @@ type ClientService interface {
ArkServiceGetRoundByID(params *ArkServiceGetRoundByIDParams, opts ...ClientOption) (*ArkServiceGetRoundByIDOK, error)
ArkServiceGetTransactionsStream(params *ArkServiceGetTransactionsStreamParams, opts ...ClientOption) (*ArkServiceGetTransactionsStreamOK, error)
ArkServiceListVtxos(params *ArkServiceListVtxosParams, opts ...ClientOption) (*ArkServiceListVtxosOK, error)
ArkServicePing(params *ArkServicePingParams, opts ...ClientOption) (*ArkServicePingOK, error)
@@ -344,6 +346,43 @@ func (a *Client) ArkServiceGetRoundByID(params *ArkServiceGetRoundByIDParams, op
return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
}
/*
ArkServiceGetTransactionsStream ark service get transactions stream API
*/
func (a *Client) ArkServiceGetTransactionsStream(params *ArkServiceGetTransactionsStreamParams, opts ...ClientOption) (*ArkServiceGetTransactionsStreamOK, error) {
// TODO: Validate the params before sending
if params == nil {
params = NewArkServiceGetTransactionsStreamParams()
}
op := &runtime.ClientOperation{
ID: "ArkService_GetTransactionsStream",
Method: "GET",
PathPattern: "/v1/transactions",
ProducesMediaTypes: []string{"application/json"},
ConsumesMediaTypes: []string{"application/json"},
Schemes: []string{"http"},
Params: params,
Reader: &ArkServiceGetTransactionsStreamReader{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.(*ArkServiceGetTransactionsStreamOK)
if ok {
return success, nil
}
// unexpected success response
unexpectedSuccess := result.(*ArkServiceGetTransactionsStreamDefault)
return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
}
/*
ArkServiceListVtxos ark service list vtxos API
*/

View File

@@ -0,0 +1,128 @@
// 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"
)
// NewArkServiceGetTransactionsStreamParams creates a new ArkServiceGetTransactionsStreamParams 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 NewArkServiceGetTransactionsStreamParams() *ArkServiceGetTransactionsStreamParams {
return &ArkServiceGetTransactionsStreamParams{
timeout: cr.DefaultTimeout,
}
}
// NewArkServiceGetTransactionsStreamParamsWithTimeout creates a new ArkServiceGetTransactionsStreamParams object
// with the ability to set a timeout on a request.
func NewArkServiceGetTransactionsStreamParamsWithTimeout(timeout time.Duration) *ArkServiceGetTransactionsStreamParams {
return &ArkServiceGetTransactionsStreamParams{
timeout: timeout,
}
}
// NewArkServiceGetTransactionsStreamParamsWithContext creates a new ArkServiceGetTransactionsStreamParams object
// with the ability to set a context for a request.
func NewArkServiceGetTransactionsStreamParamsWithContext(ctx context.Context) *ArkServiceGetTransactionsStreamParams {
return &ArkServiceGetTransactionsStreamParams{
Context: ctx,
}
}
// NewArkServiceGetTransactionsStreamParamsWithHTTPClient creates a new ArkServiceGetTransactionsStreamParams object
// with the ability to set a custom HTTPClient for a request.
func NewArkServiceGetTransactionsStreamParamsWithHTTPClient(client *http.Client) *ArkServiceGetTransactionsStreamParams {
return &ArkServiceGetTransactionsStreamParams{
HTTPClient: client,
}
}
/*
ArkServiceGetTransactionsStreamParams contains all the parameters to send to the API endpoint
for the ark service get transactions stream operation.
Typically these are written to a http.Request.
*/
type ArkServiceGetTransactionsStreamParams struct {
timeout time.Duration
Context context.Context
HTTPClient *http.Client
}
// WithDefaults hydrates default values in the ark service get transactions stream params (not the query body).
//
// All values with no default are reset to their zero value.
func (o *ArkServiceGetTransactionsStreamParams) WithDefaults() *ArkServiceGetTransactionsStreamParams {
o.SetDefaults()
return o
}
// SetDefaults hydrates default values in the ark service get transactions stream params (not the query body).
//
// All values with no default are reset to their zero value.
func (o *ArkServiceGetTransactionsStreamParams) SetDefaults() {
// no default values defined for this parameter
}
// WithTimeout adds the timeout to the ark service get transactions stream params
func (o *ArkServiceGetTransactionsStreamParams) WithTimeout(timeout time.Duration) *ArkServiceGetTransactionsStreamParams {
o.SetTimeout(timeout)
return o
}
// SetTimeout adds the timeout to the ark service get transactions stream params
func (o *ArkServiceGetTransactionsStreamParams) SetTimeout(timeout time.Duration) {
o.timeout = timeout
}
// WithContext adds the context to the ark service get transactions stream params
func (o *ArkServiceGetTransactionsStreamParams) WithContext(ctx context.Context) *ArkServiceGetTransactionsStreamParams {
o.SetContext(ctx)
return o
}
// SetContext adds the context to the ark service get transactions stream params
func (o *ArkServiceGetTransactionsStreamParams) SetContext(ctx context.Context) {
o.Context = ctx
}
// WithHTTPClient adds the HTTPClient to the ark service get transactions stream params
func (o *ArkServiceGetTransactionsStreamParams) WithHTTPClient(client *http.Client) *ArkServiceGetTransactionsStreamParams {
o.SetHTTPClient(client)
return o
}
// SetHTTPClient adds the HTTPClient to the ark service get transactions stream params
func (o *ArkServiceGetTransactionsStreamParams) SetHTTPClient(client *http.Client) {
o.HTTPClient = client
}
// WriteToRequest writes these params to a swagger request
func (o *ArkServiceGetTransactionsStreamParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
if err := r.SetTimeout(o.timeout); err != nil {
return err
}
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,337 @@
// 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"
"encoding/json"
"fmt"
"io"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models"
)
// ArkServiceGetTransactionsStreamReader is a Reader for the ArkServiceGetTransactionsStream structure.
type ArkServiceGetTransactionsStreamReader struct {
formats strfmt.Registry
}
// ReadResponse reads a server response into the received o.
func (o *ArkServiceGetTransactionsStreamReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
switch response.Code() {
case 200:
result := NewArkServiceGetTransactionsStreamOK()
if err := result.readResponse(response, consumer, o.formats); err != nil {
return nil, err
}
return result, nil
default:
result := NewArkServiceGetTransactionsStreamDefault(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
}
}
// NewArkServiceGetTransactionsStreamOK creates a ArkServiceGetTransactionsStreamOK with default headers values
func NewArkServiceGetTransactionsStreamOK() *ArkServiceGetTransactionsStreamOK {
return &ArkServiceGetTransactionsStreamOK{}
}
/*
ArkServiceGetTransactionsStreamOK describes a response with status code 200, with default header values.
A successful response.(streaming responses)
*/
type ArkServiceGetTransactionsStreamOK struct {
Payload *ArkServiceGetTransactionsStreamOKBody
}
// IsSuccess returns true when this ark service get transactions stream o k response has a 2xx status code
func (o *ArkServiceGetTransactionsStreamOK) IsSuccess() bool {
return true
}
// IsRedirect returns true when this ark service get transactions stream o k response has a 3xx status code
func (o *ArkServiceGetTransactionsStreamOK) IsRedirect() bool {
return false
}
// IsClientError returns true when this ark service get transactions stream o k response has a 4xx status code
func (o *ArkServiceGetTransactionsStreamOK) IsClientError() bool {
return false
}
// IsServerError returns true when this ark service get transactions stream o k response has a 5xx status code
func (o *ArkServiceGetTransactionsStreamOK) IsServerError() bool {
return false
}
// IsCode returns true when this ark service get transactions stream o k response a status code equal to that given
func (o *ArkServiceGetTransactionsStreamOK) IsCode(code int) bool {
return code == 200
}
// Code gets the status code for the ark service get transactions stream o k response
func (o *ArkServiceGetTransactionsStreamOK) Code() int {
return 200
}
func (o *ArkServiceGetTransactionsStreamOK) Error() string {
payload, _ := json.Marshal(o.Payload)
return fmt.Sprintf("[GET /v1/transactions][%d] arkServiceGetTransactionsStreamOK %s", 200, payload)
}
func (o *ArkServiceGetTransactionsStreamOK) String() string {
payload, _ := json.Marshal(o.Payload)
return fmt.Sprintf("[GET /v1/transactions][%d] arkServiceGetTransactionsStreamOK %s", 200, payload)
}
func (o *ArkServiceGetTransactionsStreamOK) GetPayload() *ArkServiceGetTransactionsStreamOKBody {
return o.Payload
}
func (o *ArkServiceGetTransactionsStreamOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
o.Payload = new(ArkServiceGetTransactionsStreamOKBody)
// response payload
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
return err
}
return nil
}
// NewArkServiceGetTransactionsStreamDefault creates a ArkServiceGetTransactionsStreamDefault with default headers values
func NewArkServiceGetTransactionsStreamDefault(code int) *ArkServiceGetTransactionsStreamDefault {
return &ArkServiceGetTransactionsStreamDefault{
_statusCode: code,
}
}
/*
ArkServiceGetTransactionsStreamDefault describes a response with status code -1, with default header values.
An unexpected error response.
*/
type ArkServiceGetTransactionsStreamDefault struct {
_statusCode int
Payload *models.RPCStatus
}
// IsSuccess returns true when this ark service get transactions stream default response has a 2xx status code
func (o *ArkServiceGetTransactionsStreamDefault) IsSuccess() bool {
return o._statusCode/100 == 2
}
// IsRedirect returns true when this ark service get transactions stream default response has a 3xx status code
func (o *ArkServiceGetTransactionsStreamDefault) IsRedirect() bool {
return o._statusCode/100 == 3
}
// IsClientError returns true when this ark service get transactions stream default response has a 4xx status code
func (o *ArkServiceGetTransactionsStreamDefault) IsClientError() bool {
return o._statusCode/100 == 4
}
// IsServerError returns true when this ark service get transactions stream default response has a 5xx status code
func (o *ArkServiceGetTransactionsStreamDefault) IsServerError() bool {
return o._statusCode/100 == 5
}
// IsCode returns true when this ark service get transactions stream default response a status code equal to that given
func (o *ArkServiceGetTransactionsStreamDefault) IsCode(code int) bool {
return o._statusCode == code
}
// Code gets the status code for the ark service get transactions stream default response
func (o *ArkServiceGetTransactionsStreamDefault) Code() int {
return o._statusCode
}
func (o *ArkServiceGetTransactionsStreamDefault) Error() string {
payload, _ := json.Marshal(o.Payload)
return fmt.Sprintf("[GET /v1/transactions][%d] ArkService_GetTransactionsStream default %s", o._statusCode, payload)
}
func (o *ArkServiceGetTransactionsStreamDefault) String() string {
payload, _ := json.Marshal(o.Payload)
return fmt.Sprintf("[GET /v1/transactions][%d] ArkService_GetTransactionsStream default %s", o._statusCode, payload)
}
func (o *ArkServiceGetTransactionsStreamDefault) GetPayload() *models.RPCStatus {
return o.Payload
}
func (o *ArkServiceGetTransactionsStreamDefault) 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
}
/*
ArkServiceGetTransactionsStreamOKBody Stream result of v1GetTransactionsStreamResponse
swagger:model ArkServiceGetTransactionsStreamOKBody
*/
type ArkServiceGetTransactionsStreamOKBody struct {
// error
Error *models.RPCStatus `json:"error,omitempty"`
// result
Result *models.V1GetTransactionsStreamResponse `json:"result,omitempty"`
}
// Validate validates this ark service get transactions stream o k body
func (o *ArkServiceGetTransactionsStreamOKBody) Validate(formats strfmt.Registry) error {
var res []error
if err := o.validateError(formats); err != nil {
res = append(res, err)
}
if err := o.validateResult(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (o *ArkServiceGetTransactionsStreamOKBody) validateError(formats strfmt.Registry) error {
if swag.IsZero(o.Error) { // not required
return nil
}
if o.Error != nil {
if err := o.Error.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "error")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "error")
}
return err
}
}
return nil
}
func (o *ArkServiceGetTransactionsStreamOKBody) validateResult(formats strfmt.Registry) error {
if swag.IsZero(o.Result) { // not required
return nil
}
if o.Result != nil {
if err := o.Result.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "result")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "result")
}
return err
}
}
return nil
}
// ContextValidate validate this ark service get transactions stream o k body based on the context it is used
func (o *ArkServiceGetTransactionsStreamOKBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := o.contextValidateError(ctx, formats); err != nil {
res = append(res, err)
}
if err := o.contextValidateResult(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (o *ArkServiceGetTransactionsStreamOKBody) contextValidateError(ctx context.Context, formats strfmt.Registry) error {
if o.Error != nil {
if swag.IsZero(o.Error) { // not required
return nil
}
if err := o.Error.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "error")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "error")
}
return err
}
}
return nil
}
func (o *ArkServiceGetTransactionsStreamOKBody) contextValidateResult(ctx context.Context, formats strfmt.Registry) error {
if o.Result != nil {
if swag.IsZero(o.Result) { // not required
return nil
}
if err := o.Result.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "result")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "result")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (o *ArkServiceGetTransactionsStreamOKBody) MarshalBinary() ([]byte, error) {
if o == nil {
return nil, nil
}
return swag.WriteJSON(o)
}
// UnmarshalBinary interface implementation
func (o *ArkServiceGetTransactionsStreamOKBody) UnmarshalBinary(b []byte) error {
var res ArkServiceGetTransactionsStreamOKBody
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*o = res
return nil
}

View File

@@ -19,9 +19,6 @@ 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

View File

@@ -19,9 +19,6 @@ 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

View File

@@ -0,0 +1,160 @@
// 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/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// V1GetTransactionsStreamResponse v1 get transactions stream response
//
// swagger:model v1GetTransactionsStreamResponse
type V1GetTransactionsStreamResponse struct {
// redeem
Redeem *V1RedeemTransaction `json:"redeem,omitempty"`
// round
Round *V1RoundTransaction `json:"round,omitempty"`
}
// Validate validates this v1 get transactions stream response
func (m *V1GetTransactionsStreamResponse) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateRedeem(formats); err != nil {
res = append(res, err)
}
if err := m.validateRound(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *V1GetTransactionsStreamResponse) validateRedeem(formats strfmt.Registry) error {
if swag.IsZero(m.Redeem) { // not required
return nil
}
if m.Redeem != nil {
if err := m.Redeem.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("redeem")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("redeem")
}
return err
}
}
return nil
}
func (m *V1GetTransactionsStreamResponse) validateRound(formats strfmt.Registry) error {
if swag.IsZero(m.Round) { // not required
return nil
}
if m.Round != nil {
if err := m.Round.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("round")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("round")
}
return err
}
}
return nil
}
// ContextValidate validate this v1 get transactions stream response based on the context it is used
func (m *V1GetTransactionsStreamResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateRedeem(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateRound(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *V1GetTransactionsStreamResponse) contextValidateRedeem(ctx context.Context, formats strfmt.Registry) error {
if m.Redeem != nil {
if swag.IsZero(m.Redeem) { // not required
return nil
}
if err := m.Redeem.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("redeem")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("redeem")
}
return err
}
}
return nil
}
func (m *V1GetTransactionsStreamResponse) contextValidateRound(ctx context.Context, formats strfmt.Registry) error {
if m.Round != nil {
if swag.IsZero(m.Round) { // not required
return nil
}
if err := m.Round.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("round")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("round")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *V1GetTransactionsStreamResponse) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *V1GetTransactionsStreamResponse) UnmarshalBinary(b []byte) error {
var res V1GetTransactionsStreamResponse
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -19,9 +19,6 @@ type V1PendingPayment struct {
// redeem tx
RedeemTx string `json:"redeemTx,omitempty"`
// unconditional forfeit txs
UnconditionalForfeitTxs []string `json:"unconditionalForfeitTxs"`
}
// Validate validates this v1 pending payment

View File

@@ -0,0 +1,186 @@
// 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"
)
// V1RedeemTransaction v1 redeem transaction
//
// swagger:model v1RedeemTransaction
type V1RedeemTransaction struct {
// spendable vtxos
SpendableVtxos []*V1Vtxo `json:"spendableVtxos"`
// spent vtxos
SpentVtxos []*V1Outpoint `json:"spentVtxos"`
// txid
Txid string `json:"txid,omitempty"`
}
// Validate validates this v1 redeem transaction
func (m *V1RedeemTransaction) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateSpendableVtxos(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpentVtxos(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *V1RedeemTransaction) validateSpendableVtxos(formats strfmt.Registry) error {
if swag.IsZero(m.SpendableVtxos) { // not required
return nil
}
for i := 0; i < len(m.SpendableVtxos); i++ {
if swag.IsZero(m.SpendableVtxos[i]) { // not required
continue
}
if m.SpendableVtxos[i] != nil {
if err := m.SpendableVtxos[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("spendableVtxos" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("spendableVtxos" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *V1RedeemTransaction) validateSpentVtxos(formats strfmt.Registry) error {
if swag.IsZero(m.SpentVtxos) { // not required
return nil
}
for i := 0; i < len(m.SpentVtxos); i++ {
if swag.IsZero(m.SpentVtxos[i]) { // not required
continue
}
if m.SpentVtxos[i] != nil {
if err := m.SpentVtxos[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("spentVtxos" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("spentVtxos" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// ContextValidate validate this v1 redeem transaction based on the context it is used
func (m *V1RedeemTransaction) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateSpendableVtxos(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateSpentVtxos(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *V1RedeemTransaction) contextValidateSpendableVtxos(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.SpendableVtxos); i++ {
if m.SpendableVtxos[i] != nil {
if swag.IsZero(m.SpendableVtxos[i]) { // not required
return nil
}
if err := m.SpendableVtxos[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("spendableVtxos" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("spendableVtxos" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *V1RedeemTransaction) contextValidateSpentVtxos(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.SpentVtxos); i++ {
if m.SpentVtxos[i] != nil {
if swag.IsZero(m.SpentVtxos[i]) { // not required
return nil
}
if err := m.SpentVtxos[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("spentVtxos" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("spentVtxos" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *V1RedeemTransaction) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *V1RedeemTransaction) UnmarshalBinary(b []byte) error {
var res V1RedeemTransaction
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,248 @@
// 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"
)
// V1RoundTransaction v1 round transaction
//
// swagger:model v1RoundTransaction
type V1RoundTransaction struct {
// claimed boarding utxos
ClaimedBoardingUtxos []*V1Outpoint `json:"claimedBoardingUtxos"`
// spendable vtxos
SpendableVtxos []*V1Vtxo `json:"spendableVtxos"`
// spent vtxos
SpentVtxos []*V1Outpoint `json:"spentVtxos"`
// txid
Txid string `json:"txid,omitempty"`
}
// Validate validates this v1 round transaction
func (m *V1RoundTransaction) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateClaimedBoardingUtxos(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpendableVtxos(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpentVtxos(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *V1RoundTransaction) validateClaimedBoardingUtxos(formats strfmt.Registry) error {
if swag.IsZero(m.ClaimedBoardingUtxos) { // not required
return nil
}
for i := 0; i < len(m.ClaimedBoardingUtxos); i++ {
if swag.IsZero(m.ClaimedBoardingUtxos[i]) { // not required
continue
}
if m.ClaimedBoardingUtxos[i] != nil {
if err := m.ClaimedBoardingUtxos[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("claimedBoardingUtxos" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("claimedBoardingUtxos" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *V1RoundTransaction) validateSpendableVtxos(formats strfmt.Registry) error {
if swag.IsZero(m.SpendableVtxos) { // not required
return nil
}
for i := 0; i < len(m.SpendableVtxos); i++ {
if swag.IsZero(m.SpendableVtxos[i]) { // not required
continue
}
if m.SpendableVtxos[i] != nil {
if err := m.SpendableVtxos[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("spendableVtxos" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("spendableVtxos" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *V1RoundTransaction) validateSpentVtxos(formats strfmt.Registry) error {
if swag.IsZero(m.SpentVtxos) { // not required
return nil
}
for i := 0; i < len(m.SpentVtxos); i++ {
if swag.IsZero(m.SpentVtxos[i]) { // not required
continue
}
if m.SpentVtxos[i] != nil {
if err := m.SpentVtxos[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("spentVtxos" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("spentVtxos" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// ContextValidate validate this v1 round transaction based on the context it is used
func (m *V1RoundTransaction) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateClaimedBoardingUtxos(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateSpendableVtxos(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateSpentVtxos(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *V1RoundTransaction) contextValidateClaimedBoardingUtxos(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.ClaimedBoardingUtxos); i++ {
if m.ClaimedBoardingUtxos[i] != nil {
if swag.IsZero(m.ClaimedBoardingUtxos[i]) { // not required
return nil
}
if err := m.ClaimedBoardingUtxos[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("claimedBoardingUtxos" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("claimedBoardingUtxos" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *V1RoundTransaction) contextValidateSpendableVtxos(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.SpendableVtxos); i++ {
if m.SpendableVtxos[i] != nil {
if swag.IsZero(m.SpendableVtxos[i]) { // not required
return nil
}
if err := m.SpendableVtxos[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("spendableVtxos" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("spendableVtxos" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *V1RoundTransaction) contextValidateSpentVtxos(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.SpentVtxos); i++ {
if m.SpentVtxos[i] != nil {
if swag.IsZero(m.SpentVtxos[i]) { // not required
return nil
}
if err := m.SpentVtxos[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("spentVtxos" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("spentVtxos" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *V1RoundTransaction) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *V1RoundTransaction) UnmarshalBinary(b []byte) error {
var res V1RoundTransaction
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -33,8 +33,8 @@ type V1Vtxo struct {
// pending
Pending bool `json:"pending,omitempty"`
// pending data
PendingData *V1PendingPayment `json:"pendingData,omitempty"`
// redeem tx
RedeemTx string `json:"redeemTx,omitempty"`
// round txid
RoundTxid string `json:"roundTxid,omitempty"`
@@ -57,10 +57,6 @@ func (m *V1Vtxo) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validatePendingData(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
@@ -86,25 +82,6 @@ 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
}
// ContextValidate validate this v1 vtxo based on the context it is used
func (m *V1Vtxo) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
@@ -113,10 +90,6 @@ 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 len(res) > 0 {
return errors.CompositeValidationError(res...)
}
@@ -144,27 +117,6 @@ 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
}
// MarshalBinary interface implementation
func (m *V1Vtxo) MarshalBinary() ([]byte, error) {
if m == nil {

View File

@@ -196,13 +196,12 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) {
Txid: vtxo.Outpoint.Txid,
VOut: vtxo.Outpoint.Vout,
},
Amount: amount,
RoundTxid: vtxo.PoolTxid,
ExpiresAt: &expireAt,
RedeemTx: vtxo.PendingData.RedeemTx,
UnconditionalForfeitTxs: vtxo.PendingData.UnconditionalForfeitTxs,
Pending: vtxo.Pending,
SpentBy: vtxo.SpentBy,
Amount: amount,
RoundTxid: vtxo.PoolTxid,
ExpiresAt: &expireAt,
RedeemTx: vtxo.PendingData.RedeemTx,
Pending: vtxo.Pending,
SpentBy: vtxo.SpentBy,
}
}
@@ -221,13 +220,12 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) {
Txid: vtxo.Outpoint.Txid,
VOut: vtxo.Outpoint.Vout,
},
Amount: amount,
RoundTxid: vtxo.PoolTxid,
ExpiresAt: &expireAt,
RedeemTx: vtxo.PendingData.RedeemTx,
UnconditionalForfeitTxs: vtxo.PendingData.UnconditionalForfeitTxs,
Pending: vtxo.Pending,
SpentBy: vtxo.SpentBy,
Amount: amount,
RoundTxid: vtxo.PoolTxid,
ExpiresAt: &expireAt,
RedeemTx: vtxo.PendingData.RedeemTx,
Pending: vtxo.Pending,
SpentBy: vtxo.SpentBy,
}
}

View File

@@ -653,8 +653,7 @@ func (a *covenantlessArkClient) SendAsync(
})
}
redeemTx, unconditionalForfeitTxs, err := a.client.CreatePayment(
ctx, inputs, receiversOutput)
redeemTx, err := a.client.CreatePayment(ctx, inputs, receiversOutput)
if err != nil {
return "", err
}
@@ -667,7 +666,7 @@ func (a *covenantlessArkClient) SendAsync(
}
if err = a.client.CompletePayment(
ctx, signedRedeemTx, unconditionalForfeitTxs,
ctx, signedRedeemTx,
); err != nil {
return "", err
}

View File

@@ -320,12 +320,12 @@ func (s *covenantService) UpdatePaymentStatus(_ context.Context, id string) (dom
return s.lastEvent, nil
}
func (s *covenantService) CompleteAsyncPayment(ctx context.Context, redeemTx string, unconditionalForfeitTxs []string) error {
func (s *covenantService) CompleteAsyncPayment(ctx context.Context, redeemTx string) error {
return fmt.Errorf("unimplemented")
}
func (s *covenantService) CreateAsyncPayment(ctx context.Context, inputs []ports.Input, receivers []domain.Receiver) (string, []string, error) {
return "", nil, fmt.Errorf("unimplemented")
func (s *covenantService) CreateAsyncPayment(ctx context.Context, inputs []ports.Input, receivers []domain.Receiver) (string, error) {
return "", fmt.Errorf("unimplemented")
}
func (s *covenantService) SignVtxos(ctx context.Context, forfeitTxs []string) error {

View File

@@ -139,7 +139,7 @@ func (s *covenantlessService) Stop() {
}
func (s *covenantlessService) CompleteAsyncPayment(
ctx context.Context, redeemTx string, unconditionalForfeitTxs []string,
ctx context.Context, redeemTx string,
) error {
redeemPtx, err := psbt.NewFromRawBytes(strings.NewReader(redeemTx), true)
if err != nil {
@@ -274,11 +274,8 @@ func (s *covenantlessService) CompleteAsyncPayment(
Amount: uint64(out.Value),
},
ExpireAt: asyncPayData.expireAt,
AsyncPayment: &domain.AsyncPaymentTxs{
RedeemTx: redeemTx,
UnconditionalForfeitTxs: unconditionalForfeitTxs,
},
Pending: isPending,
RedeemTx: redeemTx,
Pending: isPending,
})
}
@@ -313,7 +310,7 @@ func (s *covenantlessService) CompleteAsyncPayment(
func (s *covenantlessService) CreateAsyncPayment(
ctx context.Context, inputs []ports.Input, receivers []domain.Receiver,
) (string, []string, error) {
) (string, error) {
vtxosKeys := make([]domain.VtxoKey, 0, len(inputs))
for _, in := range inputs {
vtxosKeys = append(vtxosKeys, in.VtxoKey)
@@ -321,10 +318,10 @@ func (s *covenantlessService) CreateAsyncPayment(
vtxos, err := s.repoManager.Vtxos().GetVtxos(ctx, vtxosKeys)
if err != nil {
return "", nil, err
return "", err
}
if len(vtxos) <= 0 {
return "", nil, fmt.Errorf("vtxos not found")
return "", fmt.Errorf("vtxos not found")
}
vtxosInputs := make([]domain.Vtxo, 0, len(inputs))
@@ -332,18 +329,18 @@ func (s *covenantlessService) CreateAsyncPayment(
expiration := vtxos[0].ExpireAt
for _, vtxo := range vtxos {
if vtxo.Spent {
return "", nil, fmt.Errorf("all vtxos must be unspent")
return "", fmt.Errorf("all vtxos must be unspent")
}
if vtxo.Redeemed {
return "", nil, fmt.Errorf("all vtxos must be redeemed")
return "", fmt.Errorf("all vtxos must be redeemed")
}
if vtxo.Swept {
return "", nil, fmt.Errorf("all vtxos must be swept")
return "", fmt.Errorf("all vtxos must be swept")
}
if vtxo.Pending {
return "", nil, fmt.Errorf("all vtxos must be claimed")
return "", fmt.Errorf("all vtxos must be claimed")
}
if vtxo.ExpireAt < expiration {
@@ -353,19 +350,19 @@ func (s *covenantlessService) CreateAsyncPayment(
vtxosInputs = append(vtxosInputs, vtxo)
}
res, err := s.builder.BuildAsyncPaymentTransactions(
redeemTx, err := s.builder.BuildAsyncPaymentTransactions(
vtxosInputs, s.pubkey, receivers,
)
if err != nil {
return "", nil, fmt.Errorf("failed to build async payment txs: %s", err)
return "", fmt.Errorf("failed to build async payment txs: %s", err)
}
redeemTx, err := psbt.NewFromRawBytes(strings.NewReader(res.RedeemTx), true)
redeemPtx, err := psbt.NewFromRawBytes(strings.NewReader(redeemTx), true)
if err != nil {
return "", nil, fmt.Errorf("failed to parse redeem tx: %s", err)
return "", fmt.Errorf("failed to parse redeem tx: %s", err)
}
s.asyncPaymentsCache[redeemTx.UnsignedTx.TxID()] = struct {
s.asyncPaymentsCache[redeemPtx.UnsignedTx.TxID()] = struct {
receivers []domain.Receiver
expireAt int64
}{
@@ -373,7 +370,7 @@ func (s *covenantlessService) CreateAsyncPayment(
expireAt: expiration,
}
return res.RedeemTx, res.UnconditionalForfeitTxs, nil
return redeemTx, nil
}
func (s *covenantlessService) GetBoardingAddress(
@@ -1536,7 +1533,7 @@ func (s *covenantlessService) reactToFraud(ctx context.Context, vtxo domain.Vtxo
log.Debugf("vtxo %s:%d has been spent by async payment", vtxo.Txid, vtxo.VOut)
redeemTxHex, err := s.builder.FinalizeAndExtract(asyncPayVtxo.AsyncPayment.RedeemTx)
redeemTxHex, err := s.builder.FinalizeAndExtract(asyncPayVtxo.RedeemTx)
if err != nil {
return fmt.Errorf("failed to finalize redeem tx: %s", err)
}

View File

@@ -33,9 +33,9 @@ type Service interface {
// Async payments
CreateAsyncPayment(
ctx context.Context, inputs []ports.Input, receivers []domain.Receiver,
) (string, []string, error)
) (string, error)
CompleteAsyncPayment(
ctx context.Context, redeemTx string, unconditionalForfeitTxs []string,
ctx context.Context, redeemTx string,
) error
GetBoardingAddress(
ctx context.Context, userPubkey *secp256k1.PublicKey,

View File

@@ -108,17 +108,12 @@ func (r Receiver) IsOnchain() bool {
type Vtxo struct {
VtxoKey
Receiver
RoundTxid string
SpentBy string // round txid or async redeem txid
Spent bool
Redeemed bool
Swept bool
ExpireAt int64
AsyncPayment *AsyncPaymentTxs // nil if not async vtxo
Pending bool
}
type AsyncPaymentTxs struct {
RedeemTx string // always signed by the ASP when created
UnconditionalForfeitTxs []string
RoundTxid string
SpentBy string // round txid or async redeem txid
Spent bool
Redeemed bool
Swept bool
ExpireAt int64
RedeemTx string // empty if in-round vtxo
Pending bool
}

View File

@@ -42,7 +42,7 @@ type TxBuilder interface {
BuildAsyncPaymentTransactions(
vtxosToSpend []domain.Vtxo,
aspPubKey *secp256k1.PublicKey, receivers []domain.Receiver,
) (*domain.AsyncPaymentTxs, error)
) (string, error)
VerifyAndCombinePartialTx(dest string, src string) (string, error)
GetTxID(tx string) (string, error)
}

View File

@@ -41,15 +41,6 @@ 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,
vout INTEGER NOT NULL,
@@ -86,8 +77,3 @@ CREATE VIEW payment_vtxo_vw AS SELECT vtxo.*
FROM payment
LEFT OUTER JOIN vtxo
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;

View File

@@ -87,22 +87,6 @@ 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

View File

@@ -54,16 +54,12 @@ func (q *Queries) MarkVtxoAsSwept(ctx context.Context, arg MarkVtxoAsSweptParams
}
const selectNotRedeemedVtxos = `-- name: SelectNotRedeemedVtxos :many
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending,
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
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo
WHERE redeemed = false
`
type SelectNotRedeemedVtxosRow struct {
Vtxo Vtxo
UncondForfeitTxVw UncondForfeitTxVw
Vtxo Vtxo
}
func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeemedVtxosRow, error) {
@@ -89,11 +85,6 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
&i.Vtxo.RedeemTx,
&i.Vtxo.Descriptor,
&i.Vtxo.Pending,
&i.UncondForfeitTxVw.ID,
&i.UncondForfeitTxVw.Tx,
&i.UncondForfeitTxVw.VtxoTxid,
&i.UncondForfeitTxVw.VtxoVout,
&i.UncondForfeitTxVw.Position,
); err != nil {
return nil, err
}
@@ -109,16 +100,12 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
}
const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending,
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
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo
WHERE redeemed = false AND INSTR(descriptor, ?) > 0
`
type SelectNotRedeemedVtxosWithPubkeyRow struct {
Vtxo Vtxo
UncondForfeitTxVw UncondForfeitTxVw
Vtxo Vtxo
}
func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, instr string) ([]SelectNotRedeemedVtxosWithPubkeyRow, error) {
@@ -144,11 +131,6 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, instr st
&i.Vtxo.RedeemTx,
&i.Vtxo.Descriptor,
&i.Vtxo.Pending,
&i.UncondForfeitTxVw.ID,
&i.UncondForfeitTxVw.Tx,
&i.UncondForfeitTxVw.VtxoTxid,
&i.UncondForfeitTxVw.VtxoVout,
&i.UncondForfeitTxVw.Position,
); err != nil {
return nil, err
}
@@ -481,16 +463,12 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
}
const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending,
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
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo
WHERE redeemed = false AND swept = false
`
type SelectSweepableVtxosRow struct {
Vtxo Vtxo
UncondForfeitTxVw UncondForfeitTxVw
Vtxo Vtxo
}
func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVtxosRow, error) {
@@ -516,11 +494,6 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt
&i.Vtxo.RedeemTx,
&i.Vtxo.Descriptor,
&i.Vtxo.Pending,
&i.UncondForfeitTxVw.ID,
&i.UncondForfeitTxVw.Tx,
&i.UncondForfeitTxVw.VtxoTxid,
&i.UncondForfeitTxVw.VtxoVout,
&i.UncondForfeitTxVw.Position,
); err != nil {
return nil, err
}
@@ -622,10 +595,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
}
const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending,
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
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo
WHERE txid = ? AND vout = ?
`
@@ -635,8 +605,7 @@ type SelectVtxoByOutpointParams struct {
}
type SelectVtxoByOutpointRow struct {
Vtxo Vtxo
UncondForfeitTxVw UncondForfeitTxVw
Vtxo Vtxo
}
func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutpointParams) (SelectVtxoByOutpointRow, error) {
@@ -656,26 +625,17 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp
&i.Vtxo.RedeemTx,
&i.Vtxo.Descriptor,
&i.Vtxo.Pending,
&i.UncondForfeitTxVw.ID,
&i.UncondForfeitTxVw.Tx,
&i.UncondForfeitTxVw.VtxoTxid,
&i.UncondForfeitTxVw.VtxoVout,
&i.UncondForfeitTxVw.Position,
)
return i, err
}
const selectVtxosByPoolTxid = `-- name: SelectVtxosByPoolTxid :many
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending,
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
SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo
WHERE pool_tx = ?
`
type SelectVtxosByPoolTxidRow struct {
Vtxo Vtxo
UncondForfeitTxVw UncondForfeitTxVw
Vtxo Vtxo
}
func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]SelectVtxosByPoolTxidRow, error) {
@@ -701,11 +661,6 @@ func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]S
&i.Vtxo.RedeemTx,
&i.Vtxo.Descriptor,
&i.Vtxo.Pending,
&i.UncondForfeitTxVw.ID,
&i.UncondForfeitTxVw.Tx,
&i.UncondForfeitTxVw.VtxoTxid,
&i.UncondForfeitTxVw.VtxoVout,
&i.UncondForfeitTxVw.Position,
); err != nil {
return nil, err
}
@@ -891,32 +846,6 @@ 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, descriptor, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET

View File

@@ -111,14 +111,6 @@ 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, descriptor, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
@@ -134,38 +126,23 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SE
pending = EXCLUDED.pending;
-- name: SelectSweepableVtxos :many
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
SELECT sqlc.embed(vtxo) FROM vtxo
WHERE redeemed = false AND swept = false;
-- name: SelectNotRedeemedVtxos :many
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
SELECT sqlc.embed(vtxo) FROM vtxo
WHERE redeemed = false;
-- name: SelectNotRedeemedVtxosWithPubkey :many
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
SELECT sqlc.embed(vtxo) FROM vtxo
WHERE redeemed = false AND INSTR(descriptor, ?) > 0;
-- name: SelectVtxoByOutpoint :one
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
SELECT sqlc.embed(vtxo) FROM vtxo
WHERE txid = ? AND vout = ?;
-- name: SelectVtxosByPoolTxid :many
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
SELECT sqlc.embed(vtxo) FROM vtxo
WHERE pool_tx = ?;
-- name: MarkVtxoAsRedeemed :exec

View File

@@ -37,10 +37,6 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro
txBody := func(querierWithTx *queries.Queries) error {
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{
Txid: vtxo.Txid,
@@ -53,25 +49,12 @@ 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},
RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true},
Pending: vtxo.Pending,
},
); 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
@@ -86,12 +69,9 @@ func (v *vxtoRepository) GetAllSweepableVtxos(ctx context.Context) ([]domain.Vtx
return nil, err
}
rows := make([]vtxoWithUnconditionalForfeitTxs, 0, len(res))
rows := make([]queries.Vtxo, 0, len(res))
for _, row := range res {
rows = append(rows, vtxoWithUnconditionalForfeitTxs{
vtxo: row.Vtxo,
tx: row.UncondForfeitTxVw,
})
rows = append(rows, row.Vtxo)
}
return readRows(rows)
}
@@ -99,7 +79,7 @@ func (v *vxtoRepository) GetAllSweepableVtxos(ctx context.Context) ([]domain.Vtx
func (v *vxtoRepository) GetAllVtxos(ctx context.Context, pubkey string) ([]domain.Vtxo, []domain.Vtxo, error) {
withPubkey := len(pubkey) > 0
var rows []vtxoWithUnconditionalForfeitTxs
var rows []queries.Vtxo
if withPubkey {
if len(pubkey) == 66 {
pubkey = pubkey[2:]
@@ -109,24 +89,18 @@ func (v *vxtoRepository) GetAllVtxos(ctx context.Context, pubkey string) ([]doma
if err != nil {
return nil, nil, err
}
rows = make([]vtxoWithUnconditionalForfeitTxs, 0, len(res))
rows = make([]queries.Vtxo, 0, len(res))
for _, row := range res {
rows = append(rows, vtxoWithUnconditionalForfeitTxs{
vtxo: row.Vtxo,
tx: row.UncondForfeitTxVw,
})
rows = append(rows, row.Vtxo)
}
} else {
res, err := v.querier.SelectNotRedeemedVtxos(ctx)
if err != nil {
return nil, nil, err
}
rows = make([]vtxoWithUnconditionalForfeitTxs, 0, len(res))
rows = make([]queries.Vtxo, 0, len(res))
for _, row := range res {
rows = append(rows, vtxoWithUnconditionalForfeitTxs{
vtxo: row.Vtxo,
tx: row.UncondForfeitTxVw,
})
rows = append(rows, row.Vtxo)
}
}
@@ -163,12 +137,7 @@ func (v *vxtoRepository) GetVtxos(ctx context.Context, outpoints []domain.VtxoKe
return nil, err
}
result, err := readRows([]vtxoWithUnconditionalForfeitTxs{
{
vtxo: res.Vtxo,
tx: res.UncondForfeitTxVw,
},
})
result, err := readRows([]queries.Vtxo{res.Vtxo})
if err != nil {
return nil, err
}
@@ -188,12 +157,9 @@ func (v *vxtoRepository) GetVtxosForRound(ctx context.Context, txid string) ([]d
if err != nil {
return nil, err
}
rows := make([]vtxoWithUnconditionalForfeitTxs, 0, len(res))
rows := make([]queries.Vtxo, 0, len(res))
for _, row := range res {
rows = append(rows, vtxoWithUnconditionalForfeitTxs{
vtxo: row.Vtxo,
tx: row.UncondForfeitTxVw,
})
rows = append(rows, row.Vtxo)
}
return readRows(rows)
@@ -281,18 +247,7 @@ func (v *vxtoRepository) UpdateExpireAt(ctx context.Context, vtxos []domain.Vtxo
return execTx(ctx, v.db, txBody)
}
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,
}
}
func rowToVtxo(row queries.Vtxo) domain.Vtxo {
return domain.Vtxo{
VtxoKey: domain.VtxoKey{
Txid: row.Txid,
@@ -302,49 +257,21 @@ func rowToVtxo(row queries.Vtxo, uncondForfeitTxs []queries.UncondForfeitTxVw) d
Descriptor: row.Descriptor.String,
Amount: uint64(row.Amount),
},
RoundTxid: row.PoolTx,
SpentBy: row.SpentBy,
Spent: row.Spent,
Redeemed: row.Redeemed,
Swept: row.Swept,
ExpireAt: row.ExpireAt,
AsyncPayment: asyncPayment,
Pending: row.Pending,
RoundTxid: row.PoolTx,
SpentBy: row.SpentBy,
Spent: row.Spent,
Redeemed: row.Redeemed,
Swept: row.Swept,
ExpireAt: row.ExpireAt,
RedeemTx: row.RedeemTx.String,
Pending: row.Pending,
}
}
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,
)
}
}
func readRows(rows []queries.Vtxo) ([]domain.Vtxo, error) {
vtxos := make([]domain.Vtxo, 0, len(rows))
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))
for _, vtxo := range rows {
vtxos = append(vtxos, rowToVtxo(vtxo))
}
return vtxos, nil

View File

@@ -365,8 +365,8 @@ func (b *txBuilder) FindLeaves(
func (b *txBuilder) BuildAsyncPaymentTransactions(
_ []domain.Vtxo, _ *secp256k1.PublicKey, _ []domain.Receiver,
) (*domain.AsyncPaymentTxs, error) {
return nil, fmt.Errorf("not implemented")
) (string, error) {
return "", fmt.Errorf("not implemented")
}
func (b *txBuilder) createPoolTx(

View File

@@ -402,28 +402,25 @@ func (b *txBuilder) FindLeaves(congestionTree tree.CongestionTree, fromtxid stri
func (b *txBuilder) BuildAsyncPaymentTransactions(
vtxos []domain.Vtxo, aspPubKey *secp256k1.PublicKey, receivers []domain.Receiver,
) (*domain.AsyncPaymentTxs, error) {
) (string, error) {
if len(vtxos) <= 0 {
return nil, fmt.Errorf("missing vtxos")
return "", fmt.Errorf("missing vtxos")
}
ins := make([]*wire.OutPoint, 0, len(vtxos))
outs := make([]*wire.TxOut, 0, len(receivers))
unconditionalForfeitTxs := make([]string, 0, len(vtxos))
redeemTxWeightEstimator := &input.TxWeightEstimator{}
for _, vtxo := range vtxos {
if vtxo.Spent || vtxo.Redeemed || vtxo.Swept {
return nil, fmt.Errorf("all vtxos must be unspent")
}
witnessUtxos := make(map[int]*wire.TxOut)
tapscripts := make(map[int]*psbt.TaprootTapLeafScript)
aspScript, err := common.P2TRScript(aspPubKey)
if err != nil {
return nil, err
redeemTxWeightEstimator := &input.TxWeightEstimator{}
for index, vtxo := range vtxos {
if vtxo.Spent || vtxo.Redeemed || vtxo.Swept {
return "", fmt.Errorf("all vtxos must be unspent")
}
vtxoTxID, err := chainhash.NewHashFromStr(vtxo.Txid)
if err != nil {
return nil, err
return "", err
}
vtxoOutpoint := &wire.OutPoint{
@@ -433,104 +430,60 @@ func (b *txBuilder) BuildAsyncPaymentTransactions(
vtxoScript, err := bitcointree.ParseVtxoScript(vtxo.Descriptor)
if err != nil {
return nil, err
return "", err
}
vtxoTapKey, vtxoTree, err := vtxoScript.TapTree()
if err != nil {
return nil, err
return "", err
}
vtxoOutputScript, err := common.P2TRScript(vtxoTapKey)
if err != nil {
return nil, err
return "", err
}
var tapscript *waddrmgr.Tapscript
forfeitTxWeightEstimator := &input.TxWeightEstimator{}
if defaultVtxoScript, ok := vtxoScript.(*bitcointree.DefaultVtxoScript); ok {
forfeitClosure := &bitcointree.MultisigClosure{
Pubkey: defaultVtxoScript.Owner,
AspPubkey: defaultVtxoScript.Asp,
}
forfeitLeaf, err := forfeitClosure.Leaf()
if err != nil {
return nil, err
}
forfeitProof, err := vtxoTree.GetTaprootMerkleProof(forfeitLeaf.TapHash())
if err != nil {
return nil, err
}
ctrlBlock, err := txscript.ParseControlBlock(forfeitProof.ControlBlock)
if err != nil {
return nil, err
}
tapscript = &waddrmgr.Tapscript{
RevealedScript: forfeitProof.Script,
ControlBlock: ctrlBlock,
}
forfeitTxWeightEstimator.AddTapscriptInput(64*2, tapscript)
forfeitTxWeightEstimator.AddP2TROutput() // ASP output
} else {
return nil, fmt.Errorf("vtxo script is not a default vtxo script, cannot be async spent")
}
forfeitTxFee, err := b.wallet.MinRelayFee(context.Background(), uint64(forfeitTxWeightEstimator.VSize()))
if err != nil {
return nil, err
}
if forfeitTxFee >= vtxo.Amount {
return nil, fmt.Errorf("forfeit tx fee is higher than the amount of the vtxo")
}
output := &wire.TxOut{
PkScript: aspScript,
Value: int64(vtxo.Amount - forfeitTxFee),
}
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{
witnessUtxos[index] = &wire.TxOut{
Value: int64(vtxo.Amount),
PkScript: vtxoOutputScript,
}
ctrlBlock, err := tapscript.ControlBlock.ToBytes()
if err != nil {
return nil, err
}
if defaultVtxoScript, ok := vtxoScript.(*bitcointree.DefaultVtxoScript); ok {
forfeitLeaf := bitcointree.MultisigClosure{
Pubkey: defaultVtxoScript.Owner,
AspPubkey: defaultVtxoScript.Asp,
}
unconditionnalForfeitPtx.Inputs[0].TaprootLeafScript = []*psbt.TaprootTapLeafScript{
{
Script: tapscript.RevealedScript,
ControlBlock: ctrlBlock,
tapLeaf, err := forfeitLeaf.Leaf()
if err != nil {
return "", err
}
leafProof, err := vtxoTree.GetTaprootMerkleProof(tapLeaf.TapHash())
if err != nil {
return "", err
}
tapscripts[index] = &psbt.TaprootTapLeafScript{
ControlBlock: leafProof.ControlBlock,
Script: leafProof.Script,
LeafVersion: txscript.BaseLeafVersion,
},
}
ctrlBlock, err := txscript.ParseControlBlock(leafProof.ControlBlock)
if err != nil {
return "", err
}
redeemTxWeightEstimator.AddTapscriptInput(64*2, &waddrmgr.Tapscript{
RevealedScript: leafProof.Script,
ControlBlock: ctrlBlock,
})
} else {
return "", fmt.Errorf("vtxo %s:%d script is not default script, can't be async spent", vtxo.Txid, vtxo.VOut)
}
forfeitTx, err := unconditionnalForfeitPtx.B64Encode()
if err != nil {
return nil, err
}
unconditionalForfeitTxs = append(unconditionalForfeitTxs, forfeitTx)
ins = append(ins, vtxoOutpoint)
redeemTxWeightEstimator.AddTapscriptInput(64*2, tapscript)
}
for range receivers {
@@ -539,27 +492,27 @@ func (b *txBuilder) BuildAsyncPaymentTransactions(
redeemTxMinRelayFee, err := b.wallet.MinRelayFee(context.Background(), uint64(redeemTxWeightEstimator.VSize()))
if err != nil {
return nil, err
return "", err
}
if redeemTxMinRelayFee >= receivers[len(receivers)-1].Amount {
return nil, fmt.Errorf("redeem tx fee is higher than the amount of the change receiver")
return "", fmt.Errorf("redeem tx fee is higher than the amount of the change receiver")
}
for i, receiver := range receivers {
offchainScript, err := bitcointree.ParseVtxoScript(receiver.Descriptor)
if err != nil {
return nil, err
return "", err
}
receiverVtxoTaprootKey, _, err := offchainScript.TapTree()
if err != nil {
return nil, err
return "", err
}
newVtxoScript, err := common.P2TRScript(receiverVtxoTaprootKey)
if err != nil {
return nil, err
return "", err
}
// Deduct the min relay fee from the very last receiver which is supposed
@@ -583,34 +536,27 @@ func (b *txBuilder) BuildAsyncPaymentTransactions(
ins, outs, 2, 0, sequences,
)
if err != nil {
return nil, err
return "", 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
redeemPtx.Inputs[i].WitnessUtxo = witnessUtxos[i]
redeemPtx.Inputs[i].TaprootLeafScript = []*psbt.TaprootTapLeafScript{tapscripts[i]}
}
redeemTx, err := redeemPtx.B64Encode()
if err != nil {
return nil, err
return "", err
}
signedRedeemTx, err := b.wallet.SignTransactionTapscript(
context.Background(), redeemTx, nil,
)
if err != nil {
return nil, err
return "", err
}
return &domain.AsyncPaymentTxs{
RedeemTx: signedRedeemTx,
UnconditionalForfeitTxs: unconditionalForfeitTxs,
}, nil
return signedRedeemTx, nil
}
// TODO use lnd CoinSelect to craft the pool tx

View File

@@ -362,7 +362,7 @@ func (h *handler) CreatePayment(
}
}
redeemTx, unconditionalForfeitTxs, err := h.svc.CreateAsyncPayment(
redeemTx, err := h.svc.CreateAsyncPayment(
ctx, inputs, receivers,
)
if err != nil {
@@ -370,8 +370,7 @@ func (h *handler) CreatePayment(
}
return &arkv1.CreatePaymentResponse{
SignedRedeemTx: redeemTx,
UsignedUnconditionalForfeitTxs: unconditionalForfeitTxs,
SignedRedeemTx: redeemTx,
}, nil
}
@@ -382,12 +381,8 @@ func (h *handler) CompletePayment(
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(),
ctx, req.GetSignedRedeemTx(),
); err != nil {
return nil, err
}

View File

@@ -65,13 +65,6 @@ type vtxoList []domain.Vtxo
func (v vtxoList) toProto() []*arkv1.Vtxo {
list := make([]*arkv1.Vtxo, 0, len(v))
for _, vv := range v {
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.Outpoint{
Txid: vv.Txid,
@@ -84,7 +77,7 @@ func (v vtxoList) toProto() []*arkv1.Vtxo {
ExpireAt: vv.ExpireAt,
SpentBy: vv.SpentBy,
Swept: vv.Swept,
PendingData: pendingData,
RedeemTx: vv.RedeemTx,
Pending: vv.Pending,
})
}