mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-17 12:14:21 +01:00
* Add gRPC, REST, and gRPC-Web clients for server access This commit introduces clients for gRPC, REST, and gRPC-Web to access the server. - gRPC client: Includes additional argument opts ...grpc.CallOption in the interface for future extensibility. - REST client: Factory function accepts http.Client as an argument to allow user customization. - gRPC-Web client: Added a Log method for fast debugging in JavaScript. The decision to use different interfaces for each client type is to accommodate specific features and extensibility requirements for each protocol. * remove grpc web * generate rest * use grpc sdk in CLI * temp wasm * ark sdk * renaming * pr review refactor * pr review refactor * walletStore & configStore * ark sdk wasm wrapper * handle event stream with rest * wip on supporting rest * store init * simulate event stream with rest * fix rest sdk wip * Fix returning forfeit txs in round event * wasm first working e2e example * pr review refactor * pr review refactor * pr review refactor * Fixes --------- Co-authored-by: altafan <18440657+altafan@users.noreply.github.com>
815 lines
20 KiB
Go
815 lines
20 KiB
Go
package arksdk
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/url"
|
|
"strconv"
|
|
"time"
|
|
|
|
arkgrpcclient "github.com/ark-network/ark-sdk/grpc"
|
|
"github.com/ark-network/ark-sdk/rest/service/arkservicerestclient"
|
|
"github.com/ark-network/ark-sdk/rest/service/arkservicerestclient/ark_service"
|
|
"github.com/ark-network/ark-sdk/rest/service/models"
|
|
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
|
"github.com/ark-network/ark/common/tree"
|
|
httptransport "github.com/go-openapi/runtime/client"
|
|
"github.com/go-openapi/strfmt"
|
|
"github.com/vulpemventures/go-elements/psetv2"
|
|
)
|
|
|
|
type arkTransportClient interface {
|
|
getInfo(ctx context.Context) (*arkv1.GetInfoResponse, error)
|
|
listVtxos(ctx context.Context, addr string) (*arkv1.ListVtxosResponse, error)
|
|
getSpendableVtxos(
|
|
ctx context.Context, addr string, computeExpiryDetails bool,
|
|
) ([]vtxo, error)
|
|
getRound(ctx context.Context, txID string) (*arkv1.GetRoundResponse, error)
|
|
getRoundByID(ctx context.Context, roundID string) (*arkv1.GetRoundByIdResponse, error)
|
|
getRedeemBranches(
|
|
ctx context.Context,
|
|
explorer Explorer,
|
|
vtxos []vtxo,
|
|
) (map[string]*redeemBranch, error)
|
|
getOffchainBalance(
|
|
ctx context.Context, addr string, computeExpiration bool,
|
|
) (uint64, map[int64]uint64, error)
|
|
onboard(
|
|
ctx context.Context, req *arkv1.OnboardRequest,
|
|
) (*arkv1.OnboardResponse, error)
|
|
registerPayment(
|
|
ctx context.Context, req *arkv1.RegisterPaymentRequest,
|
|
) (*arkv1.RegisterPaymentResponse, error)
|
|
claimPayment(
|
|
ctx context.Context, req *arkv1.ClaimPaymentRequest,
|
|
) (*arkv1.ClaimPaymentResponse, error)
|
|
getEventStream(
|
|
ctx context.Context, paymentID string, req *arkv1.GetEventStreamRequest,
|
|
) (*EventStream, error)
|
|
ping(ctx context.Context, req *arkv1.PingRequest) (*arkv1.PingResponse, error)
|
|
finalizePayment(
|
|
ctx context.Context, req *arkv1.FinalizePaymentRequest,
|
|
) (*arkv1.FinalizePaymentResponse, error)
|
|
setExplorerSvc(explorerSvc Explorer)
|
|
}
|
|
|
|
func newArkTransportClient(
|
|
aspUrl string, protocol TransportProtocol, explorer Explorer,
|
|
) (arkTransportClient, error) {
|
|
switch protocol {
|
|
case Grpc:
|
|
grpcClient, closeFn, err := newGrpcClient(aspUrl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &arkInnerClient{
|
|
grpcClient: grpcClient,
|
|
grpcCloseFn: closeFn,
|
|
explorerSvc: explorer,
|
|
eventStream: &EventStream{
|
|
eventResp: make(chan *arkv1.GetEventStreamResponse),
|
|
err: make(chan error),
|
|
},
|
|
}, nil
|
|
case Rest:
|
|
resClient, err := newRestClient(aspUrl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &arkInnerClient{
|
|
resClient: resClient,
|
|
explorerSvc: explorer,
|
|
eventStream: &EventStream{
|
|
eventResp: make(chan *arkv1.GetEventStreamResponse),
|
|
err: make(chan error),
|
|
},
|
|
}, nil
|
|
default:
|
|
return nil, errors.New("unknown protocol")
|
|
}
|
|
}
|
|
|
|
type arkInnerClient struct {
|
|
grpcClient arkgrpcclient.ArkGrpcClient
|
|
grpcCloseFn func()
|
|
|
|
resClient *arkservicerestclient.ArkV1ServiceProto
|
|
|
|
explorerSvc Explorer
|
|
|
|
eventStream *EventStream
|
|
}
|
|
|
|
func (a *arkInnerClient) setExplorerSvc(explorerSvc Explorer) {
|
|
a.explorerSvc = explorerSvc
|
|
}
|
|
|
|
type EventStream struct {
|
|
eventResp chan *arkv1.GetEventStreamResponse
|
|
err chan error
|
|
}
|
|
|
|
func (a *arkInnerClient) getEventStream(
|
|
ctx context.Context, paymentID string, req *arkv1.GetEventStreamRequest,
|
|
) (*EventStream, error) {
|
|
switch {
|
|
case a.grpcClient != nil:
|
|
stream, err := a.grpcClient.Service().GetEventStream(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
go func() {
|
|
defer close(a.eventStream.eventResp)
|
|
defer close(a.eventStream.err)
|
|
|
|
for {
|
|
resp, err := stream.Recv()
|
|
if err != nil {
|
|
a.eventStream.err <- err
|
|
return
|
|
}
|
|
|
|
a.eventStream.eventResp <- resp
|
|
}
|
|
}()
|
|
case a.resClient != nil:
|
|
go func(payID string) {
|
|
defer close(a.eventStream.eventResp)
|
|
defer close(a.eventStream.err)
|
|
|
|
timeout := time.After(30 * time.Second) // TODO make this configurable
|
|
|
|
mainloop:
|
|
for {
|
|
select {
|
|
case <-timeout:
|
|
a.eventStream.err <- errors.New("timeout reached")
|
|
break mainloop
|
|
default:
|
|
resp, err := a.ping(ctx, &arkv1.PingRequest{
|
|
PaymentId: payID,
|
|
})
|
|
if err != nil {
|
|
a.eventStream.err <- err
|
|
}
|
|
|
|
if resp.GetEvent() != nil {
|
|
levels := make([]*arkv1.TreeLevel, 0, len(resp.GetEvent().GetCongestionTree().GetLevels()))
|
|
for _, l := range resp.GetEvent().GetCongestionTree().GetLevels() {
|
|
nodes := make([]*arkv1.Node, 0, len(l.Nodes))
|
|
for _, n := range l.Nodes {
|
|
nodes = append(nodes, &arkv1.Node{
|
|
Txid: n.Txid,
|
|
Tx: n.Tx,
|
|
ParentTxid: n.ParentTxid,
|
|
})
|
|
}
|
|
levels = append(levels, &arkv1.TreeLevel{
|
|
Nodes: nodes,
|
|
})
|
|
}
|
|
a.eventStream.eventResp <- &arkv1.GetEventStreamResponse{
|
|
Event: &arkv1.GetEventStreamResponse_RoundFinalization{
|
|
RoundFinalization: &arkv1.RoundFinalizationEvent{
|
|
Id: resp.GetEvent().GetId(),
|
|
PoolTx: resp.GetEvent().GetPoolTx(),
|
|
ForfeitTxs: resp.GetEvent().GetForfeitTxs(),
|
|
CongestionTree: &arkv1.Tree{
|
|
Levels: levels,
|
|
},
|
|
Connectors: resp.GetEvent().GetConnectors(),
|
|
},
|
|
},
|
|
}
|
|
|
|
for {
|
|
roundID := resp.GetEvent().GetId()
|
|
round, err := a.getRoundByID(ctx, roundID)
|
|
if err != nil {
|
|
a.eventStream.err <- err
|
|
}
|
|
|
|
if round.GetRound().GetStage() == arkv1.RoundStage_ROUND_STAGE_FINALIZED {
|
|
ptx, _ := psetv2.NewPsetFromBase64(round.GetRound().GetPoolTx())
|
|
utx, _ := ptx.UnsignedTx()
|
|
a.eventStream.eventResp <- &arkv1.GetEventStreamResponse{
|
|
Event: &arkv1.GetEventStreamResponse_RoundFinalized{
|
|
RoundFinalized: &arkv1.RoundFinalizedEvent{
|
|
PoolTxid: utx.TxHash().String(),
|
|
},
|
|
},
|
|
}
|
|
|
|
break mainloop
|
|
}
|
|
|
|
if round.GetRound().GetStage() == arkv1.RoundStage_ROUND_STAGE_FAILED {
|
|
a.eventStream.eventResp <- &arkv1.GetEventStreamResponse{
|
|
Event: &arkv1.GetEventStreamResponse_RoundFailed{
|
|
RoundFailed: &arkv1.RoundFailed{
|
|
Id: round.GetRound().GetId(),
|
|
Reason: "unknown reason", //TODO getRoundByID should return the reason
|
|
},
|
|
},
|
|
}
|
|
|
|
break mainloop
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
}
|
|
}(paymentID)
|
|
}
|
|
|
|
return a.eventStream, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) getInfo(ctx context.Context) (*arkv1.GetInfoResponse, error) {
|
|
switch {
|
|
case a.grpcClient != nil:
|
|
return a.grpcClient.Service().GetInfo(ctx, &arkv1.GetInfoRequest{})
|
|
case a.resClient != nil:
|
|
resp, err := a.resClient.ArkService.ArkServiceGetInfo(ark_service.NewArkServiceGetInfoParams())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
roundLifetime, err := strconv.Atoi(resp.Payload.RoundLifetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
unilateralExitDelay, err := strconv.Atoi(resp.Payload.UnilateralExitDelay)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
roundInterval, err := strconv.Atoi(resp.Payload.RoundInterval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
minRelayFee, err := strconv.Atoi(resp.Payload.MinRelayFee)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &arkv1.GetInfoResponse{
|
|
Pubkey: resp.Payload.Pubkey,
|
|
RoundLifetime: int64(roundLifetime),
|
|
UnilateralExitDelay: int64(unilateralExitDelay),
|
|
RoundInterval: int64(roundInterval),
|
|
Network: resp.Payload.Network,
|
|
MinRelayFee: int64(minRelayFee),
|
|
}, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) listVtxos(
|
|
ctx context.Context,
|
|
addr string,
|
|
) (*arkv1.ListVtxosResponse, error) {
|
|
switch {
|
|
case a.grpcClient != nil:
|
|
return a.grpcClient.Service().ListVtxos(
|
|
ctx, &arkv1.ListVtxosRequest{
|
|
Address: addr,
|
|
},
|
|
)
|
|
case a.resClient != nil:
|
|
resp, err := a.resClient.ArkService.ArkServiceListVtxos(
|
|
ark_service.NewArkServiceListVtxosParams().WithAddress(addr),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
vtxos := make([]*arkv1.Vtxo, 0, len(resp.Payload.SpendableVtxos))
|
|
for _, v := range resp.Payload.SpendableVtxos {
|
|
expAt, err := strconv.Atoi(v.ExpireAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
amount, err := strconv.Atoi(v.Receiver.Amount)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
vtxos = append(vtxos, &arkv1.Vtxo{
|
|
Outpoint: &arkv1.Input{
|
|
Txid: v.Outpoint.Txid,
|
|
Vout: uint32(v.Outpoint.Vout),
|
|
},
|
|
Receiver: &arkv1.Output{
|
|
Address: v.Receiver.Address,
|
|
Amount: uint64(amount),
|
|
},
|
|
Spent: v.Spent,
|
|
PoolTxid: v.PoolTxid,
|
|
SpentBy: v.SpentBy,
|
|
ExpireAt: int64(expAt),
|
|
Swept: v.Swept,
|
|
})
|
|
}
|
|
|
|
return &arkv1.ListVtxosResponse{
|
|
SpendableVtxos: vtxos,
|
|
}, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) getRound(
|
|
ctx context.Context, txID string,
|
|
) (*arkv1.GetRoundResponse, error) {
|
|
switch {
|
|
case a.grpcClient != nil:
|
|
return a.grpcClient.Service().GetRound(
|
|
ctx, &arkv1.GetRoundRequest{
|
|
Txid: txID,
|
|
},
|
|
)
|
|
case a.resClient != nil:
|
|
resp, err := a.resClient.ArkService.ArkServiceGetRound(
|
|
ark_service.NewArkServiceGetRoundParams().WithTxid(txID),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
start, err := strconv.Atoi(resp.Payload.Round.Start)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
end, err := strconv.Atoi(resp.Payload.Round.End)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
levels := make([]*arkv1.TreeLevel, 0, len(resp.Payload.Round.CongestionTree.Levels))
|
|
for _, l := range resp.Payload.Round.CongestionTree.Levels {
|
|
nodes := make([]*arkv1.Node, 0, len(l.Nodes))
|
|
for _, n := range l.Nodes {
|
|
nodes = append(nodes, &arkv1.Node{
|
|
Txid: n.Txid,
|
|
Tx: n.Tx,
|
|
ParentTxid: n.ParentTxid,
|
|
})
|
|
}
|
|
levels = append(levels, &arkv1.TreeLevel{
|
|
Nodes: nodes,
|
|
})
|
|
}
|
|
|
|
return &arkv1.GetRoundResponse{
|
|
Round: &arkv1.Round{
|
|
Id: resp.Payload.Round.ID,
|
|
Start: int64(start),
|
|
End: int64(end),
|
|
PoolTx: resp.Payload.Round.PoolTx,
|
|
CongestionTree: &arkv1.Tree{
|
|
Levels: levels,
|
|
},
|
|
ForfeitTxs: resp.Payload.Round.ForfeitTxs,
|
|
Connectors: resp.Payload.Round.Connectors,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) getSpendableVtxos(
|
|
ctx context.Context, addr string, computeExpiryDetails bool,
|
|
) ([]vtxo, error) {
|
|
allVtxos, err := a.listVtxos(ctx, addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
vtxos := make([]vtxo, 0, len(allVtxos.GetSpendableVtxos()))
|
|
for _, v := range allVtxos.GetSpendableVtxos() {
|
|
var expireAt *time.Time
|
|
if v.ExpireAt > 0 {
|
|
t := time.Unix(v.ExpireAt, 0)
|
|
expireAt = &t
|
|
}
|
|
if v.Swept {
|
|
continue
|
|
}
|
|
vtxos = append(vtxos, vtxo{
|
|
amount: v.Receiver.Amount,
|
|
txid: v.Outpoint.Txid,
|
|
vout: v.Outpoint.Vout,
|
|
poolTxid: v.PoolTxid,
|
|
expireAt: expireAt,
|
|
})
|
|
}
|
|
|
|
if !computeExpiryDetails {
|
|
return vtxos, nil
|
|
}
|
|
|
|
redeemBranches, err := a.getRedeemBranches(ctx, a.explorerSvc, vtxos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for vtxoTxid, branch := range redeemBranches {
|
|
expiration, err := branch.expireAt(a.explorerSvc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i, vtxo := range vtxos {
|
|
if vtxo.txid == vtxoTxid {
|
|
vtxos[i].expireAt = expiration
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return vtxos, nil
|
|
}
|
|
|
|
type vtxo struct {
|
|
amount uint64
|
|
txid string
|
|
vout uint32
|
|
poolTxid string
|
|
expireAt *time.Time
|
|
}
|
|
|
|
func newGrpcClient(
|
|
aspUrl string,
|
|
) (arkgrpcclient.ArkGrpcClient, func(), error) {
|
|
return arkgrpcclient.New(aspUrl)
|
|
}
|
|
|
|
func newRestClient(
|
|
serviceURL string,
|
|
) (*arkservicerestclient.ArkV1ServiceProto, error) {
|
|
parsedURL, err := url.Parse(serviceURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
schemes := []string{parsedURL.Scheme}
|
|
host := parsedURL.Host
|
|
basePath := parsedURL.Path
|
|
|
|
if basePath == "" {
|
|
basePath = arkservicerestclient.DefaultBasePath
|
|
}
|
|
|
|
cfg := &arkservicerestclient.TransportConfig{
|
|
Host: host,
|
|
BasePath: basePath,
|
|
Schemes: schemes,
|
|
}
|
|
|
|
transport := httptransport.New(cfg.Host, cfg.BasePath, cfg.Schemes)
|
|
return arkservicerestclient.New(transport, strfmt.Default), nil
|
|
}
|
|
|
|
func (a *arkInnerClient) getRedeemBranches(
|
|
ctx context.Context,
|
|
explorer Explorer,
|
|
vtxos []vtxo,
|
|
) (map[string]*redeemBranch, error) {
|
|
congestionTrees := make(map[string]tree.CongestionTree, 0)
|
|
redeemBranches := make(map[string]*redeemBranch, 0)
|
|
|
|
for _, vtxo := range vtxos {
|
|
if _, ok := congestionTrees[vtxo.poolTxid]; !ok {
|
|
round, err := a.getRound(ctx, vtxo.poolTxid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
treeFromRound := round.GetRound().GetCongestionTree()
|
|
congestionTree, err := toCongestionTree(treeFromRound)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
congestionTrees[vtxo.poolTxid] = congestionTree
|
|
}
|
|
|
|
redeemBranch, err := newRedeemBranch(
|
|
explorer, congestionTrees[vtxo.poolTxid], vtxo,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
redeemBranches[vtxo.txid] = redeemBranch
|
|
}
|
|
|
|
return redeemBranches, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) getOffchainBalance(
|
|
ctx context.Context, addr string, computeExpiration bool,
|
|
) (uint64, map[int64]uint64, error) {
|
|
amountByExpiration := make(map[int64]uint64, 0)
|
|
|
|
vtxos, err := a.getSpendableVtxos(ctx, addr, computeExpiration)
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
var balance uint64
|
|
for _, vtxo := range vtxos {
|
|
balance += vtxo.amount
|
|
|
|
if vtxo.expireAt != nil {
|
|
expiration := vtxo.expireAt.Unix()
|
|
|
|
if _, ok := amountByExpiration[expiration]; !ok {
|
|
amountByExpiration[expiration] = 0
|
|
}
|
|
|
|
amountByExpiration[expiration] += vtxo.amount
|
|
}
|
|
}
|
|
|
|
return balance, amountByExpiration, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) onboard(
|
|
ctx context.Context, req *arkv1.OnboardRequest,
|
|
) (*arkv1.OnboardResponse, error) {
|
|
switch {
|
|
case a.grpcClient != nil:
|
|
return a.grpcClient.Service().Onboard(ctx, req)
|
|
case a.resClient != nil:
|
|
levels := make([]*models.V1TreeLevel, 0, len(req.GetCongestionTree().GetLevels()))
|
|
for _, l := range req.GetCongestionTree().GetLevels() {
|
|
nodes := make([]*models.V1Node, 0, len(l.GetNodes()))
|
|
for _, n := range l.GetNodes() {
|
|
nodes = append(nodes, &models.V1Node{
|
|
Txid: n.GetTxid(),
|
|
Tx: n.GetTx(),
|
|
ParentTxid: n.GetParentTxid(),
|
|
})
|
|
}
|
|
levels = append(levels, &models.V1TreeLevel{
|
|
Nodes: nodes,
|
|
})
|
|
}
|
|
congestionTree := models.V1Tree{
|
|
Levels: levels,
|
|
}
|
|
body := models.V1OnboardRequest{
|
|
BoardingTx: req.GetBoardingTx(),
|
|
CongestionTree: &congestionTree,
|
|
UserPubkey: req.GetUserPubkey(),
|
|
}
|
|
_, err := a.resClient.ArkService.ArkServiceOnboard(
|
|
ark_service.NewArkServiceOnboardParams().WithBody(&body),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &arkv1.OnboardResponse{}, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) registerPayment(
|
|
ctx context.Context, req *arkv1.RegisterPaymentRequest,
|
|
) (*arkv1.RegisterPaymentResponse, error) {
|
|
switch {
|
|
case a.grpcClient != nil:
|
|
return a.grpcClient.Service().RegisterPayment(ctx, req)
|
|
case a.resClient != nil:
|
|
inputs := make([]*models.V1Input, 0, len(req.GetInputs()))
|
|
for _, i := range req.GetInputs() {
|
|
inputs = append(inputs, &models.V1Input{
|
|
Txid: i.GetTxid(),
|
|
Vout: int64(i.GetVout()),
|
|
})
|
|
}
|
|
body := models.V1RegisterPaymentRequest{
|
|
Inputs: inputs,
|
|
}
|
|
resp, err := a.resClient.ArkService.ArkServiceRegisterPayment(
|
|
ark_service.NewArkServiceRegisterPaymentParams().WithBody(&body),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &arkv1.RegisterPaymentResponse{
|
|
Id: resp.Payload.ID,
|
|
}, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) claimPayment(
|
|
ctx context.Context, req *arkv1.ClaimPaymentRequest,
|
|
) (*arkv1.ClaimPaymentResponse, error) {
|
|
switch {
|
|
case a.grpcClient != nil:
|
|
return a.grpcClient.Service().ClaimPayment(ctx, req)
|
|
case a.resClient != nil:
|
|
outputs := make([]*models.V1Output, 0, len(req.GetOutputs()))
|
|
for _, o := range req.GetOutputs() {
|
|
outputs = append(outputs, &models.V1Output{
|
|
Address: o.GetAddress(),
|
|
Amount: strconv.Itoa(int(o.GetAmount())),
|
|
})
|
|
}
|
|
body := models.V1ClaimPaymentRequest{
|
|
ID: req.GetId(),
|
|
Outputs: outputs,
|
|
}
|
|
|
|
_, err := a.resClient.ArkService.ArkServiceClaimPayment(
|
|
ark_service.NewArkServiceClaimPaymentParams().WithBody(&body),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &arkv1.ClaimPaymentResponse{}, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) ping(
|
|
ctx context.Context, req *arkv1.PingRequest,
|
|
) (*arkv1.PingResponse, error) {
|
|
switch {
|
|
case a.grpcClient != nil:
|
|
return a.grpcClient.Service().Ping(ctx, req)
|
|
case a.resClient != nil:
|
|
r := ark_service.NewArkServicePingParams()
|
|
r.SetPaymentID(req.GetPaymentId())
|
|
resp, err := a.resClient.ArkService.ArkServicePing(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var event *arkv1.RoundFinalizationEvent
|
|
if resp.Payload.Event != nil &&
|
|
resp.Payload.Event.ID != "" &&
|
|
len(resp.Payload.Event.ForfeitTxs) > 0 &&
|
|
len(resp.Payload.Event.CongestionTree.Levels) > 0 &&
|
|
len(resp.Payload.Event.Connectors) > 0 &&
|
|
resp.Payload.Event.PoolTx != "" {
|
|
levels := make([]*arkv1.TreeLevel, 0, len(resp.Payload.Event.CongestionTree.Levels))
|
|
for _, l := range resp.Payload.Event.CongestionTree.Levels {
|
|
nodes := make([]*arkv1.Node, 0, len(l.Nodes))
|
|
for _, n := range l.Nodes {
|
|
nodes = append(nodes, &arkv1.Node{
|
|
Txid: n.Txid,
|
|
Tx: n.Tx,
|
|
ParentTxid: n.ParentTxid,
|
|
})
|
|
}
|
|
levels = append(levels, &arkv1.TreeLevel{
|
|
Nodes: nodes,
|
|
})
|
|
}
|
|
|
|
event = &arkv1.RoundFinalizationEvent{
|
|
Id: resp.Payload.Event.ID,
|
|
PoolTx: resp.Payload.Event.PoolTx,
|
|
ForfeitTxs: resp.Payload.Event.ForfeitTxs,
|
|
CongestionTree: &arkv1.Tree{
|
|
Levels: levels,
|
|
},
|
|
Connectors: resp.Payload.Event.Connectors,
|
|
}
|
|
}
|
|
|
|
return &arkv1.PingResponse{
|
|
ForfeitTxs: resp.Payload.ForfeitTxs,
|
|
Event: event,
|
|
}, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) finalizePayment(
|
|
ctx context.Context, req *arkv1.FinalizePaymentRequest,
|
|
) (*arkv1.FinalizePaymentResponse, error) {
|
|
switch {
|
|
case a.grpcClient != nil:
|
|
return a.grpcClient.Service().FinalizePayment(ctx, req)
|
|
case a.resClient != nil:
|
|
body := models.V1FinalizePaymentRequest{
|
|
SignedForfeitTxs: req.GetSignedForfeitTxs(),
|
|
}
|
|
_, err := a.resClient.ArkService.ArkServiceFinalizePayment(
|
|
ark_service.NewArkServiceFinalizePaymentParams().WithBody(&body),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &arkv1.FinalizePaymentResponse{}, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (a *arkInnerClient) getRoundByID(
|
|
ctx context.Context, roundID string,
|
|
) (*arkv1.GetRoundByIdResponse, error) {
|
|
switch {
|
|
case a.grpcClient != nil:
|
|
return a.grpcClient.Service().GetRoundById(ctx, &arkv1.GetRoundByIdRequest{
|
|
Id: roundID,
|
|
})
|
|
case a.resClient != nil:
|
|
resp, err := a.resClient.ArkService.ArkServiceGetRoundByID(
|
|
ark_service.NewArkServiceGetRoundByIDParams().WithID(roundID),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
start, err := strconv.Atoi(resp.Payload.Round.Start)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
end, err := strconv.Atoi(resp.Payload.Round.End)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
levels := make([]*arkv1.TreeLevel, 0, len(resp.Payload.Round.CongestionTree.Levels))
|
|
for _, l := range resp.Payload.Round.CongestionTree.Levels {
|
|
nodes := make([]*arkv1.Node, 0, len(l.Nodes))
|
|
for _, n := range l.Nodes {
|
|
nodes = append(nodes, &arkv1.Node{
|
|
Txid: n.Txid,
|
|
Tx: n.Tx,
|
|
ParentTxid: n.ParentTxid,
|
|
})
|
|
}
|
|
levels = append(levels, &arkv1.TreeLevel{
|
|
Nodes: nodes,
|
|
})
|
|
}
|
|
|
|
stage := stageStrToInt(resp.Payload.Round.Stage)
|
|
|
|
return &arkv1.GetRoundByIdResponse{
|
|
Round: &arkv1.Round{
|
|
Id: resp.Payload.Round.ID,
|
|
Start: int64(start),
|
|
End: int64(end),
|
|
PoolTx: resp.Payload.Round.PoolTx,
|
|
CongestionTree: &arkv1.Tree{
|
|
Levels: levels,
|
|
},
|
|
ForfeitTxs: resp.Payload.Round.ForfeitTxs,
|
|
Connectors: resp.Payload.Round.Connectors,
|
|
Stage: arkv1.RoundStage(stage),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func stageStrToInt(stage models.V1RoundStage) int {
|
|
switch stage {
|
|
case models.V1RoundStageROUNDSTAGEUNSPECIFIED:
|
|
return 0
|
|
case models.V1RoundStageROUNDSTAGEREGISTRATION:
|
|
return 1
|
|
case models.V1RoundStageROUNDSTAGEFINALIZATION:
|
|
return 2
|
|
case models.V1RoundStageROUNDSTAGEFINALIZED:
|
|
return 3
|
|
case models.V1RoundStageROUNDSTAGEFAILED:
|
|
return 4
|
|
}
|
|
|
|
return -1
|
|
}
|