Files
ark/pkg/client-sdk/unilateral_redeem.go
Dusan Sekulic 1f42843a69 Add first version of client SDK (#193)
* 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>
2024-07-18 16:27:14 +02:00

181 lines
3.9 KiB
Go

package arksdk
import (
"fmt"
"time"
"github.com/ark-network/ark/common/tree"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/vulpemventures/go-elements/psetv2"
"github.com/vulpemventures/go-elements/taproot"
)
type redeemBranch struct {
vtxo *vtxo
branch []*psetv2.Pset
internalKey *secp256k1.PublicKey
sweepClosure *taproot.TapElementsLeaf
lifetime time.Duration
explorer Explorer
}
func newRedeemBranch(
explorer Explorer,
congestionTree tree.CongestionTree, vtxo vtxo,
) (*redeemBranch, error) {
sweepClosure, seconds, err := findSweepClosure(congestionTree)
if err != nil {
return nil, err
}
lifetime, err := time.ParseDuration(fmt.Sprintf("%ds", seconds))
if err != nil {
return nil, err
}
nodes, err := congestionTree.Branch(vtxo.txid)
if err != nil {
return nil, err
}
branch := make([]*psetv2.Pset, 0, len(nodes))
for _, node := range nodes {
pset, err := psetv2.NewPsetFromBase64(node.Tx)
if err != nil {
return nil, err
}
branch = append(branch, pset)
}
xOnlyKey := branch[0].Inputs[0].TapInternalKey
internalKey, err := schnorr.ParsePubKey(xOnlyKey)
if err != nil {
return nil, err
}
return &redeemBranch{
vtxo: &vtxo,
branch: branch,
internalKey: internalKey,
sweepClosure: sweepClosure,
lifetime: lifetime,
explorer: explorer,
}, nil
}
// RedeemPath returns the list of transactions to broadcast in order to access the vtxo output
func (r *redeemBranch) redeemPath() ([]string, error) {
transactions := make([]string, 0, len(r.branch))
offchainPath, err := r.offchainPath()
if err != nil {
return nil, err
}
for _, pset := range offchainPath {
for i, input := range pset.Inputs {
if len(input.TapLeafScript) == 0 {
return nil, fmt.Errorf("tap leaf script not found on input #%d", i)
}
for _, leaf := range input.TapLeafScript {
closure, err := tree.DecodeClosure(leaf.Script)
if err != nil {
return nil, err
}
switch closure.(type) {
case *tree.UnrollClosure:
controlBlock, err := leaf.ControlBlock.ToBytes()
if err != nil {
return nil, err
}
unsignedTx, err := pset.UnsignedTx()
if err != nil {
return nil, err
}
unsignedTx.Inputs[i].Witness = [][]byte{
leaf.Script,
controlBlock[:],
}
hex, err := unsignedTx.ToHex()
if err != nil {
return nil, err
}
transactions = append(transactions, hex)
}
}
}
}
return transactions, nil
}
func (r *redeemBranch) expireAt(explorer Explorer) (*time.Time, error) {
lastKnownBlocktime := int64(0)
confirmed, blocktime, _ := explorer.GetTxBlockTime(r.vtxo.poolTxid)
if confirmed {
lastKnownBlocktime = blocktime
} else {
expirationFromNow := time.Now().Add(time.Minute).Add(r.lifetime)
return &expirationFromNow, nil
}
for _, pset := range r.branch {
utx, _ := pset.UnsignedTx()
txid := utx.TxHash().String()
confirmed, blocktime, err := explorer.GetTxBlockTime(txid)
if err != nil {
break
}
if confirmed {
lastKnownBlocktime = blocktime
continue
}
break
}
t := time.Unix(lastKnownBlocktime, 0).Add(r.lifetime)
return &t, nil
}
// offchainPath checks for transactions of the branch onchain and returns only the offchain part
func (r *redeemBranch) offchainPath() ([]*psetv2.Pset, error) {
offchainPath := append([]*psetv2.Pset{}, r.branch...)
for i := len(r.branch) - 1; i >= 0; i-- {
pset := r.branch[i]
unsignedTx, err := pset.UnsignedTx()
if err != nil {
return nil, err
}
txHash := unsignedTx.TxHash().String()
_, err = r.explorer.GetTxHex(txHash)
if err != nil {
continue
}
// if no error, the tx exists onchain, so we can remove it (+ the parents) from the branch
if i == len(r.branch)-1 {
offchainPath = []*psetv2.Pset{}
} else {
offchainPath = r.branch[i+1:]
}
break
}
return offchainPath, nil
}