nockchain-grpc/wallet/service_test.go

477 lines
18 KiB
Go
Raw Normal View History

2025-10-11 13:04:57 +07:00
package wallet_test
import (
"context"
2025-10-16 12:31:21 +07:00
"fmt"
2025-10-11 13:04:57 +07:00
"math/big"
"slices"
"testing"
"github.com/btcsuite/btcd/btcutil/base58"
"github.com/cosmos/go-bip39"
"github.com/phamminh0811/private-grpc/crypto"
"github.com/phamminh0811/private-grpc/nockchain"
"github.com/phamminh0811/private-grpc/wallet"
"github.com/stretchr/testify/assert"
)
// The entropy, salt and result is taken from "nockchain-wallet keygen" command
func TestKeyGen(t *testing.T) {
entropyBigInt, isOk := new(big.Int).SetString("37133536588676344913489312523941366110857274548479981512263368615793750653450", 10)
2025-10-11 13:04:57 +07:00
assert.True(t, isOk)
entropy := entropyBigInt.Bytes()
assert.Len(t, entropy, 32)
saltBigInt, isOk := new(big.Int).SetString("251632902249061493058993135304695174381", 10)
2025-10-11 13:04:57 +07:00
assert.True(t, isOk)
salt := saltBigInt.Bytes()
assert.Len(t, salt, 16)
argonBytes := crypto.DeriveKey(0, entropy[:], salt[:], nil, nil, 6, 786432, 4, 32)
slices.Reverse(argonBytes)
mnemonic, err := bip39.NewMnemonic(argonBytes)
assert.NoError(t, err)
assert.Equal(t, mnemonic, "include lounge salad chicken trumpet embrace grace mercy pulp submit alter weapon plastic welcome funny female palm satoshi open file knock sun fade match")
2025-10-11 13:04:57 +07:00
masterKey, err := crypto.MasterKeyFromSeed(mnemonic)
assert.NoError(t, err)
assert.Equal(t,
base58.Encode(masterKey.PublicKey),
"34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6",
2025-10-11 13:04:57 +07:00
)
assert.Equal(t,
base58.Encode(masterKey.PrivateKey),
"3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg",
2025-10-11 13:04:57 +07:00
)
assert.Equal(t,
base58.Encode(masterKey.ChainCode),
"2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8",
2025-10-11 13:04:57 +07:00
)
// assert import priv/pubkey
privBytes := append([]byte{0x00}, masterKey.PrivateKey...)
importPrivKey := crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.KeyType_PRIVATE)
assert.Len(t, importPrivKey, 83)
importPubKey := crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.KeyType_PUBLIC)
assert.Len(t, importPubKey, 147)
2025-10-11 13:04:57 +07:00
assert.Equal(t,
base58.Encode(importPrivKey),
"zprvLpf3WSvYWmHRd3jj5oR8UXr7bi88pGnfJXj1dM9RnwJwu1MLo6fZzcKauqpxL2W7dk2fmjYKAUzavbyaGpnvqY5QndcrUzsBrPQAHXNnhiXx",
2025-10-11 13:04:57 +07:00
)
assert.Equal(t,
base58.Encode(importPubKey),
"zpub2jgndknkQprVYB4X4mqREyn7ZTUE5zp9qkSugdpiqhC5NSeNBceafoz6jGSLEpzJhaLryvY8MF6TokwZN627UXhsg5zd2U12woEL82UtZaLHRL8PZi8YiQnE41BiNJwkfpWjzAbq8mwbAHV3nXUEFgJW2BKCz4GmfoMCkCUDhJppYh5KFsMUkN41DzsfFZHuFrzo",
2025-10-11 13:04:57 +07:00
)
}
func TestImportKey(t *testing.T) {
type Input struct {
req *nockchain.ImportKeysRequest
expectResp *nockchain.ImportKeysResponse
isErr bool
errStr string
}
correctImportPrivKey := base58.Decode("zprvLpf3WSvYWmHRd3jj5oR8UXr7bi88pGnfJXj1dM9RnwJwu1MLo6fZzcKauqpxL2W7dk2fmjYKAUzavbyaGpnvqY5QndcrUzsBrPQAHXNnhiXx")
invalidImportPrivKeyPrefix := make([]byte, 83)
2025-10-11 13:04:57 +07:00
copy(invalidImportPrivKeyPrefix[:], correctImportPrivKey)
invalidImportPrivKeyPrefix[46] = 0x01
2025-10-11 13:04:57 +07:00
invalidImportPrivKeyChecksum := make([]byte, 83)
2025-10-11 13:04:57 +07:00
copy(invalidImportPrivKeyChecksum[:], correctImportPrivKey)
copy(invalidImportPrivKeyChecksum[79:], []byte{1, 2, 3, 4})
2025-10-11 13:04:57 +07:00
correctImportPubkey := base58.Decode("zpub2jgndknkQprVYB4X4mqREyn7ZTUE5zp9qkSugdpiqhC5NSeNBceafoz6jGSLEpzJhaLryvY8MF6TokwZN627UXhsg5zd2U12woEL82UtZaLHRL8PZi8YiQnE41BiNJwkfpWjzAbq8mwbAHV3nXUEFgJW2BKCz4GmfoMCkCUDhJppYh5KFsMUkN41DzsfFZHuFrzo")
invalidImportPubkeyChecksum := make([]byte, 147)
2025-10-11 13:04:57 +07:00
copy(invalidImportPubkeyChecksum[:], correctImportPubkey)
copy(invalidImportPubkeyChecksum[143:], []byte{1, 2, 3, 4})
2025-10-11 13:04:57 +07:00
response := &nockchain.ImportKeysResponse{
PublicKey: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6",
PrivateKey: "3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg",
ChainCode: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8",
2025-10-11 13:04:57 +07:00
ImportPrivateKey: base58.Encode(correctImportPrivKey),
ImportPublicKey: base58.Encode(correctImportPubkey),
}
responseReadOnly := &nockchain.ImportKeysResponse{
PublicKey: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6",
2025-10-11 13:04:57 +07:00
PrivateKey: "",
ChainCode: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8",
2025-10-11 13:04:57 +07:00
ImportPrivateKey: "",
ImportPublicKey: base58.Encode(correctImportPubkey),
}
inputs := []Input{
// case invalid type
{
req: &nockchain.ImportKeysRequest{
Key: "",
ImportType: nockchain.ImportType_UNDEFINED,
},
expectResp: nil,
isErr: true,
errStr: "invalid import type",
},
// case invalid extended key
{
req: &nockchain.ImportKeysRequest{
Key: "some wrong string",
ImportType: nockchain.ImportType_EXTENDED_KEY,
},
expectResp: nil,
isErr: true,
errStr: "invalid extended key",
},
{
req: &nockchain.ImportKeysRequest{
Key: "zprv wrong priv import key length",
ImportType: nockchain.ImportType_EXTENDED_KEY,
},
expectResp: nil,
isErr: true,
errStr: "invalid extended private key length",
},
{
req: &nockchain.ImportKeysRequest{
Key: base58.Encode(invalidImportPrivKeyPrefix),
ImportType: nockchain.ImportType_EXTENDED_KEY,
},
expectResp: nil,
isErr: true,
errStr: "invalid private key prefix at byte 46",
2025-10-11 13:04:57 +07:00
},
{
req: &nockchain.ImportKeysRequest{
Key: base58.Encode(invalidImportPrivKeyChecksum),
ImportType: nockchain.ImportType_EXTENDED_KEY,
},
expectResp: nil,
isErr: true,
errStr: "invalid checksum",
},
// case success import priv key
{
req: &nockchain.ImportKeysRequest{
Key: base58.Encode(correctImportPrivKey),
ImportType: nockchain.ImportType_EXTENDED_KEY,
},
expectResp: response,
isErr: false,
errStr: "",
},
// case invalid import pub key
{
req: &nockchain.ImportKeysRequest{
Key: "zpub wrong public import key length",
ImportType: nockchain.ImportType_EXTENDED_KEY,
},
expectResp: nil,
isErr: true,
errStr: "invalid extended public key length",
},
{
req: &nockchain.ImportKeysRequest{
Key: base58.Encode(invalidImportPubkeyChecksum),
ImportType: nockchain.ImportType_EXTENDED_KEY,
},
expectResp: nil,
isErr: true,
errStr: "invalid checksum",
},
// case success import pub key
{
req: &nockchain.ImportKeysRequest{
Key: base58.Encode(correctImportPubkey),
ImportType: nockchain.ImportType_EXTENDED_KEY,
},
expectResp: responseReadOnly,
isErr: false,
errStr: "",
},
// case missing chaincode when import master privkey
{
req: &nockchain.ImportKeysRequest{
Key: "3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg",
2025-10-11 13:04:57 +07:00
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
},
expectResp: nil,
isErr: true,
errStr: "master key must be in [chain_code],[key] format",
},
// case invalid length
{
req: &nockchain.ImportKeysRequest{
Key: "abcdxyz,3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg",
2025-10-11 13:04:57 +07:00
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
},
expectResp: nil,
isErr: true,
errStr: "invalid chain code length",
},
{
req: &nockchain.ImportKeysRequest{
Key: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8,abcdxyz",
2025-10-11 13:04:57 +07:00
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
},
expectResp: nil,
isErr: true,
errStr: "invalid priv key length",
},
// case success import master privkey
{
req: &nockchain.ImportKeysRequest{
Key: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8,3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg",
2025-10-11 13:04:57 +07:00
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
},
expectResp: response,
isErr: false,
errStr: "",
},
// case success import seed
{
req: &nockchain.ImportKeysRequest{
Key: "include lounge salad chicken trumpet embrace grace mercy pulp submit alter weapon plastic welcome funny female palm satoshi open file knock sun fade match",
2025-10-11 13:04:57 +07:00
ImportType: nockchain.ImportType_SEEDPHRASE,
},
expectResp: response,
isErr: false,
errStr: "",
},
// case sucess import pubkey
{
req: &nockchain.ImportKeysRequest{
Key: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6",
2025-10-11 13:04:57 +07:00
ImportType: nockchain.ImportType_WATCH_ONLY,
},
expectResp: &nockchain.ImportKeysResponse{
PublicKey: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6",
2025-10-11 13:04:57 +07:00
},
isErr: false,
errStr: "",
},
}
for _, input := range inputs {
nc, err := wallet.NewNockchainClient("nockchain-api.zorp.io:443")
assert.NoError(t, err)
handler := wallet.NewGprcHandler(*nc)
resp, err := handler.ImportKeys(context.Background(), input.req)
if input.isErr {
assert.ErrorContains(t, err, input.errStr)
} else {
assert.NoError(t, err)
assert.Equal(t, resp, input.expectResp)
}
}
}
// This test should be run with timeout 120s
func TestScan(t *testing.T) {
// some random seed we take to get empty notes
seed1 := "foster chicken claw fade income frown junior abandon price lesson mango wrap dry clay loyal camera caught during property useless puppy royal soccer arm"
seed2 := "brass vacuum stairs hurt brisk govern describe enforce fly exact rescue capable belt flavor lottery sauce easy frame orange legal injury border obey novel"
masterKey1, err := crypto.MasterKeyFromSeed(seed1)
assert.NoError(t, err)
nc, err := wallet.NewNockchainClient("nockchain-api.zorp.io:443")
assert.NoError(t, err)
masterKey1Scan, err := nc.WalletGetBalance(base58.Encode(masterKey1.PublicKey))
assert.NoError(t, err)
assert.Empty(t, masterKey1Scan.Notes)
masterKey2, err := crypto.MasterKeyFromSeed(seed2)
assert.NoError(t, err)
masterKey2Scan, err := nc.WalletGetBalance(base58.Encode(masterKey2.PublicKey))
assert.NoError(t, err)
assert.Len(t, masterKey2Scan.Notes, 1)
assert.Equal(t, masterKey2Scan.Notes[0].Note.Assets.Value, uint64(100000))
assert.Equal(t, masterKey2Scan.Notes[0].Note.OriginPage.Value, uint64(35054))
}
// This test should be run with timeout 120s
func TestFullFlow(t *testing.T) {
mnemonic1 := "rail nurse smile angle uphold gun kitten spoon quick frozen trigger cable decorate episode blame tray off bag arena taxi approve breeze job letter"
masterKey1, err := crypto.MasterKeyFromSeed(mnemonic1)
assert.NoError(t, err)
mnemonic2 := "brass vacuum stairs hurt brisk govern describe enforce fly exact rescue capable belt flavor lottery sauce easy frame orange legal injury border obey novel"
masterKey2, err := crypto.MasterKeyFromSeed(mnemonic2)
assert.NoError(t, err)
inputName := "[4taoqkpysafnp64WBQyzHDKVrqkMeNrdAiVSbWdzZmj7yQYZgQtCq4W 9cjUFbdtaFHeXNWCAKjsTphBchHmCoUU6a1aDbJAFz9qHqeG8osh4wF]"
nc, err := wallet.NewNockchainClient("nockchain-api.zorp.io:443")
assert.NoError(t, err)
masterKeyScan, err := nc.WalletGetBalance(base58.Encode(masterKey1.PublicKey))
assert.NoError(t, err)
var note *nockchain.NockchainNote
for _, balanceEntry := range masterKeyScan.Notes {
firstName := crypto.Tip5HashToBase58([5]uint64{
balanceEntry.Name.First.Belt_1.Value,
balanceEntry.Name.First.Belt_2.Value,
balanceEntry.Name.First.Belt_3.Value,
balanceEntry.Name.First.Belt_4.Value,
balanceEntry.Name.First.Belt_5.Value,
})
lastName := crypto.Tip5HashToBase58([5]uint64{
balanceEntry.Name.Last.Belt_1.Value,
balanceEntry.Name.Last.Belt_2.Value,
balanceEntry.Name.Last.Belt_3.Value,
balanceEntry.Name.Last.Belt_4.Value,
balanceEntry.Name.Last.Belt_5.Value,
})
nname := "[" + firstName + " " + lastName + "]"
if nname == inputName {
nnote := wallet.ParseBalanceEntry(balanceEntry)
note = &nnote
break
}
}
assert.NotNil(t, note)
parentHash, err := wallet.HashNote(note)
assert.NoError(t, err)
gift := uint64(100000)
seed1 := &nockchain.NockchainSeed{
OutputSource: nil,
Recipient: &nockchain.NockchainLock{
KeysRequired: 1,
Pubkeys: []string{base58.Encode(masterKey2.PublicKey)},
},
TimelockIntent: nil,
Gift: gift,
ParentHash: crypto.Tip5HashToBase58(parentHash),
}
gift = note.Asset - gift - 100
seed2 := &nockchain.NockchainSeed{
OutputSource: nil,
Recipient: &nockchain.NockchainLock{
KeysRequired: 1,
Pubkeys: []string{base58.Encode(masterKey1.PublicKey)},
},
TimelockIntent: nil,
Gift: gift,
ParentHash: crypto.Tip5HashToBase58(parentHash),
}
spend := nockchain.NockchainSpend{
Signatures: nil,
Seeds: []*nockchain.NockchainSeed{
seed1, seed2,
},
Fee: 100,
}
msg, err := wallet.HashMsg(&spend)
assert.NoError(t, err)
chalT8, sigT8, err := wallet.ComputeSig(*masterKey1, msg)
assert.NoError(t, err)
spend.Signatures = []*nockchain.NockchainSignature{
{
Pubkey: base58.Encode(masterKey1.PublicKey),
Chal: chalT8[:],
Sig: sigT8[:],
},
}
input := nockchain.NockchainInput{
Name: &nockchain.NockchainName{
First: "4taoqkpysafnp64WBQyzHDKVrqkMeNrdAiVSbWdzZmj7yQYZgQtCq4W",
Last: "9cjUFbdtaFHeXNWCAKjsTphBchHmCoUU6a1aDbJAFz9qHqeG8osh4wF",
},
Note: note,
Spend: &spend,
}
id, err := wallet.ComputeTxId([]*nockchain.NockchainInput{&input}, &nockchain.TimelockRange{
Min: nil,
Max: nil,
}, 100)
assert.NoError(t, err)
rawTx := nockchain.RawTx{
TxId: crypto.Tip5HashToBase58(id),
Inputs: []*nockchain.NockchainInput{&input},
TimelockRange: &nockchain.TimelockRange{
Min: nil,
Max: nil,
},
TotalFees: 100,
}
resp, err := nc.WalletSendTransaction(&rawTx)
assert.NoError(t, err)
assert.Equal(t, resp.Result, &nockchain.WalletSendTransactionResponse_Ack{
Ack: &nockchain.Acknowledged{},
})
txAcceptedResp, err := nc.TxAccepted(rawTx.TxId)
assert.NoError(t, err)
assert.Equal(t, txAcceptedResp.Result, &nockchain.TransactionAcceptedResponse_Accepted{
Accepted: true,
})
}
2025-10-16 12:31:21 +07:00
// This test should be run with timeout 300s
func TestCreateTx(t *testing.T) {
2025-10-16 16:15:48 +07:00
seed1 := "brass vacuum stairs hurt brisk govern describe enforce fly exact rescue capable belt flavor lottery sauce easy frame orange legal injury border obey novel"
2025-10-16 12:31:21 +07:00
masterKey1, err := crypto.MasterKeyFromSeed(seed1)
assert.NoError(t, err)
2025-10-16 16:15:48 +07:00
fmt.Println("masterKey1: ", base58.Encode(masterKey1.PublicKey))
2025-10-16 12:31:21 +07:00
2025-10-20 09:35:25 +07:00
// nockchain-wallet create-tx --names '[5bcr83LnCYyHqQh4ExK6metaf1cs7JYDqMYc92Awqhwc6VEpZJL9wj9 AmSjC3SDNtb7ZrUkTXc242BvGeimeL1nAV4CqV63HpLMryhom4L9W59],[5bcr83LnCYyHqQh4ExK6metaf1cs7JYDqMYc92Awqhwc6VEpZJL9wj9 7oo4x3fuwcJ5DrbFY1yf715ctjtE6CqqPagJWT8d687Q6sgMVWc1SXz],[5bcr83LnCYyHqQh4ExK6metaf1cs7JYDqMYc92Awqhwc6VEpZJL9wj9 8BrF9XAKwzvFdy8p7KAB8VEvcswPADhs2WamhWSLUErYN9z8U8cynaA]' --recipients '37Ttw4d6Fq1WGis5qVz8SbeEtpqsbg2ihArBedi4ZeuhFFo8tbCNvwWNq9D8KFBc2qv7uzvPJmKmJg68aEHEh21FiXk9iJCmzyE3NqdSgpsPMCx7Q39yhUUrKkKvGnHUKzMe,37Ttw4d6Fq1WGis5qVz8SbeEtpqsbg2ihArBedi4ZeuhFFo8tbCNvwWNq9D8KFBc2qv7uzvPJmKmJg68aEHEh21FiXk9iJCmzyE3NqdSgpsPMCx7Q39yhUUrKkKvGnHUKzMe,37Ttw4d6Fq1WGis5qVz8SbeEtpqsbg2ihArBedi4ZeuhFFo8tbCNvwWNq9D8KFBc2qv7uzvPJmKmJg68aEHEh21FiXk9iJCmzyE3NqdSgpsPMCx7Q39yhUUrKkKvGnHUKzMe' --gifts '50,50,50' --fee 100
2025-10-16 16:15:48 +07:00
seed2 := "chimney endorse scan ramp cheap harvest mother ball winter way barrel foil tissue pupil answer worth right undo one chimney element grape image unlock"
2025-10-16 12:31:21 +07:00
masterKey2, err := crypto.MasterKeyFromSeed(seed2)
assert.NoError(t, err)
2025-10-16 16:15:48 +07:00
fmt.Println("masterKey2", base58.Encode(masterKey2.PublicKey))
2025-10-16 12:31:21 +07:00
nc, err := wallet.NewNockchainClient("nockchain-api.zorp.io:443")
assert.NoError(t, err)
handler := wallet.NewGprcHandler(*nc)
2025-10-16 16:15:48 +07:00
//inputs := "[CxiTK4HjqPRebUkoy6rH89ZcNGuH4goHkmKgmgCJxZEFS2C3qrDoh4y 91z5muQKgHZDcChdnCSTEtvuN8dbbXp5wzNs5xrCkce2YvSX1q6fu3d],[CxiTK4HjqPRebUkoy6rH89ZcNGuH4goHkmKgmgCJxZEFS2C3qrDoh4y 3a9VWigUM1TuS79yM4dAqkiUg6WJMUPGwVKJpQUHCLJcFZsCWM2q6pF],[CxiTK4HjqPRebUkoy6rH89ZcNGuH4goHkmKgmgCJxZEFS2C3qrDoh4y 9RkPvHMtuYtjV2qvB86u7zcnr26SdVcKzFnHgfhUSEG1W3FgKgLpbBm]"
//inputs := "[7PGL7VUMmBBFLxBta9hxJy2yyPNyWww6mXB4B7L78CEM7vu8E1gZY6B 76WR3YEgKaPXN9YshstDhHo82yg7ghJwoYGy9hVoihdSogAMpb9Jbyb],[7PGL7VUMmBBFLxBta9hxJy2yyPNyWww6mXB4B7L78CEM7vu8E1gZY6B 4h3Y3RPn5oAuHEmVtysyxNfJgUiy1DsjfRn4GzgZvM7asPPgJ6Jb7iy]"
//inputs := "[7PGL7VUMmBBFLxBta9hxJy2yyPNyWww6mXB4B7L78CEM7vu8E1gZY6B 76WR3YEgKaPXN9YshstDhHo82yg7ghJwoYGy9hVoihdSogAMpb9Jbyb],[7PGL7VUMmBBFLxBta9hxJy2yyPNyWww6mXB4B7L78CEM7vu8E1gZY6B 4h3Y3RPn5oAuHEmVtysyxNfJgUiy1DsjfRn4GzgZvM7asPPgJ6Jb7iy]"
2025-10-20 09:35:25 +07:00
inputs := "[5bcr83LnCYyHqQh4ExK6metaf1cs7JYDqMYc92Awqhwc6VEpZJL9wj9 AmSjC3SDNtb7ZrUkTXc242BvGeimeL1nAV4CqV63HpLMryhom4L9W59],[5bcr83LnCYyHqQh4ExK6metaf1cs7JYDqMYc92Awqhwc6VEpZJL9wj9 7oo4x3fuwcJ5DrbFY1yf715ctjtE6CqqPagJWT8d687Q6sgMVWc1SXz],[5bcr83LnCYyHqQh4ExK6metaf1cs7JYDqMYc92Awqhwc6VEpZJL9wj9 8BrF9XAKwzvFdy8p7KAB8VEvcswPADhs2WamhWSLUErYN9z8U8cynaA]"
2025-10-16 16:15:48 +07:00
//inputs := "[5bcr83LnCYyHqQh4ExK6metaf1cs7JYDqMYc92Awqhwc6VEpZJL9wj9 4d5zzcHuc9spJ1k3ozKYc4SyGm3APeN8wjsg5RkN27FaFsa7oMoJCDf],[5bcr83LnCYyHqQh4ExK6metaf1cs7JYDqMYc92Awqhwc6VEpZJL9wj9 5ZgFvf8qt8YCa2Nru2VZG2FF7P82Xcj4EsM2EQKiddQ8C43ciG91276]"
2025-10-20 09:35:25 +07:00
recipients := fmt.Sprintf("%s,%s,%s", base58.Encode(masterKey2.PublicKey), base58.Encode(masterKey2.PublicKey), base58.Encode(masterKey2.PublicKey))
2025-10-16 16:15:48 +07:00
//recipients := "37Ttw4d6Fq1WGis5qVz8SbeEtpqsbg2ihArBedi4ZeuhFFo8tbCNvwWNq9D8KFBc2qv7uzvPJmKmJg68aEHEh21FiXk9iJCmzyE3NqdSgpsPMCx7Q39yhUUrKkKvGnHUKzMe,37Ttw4d6Fq1WGis5qVz8SbeEtpqsbg2ihArBedi4ZeuhFFo8tbCNvwWNq9D8KFBc2qv7uzvPJmKmJg68aEHEh21FiXk9iJCmzyE3NqdSgpsPMCx7Q39yhUUrKkKvGnHUKzMe" //fmt.Sprintf("%s", base58.Encode(masterKey2.PublicKey))
2025-10-20 09:35:25 +07:00
gifts := "50,50,50"
2025-10-16 12:31:21 +07:00
fee := 100
req := &nockchain.CreateTxRequest{
Names: inputs,
Recipients: recipients,
Gifts: gifts,
Fee: uint64(fee),
IsMasterKey: true,
Key: base58.Encode(masterKey1.PrivateKey),
ChainCode: base58.Encode(masterKey1.ChainCode),
Index: 0,
Hardened: false,
TimelockIntent: nil,
}
res, err := handler.CreateTx(context.Background(), req)
assert.NoError(t, err)
// the result is taken from create-tx scripts
2025-10-22 08:44:24 +07:00
assert.Equal(t, res.RawTx.TxId, "A8vSeRde61B4sZccSnNPEnkQgTe15EssoFwyhQXbkhtk4UNm5hyGSid")
2025-10-16 12:31:21 +07:00
}