Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: pre aip11 compatibility #82

Merged
merged 2 commits into from Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
67 changes: 56 additions & 11 deletions packages/rpc/__tests__/server/transactions.test.ts
@@ -1,6 +1,7 @@
import "jest-extended";

import { Transactions } from "@arkecosystem/crypto";
import { BigNumber } from "@arkecosystem/crypto/dist/utils";
import { Server } from "@hapi/hapi";
import { randomBytes } from "crypto";
import nock from "nock";
Expand Down Expand Up @@ -61,17 +62,7 @@ describe("Transactions", () => {
},
],
})
.get("/api/blockchain")
.reply(200, {
data: {
block: {
height: 10702529,
id: "aa6f13f08e32db84991ec8a7b19558b99027eac2d02920d19ded2222a55fba0a",
},
supply: "14625386000000004",
},
})
.get("/api/node/fees")
.get("/api/node/fees?days=30")
.reply(200, {
meta: {
days: 7,
Expand Down Expand Up @@ -118,6 +109,19 @@ describe("Transactions", () => {
},
});

const blockchainNock = nock(/\d+\.\d+\.\d+\.\d+/)
.persist()
.get("/api/blockchain")
.reply(200, {
data: {
block: {
height: 10702529,
id: "aa6f13f08e32db84991ec8a7b19558b99027eac2d02920d19ded2222a55fba0a",
},
supply: "14625386000000004",
},
});

describe("POST transactions.info", () => {
it("should get the transaction for the given ID", async () => {
nock(/\d+\.\d+\.\d+\.\d+/)
Expand Down Expand Up @@ -167,6 +171,18 @@ describe("Transactions", () => {
expect(verifyTransaction(response.body.result)).toBeTrue();
});

it("should add a nonce if aip11 is enabled", async () => {
const response = await sendRequest(server, "transactions.create", {
amount: 100000000,
recipientId: "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
passphrase: "this is a top secret passphrase",
});

expect(response.body.result.recipientId).toBe("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib");
expect(response.body.result.nonce.toString()).toBe("2");
expect(verifyTransaction(response.body.result)).toBeTrue();
});

it("should create a new transfer with a vendor field and verify", async () => {
const response = await sendRequest(server, "transactions.create", {
amount: 100000000,
Expand Down Expand Up @@ -386,5 +402,34 @@ describe("Transactions", () => {
expect(response.body.error.code).toBe(422);
expect(spyVerify).toHaveBeenCalled();
});

it("should not add a nonce if aip11 is not enabled", async () => {
blockchainNock.persist(false);
nock.abortPendingRequests();

nock(/\d+\.\d+\.\d+\.\d+/)
.persist()
.get("/api/blockchain")
.reply(200, {
data: {
block: {
height: 1,
id: "aa6f13f08e32db84991ec8a7b19558b99027eac2d02920d19ded2222a55fba0a",
},
supply: "14625386000000004",
},
});

const newServer = await createServer(); // Need to initialize it again to set the height
const response = await sendRequest(newServer, "transactions.create", {
amount: 100000000,
recipientId: "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib",
passphrase: "this is a top secret passphrase",
});

expect(response.body.result.recipientId).toBe("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib");
expect(response.body.result.nonce).toBe(BigNumber.ZERO);
expect(verifyTransaction(response.body.result)).toBeTrue();
});
});
});
14 changes: 12 additions & 2 deletions packages/rpc/src/services/network.ts
Expand Up @@ -26,7 +26,7 @@ class Network {

Managers.configManager.setFromPreset(options.network);

Managers.configManager.setHeight(await this.getHeight());
this.checkForAip11Enabled();
}

public async sendGET({ path, query = {} }: { path: string; query?: Record<string, any> }) {
Expand All @@ -37,10 +37,20 @@ class Network {
return this.sendRequest("post", path, { body });
}

private async getHeight(): Promise<number> {
public async getHeight(): Promise<number> {
return (await this.sendGET({ path: "blockchain" })).data.block.height;
}

private async checkForAip11Enabled() {
const height = await this.getHeight();
Managers.configManager.setHeight(height);

const milestone = Managers.configManager.getMilestone(height);
if (!milestone.aip11) {
setTimeout(() => this.checkForAip11Enabled(), milestone.blocktime * 1000);
}
}

private async sendRequest(method: string, url: string, options, tries: number = 0, useSeed: boolean = false) {
try {
const peer: IPeer = await this.getPeer();
Expand Down
45 changes: 24 additions & 21 deletions packages/rpc/src/utils/transactions.ts
@@ -1,4 +1,4 @@
import { Identities, Interfaces, Transactions, Utils } from "@arkecosystem/crypto";
import { Identities, Interfaces, Managers, Transactions, Utils } from "@arkecosystem/crypto";
import { notStrictEqual } from "assert";
import { logger } from "../services/logger";
import { network } from "../services/network";
Expand Down Expand Up @@ -29,26 +29,29 @@ const buildTransaction = async (
}
}

// Get the nonce of the sender wallet
const senderAddress: string =
method === "sign"
? Identities.Address.fromPassphrase(params.passphrase)
: Identities.Address.fromPublicKey(Identities.PublicKey.fromWIF(params.passphrase));

try {
const { data } = await network.sendGET({
path: `wallets/${senderAddress}`,
});

notStrictEqual(data.nonce, undefined);

transactionBuilder.nonce(
Utils.BigNumber.make(data.nonce)
.plus(1)
.toFixed(),
);
} catch (error) {
throw new Error(`Failed to retrieve the nonce for ${senderAddress}.`);
const milestone = Managers.configManager.getMilestone(await network.getHeight());
if (milestone.aip11) {
// If AIP11 is enabled, get the nonce of the sender wallet
const senderAddress: string =
method === "sign"
? Identities.Address.fromPassphrase(params.passphrase)
: Identities.Address.fromPublicKey(Identities.PublicKey.fromWIF(params.passphrase));

try {
const { data } = await network.sendGET({
path: `wallets/${senderAddress}`,
});

notStrictEqual(data.nonce, undefined);

transactionBuilder.nonce(
Utils.BigNumber.make(data.nonce)
.plus(1)
.toFixed(),
);
} catch (error) {
throw new Error(`Failed to retrieve the nonce for ${senderAddress}.`);
}
}

const transaction: Interfaces.ITransactionData = transactionBuilder[method](params.passphrase).getStruct();
Expand Down