Changeset View
Changeset View
Standalone View
Standalone View
apps/alias-server/test/aliasTests.js
// Copyright (c) 2023 The Bitcoin developers | // Copyright (c) 2023 The Bitcoin developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
'use strict'; | 'use strict'; | ||||
const assert = require('assert'); | const assert = require('assert'); | ||||
const cashaddr = require('ecashaddrjs'); | const cashaddr = require('ecashaddrjs'); | ||||
const config = require('../config'); | const config = require('../config'); | ||||
const { | const { | ||||
parseAliasTx, | parseAliasTx, | ||||
getAliasTxs, | getAliasTxs, | ||||
sortAliasTxsByTxidAndBlockheight, | sortAliasTxsByTxidAndBlockheight, | ||||
registerAliases, | registerAliases, | ||||
} = require('../src/alias'); | } = require('../src/alias'); | ||||
const { removeUnconfirmedTxsFromTxHistory } = require('../src/utils'); | const { removeUnconfirmedTxsFromTxHistory } = require('../src/utils'); | ||||
const { | const { generated, templates } = require('./mocks/aliasMocks'); | ||||
testAddressAliases, | |||||
testAddressAliasesWithUnconfirmedTxs, | |||||
aliases_fake_data, | |||||
} = require('./mocks/aliasMocks'); | |||||
// Mock mongodb | // Mock mongodb | ||||
const { MongoClient } = require('mongodb'); | const { MongoClient } = require('mongodb'); | ||||
const { MongoMemoryServer } = require('mongodb-memory-server'); | const { MongoMemoryServer } = require('mongodb-memory-server'); | ||||
const { initializeDb, addAliasesToDb } = require('../src/db'); | const { initializeDb, addAliasesToDb } = require('../src/db'); | ||||
const { aliasConstants } = require('../config'); | |||||
describe('alias-server alias.js', async function () { | describe('alias-server alias.js', async function () { | ||||
let mongoServer, testMongoClient; | let mongoServer, testMongoClient, registrationOutputScript; | ||||
before(async () => { | before(async () => { | ||||
// Start mongo memory server before running this suite of unit tests | // Start mongo memory server before running this suite of unit tests | ||||
mongoServer = await MongoMemoryServer.create(); | mongoServer = await MongoMemoryServer.create(); | ||||
const mongoUri = mongoServer.getUri(); | const mongoUri = mongoServer.getUri(); | ||||
testMongoClient = new MongoClient(mongoUri); | testMongoClient = new MongoClient(mongoUri); | ||||
// Get outputScript for the IFP address used in parseAliasTx tests | |||||
registrationOutputScript = cashaddr.getOutputScriptFromAddress( | |||||
config.aliasConstants.registrationAddress, | |||||
); | |||||
}); | }); | ||||
after(async () => { | after(async () => { | ||||
// Shut down mongo memory server after running this suite of unit tests | // Shut down mongo memory server after running this suite of unit tests | ||||
await testMongoClient.close(); | await testMongoClient.close(); | ||||
await mongoServer.stop(); | await mongoServer.stop(); | ||||
}); | }); | ||||
it('Correctly parses a 5-character alias transaction', function () { | it('parseAliasTx returns tx info for an alias tx with an overpaid fee', function () { | ||||
const registrationOutputScript = cashaddr.getOutputScriptFromAddress( | // txid of a valid alias registration at these aliasConstants | ||||
config.aliasConstants.registrationAddress, | const validRegistrationTxid = | ||||
); | 'ec92610fc41df2387e7febbb358b138a802ac26023f30b2442aa01ca733fff7d'; | ||||
const chronikTxObject = | |||||
generated.txHistory[ | |||||
generated.txHistory.findIndex( | |||||
i => i.txid === validRegistrationTxid, | |||||
) | |||||
]; | |||||
// Set the fee to be overpaid by adding another output to the registration address | |||||
chronikTxObject.outputs.push({ | |||||
value: '558', | |||||
outputScript: 'a914d37c4c809fe9840e7bfa77b86bd47163f6fb6c6087', | |||||
}); | |||||
const expectedResult = | |||||
generated.validAliasRegistrations[ | |||||
generated.validAliasRegistrations.findIndex( | |||||
i => i.txid === validRegistrationTxid, | |||||
) | |||||
]; | |||||
// Get this tx from txHistory | |||||
assert.deepEqual( | assert.deepEqual( | ||||
parseAliasTx( | parseAliasTx( | ||||
testAddressAliases.txHistory[ | chronikTxObject, | ||||
testAddressAliases.txHistory.findIndex( | |||||
i => | |||||
i.txid === | |||||
'9d9fd465f56a7946c48b2e214386b51d7968a3a40d46cc697036e4fc1cc644df', | |||||
) | |||||
], | |||||
config.aliasConstants, | config.aliasConstants, | ||||
registrationOutputScript, | registrationOutputScript, | ||||
), | ), | ||||
testAddressAliases.allAliasTxs[ | expectedResult, | ||||
testAddressAliases.allAliasTxs.findIndex( | |||||
i => | |||||
i.txid === | |||||
'9d9fd465f56a7946c48b2e214386b51d7968a3a40d46cc697036e4fc1cc644df', | |||||
) | |||||
], | |||||
); | ); | ||||
}); | }); | ||||
it('Correctly parses a 6-character alias transaction', function () { | it('parseAliasTx returns false for an alias tx with an underpaid fee', function () { | ||||
const registrationOutputScript = cashaddr.getOutputScriptFromAddress( | // txid of an alias registering tx with underpaid fee | ||||
config.aliasConstants.registrationAddress, | const testTxid = | ||||
'5488810babff675cd1da1ca213ffece96c6b4a372f304833734d8f5dbb197dc5'; | |||||
const chronikTxObject = | |||||
generated.txHistory[ | |||||
generated.txHistory.findIndex(i => i.txid === testTxid) | |||||
]; | |||||
// Get this tx from txHistory | |||||
assert.deepEqual( | |||||
parseAliasTx( | |||||
chronikTxObject, | |||||
config.aliasConstants, | |||||
registrationOutputScript, | |||||
), | |||||
false, | |||||
); | ); | ||||
}); | |||||
it('parseAliasTx returns false for an etoken tx', function () { | |||||
const testTxid = | |||||
'e4d80b015e75fe2e54b5ef10571ce78c17086f96a7876d466f92d8c2a8c92b64'; | |||||
const chronikTxObject = | |||||
generated.txHistory[ | |||||
generated.txHistory.findIndex(i => i.txid === testTxid) | |||||
]; | |||||
// Get this tx from txHistory | |||||
assert.deepEqual( | assert.deepEqual( | ||||
parseAliasTx( | parseAliasTx( | ||||
testAddressAliases.txHistory[ | chronikTxObject, | ||||
testAddressAliases.txHistory.findIndex( | |||||
i => | |||||
i.txid === | |||||
'36fdab59d25625b6ff3661aa5ab22a4893698fa5618e5e958e1d75bf921e6107', | |||||
) | |||||
], | |||||
config.aliasConstants, | config.aliasConstants, | ||||
registrationOutputScript, | registrationOutputScript, | ||||
), | ), | ||||
testAddressAliases.allAliasTxs[ | false, | ||||
testAddressAliases.allAliasTxs.findIndex( | |||||
i => | |||||
i.txid === | |||||
'36fdab59d25625b6ff3661aa5ab22a4893698fa5618e5e958e1d75bf921e6107', | |||||
) | |||||
], | |||||
); | ); | ||||
}); | }); | ||||
it('Returns false for an eToken transaction', function () { | it('parseAliasTx returns false for a Cashtab msg tx including the text .xec', function () { | ||||
const registrationOutputScript = cashaddr.getOutputScriptFromAddress( | const testTxid = | ||||
config.aliasConstants.registrationAddress, | 'e73030820d7db7727151c167de2bf5c4a5d3728b757af78b60bb11143d418f71'; | ||||
const chronikTxObject = | |||||
generated.txHistory[ | |||||
generated.txHistory.findIndex(i => i.txid === testTxid) | |||||
]; | |||||
// Get this tx from txHistory | |||||
assert.deepEqual( | |||||
parseAliasTx( | |||||
chronikTxObject, | |||||
config.aliasConstants, | |||||
registrationOutputScript, | |||||
), | |||||
false, | |||||
); | ); | ||||
}); | |||||
it('parseAliasTx returns false for a registration with non-alphanumeric characters', function () { | |||||
// CapitalLetters_And_+! | |||||
const testTxid = | |||||
'a2861460d090243af6b85eda5439e9545ad5abcc652cc6e65ac1c0222a553c06'; | |||||
const chronikTxObject = | |||||
generated.txHistory[ | |||||
generated.txHistory.findIndex(i => i.txid === testTxid) | |||||
]; | |||||
// Get this tx from txHistory | |||||
assert.deepEqual( | assert.deepEqual( | ||||
parseAliasTx( | parseAliasTx( | ||||
testAddressAliases.txHistory[ | chronikTxObject, | ||||
testAddressAliases.txHistory.findIndex( | |||||
i => | |||||
i.txid === | |||||
'feafd053d4166601d42949a768b9c3e8ee1f27912fc84b6190aeb022fba7fa39', | |||||
) | |||||
], | |||||
config.aliasConstants, | config.aliasConstants, | ||||
registrationOutputScript, | registrationOutputScript, | ||||
), | ), | ||||
false, | false, | ||||
); | ); | ||||
}); | }); | ||||
it('Returns false for a standard tx without an OP_RETURN', function () { | it('parseAliasTx returns false for a registration of an empty string', function () { | ||||
const registrationOutputScript = cashaddr.getOutputScriptFromAddress( | // '' | ||||
config.aliasConstants.registrationAddress, | const testTxid = | ||||
'c6a24c60f7d05fcd283f20ac15e323eb831c1bac50e9a03cb9d6b78db188a055'; | |||||
const chronikTxObject = | |||||
generated.txHistory[ | |||||
generated.txHistory.findIndex(i => i.txid === testTxid) | |||||
]; | |||||
// Get this tx from txHistory | |||||
assert.deepEqual( | |||||
parseAliasTx( | |||||
chronikTxObject, | |||||
config.aliasConstants, | |||||
registrationOutputScript, | |||||
), | |||||
false, | |||||
); | ); | ||||
}); | |||||
it('parseAliasTx returns false for a registration of an emoji', function () { | |||||
// 🎯 | |||||
const testTxid = | |||||
'0ded74eeacdc80cec493dfbe61dab9c35e51d0fe2a13709a4098c4278143f5ac'; | |||||
const chronikTxObject = | |||||
generated.txHistory[ | |||||
generated.txHistory.findIndex(i => i.txid === testTxid) | |||||
]; | |||||
// Get this tx from txHistory | |||||
assert.deepEqual( | assert.deepEqual( | ||||
parseAliasTx( | parseAliasTx( | ||||
testAddressAliases.txHistory[ | chronikTxObject, | ||||
testAddressAliases.txHistory.findIndex( | |||||
i => | |||||
i.txid === | |||||
'7440fb8810610f29197701c53f4a29479a9aede8c66feabb44b049232f990791', | |||||
) | |||||
], | |||||
config.aliasConstants, | config.aliasConstants, | ||||
registrationOutputScript, | registrationOutputScript, | ||||
), | ), | ||||
false, | false, | ||||
); | ); | ||||
}); | }); | ||||
it('Correctly parses all aliases through transactions at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8', function () { | it('parseAliasTx returns false for a registration longer than 21 characters', function () { | ||||
// twentytwocharactertest (length 22) | |||||
const testTxid = | |||||
'5a811654b8aaf68c01b0c92a4350d6109ea3857c1838bacc558b277f11a800b9'; | |||||
const chronikTxObject = | |||||
generated.txHistory[ | |||||
generated.txHistory.findIndex(i => i.txid === testTxid) | |||||
]; | |||||
// Get this tx from txHistory | |||||
assert.deepEqual( | assert.deepEqual( | ||||
getAliasTxs(testAddressAliases.txHistory, config.aliasConstants), | parseAliasTx( | ||||
testAddressAliases.allAliasTxs, | chronikTxObject, | ||||
config.aliasConstants, | |||||
registrationOutputScript, | |||||
), | |||||
false, | |||||
); | ); | ||||
}); | }); | ||||
it('Correctly parses all aliases through transactions at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8 including unconfirmed txs', function () { | it('parseAliasTx returns false for a registration of an invalid cash address', function () { | ||||
// Registering "notanaddress" in address field | |||||
const testTxid = | |||||
'13d9990104217e6dfb37df95ec07f1a329b2bbfa9ce8042a49bd7a0600137adb'; | |||||
const chronikTxObject = | |||||
generated.txHistory[ | |||||
generated.txHistory.findIndex(i => i.txid === testTxid) | |||||
]; | |||||
// Get this tx from txHistory | |||||
assert.deepEqual( | assert.deepEqual( | ||||
getAliasTxs( | parseAliasTx( | ||||
testAddressAliasesWithUnconfirmedTxs.txHistory, | chronikTxObject, | ||||
config.aliasConstants, | config.aliasConstants, | ||||
registrationOutputScript, | |||||
), | ), | ||||
testAddressAliasesWithUnconfirmedTxs.allAliasTxs, | false, | ||||
); | ); | ||||
}); | }); | ||||
it('Correctly sorts simple template alias txs including unconfirmed alias txs by blockheight and txid', function () { | it('getAliasTxs correctly parses all test vectors including all character lengths, p2pkh, and p2sh addresses', function () { | ||||
assert.deepEqual( | |||||
getAliasTxs(generated.txHistory, config.aliasConstants), | |||||
generated.allAliasTxs, | |||||
); | |||||
}); | |||||
it('getAliasTxs correctly parses all test vectors including all character lengths, p2pkh, and p2sh addresses, if some txs are still unconfirmed', function () { | |||||
// Modify a few txs to be unconfirmed | |||||
// Note: unconfirmed txs in chronik simply lack the 'block' key | |||||
// First, clone the mock so that you are not modifying it in place | |||||
const txHistoryWithSomeUnconfirmedTxs = JSON.parse( | |||||
JSON.stringify(generated.txHistory), | |||||
); | |||||
// Then, delete the 'block' key of the most recent 3 txs | |||||
// NB these do not include valid alias registrations | |||||
delete txHistoryWithSomeUnconfirmedTxs[0].block; // db09c578d38f37bd9f2bb69eeb8ecb2e24c5be01aa2914f17d94759aadf71386 | |||||
delete txHistoryWithSomeUnconfirmedTxs[1].block; // c040ccdc46df2951b2ab0cd6d48cf9db7c518068d1f871e60379ee8ccd1caa0e | |||||
delete txHistoryWithSomeUnconfirmedTxs[2].block; // 828201e4680e6617636193d3f2a319daab80a8cc5772b9a5b6e068de639f2d9c | |||||
// Delete the 'block' key of the most recent valid alias registration | |||||
delete txHistoryWithSomeUnconfirmedTxs[ | |||||
txHistoryWithSomeUnconfirmedTxs.findIndex( | |||||
tx => | |||||
tx.txid === | |||||
'e9f0a9984b4ae354fb8b4dd8193c974074942b0ee6fba14bf85fa1ca14dc5987', | |||||
) | |||||
].block; | |||||
// Clone your expected result | |||||
const expectedResult = JSON.parse( | |||||
JSON.stringify(generated.allAliasTxs), | |||||
); | |||||
// Modify expected blockheight of the unconfirmed registration | |||||
expectedResult[ | |||||
expectedResult.findIndex( | |||||
result => | |||||
result.txid === | |||||
'e9f0a9984b4ae354fb8b4dd8193c974074942b0ee6fba14bf85fa1ca14dc5987', | |||||
) | |||||
].blockheight = config.unconfirmedBlockheight; | |||||
// Modify expected result to have expected "unconfirmed" blockheight | |||||
assert.deepEqual( | assert.deepEqual( | ||||
sortAliasTxsByTxidAndBlockheight(aliases_fake_data.unsortedSimple), | getAliasTxs(txHistoryWithSomeUnconfirmedTxs, config.aliasConstants), | ||||
aliases_fake_data.sortedSimple, | expectedResult, | ||||
); | ); | ||||
}); | }); | ||||
it('Correctly sorts template alias txs including unconfirmed alias txs by blockheight and txid', function () { | it('sortAliasTxsByTxidAndBlockheight correctly sorts template alias txs including unconfirmed alias txs by blockheight and txid', function () { | ||||
assert.deepEqual( | assert.deepEqual( | ||||
sortAliasTxsByTxidAndBlockheight(aliases_fake_data.allAliasTxs), | sortAliasTxsByTxidAndBlockheight(templates.unsortedSimple), | ||||
aliases_fake_data.allAliasTxsSortedByTxidAndBlockheight, | templates.sortedSimple, | ||||
); | ); | ||||
}); | }); | ||||
it('Correctly sorts alias txs including unconfirmed alias txs by blockheight and txid', function () { | it('sortAliasTxsByTxidAndBlockheight correctly sorts template alias txs including unconfirmed alias txs by blockheight and txid', function () { | ||||
assert.deepEqual( | assert.deepEqual( | ||||
sortAliasTxsByTxidAndBlockheight( | sortAliasTxsByTxidAndBlockheight(templates.allAliasTxs), | ||||
testAddressAliasesWithUnconfirmedTxs.allAliasTxs, | templates.allAliasTxsSortedByTxidAndBlockheight, | ||||
), | |||||
testAddressAliasesWithUnconfirmedTxs.allAliasTxsSortedByTxidAndBlockheight, | |||||
); | ); | ||||
}); | }); | ||||
it('Correctly returns only valid alias registrations at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8 starting with an empty database', async function () { | it('sortAliasTxsByTxidAndBlockheight correctly sorts alias txs including unconfirmed alias txs by blockheight and txid', function () { | ||||
// First, clone the mock so that you are not modifying it in place | |||||
const txHistoryWithSomeUnconfirmedTxs = JSON.parse( | |||||
JSON.stringify(generated.txHistory), | |||||
); | |||||
// Then, delete the 'block' key of the most recent 3 txs | |||||
// NB these do not include valid alias registrations | |||||
delete txHistoryWithSomeUnconfirmedTxs[0].block; // db09c578d38f37bd9f2bb69eeb8ecb2e24c5be01aa2914f17d94759aadf71386 | |||||
delete txHistoryWithSomeUnconfirmedTxs[1].block; // c040ccdc46df2951b2ab0cd6d48cf9db7c518068d1f871e60379ee8ccd1caa0e | |||||
delete txHistoryWithSomeUnconfirmedTxs[2].block; // 828201e4680e6617636193d3f2a319daab80a8cc5772b9a5b6e068de639f2d9c | |||||
// Delete the 'block' key of the most recent valid alias registration | |||||
delete txHistoryWithSomeUnconfirmedTxs[ | |||||
txHistoryWithSomeUnconfirmedTxs.findIndex( | |||||
tx => | |||||
tx.txid === | |||||
'e9f0a9984b4ae354fb8b4dd8193c974074942b0ee6fba14bf85fa1ca14dc5987', | |||||
) | |||||
].block; | |||||
// Clone your expected result | |||||
const allAliasTxsWithUnconfirmed = JSON.parse( | |||||
JSON.stringify(generated.allAliasTxs), | |||||
); | |||||
// Modify expected blockheight of the unconfirmed registration | |||||
allAliasTxsWithUnconfirmed[ | |||||
allAliasTxsWithUnconfirmed.findIndex( | |||||
result => | |||||
result.txid === | |||||
'e9f0a9984b4ae354fb8b4dd8193c974074942b0ee6fba14bf85fa1ca14dc5987', | |||||
) | |||||
].blockheight = config.unconfirmedBlockheight; | |||||
// Modify generated.allAliasTxsSortedByTxidAndBlockheight to include unconfirmed txs | |||||
const expectedResult = JSON.parse( | |||||
JSON.stringify(generated.allAliasTxsSortedByTxidAndBlockheight), | |||||
); | |||||
expectedResult[ | |||||
expectedResult.findIndex( | |||||
result => | |||||
result.txid === | |||||
'e9f0a9984b4ae354fb8b4dd8193c974074942b0ee6fba14bf85fa1ca14dc5987', | |||||
) | |||||
].blockheight = config.unconfirmedBlockheight; | |||||
assert.deepEqual( | |||||
sortAliasTxsByTxidAndBlockheight(allAliasTxsWithUnconfirmed), | |||||
expectedResult, | |||||
); | |||||
}); | |||||
it('Correctly returns only valid alias registrations starting with an empty database', async function () { | |||||
// Initialize db before each unit test | // Initialize db before each unit test | ||||
let testDb = await initializeDb(testMongoClient); | let testDb = await initializeDb(testMongoClient); | ||||
// Clone unprocessedAliasTxs since the act of adding to db gives it an _id field | // Clone unprocessedAliasTxs since the act of adding to db gives it an _id field | ||||
const mockAllAliasTxs = JSON.parse( | const mockAllAliasTxs = JSON.parse( | ||||
JSON.stringify(testAddressAliases.allAliasTxs), | JSON.stringify(generated.allAliasTxs), | ||||
); | ); | ||||
assert.deepEqual( | assert.deepEqual( | ||||
await registerAliases(testDb, mockAllAliasTxs), | await registerAliases(testDb, mockAllAliasTxs), | ||||
testAddressAliases.validAliasTxs, | generated.validAliasRegistrations, | ||||
); | ); | ||||
// Wipe the database after this unit test | // Wipe the database after this unit test | ||||
await testDb.dropDatabase(); | await testDb.dropDatabase(); | ||||
}); | }); | ||||
it('Correctly returns only new valid alias registrations at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8 given partial txHistory and list of registered aliases', async function () { | // TODO fix this | ||||
// Take only txs after registration of alias 'bytesofman' | it('Correctly returns only new valid alias registrations given partial txHistory and list of registered aliases', async function () { | ||||
// Take only txs after registration of alias '7777777' | |||||
// Note: allAliasTxs are sorted with most recent txs first | // Note: allAliasTxs are sorted with most recent txs first | ||||
const unprocessedAliasTxs = testAddressAliases.allAliasTxs.slice( | const unprocessedAliasTxs = generated.allAliasTxs.slice( | ||||
0, | 0, | ||||
testAddressAliases.allAliasTxs.findIndex( | generated.allAliasTxs.findIndex(i => i.alias === '7777777'), | ||||
i => i.alias === 'bytesofman', | |||||
), | |||||
); | ); | ||||
// Clone unprocessedAliasTxs since the act of adding to db gives it an _id field | // Clone unprocessedAliasTxs since the act of adding to db gives it an _id field | ||||
const mockUnprocessedAliasTxs = JSON.parse( | const mockUnprocessedAliasTxs = JSON.parse( | ||||
JSON.stringify(unprocessedAliasTxs), | JSON.stringify(unprocessedAliasTxs), | ||||
); | ); | ||||
// Get list of all valid alias registrations before 'bytesofman' | // Get list of all valid alias registrations before '7777777' | ||||
// Note: validAliasTxs are sorted with most recent txs last | // Note: validAliasTxs are sorted with most recent txs last | ||||
// Note: you want to include bytesofman here | // Note: you want to include 7777777 here | ||||
const registeredAliases = testAddressAliases.validAliasTxs.slice( | const registeredAliases = generated.validAliasRegistrations.slice( | ||||
0, | 0, | ||||
testAddressAliases.validAliasTxs.findIndex( | generated.validAliasRegistrations.findIndex( | ||||
i => i.alias === 'bytesofman', | i => i.alias === '7777777', | ||||
) + 1, | ) + 1, | ||||
); | ); | ||||
// newlyValidAliases will be all the valid alias txs registered after 'bytesofman' | // newlyValidAliases will be all the valid alias txs registered after '7777777' | ||||
// Note: you do not want bytesofman in this set | // Note: you do not want 7777777 in this set | ||||
const newlyValidAliases = testAddressAliases.validAliasTxs.slice( | const newlyValidAliases = generated.validAliasRegistrations.slice( | ||||
testAddressAliases.validAliasTxs.findIndex( | generated.validAliasRegistrations.findIndex( | ||||
i => i.alias === 'bytesofman', | i => i.alias === '7777777', | ||||
) + 1, | ) + 1, | ||||
); | ); | ||||
// Initialize db before each unit test | // Initialize db before each unit test | ||||
let testDb = await initializeDb(testMongoClient); | let testDb = await initializeDb(testMongoClient); | ||||
// mockRegisteredAliases needs to be a clone of the mock because | // mockRegisteredAliases needs to be a clone of the mock because | ||||
// each object gets an _id field when added to the database | // each object gets an _id field when added to the database | ||||
const mockRegisteredAliases = JSON.parse( | const mockRegisteredAliases = JSON.parse( | ||||
JSON.stringify(registeredAliases), | JSON.stringify(registeredAliases), | ||||
); | ); | ||||
// Add expected registered aliases to the db | // Add expected registered aliases to the db | ||||
await addAliasesToDb(testDb, mockRegisteredAliases); | await addAliasesToDb(testDb, mockRegisteredAliases); | ||||
assert.deepEqual( | assert.deepEqual( | ||||
await registerAliases(testDb, mockUnprocessedAliasTxs), | await registerAliases(testDb, mockUnprocessedAliasTxs), | ||||
newlyValidAliases, | newlyValidAliases, | ||||
); | ); | ||||
// Wipe the database after this unit test | // Wipe the database after this unit test | ||||
await testDb.dropDatabase(); | await testDb.dropDatabase(); | ||||
}); | }); | ||||
it('Correctly returns valid alias registrations at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8 given some unconfirmed txs in history', async function () { | it('Correctly returns valid alias registrations given some unconfirmed txs in history', async function () { | ||||
// Initialize db before each unit test | // Initialize db | ||||
let testDb = await initializeDb(testMongoClient); | let testDb = await initializeDb(testMongoClient); | ||||
// Start with the raw tx history | // First, clone the mock so that you are not modifying it in place | ||||
const txHistoryWithSomeUnconfirmedTxs = JSON.parse( | |||||
JSON.stringify(generated.txHistory), | |||||
); | |||||
// Then, delete the 'block' key of the most recent 3 txs | |||||
// NB these do not include valid alias registrations | |||||
delete txHistoryWithSomeUnconfirmedTxs[0].block; // db09c578d38f37bd9f2bb69eeb8ecb2e24c5be01aa2914f17d94759aadf71386 | |||||
delete txHistoryWithSomeUnconfirmedTxs[1].block; // c040ccdc46df2951b2ab0cd6d48cf9db7c518068d1f871e60379ee8ccd1caa0e | |||||
delete txHistoryWithSomeUnconfirmedTxs[2].block; // 828201e4680e6617636193d3f2a319daab80a8cc5772b9a5b6e068de639f2d9c | |||||
// Delete the 'block' key of the most recent valid alias registration | |||||
delete txHistoryWithSomeUnconfirmedTxs[ | |||||
txHistoryWithSomeUnconfirmedTxs.findIndex( | |||||
tx => | |||||
tx.txid === | |||||
'e9f0a9984b4ae354fb8b4dd8193c974074942b0ee6fba14bf85fa1ca14dc5987', | |||||
) | |||||
].block; | |||||
// First, remove the unconfirmed txs, following the logic used in events.js | // App logic is to remove unconfirmed txs before calling registeredAliases. Follow this. | ||||
const confirmedUnsortedTxs = removeUnconfirmedTxsFromTxHistory( | const onlyConfirmedAliasTxs = removeUnconfirmedTxsFromTxHistory( | ||||
testAddressAliasesWithUnconfirmedTxs.txHistory, | txHistoryWithSomeUnconfirmedTxs, | ||||
); | |||||
// Get the alias txs | |||||
const confirmedUnsortedAliasTxs = getAliasTxs( | |||||
confirmedUnsortedTxs, | |||||
config.aliasConstants, | |||||
); | ); | ||||
// Now getAllAlias txs from this set | |||||
const allAliasTxs = getAliasTxs(onlyConfirmedAliasTxs, aliasConstants); | |||||
// registerAliases won't see the unconfirmed tx, so remove this from your expected result | |||||
const validAliasRegistrationsClone = JSON.parse( | |||||
JSON.stringify(generated.validAliasRegistrations), | |||||
); | |||||
const registeredAliasesCloneLessUnconfirmed = | |||||
validAliasRegistrationsClone.slice( | |||||
0, | |||||
validAliasRegistrationsClone.findIndex( | |||||
result => | |||||
result.txid === | |||||
'e9f0a9984b4ae354fb8b4dd8193c974074942b0ee6fba14bf85fa1ca14dc5987', | |||||
), | |||||
); | |||||
// This tests startup condition, so add no aliases to the database | // This tests startup condition, so add no aliases to the database | ||||
assert.deepEqual( | assert.deepEqual( | ||||
await registerAliases(testDb, confirmedUnsortedAliasTxs), | await registerAliases(testDb, allAliasTxs), | ||||
testAddressAliasesWithUnconfirmedTxs.validAliasTxs, | registeredAliasesCloneLessUnconfirmed, | ||||
); | ); | ||||
// Wipe the database after this unit test | // Wipe the database after this unit test | ||||
await testDb.dropDatabase(); | await testDb.dropDatabase(); | ||||
}); | }); | ||||
it('Correctly parses a 3-character alias transaction for correct registration tx format and registration fee', function () { | |||||
const registrationOutputScript = cashaddr.getOutputScriptFromAddress( | |||||
config.aliasConstants.registrationAddress, | |||||
); | |||||
const thisTx = { | |||||
txid: 'cf6e83f79e62a067e490472acac8290e8ca7698c786d7b1fcaadcc1a8e66aebf', | |||||
version: 2, | |||||
inputs: [ | |||||
{ | |||||
prevOut: { | |||||
txid: '37b13f4b51c6a3d67fd93f92bbb009a9fbfa20dfb6ae62333e564affa29db3a6', | |||||
outIdx: 2, | |||||
}, | |||||
inputScript: | |||||
'473044022035c5d8f7c3e6afba45e8b6b0e9fd306c3e54ab4c73662149011a7c56259ecaf202200cc08308335fa8cbc2a584b4c2fe12eff7daf7d210763a3bf923d1a5ecd0fbed412102c237f49dd4c812f27b09d69d4c8a4da12744fda8ad63ce151fed2a3f41fd8795', | |||||
outputScript: | |||||
'76a91476458db0ed96fe9863fc1ccec9fa2cfab884b0f688ac', | |||||
value: '24988', | |||||
sequenceNo: 4294967295, | |||||
}, | |||||
], | |||||
outputs: [ | |||||
{ | |||||
value: '0', | |||||
outputScript: | |||||
'6a042e78656300036966701508d37c4c809fe9840e7bfa77b86bd47163f6fb6c60', | |||||
}, | |||||
{ | |||||
value: '556', | |||||
outputScript: | |||||
'a914d37c4c809fe9840e7bfa77b86bd47163f6fb6c6087', | |||||
}, | |||||
{ | |||||
value: '23977', | |||||
outputScript: | |||||
'76a91476458db0ed96fe9863fc1ccec9fa2cfab884b0f688ac', | |||||
}, | |||||
], | |||||
lockTime: 0, | |||||
block: { | |||||
height: 792422, | |||||
hash: '00000000000000001021d99a747d141c636bac5a0438ef8af7927b9f0f531b8e', | |||||
timestamp: '1684345054', | |||||
}, | |||||
timeFirstSeen: '1684344696', | |||||
size: 265, | |||||
isCoinbase: false, | |||||
network: 'XEC', | |||||
}; | |||||
const expectedResult = { | |||||
address: 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07', | |||||
alias: 'ifp', | |||||
txid: 'cf6e83f79e62a067e490472acac8290e8ca7698c786d7b1fcaadcc1a8e66aebf', | |||||
blockheight: '792422', | |||||
}; | |||||
assert.deepEqual( | |||||
parseAliasTx( | |||||
thisTx, | |||||
config.aliasConstants, | |||||
registrationOutputScript, | |||||
), | |||||
expectedResult, | |||||
); | |||||
}); | |||||
}); | }); |