package wallet import ( context "context" "fmt" "github.com/phamminh0811/private-grpc/nockchain" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) type NockchainClient struct { conn *grpc.ClientConn client nockchain.NockchainServiceClient appClient nockchain.NockAppServiceClient } // NewNockchainClient creates a new gRPC client connection func NewNockchainClient(address string) (*NockchainClient, error) { creds := credentials.NewClientTLSFromCert(nil, "") conn, err := grpc.NewClient( address, grpc.WithTransportCredentials(creds), ) if err != nil { return nil, fmt.Errorf("failed to connect: %w", err) } client := nockchain.NewNockchainServiceClient(conn) appClient := nockchain.NewNockAppServiceClient(conn) return &NockchainClient{ conn: conn, client: client, appClient: appClient, }, nil } func (nc *NockchainClient) Close() error { return nc.conn.Close() } func (nc *NockchainClient) WalletGetBalance(address string) (*nockchain.WalletBalanceData, error) { pageToken := "" allNotes := []*nockchain.BalanceEntry{} var height *nockchain.BlockHeight var blockId *nockchain.Hash for { req := nockchain.WalletGetBalanceRequest{ Address: address, Page: &nockchain.PageRequest{ ClientPageItemsLimit: 0, PageToken: pageToken, MaxBytes: 0, }, } resp, err := nc.client.WalletGetBalance(context.Background(), &req) if err != nil { return nil, err } balance := nockchain.WalletBalanceData{} switch resp.Result.(type) { case *nockchain.WalletGetBalanceResponse_Balance: balance = *resp.GetBalance() case *nockchain.WalletGetBalanceResponse_Error: return nil, fmt.Errorf("error: %s", resp.GetError().Message) default: return nil, fmt.Errorf("invalid result type") } if height == nil { height = balance.Height blockId = balance.BlockId } if balance.Height != height || balance.BlockId != blockId { return nil, fmt.Errorf("snapshot changed during pagination; retry") } allNotes = append(allNotes, balance.Notes...) if balance.Page.NextPageToken == "" { break } else { pageToken = balance.Page.NextPageToken } } return &nockchain.WalletBalanceData{ Notes: allNotes, Height: height, BlockId: blockId, Page: &nockchain.PageResponse{ NextPageToken: "", }, }, nil } func (nc *NockchainClient) TxAccepted(txId string) (*nockchain.TransactionAcceptedResponse, error) { req := nockchain.TransactionAcceptedRequest{ TxId: &nockchain.Base58Hash{ Hash: txId, }, } resp, err := nc.client.TransactionAccepted(context.Background(), &req) if err != nil { return nil, err } switch resp.Result.(type) { case *nockchain.TransactionAcceptedResponse_Accepted: return resp, nil case *nockchain.TransactionAcceptedResponse_Error: return nil, fmt.Errorf("error: %s", resp.GetError().Message) default: return nil, fmt.Errorf("invalid result type") } } func (nc *NockchainClient) WalletSendTransaction(tx *nockchain.RawTx) (*nockchain.WalletSendTransactionResponse, error) { inputs := []*nockchain.NamedInput{} for _, i := range tx.Inputs { input, err := ConvertInput(i) if err != nil { return nil, fmt.Errorf("error converting input: %v", err) } inputs = append(inputs, input) } req := nockchain.WalletSendTransactionRequest{ TxId: ParseHash(tx.TxId), RawTx: &nockchain.RawTransaction{ Id: ParseHash(tx.TxId), NamedInputs: inputs, TotalFees: &nockchain.Nicks{Value: tx.TotalFees}, TimelockRange: &nockchain.TimeLockRangeAbsolute{ Min: (*nockchain.BlockHeight)(tx.TimelockRange.Min), Max: (*nockchain.BlockHeight)(tx.TimelockRange.Max), }, }, } resp, err := nc.client.WalletSendTransaction(context.Background(), &req) if err != nil { return nil, err } switch resp.Result.(type) { case *nockchain.WalletSendTransactionResponse_Ack: return resp, nil case *nockchain.WalletSendTransactionResponse_Error: return nil, fmt.Errorf("error: %s", resp.GetError().Message) default: return nil, fmt.Errorf("invalid result type") } } func (nc *NockchainClient) Peek(pid int32, path []byte) ([]byte, error) { req := nockchain.PeekRequest{ Pid: pid, Path: path, } resp, err := nc.appClient.Peek(context.Background(), &req) if err != nil { return nil, err } switch resp.Result.(type) { case *nockchain.PeekResponse_Data: return resp.GetData(), nil case *nockchain.PeekResponse_Error: return nil, fmt.Errorf("error: %s", resp.GetError().Message) default: return nil, fmt.Errorf("invalid result type") } } func (nc *NockchainClient) Poke(pid int32, wire *nockchain.Wire, payload []byte) (bool, error) { req := nockchain.PokeRequest{ Pid: pid, Wire: wire, Payload: payload, } resp, err := nc.appClient.Poke(context.Background(), &req) if err != nil { return false, err } switch resp.Result.(type) { case *nockchain.PokeResponse_Acknowledged: return resp.GetAcknowledged(), nil case *nockchain.PokeResponse_Error: return false, fmt.Errorf("error: %s", resp.GetError().Message) default: return false, fmt.Errorf("invalid result type") } }