Files
ark/server/test/e2e/covenantless/e2e_test.go
Louis Singer 02542c3634 Support forfeit with CHECKLOCKTIMEVERIFY (#389)
* explicit Timelock struct

* support & test CLTV forfeit path

* fix wasm pkg

* fix wasm

* fix liquid GetCurrentBlockTime

* cleaning

* move esplora URL check
2024-11-28 14:51:06 +01:00

827 lines
22 KiB
Go

package e2e_test
import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"testing"
"time"
"github.com/ark-network/ark/common"
"github.com/ark-network/ark/common/bitcointree"
"github.com/ark-network/ark/common/tree"
arksdk "github.com/ark-network/ark/pkg/client-sdk"
"github.com/ark-network/ark/pkg/client-sdk/client"
grpcclient "github.com/ark-network/ark/pkg/client-sdk/client/grpc"
"github.com/ark-network/ark/pkg/client-sdk/explorer"
"github.com/ark-network/ark/pkg/client-sdk/redemption"
"github.com/ark-network/ark/pkg/client-sdk/store"
inmemorystoreconfig "github.com/ark-network/ark/pkg/client-sdk/store/inmemory"
"github.com/ark-network/ark/pkg/client-sdk/types"
singlekeywallet "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey"
inmemorystore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store/inmemory"
utils "github.com/ark-network/ark/server/test/e2e"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip04"
"github.com/stretchr/testify/require"
)
const (
composePath = "../../../../docker-compose.clark.regtest.yml"
redeemAddress = "bcrt1q2wrgf2hrkfegt0t97cnv4g5yvfjua9k6vua54d"
)
func TestMain(m *testing.M) {
_, err := utils.RunCommand("docker", "compose", "-f", composePath, "up", "-d", "--build")
if err != nil {
fmt.Printf("error starting docker-compose: %s", err)
os.Exit(1)
}
time.Sleep(10 * time.Second)
if err := utils.GenerateBlock(); err != nil {
fmt.Printf("error generating block: %s", err)
os.Exit(1)
}
if err := setupServerWallet(); err != nil {
fmt.Println(err)
os.Exit(1)
}
time.Sleep(3 * time.Second)
_, err = runClarkCommand("init", "--server-url", "localhost:7070", "--password", utils.Password, "--network", "regtest", "--explorer", "http://chopsticks:3000")
if err != nil {
fmt.Printf("error initializing ark config: %s", err)
os.Exit(1)
}
code := m.Run()
_, err = utils.RunCommand("docker", "compose", "-f", composePath, "down")
if err != nil {
fmt.Printf("error stopping docker-compose: %s", err)
os.Exit(1)
}
os.Exit(code)
}
func TestSendOffchain(t *testing.T) {
var receive utils.ArkReceive
receiveStr, err := runClarkCommand("receive")
require.NoError(t, err)
err = json.Unmarshal([]byte(receiveStr), &receive)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", receive.Boarding)
require.NoError(t, err)
time.Sleep(5 * time.Second)
_, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
_, err = runClarkCommand("send", "--amount", "10000", "--to", receive.Offchain, "--password", utils.Password)
require.NoError(t, err)
var balance utils.ArkBalance
balanceStr, err := runClarkCommand("balance")
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.NotZero(t, balance.Offchain.Total)
_, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
balanceStr, err = runClarkCommand("balance")
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.NotZero(t, balance.Offchain.Total)
}
func TestUnilateralExit(t *testing.T) {
var receive utils.ArkReceive
receiveStr, err := runClarkCommand("receive")
require.NoError(t, err)
err = json.Unmarshal([]byte(receiveStr), &receive)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", receive.Boarding)
require.NoError(t, err)
time.Sleep(5 * time.Second)
_, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
var balance utils.ArkBalance
balanceStr, err := runClarkCommand("balance")
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.NotZero(t, balance.Offchain.Total)
_, err = runClarkCommand("redeem", "--force", "--password", utils.Password)
require.NoError(t, err)
err = utils.GenerateBlock()
require.NoError(t, err)
time.Sleep(5 * time.Second)
balanceStr, err = runClarkCommand("balance")
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.Zero(t, balance.Offchain.Total)
require.Greater(t, len(balance.Onchain.Locked), 0)
lockedBalance := balance.Onchain.Locked[0].Amount
require.NotZero(t, lockedBalance)
}
func TestCollaborativeExit(t *testing.T) {
var receive utils.ArkReceive
receiveStr, err := runClarkCommand("receive")
require.NoError(t, err)
err = json.Unmarshal([]byte(receiveStr), &receive)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", receive.Boarding)
require.NoError(t, err)
time.Sleep(5 * time.Second)
_, err = runClarkCommand("redeem", "--amount", "1000", "--address", redeemAddress, "--password", utils.Password)
require.NoError(t, err)
}
func TestReactToRedemptionOfRefreshedVtxos(t *testing.T) {
ctx := context.Background()
client, grpcClient := setupArkSDK(t)
defer grpcClient.Close()
_, boardingAddress, err := client.Receive(ctx)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", boardingAddress)
require.NoError(t, err)
time.Sleep(5 * time.Second)
_, err = client.Settle(ctx)
require.NoError(t, err)
time.Sleep(2 * time.Second)
_, err = client.Settle(ctx)
require.NoError(t, err)
_, spentVtxos, err := client.ListVtxos(ctx)
require.NoError(t, err)
require.NotEmpty(t, spentVtxos)
vtxo := spentVtxos[0]
round, err := grpcClient.GetRound(ctx, vtxo.RoundTxid)
require.NoError(t, err)
expl := explorer.NewExplorer("http://localhost:3000", common.BitcoinRegTest)
branch, err := redemption.NewCovenantlessRedeemBranch(expl, round.Tree, vtxo)
require.NoError(t, err)
txs, err := branch.RedeemPath()
require.NoError(t, err)
for _, tx := range txs {
_, err := expl.Broadcast(tx)
require.NoError(t, err)
}
// give time for the server to detect and process the fraud
time.Sleep(20 * time.Second)
balance, err := client.Balance(ctx, false)
require.NoError(t, err)
require.Empty(t, balance.OnchainBalance.LockedAmount)
}
func TestReactToRedemptionOfVtxosSpentOOR(t *testing.T) {
ctx := context.Background()
sdkClient, grpcClient := setupArkSDK(t)
defer grpcClient.Close()
offchainAddress, boardingAddress, err := sdkClient.Receive(ctx)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", boardingAddress)
require.NoError(t, err)
time.Sleep(5 * time.Second)
roundId, err := sdkClient.Settle(ctx)
require.NoError(t, err)
err = utils.GenerateBlock()
require.NoError(t, err)
_, err = sdkClient.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)})
require.NoError(t, err)
_, err = sdkClient.Settle(ctx)
require.NoError(t, err)
time.Sleep(5 * time.Second)
_, spentVtxos, err := sdkClient.ListVtxos(ctx)
require.NoError(t, err)
require.NotEmpty(t, spentVtxos)
var vtxo client.Vtxo
for _, v := range spentVtxos {
if v.RoundTxid == roundId {
vtxo = v
break
}
}
require.NotEmpty(t, vtxo)
round, err := grpcClient.GetRound(ctx, vtxo.RoundTxid)
require.NoError(t, err)
expl := explorer.NewExplorer("http://localhost:3000", common.BitcoinRegTest)
branch, err := redemption.NewCovenantlessRedeemBranch(expl, round.Tree, vtxo)
require.NoError(t, err)
txs, err := branch.RedeemPath()
require.NoError(t, err)
for _, tx := range txs {
_, err := expl.Broadcast(tx)
require.NoError(t, err)
}
// give time for the server to detect and process the fraud
time.Sleep(50 * time.Second)
balance, err := sdkClient.Balance(ctx, false)
require.NoError(t, err)
require.Empty(t, balance.OnchainBalance.LockedAmount)
}
func TestChainOutOfRoundTransactions(t *testing.T) {
var receive utils.ArkReceive
receiveStr, err := runClarkCommand("receive")
require.NoError(t, err)
err = json.Unmarshal([]byte(receiveStr), &receive)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", receive.Boarding)
require.NoError(t, err)
time.Sleep(5 * time.Second)
_, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
_, err = runClarkCommand("send", "--amount", "10000", "--to", receive.Offchain, "--password", utils.Password)
require.NoError(t, err)
var balance utils.ArkBalance
balanceStr, err := runClarkCommand("balance")
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.NotZero(t, balance.Offchain.Total)
_, err = runClarkCommand("send", "--amount", "10000", "--to", receive.Offchain, "--password", utils.Password)
require.NoError(t, err)
balanceStr, err = runClarkCommand("balance")
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.NotZero(t, balance.Offchain.Total)
}
func TestAliceSendsSeveralTimesToBob(t *testing.T) {
ctx := context.Background()
alice, grpcAlice := setupArkSDK(t)
defer grpcAlice.Close()
bob, grpcBob := setupArkSDK(t)
defer grpcBob.Close()
_, boardingAddress, err := alice.Receive(ctx)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", boardingAddress)
require.NoError(t, err)
time.Sleep(5 * time.Second)
_, err = alice.Settle(ctx)
require.NoError(t, err)
time.Sleep(5 * time.Second)
bobAddress, _, err := bob.Receive(ctx)
require.NoError(t, err)
_, err = alice.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 1000)})
require.NoError(t, err)
time.Sleep(2 * time.Second)
bobVtxos, _, err := bob.ListVtxos(ctx)
require.NoError(t, err)
require.Len(t, bobVtxos, 1)
_, err = alice.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 10000)})
require.NoError(t, err)
time.Sleep(2 * time.Second)
bobVtxos, _, err = bob.ListVtxos(ctx)
require.NoError(t, err)
require.Len(t, bobVtxos, 2)
_, err = alice.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 10000)})
require.NoError(t, err)
time.Sleep(2 * time.Second)
bobVtxos, _, err = bob.ListVtxos(ctx)
require.NoError(t, err)
require.Len(t, bobVtxos, 3)
_, err = alice.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 10000)})
require.NoError(t, err)
time.Sleep(2 * time.Second)
bobVtxos, _, err = bob.ListVtxos(ctx)
require.NoError(t, err)
require.Len(t, bobVtxos, 4)
// bobVtxos should be unique
uniqueVtxos := make(map[string]struct{})
for _, v := range bobVtxos {
uniqueVtxos[fmt.Sprintf("%s:%d", v.Txid, v.VOut)] = struct{}{}
}
require.Len(t, uniqueVtxos, 4)
require.NoError(t, err)
}
func TestRedeemNotes(t *testing.T) {
note := generateNote(t, 10_000)
balanceBeforeStr, err := runClarkCommand("balance")
require.NoError(t, err)
var balanceBefore utils.ArkBalance
require.NoError(t, json.Unmarshal([]byte(balanceBeforeStr), &balanceBefore))
_, err = runClarkCommand("redeem-notes", "--notes", note)
require.NoError(t, err)
time.Sleep(2 * time.Second)
balanceAfterStr, err := runClarkCommand("balance")
require.NoError(t, err)
var balanceAfter utils.ArkBalance
require.NoError(t, json.Unmarshal([]byte(balanceAfterStr), &balanceAfter))
require.Greater(t, balanceAfter.Offchain.Total, balanceBefore.Offchain.Total)
_, err = runClarkCommand("redeem-notes", "--notes", note)
require.Error(t, err)
}
func TestSendToCLTVMultisigClosure(t *testing.T) {
ctx := context.Background()
alice, grpcAlice := setupArkSDK(t)
defer grpcAlice.Close()
bobPrivKey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err)
configStore, err := inmemorystoreconfig.NewConfigStore()
require.NoError(t, err)
walletStore, err := inmemorystore.NewWalletStore()
require.NoError(t, err)
bobWallet, err := singlekeywallet.NewBitcoinWallet(
configStore,
walletStore,
)
require.NoError(t, err)
_, err = bobWallet.Create(ctx, utils.Password, hex.EncodeToString(bobPrivKey.Serialize()))
require.NoError(t, err)
_, err = bobWallet.Unlock(ctx, utils.Password)
require.NoError(t, err)
bobPubKey := bobPrivKey.PubKey()
// Fund Alice's account
offchainAddr, boardingAddress, err := alice.Receive(ctx)
require.NoError(t, err)
aliceAddr, err := common.DecodeAddress(offchainAddr)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", boardingAddress)
require.NoError(t, err)
time.Sleep(5 * time.Second)
_, err = alice.Settle(ctx)
require.NoError(t, err)
time.Sleep(5 * time.Second)
const cltvBlocks = 10
const sendAmount = 10000
currentHeight, err := utils.GetBlockHeight(false)
require.NoError(t, err)
vtxoScript := bitcointree.TapscriptsVtxoScript{
TapscriptsVtxoScript: tree.TapscriptsVtxoScript{
Closures: []tree.Closure{
&tree.CLTVMultisigClosure{
Locktime: common.Locktime{Type: common.LocktimeTypeBlock, Value: currentHeight + cltvBlocks},
MultisigClosure: tree.MultisigClosure{
PubKeys: []*secp256k1.PublicKey{bobPubKey},
},
},
},
},
}
vtxoTapKey, vtxoTapTree, err := vtxoScript.TapTree()
require.NoError(t, err)
closure := vtxoScript.ForfeitClosures()[0]
bobAddr := common.Address{
HRP: "tark",
VtxoTapKey: vtxoTapKey,
Server: aliceAddr.Server,
}
script, err := closure.Script()
require.NoError(t, err)
merkleProof, err := vtxoTapTree.GetTaprootMerkleProof(txscript.NewBaseTapLeaf(script).TapHash())
require.NoError(t, err)
ctrlBlock, err := txscript.ParseControlBlock(merkleProof.ControlBlock)
require.NoError(t, err)
tapscript := &waddrmgr.Tapscript{
ControlBlock: ctrlBlock,
RevealedScript: merkleProof.Script,
}
bobAddrStr, err := bobAddr.Encode()
require.NoError(t, err)
redeemTx, err := alice.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddrStr, sendAmount)})
require.NoError(t, err)
redeemPtx, err := psbt.NewFromRawBytes(strings.NewReader(redeemTx), true)
require.NoError(t, err)
var bobOutput *wire.TxOut
var bobOutputIndex uint32
for i, out := range redeemPtx.UnsignedTx.TxOut {
if bytes.Equal(out.PkScript[2:], schnorr.SerializePubKey(bobAddr.VtxoTapKey)) {
bobOutput = out
bobOutputIndex = uint32(i)
break
}
}
require.NotNil(t, bobOutput)
time.Sleep(2 * time.Second)
alicePkScript, err := common.P2TRScript(aliceAddr.VtxoTapKey)
require.NoError(t, err)
ptx, err := bitcointree.BuildRedeemTx(
[]common.VtxoInput{
{
Outpoint: &wire.OutPoint{
Hash: redeemPtx.UnsignedTx.TxHash(),
Index: bobOutputIndex,
},
Tapscript: tapscript,
WitnessSize: closure.WitnessSize(),
Amount: bobOutput.Value,
},
},
[]*wire.TxOut{
{
Value: bobOutput.Value - 500,
PkScript: alicePkScript,
},
},
)
require.NoError(t, err)
signedTx, err := bobWallet.SignTransaction(
ctx,
explorer.NewExplorer("http://localhost:3000", common.BitcoinRegTest),
ptx,
)
require.NoError(t, err)
// should fail because the tx is not yet valid
_, err = grpcAlice.SubmitRedeemTx(ctx, signedTx)
require.Error(t, err)
// Generate blocks to pass the timelock
for i := 0; i < cltvBlocks+1; i++ {
err = utils.GenerateBlock()
require.NoError(t, err)
time.Sleep(1 * time.Second)
}
_, err = grpcAlice.SubmitRedeemTx(ctx, signedTx)
require.NoError(t, err)
}
func TestSweep(t *testing.T) {
var receive utils.ArkReceive
receiveStr, err := runClarkCommand("receive")
require.NoError(t, err)
err = json.Unmarshal([]byte(receiveStr), &receive)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", receive.Boarding)
require.NoError(t, err)
time.Sleep(5 * time.Second)
_, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
secretKey, pubkey, npub, err := utils.GetNostrKeys()
require.NoError(t, err)
_, err = runClarkCommand("register-nostr", "--profile", npub, "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
// connect to relay
relay, err := nostr.RelayConnect(context.Background(), "ws://localhost:10547")
require.NoError(t, err)
defer relay.Close()
sub, err := relay.Subscribe(context.Background(), nostr.Filters{
{
Kinds: []int{nostr.KindEncryptedDirectMessage},
},
{
Tags: nostr.TagMap{
"p": []string{pubkey},
},
},
})
require.NoError(t, err)
defer sub.Close()
_, err = utils.RunCommand("nigiri", "rpc", "generatetoaddress", "100", "bcrt1qe8eelqalnch946nzhefd5ajhgl2afjw5aegc59")
require.NoError(t, err)
time.Sleep(40 * time.Second)
var balance utils.ArkBalance
balanceStr, err := runClarkCommand("balance")
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.Zero(t, balance.Offchain.Total) // all funds should be swept
var note string
for event := range sub.Events {
sharedSecret, err := nip04.ComputeSharedSecret(event.PubKey, secretKey)
require.NoError(t, err)
// Decrypt the NIP04 message
decrypted, err := nip04.Decrypt(event.Content, sharedSecret)
require.NoError(t, err)
note = decrypted
break // Exit after processing the first message
}
require.NotEmpty(t, note)
// redeem the note
_, err = runClarkCommand("redeem-notes", "--notes", note)
require.NoError(t, err)
}
func runClarkCommand(arg ...string) (string, error) {
args := append([]string{"ark"}, arg...)
return utils.RunDockerExec("clarkd", args...)
}
func setupServerWallet() error {
adminHttpClient := &http.Client{
Timeout: 15 * time.Second,
}
req, err := http.NewRequest("GET", "http://localhost:7070/v1/admin/wallet/seed", nil)
if err != nil {
return fmt.Errorf("failed to prepare generate seed request: %s", err)
}
req.Header.Set("Authorization", "Basic YWRtaW46YWRtaW4=")
seedResp, err := adminHttpClient.Do(req)
if err != nil {
return fmt.Errorf("failed to generate seed: %s", err)
}
var seed struct {
Seed string `json:"seed"`
}
if err := json.NewDecoder(seedResp.Body).Decode(&seed); err != nil {
return fmt.Errorf("failed to parse response: %s", err)
}
reqBody := bytes.NewReader([]byte(fmt.Sprintf(`{"seed": "%s", "password": "%s"}`, seed.Seed, utils.Password)))
req, err = http.NewRequest("POST", "http://localhost:7070/v1/admin/wallet/create", reqBody)
if err != nil {
return fmt.Errorf("failed to prepare wallet create request: %s", err)
}
req.Header.Set("Authorization", "Basic YWRtaW46YWRtaW4=")
req.Header.Set("Content-Type", "application/json")
if _, err := adminHttpClient.Do(req); err != nil {
return fmt.Errorf("failed to create wallet: %s", err)
}
reqBody = bytes.NewReader([]byte(fmt.Sprintf(`{"password": "%s"}`, utils.Password)))
req, err = http.NewRequest("POST", "http://localhost:7070/v1/admin/wallet/unlock", reqBody)
if err != nil {
return fmt.Errorf("failed to prepare wallet unlock request: %s", err)
}
req.Header.Set("Authorization", "Basic YWRtaW46YWRtaW4=")
req.Header.Set("Content-Type", "application/json")
if _, err := adminHttpClient.Do(req); err != nil {
return fmt.Errorf("failed to unlock wallet: %s", err)
}
var status struct {
Initialized bool `json:"initialized"`
Unlocked bool `json:"unlocked"`
Synced bool `json:"synced"`
}
for {
time.Sleep(time.Second)
req, err := http.NewRequest("GET", "http://localhost:7070/v1/admin/wallet/status", nil)
if err != nil {
return fmt.Errorf("failed to prepare status request: %s", err)
}
resp, err := adminHttpClient.Do(req)
if err != nil {
return fmt.Errorf("failed to get status: %s", err)
}
if err := json.NewDecoder(resp.Body).Decode(&status); err != nil {
return fmt.Errorf("failed to parse status response: %s", err)
}
if status.Initialized && status.Unlocked && status.Synced {
break
}
}
var addr struct {
Address string `json:"address"`
}
for addr.Address == "" {
time.Sleep(time.Second)
req, err = http.NewRequest("GET", "http://localhost:7070/v1/admin/wallet/address", nil)
if err != nil {
return fmt.Errorf("failed to prepare new address request: %s", err)
}
req.Header.Set("Authorization", "Basic YWRtaW46YWRtaW4=")
resp, err := adminHttpClient.Do(req)
if err != nil {
return fmt.Errorf("failed to get new address: %s", err)
}
if err := json.NewDecoder(resp.Body).Decode(&addr); err != nil {
return fmt.Errorf("failed to parse response: %s", err)
}
}
const numberOfFaucet = 15 // must cover the liquidity needed for all tests
for i := 0; i < numberOfFaucet; i++ {
_, err = utils.RunCommand("nigiri", "faucet", addr.Address)
if err != nil {
return fmt.Errorf("failed to fund wallet: %s", err)
}
}
time.Sleep(5 * time.Second)
return nil
}
func setupArkSDK(t *testing.T) (arksdk.ArkClient, client.TransportClient) {
appDataStore, err := store.NewStore(store.Config{
ConfigStoreType: types.FileStore,
AppDataStoreType: types.KVStore,
BaseDir: t.TempDir(),
})
require.NoError(t, err)
client, err := arksdk.NewCovenantlessClient(appDataStore)
require.NoError(t, err)
err = client.Init(context.Background(), arksdk.InitArgs{
WalletType: arksdk.SingleKeyWallet,
ClientType: arksdk.GrpcClient,
ServerUrl: "localhost:7070",
Password: utils.Password,
})
require.NoError(t, err)
err = client.Unlock(context.Background(), utils.Password)
require.NoError(t, err)
grpcClient, err := grpcclient.NewClient("localhost:7070")
require.NoError(t, err)
return client, grpcClient
}
func generateNote(t *testing.T, amount uint32) string {
adminHttpClient := &http.Client{
Timeout: 15 * time.Second,
}
reqBody := bytes.NewReader([]byte(fmt.Sprintf(`{"amount": "%d"}`, amount)))
req, err := http.NewRequest("POST", "http://localhost:7070/v1/admin/note", reqBody)
if err != nil {
t.Fatalf("failed to prepare note request: %s", err)
}
req.Header.Set("Authorization", "Basic YWRtaW46YWRtaW4=")
req.Header.Set("Content-Type", "application/json")
resp, err := adminHttpClient.Do(req)
if err != nil {
t.Fatalf("failed to create note: %s", err)
}
var noteResp struct {
Notes []string `json:"notes"`
}
if err := json.NewDecoder(resp.Body).Decode(&noteResp); err != nil {
t.Fatalf("failed to parse response: %s", err)
}
return noteResp.Notes[0]
}