postchain-client
Version:
Client library for accessing a Postchain node through REST.
216 lines • 13 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { DifferentNumberOfSignersException, MissingTransactionProof, ProofRidException, SignatureException, SystemChainException, } from "../../../src/ICCF/error";
import { calculateBlockRID, fetchAndVerifyTransaction, getAnchoringTransactionForBlockRid, getClusterOfBlockchain, } from "../../../src/ICCF/utils";
import { ensureString, toBuffer } from "../../../src/formatter";
import { gtvHash } from "../../../src/gtv";
import { encodeValue, encodeValueGtx } from "../../../src/gtx/serialization";
import { getDigestToSign, gtxToRawGtx, sign } from "../../../src/gtx/gtx";
import { makeKeyPair } from "../../../src/encryption/encryption";
import { createHeaderArray, getTestsClient } from "../../common/setups";
import { HttpResponse, http } from "msw";
import { server } from "../../../mocks/servers";
import { contentTypes, LOCAL_POOL, mockBufferBlockchainRid, mockStringBlockchainRid, mockThirtyTwoBytesBuffer, } from "../../common/mocks";
import { MERKLE_HASH_VERSIONS } from "../../../src/utils/constants";
import { UnexpectedStatusError } from "../../../src/blockchainClient/errors";
function encodeGtxToBuffer(gtx) {
return encodeValueGtx(gtxToRawGtx(gtx));
}
function createGtxObject(keypairs, op) {
return __awaiter(this, void 0, void 0, function* () {
let gtx = {
blockchainRid: toBuffer("fcd29927aa06e75547036c8860633af2d8df8ff6c78f947a2d7c816cb13dc201"),
operations: op === undefined
? [
{
opName: "setInteger",
args: ["126d2"],
},
]
: op,
signers: keypairs.map(key => key.pubKey),
signatures: [],
};
keypairs.forEach((key) => __awaiter(this, void 0, void 0, function* () {
gtx = yield sign(gtx, key.privKey, 1, key.pubKey);
}));
return gtx;
});
}
const privkeyA = toBuffer("0101010101010101010101010101010101010101010101010101010101010101");
const keypairA = makeKeyPair(privkeyA);
const privkeyB = toBuffer("2222222222222222222222222222222222222222222222222222222222222222");
const keypairB = makeKeyPair(privkeyB);
let client;
describe("ICCF tests", () => {
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
server.listen();
client = yield getTestsClient({
failOverConfig: { attemptInterval: 100 },
});
}));
afterEach(() => {
server.resetHandlers();
});
const mockTransactionRequest = (rid, gtxResponseTx) => {
server.use(http.get(`${LOCAL_POOL}/tx/${client.config.blockchainRid}/${rid.toString("hex")}`, () => {
return HttpResponse.json({
tx: encodeGtxToBuffer(gtxResponseTx).toString("hex").toUpperCase(),
});
}));
};
const mockTransactionErrorOnRequest = (rid, error) => {
server.use(http.get(`${LOCAL_POOL}/tx/${client.config.blockchainRid}/${ensureString(rid)}`, () => {
return new HttpResponse(error, {
status: 404,
});
}));
};
describe("fetchAndVerifyTransaction", () => {
it("should fetch and verify transaction", () => __awaiter(void 0, void 0, void 0, function* () {
const gtxResponseTx = yield createGtxObject([keypairA]);
const rid = getDigestToSign(gtxResponseTx, 1);
const txProofHash = gtvHash(gtxToRawGtx(gtxResponseTx), 1);
mockTransactionRequest(rid, gtxResponseTx);
const { verifiedTxHash } = yield fetchAndVerifyTransaction(client, rid, txProofHash, [
keypairA.pubKey,
]);
expect(verifiedTxHash).toEqual(txProofHash);
}));
it("throws error if fetched tx hash does not match proof hash", () => __awaiter(void 0, void 0, void 0, function* () {
const gtxResponseTx = yield createGtxObject([keypairA]);
mockTransactionRequest(mockThirtyTwoBytesBuffer, gtxResponseTx);
const proofHash = Buffer.alloc(32, "a");
yield expect(fetchAndVerifyTransaction(client, mockThirtyTwoBytesBuffer, proofHash, [keypairA.pubKey])).rejects.toThrow(/Unable to verify source transaction proof/);
}));
it("throws error if fetched tx number of signatures mismatches", () => __awaiter(void 0, void 0, void 0, function* () {
const gtxResponseTx = yield createGtxObject([keypairA]);
mockTransactionRequest(mockThirtyTwoBytesBuffer, gtxResponseTx);
const txProofHash = gtvHash(gtxToRawGtx(gtxResponseTx), 1);
yield expect(fetchAndVerifyTransaction(client, mockThirtyTwoBytesBuffer, txProofHash, [])).rejects.toThrow(/Transaction signatures amount 1 do not match expected amount of signers 0/);
}));
it("throws error if transaction RID cannot be found", () => __awaiter(void 0, void 0, void 0, function* () {
const gtxResponseTx = yield createGtxObject([keypairA]);
const rid = getDigestToSign(gtxResponseTx, 1);
const txProofHash = gtvHash(gtxToRawGtx(gtxResponseTx), 1);
mockTransactionErrorOnRequest(rid, `{ error: "Can't find transaction with RID: ${rid.toString("hex")}" }`);
yield expect(fetchAndVerifyTransaction(client, rid, txProofHash, [keypairA.pubKey])).rejects.toThrow(new UnexpectedStatusError(404, `{ error: "Can't find transaction with RID: ${rid.toString("hex")}" }`));
}));
it("fetch and verify transaction with unordered signatures", () => __awaiter(void 0, void 0, void 0, function* () {
const gtxResponseTx = yield createGtxObject([keypairA, keypairB]);
const rid = getDigestToSign(gtxResponseTx, 1);
const txProofHash = gtvHash(gtxToRawGtx(gtxResponseTx), 1);
mockTransactionRequest(rid, gtxResponseTx);
const { verifiedTxHash, verifiedTx } = yield fetchAndVerifyTransaction(client, rid, txProofHash, [keypairB.pubKey, keypairA.pubKey]);
expect(verifiedTxHash).toEqual(txProofHash);
expect(verifiedTx).toEqual(gtxResponseTx);
}));
it("should throw MissingTransactionProof when fetchedTxHash mismatches proofHash", () => __awaiter(void 0, void 0, void 0, function* () {
const proofHash = Buffer.from("SomeDifferentHash");
const gtxResponseTx = yield createGtxObject([keypairA]);
const rid = getDigestToSign(gtxResponseTx, 2);
mockTransactionRequest(rid, gtxResponseTx);
yield expect(fetchAndVerifyTransaction(client, rid, proofHash, [keypairA.pubKey])).rejects.toThrow(MissingTransactionProof);
}));
it("throws DifferentNumberOfSignersException for mismatched signature count", () => __awaiter(void 0, void 0, void 0, function* () {
const gtxResponseTx = yield createGtxObject([keypairA]);
const rid = getDigestToSign(gtxResponseTx, 1);
const txProofHash = gtvHash(gtxToRawGtx(gtxResponseTx), 1);
mockTransactionRequest(rid, gtxResponseTx);
yield expect(fetchAndVerifyTransaction(client, rid, txProofHash, [keypairA.pubKey, keypairB.pubKey])).rejects.toThrow(DifferentNumberOfSignersException);
}));
it("throws SignatureException if signer did not sign transaction", () => __awaiter(void 0, void 0, void 0, function* () {
const gtxResponseTx = yield createGtxObject([keypairA]);
const rid = getDigestToSign(gtxResponseTx, 1);
const txProofHash = gtvHash(gtxToRawGtx(gtxResponseTx), 1);
mockTransactionRequest(rid, gtxResponseTx);
yield expect(fetchAndVerifyTransaction(client, rid, txProofHash, [keypairB.pubKey])).rejects.toThrow(SignatureException);
}));
it("throws ProofRidException when fetched transaction RID differs", () => __awaiter(void 0, void 0, void 0, function* () {
const gtxResponseTx = yield createGtxObject([keypairA]);
const gtxFalseResponseTx = yield createGtxObject([keypairA], [{ opName: "fakeOperation", args: ["foo"] }]);
const rid = getDigestToSign(gtxResponseTx, 1);
mockTransactionRequest(rid, gtxFalseResponseTx);
yield expect(fetchAndVerifyTransaction(client, rid, gtvHash(gtxToRawGtx(gtxFalseResponseTx), MERKLE_HASH_VERSIONS.ONE), [keypairA.pubKey])).rejects.toThrow(ProofRidException);
}));
});
describe("getClusterOfBlockchain", () => {
it("returns cluster name", () => __awaiter(void 0, void 0, void 0, function* () {
const serverReturnCluster = "system";
server.use(http.post(`${LOCAL_POOL}/query_gtv/${mockStringBlockchainRid}`, () => HttpResponse.arrayBuffer(encodeValue(serverReturnCluster), {
status: 200,
headers: { "Content-Type": contentTypes.octetStream },
})));
const res = yield getClusterOfBlockchain(client, mockBufferBlockchainRid);
expect(res).toEqual(serverReturnCluster);
}));
it("throws error if query fails", () => __awaiter(void 0, void 0, void 0, function* () {
const serverReturn = toBuffer(`{"error": "Missing query type"}`);
server.use(http.post(`${LOCAL_POOL}/query_gtv/${mockStringBlockchainRid}`, () => new HttpResponse(`{ message: ${serverReturn} }`, { status: 400 })));
yield expect(getClusterOfBlockchain(client, mockBufferBlockchainRid)).rejects.toThrow(SystemChainException);
yield expect(getClusterOfBlockchain(client, mockBufferBlockchainRid)).rejects.toThrow(serverReturn.toString());
}));
});
describe("getClusterInfo", () => {
it("should throw error if query to get cluster info fails", () => __awaiter(void 0, void 0, void 0, function* () {
const serverReturn = toBuffer(`{"error": "Missing query type"}`);
server.use(http.post(`${LOCAL_POOL}/query_gtv/${mockStringBlockchainRid}`, () => {
return new HttpResponse(`{ message: ${serverReturn}}`, {
status: 400,
});
}));
yield expect(getClusterOfBlockchain(client, mockBufferBlockchainRid)).rejects.toThrow(SystemChainException);
}));
});
describe("getAnchoringTransactionForBlockRid", () => {
it("should throw error if query to get anchoring tx fails", () => __awaiter(void 0, void 0, void 0, function* () {
const apiResponse = `{error": "Missing query type"}`;
server.use(http.post(`${LOCAL_POOL}/query_gtv/${mockStringBlockchainRid}`, () => {
return new HttpResponse(`${apiResponse}`, {
status: 400,
});
}));
try {
yield getAnchoringTransactionForBlockRid(client, mockBufferBlockchainRid, mockThirtyTwoBytesBuffer);
}
catch (error) {
expect(error).toBeInstanceOf(SystemChainException);
expect(error.message).toContain(`Query to system chain failed with error: Unexpected status code from server. Code: 400. Message: ${apiResponse}.`);
}
}));
});
describe("calculateBlockRID", () => {
it("calculates block rid from a decoded proof", () => {
const arrayHeader = createHeaderArray();
const decodedTxProof = {
blockHeader: encodeValue(arrayHeader),
};
const sourceBlockRid = calculateBlockRID(decodedTxProof, 2);
expect(sourceBlockRid).toEqual(gtvHash(arrayHeader, 2));
});
it("throws an error when blockHeader is undefined", () => {
const decodedTxProof = {
blockHeader: undefined,
};
expect(() => {
calculateBlockRID(decodedTxProof, 2);
}).toThrow("Failed to get blockHeader from confirmation proof");
});
it("throws an error when block header can not be decoded", () => {
const decodedTxProof = {
blockHeader: mockThirtyTwoBytesBuffer,
};
expect(() => {
calculateBlockRID(decodedTxProof, 2);
}).toThrow("Choice not matched at: (shallow)");
});
});
});
//# sourceMappingURL=iccf.test.js.map