Transfer jest tests (#1180)
better daedalus tests
legacy Yoroi transfer tests
Transfer jest tests (#1180)
better daedalus tests
legacy Yoroi transfer tests
amount: '1100000'
},
{
// eslint-disable-next-line max-len
// '0465267961fefd53aefe4cf741dc0df9902d360bca0de4c0abe88ca89d0d08dd3dd993c5b8ca62c78801d3228a8de6b9e18217b001820c24d60c1bcd91c895d585'
address: getAddressForType(
TX_TEST_MNEMONIC_1,
[
} from '../../../../types/TransferTypes';
import { RustModule } from '../../lib/cardanoCrypto/rustLoader';
import type { AddressKeyMap } from '../types';
import environment from '../../../../environment';
import { buildDaedalusTransferTx as shelleyFormatDaedalusTx } from '../shelley/daedalusTransfer';
import { buildDaedalusTransferTx as legacyFormatDaedalusTx } from '../byron/daedalusTransfer';
addressKeys: AddressKeyMap,
outputAddr: string,
getUTXOsForAddresses: AddressUtxoFunc,
legacy: boolean,
|}): Promise<TransferTx> {
const senderUtxos = await toSenderUtxos({
outputAddr: payload.outputAddr,
addressKeys: payload.addressKeys,
senderUtxos,
};
return environment.isShelley()
? shelleyFormatDaedalusTx(txRequest)
: legacyFormatDaedalusTx(txRequest);
return payload.legacy
? legacyFormatDaedalusTx(txRequest)
: shelleyFormatDaedalusTx(txRequest);
}
/* eslint-disable camelcase */
// @flow
import '../../lib/test-config';
import { schema } from 'lovefield';
import {
getCryptoDaedalusWalletFromMnemonics,
} from '../../lib/cardanoCrypto/cryptoWallet';
import {
getAddressesKeys,
buildDaedalusTransferTx,
} from './legacyDaedalus';
import { buildDaedalusTransferTx } from '../byron/daedalusTransfer';
import {
GenerateTransferTxError,
} from '../../errors';
silenceLogsForTesting();
});
test('Daedalus transfer from single small UTXO', async () => {
const words = 'note park thrive ignore spare latin common balance clap soup school tiny';
const address = 'DdzFFzCqrhsmcx7z25PRkdbeUNqNNW4brhznpVxbm1EknAahjaCFEjYXg9KJRqkixjgGyz8D9GSX3CFDRoNrZyfJsi61N2FxCnq9yWBy';
const txId = '915f2e6865fb31cc93410efb6c0e580ca74862374b3da461e20135c01f312e7c';
const inputAmount = '1000000';
const txIndex = 0;
const outAddress = 'Ae2tdPwUPEZ4Gg5gmqwW2t7ottKBMjWunmPt7DwKkAGsxx9XNSfWqrE1Gbk';
const daedalusWallet = getCryptoDaedalusWalletFromMnemonics(words);
const checker = RustModule.WalletV2.DaedalusAddressChecker.new(daedalusWallet);
const addressMap = getAddressesKeys({
checker,
fullUtxo: [address]
});
describe('Daedalus checker tests', () => {
test('Daedalus transfer filters address not belonging to user', async () => {
const words = 'note park thrive ignore spare latin common balance clap soup school tiny';
const myAddress = 'DdzFFzCqrhsmcx7z25PRkdbeUNqNNW4brhznpVxbm1EknAahjaCFEjYXg9KJRqkixjgGyz8D9GSX3CFDRoNrZyfJsi61N2FxCnq9yWBy';
const notMyAddress = 'DdzFFzCqrhsf69sXiAinAVVE9ZazKaoiKk9aSTRLJZSP4wMFGi4ogmcwjvSPMFuGD4a74HWemc3zfh3Eh4GdFRvmt3Jf88e77wCEUJgH';
const utxo = {
utxo_id: 'ignore',
tx_hash: txId,
tx_index: txIndex,
receiver: address,
amount: inputAmount
};
const transferInfo = await buildDaedalusTransferTx({
addressKeys: addressMap,
senderUtxos: [utxo],
outputAddr: outAddress
});
const daedalusWallet = getCryptoDaedalusWalletFromMnemonics(words);
const checker = RustModule.WalletV2.DaedalusAddressChecker.new(daedalusWallet);
const addressMap = getAddressesKeys({
checker,
fullUtxo: [myAddress, notMyAddress]
});
expect(transferInfo.fee.toString()).toBe('0.165841');
expect(transferInfo.recoveredBalance.toString()).toBe('1');
expect(transferInfo.senders).toEqual([address]);
expect(transferInfo.receiver).toBe(outAddress);
// check tx itself
const signedTx = RustModule.WalletV2.SignedTransaction.from_bytes(transferInfo.encodedTx);
const txJson = signedTx.to_json();
expect(txJson.tx.inputs).toHaveLength(1);
expect(txJson.tx.inputs[0].id).toBe(txId);
expect(txJson.tx.inputs[0].index).toBe(txIndex);
expect(txJson.tx.outputs).toHaveLength(1);
expect(txJson.tx.outputs[0].address).toBe(outAddress);
expect(txJson.tx.outputs[0].value).toBe(834159);
expect(txJson.witness).toHaveLength(1);
expect(txJson.witness[0].PkWitness).toEqual([
'1e74f51418f5835a063c1f4c69808134852b7ebdb85d0c08e867572c0a035e7b06b6bd7a7baa2a5dc191cd08f0ca81ada7298cfa20db44d7eda31e7777b4bbe0',
'6e233a6365e9b371d3b8ce95b9f7f565a901109cbfd19e9bf32c4355f73a7466c6b57ec8867e8420d02ec9c2e42fa36e90ae080d6d184f2c9336bee079585c05',
]);
expect(addressMap[myAddress]).not.toBe(undefined);
expect(addressMap[notMyAddress]).toBe(undefined);
});
});
test('Daedalus transfer fails from too small UTXO', async () => {
const words = 'note park thrive ignore spare latin common balance clap soup school tiny';
const address = 'DdzFFzCqrhsmcx7z25PRkdbeUNqNNW4brhznpVxbm1EknAahjaCFEjYXg9KJRqkixjgGyz8D9GSX3CFDRoNrZyfJsi61N2FxCnq9yWBy';
const txId = '915f2e6865fb31cc93410efb6c0e580ca74862374b3da461e20135c01f312e7c';
const inputAmount = '1000';
const txIndex = 0;
const outAddress = 'Ae2tdPwUPEZ4Gg5gmqwW2t7ottKBMjWunmPt7DwKkAGsxx9XNSfWqrE1Gbk';
const daedalusWallet = getCryptoDaedalusWalletFromMnemonics(words);
const checker = RustModule.WalletV2.DaedalusAddressChecker.new(daedalusWallet);
const addressMap = getAddressesKeys({
checker,
fullUtxo: [address]
describe('Byron era tx format tests', () => {
test('Daedalus transfer from single small UTXO', async () => {
const words = 'note park thrive ignore spare latin common balance clap soup school tiny';
const address = 'DdzFFzCqrhsmcx7z25PRkdbeUNqNNW4brhznpVxbm1EknAahjaCFEjYXg9KJRqkixjgGyz8D9GSX3CFDRoNrZyfJsi61N2FxCnq9yWBy';
const txId = '915f2e6865fb31cc93410efb6c0e580ca74862374b3da461e20135c01f312e7c';
const inputAmount = '1000000';
const txIndex = 0;
const outAddress = 'Ae2tdPwUPEZ4Gg5gmqwW2t7ottKBMjWunmPt7DwKkAGsxx9XNSfWqrE1Gbk';
const daedalusWallet = getCryptoDaedalusWalletFromMnemonics(words);
const checker = RustModule.WalletV2.DaedalusAddressChecker.new(daedalusWallet);
const addressMap = getAddressesKeys({
checker,
fullUtxo: [address]
});
const utxo = {
utxo_id: 'ignore',
tx_hash: txId,
tx_index: txIndex,
receiver: address,
amount: inputAmount
};
const transferInfo = await buildDaedalusTransferTx({
addressKeys: addressMap,
getUTXOsForAddresses: (_addresses) => Promise.resolve([utxo]),
outputAddr: outAddress,
legacy: true
});
expect(transferInfo.fee.toString()).toBe('0.165841');
expect(transferInfo.recoveredBalance.toString()).toBe('1');
expect(transferInfo.senders).toEqual([address]);
expect(transferInfo.receiver).toBe(outAddress);
// check tx itself
const signedTx = RustModule.WalletV2.SignedTransaction.from_bytes(transferInfo.encodedTx);
const txJson = signedTx.to_json();
expect(txJson.tx.inputs).toHaveLength(1);
expect(txJson.tx.inputs[0].id).toBe(txId);
expect(txJson.tx.inputs[0].index).toBe(txIndex);
expect(txJson.tx.outputs).toHaveLength(1);
expect(txJson.tx.outputs[0].address).toBe(outAddress);
expect(txJson.tx.outputs[0].value).toBe(834159);
expect(txJson.witness).toHaveLength(1);
expect(txJson.witness[0].PkWitness).toEqual([
'1e74f51418f5835a063c1f4c69808134852b7ebdb85d0c08e867572c0a035e7b06b6bd7a7baa2a5dc191cd08f0ca81ada7298cfa20db44d7eda31e7777b4bbe0',
'6e233a6365e9b371d3b8ce95b9f7f565a901109cbfd19e9bf32c4355f73a7466c6b57ec8867e8420d02ec9c2e42fa36e90ae080d6d184f2c9336bee079585c05',
]);
});
const utxo = {
utxo_id: 'ignore',
tx_hash: txId,
tx_index: txIndex,
receiver: address,
amount: inputAmount
};
expect(buildDaedalusTransferTx({
addressKeys: addressMap,
senderUtxos: [utxo],
outputAddr: outAddress
})).rejects.toThrow(GenerateTransferTxError);
});
test('Daedalus transfer fails from too small UTXO', async () => {
const words = 'note park thrive ignore spare latin common balance clap soup school tiny';
const address = 'DdzFFzCqrhsmcx7z25PRkdbeUNqNNW4brhznpVxbm1EknAahjaCFEjYXg9KJRqkixjgGyz8D9GSX3CFDRoNrZyfJsi61N2FxCnq9yWBy';
const txId = '915f2e6865fb31cc93410efb6c0e580ca74862374b3da461e20135c01f312e7c';
const inputAmount = '1000';
const txIndex = 0;
const outAddress = 'Ae2tdPwUPEZ4Gg5gmqwW2t7ottKBMjWunmPt7DwKkAGsxx9XNSfWqrE1Gbk';
const daedalusWallet = getCryptoDaedalusWalletFromMnemonics(words);
const checker = RustModule.WalletV2.DaedalusAddressChecker.new(daedalusWallet);
const addressMap = getAddressesKeys({
checker,
fullUtxo: [address]
});
test('Daedalus transfer filters address not belonging to user', async () => {
const words = 'note park thrive ignore spare latin common balance clap soup school tiny';
const myAddress = 'DdzFFzCqrhsmcx7z25PRkdbeUNqNNW4brhznpVxbm1EknAahjaCFEjYXg9KJRqkixjgGyz8D9GSX3CFDRoNrZyfJsi61N2FxCnq9yWBy';
const notMyAddress = 'DdzFFzCqrhsf69sXiAinAVVE9ZazKaoiKk9aSTRLJZSP4wMFGi4ogmcwjvSPMFuGD4a74HWemc3zfh3Eh4GdFRvmt3Jf88e77wCEUJgH';
const utxo = {
utxo_id: 'ignore',
tx_hash: txId,
tx_index: txIndex,
receiver: address,
amount: inputAmount
};
const daedalusWallet = getCryptoDaedalusWalletFromMnemonics(words);
const checker = RustModule.WalletV2.DaedalusAddressChecker.new(daedalusWallet);
const addressMap = getAddressesKeys({
checker,
fullUtxo: [myAddress, notMyAddress]
NoInputsError,
} from '../../errors';
import type { AddressedUtxo } from '../types';
import environment from '../../../../environment';
import type {
AddressUtxoFunc,
} from '../../lib/state-fetch/types';
keyLevel: number,
signingKey: RustModule.WalletV3.Bip32PrivateKey,
getUTXOsForAddresses: AddressUtxoFunc,
legacy: boolean,
|}): Promise<TransferTx> {
const senderUtxos = await toSenderUtxos(payload);
const { legacy, ...rest } = payload;
const senderUtxos = await toSenderUtxos(rest);
const txRequest = {
outputAddr: payload.outputAddr,
keyLevel: payload.keyLevel,
signingKey: payload.signingKey,
senderUtxos,
};
return environment.isShelley()
? shelleyFormatYoroiTx(txRequest)
: legacyFormatYoroiTx(txRequest);
return legacy
? legacyFormatYoroiTx(txRequest)
: shelleyFormatYoroiTx(txRequest);
}
// @flow
import '../../lib/test-config';
import { schema } from 'lovefield';
import {
generateLegacyYoroiTransferTx,
} from './legacyYoroi';
import {
silenceLogsForTesting,
} from '../../../../utils/logging';
import {
Bip44DerivationLevels,
} from '../../lib/storage/database/walletTypes/bip44/api/utils';
import type {
Address, Addressing
} from '../../lib/storage/models/PublicDeriver/interfaces';
import {
ChainDerivations,
} from '../../../../config/numbersConfig';
import {
loadLovefieldDB,
} from '../../lib/storage/database/index';
import type { ConfigType } from '../../../../../config/config-types';
import { RustModule } from '../../lib/cardanoCrypto/rustLoader';
declare var CONFIG: ConfigType;
const protocolMagic = CONFIG.network.protocolMagic;
beforeAll(async () => {
await RustModule.load();
await loadLovefieldDB(schema.DataStoreType.MEMORY);
silenceLogsForTesting();
});
function getShelleyAddress(
accountKey: RustModule.WalletV3.Bip32PrivateKey,
derivationId: number
): {| ...Address, ...Addressing |} {
const addr = RustModule.WalletV3.Address.single_from_public_key(
accountKey
.derive(ChainDerivations.EXTERNAL)
.derive(0)
.to_raw_key()
.to_public(),
RustModule.WalletV3.AddressDiscrimination.Production
);
return {
address: addr.to_string('addr'),
addressing: {
path: [ChainDerivations.EXTERNAL, derivationId],
startLevel: Bip44DerivationLevels.CHAIN.level,
}
};
}
function getByronAddress(
accountKey: RustModule.WalletV3.Bip32PrivateKey,
derivationId: number
): {| ...Address, ...Addressing |} {
const v3Key = accountKey
.derive(ChainDerivations.EXTERNAL)
.derive(0)
.to_public();
const v2Key = RustModule.WalletV2.PublicKey.from_hex(
Buffer.from(v3Key.as_bytes()).toString('hex')
);
const addr = v2Key.bootstrap_era_address(
RustModule.WalletV2.BlockchainSettings.from_json({
protocol_magic: protocolMagic
})
);
return {
address: addr.to_base58(),
addressing: {
path: [ChainDerivations.EXTERNAL, derivationId],
startLevel: Bip44DerivationLevels.CHAIN.level,
}
};
}
describe('Byron era tx format tests', () => {
test('Yoroi transfer from single small UTXO', async () => {
const txId = '915f2e6865fb31cc93410efb6c0e580ca74862374b3da461e20135c01f312e7c';
const inputAmount = '1000000';
const txIndex = 0;
const outAddress = 'Ae2tdPwUPEZKX8N2TjzBXLy5qrecnQUniTd2yxE8mWyrh2djNpUkbAtXtP4';
const accountPrivateKey = RustModule.WalletV3.Bip32PrivateKey.from_bytes(
Buffer.from(
'408a1cb637d615c49e8696c30dd54883302a20a7b9b8a9d1c307d2ed3cd50758c9402acd000461a8fc0f25728666e6d3b86d031b8eea8d2f69b21e8aa6ba2b153e3ec212cc8a36ed9860579dfe1e3ef4d6de778c5dbdd981623b48727cd96247',
'hex',
),
);
const addr1 = getByronAddress(accountPrivateKey, 0);
const addr2 = getByronAddress(accountPrivateKey, 1);
const utxo = {
utxo_id: 'ignore',
tx_hash: txId,
tx_index: txIndex,
receiver: addr1.address,
amount: inputAmount
};
const transferInfo = await generateLegacyYoroiTransferTx({
addresses: [addr1, addr2],
getUTXOsForAddresses: (_addresses) => Promise.resolve([utxo]),
keyLevel: Bip44DerivationLevels.ACCOUNT.level,
signingKey: accountPrivateKey,
outputAddr: outAddress,
legacy: true
});
expect(transferInfo.fee.toString()).toBe('0.165841');
expect(transferInfo.recoveredBalance.toString()).toBe('1');
expect(transferInfo.senders).toEqual([addr1.address]);
expect(transferInfo.receiver).toBe(outAddress);
// check tx itself
const signedTx = RustModule.WalletV2.SignedTransaction.from_bytes(transferInfo.encodedTx);
const txJson = signedTx.to_json();
expect(txJson.tx.inputs).toHaveLength(1);
expect(txJson.tx.inputs[0].id).toBe(txId);
expect(txJson.tx.inputs[0].index).toBe(txIndex);
expect(txJson.tx.outputs).toHaveLength(1);
expect(txJson.tx.outputs[0].address).toBe(outAddress);
expect(txJson.tx.outputs[0].value).toBe(834159);
expect(txJson.witness).toHaveLength(1);
expect(txJson.witness[0].PkWitness).toEqual([
'07cc5b01ab460562479f3e7fdf782b11636c4a1b721c19b9c1609bc7360b518ef3748736afd541361c4fb90b2963723fe9a10d237a024530d378181df4bf2c68',
'c7beab6de0a38171bb4530c5f287239fba79fd8f2d89ba05a233c172bac4995d6933634521aba2ae43a175929ef0738ca531b22cf564071bd7149d8e80845500',
]);
});
});
describe('Shelley era tx format tests', () => {
test('Yoroi transfer from single small UTXO', async () => {
const txId = '915f2e6865fb31cc93410efb6c0e580ca74862374b3da461e20135c01f312e7c';
const inputAmount = '1000000';
const txIndex = 0;
const outAddress = RustModule.WalletV3.Address.from_bytes(
Buffer.from('038e2840fed90d2138761d8a14a4cbed08ed00cf908b07f94ec5fa9db6f4d7e74f', 'hex')
).to_string('addr');
const accountPrivateKey = RustModule.WalletV3.Bip32PrivateKey.from_bytes(
Buffer.from(
'408a1cb637d615c49e8696c30dd54883302a20a7b9b8a9d1c307d2ed3cd50758c9402acd000461a8fc0f25728666e6d3b86d031b8eea8d2f69b21e8aa6ba2b153e3ec212cc8a36ed9860579dfe1e3ef4d6de778c5dbdd981623b48727cd96247',
'hex',
),
);
const addr1 = getShelleyAddress(accountPrivateKey, 0);
const addr2 = getShelleyAddress(accountPrivateKey, 1);
const utxo = {
utxo_id: 'ignore',
tx_hash: txId,
tx_index: txIndex,
receiver: addr1.address,
amount: inputAmount
};
const transferInfo = await generateLegacyYoroiTransferTx({
addresses: [addr1, addr2],
getUTXOsForAddresses: (_addresses) => Promise.resolve([utxo]),
keyLevel: Bip44DerivationLevels.ACCOUNT.level,
signingKey: accountPrivateKey,
outputAddr: outAddress,
legacy: false
});
expect(transferInfo.fee.toString()).toBe('0.155383');
expect(transferInfo.recoveredBalance.toString()).toBe('1');
expect(transferInfo.senders).toEqual([addr1.address]);
expect(transferInfo.receiver).toBe(outAddress);
// check tx itself
const fragment = RustModule.WalletV3.Fragment.from_bytes(transferInfo.encodedTx);
const signedTx = fragment.get_transaction();
const inputs = signedTx.inputs();
expect(inputs.size()).toEqual(1);
expect(inputs.get(0).value().to_str()).toEqual(inputAmount);
const pointer = inputs.get(0).get_utxo_pointer();
expect(Buffer.from(pointer.fragment_id().as_bytes()).toString('hex')).toEqual(txId);
expect(pointer.output_index()).toEqual(txIndex);
const outputs = signedTx.outputs();
expect(outputs.size()).toEqual(1);
const output = outputs.get(0);
expect(output.address().to_string('addr')).toEqual(outAddress);
expect(output.value().to_str()).toEqual('844617');
outputAddr: nextInternalAddress,
getUTXOsForAddresses:
this.stores.substores.ada.stateFetchStore.fetcher.getUTXOsForAddresses,
legacy: !environment.isShelley()
});
runInAction(() => {
this.transferTx = transferTx;
signingKey: accountKey,
getUTXOsForAddresses:
this.stores.substores.ada.stateFetchStore.fetcher.getUTXOsForAddresses,
legacy: !environment.isShelley(),
});
// Possible exception: NotEnoughMoneyToSendError
return transferTx;