From ae6cab5d34ce7eba7d31dbf2642cf63a2f9c5f47 Mon Sep 17 00:00:00 2001 From: supaiku <1311798+supaiku0@users.noreply.github.com> Date: Fri, 29 Nov 2019 04:07:32 +0100 Subject: [PATCH] feat(core-transactions): versioned transaction types --- .../delegate-registration.test.ts | 5 +- .../transactions/delegate-resignation.test.ts | 4 +- .../builders/transactions/htlc-claim.test.ts | 4 +- .../builders/transactions/htlc-lock.test.ts | 4 +- .../builders/transactions/htlc-refund.test.ts | 4 +- .../builders/transactions/ipfs.test.ts | 5 +- .../transactions/multi-payment.test.ts | 5 +- .../transactions/multi-signature.test.ts | 5 +- .../transactions/second-signature.test.ts | 5 +- .../builders/transactions/transfer.test.ts | 5 +- .../builders/transactions/vote.test.ts | 5 +- .../crypto/transactions/transaction.test.ts | 41 +++--- packages/core-api/src/controllers/node.ts | 2 +- .../core-api/src/controllers/transactions.ts | 4 +- .../src/processor/block-processor.ts | 12 +- .../core-database/src/database-service.ts | 8 +- .../transactions/bridgechain-registration.ts | 9 +- .../transactions/bridgechain-resignation.ts | 1 + .../src/transactions/bridgechain-update.ts | 5 +- .../src/transactions/business-registration.ts | 1 + .../src/transactions/business-resignation.ts | 1 + .../src/transactions/business-update.ts | 1 + packages/core-state/src/block-state.ts | 8 +- packages/core-state/src/state-builder.ts | 16 +- .../core-transaction-pool/src/connection.ts | 28 ++-- .../core-transaction-pool/src/dynamic-fee.ts | 6 +- .../src/pool-wallet-repository.ts | 4 +- .../core-transaction-pool/src/processor.ts | 8 +- .../src/handlers/handler-registry.ts | 138 +++++++++--------- .../core-transactions/src/handlers/index.ts | 10 +- .../{ => one}/delegate-registration.ts | 59 ++------ .../src/handlers/one/index.ts | 5 + .../one/multi-signature-registration.ts | 74 ++++++++++ .../second-signature-registration.ts} | 27 ++-- .../src/handlers/{ => one}/transfer.ts | 18 +-- .../src/handlers/{ => one}/vote.ts | 47 ++---- .../src/handlers/transaction.ts | 8 +- .../src/handlers/two/delegate-registration.ts | 56 +++++++ .../{ => two}/delegate-resignation.ts | 27 ++-- .../src/handlers/{ => two}/htlc-claim.ts | 24 +-- .../src/handlers/{ => two}/htlc-lock.ts | 18 +-- .../src/handlers/{ => two}/htlc-refund.ts | 27 ++-- .../src/handlers/two/index.ts | 11 ++ .../src/handlers/{ => two}/ipfs.ts | 21 +-- .../src/handlers/{ => two}/multi-payment.ts | 18 +-- .../multi-signature-registration.ts} | 96 +++++------- .../two/second-signature-registration.ts | 23 +++ .../src/handlers/two/transfer.ts | 20 +++ .../src/handlers/two/vote.ts | 43 ++++++ packages/crypto/src/errors.ts | 10 +- .../transactions/delegate-registration.ts | 8 +- .../transactions/delegate-resignation.ts | 8 +- .../builders/transactions/htlc-claim.ts | 8 +- .../builders/transactions/htlc-lock.ts | 8 +- .../builders/transactions/htlc-refund.ts | 8 +- .../builders/transactions/ipfs.ts | 8 +- .../builders/transactions/multi-payment.ts | 8 +- .../builders/transactions/multi-signature.ts | 10 +- .../builders/transactions/second-signature.ts | 8 +- .../builders/transactions/transfer.ts | 8 +- .../builders/transactions/vote.ts | 8 +- packages/crypto/src/transactions/registry.ts | 111 +++++++------- .../crypto/src/transactions/serializer.ts | 2 +- .../crypto/src/transactions/types/factory.ts | 24 ++- .../crypto/src/transactions/types/index.ts | 16 +- .../types/internal-transaction-type.ts | 10 +- .../src/transactions/types/multi-signature.ts | 119 --------------- .../types/{ => one}/delegate-registration.ts | 13 +- .../src/transactions/types/one/index.ts | 5 + .../types/one/multi-signature-registration.ts | 68 +++++++++ .../second-signature-registration.ts} | 13 +- .../transactions/types/{ => one}/transfer.ts | 17 ++- .../src/transactions/types/{ => one}/vote.ts | 11 +- .../src/transactions/types/transaction.ts | 1 + .../types/two/delegate-registration.ts | 5 + .../types/{ => two}/delegate-resignation.ts | 15 +- .../types/{ => two}/htlc-claim.ts | 17 ++- .../transactions/types/{ => two}/htlc-lock.ts | 17 ++- .../types/{ => two}/htlc-refund.ts | 17 ++- .../src/transactions/types/two/index.ts | 11 ++ .../src/transactions/types/{ => two}/ipfs.ts | 17 ++- .../types/{ => two}/multi-payment.ts | 19 +-- .../types/two/multi-signature-registration.ts | 63 ++++++++ .../two/second-signature-registration.ts | 5 + .../src/transactions/types/two/transfer.ts | 5 + .../crypto/src/transactions/types/two/vote.ts | 5 + packages/crypto/src/transactions/verifier.ts | 4 +- packages/crypto/src/validation/index.ts | 15 +- 88 files changed, 960 insertions(+), 741 deletions(-) rename packages/core-transactions/src/handlers/{ => one}/delegate-registration.ts (77%) create mode 100644 packages/core-transactions/src/handlers/one/index.ts create mode 100644 packages/core-transactions/src/handlers/one/multi-signature-registration.ts rename packages/core-transactions/src/handlers/{second-signature.ts => one/second-signature-registration.ts} (83%) rename packages/core-transactions/src/handlers/{ => one}/transfer.ts (87%) rename packages/core-transactions/src/handlers/{ => one}/vote.ts (80%) create mode 100644 packages/core-transactions/src/handlers/two/delegate-registration.ts rename packages/core-transactions/src/handlers/{ => two}/delegate-resignation.ts (93%) rename packages/core-transactions/src/handlers/{ => two}/htlc-claim.ts (96%) rename packages/core-transactions/src/handlers/{ => two}/htlc-lock.ts (96%) rename packages/core-transactions/src/handlers/{ => two}/htlc-refund.ts (95%) create mode 100644 packages/core-transactions/src/handlers/two/index.ts rename packages/core-transactions/src/handlers/{ => two}/ipfs.ts (93%) rename packages/core-transactions/src/handlers/{ => two}/multi-payment.ts (95%) rename packages/core-transactions/src/handlers/{multi-signature.ts => two/multi-signature-registration.ts} (56%) create mode 100644 packages/core-transactions/src/handlers/two/second-signature-registration.ts create mode 100644 packages/core-transactions/src/handlers/two/transfer.ts create mode 100644 packages/core-transactions/src/handlers/two/vote.ts delete mode 100644 packages/crypto/src/transactions/types/multi-signature.ts rename packages/crypto/src/transactions/types/{ => one}/delegate-registration.ts (75%) create mode 100644 packages/crypto/src/transactions/types/one/index.ts create mode 100644 packages/crypto/src/transactions/types/one/multi-signature-registration.ts rename packages/crypto/src/transactions/types/{second-signature.ts => one/second-signature-registration.ts} (71%) rename packages/crypto/src/transactions/types/{ => one}/transfer.ts (76%) rename packages/crypto/src/transactions/types/{ => one}/vote.ts (82%) create mode 100644 packages/crypto/src/transactions/types/two/delegate-registration.ts rename packages/crypto/src/transactions/types/{ => two}/delegate-resignation.ts (62%) rename packages/crypto/src/transactions/types/{ => two}/htlc-claim.ts (74%) rename packages/crypto/src/transactions/types/{ => two}/htlc-lock.ts (80%) rename packages/crypto/src/transactions/types/{ => two}/htlc-refund.ts (72%) create mode 100644 packages/crypto/src/transactions/types/two/index.ts rename packages/crypto/src/transactions/types/{ => two}/ipfs.ts (77%) rename packages/crypto/src/transactions/types/{ => two}/multi-payment.ts (79%) create mode 100644 packages/crypto/src/transactions/types/two/multi-signature-registration.ts create mode 100644 packages/crypto/src/transactions/types/two/second-signature-registration.ts create mode 100644 packages/crypto/src/transactions/types/two/transfer.ts create mode 100644 packages/crypto/src/transactions/types/two/vote.ts diff --git a/__tests__/unit/crypto/transactions/builders/transactions/delegate-registration.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/delegate-registration.test.ts index 89a0751275..fb7ef5e370 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/delegate-registration.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/delegate-registration.test.ts @@ -2,7 +2,8 @@ import "jest-extended"; import { configManager } from "@packages/crypto/src/managers"; import { TransactionType } from "@packages/crypto/src/enums"; -import { DelegateRegistrationTransaction, Utils } from "@packages/crypto/src/transactions"; +import { Utils } from "@packages/crypto/src/transactions"; +import { Two } from "@packages/crypto/src/transactions/types"; import { BuilderFactory } from "@packages/crypto/src/transactions/builders"; import { DelegateRegistrationBuilder } from "@packages/crypto/src/transactions/builders/transactions/delegate-registration"; import { BigNumber } from "@packages/crypto/src/utils"; @@ -49,7 +50,7 @@ describe("Delegate Registration Transaction", () => { it("should have its specific properties", () => { expect(builder).toHaveProperty("data.type", TransactionType.DelegateRegistration); expect(builder).toHaveProperty("data.amount", BigNumber.ZERO); - expect(builder).toHaveProperty("data.fee", DelegateRegistrationTransaction.staticFee()); + expect(builder).toHaveProperty("data.fee", Two.DelegateRegistrationTransaction.staticFee()); expect(builder).toHaveProperty("data.recipientId", undefined); expect(builder).toHaveProperty("data.senderPublicKey", undefined); expect(builder).toHaveProperty("data.asset", { delegate: {} }); diff --git a/__tests__/unit/crypto/transactions/builders/transactions/delegate-resignation.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/delegate-resignation.test.ts index 97c3b7351b..9c57a39010 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/delegate-resignation.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/delegate-resignation.test.ts @@ -2,10 +2,10 @@ import "jest-extended"; import { configManager } from "@packages/crypto/src/managers"; import { TransactionType } from "@packages/crypto/src/enums"; -import { DelegateResignationTransaction } from "@packages/crypto/src/transactions/"; import { BuilderFactory } from "@packages/crypto/src/transactions/builders"; import { DelegateResignationBuilder } from "@packages/crypto/src/transactions/builders/transactions/delegate-resignation"; import { BigNumber } from "@packages/crypto/src/utils"; +import { Two } from "@packages/crypto/src/transactions/types"; import { Generators } from "@packages/core-test-framework/src"; @@ -40,7 +40,7 @@ describe("Delegate Resignation Transaction", () => { it("should have its specific properties", () => { expect(builder).toHaveProperty("data.type", TransactionType.DelegateResignation); expect(builder).toHaveProperty("data.amount", BigNumber.ZERO); - expect(builder).toHaveProperty("data.fee", DelegateResignationTransaction.staticFee()); + expect(builder).toHaveProperty("data.fee", Two.DelegateResignationTransaction.staticFee()); expect(builder).toHaveProperty("data.senderPublicKey", undefined); }); diff --git a/__tests__/unit/crypto/transactions/builders/transactions/htlc-claim.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/htlc-claim.test.ts index c145ebb10d..3af15c56c2 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/htlc-claim.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/htlc-claim.test.ts @@ -4,7 +4,7 @@ import { configManager } from "@packages/crypto/src/managers"; import { TransactionType } from "@packages/crypto/src/enums"; import { BuilderFactory } from "@packages/crypto/src/transactions"; import { HtlcClaimBuilder } from "@packages/crypto/src/transactions/builders/transactions/htlc-claim"; -import { HtlcClaimTransaction } from "@packages/crypto/src/transactions/types/htlc-claim"; +import { Two } from "@packages/crypto/src/transactions/types"; import { BigNumber } from "@packages/crypto/src/utils"; import { Generators } from "@packages/core-test-framework/src"; @@ -22,7 +22,7 @@ beforeEach(() => { describe("Htlc claim Transaction", () => { it("should have its specific properties", () => { expect(builder).toHaveProperty("data.type", TransactionType.HtlcClaim); - expect(builder).toHaveProperty("data.fee", HtlcClaimTransaction.staticFee()); + expect(builder).toHaveProperty("data.fee", Two.HtlcClaimTransaction.staticFee()); expect(builder).toHaveProperty("data.amount", BigNumber.make(0)); expect(builder).toHaveProperty("data.asset", {}); }); diff --git a/__tests__/unit/crypto/transactions/builders/transactions/htlc-lock.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/htlc-lock.test.ts index 1769993c32..26aa9c4615 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/htlc-lock.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/htlc-lock.test.ts @@ -4,7 +4,7 @@ import { configManager } from "@packages/crypto/src/managers"; import { HtlcLockExpirationType, TransactionType } from "@packages/crypto/src/enums"; import { BuilderFactory } from "@packages/crypto/src/transactions"; import { HtlcLockBuilder } from "@packages/crypto/src/transactions/builders/transactions/htlc-lock"; -import { HtlcLockTransaction } from "@packages/crypto/src/transactions/types/htlc-lock"; +import { Two } from "@packages/crypto/src/transactions/types"; import { Generators } from "@packages/core-test-framework/src"; @@ -23,7 +23,7 @@ beforeEach(() => { describe("Htlc lock Transaction", () => { it("should have its specific properties", () => { expect(builder).toHaveProperty("data.type", TransactionType.HtlcLock); - expect(builder).toHaveProperty("data.fee", HtlcLockTransaction.staticFee()); + expect(builder).toHaveProperty("data.fee", Two.HtlcLockTransaction.staticFee()); expect(builder).toHaveProperty("data.asset", {}); }); diff --git a/__tests__/unit/crypto/transactions/builders/transactions/htlc-refund.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/htlc-refund.test.ts index 697fe2f4fc..92bc488e55 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/htlc-refund.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/htlc-refund.test.ts @@ -4,7 +4,7 @@ import { configManager } from "@packages/crypto/src/managers"; import { TransactionType } from "@packages/crypto/src/enums"; import { BuilderFactory } from "@packages/crypto/src/transactions"; import { HtlcRefundBuilder } from "@packages/crypto/src/transactions/builders/transactions/htlc-refund"; -import { HtlcRefundTransaction } from "@packages/crypto/src/transactions/types/htlc-refund"; +import { Two } from "@packages/crypto/src/transactions/types"; import { BigNumber } from "@packages/crypto/src/utils"; import { Generators } from "@packages/core-test-framework/src"; @@ -22,7 +22,7 @@ beforeEach(() => { describe("Htlc refund Transaction", () => { it("should have its specific properties", () => { expect(builder).toHaveProperty("data.type", TransactionType.HtlcRefund); - expect(builder).toHaveProperty("data.fee", HtlcRefundTransaction.staticFee()); + expect(builder).toHaveProperty("data.fee", Two.HtlcRefundTransaction.staticFee()); expect(builder).toHaveProperty("data.amount", BigNumber.make(0)); expect(builder).toHaveProperty("data.asset", {}); }); diff --git a/__tests__/unit/crypto/transactions/builders/transactions/ipfs.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/ipfs.test.ts index 0f149f713e..0328c20fe4 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/ipfs.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/ipfs.test.ts @@ -3,7 +3,8 @@ import "jest-extended"; import { configManager } from "@packages/crypto/src/managers"; import { Utils } from "@arkecosystem/crypto"; import { TransactionType } from "@packages/crypto/src/enums"; -import { BuilderFactory, IpfsTransaction } from "@packages/crypto/src/transactions"; +import { BuilderFactory } from "@packages/crypto/src/transactions"; +import { Two } from "@packages/crypto/src/transactions/types"; import { IPFSBuilder } from "@packages/crypto/src/transactions/builders/transactions/ipfs"; import { Generators } from "@packages/core-test-framework/src"; @@ -21,7 +22,7 @@ beforeEach(() => { describe("IPFS Transaction", () => { it("should have its specific properties", () => { expect(builder).toHaveProperty("data.type", TransactionType.Ipfs); - expect(builder).toHaveProperty("data.fee", IpfsTransaction.staticFee()); + expect(builder).toHaveProperty("data.fee", Two.IpfsTransaction.staticFee()); expect(builder).toHaveProperty("data.amount", Utils.BigNumber.make(0)); expect(builder).toHaveProperty("data.asset", {}); }); diff --git a/__tests__/unit/crypto/transactions/builders/transactions/multi-payment.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/multi-payment.test.ts index 8d1973b3ab..7ed322f3dd 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/multi-payment.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/multi-payment.test.ts @@ -3,8 +3,9 @@ import "jest-extended"; import { configManager } from "@packages/crypto/src/managers"; import { TransactionType } from "@packages/crypto/src/enums"; import { MaximumPaymentCountExceededError } from "@packages/crypto/src/errors"; -import { BuilderFactory, MultiPaymentTransaction } from "@packages/crypto/src/transactions"; +import { BuilderFactory } from "@packages/crypto/src/transactions"; import { MultiPaymentBuilder } from "@packages/crypto/src/transactions/builders/transactions/multi-payment"; +import { Two } from "@packages/crypto/src/transactions/types"; import { BigNumber } from "@packages/crypto/src/utils"; import { Generators } from "@packages/core-test-framework/src"; @@ -22,7 +23,7 @@ beforeEach(() => { describe("Multi Payment Transaction", () => { it("should have its specific properties", () => { expect(builder).toHaveProperty("data.type", TransactionType.MultiPayment); - expect(builder).toHaveProperty("data.fee", MultiPaymentTransaction.staticFee()); + expect(builder).toHaveProperty("data.fee", Two.MultiPaymentTransaction.staticFee()); expect(builder).toHaveProperty("data.asset.payments", []); expect(builder).toHaveProperty("data.vendorField", undefined); }); diff --git a/__tests__/unit/crypto/transactions/builders/transactions/multi-signature.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/multi-signature.test.ts index 163857462d..ac24efbac2 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/multi-signature.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/multi-signature.test.ts @@ -3,7 +3,8 @@ import "jest-extended"; import { configManager } from "@packages/crypto/src/managers"; import { TransactionType } from "@packages/crypto/src/enums"; import { TransactionVersionError } from "@packages/crypto/src/errors"; -import { BuilderFactory, MultiSignatureRegistrationTransaction } from "@packages/crypto/src/transactions"; +import { BuilderFactory } from "@packages/crypto/src/transactions"; +import { Two } from "@packages/crypto/src/transactions/types"; import { MultiSignatureBuilder } from "@packages/crypto/src/transactions/builders/transactions/multi-signature"; import * as Utils from "@packages/crypto/src/utils"; @@ -71,7 +72,7 @@ describe("Multi Signature Transaction", () => { }); describe("multiSignatureAsset", () => { - const multiSignatureFee = MultiSignatureRegistrationTransaction.staticFee(); + const multiSignatureFee = Two.MultiSignatureRegistrationTransaction.staticFee(); const multiSignature = { publicKeys: ["key a", "key b", "key c"], min: 1, diff --git a/__tests__/unit/crypto/transactions/builders/transactions/second-signature.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/second-signature.test.ts index b7bd13db5b..93e0cba219 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/second-signature.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/second-signature.test.ts @@ -4,7 +4,8 @@ import { configManager } from "@packages/crypto/src/managers"; import { Utils } from "@arkecosystem/crypto"; import { TransactionType } from "@packages/crypto/src/enums"; import { Keys } from "@packages/crypto/src/identities"; -import { BuilderFactory, SecondSignatureRegistrationTransaction } from "@packages/crypto/src/transactions"; +import { BuilderFactory } from "@packages/crypto/src/transactions"; +import { Two } from "@packages/crypto/src/transactions/types"; import { SecondSignatureBuilder } from "@packages/crypto/src/transactions/builders/transactions/second-signature"; import { Factories, Generators } from "@packages/core-test-framework/src"; @@ -36,7 +37,7 @@ describe("Second Signature Transaction", () => { it("should have its specific properties", () => { expect(builder).toHaveProperty("data.type", TransactionType.SecondSignature); - expect(builder).toHaveProperty("data.fee", SecondSignatureRegistrationTransaction.staticFee()); + expect(builder).toHaveProperty("data.fee", Two.SecondSignatureRegistrationTransaction.staticFee()); expect(builder).toHaveProperty("data.amount", Utils.BigNumber.make(0)); expect(builder).toHaveProperty("data.recipientId", undefined); expect(builder).toHaveProperty("data.senderPublicKey", undefined); diff --git a/__tests__/unit/crypto/transactions/builders/transactions/transfer.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/transfer.test.ts index 77d10ae616..deab81b62f 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/transfer.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/transfer.test.ts @@ -6,7 +6,8 @@ import { configManager } from "@packages/crypto/src/managers"; import { TransactionType } from "@packages/crypto/src/enums"; import { Keys, WIF } from "@packages/crypto/src/identities"; import { devnet } from "@packages/crypto/src/networks"; -import { BuilderFactory, TransferTransaction } from "@packages/crypto/src/transactions"; +import { BuilderFactory } from "@packages/crypto/src/transactions"; +import { Two } from "@packages/crypto/src/transactions/types"; import { TransferBuilder } from "@packages/crypto/src/transactions/builders/transactions/transfer"; import { Factories, Generators } from "@packages/core-test-framework/src"; @@ -102,7 +103,7 @@ describe("Transfer Transaction", () => { it("should have its specific properties", () => { expect(builder).toHaveProperty("data.type", TransactionType.Transfer); - expect(builder).toHaveProperty("data.fee", TransferTransaction.staticFee()); + expect(builder).toHaveProperty("data.fee", Two.TransferTransaction.staticFee()); expect(builder).toHaveProperty("data.amount", Utils.BigNumber.make(0)); expect(builder).toHaveProperty("data.recipientId", undefined); expect(builder).toHaveProperty("data.senderPublicKey", undefined); diff --git a/__tests__/unit/crypto/transactions/builders/transactions/vote.test.ts b/__tests__/unit/crypto/transactions/builders/transactions/vote.test.ts index a447fbf107..7cdfeeaa68 100644 --- a/__tests__/unit/crypto/transactions/builders/transactions/vote.test.ts +++ b/__tests__/unit/crypto/transactions/builders/transactions/vote.test.ts @@ -5,7 +5,8 @@ import { Factories, Generators } from "@packages/core-test-framework/src"; import { configManager } from "@packages/crypto/src/managers"; import { TransactionType } from "@packages/crypto/src/enums"; import { Keys } from "@packages/crypto/src/identities"; -import { BuilderFactory, VoteTransaction } from "@packages/crypto/src/transactions"; +import { BuilderFactory } from "@packages/crypto/src/transactions"; +import { Two } from "@packages/crypto/src/transactions/types"; import { VoteBuilder } from "@packages/crypto/src/transactions/builders/transactions/vote"; import * as Utils from "@packages/crypto/src/utils"; @@ -48,7 +49,7 @@ describe("Vote Transaction", () => { it("should have its specific properties", () => { expect(builder).toHaveProperty("data.type", TransactionType.Vote); - expect(builder).toHaveProperty("data.fee", VoteTransaction.staticFee()); + expect(builder).toHaveProperty("data.fee", Two.VoteTransaction.staticFee()); expect(builder).toHaveProperty("data.amount", Utils.BigNumber.make(0)); expect(builder).toHaveProperty("data.recipientId", undefined); expect(builder).toHaveProperty("data.senderPublicKey", undefined); diff --git a/__tests__/unit/crypto/transactions/transaction.test.ts b/__tests__/unit/crypto/transactions/transaction.test.ts index 7e5e38cdf4..d1a81432b6 100644 --- a/__tests__/unit/crypto/transactions/transaction.test.ts +++ b/__tests__/unit/crypto/transactions/transaction.test.ts @@ -3,17 +3,10 @@ import "jest-extended"; import { configManager } from "../../../../packages/crypto/src/managers"; import { devnet } from "../../../../packages/crypto/src/networks"; import { - DelegateRegistrationTransaction, - DelegateResignationTransaction, - IpfsTransaction, - MultiPaymentTransaction, - MultiSignatureRegistrationTransaction, - SecondSignatureRegistrationTransaction, TransactionFactory, - TransferTransaction, - VoteTransaction, } from "../../../../packages/crypto/src/transactions"; import { BigNumber } from "../../../../packages/crypto/src/utils"; +import { Two } from "../../../../packages/crypto/src/transactions/types"; describe("Transaction", () => { describe("should deserialize correctly some tests transactions", () => { @@ -145,38 +138,38 @@ describe("Transaction", () => { configManager.setHeight(100000000); let { staticFees } = configManager.getMilestone().fees; - expect(TransferTransaction.staticFee()).toEqual(BigNumber.make(1234)); - expect(SecondSignatureRegistrationTransaction.staticFee()).toEqual( + expect(Two.TransferTransaction.staticFee()).toEqual(BigNumber.make(1234)); + expect(Two.SecondSignatureRegistrationTransaction.staticFee()).toEqual( BigNumber.make(staticFees.secondSignature), ); - expect(DelegateRegistrationTransaction.staticFee()).toEqual( + expect(Two.DelegateRegistrationTransaction.staticFee()).toEqual( BigNumber.make(staticFees.delegateRegistration), ); - expect(VoteTransaction.staticFee()).toEqual(BigNumber.make(staticFees.vote)); - expect(MultiSignatureRegistrationTransaction.staticFee()).toEqual( + expect(Two.VoteTransaction.staticFee()).toEqual(BigNumber.make(staticFees.vote)); + expect(Two.MultiSignatureRegistrationTransaction.staticFee()).toEqual( BigNumber.make(staticFees.multiSignature), ); - expect(IpfsTransaction.staticFee()).toEqual(BigNumber.make(staticFees.ipfs)); - expect(MultiPaymentTransaction.staticFee()).toEqual(BigNumber.make(staticFees.multiPayment)); - expect(DelegateResignationTransaction.staticFee()).toEqual(BigNumber.make(staticFees.delegateResignation)); + expect(Two.IpfsTransaction.staticFee()).toEqual(BigNumber.make(staticFees.ipfs)); + expect(Two.MultiPaymentTransaction.staticFee()).toEqual(BigNumber.make(staticFees.multiPayment)); + expect(Two.DelegateResignationTransaction.staticFee()).toEqual(BigNumber.make(staticFees.delegateResignation)); configManager.setHeight(1); staticFees = configManager.getMilestone().fees.staticFees; - expect(TransferTransaction.staticFee()).toEqual(BigNumber.make(staticFees.transfer)); - expect(SecondSignatureRegistrationTransaction.staticFee()).toEqual( + expect(Two.TransferTransaction.staticFee()).toEqual(BigNumber.make(staticFees.transfer)); + expect(Two.SecondSignatureRegistrationTransaction.staticFee()).toEqual( BigNumber.make(staticFees.secondSignature), ); - expect(DelegateRegistrationTransaction.staticFee()).toEqual( + expect(Two.DelegateRegistrationTransaction.staticFee()).toEqual( BigNumber.make(staticFees.delegateRegistration), ); - expect(VoteTransaction.staticFee()).toEqual(BigNumber.make(staticFees.vote)); - expect(MultiSignatureRegistrationTransaction.staticFee()).toEqual( + expect(Two.VoteTransaction.staticFee()).toEqual(BigNumber.make(staticFees.vote)); + expect(Two.MultiSignatureRegistrationTransaction.staticFee()).toEqual( BigNumber.make(staticFees.multiSignature), ); - expect(IpfsTransaction.staticFee()).toEqual(BigNumber.make(staticFees.ipfs)); - expect(MultiPaymentTransaction.staticFee()).toEqual(BigNumber.make(staticFees.multiPayment)); - expect(DelegateResignationTransaction.staticFee()).toEqual(BigNumber.make(staticFees.delegateResignation)); + expect(Two.IpfsTransaction.staticFee()).toEqual(BigNumber.make(staticFees.ipfs)); + expect(Two.MultiPaymentTransaction.staticFee()).toEqual(BigNumber.make(staticFees.multiPayment)); + expect(Two.DelegateResignationTransaction.staticFee()).toEqual(BigNumber.make(staticFees.delegateResignation)); devnet.milestones.pop(); }); diff --git a/packages/core-api/src/controllers/node.ts b/packages/core-api/src/controllers/node.ts index 3f60b548f5..ea1bd0a7a1 100644 --- a/packages/core-api/src/controllers/node.ts +++ b/packages/core-api/src/controllers/node.ts @@ -102,7 +102,7 @@ export class NodeController extends Controller { const handler: Handlers.TransactionHandler = await this.app .get(Container.Identifiers.TransactionHandlerRegistry) - .get(result.type, result.typeGroup); + .get(result); groupedByTypeGroup[result.typeGroup][handler.getConstructor().key] = { avg: result.avg, max: result.max, diff --git a/packages/core-api/src/controllers/transactions.ts b/packages/core-api/src/controllers/transactions.ts index ad3dcf99ea..ede898b251 100644 --- a/packages/core-api/src/controllers/transactions.ts +++ b/packages/core-api/src/controllers/transactions.ts @@ -114,7 +114,7 @@ export class TransactionsController extends Controller { public async types(request: Hapi.Request, h: Hapi.ResponseToolkit) { const activatedTransactionHandlers: Handlers.TransactionHandler[] = await this.app - .get(Container.Identifiers.TransactionHandlerRegistry) + .get(Container.Identifiers.TransactionHandlerRegistry) .getActivatedTransactionHandlers(); const typeGroups: Record> = {}; @@ -141,7 +141,7 @@ export class TransactionsController extends Controller { public async schemas(request: Hapi.Request, h: Hapi.ResponseToolkit) { const activatedTransactionHandlers: Handlers.TransactionHandler[] = await this.app - .get(Container.Identifiers.TransactionHandlerRegistry) + .get(Container.Identifiers.TransactionHandlerRegistry) .getActivatedTransactionHandlers(); const schemasByType: Record> = {}; diff --git a/packages/core-blockchain/src/processor/block-processor.ts b/packages/core-blockchain/src/processor/block-processor.ts index 65bf5272e4..152900089f 100644 --- a/packages/core-blockchain/src/processor/block-processor.ts +++ b/packages/core-blockchain/src/processor/block-processor.ts @@ -82,8 +82,8 @@ export class BlockProcessor { try { for (const transaction of block.transactions) { const handler: Handlers.TransactionHandler = await this.app - .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(Container.Identifiers.TransactionHandlerRegistry) + .get(transaction.data); await handler.verify(transaction); } @@ -98,7 +98,7 @@ export class BlockProcessor { if (!verified) { this.logger.warning( `Block ${block.data.height.toLocaleString()} (${ - block.data.id + block.data.id }) disregarded because verification failed`, ); @@ -181,9 +181,9 @@ export class BlockProcessor { if (!nonceBySender[sender].plus(1).isEqualTo(nonce)) { this.logger.warning( `Block { height: ${block.data.height.toLocaleString()}, id: ${block.data.id} } ` + - `not accepted: invalid nonce order for sender ${sender}: ` + - `preceding nonce: ${nonceBySender[sender].toFixed()}, ` + - `transaction ${data.id} has nonce ${nonce.toFixed()}.`, + `not accepted: invalid nonce order for sender ${sender}: ` + + `preceding nonce: ${nonceBySender[sender].toFixed()}, ` + + `transaction ${data.id} has nonce ${nonce.toFixed()}.`, ); return true; } diff --git a/packages/core-database/src/database-service.ts b/packages/core-database/src/database-service.ts index 24ec388ee5..d19ce995c1 100644 --- a/packages/core-database/src/database-service.ts +++ b/packages/core-database/src/database-service.ts @@ -193,7 +193,7 @@ export class DatabaseService { delegates = delegates.map(delegate => delegate.clone()); for (let i = 0, delCount = delegates.length; i < delCount; i++) { - for (let x = 0; x < 4 && i < delCount; i++, x++) { + for (let x = 0; x < 4 && i < delCount; i++ , x++) { const newIndex = currentSeed[x] % delCount; const b = delegates[newIndex]; delegates[newIndex] = delegates[i]; @@ -533,8 +533,8 @@ export class DatabaseService { // const sender: Contracts.State.Wallet = this.walletRepository.findByAddress(senderId); // const transactionHandler: Handlers.TransactionHandler = await this.app - // .get(Container.Identifiers.TransactionHandlerRegistry) - // .get(transaction.type, transaction.typeGroup); + // .get(Container.Identifiers.TransactionHandlerRegistry) + // .get(transaction.data); // if (!sender.publicKey) { // sender.publicKey = transaction.data.senderPublicKey; @@ -750,7 +750,7 @@ export class DatabaseService { ( await this.app .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup) + .get(transaction.data) ).emitEvents(transaction, this.emitter); } diff --git a/packages/core-magistrate-crypto/src/transactions/bridgechain-registration.ts b/packages/core-magistrate-crypto/src/transactions/bridgechain-registration.ts index f1dd32fbe7..1fe6db97fb 100644 --- a/packages/core-magistrate-crypto/src/transactions/bridgechain-registration.ts +++ b/packages/core-magistrate-crypto/src/transactions/bridgechain-registration.ts @@ -12,6 +12,7 @@ export class BridgechainRegistrationTransaction extends Transactions.Transaction public static typeGroup: number = MagistrateTransactionGroup; public static type: number = MagistrateTransactionType.BridgechainRegistration; public static key: string = "bridgechainRegistration"; + public static version: number = 2; public static getSchema(): Transactions.schemas.TransactionSchema { return schemas.extend(schemas.transactionBaseSchema, { @@ -98,10 +99,10 @@ export class BridgechainRegistrationTransaction extends Transactions.Transaction const buffer: ByteBuffer = new ByteBuffer( bridgechainNameBufferLength + - seedNodesBuffersLength + - bridgechainGenesisHash.length + - bridgechainRepositoryBufferLength + - portsBuffersLength, + seedNodesBuffersLength + + bridgechainGenesisHash.length + + bridgechainRepositoryBufferLength + + portsBuffersLength, true, ); diff --git a/packages/core-magistrate-crypto/src/transactions/bridgechain-resignation.ts b/packages/core-magistrate-crypto/src/transactions/bridgechain-resignation.ts index 0a9e402686..766640ffa2 100644 --- a/packages/core-magistrate-crypto/src/transactions/bridgechain-resignation.ts +++ b/packages/core-magistrate-crypto/src/transactions/bridgechain-resignation.ts @@ -13,6 +13,7 @@ export class BridgechainResignationTransaction extends Transactions.Transaction public static typeGroup: number = MagistrateTransactionGroup; public static type = bridgechainResignationType; public static key: string = "bridgechainResignation"; + public static version: number = 2; public static getSchema(): Transactions.schemas.TransactionSchema { return schemas.extend(schemas.transactionBaseSchema, { diff --git a/packages/core-magistrate-crypto/src/transactions/bridgechain-update.ts b/packages/core-magistrate-crypto/src/transactions/bridgechain-update.ts index ef4e1b1ad2..f66a5aa044 100644 --- a/packages/core-magistrate-crypto/src/transactions/bridgechain-update.ts +++ b/packages/core-magistrate-crypto/src/transactions/bridgechain-update.ts @@ -12,6 +12,7 @@ export class BridgechainUpdateTransaction extends Transactions.Transaction { public static typeGroup: number = MagistrateTransactionGroup; public static type = MagistrateTransactionType.BridgechainUpdate; public static key: string = "bridgechainUpdate"; + public static version: number = 2; public static getSchema(): Transactions.schemas.TransactionSchema { return schemas.extend(schemas.transactionBaseSchema, { @@ -96,8 +97,8 @@ export class BridgechainUpdateTransaction extends Transactions.Transaction { const buffer: ByteBuffer = new ByteBuffer( 32 + // bridgechain id - seedNodesBuffersLength + - portsBuffersLength, + seedNodesBuffersLength + + portsBuffersLength, true, ); diff --git a/packages/core-magistrate-crypto/src/transactions/business-registration.ts b/packages/core-magistrate-crypto/src/transactions/business-registration.ts index d8d5093dd6..d5128c5746 100644 --- a/packages/core-magistrate-crypto/src/transactions/business-registration.ts +++ b/packages/core-magistrate-crypto/src/transactions/business-registration.ts @@ -12,6 +12,7 @@ export class BusinessRegistrationTransaction extends Transactions.Transaction { public static typeGroup: number = MagistrateTransactionGroup; public static type: number = MagistrateTransactionType.BusinessRegistration; public static key: string = "businessRegistration"; + public static version: number = 2; public static getSchema(): Transactions.schemas.TransactionSchema { return schemas.extend(schemas.transactionBaseSchema, { diff --git a/packages/core-magistrate-crypto/src/transactions/business-resignation.ts b/packages/core-magistrate-crypto/src/transactions/business-resignation.ts index 638b404985..c3b2fdcde9 100644 --- a/packages/core-magistrate-crypto/src/transactions/business-resignation.ts +++ b/packages/core-magistrate-crypto/src/transactions/business-resignation.ts @@ -9,6 +9,7 @@ export class BusinessResignationTransaction extends Transactions.Transaction { public static typeGroup: number = MagistrateTransactionGroup; public static type: number = MagistrateTransactionType.BusinessResignation; public static key: string = "businessResignation"; + public static version: number = 2; public static getSchema(): Transactions.schemas.TransactionSchema { return schemas.extend(schemas.transactionBaseSchema, { diff --git a/packages/core-magistrate-crypto/src/transactions/business-update.ts b/packages/core-magistrate-crypto/src/transactions/business-update.ts index 363086cf20..236dd549a6 100644 --- a/packages/core-magistrate-crypto/src/transactions/business-update.ts +++ b/packages/core-magistrate-crypto/src/transactions/business-update.ts @@ -12,6 +12,7 @@ export class BusinessUpdateTransaction extends Transactions.Transaction { public static typeGroup: number = MagistrateTransactionGroup; public static type: number = MagistrateTransactionType.BusinessUpdate; public static key: string = "businessUpdate"; + public static version: number = 2; public static getSchema(): Transactions.schemas.TransactionSchema { return schemas.extend(schemas.transactionBaseSchema, { diff --git a/packages/core-state/src/block-state.ts b/packages/core-state/src/block-state.ts index 01f744fcc2..db5fa012be 100644 --- a/packages/core-state/src/block-state.ts +++ b/packages/core-state/src/block-state.ts @@ -109,8 +109,8 @@ export class BlockState { public async applyTransaction(transaction: Interfaces.ITransaction): Promise { const transactionHandler: Handlers.TransactionHandler = await this.app - .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(Container.Identifiers.TransactionHandlerRegistry) + .get(transaction.data); let lockWallet: Contracts.State.Wallet | undefined; let lockTransaction: Interfaces.ITransactionData | undefined; @@ -148,8 +148,8 @@ export class BlockState { const { data } = transaction; const transactionHandler: Handlers.TransactionHandler = await this.app - .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(Container.Identifiers.TransactionHandlerRegistry) + .get(transaction.data); AppUtils.assert.defined(data.senderPublicKey); diff --git a/packages/core-state/src/state-builder.ts b/packages/core-state/src/state-builder.ts index 2f390acf3e..da24a426c2 100644 --- a/packages/core-state/src/state-builder.ts +++ b/packages/core-state/src/state-builder.ts @@ -32,7 +32,7 @@ export class StateBuilder { public async run(): Promise { this.logger = this.app.log; this.emitter = this.app.get(Container.Identifiers.EventDispatcherService); - const transactionHandlers: Handlers.TransactionHandler[] = this.app + const transactionHandlers: Map[] = this.app .get(Container.Identifiers.TransactionHandlerRegistry) .getAll(); const steps = transactionHandlers.length + 3; @@ -46,15 +46,15 @@ export class StateBuilder { const capitalize = (key: string) => key[0].toUpperCase() + key.slice(1); for (let i = 0; i < transactionHandlers.length; i++) { - const transactionHandler = transactionHandlers[i]; + const transactionHandlerVersions = [...transactionHandlers[i].values()]; - const constructoKey: string | undefined = transactionHandler.getConstructor().key; + const constructorKey: string | undefined = transactionHandlerVersions[0].getConstructor().key; + AppUtils.assert.defined(constructorKey); - AppUtils.assert.defined(constructoKey); - - this.logger.info(`State Generation - Step ${3 + i} of ${steps}: ${capitalize(constructoKey)}`); - - await transactionHandler.bootstrap(); + this.logger.info(`State Generation - Step ${3 + i} of ${steps}: ${capitalize(constructorKey)}`); + for (const version of transactionHandlerVersions) { + await version.bootstrap(); + } } this.logger.info(`State Generation - Step ${steps} of ${steps}: Vote Balances & Delegate Ranking`); diff --git a/packages/core-transaction-pool/src/connection.ts b/packages/core-transaction-pool/src/connection.ts index 8ca98e3182..545abe9443 100644 --- a/packages/core-transaction-pool/src/connection.ts +++ b/packages/core-transaction-pool/src/connection.ts @@ -201,7 +201,7 @@ export class Connection implements Contracts.TransactionPool.Connection { if (!this.loggedAllowedSenders.includes(senderPublicKey)) { this.logger.debug( `Transaction pool: allowing sender public key ${senderPublicKey} ` + - `(listed in options.allowedSenders), thus skipping throttling.`, + `(listed in options.allowedSenders), thus skipping throttling.`, ); this.loggedAllowedSenders.push(senderPublicKey); @@ -242,8 +242,8 @@ export class Connection implements Contracts.TransactionPool.Connection { const senderPublicKey: string = data.senderPublicKey; const transactionHandler: Handlers.TransactionHandler = await this.app - .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(Container.Identifiers.TransactionHandlerRegistry) + .get(transaction.data); const senderWallet: Contracts.State.Wallet = this.poolWalletRepository.findByPublicKey(senderPublicKey); @@ -274,7 +274,7 @@ export class Connection implements Contracts.TransactionPool.Connection { this.logger.error( `[Pool] Cannot apply transaction ${transaction.id} when trying to accept ` + - `block ${block.data.id}: ${error.message}`, + `block ${block.data.id}: ${error.message}`, ); continue; @@ -320,7 +320,7 @@ export class Connection implements Contracts.TransactionPool.Connection { try { const transactionHandler: Handlers.TransactionHandler = await this.app .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(transaction.data); await transactionHandler.throwIfCannotBeApplied(transaction, senderWallet, this.walletRepository); await transactionHandler.applyToSender(transaction, this.poolWalletRepository); } catch (error) { @@ -376,7 +376,7 @@ export class Connection implements Contracts.TransactionPool.Connection { try { const handler: Handlers.TransactionHandler = await this.app .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(transaction.data); await handler.applyToSender(transaction, this.poolWalletRepository); await handler.applyToRecipient(transaction, this.poolWalletRepository); @@ -448,7 +448,7 @@ export class Connection implements Contracts.TransactionPool.Connection { if (await this.has(transaction.id)) { this.logger.debug( "Transaction pool: ignoring attempt to add a transaction that is already " + - `in the pool, id: ${transaction.id}`, + `in the pool, id: ${transaction.id}`, ); return { transaction, type: "ERR_ALREADY_IN_POOL", message: "Already in pool" }; @@ -488,8 +488,8 @@ export class Connection implements Contracts.TransactionPool.Connection { await this.poolWalletRepository.throwIfCannotBeApplied(transaction); const handler: Handlers.TransactionHandler = await this.app - .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(Container.Identifiers.TransactionHandlerRegistry) + .get(transaction.data); await handler.applyToSender(transaction, this.poolWalletRepository); } catch (error) { this.logger.error(`[Pool] ${error.message}`); @@ -530,7 +530,7 @@ export class Connection implements Contracts.TransactionPool.Connection { const validTransactions: Interfaces.ITransaction[] = []; const forgedIds: string[] = await this.removeForgedTransactions(transactions); - const unforgedTransactions = differenceWith(transactions, forgedIds, (t, forgedId) => t.id === forgedId); + const unforgedTransactions: Interfaces.ITransaction[] = differenceWith(transactions, forgedIds, (t, forgedId) => t.id === forgedId); if (walletRepository === undefined) { walletRepository = this.app @@ -547,7 +547,7 @@ export class Connection implements Contracts.TransactionPool.Connection { strictEqual(transaction.id, deserialized.id); const sender: Contracts.State.Wallet = walletRepository.findByPublicKey( - transaction.data.senderPublicKey, + transaction.data.senderPublicKey!, ); let recipient: Contracts.State.Wallet | undefined; @@ -557,8 +557,8 @@ export class Connection implements Contracts.TransactionPool.Connection { } const handler: Handlers.TransactionHandler = await this.app - .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(Container.Identifiers.TransactionHandlerRegistry) + .get(transaction.data); await handler.applyToSender(transaction, walletRepository); @@ -569,7 +569,7 @@ export class Connection implements Contracts.TransactionPool.Connection { validTransactions.push(transaction); } catch (error) { console.error(error.stack); - this.removeTransactionById(transaction.id); + this.removeTransactionById(transaction.id!); this.logger.error( `[Pool] Removed ${transaction.id} before forging because it is no longer valid: ${error.message}`, diff --git a/packages/core-transaction-pool/src/dynamic-fee.ts b/packages/core-transaction-pool/src/dynamic-fee.ts index 9c79e50f47..18fc1642f9 100644 --- a/packages/core-transaction-pool/src/dynamic-fee.ts +++ b/packages/core-transaction-pool/src/dynamic-fee.ts @@ -30,7 +30,7 @@ export const dynamicFeeMatcher = async ( if (dynamicFees.enabled) { const handler: Handlers.TransactionHandler = await app .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(transaction.data); const addonBytes: number = dynamicFees.addonBytes[transaction.key]; const minFeeBroadcast: Utils.BigNumber = handler.dynamicFee({ @@ -45,7 +45,7 @@ export const dynamicFeeMatcher = async ( app.log.debug( `Transaction ${id} eligible for broadcast - fee of ${Utils.formatSatoshi(fee)} is ${ - fee.isEqualTo(minFeeBroadcast) ? "equal to" : "greater than" + fee.isEqualTo(minFeeBroadcast) ? "equal to" : "greater than" } minimum fee (${Utils.formatSatoshi(minFeeBroadcast)})`, ); } else { @@ -70,7 +70,7 @@ export const dynamicFeeMatcher = async ( app.log.debug( `Transaction ${id} eligible to enter pool - fee of ${Utils.formatSatoshi(fee)} is ${ - fee.isEqualTo(minFeePool) ? "equal to" : "greater than" + fee.isEqualTo(minFeePool) ? "equal to" : "greater than" } minimum fee (${Utils.formatSatoshi(minFeePool)})`, ); } else { diff --git a/packages/core-transaction-pool/src/pool-wallet-repository.ts b/packages/core-transaction-pool/src/pool-wallet-repository.ts index 9f310130ea..3ef4a864db 100644 --- a/packages/core-transaction-pool/src/pool-wallet-repository.ts +++ b/packages/core-transaction-pool/src/pool-wallet-repository.ts @@ -68,7 +68,7 @@ export class PoolWalletRepository extends Wallets.WalletRepository { const handler: Handlers.TransactionHandler = await this.app .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(transaction.data); return handler.throwIfCannotBeApplied(transaction, sender); } @@ -76,7 +76,7 @@ export class PoolWalletRepository extends Wallets.WalletRepository { public async revertTransactionForSender(transaction: Interfaces.ITransaction): Promise { const handler: Handlers.TransactionHandler = await this.app .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(transaction.data); return handler.revertForSender(transaction, this); } diff --git a/packages/core-transaction-pool/src/processor.ts b/packages/core-transaction-pool/src/processor.ts index f0a95436db..20c5cda294 100644 --- a/packages/core-transaction-pool/src/processor.ts +++ b/packages/core-transaction-pool/src/processor.ts @@ -155,8 +155,8 @@ export class Processor implements Contracts.TransactionPool.Processor { Utils.assert.defined(transactionInstance.data.id); const handler: Handlers.TransactionHandler = await this.app - .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transactionInstance.type, transactionInstance.typeGroup); + .get(Container.Identifiers.TransactionHandlerRegistry) + .get(transactionInstance.data); if (await handler.verify(transactionInstance, this.poolWalletRepository)) { try { @@ -261,8 +261,8 @@ export class Processor implements Contracts.TransactionPool.Processor { try { // @TODO: this leaks private members, refactor this const handler: Handlers.TransactionHandler = await this.app - .get(Container.Identifiers.TransactionHandlerRegistry) - .get(transaction.type, transaction.typeGroup); + .get(Container.Identifiers.TransactionHandlerRegistry) + .get(transaction); return handler.canEnterTransactionPool(transaction, this.pool, this); } catch (error) { if (error instanceof Errors.InvalidTransactionTypeError) { diff --git a/packages/core-transactions/src/handlers/handler-registry.ts b/packages/core-transactions/src/handlers/handler-registry.ts index 99dc968654..a39bdf81c5 100644 --- a/packages/core-transactions/src/handlers/handler-registry.ts +++ b/packages/core-transactions/src/handlers/handler-registry.ts @@ -1,19 +1,9 @@ import { Container, Contracts, Services, Utils } from "@arkecosystem/core-kernel"; -import { Enums, Errors, Transactions } from "@arkecosystem/crypto"; +import { Enums, Transactions } from "@arkecosystem/crypto"; import { DeactivatedTransactionHandlerError, InvalidTransactionTypeError } from "../errors"; -import { DelegateRegistrationTransactionHandler } from "./delegate-registration"; -import { DelegateResignationTransactionHandler } from "./delegate-resignation"; -import { HtlcClaimTransactionHandler } from "./htlc-claim"; -import { HtlcLockTransactionHandler } from "./htlc-lock"; -import { HtlcRefundTransactionHandler } from "./htlc-refund"; -import { IpfsTransactionHandler } from "./ipfs"; -import { MultiPaymentTransactionHandler } from "./multi-payment"; -import { MultiSignatureTransactionHandler } from "./multi-signature"; -import { SecondSignatureTransactionHandler } from "./second-signature"; import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; -import { TransferTransactionHandler } from "./transfer"; -import { VoteTransactionHandler } from "./vote"; +import { One, Two } from "."; // todo: review the implementation @Container.injectable() @@ -28,55 +18,71 @@ export class TransactionHandlerRegistry { private readonly registeredTransactionHandlers: Map< Transactions.InternalTransactionType, - TransactionHandler + Map > = new Map(); public init(): void { - this.registerTransactionHandler(TransferTransactionHandler); - this.registerTransactionHandler(SecondSignatureTransactionHandler); - this.registerTransactionHandler(DelegateRegistrationTransactionHandler); - this.registerTransactionHandler(VoteTransactionHandler); - this.registerTransactionHandler(MultiSignatureTransactionHandler); - this.registerTransactionHandler(IpfsTransactionHandler); - this.registerTransactionHandler(MultiPaymentTransactionHandler); - this.registerTransactionHandler(DelegateResignationTransactionHandler); - this.registerTransactionHandler(HtlcLockTransactionHandler); - this.registerTransactionHandler(HtlcClaimTransactionHandler); - this.registerTransactionHandler(HtlcRefundTransactionHandler); + this.registerTransactionHandler(One.TransferTransactionHandler); + this.registerTransactionHandler(Two.TransferTransactionHandler); + + this.registerTransactionHandler(One.SecondSignatureRegistrationTransactionHandler); + this.registerTransactionHandler(Two.SecondSignatureRegistrationTransactionHandler); + + this.registerTransactionHandler(One.DelegateRegistrationTransactionHandler); + this.registerTransactionHandler(Two.DelegateRegistrationTransactionHandler); + + this.registerTransactionHandler(One.VoteTransactionHandler); + this.registerTransactionHandler(Two.VoteTransactionHandler); + + this.registerTransactionHandler(One.MultiSignatureRegistrationTransactionHandler); + this.registerTransactionHandler(Two.MultiSignatureRegistrationTransactionHandler); + + this.registerTransactionHandler(Two.IpfsTransactionHandler); + + this.registerTransactionHandler(Two.MultiPaymentTransactionHandler); + + this.registerTransactionHandler(Two.DelegateResignationTransactionHandler); + + this.registerTransactionHandler(Two.HtlcLockTransactionHandler); + this.registerTransactionHandler(Two.HtlcClaimTransactionHandler); + this.registerTransactionHandler(Two.HtlcRefundTransactionHandler); } - public getAll(): TransactionHandler[] { + public getAll(): Map[] { return [...this.registeredTransactionHandlers.values()]; } - public async get(type: number, typeGroup?: number): Promise { - const internalType: - | Transactions.InternalTransactionType - | undefined = Transactions.InternalTransactionType.from(type, typeGroup); + public async get({ type, typeGroup, version }: { type: number, typeGroup?: number, version?: number }): Promise { + const internalType: Transactions.InternalTransactionType = Transactions.InternalTransactionType.from(type, typeGroup); + version = version ?? 1; Utils.assert.defined(internalType); - if (this.registeredTransactionHandlers.has(internalType)) { - const handler: TransactionHandler | undefined = this.registeredTransactionHandlers.get(internalType); - - Utils.assert.defined(handler); + if (!this.registeredTransactionHandlers.has(internalType)) { + throw new InvalidTransactionTypeError(internalType.toString()); + } - if (!(await handler.isActivated())) { - throw new DeactivatedTransactionHandlerError(internalType); - } + const handler: Map | undefined = this.registeredTransactionHandlers.get(internalType)!; + if (!handler.has(version)) { + throw new InvalidTransactionTypeError(internalType.toString()); + } - return handler; + const handlerVersion: TransactionHandler = handler.get(version)!; + if (!(await handlerVersion.isActivated())) { + throw new DeactivatedTransactionHandlerError(internalType); } - throw new InvalidTransactionTypeError(internalType.toString()); + return handlerVersion; } public async getActivatedTransactionHandlers(): Promise { const activatedTransactionHandlers: TransactionHandler[] = []; - for (const handler of this.registeredTransactionHandlers.values()) { - if (await handler.isActivated()) { - activatedTransactionHandlers.push(handler); + for (const handlers of this.registeredTransactionHandlers.values()) { + for (const version of handlers.values()) { + if (await version.isActivated()) { + activatedTransactionHandlers.push(version); + } } } @@ -84,26 +90,22 @@ export class TransactionHandlerRegistry { } public registerTransactionHandler(constructor: TransactionHandlerConstructor) { - const service: TransactionHandler = this.app.resolve(constructor); - const transactionConstructor = service.getConstructor(); + const handler: TransactionHandler = this.app.resolve(constructor); + const transactionConstructor = handler.getConstructor(); Utils.assert.defined(transactionConstructor.type); Utils.assert.defined(transactionConstructor.typeGroup); - for (const dependency of service.dependencies()) { + for (const dependency of handler.dependencies()) { this.registerTransactionHandler(dependency); } - const internalType: - | Transactions.InternalTransactionType - | undefined = Transactions.InternalTransactionType.from( + const internalType: Transactions.InternalTransactionType = Transactions.InternalTransactionType.from( transactionConstructor.type, transactionConstructor.typeGroup, ); - Utils.assert.defined(internalType); - - if (this.registeredTransactionHandlers.has(internalType)) { + if (this.registeredTransactionHandlers.get(internalType)?.has(transactionConstructor.version)) { return; } @@ -111,11 +113,19 @@ export class TransactionHandlerRegistry { Transactions.TransactionRegistry.registerTransactionType(transactionConstructor); } - for (const attribute of service.walletAttributes()) { - this.app.get(Container.Identifiers.WalletAttributes).set(attribute); + for (const attribute of handler.walletAttributes()) { + + // TODO: scope attribute by `handler.getConstructor().key` to distinguish between duplicate attributes ? + if (!this.app.get(Container.Identifiers.WalletAttributes).has(attribute)) { + this.app.get(Container.Identifiers.WalletAttributes).set(attribute); + } + } + + if (!this.registeredTransactionHandlers.has(internalType)) { + this.registeredTransactionHandlers.set(internalType, new Map()); } - this.registeredTransactionHandlers.set(internalType, service); + this.registeredTransactionHandlers.get(internalType)?.set(transactionConstructor.version, handler); } public deregisterTransactionHandler(constructor: TransactionHandlerConstructor): void { @@ -125,23 +135,16 @@ export class TransactionHandlerRegistry { Utils.assert.defined(transactionConstructor.type); Utils.assert.defined(transactionConstructor.typeGroup); - if ( - transactionConstructor.typeGroup === Enums.TransactionTypeGroup.Core || - transactionConstructor.typeGroup === undefined - ) { - throw new Errors.CoreTransactionTypeGroupImmutableError(); - } - const internalType: | Transactions.InternalTransactionType | undefined = Transactions.InternalTransactionType.from( - transactionConstructor.type, - transactionConstructor.typeGroup, - ); + transactionConstructor.type, + transactionConstructor.typeGroup, + ); Utils.assert.defined(internalType); - if (!this.registeredTransactionHandlers.has(internalType)) { + if (!this.registeredTransactionHandlers.has(internalType) && !this.registeredTransactionHandlers.get(internalType)!.has(transactionConstructor.version)) { throw new InvalidTransactionTypeError(internalType.toString()); } @@ -150,6 +153,11 @@ export class TransactionHandlerRegistry { } Transactions.TransactionRegistry.deregisterTransactionType(transactionConstructor); - this.registeredTransactionHandlers.delete(internalType); + + this.registeredTransactionHandlers.get(internalType)!.delete(transactionConstructor.version); + + if (this.registeredTransactionHandlers.get(internalType)!.size === 0) { + this.registeredTransactionHandlers.delete(internalType); + } } } diff --git a/packages/core-transactions/src/handlers/index.ts b/packages/core-transactions/src/handlers/index.ts index f1ed1fd4dd..bca4708540 100644 --- a/packages/core-transactions/src/handlers/index.ts +++ b/packages/core-transactions/src/handlers/index.ts @@ -1,4 +1,12 @@ import { TransactionHandlerRegistry } from "./handler-registry"; import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; -export { TransactionHandler, TransactionHandlerConstructor, TransactionHandlerRegistry as Registry }; +import * as One from "./one"; +import * as Two from "./two"; + +export { + One, Two, + TransactionHandler, + TransactionHandlerConstructor, + TransactionHandlerRegistry as Registry +}; diff --git a/packages/core-transactions/src/handlers/delegate-registration.ts b/packages/core-transactions/src/handlers/one/delegate-registration.ts similarity index 77% rename from packages/core-transactions/src/handlers/delegate-registration.ts rename to packages/core-transactions/src/handlers/one/delegate-registration.ts index fd826651bc..fab03dd7f7 100644 --- a/packages/core-transactions/src/handlers/delegate-registration.ts +++ b/packages/core-transactions/src/handlers/one/delegate-registration.ts @@ -1,15 +1,13 @@ -import { Models } from "@arkecosystem/core-database"; import { Container, Contracts, Enums as AppEnums, Utils as AppUtils } from "@arkecosystem/core-kernel"; -import { Enums, Interfaces, Transactions, Utils } from "@arkecosystem/crypto"; +import { Enums, Interfaces, Utils, Transactions } from "@arkecosystem/crypto"; import { NotSupportedForMultiSignatureWalletError, WalletIsAlreadyDelegateError, WalletNotADelegateError, WalletUsernameAlreadyRegisteredError, -} from "../errors"; -import { TransactionReader } from "../transaction-reader"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; +} from "../../errors"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; const { TransactionType, TransactionTypeGroup } = Enums; @@ -17,9 +15,6 @@ const { TransactionType, TransactionTypeGroup } = Enums; // todo: replace unnecessary function arguments with dependency injection to avoid passing around references @Container.injectable() export class DelegateRegistrationTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.DelegateRegistrationTransaction; - } public dependencies(): ReadonlyArray { return []; @@ -41,46 +36,12 @@ export class DelegateRegistrationTransactionHandler extends TransactionHandler { ]; } - public async bootstrap(): Promise { - const reader: TransactionReader = this.getTransactionReader(); - const transactions: Models.Transaction[] = await reader.read(); - - for (const transaction of transactions) { - const wallet = this.walletRepository.findByPublicKey(transaction.senderPublicKey); - - wallet.setAttribute("delegate", { - username: transaction.asset.delegate!.username, - voteBalance: Utils.BigNumber.ZERO, - forgedFees: Utils.BigNumber.ZERO, - forgedRewards: Utils.BigNumber.ZERO, - producedBlocks: 0, - rank: undefined, - }); - - this.walletRepository.reindex(wallet); - } - - const forgedBlocks = await this.blockRepository.getDelegatesForgedBlocks(); - const lastForgedBlocks = await this.blockRepository.getLastForgedBlocks(); - for (const block of forgedBlocks) { - const wallet: Contracts.State.Wallet = this.walletRepository.findByPublicKey(block.generatorPublicKey); - - // Genesis wallet is empty - if (!wallet.hasAttribute("delegate")) { - continue; - } - - const delegate: Contracts.State.WalletDelegateAttributes = wallet.getAttribute("delegate"); - delegate.forgedFees = delegate.forgedFees.plus(block.totalFees); - delegate.forgedRewards = delegate.forgedRewards.plus(block.totalRewards); - delegate.producedBlocks += +block.totalProduced; - } - - for (const block of lastForgedBlocks) { - const wallet: Contracts.State.Wallet = this.walletRepository.findByPublicKey(block.generatorPublicKey); + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.One.DelegateRegistrationTransaction; + } - wallet.setAttribute("delegate.lastBlock", block); - } + public async bootstrap(): Promise { + return; } public async isActivated(): Promise { @@ -222,10 +183,10 @@ export class DelegateRegistrationTransactionHandler extends TransactionHandler { public async applyToRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } public async revertForRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } } diff --git a/packages/core-transactions/src/handlers/one/index.ts b/packages/core-transactions/src/handlers/one/index.ts new file mode 100644 index 0000000000..0bfabe8e16 --- /dev/null +++ b/packages/core-transactions/src/handlers/one/index.ts @@ -0,0 +1,5 @@ +export * from "./transfer"; +export * from "./second-signature-registration"; +export * from "./delegate-registration"; +export * from "./vote"; +export * from "./multi-signature-registration"; diff --git a/packages/core-transactions/src/handlers/one/multi-signature-registration.ts b/packages/core-transactions/src/handlers/one/multi-signature-registration.ts new file mode 100644 index 0000000000..ff64eeace7 --- /dev/null +++ b/packages/core-transactions/src/handlers/one/multi-signature-registration.ts @@ -0,0 +1,74 @@ +import { Container, Contracts } from "@arkecosystem/core-kernel"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; +import { Transactions, Interfaces, Utils } from "@arkecosystem/crypto"; +import { TransactionReader } from "../../transaction-reader"; +import { Models } from "@arkecosystem/core-database"; +import { MultiSignatureAlreadyRegisteredError, LegacyMultiSignatureError } from "../../errors"; + +// todo: revisit the implementation, container usage and arguments after core-database rework +// todo: replace unnecessary function arguments with dependency injection to avoid passing around references +@Container.injectable() +export class MultiSignatureRegistrationTransactionHandler extends TransactionHandler { + public dependencies(): ReadonlyArray { + return []; + } + + public walletAttributes(): ReadonlyArray { + return ["multiSignature"]; + } + + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.One.MultiSignatureRegistrationTransaction; + } + + public async bootstrap(): Promise { + const reader: TransactionReader = this.getTransactionReader(); + const transactions: Models.Transaction[] = await reader.read(); + for (const transaction of transactions) { + const wallet: Contracts.State.Wallet = this.walletRepository.findByPublicKey(transaction.senderPublicKey); + const multiSignature: Contracts.State.WalletMultiSignatureAttributes = transaction.asset.multisignature || transaction.asset.multiSignatureLegacy; + multiSignature.legacy = true; + + if (wallet.hasMultiSignature()) { + throw new MultiSignatureAlreadyRegisteredError(); + } + + wallet.setAttribute("multiSignature", multiSignature); + this.walletRepository.reindex(wallet); + } + } + + public async isActivated(): Promise { + return false; + } + + public async throwIfCannotBeApplied( + transaction: Interfaces.ITransaction, + wallet: Contracts.State.Wallet, + customWalletRepository?: Contracts.State.WalletRepository, + ): Promise { + const { data }: Interfaces.ITransaction = transaction; + + if (Utils.isException(data.id)) { + return; + } + + throw new LegacyMultiSignatureError(); + } + + public async canEnterTransactionPool( + data: Interfaces.ITransactionData, + pool: Contracts.TransactionPool.Connection, + processor: Contracts.TransactionPool.Processor, + ): Promise { + return false; + } + + public async applyToRecipient(transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository | undefined): Promise { + return; + } + + public async revertForRecipient(transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository | undefined): Promise { + return; + } +} diff --git a/packages/core-transactions/src/handlers/second-signature.ts b/packages/core-transactions/src/handlers/one/second-signature-registration.ts similarity index 83% rename from packages/core-transactions/src/handlers/second-signature.ts rename to packages/core-transactions/src/handlers/one/second-signature-registration.ts index cfc0859d49..f39389dbb3 100644 --- a/packages/core-transactions/src/handlers/second-signature.ts +++ b/packages/core-transactions/src/handlers/one/second-signature-registration.ts @@ -1,19 +1,13 @@ -import { Models } from "@arkecosystem/core-database"; import { Container, Contracts, Utils } from "@arkecosystem/core-kernel"; import { Interfaces, Transactions } from "@arkecosystem/crypto"; -import { NotSupportedForMultiSignatureWalletError, SecondSignatureAlreadyRegisteredError } from "../errors"; -import { TransactionReader } from "../transaction-reader"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; +import { NotSupportedForMultiSignatureWalletError, SecondSignatureAlreadyRegisteredError } from "../../errors"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; // todo: revisit the implementation, container usage and arguments after core-database rework // todo: replace unnecessary function arguments with dependency injection to avoid passing around references @Container.injectable() -export class SecondSignatureTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.SecondSignatureRegistrationTransaction; - } - +export class SecondSignatureRegistrationTransactionHandler extends TransactionHandler { public dependencies(): ReadonlyArray { return []; } @@ -22,13 +16,12 @@ export class SecondSignatureTransactionHandler extends TransactionHandler { return ["secondPublicKey"]; } + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.One.SecondSignatureRegistrationTransaction; + } + public async bootstrap(): Promise { - const reader: TransactionReader = this.getTransactionReader(); - const transactions: Models.Transaction[] = await reader.read(); - for (const transaction of transactions) { - const wallet = this.walletRepository.findByPublicKey(transaction.senderPublicKey); - wallet.setAttribute("secondPublicKey", transaction.asset.signature!.publicKey); - } + return; } public async isActivated(): Promise { @@ -102,10 +95,10 @@ export class SecondSignatureTransactionHandler extends TransactionHandler { public async applyToRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } public async revertForRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } } diff --git a/packages/core-transactions/src/handlers/transfer.ts b/packages/core-transactions/src/handlers/one/transfer.ts similarity index 87% rename from packages/core-transactions/src/handlers/transfer.ts rename to packages/core-transactions/src/handlers/one/transfer.ts index 9bc23c6926..6c82cae9c1 100644 --- a/packages/core-transactions/src/handlers/transfer.ts +++ b/packages/core-transactions/src/handlers/one/transfer.ts @@ -1,17 +1,13 @@ import { Container, Contracts, Utils } from "@arkecosystem/core-kernel"; import { Interfaces, Managers, Transactions } from "@arkecosystem/crypto"; -import { isRecipientOnActiveNetwork } from "../utils"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; +import { isRecipientOnActiveNetwork } from "../../utils"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; // todo: revisit the implementation, container usage and arguments after core-database rework // todo: replace unnecessary function arguments with dependency injection to avoid passing around references @Container.injectable() export class TransferTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.TransferTransaction; - } - public dependencies(): ReadonlyArray { return []; } @@ -20,12 +16,12 @@ export class TransferTransactionHandler extends TransactionHandler { return []; } + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.One.TransferTransaction; + } + public async bootstrap(): Promise { - const transactions = await this.transactionRepository.findReceivedTransactions(); - for (const transaction of transactions) { - const wallet: Contracts.State.Wallet = this.walletRepository.findByAddress(transaction.recipientId); - wallet.balance = wallet.balance.plus(transaction.amount); - } + return; } public async isActivated(): Promise { diff --git a/packages/core-transactions/src/handlers/vote.ts b/packages/core-transactions/src/handlers/one/vote.ts similarity index 80% rename from packages/core-transactions/src/handlers/vote.ts rename to packages/core-transactions/src/handlers/one/vote.ts index 1b6380ef73..0f7ec8fe9b 100644 --- a/packages/core-transactions/src/handlers/vote.ts +++ b/packages/core-transactions/src/handlers/one/vote.ts @@ -1,4 +1,3 @@ -import { Models } from "@arkecosystem/core-database"; import { Container, Contracts, Utils } from "@arkecosystem/core-kernel"; import { Interfaces, Transactions } from "@arkecosystem/crypto"; @@ -8,49 +7,29 @@ import { UnvoteMismatchError, VotedForNonDelegateError, VotedForResignedDelegateError, -} from "../errors"; -import { TransactionReader } from "../transaction-reader"; -import { DelegateRegistrationTransactionHandler } from "./delegate-registration"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; +} from "../../errors"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; +import { One } from "../index"; // todo: revisit the implementation, container usage and arguments after core-database rework // todo: replace unnecessary function arguments with dependency injection to avoid passing around references @Container.injectable() export class VoteTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.VoteTransaction; + + public walletAttributes(): ReadonlyArray { + return ["vote"]; } - public dependencies(): ReadonlyArray { - return [DelegateRegistrationTransactionHandler]; + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.One.VoteTransaction; } - public walletAttributes(): ReadonlyArray { - return ["vote"]; + public dependencies(): ReadonlyArray { + return [One.DelegateRegistrationTransactionHandler]; } public async bootstrap(): Promise { - const reader: TransactionReader = this.getTransactionReader(); - const transactions: Models.Transaction[] = await reader.read(); - for (const transaction of transactions) { - const wallet = this.walletRepository.findByPublicKey(transaction.senderPublicKey); - const vote = transaction.asset.votes![0]; - const hasVoted: boolean = wallet.hasAttribute("vote"); - - if (vote.startsWith("+")) { - if (hasVoted) { - throw new AlreadyVotedError(); - } - wallet.setAttribute("vote", vote.slice(1)); - } else { - if (!hasVoted) { - throw new NoVoteError(); - } else if (wallet.getAttribute("vote") !== vote.slice(1)) { - throw new UnvoteMismatchError(); - } - wallet.forgetAttribute("vote"); - } - } + return; } public async isActivated(): Promise { @@ -171,10 +150,10 @@ export class VoteTransactionHandler extends TransactionHandler { public async applyToRecipient( transaction: Interfaces.ITransaction, walletRepository: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } public async revertForRecipient( transaction: Interfaces.ITransaction, walletRepository: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } } diff --git a/packages/core-transactions/src/handlers/transaction.ts b/packages/core-transactions/src/handlers/transaction.ts index aec677cd44..cd0ce12095 100644 --- a/packages/core-transactions/src/handlers/transaction.ts +++ b/packages/core-transactions/src/handlers/transaction.ts @@ -209,6 +209,7 @@ export abstract class TransactionHandler { await this.throwIfCannotBeApplied(transaction, sender, customWalletRepository); + // TODO: extract version specific code if (data.version && data.version > 1) { this.verifyTransactionNonceApply(sender, transaction); @@ -255,9 +256,8 @@ export abstract class TransactionHandler { sender.balance = sender.balance.plus(data.amount).plus(data.fee); - if (data.version && data.version > 1) { - this.verifyTransactionNonceRevert(sender, transaction); - } + // TODO: extract version specific code + this.verifyTransactionNonceRevert(sender, transaction); sender.nonce = sender.nonce.minus(1); } @@ -275,7 +275,7 @@ export abstract class TransactionHandler { /** * Database Service */ - public emitEvents(transaction: Interfaces.ITransaction, emitter: Contracts.Kernel.EventDispatcher): void {} + public emitEvents(transaction: Interfaces.ITransaction, emitter: Contracts.Kernel.EventDispatcher): void { } /** * Transaction Pool logic diff --git a/packages/core-transactions/src/handlers/two/delegate-registration.ts b/packages/core-transactions/src/handlers/two/delegate-registration.ts new file mode 100644 index 0000000000..892cf2e883 --- /dev/null +++ b/packages/core-transactions/src/handlers/two/delegate-registration.ts @@ -0,0 +1,56 @@ +import { Container, Contracts } from "@arkecosystem/core-kernel"; +import { Transactions, Utils } from "@arkecosystem/crypto"; +import { TransactionReader } from "../../transaction-reader"; +import { Models } from "@arkecosystem/core-database"; +import { One } from "../index"; + +@Container.injectable() +export class DelegateRegistrationTransactionHandler extends One.DelegateRegistrationTransactionHandler { + + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.DelegateRegistrationTransaction; + } + + public async bootstrap(): Promise { + const reader: TransactionReader = this.getTransactionReader(); + const transactions: Models.Transaction[] = await reader.read(); + + for (const transaction of transactions) { + const wallet = this.walletRepository.findByPublicKey(transaction.senderPublicKey); + + wallet.setAttribute("delegate", { + username: transaction.asset.delegate!.username, + voteBalance: Utils.BigNumber.ZERO, + forgedFees: Utils.BigNumber.ZERO, + forgedRewards: Utils.BigNumber.ZERO, + producedBlocks: 0, + rank: undefined, + }); + + this.walletRepository.reindex(wallet); + } + + const forgedBlocks = await this.blockRepository.getDelegatesForgedBlocks(); + const lastForgedBlocks = await this.blockRepository.getLastForgedBlocks(); + for (const block of forgedBlocks) { + const wallet: Contracts.State.Wallet = this.walletRepository.findByPublicKey(block.generatorPublicKey); + + // Genesis wallet is empty + if (!wallet.hasAttribute("delegate")) { + continue; + } + + const delegate: Contracts.State.WalletDelegateAttributes = wallet.getAttribute("delegate"); + delegate.forgedFees = delegate.forgedFees.plus(block.totalFees); + delegate.forgedRewards = delegate.forgedRewards.plus(block.totalRewards); + delegate.producedBlocks += +block.totalProduced; + } + + for (const block of lastForgedBlocks) { + const wallet: Contracts.State.Wallet = this.walletRepository.findByPublicKey(block.generatorPublicKey); + + wallet.setAttribute("delegate.lastBlock", block); + } + } + +} diff --git a/packages/core-transactions/src/handlers/delegate-resignation.ts b/packages/core-transactions/src/handlers/two/delegate-resignation.ts similarity index 93% rename from packages/core-transactions/src/handlers/delegate-resignation.ts rename to packages/core-transactions/src/handlers/two/delegate-resignation.ts index c9c9d10865..97baef105c 100644 --- a/packages/core-transactions/src/handlers/delegate-resignation.ts +++ b/packages/core-transactions/src/handlers/two/delegate-resignation.ts @@ -1,26 +1,26 @@ -import { Models } from "@arkecosystem/core-database"; import { Container, Contracts, Enums, Utils } from "@arkecosystem/core-kernel"; import { Interfaces, Managers, Transactions } from "@arkecosystem/crypto"; -import { NotEnoughDelegatesError, WalletAlreadyResignedError, WalletNotADelegateError } from "../errors"; -import { TransactionReader } from "../transaction-reader"; -import { DelegateRegistrationTransactionHandler } from "./delegate-registration"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; +import { NotEnoughDelegatesError, WalletAlreadyResignedError, WalletNotADelegateError } from "../../errors"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; +import { Two } from "../index"; +import { TransactionReader } from "../../transaction-reader"; +import { Models } from "@arkecosystem/core-database"; // todo: revisit the implementation, container usage and arguments after core-database rework // todo: replace unnecessary function arguments with dependency injection to avoid passing around references @Container.injectable() export class DelegateResignationTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.DelegateResignationTransaction; + public walletAttributes(): ReadonlyArray { + return ["delegate.resigned"]; } - public dependencies(): ReadonlyArray { - return [DelegateRegistrationTransactionHandler]; + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.DelegateResignationTransaction; } - public walletAttributes(): ReadonlyArray { - return ["delegate.resigned"]; + public dependencies(): ReadonlyArray { + return [Two.DelegateRegistrationTransactionHandler]; } public async bootstrap(): Promise { @@ -32,7 +32,6 @@ export class DelegateResignationTransactionHandler extends TransactionHandler { this.walletRepository.reindex(wallet); } } - public async isActivated(): Promise { return Managers.configManager.getMilestone().aip11 === true; } @@ -128,11 +127,11 @@ export class DelegateResignationTransactionHandler extends TransactionHandler { transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, // tslint:disable-next-line: no-empty - ): Promise {} + ): Promise { } public async revertForRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, // tslint:disable-next-line: no-empty - ): Promise {} + ): Promise { } } diff --git a/packages/core-transactions/src/handlers/htlc-claim.ts b/packages/core-transactions/src/handlers/two/htlc-claim.ts similarity index 96% rename from packages/core-transactions/src/handlers/htlc-claim.ts rename to packages/core-transactions/src/handlers/two/htlc-claim.ts index 31e3f45735..45d259693f 100644 --- a/packages/core-transactions/src/handlers/htlc-claim.ts +++ b/packages/core-transactions/src/handlers/two/htlc-claim.ts @@ -1,23 +1,23 @@ import { Container, Contracts, Utils as AppUtils } from "@arkecosystem/core-kernel"; -import { Crypto, Enums, Interfaces, Managers, Transactions, Utils } from "@arkecosystem/crypto"; +import { Crypto, Enums, Interfaces, Managers, Utils, Transactions } from "@arkecosystem/crypto"; import { strict } from "assert"; -import { HtlcLockExpiredError, HtlcLockTransactionNotFoundError, HtlcSecretHashMismatchError } from "../errors"; -import { HtlcLockTransactionHandler } from "./htlc-lock"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; +import { HtlcLockExpiredError, HtlcLockTransactionNotFoundError, HtlcSecretHashMismatchError } from "../../errors"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; +import { Two } from "../index"; @Container.injectable() export class HtlcClaimTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.HtlcClaimTransaction; + public walletAttributes(): ReadonlyArray { + return []; } - public dependencies(): ReadonlyArray { - return [HtlcLockTransactionHandler]; + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.HtlcClaimTransaction; } - public walletAttributes(): ReadonlyArray { - return []; + public dependencies(): ReadonlyArray { + return [Two.HtlcLockTransactionHandler]; } public async bootstrap(): Promise { @@ -231,10 +231,10 @@ export class HtlcClaimTransactionHandler extends TransactionHandler { public async applyToRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } public async revertForRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } } diff --git a/packages/core-transactions/src/handlers/htlc-lock.ts b/packages/core-transactions/src/handlers/two/htlc-lock.ts similarity index 96% rename from packages/core-transactions/src/handlers/htlc-lock.ts rename to packages/core-transactions/src/handlers/two/htlc-lock.ts index afd924ab7a..79db5b6382 100644 --- a/packages/core-transactions/src/handlers/htlc-lock.ts +++ b/packages/core-transactions/src/handlers/two/htlc-lock.ts @@ -1,17 +1,13 @@ import { Container, Contracts, Utils as AppUtils } from "@arkecosystem/core-kernel"; -import { Enums, Interfaces, Managers, Transactions, Utils } from "@arkecosystem/crypto"; +import { Enums, Interfaces, Managers, Utils, Transactions } from "@arkecosystem/crypto"; -import { HtlcLockExpiredError } from "../errors"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; +import { HtlcLockExpiredError } from "../../errors"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; // todo: revisit the implementation, container usage and arguments after core-database rework // todo: replace unnecessary function arguments with dependency injection to avoid passing around references @Container.injectable() export class HtlcLockTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.HtlcLockTransaction; - } - public dependencies(): ReadonlyArray { return []; } @@ -20,6 +16,10 @@ export class HtlcLockTransactionHandler extends TransactionHandler { return ["htlc.locks", "htlc.lockedBalance"]; } + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.HtlcLockTransaction; + } + public async bootstrap(): Promise { const transactions = await this.transactionRepository.getOpenHtlcLocks(); const walletsToIndex: Record = {}; @@ -158,10 +158,10 @@ export class HtlcLockTransactionHandler extends TransactionHandler { public async applyToRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } public async revertForRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } } diff --git a/packages/core-transactions/src/handlers/htlc-refund.ts b/packages/core-transactions/src/handlers/two/htlc-refund.ts similarity index 95% rename from packages/core-transactions/src/handlers/htlc-refund.ts rename to packages/core-transactions/src/handlers/two/htlc-refund.ts index d238158d71..2c9d9a30af 100644 --- a/packages/core-transactions/src/handlers/htlc-refund.ts +++ b/packages/core-transactions/src/handlers/two/htlc-refund.ts @@ -1,31 +1,30 @@ import { Container, Contracts, Utils as AppUtils } from "@arkecosystem/core-kernel"; -import { Enums, Interfaces, Managers, Transactions, Utils } from "@arkecosystem/crypto"; +import { Enums, Interfaces, Managers, Utils, Transactions } from "@arkecosystem/crypto"; import assert from "assert"; -import { HtlcLockNotExpiredError, HtlcLockTransactionNotFoundError } from "../errors"; -import { HtlcLockTransactionHandler } from "./htlc-lock"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; +import { HtlcLockNotExpiredError, HtlcLockTransactionNotFoundError } from "../../errors"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; +import { Two } from "../index"; @Container.injectable() export class HtlcRefundTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.HtlcRefundTransaction; + + public walletAttributes(): ReadonlyArray { + return []; } - public dependencies(): ReadonlyArray { - return [HtlcLockTransactionHandler]; + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.HtlcRefundTransaction; } - public walletAttributes(): ReadonlyArray { - return []; + public dependencies(): ReadonlyArray { + return [Two.HtlcLockTransactionHandler]; } public async bootstrap(): Promise { const transactions = await this.transactionRepository.getRefundedHtlcLockBalances(); for (const transaction of transactions) { - AppUtils.assert.defined(transaction.senderPublicKey); - const refundWallet: Contracts.State.Wallet = this.walletRepository.findByPublicKey( transaction.senderPublicKey, ); // sender is from the original lock @@ -221,10 +220,10 @@ export class HtlcRefundTransactionHandler extends TransactionHandler { public async applyToRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } public async revertForRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } } diff --git a/packages/core-transactions/src/handlers/two/index.ts b/packages/core-transactions/src/handlers/two/index.ts new file mode 100644 index 0000000000..4a216f65e7 --- /dev/null +++ b/packages/core-transactions/src/handlers/two/index.ts @@ -0,0 +1,11 @@ +export * from "./transfer"; +export * from "./second-signature-registration"; +export * from "./delegate-registration"; +export * from "./vote"; +export * from "./multi-signature-registration"; +export * from "./ipfs"; +export * from "./multi-payment"; +export * from "./delegate-resignation"; +export * from "./htlc-lock"; +export * from "./htlc-claim"; +export * from "./htlc-refund"; diff --git a/packages/core-transactions/src/handlers/ipfs.ts b/packages/core-transactions/src/handlers/two/ipfs.ts similarity index 93% rename from packages/core-transactions/src/handlers/ipfs.ts rename to packages/core-transactions/src/handlers/two/ipfs.ts index 34dc836c45..c65b8bc350 100644 --- a/packages/core-transactions/src/handlers/ipfs.ts +++ b/packages/core-transactions/src/handlers/two/ipfs.ts @@ -1,18 +1,15 @@ -import { Models } from "@arkecosystem/core-database"; import { Container, Contracts, Utils } from "@arkecosystem/core-kernel"; -import { Interfaces, Managers, Transactions, Utils as CryptoUtils } from "@arkecosystem/crypto"; +import { Interfaces, Managers, Utils as CryptoUtils, Transactions } from "@arkecosystem/crypto"; -import { IpfsHashAlreadyExists } from "../errors"; -import { TransactionReader } from "../transaction-reader"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; +import { IpfsHashAlreadyExists } from "../../errors"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; +import { TransactionReader } from "../../transaction-reader"; +import { Models } from "@arkecosystem/core-database"; // todo: revisit the implementation, container usage and arguments after core-database rework // todo: replace unnecessary function arguments with dependency injection to avoid passing around references @Container.injectable() export class IpfsTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.IpfsTransaction; - } public dependencies(): ReadonlyArray { return []; @@ -22,6 +19,10 @@ export class IpfsTransactionHandler extends TransactionHandler { return ["ipfs", "ipfs.hashes"]; } + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.IpfsTransaction; + } + public async bootstrap(): Promise { const reader: TransactionReader = this.getTransactionReader(); const transactions: Models.Transaction[] = await reader.read(); @@ -116,10 +117,10 @@ export class IpfsTransactionHandler extends TransactionHandler { public async applyToRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } public async revertForRecipient( transaction: Interfaces.ITransaction, customWalletRepository?: Contracts.State.WalletRepository, - ): Promise {} + ): Promise { } } diff --git a/packages/core-transactions/src/handlers/multi-payment.ts b/packages/core-transactions/src/handlers/two/multi-payment.ts similarity index 95% rename from packages/core-transactions/src/handlers/multi-payment.ts rename to packages/core-transactions/src/handlers/two/multi-payment.ts index 93725c13e6..551f8d4c7f 100644 --- a/packages/core-transactions/src/handlers/multi-payment.ts +++ b/packages/core-transactions/src/handlers/two/multi-payment.ts @@ -1,17 +1,13 @@ -import { Models } from "@arkecosystem/core-database"; import { Container, Contracts, Utils as AppUtils } from "@arkecosystem/core-kernel"; -import { Interfaces, Managers, Transactions, Utils } from "@arkecosystem/crypto"; +import { Interfaces, Managers, Utils, Transactions } from "@arkecosystem/crypto"; -import { InsufficientBalanceError } from "../errors"; -import { TransactionReader } from "../transaction-reader"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; +import { InsufficientBalanceError } from "../../errors"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; +import { TransactionReader } from "../../transaction-reader"; +import { Models } from "@arkecosystem/core-database"; @Container.injectable() export class MultiPaymentTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.MultiPaymentTransaction; - } - public dependencies(): ReadonlyArray { return []; } @@ -20,6 +16,10 @@ export class MultiPaymentTransactionHandler extends TransactionHandler { return []; } + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.MultiPaymentTransaction; + } + public async bootstrap(): Promise { const reader: TransactionReader = this.getTransactionReader(); const transactions: Models.Transaction[] = await reader.read(); diff --git a/packages/core-transactions/src/handlers/multi-signature.ts b/packages/core-transactions/src/handlers/two/multi-signature-registration.ts similarity index 56% rename from packages/core-transactions/src/handlers/multi-signature.ts rename to packages/core-transactions/src/handlers/two/multi-signature-registration.ts index efd5e3800b..5eaeae1194 100644 --- a/packages/core-transactions/src/handlers/multi-signature.ts +++ b/packages/core-transactions/src/handlers/two/multi-signature-registration.ts @@ -1,24 +1,12 @@ -import { Models } from "@arkecosystem/core-database"; import { Container, Contracts, Utils as AppUtils } from "@arkecosystem/core-kernel"; -import { Identities, Interfaces, Transactions, Utils } from "@arkecosystem/crypto"; - -import { - InvalidMultiSignatureError, - MultiSignatureAlreadyRegisteredError, - MultiSignatureKeyCountMismatchError, - MultiSignatureMinimumKeysError, -} from "../errors"; -import { TransactionReader } from "../transaction-reader"; -import { TransactionHandler, TransactionHandlerConstructor } from "./transaction"; - -// todo: revisit the implementation, container usage and arguments after core-database rework -// todo: replace unnecessary function arguments with dependency injection to avoid passing around references -@Container.injectable() -export class MultiSignatureTransactionHandler extends TransactionHandler { - public getConstructor(): Transactions.TransactionConstructor { - return Transactions.MultiSignatureRegistrationTransaction; - } +import { Transactions, Identities, Interfaces, Managers } from "@arkecosystem/crypto"; +import { TransactionReader } from "../../transaction-reader"; +import { Models } from "@arkecosystem/core-database"; +import { MultiSignatureAlreadyRegisteredError, MultiSignatureMinimumKeysError, MultiSignatureKeyCountMismatchError, InvalidMultiSignatureError } from "../../errors"; +import { TransactionHandler, TransactionHandlerConstructor } from "../transaction"; +@Container.injectable() +export class MultiSignatureRegistrationTransactionHandler extends TransactionHandler { public dependencies(): ReadonlyArray { return []; } @@ -27,23 +15,19 @@ export class MultiSignatureTransactionHandler extends TransactionHandler { return ["multiSignature"]; } + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.MultiSignatureRegistrationTransaction; + } + public async bootstrap(): Promise { const reader: TransactionReader = this.getTransactionReader(); const transactions: Models.Transaction[] = await reader.read(); for (const transaction of transactions) { - let wallet: Contracts.State.Wallet; - let multiSignature: Contracts.State.WalletMultiSignatureAttributes; - - if (transaction.version === 1) { - multiSignature = transaction.asset.multisignature || transaction.asset.multiSignatureLegacy; - wallet = this.walletRepository.findByPublicKey(transaction.senderPublicKey); - multiSignature.legacy = true; - } else { - multiSignature = transaction.asset.multiSignature!; - wallet = this.walletRepository.findByAddress( - Identities.Address.fromMultiSignatureAsset(multiSignature), - ); - } + const multiSignature: Contracts.State.WalletMultiSignatureAttributes = transaction.asset.multiSignature!; + const wallet: Contracts.State.Wallet = this.walletRepository.findByAddress( + Identities.Address.fromMultiSignatureAsset(multiSignature), + ); + if (wallet.hasMultiSignature()) { throw new MultiSignatureAlreadyRegisteredError(); } @@ -53,11 +37,8 @@ export class MultiSignatureTransactionHandler extends TransactionHandler { } } - // Technically, we only enable `MultiSignatureRegistration` when the `aip11` milestone is active, - // but since there are no versioned transaction types yet we have to do it differently, to not break - // existing legacy multi signatures. TODO: becomes obsolete with 3.0 public async isActivated(): Promise { - return true; + return Managers.configManager.getMilestone().aip11 === true; } public async throwIfCannotBeApplied( @@ -67,10 +48,6 @@ export class MultiSignatureTransactionHandler extends TransactionHandler { ): Promise { const { data }: Interfaces.ITransaction = transaction; - if (Utils.isException(data.id)) { - return; - } - AppUtils.assert.defined(data.asset?.multiSignature); const { publicKeys, min } = data.asset.multiSignature; @@ -122,13 +99,11 @@ export class MultiSignatureTransactionHandler extends TransactionHandler { // Create the multi sig wallet const walletRepository: Contracts.State.WalletRepository = customWalletRepository ?? this.walletRepository; - if (transaction.data.version && transaction.data.version >= 2) { - AppUtils.assert.defined(transaction.data.asset?.multiSignature); + AppUtils.assert.defined(transaction.data.asset?.multiSignature); - walletRepository - .findByAddress(Identities.Address.fromMultiSignatureAsset(transaction.data.asset.multiSignature)) - .setAttribute("multiSignature", transaction.data.asset.multiSignature); - } + walletRepository + .findByAddress(Identities.Address.fromMultiSignatureAsset(transaction.data.asset.multiSignature)) + .setAttribute("multiSignature", transaction.data.asset.multiSignature); } public async revertForSender( @@ -146,17 +121,15 @@ export class MultiSignatureTransactionHandler extends TransactionHandler { ): Promise { const { data }: Interfaces.ITransaction = transaction; - if (data.version && data.version >= 2) { - const walletRepository: Contracts.State.WalletRepository = customWalletRepository ?? this.walletRepository; + const walletRepository: Contracts.State.WalletRepository = customWalletRepository ?? this.walletRepository; - AppUtils.assert.defined(data.asset?.multiSignature); + AppUtils.assert.defined(data.asset?.multiSignature); - const recipientWallet: Contracts.State.Wallet = walletRepository.findByAddress( - Identities.Address.fromMultiSignatureAsset(data.asset.multiSignature), - ); + const recipientWallet: Contracts.State.Wallet = walletRepository.findByAddress( + Identities.Address.fromMultiSignatureAsset(data.asset.multiSignature), + ); - recipientWallet.setAttribute("multiSignature", data.asset.multiSignature); - } + recipientWallet.setAttribute("multiSignature", data.asset.multiSignature); } public async revertForRecipient( @@ -165,16 +138,15 @@ export class MultiSignatureTransactionHandler extends TransactionHandler { ): Promise { const { data }: Interfaces.ITransaction = transaction; - if (data.version && data.version >= 2) { - const walletRepository: Contracts.State.WalletRepository = customWalletRepository ?? this.walletRepository; + const walletRepository: Contracts.State.WalletRepository = customWalletRepository ?? this.walletRepository; - AppUtils.assert.defined(data.asset?.multiSignature); + AppUtils.assert.defined(data.asset?.multiSignature); - const recipientWallet: Contracts.State.Wallet = walletRepository.findByAddress( - Identities.Address.fromMultiSignatureAsset(data.asset.multiSignature), - ); + const recipientWallet: Contracts.State.Wallet = walletRepository.findByAddress( + Identities.Address.fromMultiSignatureAsset(data.asset.multiSignature), + ); - recipientWallet.forgetAttribute("multiSignature"); - } + recipientWallet.forgetAttribute("multiSignature"); } + } diff --git a/packages/core-transactions/src/handlers/two/second-signature-registration.ts b/packages/core-transactions/src/handlers/two/second-signature-registration.ts new file mode 100644 index 0000000000..63d9a0b3e8 --- /dev/null +++ b/packages/core-transactions/src/handlers/two/second-signature-registration.ts @@ -0,0 +1,23 @@ +import { Container } from "@arkecosystem/core-kernel"; +import { Transactions } from "@arkecosystem/crypto"; +import { TransactionReader } from "../../transaction-reader"; +import { Models } from "@arkecosystem/core-database"; +import { One } from "../index"; + +@Container.injectable() +export class SecondSignatureRegistrationTransactionHandler extends One.SecondSignatureRegistrationTransactionHandler { + + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.SecondSignatureRegistrationTransaction; + } + + public async bootstrap(): Promise { + const reader: TransactionReader = this.getTransactionReader(); + const transactions: Models.Transaction[] = await reader.read(); + for (const transaction of transactions) { + const wallet = this.walletRepository.findByPublicKey(transaction.senderPublicKey); + wallet.setAttribute("secondPublicKey", transaction.asset.signature!.publicKey); + } + } + +} \ No newline at end of file diff --git a/packages/core-transactions/src/handlers/two/transfer.ts b/packages/core-transactions/src/handlers/two/transfer.ts new file mode 100644 index 0000000000..3cfb918be7 --- /dev/null +++ b/packages/core-transactions/src/handlers/two/transfer.ts @@ -0,0 +1,20 @@ +import { Transactions } from "@arkecosystem/crypto"; +import { Container, Contracts } from "@arkecosystem/core-kernel"; +import { One } from "../index"; + +@Container.injectable() +export class TransferTransactionHandler extends One.TransferTransactionHandler { + + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.TransferTransaction; + } + + public async bootstrap(): Promise { + const transactions = await this.transactionRepository.findReceivedTransactions(); + for (const transaction of transactions) { + const wallet: Contracts.State.Wallet = this.walletRepository.findByAddress(transaction.recipientId); + wallet.balance = wallet.balance.plus(transaction.amount); + } + } + +} \ No newline at end of file diff --git a/packages/core-transactions/src/handlers/two/vote.ts b/packages/core-transactions/src/handlers/two/vote.ts new file mode 100644 index 0000000000..d2cc767cf4 --- /dev/null +++ b/packages/core-transactions/src/handlers/two/vote.ts @@ -0,0 +1,43 @@ +import { Transactions } from "@arkecosystem/crypto"; +import { TransactionHandlerConstructor } from "../transaction"; +import { Container } from "@arkecosystem/core-kernel"; +import { TransactionReader } from "../../transaction-reader"; +import { Models } from "@arkecosystem/core-database"; +import { AlreadyVotedError, NoVoteError, UnvoteMismatchError } from "../../errors"; +import { One, Two } from "../index"; + +@Container.injectable() +export class VoteTransactionHandler extends One.VoteTransactionHandler { + public getConstructor(): Transactions.TransactionConstructor { + return Transactions.Two.VoteTransaction; + } + + public dependencies(): ReadonlyArray { + return [Two.DelegateRegistrationTransactionHandler]; + } + + public async bootstrap(): Promise { + const reader: TransactionReader = this.getTransactionReader(); + const transactions: Models.Transaction[] = await reader.read(); + for (const transaction of transactions) { + const wallet = this.walletRepository.findByPublicKey(transaction.senderPublicKey); + const vote = transaction.asset.votes![0]; + const hasVoted: boolean = wallet.hasAttribute("vote"); + + if (vote.startsWith("+")) { + if (hasVoted) { + throw new AlreadyVotedError(); + } + wallet.setAttribute("vote", vote.slice(1)); + } else { + if (!hasVoted) { + throw new NoVoteError(); + } else if (wallet.getAttribute("vote") !== vote.slice(1)) { + throw new UnvoteMismatchError(); + } + wallet.forgetAttribute("vote"); + } + } + } + +} \ No newline at end of file diff --git a/packages/crypto/src/errors.ts b/packages/crypto/src/errors.ts index bf003ff375..0626c77008 100644 --- a/packages/crypto/src/errors.ts +++ b/packages/crypto/src/errors.ts @@ -112,9 +112,9 @@ export class TransactionKeyAlreadyRegisteredError extends CryptoError { } } -export class CoreTransactionTypeGroupImmutableError extends CryptoError { - constructor() { - super(`The Core transaction type group is immutable.`); +export class TransactionVersionAlreadyRegisteredError extends CryptoError { + constructor(name: string, version: number) { + super(`Transaction type ${name} is already registered in version ${version}.`); } } @@ -146,8 +146,8 @@ export class PreviousBlockIdFormatError extends CryptoError { constructor(thisBlockHeight: number, previousBlockId: string) { super( `The config denotes that the block at height ${thisBlockHeight - 1} ` + - `must use full SHA256 block id, but the next block (at ${thisBlockHeight}) ` + - `contains previous block id "${previousBlockId}"`, + `must use full SHA256 block id, but the next block (at ${thisBlockHeight}) ` + + `contains previous block id "${previousBlockId}"`, ); } } diff --git a/packages/crypto/src/transactions/builders/transactions/delegate-registration.ts b/packages/crypto/src/transactions/builders/transactions/delegate-registration.ts index 547aaf1667..0335f32309 100644 --- a/packages/crypto/src/transactions/builders/transactions/delegate-registration.ts +++ b/packages/crypto/src/transactions/builders/transactions/delegate-registration.ts @@ -1,15 +1,15 @@ import { ITransactionAsset, ITransactionData } from "../../../interfaces"; import { BigNumber } from "../../../utils"; -import { DelegateRegistrationTransaction } from "../../types"; +import { Two } from "../../types"; import { TransactionBuilder } from "./transaction"; export class DelegateRegistrationBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = DelegateRegistrationTransaction.type; - this.data.typeGroup = DelegateRegistrationTransaction.typeGroup; - this.data.fee = DelegateRegistrationTransaction.staticFee(); + this.data.type = Two.DelegateRegistrationTransaction.type; + this.data.typeGroup = Two.DelegateRegistrationTransaction.typeGroup; + this.data.fee = Two.DelegateRegistrationTransaction.staticFee(); this.data.amount = BigNumber.ZERO; this.data.recipientId = undefined; this.data.senderPublicKey = undefined; diff --git a/packages/crypto/src/transactions/builders/transactions/delegate-resignation.ts b/packages/crypto/src/transactions/builders/transactions/delegate-resignation.ts index 661ad5fb80..705a6ab148 100644 --- a/packages/crypto/src/transactions/builders/transactions/delegate-resignation.ts +++ b/packages/crypto/src/transactions/builders/transactions/delegate-resignation.ts @@ -1,16 +1,16 @@ import { ITransactionData } from "../../../interfaces"; import { BigNumber } from "../../../utils"; -import { DelegateResignationTransaction } from "../../types"; +import { Two } from "../../types"; import { TransactionBuilder } from "./transaction"; export class DelegateResignationBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = DelegateResignationTransaction.type; - this.data.typeGroup = DelegateResignationTransaction.typeGroup; + this.data.type = Two.DelegateResignationTransaction.type; + this.data.typeGroup = Two.DelegateResignationTransaction.typeGroup; this.data.version = 2; - this.data.fee = DelegateResignationTransaction.staticFee(); + this.data.fee = Two.DelegateResignationTransaction.staticFee(); this.data.amount = BigNumber.ZERO; this.data.senderPublicKey = undefined; } diff --git a/packages/crypto/src/transactions/builders/transactions/htlc-claim.ts b/packages/crypto/src/transactions/builders/transactions/htlc-claim.ts index caad0c94bb..207ec20c8f 100644 --- a/packages/crypto/src/transactions/builders/transactions/htlc-claim.ts +++ b/packages/crypto/src/transactions/builders/transactions/htlc-claim.ts @@ -1,15 +1,15 @@ import { IHtlcClaimAsset, ITransactionData } from "../../../interfaces"; import { BigNumber } from "../../../utils"; -import { HtlcClaimTransaction } from "../../types"; +import { Two } from "../../types"; import { TransactionBuilder } from "./transaction"; export class HtlcClaimBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = HtlcClaimTransaction.type; - this.data.typeGroup = HtlcClaimTransaction.typeGroup; - this.data.fee = HtlcClaimTransaction.staticFee(); + this.data.type = Two.HtlcClaimTransaction.type; + this.data.typeGroup = Two.HtlcClaimTransaction.typeGroup; + this.data.fee = Two.HtlcClaimTransaction.staticFee(); this.data.amount = BigNumber.ZERO; this.data.asset = {}; } diff --git a/packages/crypto/src/transactions/builders/transactions/htlc-lock.ts b/packages/crypto/src/transactions/builders/transactions/htlc-lock.ts index 2f6323fb02..f16ea9e1e6 100644 --- a/packages/crypto/src/transactions/builders/transactions/htlc-lock.ts +++ b/packages/crypto/src/transactions/builders/transactions/htlc-lock.ts @@ -1,17 +1,17 @@ import { IHtlcLockAsset, ITransactionData } from "../../../interfaces"; import { BigNumber } from "../../../utils"; -import { HtlcLockTransaction } from "../../types"; +import { Two } from "../../types"; import { TransactionBuilder } from "./transaction"; export class HtlcLockBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = HtlcLockTransaction.type; - this.data.typeGroup = HtlcLockTransaction.typeGroup; + this.data.type = Two.HtlcLockTransaction.type; + this.data.typeGroup = Two.HtlcLockTransaction.typeGroup; this.data.recipientId = undefined; this.data.amount = BigNumber.ZERO; - this.data.fee = HtlcLockTransaction.staticFee(); + this.data.fee = Two.HtlcLockTransaction.staticFee(); this.data.vendorField = undefined; this.data.asset = {}; } diff --git a/packages/crypto/src/transactions/builders/transactions/htlc-refund.ts b/packages/crypto/src/transactions/builders/transactions/htlc-refund.ts index 70a633ca37..39a6758fec 100644 --- a/packages/crypto/src/transactions/builders/transactions/htlc-refund.ts +++ b/packages/crypto/src/transactions/builders/transactions/htlc-refund.ts @@ -1,15 +1,15 @@ import { IHtlcRefundAsset, ITransactionData } from "../../../interfaces"; import { BigNumber } from "../../../utils"; -import { HtlcRefundTransaction } from "../../types"; +import { Two } from "../../types"; import { TransactionBuilder } from "./transaction"; export class HtlcRefundBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = HtlcRefundTransaction.type; - this.data.typeGroup = HtlcRefundTransaction.typeGroup; - this.data.fee = HtlcRefundTransaction.staticFee(); + this.data.type = Two.HtlcRefundTransaction.type; + this.data.typeGroup = Two.HtlcRefundTransaction.typeGroup; + this.data.fee = Two.HtlcRefundTransaction.staticFee(); this.data.amount = BigNumber.ZERO; this.data.asset = {}; } diff --git a/packages/crypto/src/transactions/builders/transactions/ipfs.ts b/packages/crypto/src/transactions/builders/transactions/ipfs.ts index 89cb9920ea..044eb71d65 100644 --- a/packages/crypto/src/transactions/builders/transactions/ipfs.ts +++ b/packages/crypto/src/transactions/builders/transactions/ipfs.ts @@ -1,15 +1,15 @@ import { ITransactionData } from "../../../interfaces"; import { BigNumber } from "../../../utils"; -import { IpfsTransaction } from "../../types"; +import { Two } from "../../types"; import { TransactionBuilder } from "./transaction"; export class IPFSBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = IpfsTransaction.type; - this.data.typeGroup = IpfsTransaction.typeGroup; - this.data.fee = IpfsTransaction.staticFee(); + this.data.type = Two.IpfsTransaction.type; + this.data.typeGroup = Two.IpfsTransaction.typeGroup; + this.data.fee = Two.IpfsTransaction.staticFee(); this.data.amount = BigNumber.ZERO; this.data.asset = {}; } diff --git a/packages/crypto/src/transactions/builders/transactions/multi-payment.ts b/packages/crypto/src/transactions/builders/transactions/multi-payment.ts index 875eb254f5..c2ffac8347 100644 --- a/packages/crypto/src/transactions/builders/transactions/multi-payment.ts +++ b/packages/crypto/src/transactions/builders/transactions/multi-payment.ts @@ -2,16 +2,16 @@ import { MaximumPaymentCountExceededError } from "../../../errors"; import { ITransactionData } from "../../../interfaces"; import { configManager } from "../../../managers"; import { BigNumber } from "../../../utils"; -import { MultiPaymentTransaction } from "../../types"; +import { Two } from "../../types"; import { TransactionBuilder } from "./transaction"; export class MultiPaymentBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = MultiPaymentTransaction.type; - this.data.typeGroup = MultiPaymentTransaction.typeGroup; - this.data.fee = MultiPaymentTransaction.staticFee(); + this.data.type = Two.MultiPaymentTransaction.type; + this.data.typeGroup = Two.MultiPaymentTransaction.typeGroup; + this.data.fee = Two.MultiPaymentTransaction.staticFee(); this.data.vendorField = undefined; this.data.asset = { payments: [], diff --git a/packages/crypto/src/transactions/builders/transactions/multi-signature.ts b/packages/crypto/src/transactions/builders/transactions/multi-signature.ts index b4bdf480c6..5bbfd87c97 100644 --- a/packages/crypto/src/transactions/builders/transactions/multi-signature.ts +++ b/packages/crypto/src/transactions/builders/transactions/multi-signature.ts @@ -1,14 +1,14 @@ import { IMultiSignatureAsset, ITransactionData } from "../../../interfaces"; import { BigNumber } from "../../../utils"; -import { MultiSignatureRegistrationTransaction } from "../../types"; +import { Two } from "../../types"; import { TransactionBuilder } from "./transaction"; export class MultiSignatureBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = MultiSignatureRegistrationTransaction.type; - this.data.typeGroup = MultiSignatureRegistrationTransaction.typeGroup; + this.data.type = Two.MultiSignatureRegistrationTransaction.type; + this.data.typeGroup = Two.MultiSignatureRegistrationTransaction.typeGroup; this.data.version = 2; this.data.fee = BigNumber.ZERO; this.data.amount = BigNumber.ZERO; @@ -23,7 +23,7 @@ export class MultiSignatureBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = SecondSignatureRegistrationTransaction.type; - this.data.typeGroup = SecondSignatureRegistrationTransaction.typeGroup; - this.data.fee = SecondSignatureRegistrationTransaction.staticFee(); + this.data.type = Two.SecondSignatureRegistrationTransaction.type; + this.data.typeGroup = Two.SecondSignatureRegistrationTransaction.typeGroup; + this.data.fee = Two.SecondSignatureRegistrationTransaction.staticFee(); this.data.amount = BigNumber.ZERO; this.data.recipientId = undefined; this.data.senderPublicKey = undefined; diff --git a/packages/crypto/src/transactions/builders/transactions/transfer.ts b/packages/crypto/src/transactions/builders/transactions/transfer.ts index 5eeb999672..1156575beb 100644 --- a/packages/crypto/src/transactions/builders/transactions/transfer.ts +++ b/packages/crypto/src/transactions/builders/transactions/transfer.ts @@ -1,15 +1,15 @@ import { ITransactionData } from "../../../interfaces"; import { BigNumber } from "../../../utils"; -import { TransferTransaction } from "../../types"; +import { Two } from "../../types"; import { TransactionBuilder } from "./transaction"; export class TransferBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = TransferTransaction.type; - this.data.typeGroup = TransferTransaction.typeGroup; - this.data.fee = TransferTransaction.staticFee(); + this.data.type = Two.TransferTransaction.type; + this.data.typeGroup = Two.TransferTransaction.typeGroup; + this.data.fee = Two.TransferTransaction.staticFee(); this.data.amount = BigNumber.ZERO; this.data.recipientId = undefined; this.data.senderPublicKey = undefined; diff --git a/packages/crypto/src/transactions/builders/transactions/vote.ts b/packages/crypto/src/transactions/builders/transactions/vote.ts index cb1b5b3648..18881d775a 100644 --- a/packages/crypto/src/transactions/builders/transactions/vote.ts +++ b/packages/crypto/src/transactions/builders/transactions/vote.ts @@ -1,15 +1,15 @@ import { ITransactionData } from "../../../interfaces"; import { BigNumber } from "../../../utils"; -import { VoteTransaction } from "../../types"; +import { Two } from "../../types"; import { TransactionBuilder } from "./transaction"; export class VoteBuilder extends TransactionBuilder { constructor() { super(); - this.data.type = VoteTransaction.type; - this.data.typeGroup = VoteTransaction.typeGroup; - this.data.fee = VoteTransaction.staticFee(); + this.data.type = Two.VoteTransaction.type; + this.data.typeGroup = Two.VoteTransaction.typeGroup; + this.data.fee = Two.VoteTransaction.staticFee(); this.data.amount = BigNumber.ZERO; this.data.recipientId = undefined; this.data.senderPublicKey = undefined; diff --git a/packages/crypto/src/transactions/registry.ts b/packages/crypto/src/transactions/registry.ts index 76aefac6a0..013bcb22a3 100644 --- a/packages/crypto/src/transactions/registry.ts +++ b/packages/crypto/src/transactions/registry.ts @@ -1,47 +1,51 @@ -import { TransactionTypeGroup } from "../enums"; import { - CoreTransactionTypeGroupImmutableError, TransactionAlreadyRegisteredError, TransactionKeyAlreadyRegisteredError, UnkownTransactionError, + TransactionVersionAlreadyRegisteredError, } from "../errors"; import { validator } from "../validation"; import { - DelegateRegistrationTransaction, - DelegateResignationTransaction, - HtlcClaimTransaction, - HtlcLockTransaction, - HtlcRefundTransaction, - IpfsTransaction, - MultiPaymentTransaction, - MultiSignatureRegistrationTransaction, - SecondSignatureRegistrationTransaction, + One, + Two, + Transaction, TransactionTypeFactory, - TransferTransaction, - VoteTransaction, } from "./types"; import { InternalTransactionType } from "./types/internal-transaction-type"; export type TransactionConstructor = typeof Transaction; class TransactionRegistry { - private readonly transactionTypes: Map = new Map(); + private readonly transactionTypes: Map> = new Map(); constructor() { TransactionTypeFactory.initialize(this.transactionTypes); - this.registerTransactionType(TransferTransaction); - this.registerTransactionType(SecondSignatureRegistrationTransaction); - this.registerTransactionType(DelegateRegistrationTransaction); - this.registerTransactionType(VoteTransaction); - this.registerTransactionType(MultiSignatureRegistrationTransaction); - this.registerTransactionType(IpfsTransaction); - this.registerTransactionType(MultiPaymentTransaction); - this.registerTransactionType(DelegateResignationTransaction); - this.registerTransactionType(HtlcLockTransaction); - this.registerTransactionType(HtlcClaimTransaction); - this.registerTransactionType(HtlcRefundTransaction); + this.registerTransactionType(One.TransferTransaction); + this.registerTransactionType(Two.TransferTransaction); + + this.registerTransactionType(One.SecondSignatureRegistrationTransaction); + this.registerTransactionType(Two.SecondSignatureRegistrationTransaction); + + this.registerTransactionType(One.DelegateRegistrationTransaction); + this.registerTransactionType(Two.DelegateRegistrationTransaction); + + this.registerTransactionType(One.VoteTransaction); + this.registerTransactionType(Two.VoteTransaction); + + this.registerTransactionType(One.MultiSignatureRegistrationTransaction); + this.registerTransactionType(Two.MultiSignatureRegistrationTransaction); + + this.registerTransactionType(Two.IpfsTransaction); + + this.registerTransactionType(Two.MultiPaymentTransaction); + + this.registerTransactionType(Two.DelegateResignationTransaction); + + this.registerTransactionType(Two.HtlcLockTransaction); + this.registerTransactionType(Two.HtlcClaimTransaction); + this.registerTransactionType(Two.HtlcRefundTransaction); } public registerTransactionType(constructor: TransactionConstructor): void { @@ -51,52 +55,59 @@ class TransactionRegistry { throw new Error(); } - const internalType: InternalTransactionType | undefined = InternalTransactionType.from(type, typeGroup); - - if (!internalType) { - throw new Error(); - } - - if (this.transactionTypes.has(internalType)) { - throw new TransactionAlreadyRegisteredError(constructor.name); + const internalType: InternalTransactionType = InternalTransactionType.from(type, typeGroup); + for (const registeredConstructors of this.transactionTypes.values()) { + if (registeredConstructors.size) { + const first = [...registeredConstructors.values()][0]; + if ( + first.key === constructor.key && + InternalTransactionType.from(first.type!, first.typeGroup) !== internalType + ) { + throw new TransactionKeyAlreadyRegisteredError(first.key!); + } + + for (const registeredConstructor of registeredConstructors.values()) { + if (registeredConstructor === constructor) { + throw new TransactionAlreadyRegisteredError(constructor.name); + } + } + } } - if (Array.from(this.transactionTypes.values()).some(({ key }) => key === constructor.key)) { - throw new TransactionKeyAlreadyRegisteredError(constructor.key!); + if (!this.transactionTypes.has(internalType)) { + this.transactionTypes.set(internalType, new Map()); + } else if (this.transactionTypes.get(internalType)?.has(constructor.version)) { + throw new TransactionVersionAlreadyRegisteredError(constructor.name, constructor.version); } - this.transactionTypes.set(internalType, constructor); + this.transactionTypes.get(internalType)!.set(constructor.version, constructor); this.updateSchemas(constructor); } public deregisterTransactionType(constructor: TransactionConstructor): void { - const { typeGroup, type } = constructor; + const { typeGroup, type, version } = constructor; if (typeof type === "undefined" || typeof typeGroup === "undefined") { throw new Error(); } - const internalType: InternalTransactionType | undefined = InternalTransactionType.from(type, typeGroup); - - if (!internalType) { - throw new Error(); - } - + const internalType: InternalTransactionType = InternalTransactionType.from(type, typeGroup); if (!this.transactionTypes.has(internalType)) { throw new UnkownTransactionError(internalType.toString()); } - if (typeGroup === TransactionTypeGroup.Core) { - throw new CoreTransactionTypeGroupImmutableError(); + this.updateSchemas(constructor, true); + + const constructors = this.transactionTypes.get(internalType)!; + if (!constructors.has(version)) { + throw new UnkownTransactionError(internalType.toString()); } - const schema = this.transactionTypes.get(internalType); + constructors.delete(version); - if (schema) { - this.updateSchemas(schema, true); + if (constructors.size === 0) { + this.transactionTypes.delete(internalType); } - - this.transactionTypes.delete(internalType); } private updateSchemas(transaction: TransactionConstructor, remove?: boolean): void { diff --git a/packages/crypto/src/transactions/serializer.ts b/packages/crypto/src/transactions/serializer.ts index d2aca0b4a8..65dbf598e1 100644 --- a/packages/crypto/src/transactions/serializer.ts +++ b/packages/crypto/src/transactions/serializer.ts @@ -9,7 +9,7 @@ import { ISerializeOptions } from "../interfaces"; import { ITransaction, ITransactionData } from "../interfaces"; import { configManager } from "../managers/config"; import { isSupportedTansactionVersion } from "../utils"; -import { TransactionTypeFactory } from "./types"; +import { TransactionTypeFactory } from "./types/factory"; // Reference: https://github.com/ArkEcosystem/AIPs/blob/master/AIPS/aip-11.md export class Serializer { diff --git a/packages/crypto/src/transactions/types/factory.ts b/packages/crypto/src/transactions/types/factory.ts index 2cf2f5f01a..72456c0c27 100644 --- a/packages/crypto/src/transactions/types/factory.ts +++ b/packages/crypto/src/transactions/types/factory.ts @@ -4,31 +4,29 @@ import { ITransaction, ITransactionData } from "../../interfaces"; import { InternalTransactionType } from "./internal-transaction-type"; export class TransactionTypeFactory { - public static initialize(transactionTypes: Map) { + private static transactionTypes: Map>; + + public static initialize(transactionTypes: Map>) { this.transactionTypes = transactionTypes; } public static create(data: ITransactionData): ITransaction { - const instance: ITransaction = new (this.get(data.type, data.typeGroup) as any)() as ITransaction; + const instance: ITransaction = new (this.get(data.type, data.typeGroup, data.version) as any)() as ITransaction; instance.data = data; instance.data.version = data.version || 1; return instance; } - public static get(type: number, typeGroup?: number): TransactionConstructor | undefined { - const internalType: InternalTransactionType | undefined = InternalTransactionType.from(type, typeGroup); - - if (!internalType) { - throw new Error(); - } + public static get(type: number, typeGroup?: number, version?: number): TransactionConstructor | undefined { + const internalType: InternalTransactionType = InternalTransactionType.from(type, typeGroup); - if (this.transactionTypes.has(internalType)) { - return this.transactionTypes.get(internalType); + if (!this.transactionTypes.has(internalType)) { + throw new UnkownTransactionError(internalType.toString()); } - throw new UnkownTransactionError(internalType.toString()); + // Either there is a match for the provided version or use the first available constructor as a fallback + const constructor: TransactionConstructor | undefined = this.transactionTypes.get(internalType)?.get(version || 1); + return constructor ?? [...this.transactionTypes.get(internalType)!.values()][0]; } - - private static transactionTypes: Map; } diff --git a/packages/crypto/src/transactions/types/index.ts b/packages/crypto/src/transactions/types/index.ts index 1540ede66c..22ae4acc06 100644 --- a/packages/crypto/src/transactions/types/index.ts +++ b/packages/crypto/src/transactions/types/index.ts @@ -1,18 +1,10 @@ export * from "./transaction"; -export * from "./transfer"; -export * from "./second-signature"; -export * from "./delegate-registration"; -export * from "./vote"; -export * from "./multi-signature"; -export * from "./ipfs"; -export * from "./multi-payment"; -export * from "./delegate-resignation"; -export * from "./htlc-lock"; -export * from "./htlc-claim"; -export * from "./htlc-refund"; export * from "./factory"; export * from "./internal-transaction-type"; +import * as One from "./one"; +import * as Two from "./two"; + import * as schemas from "./schemas"; -export { schemas }; +export { One, Two, schemas }; diff --git a/packages/crypto/src/transactions/types/internal-transaction-type.ts b/packages/crypto/src/transactions/types/internal-transaction-type.ts index 527af68fa5..8516ab3b07 100644 --- a/packages/crypto/src/transactions/types/internal-transaction-type.ts +++ b/packages/crypto/src/transactions/types/internal-transaction-type.ts @@ -1,7 +1,9 @@ import { TransactionTypeGroup } from "../../enums"; export class InternalTransactionType { - public static from(type: number, typeGroup?: number): InternalTransactionType | undefined { + private static types: Map = new Map(); + + public static from(type: number, typeGroup?: number): InternalTransactionType { if (typeGroup === undefined) { typeGroup = TransactionTypeGroup.Core; } @@ -11,12 +13,10 @@ export class InternalTransactionType { this.types.set(compositeType, new InternalTransactionType(type, typeGroup)); } - return this.types.get(compositeType); + return this.types.get(compositeType)!; } - private static types: Map = new Map(); - - private constructor(public readonly type: number, public readonly typeGroup: number) {} + private constructor(public readonly type: number, public readonly typeGroup: number) { } public toString(): string { if (this.typeGroup === TransactionTypeGroup.Core) { diff --git a/packages/crypto/src/transactions/types/multi-signature.ts b/packages/crypto/src/transactions/types/multi-signature.ts deleted file mode 100644 index 5557c187a2..0000000000 --- a/packages/crypto/src/transactions/types/multi-signature.ts +++ /dev/null @@ -1,119 +0,0 @@ -import ByteBuffer from "bytebuffer"; - -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { - IMultiSignatureAsset, - IMultiSignatureLegacyAsset, - ISerializeOptions, - ITransactionData, -} from "../../interfaces"; -import { configManager } from "../../managers"; -import { isException } from "../../utils"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; - -export class MultiSignatureRegistrationTransaction extends Transaction { - public static typeGroup: number = TransactionTypeGroup.Core; - public static type: number = TransactionType.MultiSignature; - public static key = "multiSignature"; - - public static getSchema(): schemas.TransactionSchema { - return schemas.multiSignature; - } - - public static staticFee(feeContext: { height?: number; data?: ITransactionData } = {}): BigNumber { - const staticFee = super.staticFee(feeContext); - - const data: ITransactionData | undefined = feeContext.data; - if (data && data.asset && data.asset.multiSignature) { - if (data.version === 2) { - return staticFee.times(data.asset.multiSignature.publicKeys.length + 1); - } - - if (data.asset.multiSignatureLegacy) { - return staticFee.times(data.asset.multiSignatureLegacy.keysgroup.length + 1); - } - } - - return staticFee; - } - - protected static defaultStaticFee: BigNumber = BigNumber.make("500000000"); - - public verify(): boolean { - return isException(this.data.id) || (configManager.getMilestone().aip11 && super.verify()); - } - - public serialize(options?: ISerializeOptions): ByteBuffer | undefined { - const { data } = this; - - if (!data.asset) { - return undefined; - } - - let buffer: ByteBuffer | undefined; - - if (data.version === 2) { - if (data.asset.multiSignature) { - const { min, publicKeys } = data.asset.multiSignature; - buffer = new ByteBuffer(2 + publicKeys.length * 33); - - buffer.writeUint8(min); - buffer.writeUint8(publicKeys.length); - - for (const publicKey of publicKeys) { - buffer.append(publicKey, "hex"); - } - } - } else { - if (data.asset.multiSignatureLegacy) { - // Legacy - const joined: string = data.asset.multiSignatureLegacy.keysgroup - .map(k => (k.startsWith("+") ? k.slice(1) : k)) - .join(""); - const keysgroupBuffer: Buffer = Buffer.from(joined, "hex"); - - buffer = new ByteBuffer(keysgroupBuffer.length + 3, true); - buffer.writeByte(data.asset.multiSignatureLegacy.min); - buffer.writeByte(data.asset.multiSignatureLegacy.keysgroup.length); - buffer.writeByte(data.asset.multiSignatureLegacy.lifetime); - buffer.append(keysgroupBuffer, "hex"); - } - } - - return buffer; - } - - public deserialize(buf: ByteBuffer): void { - const { data } = this; - - if (data.version === 2) { - const multiSignature: IMultiSignatureAsset = { publicKeys: [], min: 0 }; - multiSignature.min = buf.readUint8(); - - const count = buf.readUint8(); - for (let i = 0; i < count; i++) { - const publicKey = buf.readBytes(33).toString("hex"); - multiSignature.publicKeys.push(publicKey); - } - - data.asset = { multiSignature }; - } else { - // Legacy - const multiSignatureLegacy: IMultiSignatureLegacyAsset = { keysgroup: [], lifetime: 0, min: 0 }; - - multiSignatureLegacy.min = buf.readUint8(); - - const num: number = buf.readUint8(); - multiSignatureLegacy.lifetime = buf.readUint8(); - - for (let index = 0; index < num; index++) { - const key: string = buf.readBytes(33).toString("hex"); - multiSignatureLegacy.keysgroup.push(key); - } - - data.asset = { multiSignatureLegacy }; - } - } -} diff --git a/packages/crypto/src/transactions/types/delegate-registration.ts b/packages/crypto/src/transactions/types/one/delegate-registration.ts similarity index 75% rename from packages/crypto/src/transactions/types/delegate-registration.ts rename to packages/crypto/src/transactions/types/one/delegate-registration.ts index f12ba1c529..4b1486b402 100644 --- a/packages/crypto/src/transactions/types/delegate-registration.ts +++ b/packages/crypto/src/transactions/types/one/delegate-registration.ts @@ -1,15 +1,16 @@ import ByteBuffer from "bytebuffer"; -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { ISerializeOptions } from "../../interfaces"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import { ISerializeOptions } from "../../../interfaces"; +import { BigNumber } from "../../../utils/bignum"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; -export class DelegateRegistrationTransaction extends Transaction { +export abstract class DelegateRegistrationTransaction extends Transaction { public static typeGroup: number = TransactionTypeGroup.Core; public static type: number = TransactionType.DelegateRegistration; public static key = "delegateRegistration"; + public static version: number = 1; public static getSchema(): schemas.TransactionSchema { return schemas.delegateRegistration; diff --git a/packages/crypto/src/transactions/types/one/index.ts b/packages/crypto/src/transactions/types/one/index.ts new file mode 100644 index 0000000000..0bfabe8e16 --- /dev/null +++ b/packages/crypto/src/transactions/types/one/index.ts @@ -0,0 +1,5 @@ +export * from "./transfer"; +export * from "./second-signature-registration"; +export * from "./delegate-registration"; +export * from "./vote"; +export * from "./multi-signature-registration"; diff --git a/packages/crypto/src/transactions/types/one/multi-signature-registration.ts b/packages/crypto/src/transactions/types/one/multi-signature-registration.ts new file mode 100644 index 0000000000..f41a3a9d64 --- /dev/null +++ b/packages/crypto/src/transactions/types/one/multi-signature-registration.ts @@ -0,0 +1,68 @@ +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; +import ByteBuffer from "bytebuffer"; + +import { BigNumber } from "../../../utils/bignum"; +import { isException } from "../../../utils"; +import { ITransactionData, ISerializeOptions, IMultiSignatureLegacyAsset } from "../../../interfaces"; + +export abstract class MultiSignatureRegistrationTransaction extends Transaction { + public static typeGroup: number = TransactionTypeGroup.Core; + public static type: number = TransactionType.MultiSignature; + public static key = "multiSignature"; + public static version: number = 1; + + public static getSchema(): schemas.TransactionSchema { + return schemas.multiSignature; + } + + public static staticFee(feeContext: { height?: number; data?: ITransactionData } = {}): BigNumber { + if (feeContext.data?.asset?.multiSignatureLegacy) { + return super.staticFee(feeContext).times(feeContext!.data!.asset!.multiSignatureLegacy!.keysgroup.length + 1); + } + + return super.staticFee(feeContext); + } + + protected static defaultStaticFee: BigNumber = BigNumber.make("500000000"); + + public verify(): boolean { + return isException(this.data.id); + } + + public serialize(options?: ISerializeOptions): ByteBuffer | undefined { + const { data } = this; + + const legacyAsset: IMultiSignatureLegacyAsset = data!.asset!.multiSignatureLegacy!; + const joined: string = legacyAsset.keysgroup + .map(k => (k.startsWith("+") ? k.slice(1) : k)) + .join(""); + const keysgroupBuffer: Buffer = Buffer.from(joined, "hex"); + const buffer: ByteBuffer = new ByteBuffer(keysgroupBuffer.length + 3, true); + + buffer.writeByte(legacyAsset.min); + buffer.writeByte(legacyAsset.keysgroup.length); + buffer.writeByte(legacyAsset.lifetime); + buffer.append(keysgroupBuffer, "hex"); + + return buffer; + } + + public deserialize(buf: ByteBuffer): void { + const { data } = this; + + const multiSignatureLegacy: IMultiSignatureLegacyAsset = { keysgroup: [], lifetime: 0, min: 0 }; + multiSignatureLegacy.min = buf.readUint8(); + + const num: number = buf.readUint8(); + multiSignatureLegacy.lifetime = buf.readUint8(); + + for (let index = 0; index < num; index++) { + const key: string = buf.readBytes(33).toString("hex"); + multiSignatureLegacy.keysgroup.push(key); + } + + data.asset = { multiSignatureLegacy }; + } +} diff --git a/packages/crypto/src/transactions/types/second-signature.ts b/packages/crypto/src/transactions/types/one/second-signature-registration.ts similarity index 71% rename from packages/crypto/src/transactions/types/second-signature.ts rename to packages/crypto/src/transactions/types/one/second-signature-registration.ts index cc65d9cfda..542c83b87e 100644 --- a/packages/crypto/src/transactions/types/second-signature.ts +++ b/packages/crypto/src/transactions/types/one/second-signature-registration.ts @@ -1,15 +1,16 @@ import ByteBuffer from "bytebuffer"; -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { ISerializeOptions } from "../../interfaces"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import { ISerializeOptions } from "../../../interfaces"; +import { BigNumber } from "../../../utils/bignum"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; -export class SecondSignatureRegistrationTransaction extends Transaction { +export abstract class SecondSignatureRegistrationTransaction extends Transaction { public static typeGroup: number = TransactionTypeGroup.Core; public static type: number = TransactionType.SecondSignature; public static key = "secondSignature"; + public static version: number = 1; public static getSchema(): schemas.TransactionSchema { return schemas.secondSignature; diff --git a/packages/crypto/src/transactions/types/transfer.ts b/packages/crypto/src/transactions/types/one/transfer.ts similarity index 76% rename from packages/crypto/src/transactions/types/transfer.ts rename to packages/crypto/src/transactions/types/one/transfer.ts index e5c71b84f5..38adb6542a 100644 --- a/packages/crypto/src/transactions/types/transfer.ts +++ b/packages/crypto/src/transactions/types/one/transfer.ts @@ -1,17 +1,18 @@ import ByteBuffer from "bytebuffer"; import Long from "long"; -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { Address } from "../../identities"; -import { ISerializeOptions } from "../../interfaces"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; - -export class TransferTransaction extends Transaction { +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import { Address } from "../../../identities"; +import { ISerializeOptions } from "../../../interfaces"; +import { BigNumber } from "../../../utils/bignum"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; + +export abstract class TransferTransaction extends Transaction { public static typeGroup: number = TransactionTypeGroup.Core; public static type: number = TransactionType.Transfer; public static key = "transfer"; + public static version: number = 1; public static getSchema(): schemas.TransactionSchema { return schemas.transfer; diff --git a/packages/crypto/src/transactions/types/vote.ts b/packages/crypto/src/transactions/types/one/vote.ts similarity index 82% rename from packages/crypto/src/transactions/types/vote.ts rename to packages/crypto/src/transactions/types/one/vote.ts index 6e2ba5af8b..9dc6c7520c 100644 --- a/packages/crypto/src/transactions/types/vote.ts +++ b/packages/crypto/src/transactions/types/one/vote.ts @@ -1,15 +1,16 @@ import ByteBuffer from "bytebuffer"; -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { ISerializeOptions } from "../../interfaces"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import { ISerializeOptions } from "../../../interfaces"; +import { BigNumber } from "../../../utils/bignum"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; export class VoteTransaction extends Transaction { public static typeGroup: number = TransactionTypeGroup.Core; public static type: number = TransactionType.Vote; public static key = "vote"; + public static version: number = 1; public static getSchema(): schemas.TransactionSchema { return schemas.vote; diff --git a/packages/crypto/src/transactions/types/transaction.ts b/packages/crypto/src/transactions/types/transaction.ts index 6a9be5cdc4..dba3e38c87 100644 --- a/packages/crypto/src/transactions/types/transaction.ts +++ b/packages/crypto/src/transactions/types/transaction.ts @@ -33,6 +33,7 @@ export abstract class Transaction implements ITransaction { public static type: number | undefined = undefined; public static typeGroup: number | undefined = undefined; + public static version: number = 1; public static key: string | undefined = undefined; public static getSchema(): TransactionSchema { diff --git a/packages/crypto/src/transactions/types/two/delegate-registration.ts b/packages/crypto/src/transactions/types/two/delegate-registration.ts new file mode 100644 index 0000000000..58e8339204 --- /dev/null +++ b/packages/crypto/src/transactions/types/two/delegate-registration.ts @@ -0,0 +1,5 @@ +import { One } from "../index"; + +export class DelegateRegistrationTransaction extends One.DelegateRegistrationTransaction { + public static version: number = 2; +} \ No newline at end of file diff --git a/packages/crypto/src/transactions/types/delegate-resignation.ts b/packages/crypto/src/transactions/types/two/delegate-resignation.ts similarity index 62% rename from packages/crypto/src/transactions/types/delegate-resignation.ts rename to packages/crypto/src/transactions/types/two/delegate-resignation.ts index dd4948ac06..aca3a76c15 100644 --- a/packages/crypto/src/transactions/types/delegate-resignation.ts +++ b/packages/crypto/src/transactions/types/two/delegate-resignation.ts @@ -1,16 +1,17 @@ import ByteBuffer from "bytebuffer"; -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { ISerializeOptions } from "../../interfaces"; -import { configManager } from "../../managers"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import { ISerializeOptions } from "../../../interfaces"; +import { configManager } from "../../../managers"; +import { BigNumber } from "../../../utils/bignum"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; -export class DelegateResignationTransaction extends Transaction { +export abstract class DelegateResignationTransaction extends Transaction { public static typeGroup: number = TransactionTypeGroup.Core; public static type: number = TransactionType.DelegateResignation; public static key = "delegateResignation"; + public static version: number = 2; public static getSchema(): schemas.TransactionSchema { return schemas.delegateResignation; diff --git a/packages/crypto/src/transactions/types/htlc-claim.ts b/packages/crypto/src/transactions/types/two/htlc-claim.ts similarity index 74% rename from packages/crypto/src/transactions/types/htlc-claim.ts rename to packages/crypto/src/transactions/types/two/htlc-claim.ts index 165de364fd..ce63cf14e0 100644 --- a/packages/crypto/src/transactions/types/htlc-claim.ts +++ b/packages/crypto/src/transactions/types/two/htlc-claim.ts @@ -1,16 +1,17 @@ import ByteBuffer from "bytebuffer"; -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { ISerializeOptions } from "../../interfaces"; -import { configManager } from "../../managers"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; - -export class HtlcClaimTransaction extends Transaction { +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import { ISerializeOptions } from "../../../interfaces"; +import { configManager } from "../../../managers"; +import { BigNumber } from "../../../utils/bignum"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; + +export abstract class HtlcClaimTransaction extends Transaction { public static typeGroup: number = TransactionTypeGroup.Core; public static type: number = TransactionType.HtlcClaim; public static key = "htlcClaim"; + public static version: number = 2; public static getSchema(): schemas.TransactionSchema { return schemas.htlcClaim; diff --git a/packages/crypto/src/transactions/types/htlc-lock.ts b/packages/crypto/src/transactions/types/two/htlc-lock.ts similarity index 80% rename from packages/crypto/src/transactions/types/htlc-lock.ts rename to packages/crypto/src/transactions/types/two/htlc-lock.ts index 2acf35e8e5..af60069908 100644 --- a/packages/crypto/src/transactions/types/htlc-lock.ts +++ b/packages/crypto/src/transactions/types/two/htlc-lock.ts @@ -1,18 +1,19 @@ import ByteBuffer from "bytebuffer"; import Long from "long"; -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { Address } from "../../identities"; -import { ISerializeOptions } from "../../interfaces"; -import { configManager } from "../../managers"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import { Address } from "../../../identities"; +import { ISerializeOptions } from "../../../interfaces"; +import { configManager } from "../../../managers"; +import { BigNumber } from "../../../utils/bignum"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; -export class HtlcLockTransaction extends Transaction { +export abstract class HtlcLockTransaction extends Transaction { public static typeGroup: number = TransactionTypeGroup.Core; public static type: number = TransactionType.HtlcLock; public static key = "htlcLock"; + public static version: number = 2; public static getSchema(): schemas.TransactionSchema { return schemas.htlcLock; diff --git a/packages/crypto/src/transactions/types/htlc-refund.ts b/packages/crypto/src/transactions/types/two/htlc-refund.ts similarity index 72% rename from packages/crypto/src/transactions/types/htlc-refund.ts rename to packages/crypto/src/transactions/types/two/htlc-refund.ts index f7bbedabc4..cd741453e9 100644 --- a/packages/crypto/src/transactions/types/htlc-refund.ts +++ b/packages/crypto/src/transactions/types/two/htlc-refund.ts @@ -1,16 +1,17 @@ import ByteBuffer from "bytebuffer"; -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { ISerializeOptions } from "../../interfaces"; -import { configManager } from "../../managers"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; - -export class HtlcRefundTransaction extends Transaction { +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import { ISerializeOptions } from "../../../interfaces"; +import { configManager } from "../../../managers"; +import { BigNumber } from "../../../utils/bignum"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; + +export abstract class HtlcRefundTransaction extends Transaction { public static typeGroup: number = TransactionTypeGroup.Core; public static type: number = TransactionType.HtlcRefund; public static key = "htlcRefund"; + public static version: number = 2; public static getSchema(): schemas.TransactionSchema { return schemas.htlcRefund; diff --git a/packages/crypto/src/transactions/types/two/index.ts b/packages/crypto/src/transactions/types/two/index.ts new file mode 100644 index 0000000000..4a216f65e7 --- /dev/null +++ b/packages/crypto/src/transactions/types/two/index.ts @@ -0,0 +1,11 @@ +export * from "./transfer"; +export * from "./second-signature-registration"; +export * from "./delegate-registration"; +export * from "./vote"; +export * from "./multi-signature-registration"; +export * from "./ipfs"; +export * from "./multi-payment"; +export * from "./delegate-resignation"; +export * from "./htlc-lock"; +export * from "./htlc-claim"; +export * from "./htlc-refund"; diff --git a/packages/crypto/src/transactions/types/ipfs.ts b/packages/crypto/src/transactions/types/two/ipfs.ts similarity index 77% rename from packages/crypto/src/transactions/types/ipfs.ts rename to packages/crypto/src/transactions/types/two/ipfs.ts index 7cf429d058..899ff30124 100644 --- a/packages/crypto/src/transactions/types/ipfs.ts +++ b/packages/crypto/src/transactions/types/two/ipfs.ts @@ -1,17 +1,18 @@ import { base58 } from "bstring"; import ByteBuffer from "bytebuffer"; -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { ISerializeOptions } from "../../interfaces"; -import { configManager } from "../../managers"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; - -export class IpfsTransaction extends Transaction { +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import { ISerializeOptions } from "../../../interfaces"; +import { configManager } from "../../../managers"; +import { BigNumber } from "../../../utils/bignum"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; + +export abstract class IpfsTransaction extends Transaction { public static typeGroup: number = TransactionTypeGroup.Core; public static type: number = TransactionType.Ipfs; public static key = "ipfs"; + public static version: number = 2; public static getSchema(): schemas.TransactionSchema { return schemas.ipfs; diff --git a/packages/crypto/src/transactions/types/multi-payment.ts b/packages/crypto/src/transactions/types/two/multi-payment.ts similarity index 79% rename from packages/crypto/src/transactions/types/multi-payment.ts rename to packages/crypto/src/transactions/types/two/multi-payment.ts index 3d80f40cae..d31e2d5084 100644 --- a/packages/crypto/src/transactions/types/multi-payment.ts +++ b/packages/crypto/src/transactions/types/two/multi-payment.ts @@ -1,18 +1,19 @@ import ByteBuffer from "bytebuffer"; import Long from "long"; -import { TransactionType, TransactionTypeGroup } from "../../enums"; -import { Address } from "../../identities"; -import { IMultiPaymentItem, ISerializeOptions } from "../../interfaces"; -import { configManager } from "../../managers"; -import { BigNumber } from "../../utils/bignum"; -import * as schemas from "./schemas"; -import { Transaction } from "./transaction"; - -export class MultiPaymentTransaction extends Transaction { +import { TransactionType, TransactionTypeGroup } from "../../../enums"; +import { Address } from "../../../identities"; +import { IMultiPaymentItem, ISerializeOptions } from "../../../interfaces"; +import { configManager } from "../../../managers"; +import { BigNumber } from "../../../utils/bignum"; +import * as schemas from "../schemas"; +import { Transaction } from "../transaction"; + +export abstract class MultiPaymentTransaction extends Transaction { public static typeGroup: number = TransactionTypeGroup.Core; public static type: number = TransactionType.MultiPayment; public static key = "multiPayment"; + public static version: number = 2; public static getSchema(): schemas.TransactionSchema { return schemas.multiPayment; diff --git a/packages/crypto/src/transactions/types/two/multi-signature-registration.ts b/packages/crypto/src/transactions/types/two/multi-signature-registration.ts new file mode 100644 index 0000000000..48abf35689 --- /dev/null +++ b/packages/crypto/src/transactions/types/two/multi-signature-registration.ts @@ -0,0 +1,63 @@ +import * as schemas from "../schemas"; +import ByteBuffer from "bytebuffer"; + +import { BigNumber } from "../../../utils/bignum"; +import { ITransactionData, ISerializeOptions, IMultiSignatureAsset } from "../../../interfaces"; +import { configManager } from "../../../managers"; +import { Transaction } from "../transaction"; +import { TransactionTypeGroup, TransactionType } from "../../../enums"; + +export class MultiSignatureRegistrationTransaction extends Transaction { + public static typeGroup: number = TransactionTypeGroup.Core; + public static type: number = TransactionType.MultiSignature; + public static key = "multiSignature"; + public static version: number = 2; + + protected static defaultStaticFee: BigNumber = BigNumber.make("500000000"); + + public static getSchema(): schemas.TransactionSchema { + return schemas.multiSignature; + } + + public static staticFee(feeContext: { height?: number; data?: ITransactionData } = {}): BigNumber { + if (feeContext.data?.asset?.multiSignature) { + return super.staticFee(feeContext).times(feeContext!.data!.asset!.multiSignature!.publicKeys.length + 1); + } + + return super.staticFee(feeContext); + } + + public verify(): boolean { + return configManager.getMilestone().aip11 && super.verify(); + } + + public serialize(options?: ISerializeOptions): ByteBuffer | undefined { + const { data } = this; + const { min, publicKeys } = data!.asset!.multiSignature!; + const buffer: ByteBuffer = new ByteBuffer(2 + publicKeys.length * 33); + + buffer.writeUint8(min); + buffer.writeUint8(publicKeys.length); + + for (const publicKey of publicKeys) { + buffer.append(publicKey, "hex"); + } + + return buffer; + } + + public deserialize(buf: ByteBuffer): void { + const { data } = this; + + const multiSignature: IMultiSignatureAsset = { publicKeys: [], min: 0 }; + multiSignature.min = buf.readUint8(); + + const count = buf.readUint8(); + for (let i = 0; i < count; i++) { + const publicKey = buf.readBytes(33).toString("hex"); + multiSignature.publicKeys.push(publicKey); + } + + data.asset = { multiSignature }; + } +} \ No newline at end of file diff --git a/packages/crypto/src/transactions/types/two/second-signature-registration.ts b/packages/crypto/src/transactions/types/two/second-signature-registration.ts new file mode 100644 index 0000000000..fd7a6f4568 --- /dev/null +++ b/packages/crypto/src/transactions/types/two/second-signature-registration.ts @@ -0,0 +1,5 @@ +import { One } from "../index"; + +export class SecondSignatureRegistrationTransaction extends One.SecondSignatureRegistrationTransaction { + public static version: number = 2; +} \ No newline at end of file diff --git a/packages/crypto/src/transactions/types/two/transfer.ts b/packages/crypto/src/transactions/types/two/transfer.ts new file mode 100644 index 0000000000..12a5dac9ab --- /dev/null +++ b/packages/crypto/src/transactions/types/two/transfer.ts @@ -0,0 +1,5 @@ +import { One } from "../index"; + +export class TransferTransaction extends One.TransferTransaction { + public static version: number = 2; +} \ No newline at end of file diff --git a/packages/crypto/src/transactions/types/two/vote.ts b/packages/crypto/src/transactions/types/two/vote.ts new file mode 100644 index 0000000000..afa0827e40 --- /dev/null +++ b/packages/crypto/src/transactions/types/two/vote.ts @@ -0,0 +1,5 @@ +import { One } from "../index"; + +export class VoteTransaction extends One.VoteTransaction { + public static version: number = 2; +} \ No newline at end of file diff --git a/packages/crypto/src/transactions/verifier.ts b/packages/crypto/src/transactions/verifier.ts index 0392dfe3a0..042cc8df37 100644 --- a/packages/crypto/src/transactions/verifier.ts +++ b/packages/crypto/src/transactions/verifier.ts @@ -4,7 +4,7 @@ import { IMultiSignatureAsset, ISchemaValidationResult, ITransactionData } from import { configManager } from "../managers"; import { isException } from "../utils"; import { validator } from "../validation"; -import { TransactionTypeFactory } from "./types"; +import { TransactionTypeFactory } from "./types/factory"; import { Utils } from "./utils"; export class Verifier { @@ -95,7 +95,7 @@ export class Verifier { } public static verifySchema(data: ITransactionData, strict = true): ISchemaValidationResult { - const transactionType = TransactionTypeFactory.get(data.type, data.typeGroup); + const transactionType = TransactionTypeFactory.get(data.type, data.typeGroup, data.version); if (!transactionType) { throw new Error(); diff --git a/packages/crypto/src/validation/index.ts b/packages/crypto/src/validation/index.ts index ee143ac2fb..abfd12ca1f 100644 --- a/packages/crypto/src/validation/index.ts +++ b/packages/crypto/src/validation/index.ts @@ -101,20 +101,25 @@ export class Validator { } private extendTransactionSchema(ajv: Ajv.Ajv, schema: TransactionSchema, remove?: boolean) { + if (ajv.getSchema(schema.$id)) { + remove = true; + } + if (remove) { this.transactionSchemas.delete(schema.$id); ajv.removeSchema(schema.$id); ajv.removeSchema(`${schema.$id}Signed`); ajv.removeSchema(`${schema.$id}Strict`); - } else { - this.transactionSchemas.set(schema.$id, schema); - ajv.addSchema(schema); - ajv.addSchema(signedSchema(schema)); - ajv.addSchema(strictSchema(schema)); } + this.transactionSchemas.set(schema.$id, schema); + + ajv.addSchema(schema); + ajv.addSchema(signedSchema(schema)); + ajv.addSchema(strictSchema(schema)); + this.updateTransactionArray(ajv); }