mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-18 04:34:19 +01:00
Update client sdk (#207)
* Add bitcoin networks * Refactor client * Refactor explorer * Refactor store * Refactor wallet * Refactor sdk client * Refactor wasm & Update examples * Move common util funcs to internal/utils * Move to constants for service types * Add unit tests * Parallelize tests * Lint * Add job to gh action * go mod tidy * Fixes * Fixes * Fix compose file * Fixes * Fixes after review: * Drop factory pattern * Drop password from ark client methods * Make singlekey wallet manage store and wallet store instead of defining WalletStore as extension of Store * Move constants to arksdk module * Drop config and expect directory store and wallet as ark client factory args * Fix * Add constants for bitcoin/liquid explorer * Fix test * Fix wasm * Rename client.Client to client.ASPClient * Rename store.Store to store.ConfigStore * Rename wallet.Wallet to wallet.WalletService * Renamings * Lint * Fixes * Move everything to internal/utils & move ComputeVtxoTaprootScript to common * Go mod tidy
This commit is contained in:
committed by
GitHub
parent
e45bff3c70
commit
89df461623
277
pkg/client-sdk/client/grpc/client.go
Normal file
277
pkg/client-sdk/client/grpc/client.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package grpcclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark-sdk/client"
|
||||
"github.com/ark-network/ark-sdk/explorer"
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
type grpcClient struct {
|
||||
conn *grpc.ClientConn
|
||||
svc arkv1.ArkServiceClient
|
||||
eventsCh chan client.RoundEventChannel
|
||||
}
|
||||
|
||||
func NewClient(aspUrl string) (client.ASPClient, error) {
|
||||
if len(aspUrl) <= 0 {
|
||||
return nil, fmt.Errorf("missing asp url")
|
||||
}
|
||||
|
||||
creds := insecure.NewCredentials()
|
||||
port := 80
|
||||
if strings.HasPrefix(aspUrl, "https://") {
|
||||
aspUrl = strings.TrimPrefix(aspUrl, "https://")
|
||||
creds = credentials.NewTLS(nil)
|
||||
port = 443
|
||||
}
|
||||
if !strings.Contains(aspUrl, ":") {
|
||||
aspUrl = fmt.Sprintf("%s:%d", aspUrl, port)
|
||||
}
|
||||
conn, err := grpc.NewClient(aspUrl, grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
svc := arkv1.NewArkServiceClient(conn)
|
||||
eventsCh := make(chan client.RoundEventChannel)
|
||||
|
||||
return &grpcClient{conn, svc, eventsCh}, nil
|
||||
}
|
||||
|
||||
func (c *grpcClient) Close() {
|
||||
//nolint:all
|
||||
c.conn.Close()
|
||||
}
|
||||
|
||||
func (a *grpcClient) GetEventStream(
|
||||
ctx context.Context, paymentID string, req *arkv1.GetEventStreamRequest,
|
||||
) (<-chan client.RoundEventChannel, error) {
|
||||
stream, err := a.svc.GetEventStream(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(a.eventsCh)
|
||||
|
||||
for {
|
||||
resp, err := stream.Recv()
|
||||
if err != nil {
|
||||
a.eventsCh <- client.RoundEventChannel{Err: err}
|
||||
return
|
||||
}
|
||||
|
||||
a.eventsCh <- client.RoundEventChannel{Event: resp}
|
||||
}
|
||||
}()
|
||||
|
||||
return a.eventsCh, nil
|
||||
}
|
||||
|
||||
func (a *grpcClient) GetInfo(ctx context.Context) (*arkv1.GetInfoResponse, error) {
|
||||
return a.svc.GetInfo(ctx, &arkv1.GetInfoRequest{})
|
||||
}
|
||||
|
||||
func (a *grpcClient) ListVtxos(
|
||||
ctx context.Context,
|
||||
addr string,
|
||||
) (*arkv1.ListVtxosResponse, error) {
|
||||
return a.svc.ListVtxos(ctx, &arkv1.ListVtxosRequest{Address: addr})
|
||||
}
|
||||
|
||||
func (a *grpcClient) GetRound(
|
||||
ctx context.Context, txID string,
|
||||
) (*arkv1.GetRoundResponse, error) {
|
||||
return a.svc.GetRound(ctx, &arkv1.GetRoundRequest{Txid: txID})
|
||||
}
|
||||
|
||||
func (a *grpcClient) GetSpendableVtxos(
|
||||
ctx context.Context, addr string, explorerSvc explorer.Explorer,
|
||||
) ([]*client.Vtxo, error) {
|
||||
allVtxos, err := a.ListVtxos(ctx, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vtxos := make([]*client.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, &client.Vtxo{
|
||||
Amount: v.Receiver.Amount,
|
||||
Txid: v.Outpoint.Txid,
|
||||
VOut: v.Outpoint.Vout,
|
||||
RoundTxid: v.PoolTxid,
|
||||
ExpiresAt: expireAt,
|
||||
})
|
||||
}
|
||||
|
||||
if explorerSvc == nil {
|
||||
return vtxos, nil
|
||||
}
|
||||
|
||||
redeemBranches, err := a.GetRedeemBranches(ctx, vtxos, explorerSvc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for vtxoTxid, branch := range redeemBranches {
|
||||
expiration, err := branch.ExpiresAt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, vtxo := range vtxos {
|
||||
if vtxo.Txid == vtxoTxid {
|
||||
vtxos[i].ExpiresAt = expiration
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vtxos, nil
|
||||
}
|
||||
|
||||
func (a *grpcClient) GetRedeemBranches(
|
||||
ctx context.Context, vtxos []*client.Vtxo, explorerSvc explorer.Explorer,
|
||||
) (map[string]*client.RedeemBranch, error) {
|
||||
congestionTrees := make(map[string]tree.CongestionTree, 0)
|
||||
redeemBranches := make(map[string]*client.RedeemBranch, 0)
|
||||
|
||||
for _, vtxo := range vtxos {
|
||||
if _, ok := congestionTrees[vtxo.RoundTxid]; !ok {
|
||||
round, err := a.GetRound(ctx, vtxo.RoundTxid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
treeFromRound := round.GetRound().GetCongestionTree()
|
||||
congestionTree, err := toCongestionTree(treeFromRound)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
congestionTrees[vtxo.RoundTxid] = congestionTree
|
||||
}
|
||||
|
||||
redeemBranch, err := client.NewRedeemBranch(
|
||||
explorerSvc, congestionTrees[vtxo.RoundTxid], vtxo,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
redeemBranches[vtxo.Txid] = redeemBranch
|
||||
}
|
||||
|
||||
return redeemBranches, nil
|
||||
}
|
||||
|
||||
func (a *grpcClient) GetOffchainBalance(
|
||||
ctx context.Context, addr string, explorerSvc explorer.Explorer,
|
||||
) (uint64, map[int64]uint64, error) {
|
||||
amountByExpiration := make(map[int64]uint64, 0)
|
||||
|
||||
vtxos, err := a.GetSpendableVtxos(ctx, addr, explorerSvc)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var balance uint64
|
||||
for _, vtxo := range vtxos {
|
||||
balance += vtxo.Amount
|
||||
|
||||
if vtxo.ExpiresAt != nil {
|
||||
expiration := vtxo.ExpiresAt.Unix()
|
||||
|
||||
if _, ok := amountByExpiration[expiration]; !ok {
|
||||
amountByExpiration[expiration] = 0
|
||||
}
|
||||
|
||||
amountByExpiration[expiration] += vtxo.Amount
|
||||
}
|
||||
}
|
||||
|
||||
return balance, amountByExpiration, nil
|
||||
}
|
||||
|
||||
func (a *grpcClient) Onboard(
|
||||
ctx context.Context, req *arkv1.OnboardRequest,
|
||||
) (*arkv1.OnboardResponse, error) {
|
||||
return a.svc.Onboard(ctx, req)
|
||||
}
|
||||
|
||||
func (a *grpcClient) RegisterPayment(
|
||||
ctx context.Context, req *arkv1.RegisterPaymentRequest,
|
||||
) (*arkv1.RegisterPaymentResponse, error) {
|
||||
return a.svc.RegisterPayment(ctx, req)
|
||||
}
|
||||
|
||||
func (a *grpcClient) ClaimPayment(
|
||||
ctx context.Context, req *arkv1.ClaimPaymentRequest,
|
||||
) (*arkv1.ClaimPaymentResponse, error) {
|
||||
return a.svc.ClaimPayment(ctx, req)
|
||||
}
|
||||
|
||||
func (a *grpcClient) Ping(
|
||||
ctx context.Context, req *arkv1.PingRequest,
|
||||
) (*arkv1.PingResponse, error) {
|
||||
return a.svc.Ping(ctx, req)
|
||||
}
|
||||
|
||||
func (a *grpcClient) FinalizePayment(
|
||||
ctx context.Context, req *arkv1.FinalizePaymentRequest,
|
||||
) (*arkv1.FinalizePaymentResponse, error) {
|
||||
return a.svc.FinalizePayment(ctx, req)
|
||||
}
|
||||
|
||||
func (a *grpcClient) GetRoundByID(
|
||||
ctx context.Context, roundID string,
|
||||
) (*arkv1.GetRoundByIdResponse, error) {
|
||||
return a.svc.GetRoundById(ctx, &arkv1.GetRoundByIdRequest{
|
||||
Id: roundID,
|
||||
})
|
||||
}
|
||||
|
||||
func toCongestionTree(treeFromProto *arkv1.Tree) (tree.CongestionTree, error) {
|
||||
levels := make(tree.CongestionTree, 0, len(treeFromProto.Levels))
|
||||
|
||||
for _, level := range treeFromProto.Levels {
|
||||
nodes := make([]tree.Node, 0, len(level.Nodes))
|
||||
|
||||
for _, node := range level.Nodes {
|
||||
nodes = append(nodes, tree.Node{
|
||||
Txid: node.Txid,
|
||||
Tx: node.Tx,
|
||||
ParentTxid: node.ParentTxid,
|
||||
Leaf: false,
|
||||
})
|
||||
}
|
||||
|
||||
levels = append(levels, nodes)
|
||||
}
|
||||
|
||||
for j, treeLvl := range levels {
|
||||
for i, node := range treeLvl {
|
||||
if len(levels.Children(node.Txid)) == 0 {
|
||||
levels[j][i].Leaf = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return levels, nil
|
||||
}
|
||||
Reference in New Issue
Block a user