package wallet_test import ( "context" "fmt" "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 TestKeyGenV0(t *testing.T) { entropyBigInt, isOk := new(big.Int).SetString("37133536588676344913489312523941366110857274548479981512263368615793750653450", 10) assert.True(t, isOk) entropy := entropyBigInt.Bytes() assert.Len(t, entropy, 32) saltBigInt, isOk := new(big.Int).SetString("251632902249061493058993135304695174381", 10) 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") masterKey, err := crypto.MasterKeyFromSeed(mnemonic) assert.NoError(t, err) pkPoint, err := crypto.CheetaPointFromBytes(masterKey.PublicKey) assert.NoError(t, err) fmt.Println(crypto.Tip5HashToBase58(wallet.HashPubkey(pkPoint))) assert.Equal(t, base58.Encode(masterKey.PublicKey), "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6", ) assert.Equal(t, base58.Encode(masterKey.PrivateKey), "3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg", ) assert.Equal(t, base58.Encode(masterKey.ChainCode), "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8", ) // assert import priv/pubkey privBytes := append([]byte{0x00}, masterKey.PrivateKey...) importPrivKey := crypto.SerializeExtend(masterKey.ChainCode, privBytes, 0, crypto.KeyType_PRIVATE) assert.Len(t, importPrivKey, 83) importPubKey := crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, 0, crypto.KeyType_PUBLIC) assert.Len(t, importPubKey, 147) assert.Equal(t, base58.Encode(importPrivKey), "zprvLpf3WSvYWmHRd3jj5oR8UXr7bi88pGnfJXj1dM9RnwJwu1MLo6fZzcKauqpxL2W7dk2fmjYKAUzavbyaGpnvqY5QndcrUzsBrPQAHXNnhiXx", ) assert.Equal(t, base58.Encode(importPubKey), "zpub2jgndknkQprVYB4X4mqREyn7ZTUE5zp9qkSugdpiqhC5NSeNBceafoz6jGSLEpzJhaLryvY8MF6TokwZN627UXhsg5zd2U12woEL82UtZaLHRL8PZi8YiQnE41BiNJwkfpWjzAbq8mwbAHV3nXUEFgJW2BKCz4GmfoMCkCUDhJppYh5KFsMUkN41DzsfFZHuFrzo", ) } func TestKeyGenV1(t *testing.T) { entropyBigInt, isOk := new(big.Int).SetString("90486886833626125109893864286343887304289963452245361030406651820586141463911", 10) assert.True(t, isOk) entropy := entropyBigInt.Bytes() assert.Len(t, entropy, 32) saltBigInt, isOk := new(big.Int).SetString("200764822674693794811396222039518854030", 10) 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, "holiday wage fan orange humble erode thought across boring cereal brass believe window drill until dry dish basket mean all banana tribe antenna engage") masterKey, err := crypto.MasterKeyFromSeed(mnemonic) assert.NoError(t, err) pkPoint, err := crypto.CheetaPointFromBytes(masterKey.PublicKey) assert.NoError(t, err) assert.Equal(t, crypto.Tip5HashToBase58(wallet.HashPubkey(pkPoint)), "6DGFW4qAgCDx1AnP9fkhENwaPUeVWHEDB5WuJPiN1bZBjW2igMgU7N8", ) // assert import priv/pubkey privBytes := append([]byte{0x00}, masterKey.PrivateKey...) importPrivKey := crypto.SerializeExtend(masterKey.ChainCode, privBytes, 1, crypto.KeyType_PRIVATE) assert.Len(t, importPrivKey, 83) importPubKey := crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, 1, crypto.KeyType_PUBLIC) assert.Len(t, importPubKey, 147) assert.Equal(t, base58.Encode(importPrivKey), "zprvLxxkCBq3s5HYzjhcW8wh3FhHSt5YowSGApRdHsptZQrtcCdqibUo7qwqfa7qkazttRgcaRwy4YYwv9DLhmUNcMG9uAtsgaPkfwyiGkf5Bpib", ) assert.Equal(t, base58.Encode(importPubKey), "zpub2kRJ7D6VCvzVfDg5e6iXb4T2ea97QJKN6JzjTArrhgZzwHo6rmT8Z3mEp11T67fNvU8ZWLcTTFS17NLwvYs7ErmqCJQNrtjRgVcvZNeWBebpztihXzrhvHZumDiejPmLb6QQNhkPhhK3uyS6XBbaPybqGDdWAgHKvPHkKirVsSJWEmDJYEj8ePN4ufscv1DL2pTD", ) } func TestImportKey(t *testing.T) { type Input struct { req *nockchain.ImportKeysRequest expectResp *nockchain.ImportKeysResponse isErr bool errStr string } correctImportPrivKeyV0 := base58.Decode("zprvLpf3WSvYWmHRd3jj5oR8UXr7bi88pGnfJXj1dM9RnwJwu1MLo6fZzcKauqpxL2W7dk2fmjYKAUzavbyaGpnvqY5QndcrUzsBrPQAHXNnhiXx") correctImportPrivKeyV1 := base58.Decode("zprvLxxkCBq3s5HYziigQb3G4a7pyj7pG2f8MjogoqH4UoFDJy3GRBX5LF1tJAgsTYdgZAVfuM2DGrjqAqJX96SS8cTbVAaPV23834G1GqgfEEg7") invalidImportPrivKeyPrefix := make([]byte, 83) copy(invalidImportPrivKeyPrefix[:], correctImportPrivKeyV0) invalidImportPrivKeyPrefix[46] = 0x01 invalidImportPrivKeyChecksum := make([]byte, 83) copy(invalidImportPrivKeyChecksum[:], correctImportPrivKeyV0) copy(invalidImportPrivKeyChecksum[79:], []byte{1, 2, 3, 4}) correctImportPubkeyV0 := base58.Decode("zpub2jgndknkQprVYB4X4mqREyn7ZTUE5zp9qkSugdpiqhC5NSeNBceafoz6jGSLEpzJhaLryvY8MF6TokwZN627UXhsg5zd2U12woEL82UtZaLHRL8PZi8YiQnE41BiNJwkfpWjzAbq8mwbAHV3nXUEFgJW2BKCz4GmfoMCkCUDhJppYh5KFsMUkN41DzsfFZHuFrzo") correctImportPubkeyV1 := base58.Decode("zpub2kRJ7D6VCvzVfDb4F7drLsLVdViqUSiufhwkmZogDuTVLc5ird2DinQNHvu5WXgTcMGizEaqbnANjvg43bx3Lfz4fPVt27MPsiNBTuqqXgtvYbJZNwmXEa5vt87MGCRxKpAgo1zH6K7qy5Uokc94eUd47xEcmhij9YRm8twvU5y3ccCessJ8rdtEZahX6pv8RFBi") invalidImportPubkeyChecksum := make([]byte, 147) copy(invalidImportPubkeyChecksum[:], correctImportPubkeyV0) copy(invalidImportPubkeyChecksum[143:], []byte{1, 2, 3, 4}) responseV0 := &nockchain.ImportKeysResponse{ Address: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6", PrivateKey: "3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg", ChainCode: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8", ImportPrivateKey: base58.Encode(correctImportPrivKeyV0), ImportPublicKey: base58.Encode(correctImportPubkeyV0), Version: 0, } responseV1 := &nockchain.ImportKeysResponse{ Address: "BAmYAxgpVrJeFqqPnKAZHVZZdmVfzrgu7bBqcRBNca8HpxQofzUZG8Q", PrivateKey: "3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg", ChainCode: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8", ImportPrivateKey: base58.Encode(correctImportPrivKeyV1), ImportPublicKey: base58.Encode(correctImportPubkeyV1), Version: 1, } responseV0ReadOnly := &nockchain.ImportKeysResponse{ Address: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6", PrivateKey: "", ChainCode: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8", ImportPrivateKey: "", ImportPublicKey: base58.Encode(correctImportPubkeyV0), Version: 0, } responseV1ReadOnly := &nockchain.ImportKeysResponse{ Address: "BAmYAxgpVrJeFqqPnKAZHVZZdmVfzrgu7bBqcRBNca8HpxQofzUZG8Q", PrivateKey: "", ChainCode: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8", ImportPrivateKey: "", ImportPublicKey: base58.Encode(correctImportPubkeyV1), Version: 1, } inputs := []Input{ // case invalid type { req: &nockchain.ImportKeysRequest{ Key: "", ImportType: nockchain.ImportType_UNDEFINED, Version: 0, }, expectResp: nil, isErr: true, errStr: "invalid import type", }, // case invalid extended key { req: &nockchain.ImportKeysRequest{ Key: "some wrong string", ImportType: nockchain.ImportType_EXTENDED_KEY, Version: 0, }, expectResp: nil, isErr: true, errStr: "invalid extended key", }, { req: &nockchain.ImportKeysRequest{ Key: "zprv wrong priv import key length", ImportType: nockchain.ImportType_EXTENDED_KEY, Version: 0, }, expectResp: nil, isErr: true, errStr: "invalid extended private key length", }, { req: &nockchain.ImportKeysRequest{ Key: base58.Encode(invalidImportPrivKeyPrefix), ImportType: nockchain.ImportType_EXTENDED_KEY, Version: 0, }, expectResp: nil, isErr: true, errStr: "invalid private key prefix at byte 46", }, { req: &nockchain.ImportKeysRequest{ Key: base58.Encode(invalidImportPrivKeyChecksum), ImportType: nockchain.ImportType_EXTENDED_KEY, }, expectResp: nil, isErr: true, errStr: "invalid checksum", }, // case success import priv key v0 { req: &nockchain.ImportKeysRequest{ Key: base58.Encode(correctImportPrivKeyV0), ImportType: nockchain.ImportType_EXTENDED_KEY, Version: 0, }, expectResp: responseV0, isErr: false, errStr: "", }, // case success import priv key v1 { req: &nockchain.ImportKeysRequest{ Key: base58.Encode(correctImportPrivKeyV1), ImportType: nockchain.ImportType_EXTENDED_KEY, Version: 1, }, expectResp: responseV1, isErr: false, errStr: "", }, // case invalid import pub key { req: &nockchain.ImportKeysRequest{ Key: "zpub wrong public import key length", ImportType: nockchain.ImportType_EXTENDED_KEY, Version: 0, }, expectResp: nil, isErr: true, errStr: "invalid extended public key length", }, { req: &nockchain.ImportKeysRequest{ Key: base58.Encode(invalidImportPubkeyChecksum), ImportType: nockchain.ImportType_EXTENDED_KEY, Version: 0, }, expectResp: nil, isErr: true, errStr: "invalid checksum", }, // case success import pub key v0 { req: &nockchain.ImportKeysRequest{ Key: base58.Encode(correctImportPubkeyV0), ImportType: nockchain.ImportType_EXTENDED_KEY, Version: 0, }, expectResp: responseV0ReadOnly, isErr: false, errStr: "", }, // case success import pub key v1 { req: &nockchain.ImportKeysRequest{ Key: base58.Encode(correctImportPubkeyV1), ImportType: nockchain.ImportType_EXTENDED_KEY, Version: 1, }, expectResp: responseV1ReadOnly, isErr: false, errStr: "", }, // case missing chaincode when import master privkey { req: &nockchain.ImportKeysRequest{ Key: "3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg", ImportType: nockchain.ImportType_MASTER_PRIVKEY, Version: 0, }, expectResp: nil, isErr: true, errStr: "master key must be in [chain_code],[key] format", }, // case invalid length { req: &nockchain.ImportKeysRequest{ Key: "abcdxyz,3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg", ImportType: nockchain.ImportType_MASTER_PRIVKEY, Version: 0, }, expectResp: nil, isErr: true, errStr: "invalid chain code length", }, { req: &nockchain.ImportKeysRequest{ Key: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8,abcdxyz", ImportType: nockchain.ImportType_MASTER_PRIVKEY, Version: 0, }, expectResp: nil, isErr: true, errStr: "invalid priv key length", }, // case success import master privkey v0 { req: &nockchain.ImportKeysRequest{ Key: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8,3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg", ImportType: nockchain.ImportType_MASTER_PRIVKEY, Version: 0, }, expectResp: responseV0, isErr: false, errStr: "", }, // case success import master privkey v1 { req: &nockchain.ImportKeysRequest{ Key: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8,3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg", ImportType: nockchain.ImportType_MASTER_PRIVKEY, Version: 1, }, expectResp: responseV1, isErr: false, errStr: "", }, // case success import seed v0 { 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", ImportType: nockchain.ImportType_SEEDPHRASE, Version: 0, }, expectResp: responseV0, isErr: false, errStr: "", }, // case success import seed v1 { 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", ImportType: nockchain.ImportType_SEEDPHRASE, Version: 1, }, expectResp: responseV1, isErr: false, errStr: "", }, // case sucess import pubkey v0 { req: &nockchain.ImportKeysRequest{ Key: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6", ImportType: nockchain.ImportType_WATCH_ONLY, Version: 0, }, expectResp: &nockchain.ImportKeysResponse{ Address: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6", Version: 0, }, isErr: false, errStr: "", }, // case sucess import pubkey v1 { req: &nockchain.ImportKeysRequest{ Key: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6", ImportType: nockchain.ImportType_WATCH_ONLY, Version: 1, }, expectResp: &nockchain.ImportKeysResponse{ Address: "BAmYAxgpVrJeFqqPnKAZHVZZdmVfzrgu7bBqcRBNca8HpxQofzUZG8Q", Version: 1, }, 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 := "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(seed1) assert.NoError(t, err) fmt.Println("pk: ", base58.Encode(masterKey1.PublicKey)) nc, err := wallet.NewNockchainClient("nockchain-api.zorp.io:443") assert.NoError(t, err) masterKey1Scan, err := nc.WalletGetBalance(&nockchain.GetBalanceRequest{ Selector: &nockchain.GetBalanceRequest_Address{ Address: base58.Encode(masterKey1.PublicKey), }, }) assert.NoError(t, err) fmt.Println("masterKey1Scan:", masterKey1Scan) assert.NotEmpty(t, masterKey1Scan.Notes) } func TestEncodeNoun(t *testing.T) { lock1 := &nockchain.NockchainLock{ KeysRequired: 1, Pubkeys: []string{"5wef35rKxbJDJRAtzGG1VwbMQK9jF3Wr5e6fyMsh2hxnCQZDZJV1YNQ"}, } lock1Encode := wallet.EncodeNoteData(lock1) assert.Equal(t, lock1Encode, []byte{89, 192, 131, 91, 67, 199, 5, 16, 32, 24, 199, 52, 39, 171, 121, 6, 15, 240, 167, 243, 69, 34, 254, 110, 4, 78, 3, 8, 44, 245, 128, 176, 69, 72, 150, 253, 6, 232, 167, 34, 133, 115, 154, 56, 106, 106, 192, 175, 176, 88, 89, 160, 208, 117, 55, 78, 5}) lock2 := lock1 lock2.Pubkeys = []string{"7UXNF2HXzEaPUvLDVDgGJyriKqpd2974Kj7U2RnLuBPeRGa7ZezhGmK"} lock2Encode := wallet.EncodeNoteData(lock2) assert.Equal(t, lock2Encode, []byte{89, 192, 131, 91, 67, 199, 5, 248, 151, 186, 241, 213, 183, 156, 103, 181, 1, 254, 206, 33, 184, 3, 142, 3, 99, 101, 0, 1, 246, 168, 22, 252, 21, 155, 53, 205, 0, 2, 172, 67, 150, 149, 228, 139, 80, 206, 0, 1, 203, 54, 188, 141, 54, 21, 39, 193, 84}) } // // 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) // fmt.Println(base58.Encode(masterKey1.PublicKey)) // 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, // }) // } // This test should be run with timeout 300s func TestCreateTx(t *testing.T) { seed1 := "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(seed1) assert.NoError(t, err) pkPoint1, err := crypto.CheetaPointFromBytes(masterKey1.PublicKey) assert.NoError(t, err) addr1 := wallet.HashPubkey(pkPoint1) // nockchain-wallet create-tx --names '[4taoqkpysafnp64WBQyzHDKVrqkMeNrdAiVSbWdzZmj7yQYZgQtCq4W 8yd685r3WhvRYsYWFy3LNQqSMMNwWyE3QZe5fqp1jgG3PWtLPvXdYvR]" --fee 1867776 --recipient "5wef35rKxbJDJRAtzGG1VwbMQK9jF3Wr5e6fyMsh2hxnCQZDZJV1YNQ:2000000" --refund-pkh 7UXNF2HXzEaPUvLDVDgGJyriKqpd2974Kj7U2RnLuBPeRGa7ZezhGmK seed2 := "anchor various pair jazz panel rubber virus address achieve opinion end silent runway bus wolf pony cigar nuclear moral mixed gold window timber member" masterKey2, err := crypto.MasterKeyFromSeed(seed2) assert.NoError(t, err) fmt.Println("masterKey2", base58.Encode(masterKey2.PublicKey)) nc, err := wallet.NewNockchainClient("nockchain-api.zorp.io:443") assert.NoError(t, err) handler := wallet.NewGprcHandler(*nc) inputs := "[4taoqkpysafnp64WBQyzHDKVrqkMeNrdAiVSbWdzZmj7yQYZgQtCq4W 8yd685r3WhvRYsYWFy3LNQqSMMNwWyE3QZe5fqp1jgG3PWtLPvXdYvR]" pkPoint2, err := crypto.CheetaPointFromBytes(masterKey2.PublicKey) assert.NoError(t, err) addr2 := wallet.HashPubkey(pkPoint2) recipients := fmt.Sprintf("%s:2000000", crypto.Tip5HashToBase58(addr2)) fee := 1867776 // min fee req := &nockchain.CreateTxRequest{ Names: inputs, Recipients: recipients, Fee: uint64(fee), IsMasterKey: true, Seed: seed1, Index: 0, Hardened: false, Version: 0, RefundAddress: crypto.Tip5HashToBase58(addr1), } res, err := handler.CreateTx(context.Background(), req) assert.NoError(t, err) // the result is taken from create-tx scripts assert.Equal(t, res.RawTx.TxId, "6oDgTWnk6sL98yK49hcQTRAfCdFfgKh58U6TDTKmGPirhoxkMii2D6E") }