diff --git a/__tests__/helpers/transaction-factory.ts b/__tests__/helpers/transaction-factory.ts index 1ca54e5de5..b0285575f6 100644 --- a/__tests__/helpers/transaction-factory.ts +++ b/__tests__/helpers/transaction-factory.ts @@ -193,7 +193,6 @@ export class TransactionFactory { Managers.configManager.setFromPreset(this.network); const transactions: T[] = []; - for (let i = 0; i < quantity; i++) { if (this.builder.constructor.name === "TransferBuilder") { // @FIXME: when we use any of the "withPassphrase*" methods the builder will diff --git a/__tests__/integration/core-api/v2/handlers/transactions.test.ts b/__tests__/integration/core-api/v2/handlers/transactions.test.ts index 37b32ef332..b113683f06 100644 --- a/__tests__/integration/core-api/v2/handlers/transactions.test.ts +++ b/__tests__/integration/core-api/v2/handlers/transactions.test.ts @@ -557,8 +557,8 @@ describe("API 2.0 - Transactions", () => { it.each([3, 5, 8])("should accept and broadcast %i transactions emptying a wallet", async txNumber => { const sender = delegates[txNumber]; // use txNumber so that we use a different delegate for each test case const receivers = generateWallets("testnet", 2); - const amountPlusFee = Math.floor(sender.balance / txNumber); - const lastAmountPlusFee = sender.balance - (txNumber - 1) * amountPlusFee; + const amountPlusFee = Math.floor(+sender.balance / txNumber); + const lastAmountPlusFee = +sender.balance - (txNumber - 1) * amountPlusFee; const transactions = TransactionFactory.transfer(receivers[0].address, amountPlusFee - transferFee) .withNetwork("testnet") @@ -591,8 +591,8 @@ describe("API 2.0 - Transactions", () => { async txNumber => { const sender = delegates[txNumber + 1]; // use txNumber + 1 so that we don't use the same delegates as the above test const receivers = generateWallets("testnet", 2); - const amountPlusFee = Math.floor(sender.balance / txNumber); - const lastAmountPlusFee = sender.balance - (txNumber - 1) * amountPlusFee + 1; + const amountPlusFee = Math.floor(+sender.balance / txNumber); + const lastAmountPlusFee = +sender.balance - (txNumber - 1) * amountPlusFee + 1; const transactions = TransactionFactory.transfer(receivers[0].address, amountPlusFee - transferFee) .withNetwork("testnet") diff --git a/__tests__/integration/core-blockchain/blockchain.test.ts b/__tests__/integration/core-blockchain/blockchain.test.ts index c90206d6d1..d616fbe21c 100644 --- a/__tests__/integration/core-blockchain/blockchain.test.ts +++ b/__tests__/integration/core-blockchain/blockchain.test.ts @@ -55,6 +55,14 @@ const addBlocks = async untilHeight => { } }; +const indexWalletWithSufficientBalance = (transaction: Interfaces.ITransaction): void => { + const walletManager = blockchain.database.walletManager; + + const wallet = walletManager.findByPublicKey(transaction.data.senderPublicKey); + wallet.balance = wallet.balance.abs().plus(transaction.data.amount.plus(transaction.data.fee)); + walletManager.reindex(wallet); +}; + describe("Blockchain", () => { beforeAll(async () => { container = await setUp(); @@ -91,19 +99,25 @@ describe("Blockchain", () => { describe("postTransactions", () => { it("should be ok", async () => { - const transactionsWithoutType2 = genesisBlock.transactions.filter(tx => tx.type !== 2); - blockchain.transactionPool.flush(); - await blockchain.postTransactions(transactionsWithoutType2); - const transactions = blockchain.transactionPool.getTransactions(0, 200); - expect(transactions.length).toBe(transactionsWithoutType2.length); + jest.spyOn(blockchain.transactionPool as any, "removeForgedTransactions").mockReturnValue([]); - expect(transactions).toIncludeAllMembers( - transactionsWithoutType2.map(transaction => transaction.serialized), - ); + for (const transaction of genesisBlock.transactions) { + indexWalletWithSufficientBalance(transaction); + } + + const transferTransactions = genesisBlock.transactions.filter(tx => tx.type === 0); + + await blockchain.postTransactions(transferTransactions); + const transactions = await blockchain.transactionPool.getTransactions(0, 200); + + expect(transactions.length).toBe(transferTransactions.length); + + expect(transactions).toIncludeAllMembers(transferTransactions.map(transaction => transaction.serialized)); blockchain.transactionPool.flush(); + jest.restoreAllMocks(); }); }); diff --git a/__tests__/unit/core-p2p/socket-server/versions/internal/handlers/transactions.test.ts b/__tests__/unit/core-p2p/socket-server/versions/internal/handlers/transactions.test.ts index 13753ba87a..5f623213fa 100644 --- a/__tests__/unit/core-p2p/socket-server/versions/internal/handlers/transactions.test.ts +++ b/__tests__/unit/core-p2p/socket-server/versions/internal/handlers/transactions.test.ts @@ -8,11 +8,11 @@ jest.mock("../../../../../../../packages/core-p2p/src/socket-server/utils/valida describe("Internal handlers - transactions", () => { describe("getUnconfirmedTransactions", () => { - it("should return unconfirmed transactions", () => { + it("should return unconfirmed transactions", async () => { transactionPool.getTransactionsForForging = jest.fn().mockReturnValue(["111"]); transactionPool.getPoolSize = jest.fn().mockReturnValue(1); - expect(getUnconfirmedTransactions()).toEqual({ poolSize: 1, transactions: ["111"] }); + expect(await getUnconfirmedTransactions()).toEqual({ poolSize: 1, transactions: ["111"] }); }); }); }); diff --git a/__tests__/unit/core-transaction-pool/__stubs__/connection.ts b/__tests__/unit/core-transaction-pool/__stubs__/connection.ts index 276497c99f..7758903031 100644 --- a/__tests__/unit/core-transaction-pool/__stubs__/connection.ts +++ b/__tests__/unit/core-transaction-pool/__stubs__/connection.ts @@ -70,7 +70,7 @@ export class Connection implements TransactionPool.IConnection { return; } - public getTransactionsForForging(blockSize: number): string[] { + public async getTransactionsForForging(blockSize: number): Promise { return []; } @@ -78,15 +78,11 @@ export class Connection implements TransactionPool.IConnection { return undefined; } - public getTransactions(start: number, size: number, maxBytes?: number): Buffer[] { + public async getTransactions(start: number, size: number, maxBytes?: number): Promise { return []; } - public getTransactionIdsForForging(start: number, size: number): string[] { - return undefined; - } - - public getTransactionsData(start: number, size: number, maxBytes?: number): Interfaces.ITransaction[] { + public async getTransactionIdsForForging(start: number, size: number): Promise { return undefined; } diff --git a/__tests__/unit/core-transaction-pool/connection.forging.test.ts b/__tests__/unit/core-transaction-pool/connection.forging.test.ts new file mode 100644 index 0000000000..f11d037282 --- /dev/null +++ b/__tests__/unit/core-transaction-pool/connection.forging.test.ts @@ -0,0 +1,510 @@ +import "jest-extended"; + +import "./mocks/core-container"; + +import bs58check from "bs58check"; +import ByteBuffer from "bytebuffer"; + +import { Wallets } from "@arkecosystem/core-state"; +import { Handlers } from "@arkecosystem/core-transactions"; +import { Constants, Crypto, Identities, Interfaces, Managers, Transactions, Utils } from "@arkecosystem/crypto"; +import { Connection } from "../../../packages/core-transaction-pool/src/connection"; +import { defaults } from "../../../packages/core-transaction-pool/src/defaults"; +import { Memory } from "../../../packages/core-transaction-pool/src/memory"; +import { Storage } from "../../../packages/core-transaction-pool/src/storage"; +import { WalletManager } from "../../../packages/core-transaction-pool/src/wallet-manager"; +import { TransactionFactory } from "../../helpers/transaction-factory"; +import { delegates } from "../../utils/fixtures/testnet/delegates"; + +let connection: Connection; +let memory: Memory; +let poolWalletManager: WalletManager; +let databaseWalletManager: Wallets.WalletManager; + +beforeAll(async () => { + Managers.configManager.setFromPreset("testnet"); + + memory = new Memory(); + poolWalletManager = new WalletManager(); + connection = new Connection({ + options: defaults, + walletManager: poolWalletManager, + memory, + storage: new Storage(), + }); + + await connection.make(); +}); + +const mockCurrentHeight = (height: number) => { + // @ts-ignore + jest.spyOn(memory, "currentHeight").mockReturnValue(height); + Managers.configManager.setHeight(height); +}; + +describe("Connection", () => { + beforeEach(() => { + mockCurrentHeight(1); + + connection.flush(); + poolWalletManager.reset(); + + databaseWalletManager = new Wallets.WalletManager(); + + for (let i = 0; i < delegates.length; i++) { + const { publicKey } = delegates[i]; + const wallet = databaseWalletManager.findByPublicKey(publicKey); + wallet.balance = Utils.BigNumber.make(100_000 * Constants.ARKTOSHI); + wallet.username = `delegate-${i + 1}`; + wallet.vote = publicKey; + + if (i === 50) { + wallet.secondPublicKey = Identities.PublicKey.fromPassphrase("second secret"); + } + + databaseWalletManager.reindex(wallet); + } + + databaseWalletManager.buildDelegateRanking(); + databaseWalletManager.buildVoteBalances(); + + // @ts-ignore + connection.databaseService.walletManager = databaseWalletManager; + + jest.restoreAllMocks(); + }); + + const addTransactionsToMemory = transactions => { + for (const tx of transactions) { + memory.remember(tx); + expect(memory.has(tx.id)).toBeTrue(); + } + expect(memory.count()).toBe(transactions.length); + }; + + const expectForgingTransactions = async ( + transactions: Interfaces.ITransaction[], + countGood: number, + ): Promise => { + addTransactionsToMemory(transactions); + + const forgingTransactions = await connection.getTransactionsForForging(100); + expect(forgingTransactions).toHaveLength(countGood); + expect(forgingTransactions).toEqual( + transactions.slice(transactions.length - countGood).map(({ serialized }) => serialized.toString("hex")), + ); + + return forgingTransactions; + }; + + const customSerialize = (transaction: Interfaces.ITransactionData, options: any = {}) => { + const buffer = new ByteBuffer(512, true); + const writeByte = (txField, value) => (options[txField] ? options[txField](buffer) : buffer.writeByte(value)); + const writeUint32 = (txField, value) => + options[txField] ? options[txField](buffer) : buffer.writeUint32(value); + const writeUint64 = (txField, value) => + options[txField] ? options[txField](buffer) : buffer.writeUint64(value); + const append = (txField, value, encoding = "utf8") => + options[txField] ? options[txField](buffer) : buffer.append(value, encoding); + + buffer.writeByte(0xff); // fill, to disambiguate from v1 + writeByte("version", 0x01); + writeByte("network", transaction.network); // ark = 0x17, devnet = 0x30 + writeByte("type", transaction.type); + writeUint32("timestamp", transaction.timestamp); + append("senderPublicKey", transaction.senderPublicKey, "hex"); + writeUint64("fee", +transaction.fee); + + if (options.vendorField) { + options.vendorField(buffer); + } else if (transaction.vendorField) { + const vf: Buffer = Buffer.from(transaction.vendorField, "utf8"); + buffer.writeByte(vf.length); + buffer.append(vf); + } else if (transaction.vendorFieldHex) { + buffer.writeByte(transaction.vendorFieldHex.length / 2); + buffer.append(transaction.vendorFieldHex, "hex"); + } else { + buffer.writeByte(0x00); + } + + // only for transfer right now + writeUint64("amount", +transaction.amount); + writeUint32("expiration", transaction.expiration || 0); + append("recipientId", bs58check.decode(transaction.recipientId)); + + // signatures + if (transaction.signature || options.signature) { + append("signature", transaction.signature, "hex"); + } + + const secondSignature: string = transaction.secondSignature || transaction.signSignature; + + if (secondSignature || options.secondSignature) { + append("secondSignature", secondSignature, "hex"); + } + + if (options.signatures) { + options.signatures(buffer); + } else if (transaction.signatures) { + if (transaction.version === 1 && Utils.isException(transaction)) { + buffer.append("ff", "hex"); // 0xff separator to signal start of multi-signature transactions + buffer.append(transaction.signatures.join(""), "hex"); + } else { + buffer.append(transaction.signatures.join(""), "hex"); + } + } + + return buffer.flip().toBuffer(); + }; + + describe("getTransactionsForForging", () => { + it("should call `TransactionFactory.fromBytes`", async () => { + const transactions = TransactionFactory.transfer().build(5); + const spy = jest.spyOn(Transactions.TransactionFactory, "fromBytes"); + await expectForgingTransactions(transactions, 5); + expect(spy).toHaveBeenCalled(); + }); + + it("should call `TransactionHandler.canBeApplied`", async () => { + const transactions = TransactionFactory.transfer().build(5); + const spy = jest.spyOn(Handlers.Registry.get(0), "canBeApplied"); + await expectForgingTransactions(transactions, 5); + expect(spy).toHaveBeenCalled(); + }); + + it("should call `removeForgedTransactions`", async () => { + const transactions = TransactionFactory.transfer().build(5); + const spy = jest.spyOn(connection as any, "removeForgedTransactions"); + await expectForgingTransactions(transactions, 5); + expect(spy).toHaveBeenCalled(); + }); + + it("should remove transactions that have malformed bytes", async () => { + const malformedBytesFn = [ + { version: (b: ByteBuffer) => b.writeUint64(1111111) }, + { network: (b: ByteBuffer) => b.writeUint64(1111111) }, + { type: (b: ByteBuffer) => b.writeUint64(1111111) }, + { timestamp: (b: ByteBuffer) => b.writeByte(0x01) }, + { senderPublicKey: (b: ByteBuffer) => b.writeByte(0x01) }, + { vendorField: (b: ByteBuffer) => b.writeByte(0x01) }, + { amount: (b: ByteBuffer) => b.writeByte(0x01) }, + { expiration: (b: ByteBuffer) => b.writeByte(0x01) }, + { recipientId: (b: ByteBuffer) => b.writeByte(0x01) }, + { signature: (b: ByteBuffer) => b.writeByte(0x01) }, + { secondSignature: (b: ByteBuffer) => b.writeByte(0x01) }, + { signatures: (b: ByteBuffer) => b.writeByte(0x01) }, + ]; + const transactions = TransactionFactory.transfer().build(malformedBytesFn.length + 5); + transactions.map((tx, i) => (tx.serialized = customSerialize(tx.data, malformedBytesFn[i] || {}))); + + await expectForgingTransactions(transactions, 5); + }); + + it("should remove transactions that have data from another network", async () => { + const transactions = TransactionFactory.transfer().build(5); + + transactions[0].serialized = customSerialize(transactions[0].data, { + network: (b: ByteBuffer) => b.writeUint8(3), + }); + + await expectForgingTransactions(transactions, 4); + }); + + it("should remove transactions that have wrong sender public keys", async () => { + const transactions = TransactionFactory.transfer().build(5); + + transactions[0].serialized = customSerialize(transactions[0].data, { + senderPublicKey: (b: ByteBuffer) => + b.append(Buffer.from(Identities.PublicKey.fromPassphrase("garbage"), "hex")), + }); + + await expectForgingTransactions(transactions, 4); + }); + + it("should remove transactions that have timestamps in the future", async () => { + const transactions = TransactionFactory.transfer().build(5); + + transactions[0].serialized = customSerialize(transactions[0].data, { + timestamp: (b: ByteBuffer) => b.writeUint32(Crypto.Slots.getTime() + 100 * 1000), + }); + + await expectForgingTransactions(transactions, 4); + }); + + it("should remove transactions that have different IDs when entering and leaving", async () => { + const transactions = TransactionFactory.transfer().build(5); + + transactions[0].data.id = "garbage"; + + await expectForgingTransactions(transactions, 4); + }); + + it("should remove transactions that have an unknown type", async () => { + const transactions = TransactionFactory.transfer().build(2); + transactions[0].serialized = customSerialize(transactions[0].data, { + version: (b: ByteBuffer) => b.writeUint8(255), + }); + + await expectForgingTransactions(transactions, 1); + }); + + it("should remove transactions that have a disabled type", async () => { + const transactions = TransactionFactory.transfer() + .withVersion(1) + .build(2); + transactions[0].serialized = customSerialize(transactions[0].data, { + version: (b: ByteBuffer) => b.writeUint8(4), + }); + + await expectForgingTransactions(transactions, 1); + }); + + it("should remove transactions that have have data of a another transaction type", async () => { + const handlers: Handlers.TransactionHandler[] = Handlers.Registry.all(); + const transactions: Interfaces.ITransaction[] = TransactionFactory.transfer().build(handlers.length); + + for (let i = 0; i < handlers.length; i++) { + expect(handlers[0].getConstructor().type).toEqual(0); + transactions[i].serialized = customSerialize(transactions[i].data, { + type: (b: ByteBuffer) => b.writeUint8(handlers[i].getConstructor().type), + }); + } + + await expectForgingTransactions(transactions.reverse(), 1); + }); + + it("should remove transactions that have negative numerical values", async () => { + const transactions = TransactionFactory.transfer().build(2); + transactions[0].serialized = customSerialize(transactions[0].data, { + fee: (b: ByteBuffer) => b.writeUint64(-999999), + amount: (b: ByteBuffer) => b.writeUint64(-999999), + }); + + await expectForgingTransactions(transactions, 1); + }); + + it("should remove transactions that have expired", async () => { + mockCurrentHeight(100); + + const transactions = TransactionFactory.transfer().build(5); + + transactions[0].serialized = customSerialize(transactions[0].data, { + expiration: (b: ByteBuffer) => b.writeByte(0x01), + }); + + await expectForgingTransactions(transactions, 4); + }); + + it("should remove transactions that have an amount or fee of 0", async () => { + const transactions = TransactionFactory.transfer().build(5); + + transactions[0].serialized = customSerialize(transactions[0].data, { + fee: (b: ByteBuffer) => b.writeByte(0x00), + }); + + transactions[1].serialized = customSerialize(transactions[0].data, { + amount: (b: ByteBuffer) => b.writeByte(0), + }); + + await expectForgingTransactions(transactions, 3); + }); + + it("should remove transactions that have been altered after entering the pool", async () => { + const transactions = TransactionFactory.transfer().build(2); + transactions[0].data.id = transactions[0].data.id + .split("") + .reverse() + .join(""); + + await expectForgingTransactions(transactions, 1); + }); + + it("should remove transactions that have an invalid version", async () => { + const transactions = TransactionFactory.transfer().build(2); + transactions[0].serialized = customSerialize(transactions[0].data, { + version: (b: ByteBuffer) => b.writeByte(0), + }); + + await expectForgingTransactions(transactions, 1); + }); + + it("should remove transactions that have a mismatch of expected and actual length of the vendor field", async () => { + const transactions = TransactionFactory.transfer().build(3); + transactions[0].serialized = customSerialize(transactions[0].data, { + vendorField: (b: ByteBuffer) => { + const vendorField = Buffer.from(transactions[0].data.vendorField, "utf8"); + b.writeByte(vendorField.length - 5); + b.append(vendorField); + }, + }); + + transactions[1].serialized = customSerialize(transactions[1].data, { + vendorField: (b: ByteBuffer) => { + const vendorField = Buffer.from(transactions[1].data.vendorField, "utf8"); + b.writeByte(vendorField.length + 5); + b.append(vendorField); + }, + }); + + await expectForgingTransactions(transactions, 1); + }); + + it("should remove transactions that have an invalid vendor field length", async () => { + const transactions = TransactionFactory.transfer().build(3); + transactions[0].serialized = customSerialize(transactions[0].data, { + vendorField: (b: ByteBuffer) => { + const vendorField = Buffer.from(transactions[0].data.vendorField, "utf8"); + b.writeByte(0); + b.append(vendorField); + }, + }); + + transactions[1].serialized = customSerialize(transactions[1].data, { + vendorField: (b: ByteBuffer) => { + b.writeByte(255); + }, + }); + + await expectForgingTransactions(transactions, 1); + }); + + it("should remove transactions that have an invalid vendor field", async () => { + const transactions = TransactionFactory.transfer().build(3); + transactions[0].serialized = customSerialize(transactions[0].data, { + vendorField: (b: ByteBuffer) => { + const vendorField = Buffer.from(transactions[0].data.vendorField.toUpperCase(), "utf8"); + b.writeByte(vendorField.length); + b.append(vendorField); + }, + }); + + transactions[1].serialized = customSerialize(transactions[1].data, { + vendorField: (b: ByteBuffer) => { + b.writeByte(255); + b.fill(0, b.offset); + }, + }); + + await expectForgingTransactions(transactions, 1); + }); + + it("should remove transactions that have additional bytes attached", async () => { + const transactions = TransactionFactory.transfer().build(5); + + const appendBytes = (transaction: Interfaces.ITransaction, garbage: Buffer) => { + const buffer = new ByteBuffer(512, true); + buffer.append(transaction.serialized); + buffer.append(garbage); + + transaction.serialized = buffer.flip().toBuffer(); + }; + + appendBytes(transactions[0], Buffer.from("garbage", "utf8")); + appendBytes(transactions[1], Buffer.from("ff", "hex")); + appendBytes(transactions[2], Buffer.from("00011111", "hex")); + appendBytes(transactions[3], Buffer.from("0001", "hex")); + + await expectForgingTransactions(transactions, 1); + }); + + it("should remove transactions that have malformed signatures", async () => { + const transactions = TransactionFactory.transfer().build(5); + + const makeSignature = (from: string): string => { + return Crypto.Hash.signECDSA( + Buffer.from(Crypto.HashAlgorithms.sha256(from)), + Identities.Keys.fromPassphrase("garbage"), + ); + }; + + transactions[0].serialized = customSerialize(transactions[0].data, { + signatures: (b: ByteBuffer) => { + b.append(Buffer.from(makeSignature("garbage").slice(25), "hex")); + }, + }); + + transactions[1].serialized = customSerialize(transactions[0].data, { + signatures: (b: ByteBuffer) => { + b.append(Buffer.from(makeSignature("garbage").repeat(2), "hex")); + }, + }); + + transactions[2].serialized = customSerialize(transactions[0].data, { + signatures: (b: ByteBuffer) => { + b.append(Buffer.from(makeSignature("garbage") + "affe", "hex")); + }, + }); + + await expectForgingTransactions(transactions, 2); + }); + + it("should remove transactions that have malformed second signatures", async () => { + const transactions = TransactionFactory.transfer() + .withPassphrasePair({ + passphrase: delegates[50].passphrase, + secondPassphrase: "second secret", + }) + .build(5); + + const appendBytes = (transaction: Interfaces.ITransaction, garbage: Buffer) => { + const buffer = new ByteBuffer(512, true); + buffer.append(transaction.serialized); + buffer.append(garbage); + + transaction.serialized = buffer.flip().toBuffer(); + }; + + appendBytes(transactions[0], Buffer.from("ff", "hex")); + appendBytes(transactions[1], Buffer.from("00", "hex")); + appendBytes(transactions[2], Buffer.from("0011001100", "hex")); + + await expectForgingTransactions(transactions, 2); + }); + + it("should remove transactions that have malformed multi signatures", async () => { + const transactions = TransactionFactory.transfer().build(5); + + const appendBytes = (transaction: Interfaces.ITransaction, garbage: Buffer) => { + const buffer = new ByteBuffer(512, true); + buffer.append(transaction.serialized); + buffer.append(garbage); + + transaction.serialized = buffer.flip().toBuffer(); + }; + + const makeSignature = (from: string): string => { + return Crypto.Hash.signECDSA( + Buffer.from(Crypto.HashAlgorithms.sha256(from)), + Identities.Keys.fromPassphrase("garbage"), + ); + }; + + appendBytes(transactions[0], Buffer.from("ff" + makeSignature("garbage").repeat(5), "hex")); + + await expectForgingTransactions(transactions, 4); + }); + + it("should remove transactions that have malformed multi signatures", async () => { + const transactions = TransactionFactory.transfer().build(5); + + const appendBytes = (transaction: Interfaces.ITransaction, garbage: Buffer) => { + const buffer = new ByteBuffer(512, true); + buffer.append(transaction.serialized); + buffer.append(garbage); + + transaction.serialized = buffer.flip().toBuffer(); + }; + + const makeSignature = (from: string): string => { + return Crypto.Hash.signECDSA( + Buffer.from(Crypto.HashAlgorithms.sha256(from)), + Identities.Keys.fromPassphrase("garbage"), + ); + }; + appendBytes(transactions[0], Buffer.from("ff" + makeSignature("garbage").repeat(5), "hex")); + + await expectForgingTransactions(transactions, 4); + }); + }); +}); diff --git a/__tests__/unit/core-transaction-pool/connection.test.ts b/__tests__/unit/core-transaction-pool/connection.test.ts index 661157a830..f11b85bff4 100644 --- a/__tests__/unit/core-transaction-pool/connection.test.ts +++ b/__tests__/unit/core-transaction-pool/connection.test.ts @@ -28,6 +28,15 @@ const delegatesSecrets = delegates.map(d => d.secret); let connection: Connection; let memory: Memory; +const indexWalletWithSufficientBalance = (transaction: Interfaces.ITransaction): void => { + // @ts-ignore + const walletManager = connection.databaseService.walletManager; + + const wallet = walletManager.findByPublicKey(transaction.data.senderPublicKey); + wallet.balance = wallet.balance.plus(transaction.data.amount.plus(transaction.data.fee)); + walletManager.reindex(wallet); +}; + beforeAll(async () => { memory = new Memory(); @@ -41,6 +50,10 @@ beforeAll(async () => { // @ts-ignore connection.databaseService.walletManager = new Wallets.WalletManager(); + for (const transaction of Object.values(mockData)) { + indexWalletWithSufficientBalance(transaction); + } + await connection.make(); }); @@ -134,7 +147,7 @@ describe("Connection", () => { connection.options.maxTransactionsInPool = maxTransactionsInPoolOrig; }); - it("should replace lowest fee transaction when adding 1 more transaction than maxTransactionsInPool", () => { + it("should replace lowest fee transaction when adding 1 more transaction than maxTransactionsInPool", async () => { expect(connection.getPoolSize()).toBe(0); connection.addTransactions([ @@ -150,7 +163,9 @@ describe("Connection", () => { connection.options.maxTransactionsInPool = 4; expect(connection.addTransactions([mockData.dummy5])).toEqual({}); - expect(connection.getTransactionIdsForForging(0, 10)).toEqual([ + + const transactionIds = await connection.getTransactionIdsForForging(0, 10); + expect(transactionIds).toEqual([ mockData.dummy1.id, mockData.dummy2.id, mockData.dummy3.id, @@ -402,7 +417,7 @@ describe("Connection", () => { }); describe("getTransactions", () => { - it("should return transactions within the specified range", () => { + it("should return transactions within the specified range", async () => { const transactions = [mockData.dummy1, mockData.dummy2]; addTransactions(transactions); @@ -412,9 +427,9 @@ describe("Connection", () => { } for (const i of [0, 1]) { - const retrieved = connection - .getTransactions(i, 1) - .map(serializedTx => Transactions.TransactionFactory.fromBytes(serializedTx)); + const retrieved = (await connection.getTransactions(i, 1)).map(serializedTx => + Transactions.TransactionFactory.fromBytes(serializedTx), + ); expect(retrieved.length).toBe(1); expect(retrieved[0]).toBeObject(); @@ -424,7 +439,7 @@ describe("Connection", () => { }); describe("getTransactionIdsForForging", () => { - it("should return an array of transactions ids", () => { + it("should return an array of transactions ids", async () => { addTransactions([ mockData.dummy1, mockData.dummy2, @@ -434,7 +449,7 @@ describe("Connection", () => { mockData.dummy6, ]); - const transactionIds = connection.getTransactionIdsForForging(0, 6); + const transactionIds = await connection.getTransactionIdsForForging(0, 6); expect(transactionIds).toBeArray(); expect(transactionIds[0]).toBe(mockData.dummy1.id); @@ -445,7 +460,7 @@ describe("Connection", () => { expect(transactionIds[5]).toBe(mockData.dummy6.id); }); - it("should only return transaction ids for transactions not exceeding the maximum payload size", () => { + it("should only return transaction ids for transactions not exceeding the maximum payload size", async () => { // @FIXME: Uhm excuse me, what the? mockData.dummyLarge1.data.signatures = mockData.dummyLarge2.data.signatures = [""]; for (let i = 0; i < connection.options.maxTransactionBytes * 0.6; i++) { @@ -467,43 +482,57 @@ describe("Connection", () => { addTransactions(transactions); - let transactionIds = connection.getTransactionIdsForForging(0, 7); + let transactionIds = await connection.getTransactionIdsForForging(0, 7); expect(transactionIds).toBeArray(); - expect(transactionIds.length).toBe(6); - expect(transactionIds[0]).toBe(mockData.dummyLarge1.id); - expect(transactionIds[1]).toBe(mockData.dummy3.id); - expect(transactionIds[2]).toBe(mockData.dummy4.id); - expect(transactionIds[3]).toBe(mockData.dummy5.id); - expect(transactionIds[4]).toBe(mockData.dummy6.id); - expect(transactionIds[5]).toBe(mockData.dummy7.id); + expect(transactionIds).toHaveLength(5); + expect(transactionIds[0]).toBe(mockData.dummy3.id); + expect(transactionIds[1]).toBe(mockData.dummy4.id); + expect(transactionIds[2]).toBe(mockData.dummy5.id); + expect(transactionIds[3]).toBe(mockData.dummy6.id); + expect(transactionIds[4]).toBe(mockData.dummy7.id); - connection.removeTransactionById(mockData.dummyLarge1.id); connection.removeTransactionById(mockData.dummy3.id); connection.removeTransactionById(mockData.dummy4.id); connection.removeTransactionById(mockData.dummy5.id); connection.removeTransactionById(mockData.dummy6.id); connection.removeTransactionById(mockData.dummy7.id); - transactionIds = connection.getTransactionIdsForForging(0, 7); + transactionIds = await connection.getTransactionIdsForForging(0, 7); expect(transactionIds).toBeArray(); - expect(transactionIds.length).toBe(1); - expect(transactionIds[0]).toBe(mockData.dummyLarge2.id); + expect(transactionIds).toHaveLength(0); }); }); describe("getTransactionsForForging", () => { - it("should return an array of transactions serialized", () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + it("should return an array of transactions serialized", async () => { const transactions = [mockData.dummy1, mockData.dummy2, mockData.dummy3, mockData.dummy4]; addTransactions(transactions); - const spy = jest.spyOn(Handlers.Registry.get(0), "canBeApplied").mockReturnValue(true); - const transactionsForForging = connection.getTransactionsForForging(4); - spy.mockRestore(); + jest.spyOn(Handlers.Registry.get(0), "canBeApplied").mockReturnValue(true); + const transactionsForForging = await connection.getTransactionsForForging(4); expect(transactionsForForging).toEqual(transactions.map(tx => tx.serialized.toString("hex"))); }); - it("should only return transactions not exceeding the maximum payload size", () => { + it("should only return unforged transactions", async () => { + const transactions = [mockData.dummy1, mockData.dummy2, mockData.dummy3]; + addTransactions(transactions); + + jest.spyOn(databaseService, "getForgedTransactionsIds").mockReturnValue([ + mockData.dummy1.id, + mockData.dummy3.id, + ]); + jest.spyOn(Handlers.Registry.get(0), "canBeApplied").mockReturnValue(true); + + const transactionsForForging = await connection.getTransactionsForForging(3); + expect(transactionsForForging.length).toBe(1); + expect(transactionsForForging[0]).toEqual(mockData.dummy2.serialized.toString("hex")); + }); + + it("should only return transactions not exceeding the maximum payload size", async () => { // @FIXME: Uhm excuse me, what the? mockData.dummyLarge1.data.signatures = mockData.dummyLarge2.data.signatures = [""]; for (let i = 0; i < connection.options.maxTransactionBytes * 0.6; i++) { @@ -525,8 +554,8 @@ describe("Connection", () => { addTransactions(transactions); - const spy = jest.spyOn(Handlers.Registry.get(0), "canBeApplied").mockReturnValue(true); - let transactionsForForging = connection.getTransactionsForForging(7); + jest.spyOn(Handlers.Registry.get(0), "canBeApplied").mockReturnValue(true); + let transactionsForForging = await connection.getTransactionsForForging(7); expect(transactionsForForging.length).toBe(6); expect(transactionsForForging[0]).toEqual(mockData.dummyLarge1.serialized.toString("hex")); @@ -543,8 +572,7 @@ describe("Connection", () => { connection.removeTransactionById(mockData.dummy6.id); connection.removeTransactionById(mockData.dummy7.id); - transactionsForForging = connection.getTransactionsForForging(7); - spy.mockRestore(); + transactionsForForging = await connection.getTransactionsForForging(7); expect(transactionsForForging.length).toBe(1); expect(transactionsForForging[0]).toEqual(mockData.dummyLarge2.serialized.toString("hex")); @@ -626,17 +654,19 @@ describe("Connection", () => { expect(+mockWallet.balance).toBe(+balanceBefore.minus(block2.totalFee)); }); - it("should remove transaction from pool if it's in the chained block", () => { + it("should remove transaction from pool if it's in the chained block", async () => { addTransactions([mockData.dummy2]); - expect(connection.getTransactions(0, 10)).toEqual([mockData.dummy2.serialized]); + let transactions = await connection.getTransactions(0, 10); + expect(transactions).toEqual([mockData.dummy2.serialized]); const chainedBlock = BlockFactory.fromData(block2); chainedBlock.transactions.push(mockData.dummy2); connection.acceptChainedBlock(chainedBlock); - expect(connection.getTransactions(0, 10)).toEqual([]); + transactions = await connection.getTransactions(0, 10); + expect(transactions).toEqual([]); }); it("should purge and block sender if throwIfApplyingFails() failed for a transaction in the chained block", () => { @@ -666,12 +696,18 @@ describe("Connection", () => { let findByPublicKey; let canBeApplied; let applyToSenderInPool; - const findByPublicKeyWallet = new Wallets.Wallet("thisIsAnAddressIMadeUpJustLikeThis"); + const findByPublicKeyWallet = new Wallets.Wallet("ANwc3YQe3EBjuE5sNRacP7fhkngAPaBW4Y"); + findByPublicKeyWallet.publicKey = "02778aa3d5b332965ea2a5ef6ac479ce2478535bc681a098dff1d683ff6eccc417"; + beforeEach(() => { const transactionHandler = Handlers.Registry.get(TransactionTypes.Transfer); canBeApplied = jest.spyOn(transactionHandler, "canBeApplied").mockReturnValue(true); applyToSenderInPool = jest.spyOn(transactionHandler, "applyToSenderInPool").mockReturnValue(); + (connection as any).databaseService.walletManager.findByPublicKey( + mockData.dummy1.data.senderPublicKey, + ).balance = Utils.BigNumber.ZERO; + jest.spyOn(connection.walletManager, "has").mockReturnValue(true); findByPublicKey = jest .spyOn(connection.walletManager, "findByPublicKey") @@ -687,7 +723,8 @@ describe("Connection", () => { it("should build wallets from transactions in the pool", async () => { addTransactions([mockData.dummy1]); - expect(connection.getTransactions(0, 10)).toEqual([mockData.dummy1.serialized]); + const transactions = await connection.getTransactions(0, 10); + expect(transactions).toEqual([mockData.dummy1.serialized]); await connection.buildWallets(); @@ -708,7 +745,7 @@ describe("Connection", () => { expect(getTransaction).toHaveBeenCalled(); expect(findByPublicKey).not.toHaveBeenCalled(); - expect(canBeApplied).not.toHaveBeenCalled(); + expect(canBeApplied).toHaveBeenCalled(); expect(applyToSenderInPool).not.toHaveBeenCalled(); }); @@ -728,7 +765,7 @@ describe("Connection", () => { findByPublicKeyWallet, (connection as any).databaseService.walletManager, ); - expect(purgeByPublicKey).toHaveBeenCalledWith(mockData.dummy1.data.senderPublicKey); + expect(purgeByPublicKey).not.toHaveBeenCalledWith(mockData.dummy1.data.senderPublicKey); }); }); @@ -768,6 +805,9 @@ describe("Connection", () => { it("save and restore transactions", async () => { expect(connection.getPoolSize()).toBe(0); + indexWalletWithSufficientBalance(mockData.dummy1); + indexWalletWithSufficientBalance(mockData.dummy4); + const transactions = [mockData.dummy1, mockData.dummy4]; addTransactions(transactions); @@ -792,6 +832,10 @@ describe("Connection", () => { jest.spyOn(databaseService, "getForgedTransactionsIds").mockReturnValue([mockData.dummy2.id]); + indexWalletWithSufficientBalance(mockData.dummy1); + indexWalletWithSufficientBalance(mockData.dummy2); + indexWalletWithSufficientBalance(mockData.dummy4); + const transactions = [mockData.dummy1, mockData.dummy2, mockData.dummy4]; addTransactions(transactions); @@ -840,7 +884,7 @@ describe("Connection", () => { return testTransactions; }; - it("multiple additions and retrievals", () => { + it("multiple additions and retrievals", async () => { // Abstract number which decides how many iterations are run by the test. // Increase it to run more iterations. const testSize = connection.options.syncInterval * 2; @@ -867,7 +911,7 @@ describe("Connection", () => { connection.hasExceededMaxTransactions(senderPublicKey); } connection.getTransaction(transaction.id); - connection.getTransactions(0, i); + await connection.getTransactions(0, i); } for (let i = 0; i < testSize; i++) { @@ -888,18 +932,24 @@ describe("Connection", () => { connection.addTransactions([testTransactions[0]]); }); - it("add many then get first few", () => { + it("add many then get first few", async () => { const nAdd = 2000; // We use a predictable random number calculator in order to get // a deterministic test. const rand = randomSeed.create("0"); - const testTransactions: Interfaces.ITransaction[] = generateTestTransactions(nAdd); + const testTransactions: Interfaces.ITransaction[] = []; for (let i = 0; i < nAdd; i++) { - // This will invalidate the transactions' signatures, not good, but irrelevant for this test. - testTransactions[i].data.fee = Utils.BigNumber.make(rand.intBetween(0.002 * SATOSHI, 2 * SATOSHI)); - testTransactions[i].serialized = Transactions.Utils.toBytes(testTransactions[i].data); + const transaction = TransactionFactory.transfer("AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5") + .withNetwork("unitnet") + .withFee(rand.intBetween(0.002 * SATOSHI, 2 * SATOSHI)) + .withPassphrase(String(i)) + .build()[0]; + + testTransactions.push(transaction); + + indexWalletWithSufficientBalance(transaction); } // console.time(`time to add ${nAdd}`) @@ -915,7 +965,7 @@ describe("Connection", () => { .map(f => f.toString()); // console.time(`time to get first ${nGet}`) - const topTransactionsSerialized = connection.getTransactions(0, nGet); + const topTransactionsSerialized = await connection.getTransactions(0, nGet); // console.timeEnd(`time to get first ${nGet}`) const topFeesReceived = topTransactionsSerialized.map(e => diff --git a/__tests__/utils/fixtures/testnet/delegates.ts b/__tests__/utils/fixtures/testnet/delegates.ts index d591d84077..34a618ced3 100644 --- a/__tests__/utils/fixtures/testnet/delegates.ts +++ b/__tests__/utils/fixtures/testnet/delegates.ts @@ -1,19 +1,20 @@ -import { Identities, Managers } from "@arkecosystem/crypto"; - -/** - * Get the testnet genesis delegates information - * @return {Array} array of objects like { secret, publicKey, address, balance } - */ +import { Identities, Managers, Utils } from "@arkecosystem/crypto"; Managers.configManager.setFromPreset("testnet"); import { secrets } from "../../config/testnet/delegates.json"; import { genesisBlock } from "../../config/testnet/genesisBlock"; -export const delegates: any = secrets.map(secret => { +export const delegates: Array<{ + secret: string; + passphrase: string; + publicKey: string; + address: string; + balance: Utils.BigNumber; +}> = secrets.map(secret => { const publicKey: string = Identities.PublicKey.fromPassphrase(secret); const address: string = Identities.Address.fromPassphrase(secret); - const balance = genesisBlock.transactions.find( + const balance: Utils.BigNumber = genesisBlock.transactions.find( transaction => transaction.recipientId === address && transaction.type === 0, ).amount; return { diff --git a/lerna.json b/lerna.json index 02e7a7b04d..28ea367424 100644 --- a/lerna.json +++ b/lerna.json @@ -3,5 +3,5 @@ "npmClient": "yarn", "packages": ["packages/*", "plugins/*"], "useWorkspaces": true, - "version": "2.4.0-next.9" + "version": "2.4.0-next.10" } diff --git a/packages/core-api/package.json b/packages/core-api/package.json index ed5cd8f672..37538b4c6b 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-api", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Public API for ARK Core", "license": "MIT", "contributors": [ @@ -21,12 +21,12 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-http-utils": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-transaction-pool": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-http-utils": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-transaction-pool": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@arkecosystem/utils": "^0.3.0", "@faustbrian/hapi-version": "^0.2.11", "@hapi/boom": "^7.4.2", diff --git a/packages/core-api/src/versions/1/transactions/controller.ts b/packages/core-api/src/versions/1/transactions/controller.ts index 3ae9523913..18507fccb7 100644 --- a/packages/core-api/src/versions/1/transactions/controller.ts +++ b/packages/core-api/src/versions/1/transactions/controller.ts @@ -33,11 +33,13 @@ export class TransactionsController extends Controller { try { const pagination = super.paginate(request); - const transactions = this.transactionPool - .getTransactions(pagination.offset, pagination.limit, 0) - .map(transaction => ({ - serialized: transaction, - })); + const transactions = (await this.transactionPool.getTransactions( + pagination.offset, + pagination.limit, + 0, + )).map(transaction => ({ + serialized: transaction, + })); return super.respondWith({ transactions: super.toCollection(request, transactions, "transaction"), diff --git a/packages/core-api/src/versions/2/transactions/controller.ts b/packages/core-api/src/versions/2/transactions/controller.ts index 68b1d994a4..facbbd51cc 100644 --- a/packages/core-api/src/versions/2/transactions/controller.ts +++ b/packages/core-api/src/versions/2/transactions/controller.ts @@ -59,10 +59,11 @@ export class TransactionsController extends Controller { try { const pagination = super.paginate(request); - const transactions = this.transactionPool.getTransactions(pagination.offset, pagination.limit); - const data = transactions.map(transaction => ({ - serialized: transaction.toString("hex"), - })); + const data = (await this.transactionPool.getTransactions(pagination.offset, pagination.limit)).map( + transaction => ({ + serialized: transaction.toString("hex"), + }), + ); return super.toPagination( request, diff --git a/packages/core-blockchain/package.json b/packages/core-blockchain/package.json index 16512ecc87..6ecac95f08 100644 --- a/packages/core-blockchain/package.json +++ b/packages/core-blockchain/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-blockchain", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Blockchain Manager for ARK Core", "license": "MIT", "contributors": [ @@ -22,14 +22,14 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-database": "^2.4.0-next.9", - "@arkecosystem/core-event-emitter": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-state": "^2.4.0-next.9", - "@arkecosystem/core-transactions": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-database": "^2.4.0-next.10", + "@arkecosystem/core-event-emitter": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-state": "^2.4.0-next.10", + "@arkecosystem/core-transactions": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "async": "^3.0.0", "delay": "^4.2.0", "immutable": "^4.0.0-rc.12", @@ -39,7 +39,7 @@ "xstate": "^4.6.0" }, "devDependencies": { - "@arkecosystem/core-p2p": "^2.4.0-next.9", + "@arkecosystem/core-p2p": "^2.4.0-next.10", "@types/async": "^2.4.2", "@types/lodash.get": "^4.4.6", "@types/pluralize": "^0.0.29", diff --git a/packages/core-container/package.json b/packages/core-container/package.json index 90cf6c917e..b2aa505191 100644 --- a/packages/core-container/package.json +++ b/packages/core-container/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-container", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Container for ARK Core", "license": "MIT", "contributors": [ @@ -19,8 +19,8 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@hapi/hoek": "^6.2.3", "@hapi/joi": "^15.0.3", "awilix": "^4.2.2", diff --git a/packages/core-database-postgres/package.json b/packages/core-database-postgres/package.json index fa87b35d66..2ec64e2acf 100644 --- a/packages/core-database-postgres/package.json +++ b/packages/core-database-postgres/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-database-postgres", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "PostgreSQL integration for ARK Core", "license": "MIT", "contributors": [ @@ -21,13 +21,13 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-database": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-state": "^2.4.0-next.9", - "@arkecosystem/core-transactions": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-database": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-state": "^2.4.0-next.10", + "@arkecosystem/core-transactions": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@arkecosystem/utils": "^0.3.0", "bluebird": "^3.5.5", "cpy-cli": "^2.0.0", diff --git a/packages/core-database/package.json b/packages/core-database/package.json index 466277a3b8..655403bd60 100644 --- a/packages/core-database/package.json +++ b/packages/core-database/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-database", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Database Interface for ARK Core", "license": "MIT", "contributors": [ @@ -22,12 +22,12 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-event-emitter": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-transactions": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-event-emitter": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-transactions": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@arkecosystem/utils": "^0.3.0", "lodash.clonedeep": "^4.5.0", "lodash.compact": "^3.0.1", diff --git a/packages/core-elasticsearch/package.json b/packages/core-elasticsearch/package.json index 44fd70e434..6daa436d92 100644 --- a/packages/core-elasticsearch/package.json +++ b/packages/core-elasticsearch/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-elasticsearch", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "A powerful Elasticsearch integration for ARK Core", "license": "MIT", "contributors": [ @@ -18,11 +18,11 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-event-emitter": "^2.4.0-next.9", - "@arkecosystem/core-http-utils": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-event-emitter": "^2.4.0-next.10", + "@arkecosystem/core-http-utils": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@elastic/elasticsearch": "^7.1.0", "@hapi/boom": "^7.4.2", "@hapi/joi": "^15.0.3", diff --git a/packages/core-error-tracker-airbrake/package.json b/packages/core-error-tracker-airbrake/package.json index b6eb44f7ee..9084707295 100644 --- a/packages/core-error-tracker-airbrake/package.json +++ b/packages/core-error-tracker-airbrake/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-error-tracker-airbrake", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Airbrake error tracker integration for ARK Core.", "license": "MIT", "contributors": [ @@ -18,7 +18,7 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", "airbrake-js": "^1.6.8" }, "engines": { diff --git a/packages/core-error-tracker-bugsnag/package.json b/packages/core-error-tracker-bugsnag/package.json index 4cf7a022b0..5fcdfb1773 100644 --- a/packages/core-error-tracker-bugsnag/package.json +++ b/packages/core-error-tracker-bugsnag/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-error-tracker-bugsnag", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Bugsnag error tracker integration for ARK Core.", "license": "MIT", "contributors": [ @@ -18,7 +18,7 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", "@bugsnag/js": "^6.2.0" }, "engines": { diff --git a/packages/core-error-tracker-raygun/package.json b/packages/core-error-tracker-raygun/package.json index de4881f187..e2483394e9 100644 --- a/packages/core-error-tracker-raygun/package.json +++ b/packages/core-error-tracker-raygun/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-error-tracker-raygun", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Raygun error tracker integration for ARK Core.", "license": "MIT", "contributors": [ @@ -18,7 +18,7 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", "raygun": "^0.10.1" }, "devDependencies": { diff --git a/packages/core-error-tracker-rollbar/package.json b/packages/core-error-tracker-rollbar/package.json index c2de59d856..849d3bfd62 100644 --- a/packages/core-error-tracker-rollbar/package.json +++ b/packages/core-error-tracker-rollbar/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-error-tracker-rollbar", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Rollbar error tracker integration for ARK Core.", "license": "MIT", "contributors": [ @@ -18,7 +18,7 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", "rollbar": "^2.7.1" }, "engines": { diff --git a/packages/core-error-tracker-sentry/package.json b/packages/core-error-tracker-sentry/package.json index b891539256..fbc2090995 100644 --- a/packages/core-error-tracker-sentry/package.json +++ b/packages/core-error-tracker-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-error-tracker-sentry", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Sentry error tracker integration for ARK Core.", "license": "MIT", "contributors": [ @@ -18,7 +18,7 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", "@sentry/node": "^5.3.0" }, "engines": { diff --git a/packages/core-event-emitter/package.json b/packages/core-event-emitter/package.json index c1d5c885c5..2d076bf255 100644 --- a/packages/core-event-emitter/package.json +++ b/packages/core-event-emitter/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-event-emitter", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Event Manager for ARK Core", "license": "MIT", "contributors": [ diff --git a/packages/core-exchange-json-rpc/package.json b/packages/core-exchange-json-rpc/package.json index 585412625b..ec23eeec42 100644 --- a/packages/core-exchange-json-rpc/package.json +++ b/packages/core-exchange-json-rpc/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-exchange-json-rpc", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Exchange JSON-RPC for ARK Core", "license": "MIT", "contributors": [ @@ -19,8 +19,8 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", "@arkecosystem/exchange-json-rpc": "^1.0.0-next.5" }, "engines": { diff --git a/packages/core-explorer/package.json b/packages/core-explorer/package.json index e1d625d31a..6eb4af062f 100644 --- a/packages/core-explorer/package.json +++ b/packages/core-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-explorer", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Explorer for ARK Core", "license": "MIT", "contributors": [ @@ -19,8 +19,8 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", "connect-history-api-fallback": "^1.6.0", "express": "^4.17.0" }, diff --git a/packages/core-forger/package.json b/packages/core-forger/package.json index 7938d4fb95..fa75ebfafc 100644 --- a/packages/core-forger/package.json +++ b/packages/core-forger/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-forger", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Forger for ARK Core", "license": "MIT", "contributors": [ @@ -21,12 +21,12 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-event-emitter": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-p2p": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-event-emitter": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-p2p": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "delay": "^4.2.0", "lodash.isempty": "^4.4.0", "lodash.uniq": "^4.5.0", diff --git a/packages/core-http-utils/package.json b/packages/core-http-utils/package.json index d75fdb9c8d..94537a9bea 100644 --- a/packages/core-http-utils/package.json +++ b/packages/core-http-utils/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-http-utils", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Http Utilities for ARK Core", "license": "MIT", "contributors": [ @@ -18,9 +18,9 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@hapi/boom": "^7.4.2", "@hapi/good": "^8.2.0", "@hapi/good-console": "^8.1.0", diff --git a/packages/core-interfaces/package.json b/packages/core-interfaces/package.json index cc97702d36..415925c671 100644 --- a/packages/core-interfaces/package.json +++ b/packages/core-interfaces/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-interfaces", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Interface types for essential ARK Core modules", "license": "MIT", "contributors": [ @@ -23,7 +23,7 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/crypto": "^2.4.0-next.10", "awilix": "^4.2.2", "dayjs": "^1.8.14" }, diff --git a/packages/core-interfaces/src/core-p2p/server.ts b/packages/core-interfaces/src/core-p2p/server.ts index 0b1055b2ec..1b62956eb3 100644 --- a/packages/core-interfaces/src/core-p2p/server.ts +++ b/packages/core-interfaces/src/core-p2p/server.ts @@ -21,3 +21,8 @@ export interface IForgingTransactions { poolSize: number; count: number; } + +export interface IUnconfirmedTransactions { + transactions: string[]; + poolSize: number; +} diff --git a/packages/core-interfaces/src/core-transaction-pool/connection.ts b/packages/core-interfaces/src/core-transaction-pool/connection.ts index f44c9e0a70..307d2aca03 100644 --- a/packages/core-interfaces/src/core-transaction-pool/connection.ts +++ b/packages/core-interfaces/src/core-transaction-pool/connection.ts @@ -28,11 +28,10 @@ export interface IConnection { buildWallets(): Promise; flush(): void; getTransaction(id: string): Interfaces.ITransaction; - getTransactionIdsForForging(start: number, size: number): string[]; - getTransactions(start: number, size: number, maxBytes?: number): Buffer[]; + getTransactionIdsForForging(start: number, size: number): Promise; + getTransactions(start: number, size: number, maxBytes?: number): Promise; getTransactionsByType(type: any): any; - getTransactionsData(start: number, size: number, maxBytes?: number): Interfaces.ITransaction[]; - getTransactionsForForging(blockSize: number): string[]; + getTransactionsForForging(blockSize: number): Promise; has(transactionId: string): any; hasExceededMaxTransactions(senderPublicKey: string): boolean; isSenderBlocked(senderPublicKey: string): boolean; diff --git a/packages/core-jest-matchers/package.json b/packages/core-jest-matchers/package.json index 1f9f3e4f77..bf6a2cc363 100644 --- a/packages/core-jest-matchers/package.json +++ b/packages/core-jest-matchers/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-jest-matchers", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Jest matchers for ARK Core", "license": "MIT", "contributors": [ @@ -20,8 +20,8 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@arkecosystem/utils": "^0.3.0", "bip39": "^3.0.2", "got": "^9.6.0", diff --git a/packages/core-logger-pino/package.json b/packages/core-logger-pino/package.json index 0e0b225411..8f5af47fc8 100644 --- a/packages/core-logger-pino/package.json +++ b/packages/core-logger-pino/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-logger-pino", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Pino integration for ARK Core", "license": "MIT", "contributors": [ @@ -19,9 +19,9 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-logger": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-logger": "^2.4.0-next.10", "pino": "^5.12.5", "pino-pretty": "^3.0.1", "pump": "^3.0.0", diff --git a/packages/core-logger-signale/package.json b/packages/core-logger-signale/package.json index 15d354a679..d6e1ad3c90 100644 --- a/packages/core-logger-signale/package.json +++ b/packages/core-logger-signale/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-logger-signale", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Signale integration for ARK Core", "license": "MIT", "contributors": [ @@ -19,8 +19,8 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-logger": "^2.4.0-next.9", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-logger": "^2.4.0-next.10", "signale": "^1.4.0" }, "devDependencies": { diff --git a/packages/core-logger-winston/package.json b/packages/core-logger-winston/package.json index 9c87f6f11b..a977899fe7 100644 --- a/packages/core-logger-winston/package.json +++ b/packages/core-logger-winston/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-logger-winston", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Winston Logger for ARK Core", "license": "MIT", "contributors": [ @@ -20,8 +20,8 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-logger": "^2.4.0-next.9", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-logger": "^2.4.0-next.10", "chalk": "^2.4.2", "colors": "^1.3.3", "dayjs": "^1.8.14", diff --git a/packages/core-logger/package.json b/packages/core-logger/package.json index aee3333f35..0ba1c3ac70 100644 --- a/packages/core-logger/package.json +++ b/packages/core-logger/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-logger", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Logger Manager for ARK Core", "license": "MIT", "contributors": [ @@ -19,8 +19,8 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", "lodash.isempty": "^4.4.0" }, "devDependencies": { diff --git a/packages/core-new-relic/package.json b/packages/core-new-relic/package.json index 62c88d4399..e5b5862a57 100644 --- a/packages/core-new-relic/package.json +++ b/packages/core-new-relic/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-new-relic", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "New Relic integration for ARK Core.", "license": "MIT", "contributors": [ @@ -18,7 +18,7 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", "newrelic": "^5.9.0" }, "devDependencies": { diff --git a/packages/core-p2p/package.json b/packages/core-p2p/package.json index 8c92e5db1a..ceb776d89d 100644 --- a/packages/core-p2p/package.json +++ b/packages/core-p2p/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-p2p", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "P2P API for ARK Core", "license": "MIT", "contributors": [ @@ -23,13 +23,13 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-event-emitter": "^2.4.0-next.9", - "@arkecosystem/core-http-utils": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-transaction-pool": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-event-emitter": "^2.4.0-next.10", + "@arkecosystem/core-http-utils": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-transaction-pool": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@hapi/boom": "^7.4.2", "@hapi/sntp": "^3.1.1", "ajv": "^6.10.0", diff --git a/packages/core-p2p/src/socket-server/versions/internal.ts b/packages/core-p2p/src/socket-server/versions/internal.ts index da9059ddbc..3771a91243 100644 --- a/packages/core-p2p/src/socket-server/versions/internal.ts +++ b/packages/core-p2p/src/socket-server/versions/internal.ts @@ -7,10 +7,7 @@ export const emitEvent = ({ req }): void => { app.resolvePlugin("event-emitter").emit(req.data.event, req.data.body); }; -export const getUnconfirmedTransactions = (): { - transactions: string[]; - poolSize: number; -} => { +export const getUnconfirmedTransactions = async (): Promise => { const blockchain = app.resolvePlugin("blockchain"); const { maxTransactions } = app.getConfig().getMilestone(blockchain.getLastBlock().data.height).block; @@ -19,7 +16,7 @@ export const getUnconfirmedTransactions = (): { ); return { - transactions: transactionPool.getTransactionsForForging(maxTransactions), + transactions: await transactionPool.getTransactionsForForging(maxTransactions), poolSize: transactionPool.getPoolSize(), }; }; diff --git a/packages/core-snapshots/package.json b/packages/core-snapshots/package.json index 27be30de30..7f5e5b37a4 100644 --- a/packages/core-snapshots/package.json +++ b/packages/core-snapshots/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-snapshots", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Provides live local streamed snapshots functionality for ARK Core", "license": "MIT", "contributors": [ @@ -20,11 +20,11 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-database-postgres": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-database-postgres": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "JSONStream": "^1.3.5", "cpy-cli": "^2.0.0", "fs-extra": "^8.0.1", diff --git a/packages/core-state/package.json b/packages/core-state/package.json index e4645da6d8..2f48739535 100644 --- a/packages/core-state/package.json +++ b/packages/core-state/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-state", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "State Management for ARK Core", "license": "MIT", "contributors": [ @@ -21,11 +21,11 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-transactions": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-transactions": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "immutable": "^4.0.0-rc.12", "lodash.clonedeep": "^4.5.0", "pluralize": "^7.0.0" diff --git a/packages/core-tester-cli/package.json b/packages/core-tester-cli/package.json index 26634db6d8..436539f820 100644 --- a/packages/core-tester-cli/package.json +++ b/packages/core-tester-cli/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-tester-cli", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Tester CLI for ARK Core", "license": "MIT", "contributors": [ @@ -28,9 +28,9 @@ "tester": "./bin/run" }, "dependencies": { - "@arkecosystem/core-forger": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-forger": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@oclif/command": "^1.5.13", "@oclif/config": "^1.13.0", "@oclif/plugin-help": "^2.1.6", diff --git a/packages/core-transaction-pool/package.json b/packages/core-transaction-pool/package.json index fa81005d7b..b3decc0449 100644 --- a/packages/core-transaction-pool/package.json +++ b/packages/core-transaction-pool/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-transaction-pool", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Transaction Pool Manager for ARK Core", "license": "MIT", "contributors": [ @@ -24,13 +24,13 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-database": "^2.4.0-next.9", - "@arkecosystem/core-event-emitter": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-state": "^2.4.0-next.9", - "@arkecosystem/core-transactions": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-database": "^2.4.0-next.10", + "@arkecosystem/core-event-emitter": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-state": "^2.4.0-next.10", + "@arkecosystem/core-transactions": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@types/better-sqlite3": "^5.4.0", "@types/fs-extra": "^7.0.0", "@types/pluralize": "^0.0.29", @@ -43,7 +43,7 @@ "pluralize": "^7.0.0" }, "devDependencies": { - "@arkecosystem/core-utils": "^2.4.0-next.9", + "@arkecosystem/core-utils": "^2.4.0-next.10", "@types/better-sqlite3": "^5.4.0", "@types/bip39": "^2.4.2", "@types/fs-extra": "^7.0.0", diff --git a/packages/core-transaction-pool/src/connection.ts b/packages/core-transaction-pool/src/connection.ts index ff3b5fd0e9..46f4b8c9ea 100644 --- a/packages/core-transaction-pool/src/connection.ts +++ b/packages/core-transaction-pool/src/connection.ts @@ -47,18 +47,18 @@ export class Connection implements TransactionPool.IConnection { this.memory.flush(); this.storage.connect(this.options.storage); - const all: Interfaces.ITransaction[] = this.storage.loadAll(); + let transactionsFromDisk: Interfaces.ITransaction[] = this.storage.loadAll(); + const validTransactions = await this.validateTransactions(transactionsFromDisk); - for (const transaction of all) { + transactionsFromDisk = transactionsFromDisk.filter(transaction => + validTransactions.includes(transaction.serialized.toString("hex")), + ); + + for (const transaction of transactionsFromDisk) { this.memory.remember(transaction, true); } this.purgeExpired(); - - const forgedIds: string[] = await this.databaseService.getForgedTransactionsIds(all.map(t => t.id)); - - this.removeTransactionsById(forgedIds); - this.purgeInvalidTransactions(); this.emitter.on("internal.milestone.changed", () => this.purgeInvalidTransactions()); @@ -138,89 +138,24 @@ export class Connection implements TransactionPool.IConnection { return this.memory.getById(id); } - public getTransactions(start: number, size: number, maxBytes?: number): Buffer[] { - return this.getTransactionsData(start, size, maxBytes).map( + public async getTransactions(start: number, size: number, maxBytes?: number): Promise { + return (await this.getValidTransactions(start, size, maxBytes)).map( (transaction: Interfaces.ITransaction) => transaction.serialized, ); } - public getTransactionsForForging(blockSize: number): string[] { - const transactionMemory: Interfaces.ITransaction[] = this.getTransactionsData( - 0, - blockSize, - this.options.maxTransactionBytes, + public async getTransactionsForForging(blockSize: number): Promise { + return (await this.getValidTransactions(0, blockSize, this.options.maxTransactionBytes)).map(transaction => + transaction.serialized.toString("hex"), ); - - const transactions: string[] = []; - - for (const transaction of transactionMemory) { - try { - const deserialized: Interfaces.ITransaction = Transactions.TransactionFactory.fromBytes( - transaction.serialized, - ); - - strictEqual(transaction.id, deserialized.id); - - const walletManager: State.IWalletManager = this.databaseService.walletManager; - const sender: State.IWallet = walletManager.findByPublicKey(transaction.data.senderPublicKey); - Handlers.Registry.get(transaction.type).canBeApplied(transaction, sender, walletManager); - - transactions.push(deserialized.serialized.toString("hex")); - } catch (error) { - this.removeTransactionById(transaction.id); - - this.logger.error(`Removed ${transaction.id} before forging because it is no longer valid.`); - } - } - - return transactions; } - public getTransactionIdsForForging(start: number, size: number): string[] { - return this.getTransactionsData(start, size, this.options.maxTransactionBytes).map( + public async getTransactionIdsForForging(start: number, size: number): Promise { + return (await this.getValidTransactions(start, size, this.options.maxTransactionBytes)).map( (transaction: Interfaces.ITransaction) => transaction.id, ); } - public getTransactionsData(start: number, size: number, maxBytes: number = 0): Interfaces.ITransaction[] { - this.purgeExpired(); - - const data: Interfaces.ITransaction[] = []; - - let transactionBytes: number = 0; - - let i = 0; - for (const transaction of this.memory.allSortedByFee()) { - if (i >= start + size) { - break; - } - - if (i >= start) { - let pushTransaction: boolean = false; - - if (maxBytes > 0) { - const transactionSize: number = JSON.stringify(transaction.data).length; - - if (transactionBytes + transactionSize <= maxBytes) { - transactionBytes += transactionSize; - pushTransaction = true; - } - } else { - pushTransaction = true; - } - - if (pushTransaction) { - data.push(transaction); - i++; - } - } else { - i++; - } - } - - return data; - } - public removeTransactionsForSender(senderPublicKey: string): void { for (const transaction of this.memory.getBySender(senderPublicKey)) { this.removeTransactionById(transaction.id); @@ -430,6 +365,50 @@ export class Connection implements TransactionPool.IConnection { return false; } + private async getValidTransactions( + start: number, + size: number, + maxBytes: number = 0, + ): Promise { + this.purgeExpired(); + + const data: Interfaces.ITransaction[] = []; + + let transactionBytes: number = 0; + + let i = 0; + for (const transaction of this.memory.allSortedByFee()) { + if (i >= start + size) { + break; + } + + if (i >= start) { + let pushTransaction: boolean = false; + + if (maxBytes > 0) { + const transactionSize: number = JSON.stringify(transaction.data).length; + + if (transactionBytes + transactionSize <= maxBytes) { + transactionBytes += transactionSize; + pushTransaction = true; + } + } else { + pushTransaction = true; + } + + if (pushTransaction) { + data.push(transaction); + i++; + } + } else { + i++; + } + } + + const validTransactions = await this.validateTransactions(data); + return data.filter(transaction => validTransactions.includes(transaction.serialized.toString("hex"))); + } + private addTransaction(transaction: Interfaces.ITransaction): TransactionPool.IAddTransactionResponse { if (this.has(transaction.id)) { this.logger.debug( @@ -495,6 +474,45 @@ export class Connection implements TransactionPool.IConnection { this.storage.bulkRemoveById(this.memory.pullDirtyRemoved()); } + private async validateTransactions(transactions: Interfaces.ITransaction[]): Promise { + const validTransactions: string[] = []; + const forgedIds: string[] = await this.removeForgedTransactions(transactions); + + const unforgedTransactions = transactions.filter( + (transaction: Interfaces.ITransaction) => !forgedIds.includes(transaction.id), + ); + + for (const transaction of unforgedTransactions) { + try { + const deserialized: Interfaces.ITransaction = Transactions.TransactionFactory.fromBytes( + transaction.serialized, + ); + + strictEqual(transaction.id, deserialized.id); + + const walletManager: State.IWalletManager = this.databaseService.walletManager; + const sender: State.IWallet = walletManager.findByPublicKey(transaction.data.senderPublicKey); + Handlers.Registry.get(transaction.type).canBeApplied(transaction, sender, walletManager); + + validTransactions.push(deserialized.serialized.toString("hex")); + } catch (error) { + this.removeTransactionById(transaction.id); + this.logger.error(`Removed ${transaction.id} before forging because it is no longer valid.`); + } + } + + return validTransactions; + } + + private async removeForgedTransactions(transactions: Interfaces.ITransaction[]): Promise { + const forgedIds: string[] = await this.databaseService.getForgedTransactionsIds( + transactions.map(({ id }) => id), + ); + + this.removeTransactionsById(forgedIds); + return forgedIds; + } + private purgeExpired(): void { this.purgeTransactions(ApplicationEvents.TransactionExpired, this.memory.getExpired()); } diff --git a/packages/core-transactions/package.json b/packages/core-transactions/package.json index f2c62fca80..3b66268c2a 100644 --- a/packages/core-transactions/package.json +++ b/packages/core-transactions/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-transactions", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Transaction Services for ARK Core", "license": "MIT", "contributors": [ @@ -19,9 +19,9 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-event-emitter": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-event-emitter": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "bs58check": "^2.1.2" }, "engines": { diff --git a/packages/core-utils/package.json b/packages/core-utils/package.json index 1e9ffe0718..345ca5114d 100644 --- a/packages/core-utils/package.json +++ b/packages/core-utils/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-utils", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Utilities for ARK Core", "license": "MIT", "contributors": [ @@ -18,9 +18,9 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "cli-table3": "^0.5.1", "dayjs": "^1.8.14", "fast-json-parse": "^1.0.3", diff --git a/packages/core-vote-report/package.json b/packages/core-vote-report/package.json index e8e8e39f20..b7480e921b 100644 --- a/packages/core-vote-report/package.json +++ b/packages/core-vote-report/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-vote-report", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Vote Report for ARK Core", "license": "MIT", "contributors": [ @@ -18,10 +18,10 @@ "prepublishOnly": "yarn build" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-http-utils": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-http-utils": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "handlebars": "^4.1.2", "lodash.sumby": "^4.6.0", "vision": "^5.4.4" diff --git a/packages/core-wallet-api/package.json b/packages/core-wallet-api/package.json index 49f4696d6c..b886ba784c 100644 --- a/packages/core-wallet-api/package.json +++ b/packages/core-wallet-api/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-wallet-api", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Wallet API for ARK Core", "license": "MIT", "contributors": [ @@ -21,10 +21,10 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-http-utils": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-http-utils": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", "@hapi/h2o2": "^8.3.0" }, "devDependencies": { diff --git a/packages/core-webhooks/package.json b/packages/core-webhooks/package.json index ddd9935a67..1982715150 100644 --- a/packages/core-webhooks/package.json +++ b/packages/core-webhooks/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core-webhooks", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Webhooks for ARK Core", "license": "MIT", "contributors": [ @@ -19,11 +19,11 @@ "pretest": "bash ../../scripts/pre-test.sh" }, "dependencies": { - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-event-emitter": "^2.4.0-next.9", - "@arkecosystem/core-http-utils": "^2.4.0-next.9", - "@arkecosystem/core-interfaces": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-event-emitter": "^2.4.0-next.10", + "@arkecosystem/core-http-utils": "^2.4.0-next.10", + "@arkecosystem/core-interfaces": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", "@hapi/boom": "^7.4.2", "@hapi/joi": "^15.0.3", "fs-extra": "^8.0.1", diff --git a/packages/core/package.json b/packages/core/package.json index eceda78e67..4722d2c6c5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/core", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Core of the ARK Blockchain", "license": "MIT", "contributors": [ @@ -44,22 +44,22 @@ "start:testnet": "cross-env CORE_PATH_CONFIG=./bin/config/testnet yarn ark core:run --env=test" }, "dependencies": { - "@arkecosystem/core-api": "^2.4.0-next.9", - "@arkecosystem/core-blockchain": "^2.4.0-next.9", - "@arkecosystem/core-container": "^2.4.0-next.9", - "@arkecosystem/core-database-postgres": "^2.4.0-next.9", - "@arkecosystem/core-event-emitter": "^2.4.0-next.9", - "@arkecosystem/core-forger": "^2.4.0-next.9", - "@arkecosystem/core-exchange-json-rpc": "^2.4.0-next.9", - "@arkecosystem/core-logger-pino": "^2.4.0-next.9", - "@arkecosystem/core-p2p": "^2.4.0-next.9", - "@arkecosystem/core-snapshots": "^2.4.0-next.9", - "@arkecosystem/core-state": "^2.4.0-next.9", - "@arkecosystem/core-transaction-pool": "^2.4.0-next.9", - "@arkecosystem/core-utils": "^2.4.0-next.9", - "@arkecosystem/core-wallet-api": "^2.4.0-next.9", - "@arkecosystem/core-webhooks": "^2.4.0-next.9", - "@arkecosystem/crypto": "^2.4.0-next.9", + "@arkecosystem/core-api": "^2.4.0-next.10", + "@arkecosystem/core-blockchain": "^2.4.0-next.10", + "@arkecosystem/core-container": "^2.4.0-next.10", + "@arkecosystem/core-database-postgres": "^2.4.0-next.10", + "@arkecosystem/core-event-emitter": "^2.4.0-next.10", + "@arkecosystem/core-exchange-json-rpc": "^2.4.0-next.10", + "@arkecosystem/core-forger": "^2.4.0-next.10", + "@arkecosystem/core-logger-pino": "^2.4.0-next.10", + "@arkecosystem/core-p2p": "^2.4.0-next.10", + "@arkecosystem/core-snapshots": "^2.4.0-next.10", + "@arkecosystem/core-state": "^2.4.0-next.10", + "@arkecosystem/core-transaction-pool": "^2.4.0-next.10", + "@arkecosystem/core-utils": "^2.4.0-next.10", + "@arkecosystem/core-wallet-api": "^2.4.0-next.10", + "@arkecosystem/core-webhooks": "^2.4.0-next.10", + "@arkecosystem/crypto": "^2.4.0-next.10", "@faustbrian/foreman": "^0.1.3", "@oclif/command": "^1.5.13", "@oclif/config": "^1.13.0", diff --git a/packages/crypto/package.json b/packages/crypto/package.json index 31f050d743..311938ab62 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -1,6 +1,6 @@ { "name": "@arkecosystem/crypto", - "version": "2.4.0-next.9", + "version": "2.4.0-next.10", "description": "Crypto utilities for the ARK Blockchain", "license": "MIT", "contributors": [ diff --git a/packages/crypto/src/transactions/deserializer.ts b/packages/crypto/src/transactions/deserializer.ts index 9fd67027dd..8c09ae4cf2 100644 --- a/packages/crypto/src/transactions/deserializer.ts +++ b/packages/crypto/src/transactions/deserializer.ts @@ -107,6 +107,10 @@ class Deserializer { const multiSignature: string = buf.readBytes(buf.limit - buf.offset).toString("hex"); transaction.signatures = [multiSignature]; } + + if (buf.remaining()) { + throw new MalformedTransactionBytesError(); + } } private deserializeSchnorr(transaction: ITransactionData, buf: ByteBuffer) {