test/specs/generic/consensus/nano/NanoMempool.spec.js
describe('NanoMempool', () => {
it('will not push the same transaction twice', (done) => {
(async function () {
const mempool = new NanoMempool({height: 1});
const wallet = await Wallet.generate();
// Create a transaction
const transaction = await wallet.createTransaction(Address.unserialize(BufferUtils.fromBase64(Dummy.address1)), 543, 42, 1);
// Push the transaction for the first time
let result = await mempool.pushTransaction(transaction);
expect(result).toBe(true);
// Push the transaction for a second time, and expect the result to be false
result = await mempool.pushTransaction(transaction);
expect(result).toBe(false);
})().then(done, done.fail);
});
it('will always verify a transaction before accepting it', (done) => {
(async function () {
const blockchain = {height: 1};
const mempool = new NanoMempool(blockchain);
const wallet = await Wallet.generate();
// Create a transaction
let transaction = await wallet.createTransaction(Address.unserialize(BufferUtils.fromBase64(Dummy.address1)), 3523, 23, 1);
// Save the valid transaction signature and replace it with an invalid one
const validSignature = transaction.signature;
transaction.signature = new Signature(BufferUtils.fromBase64(Dummy.signature3));
// Push the transaction, this should fail (return false) because of the
// invalid signature
let result = await mempool.pushTransaction(transaction);
expect(result).toBe(false);
// Set the valid transaction signature to test different scenarios
transaction.signature = validSignature;
// Make sure the transaction fails due to being outside the window
blockchain.height = 10000;
transaction = await wallet.createTransaction(Address.unserialize(BufferUtils.fromBase64(Dummy.address1)), 3523, 23, 1);
result = await mempool.pushTransaction(transaction);
expect(result).toBe(false);
})().then(done, done.fail);
});
it('can push and get a valid transaction', (done) => {
(async function () {
const mempool = new NanoMempool({height: 1});
const wallet = await Wallet.generate();
// Create a transaction
const referenceTransaction = await wallet.createTransaction(Address.unserialize(BufferUtils.fromBase64(Dummy.address1)), 523,23,1);
// The transaction should be successfully pushed
const result = await mempool.pushTransaction(referenceTransaction);
expect(result).toBe(true);
// Get back the transaction and check that it is the same one we pushed before
const hash = referenceTransaction.hash();
const transaction = await mempool.getTransaction(hash);
expect(transaction).toBe(referenceTransaction);
})().then(done, done.fail);
});
it('can push 2 transactions from same user', (done) => {
(async () => {
const mempool = new NanoMempool({height: 1});
const wallet = await Wallet.generate();
// Create transactions
const t1 = await wallet.createTransaction(Address.unserialize(BufferUtils.fromBase64(Dummy.address1)), 50, 1, 1);
const t2 = await wallet.createTransaction(Address.unserialize(BufferUtils.fromBase64(Dummy.address1)), 100, 1, 1);
// The transaction should be successfully pushed
let result = await mempool.pushTransaction(t1);
expect(result).toBe(true);
// The transaction should be successfully pushed
result = await mempool.pushTransaction(t2);
expect(result).toBe(true);
// Get back the transactions and check that they are the same one we pushed before
expect(await mempool.getTransaction(t1.hash())).toBe(t1);
expect(await mempool.getTransaction(t2.hash())).toBe(t2);
})().then(done, done.fail);
});
it('can get a list of its transactions and can evict them', (done) => {
(async function () {
const mempool = new NanoMempool({height: 1});
// How many transactions should be used in this test
const numberOfTransactions = 5;
// We can only have one transaction per sender in the mempool,
// which means we need several different wallets in order to create
// several different transactions to push
const wallets = [];
for (let i = 0; i < numberOfTransactions; i++) {
const wallet = await Wallet.generate();
wallets.push(wallet);
}
// Push a bunch of transactions into the mempool
const referenceTransactions = [];
for (let i = 0; i < numberOfTransactions; i++) {
const transaction = await wallets[i].createTransaction(Address.unserialize(BufferUtils.fromBase64(Dummy.address1)), 234, 1, 1); // eslint-disable-line no-await-in-loop
const result = await mempool.pushTransaction(transaction); // eslint-disable-line no-await-in-loop
expect(result).toBe(true);
referenceTransactions.push(transaction);
}
// Check that the transactions were successfully pushed
let transactions = await mempool.getTransactions().sort((a, b) => a.compareBlockOrder(b));
referenceTransactions.sort((a, b) => a.compareBlockOrder(b));
expect(transactions).toEqual(referenceTransactions);
mempool.changeHead({header: {height: 1}}, referenceTransactions);
// Check that all the transactions were evicted
mempool.on('transactions-ready', async function() {
transactions = await mempool.getTransactions();
expect(transactions.length).toEqual(0);
});
})().then(done, done.fail);
});
it('can evict mined transactions', (done) => {
(async function () {
const mempool = new NanoMempool({height: 1});
const wallets = [];
for (let i = 0; i < 6; i++) {
const wallet = await Wallet.generate();
wallets.push(wallet);
}
// Push a bunch of transactions into the mempool
const referenceTransactions = [];
for (let i = 1; i < 6; i++) {
const transaction = await wallets[0].createTransaction(wallets[i].address, 1, 0, 1); // eslint-disable-line no-await-in-loop
const result = await mempool.pushTransaction(transaction); // eslint-disable-line no-await-in-loop
expect(result).toBe(true);
referenceTransactions.push(transaction);
}
referenceTransactions.sort((a, b) => a.compare(b));
mempool.changeHead({header: {height: 1}}, [referenceTransactions[2]]);
// Check that all the transactions were evicted
mempool.on('transactions-ready', async function() {
const transactions = await mempool.getTransactions();
transactions.sort((a, b) => a.compare(b));
expect(transactions.length).toEqual(4);
for (let i = 0; i < transactions.length; ++i) {
if (i < 2) {
expect(transactions[i].equals(referenceTransactions[i])).toBeTruthy();
} else {
expect(transactions[i].equals(referenceTransactions[i + 1])).toBeTruthy();
}
}
});
})().then(done, done.fail);
});
it('can evict transactions outside validity window', (done) => {
(async function () {
const mempool = new NanoMempool({height: 1});
const wallets = [];
for (let i = 0; i < 6; i++) {
const wallet = await Wallet.generate();
wallets.push(wallet);
}
// Push a bunch of transactions into the mempool
const referenceTransactions = [];
for (let i = 1; i < 6; i++) {
const transaction = await wallets[0].createTransaction(wallets[i].address, 1, 0, 1, i === 2 ? 1 : 10000); // eslint-disable-line no-await-in-loop
const result = await mempool.pushTransaction(transaction); // eslint-disable-line no-await-in-loop
expect(result).toBe(true);
referenceTransactions.push(transaction);
}
referenceTransactions.sort((a, b) => a.compare(b));
mempool.changeHead({header: {height: 10000}}, []);
// Check that all the transactions were evicted
mempool.on('transactions-ready', async function() {
const transactions = await mempool.getTransactions();
transactions.sort((a, b) => a.compare(b));
expect(transactions.length).toEqual(4);
for (let i = 0; i < transactions.length; ++i) {
if (i < 2) {
expect(transactions[i].equals(referenceTransactions[i])).toBeTruthy();
} else {
expect(transactions[i].equals(referenceTransactions[i + 1])).toBeTruthy();
}
}
});
})().then(done, done.fail);
});
it('can evict transactions by addresses', (done) => {
(async function () {
const mempool = new NanoMempool({height: 1});
/** @type {Array.<Wallet>} */
const wallets = [];
/** @type {Array.<Address>} */
const addresses = [];
for (let i = 0; i < 20; i++) {
const wallet = await Wallet.generate();
wallets.push(wallet);
addresses.push(wallet.address);
}
for (let i = 0; i < 20; i++) {
const transaction = wallets[i].createTransaction(wallets[(i + 1) % 20].address, 1, (i + 1) * 200, 1);
const result = await mempool.pushTransaction(transaction); // eslint-disable-line no-await-in-loop
expect(result).toBe(true);
}
let transactions = mempool.getTransactions();
expect(transactions.length).toBe(20);
mempool.evictExceptAddresses(addresses.slice(0, 10));
transactions = mempool.getTransactions();
expect(transactions.length).toBe(11);
})().then(done, done.fail);
});
});