Commit 99553059 by chuxuewen

update liv

parent 7e2cd475
......@@ -2,11 +2,12 @@
"name": "zswjs",
"version": "1.3.5",
"description": "中数文API",
"main": "dist/index.js",
"main": "src/index.js",
"types": "src/index.ts",
"scripts": {
"cypress": "cypress run --spec 'cypress/integration/index.spec.js'",
"cypress-ui": "cypress open",
"prepare": "npm run build",
"_prepare": "npm run build",
"lint": "eslint --ext .js,.jsx,.ts,.tsx src",
"test": "jest src/tests/*zswjs*",
"test-node": "jest src/tests/*node*",
......@@ -27,35 +28,14 @@
"url": "https://github.com/zhongshuwen/zswjs.git"
},
"dependencies": {
"bn.js": "5.2.0",
"elliptic": "6.5.4",
"hash.js": "1.1.7",
"jsbn": "^1.1.0",
"pako": "2.0.3"
"bn.js": "~5.2.0",
"elliptic": "~6.5.4",
"hash.js": "~1.1.7",
"jsbn": "~1.1.0",
"pako": "~2.0.3"
},
"devDependencies": {
"@cypress/skip-test": "^2.6.1",
"@types/elliptic": "^6.4.13",
"@types/jest": "^26.0.24",
"@types/jsbn": "^1.2.30",
"@types/node": "^14.17.5",
"@types/node-fetch": "^2.5.11",
"@types/pako": "^1.0.2",
"buffer": "^6.0.3",
"clean-webpack-plugin": "^3.0.0",
"crypto-browserify": "^3.12.0",
"cypress": "^7.7.0",
"eslint": "^7.30.0",
"jest": "^26.6.3",
"jest-extended": "^0.11.5",
"jest-fetch-mock": "^3.0.3",
"rimraf": "^3.0.2",
"ts-jest": "^26.5.6",
"ts-loader": "^9.2.3",
"typescript": "^4.3.5",
"webpack": "^5.44.0",
"webpack-cli": "^4.7.2",
"zsw-crypto": "^1.1.0"
"@types/pako": "~1.0.3"
},
"jest": {
"automock": false,
......
......@@ -53,14 +53,14 @@ export class Signature {
let zswchainRecoveryParam;
if (keyType === KeyType.k1 || keyType === KeyType.r1) {
zswchainRecoveryParam = ellipticSig.recoveryParam + 27;
if (ellipticSig.recoveryParam <= 3) {
zswchainRecoveryParam = ellipticSig.recoveryParam! + 27;
if (ellipticSig.recoveryParam! <= 3) {
zswchainRecoveryParam += 4;
}
} else if (keyType === KeyType.wa) {
zswchainRecoveryParam = ellipticSig.recoveryParam;
}
const sigData = new Uint8Array([zswchainRecoveryParam].concat(r, s));
const sigData = new Uint8Array([zswchainRecoveryParam as number].concat(r, s));
if (!ec) {
ec = constructElliptic(keyType);
}
......@@ -82,7 +82,7 @@ export class Signature {
const r = new BN(this.signature.data.slice(1, lengthOfR + 1));
const s = new BN(this.signature.data.slice(lengthOfR + 1, lengthOfR + lengthOfS + 1));
let ellipticRecoveryBitField;
let ellipticRecoveryBitField: number;
if (this.signature.type === KeyType.k1 || this.signature.type === KeyType.r1) {
ellipticRecoveryBitField = this.signature.data[0] - 27;
if (ellipticRecoveryBitField > 3) {
......@@ -91,7 +91,7 @@ export class Signature {
} else if (this.signature.type === KeyType.wa) {
ellipticRecoveryBitField = this.signature.data[0];
}
const recoveryParam = ellipticRecoveryBitField & 3;
const recoveryParam = ellipticRecoveryBitField! & 3;
return { r, s, recoveryParam };
}
......
......@@ -27,12 +27,12 @@ function doEncrypt(msg: any, publicKey: any, cipherMode = 1) {
const y2 = _.hexToArray(_.leftPad(p.getY().toBigInteger().toRadix(16), 64))
// c3 = hash(x2 || msg || y2)
const c3 = _.arrayToHex(sm3([].concat(x2, msg, y2)))
const c3 = _.arrayToHex(sm3(([]as number[]).concat(x2, msg, y2)))
let ct = 1
let offset = 0
let t: any = [] // 256 位
const z = [].concat(x2, y2)
const z = ([]as number[]).concat(x2, y2)
const nextT = () => {
// (1) Hai = hash(z || ct)
// (2) ct++
......@@ -80,7 +80,7 @@ function doDecrypt(encryptData: any, privateKey: any, cipherMode = 1, {
let ct = 1
let offset = 0
let t: any = [] // 256 位
const z = [].concat(x2, y2)
const z = ([]as number[]).concat(x2, y2)
const nextT = () => {
// (1) Hai = hash(z || ct)
// (2) ct++
......@@ -99,7 +99,7 @@ function doDecrypt(encryptData: any, privateKey: any, cipherMode = 1, {
}
// c3 = hash(x2 || msg || y2)
const checkC3 = _.arrayToHex(sm3([].concat(x2, msg, y2)))
const checkC3 = _.arrayToHex(sm3(([]as number[]).concat(x2, msg, y2)))
if (checkC3 === c3) {
return output === 'array' ? msg : _.arrayToUtf8(msg)
......
......@@ -197,7 +197,7 @@ function main(array: any) {
lenArr[i] = 0
}
}
const m = [].concat(array, [0x80], kArr, lenArr)
const m = ([] as number[]).concat(array, [0x80], kArr, lenArr)
// 迭代压缩
const n = m.length / 64
......
<svg xmlns="http://www.w3.org/2000/svg" width="186" height="33"><path fill="#000" fill-rule="nonzero" d="M22.4492 32.0313v-5.836h14.3906V8.4766c-.1875-.2657-.6796-.8438-1.4765-1.7344h-12.914V.9062h-7.8985v5.836H.1602v17.3203c0 .0625.039.1094.1171.1406 1.0625 1.2344 1.6407 1.8985 1.7344 1.9922h12.539v5.836h7.8985Zm-7.8984-10.172H8.1055V8.547h.0234l1.3125 2.4843h5.1094v10.8282Zm14.3672 0h-6.4688v-10.828h6.4688v10.828Zm29.6718-5.5546c.0157 0 .0235-.0078.0235-.0235v-6h8.2969V5.9688h-8.297V.9063h-6.4687v5.0625h-8.625l1.5938 4.3125h7.0312v6c0 .0157.0157.0235.047.0235h6.3983ZM74.082 32.0312c.9375-1.3437 1.414-2.0156 1.4297-2.0156v.0235c.9219 1.2968 1.4063 1.9609 1.4531 1.9921h7.8516l-5.3672-7.5c1.75-2.4687 2.6563-3.75 2.7188-3.8437.5937-.7969.8906-1.2188.8906-1.2656V8.7344h.8203V3.7187h-8.8594c.25-1.3125.4375-2.25.5625-2.8125h-6.4922c-.0937.5157-.8046 4.0704-2.1328 10.6641h6.4688c.2031-1.0625.3125-1.5937.3281-1.5937.1406-.8282.2266-1.2422.2578-1.2422h2.5547v9.2812c0 .0157-.289.4375-.8672 1.2657h-.0234c0-.0157-.086-.1407-.2578-.375-.422-.5626-.6328-.8672-.6328-.9141v-5.625h-6.8204v7.0547c0 .0468.1329.25.3985.6093 2.2656 3.1876 3.4219 4.797 3.4687 4.8282v.0234c-.0156.0313-1.664 2.414-4.9453 7.1485h7.1953ZM51.7695 5.1954V5.172c0-.0313-.4687-1.2266-1.4062-3.586h-6.8438c.2287.5817.4226 1.0724.5817 1.4722l.1278.3204c.2586.6459.397.978.4155.9965.1875.5469.297.8203.3282.8203h6.7968Zm13.9922 0a1.9143 1.9143 0 0 0 .0238-.0573l.0364-.0899c.0964-.2394.3081-.77.635-1.5915l.1591-.3998c.1673-.4205.359-.903.5754-1.4474v-.0235h-6.7969l-1.4297 3.5625c0 .0313.0079.047.0235.047h6.7734ZM50.3633 16.3047l1.4297-5.3672h-6.8672l-1.4063 5.3672h6.8438Zm16.8281 0c-.0312-.1563-.5-1.9453-1.4062-5.3672h-6.8204c0 .0156.375 1.4531 1.125 4.3125.1875.7031.297 1.0547.3282 1.0547h6.7734ZM53.1992 32.0312a.0428.0428 0 0 0 .012-.003l.055-.0247c.2277-.111 1.0178-.5548 2.3705-1.3316.0469.0468.8281.5 2.3438 1.3593h8.25v-.6796c0-.0157-.1328-.1094-.3985-.2813-2.953-1.8281-4.6797-2.9219-5.1797-3.2812v-.0235h.0235c2.8437-1.625 4.3125-2.461 4.4062-2.5078v-2.6015h1.7813c.0312 0 .0469-.0079.0469-.0235v-4.2656H65.082v-1.3594h-6.4687v1.3594h-5.9531c-.0313 0-.047-.0156-.047-.0469v-1.3125H46.168v1.3594h-2.6485l1.5938 4.289h1.0547v2.6016l4.4297 2.5313c-.1407.1093-1.1641.7656-3.0704 1.9687-1.5156.9375-2.3046 1.4453-2.3671 1.5235v.75h8.039Zm2.4375-7.3828c-1.9531-1.2187-2.961-1.8671-3.0234-1.9453v-.0468h6v.0937c-1.9375 1.2031-2.9297 1.836-2.9766 1.8984Zm41.3203 7.3829.0662-.033c.4346-.2233 2.3031-1.267 5.6057-3.1311l.8116-.4492.4952-.2743c2.0663-1.1452 3.0995-1.7243 3.0995-1.7374.0312 0 .078.0156.1406.0468l7.4297 4.336c1.2968.7812 2.0078 1.1953 2.1328 1.2421h10.3828v-1.0078c-.25-.125-.7266-.3906-1.4297-.7968-7.91-4.574-12.072-6.9894-12.4859-7.2466l-.0297-.019c.3125-.172 1.8515-1.0313 4.6172-2.5782l.29-.1631a969.8684 969.8684 0 0 1 3.1005-1.7377l.2965-.165c.5102-.2835.8358-.462.977-.5358v-9h4.664V4.4688h-15.4452c-.2656-1-.5625-2.1875-.8906-3.5625h-7.547c.2032.8282.4923 2.0157.8673 3.5625H86.8789l1.5938 4.3125h3.8437v9c.1406.0782 1.9688 1.1485 5.4844 3.211 2.2031 1.2812 3.3125 1.9219 3.3281 1.9219 0 .0312-.0312.0625-.0937.0937-8.6563 4.8125-13.375 7.4297-14.1563 7.8516v1.1718H96.957Zm10.289-12.4922c-3.2812-1.875-4.9687-2.8594-5.0624-2.9532-1.2125-.675-1.95-1.095-2.2125-1.26l-.0762-.0499c-.018-.0126-.0285-.0213-.0316-.026V8.7812h15.0469v6.4922c-.5782.3125-3.1329 1.7344-7.6641 4.2657Zm45.9376 9.4453V3.3437h-6.5156v21.9376h-9.9375V18.578h-6.4922v8.8828c.6406.5313.9922.8204 1.0547.8672.5.4375.789.6563.8672.6563h21.0234Zm28.1484 0c.284-.013 1.2664-.0205 2.9472-.0228l.554-.0005.6004-.0002h.0234V15.4375c-.1875-.1875-.7656-.6563-1.7343-1.4063h-19.6172v-7.078h14.4843v3.7734h6.8204c.0312 0 .0468-.0157.0468-.047V4.797c-.2812-.2344-.961-.7578-2.039-1.5703h-26.1797v12.9843c.0312.0313.711.5391 2.039 1.5235h19.3125v7.5468h-14.4843v-3.7734h-6.8672v5.9531l2.0156 1.5c.1784 0 .3493 0 .5129.0002l.6856.0009c1.3416.0025 2.0124.01 2.0124.0224 1.75-.0157 3.2891-.0235 4.6172-.0235h6.4688c2.2969 0 4.8906.0079 7.7812.0235Z"/></svg>
\ No newline at end of file
const fs = require('fs');
const path = require('path');
const { JsonRpc, RpcError, Api } = require('../../dist');
const { JsSignatureProvider } = require('../../dist/zswjs-jssig');
const fetch = require('node-fetch');
const { TextEncoder, TextDecoder } = require('util');
const privateKey = '5JuH9fCXmU3xbj8nRmhPZaVrxxXrdPaRmZLW1cznNTmTQR2Kg5Z'; // replace with "bob" account private key
const r1PrivateKey = 'PVT_R1_GrfEfbv5at9kbeHcGagQmvbFLdm6jqEpgE1wsGbrfbZNjpVgT';
const cfactorPrivateKey = '5K8Sm2bB2b7ZC8tJMefrk1GFa4jgtHxxHRcjX49maMk9AEwq8hN';
/* new accounts for testing can be created by unlocking a clzsw wallet then calling:
* 1) clzsw create key --to-console (copy this privateKey & publicKey)
* 2) clzsw wallet import
* 3) clzsw create account bob publicKey
* 4) clzsw create account alice publicKey
*/
const rpc = new JsonRpc('http://localhost:8888', { fetch });
const signatureProvider = new JsSignatureProvider([privateKey, r1PrivateKey, cfactorPrivateKey]);
const api = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() });
const transactWithConfig = async (config, memo, from = 'bob', to = 'alice') => {
return await api.transact({
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: from,
permission: 'active',
}],
data: {
from,
to,
quantity: '0.0001 SYS',
memo,
},
}]
}, config);
};
const transactWithoutConfig = async () => {
const transactionResponse = await transactWithConfig({ blocksBehind: 3, expireSeconds: 30}, 'transactWithoutConfig');
const blockInfo = await rpc.get_block_info(transactionResponse.processed.block_num - 3);
const currentDate = new Date();
const timePlusTen = currentDate.getTime() + 10000;
const timeInISOString = (new Date(timePlusTen)).toISOString();
const expiration = timeInISOString.substr(0, timeInISOString.length - 1);
return await api.transact({
expiration,
ref_block_num: blockInfo.block_num & 0xffff,
ref_block_prefix: blockInfo.ref_block_prefix,
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}],
data: {
from: 'bob',
to: 'alice',
quantity: '0.0001 SYS',
memo: 'transactWithoutConfig2',
},
}]
});
};
const transactWithContextFreeAction = async () => {
return await api.transact({
actions: [{
account: 'cfhello',
name: 'normal',
authorization: [{
actor: 'cfactor',
permission: 'active'
}],
data: {
user: 'test'
}
}],
context_free_actions: [{
account: 'cfhello',
name: 'contextfree',
authorization: [],
data: {}
}]
}, {
blocksBehind: 3,
expireSeconds: 30
});
};
const transactWithContextFreeData = async () => {
return await api.transact({
actions:[{
account: 'cfhello',
name: 'normal',
authorization: [{
actor: 'cfactor',
permission: 'active'
}],
data: {
user: 'test2'
}
}],
context_free_actions: [{
account: 'cfhello',
name: 'contextfree',
authorization: [],
data: {}
}],
context_free_data: [[ '74657374', '7465737464617461' ]]
}, {
blocksBehind: 3,
expireSeconds: 30
});
};
const transactWithShorthandApiJson = async () => {
await api.getAbi('zsw.token');
return await api.transact({
actions: [
api.with('zsw.token').as('bob').transfer('bob', 'alice', '0.0001 SYS', 'transactWithShorthandApiJson')
]
}, {
blocksBehind: 3,
expireSeconds: 30
});
};
const transactWithShorthandTxJson = async () => {
await api.getAbi('zsw.token');
const tx = api.buildTransaction();
tx.with('zsw.token').as('bob').transfer('bob', 'alice', '0.0001 SYS', 'transactWithShorthandTxJson');
return await tx.send({
blocksBehind: 3,
expireSeconds: 30
});
};
const transactWithShorthandTxJsonContextFreeAction = async () => {
await api.getAbi('cfhello');
const tx = api.buildTransaction();
tx.associateContextFree(() => ({
contextFreeAction: tx.with('cfhello').as().contextfree(),
action: tx.with('cfhello').as('cfactor').normal('test')
}));
return await tx.send({
blocksBehind: 3,
expireSeconds: 30
});
};
const transactWithShorthandTxJsonContextFreeData = async () => {
await api.getAbi('cfhello');
const tx = api.buildTransaction();
tx.associateContextFree(() => ({
contextFreeData: [ '74657374', '7465737464617461' ],
contextFreeAction: tx.with('cfhello').as().contextfree(),
action: tx.with('cfhello').as('cfactor').normal('test2')
}));
return await tx.send({
blocksBehind: 3,
expireSeconds: 30
});
};
const transactWithReturnValue = async () => {
await api.getAbi('returnvalue');
const tx = api.buildTransaction();
tx.with('returnvalue').as('bob').sum(5, 5);
return await tx.send({
blocksBehind: 3,
expireSeconds: 30
});
};
const transactWithResourcePayer = async () => {
return await api.transact({
resource_payer: {
payer: 'alice',
max_net_bytes: 4096,
max_cpu_us: 400,
max_memory_bytes: 0
},
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}, {
actor: 'alice',
permission: 'active',
}],
data: {
from: 'bob',
to: 'alice',
quantity: '0.0001 SYS',
memo: 'resource payer',
},
}]
}, {
blocksBehind: 3,
expireSeconds: 30
});
};
const readOnlyQuery = async () => {
return await api.transact({
actions: [{
account: 'readonly',
name: 'get',
authorization: [{
actor: 'readonly',
permission: 'active',
}],
data: {},
}],
}, {
blocksBehind: 3,
expireSeconds: 30,
compression: true,
readOnlyTrx: true,
});
};
const readOnlyFailureTrace = async () => {
return await api.transact({
actions: [{
account: 'zswchain',
name: 'setpriv',
authorization: [{
actor: 'bob',
permission: 'active',
}],
data: {
account: 'bob',
is_priv: '1'
},
}]
}, {
blocksBehind: 3,
expireSeconds: 30,
readOnlyTrx: true,
returnFailureTraces: true,
});
};
const broadcastResult = async (signaturesAndPackedTransaction) => await api.pushSignedTransaction(signaturesAndPackedTransaction);
const transactShouldFail = async () => await api.transact({
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}],
data: {
from: 'bob',
to: 'alice',
quantity: '0.0001 SYS',
memo: '',
},
}]
});
const rpcShouldFail = async () => await rpc.get_block_info(-1);
module.exports = {
transactWithConfig,
transactWithoutConfig,
transactWithContextFreeAction,
transactWithContextFreeData,
broadcastResult,
transactShouldFail,
transactWithShorthandApiJson,
transactWithShorthandTxJson,
transactWithShorthandTxJsonContextFreeAction,
transactWithShorthandTxJsonContextFreeData,
transactWithReturnValue,
transactWithResourcePayer,
readOnlyQuery,
readOnlyFailureTrace,
rpcShouldFail
};
const tests = require('./node');
describe('Node JS environment', () => {
let transactionResponse: any;
let transactionSignatures: any;
let failedAsPlanned: boolean;
it('node tests not required for this suite, see official node tests',async()=>{
expect(1).toBe(1);
})
/*
it('transacts with configuration object containing blocksBehind', async () => {
transactionResponse = await tests.transactWithConfig({
blocksBehind: 3,
expireSeconds: 30
}, 'transactWithBlocksBehind');
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
it('transacts with configuration object containing useLastIrreversible', async () => {
transactionResponse = await tests.transactWithConfig({
useLastIrreversible: true,
expireSeconds: 30
}, 'transactWithUseLastIrreversible');
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
it('transacts with manually configured TAPOS fields', async () => {
if (process.env.NODZSW_VER && process.env.NODZSW_VER === 'release/2.0.x') return;
transactionResponse = await tests.transactWithoutConfig();
expect(Object.keys(transactionResponse)).toContain('transaction_id');
}, 10000);
it('transacts with compressed transaction', async () => {
transactionResponse = await tests.transactWithConfig({
blocksBehind: 3,
expireSeconds: 30,
compression: true
}, 'transactWithCompression');
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
it('transacts with context free action', async () => {
transactionResponse = await tests.transactWithContextFreeAction();
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
it('transacts with context free data', async () => {
transactionResponse = await tests.transactWithContextFreeData();
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
it('transacts without broadcasting, returning signatures and packed transaction', async () => {
transactionSignatures = await tests.transactWithConfig({
broadcast: false,
blocksBehind: 3,
expireSeconds: 30
}, 'transactWithoutBroadcast');
expect(Object.keys(transactionSignatures)).toContain('signatures');
expect(Object.keys(transactionSignatures)).toContain('serializedTransaction');
});
it('broadcasts packed transaction, given valid signatures', async () => {
transactionSignatures = await tests.transactWithConfig({
broadcast: false,
blocksBehind: 3,
expireSeconds: 30
}, 'transactWithoutBroadcast2');
transactionResponse = await tests.broadcastResult(transactionSignatures);
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
describe('Json Abi with Shorthand Design', () => {
it('transacts with shorthand structure using api', async () => {
transactionResponse = await tests.transactWithShorthandApiJson();
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
it('transacts with shorthand structure using tx', async () => {
transactionResponse = await tests.transactWithShorthandTxJson();
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
it('transacts with shorthand structure using tx and context free action', async () => {
transactionResponse = await tests.transactWithShorthandTxJsonContextFreeAction();
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
it('transacts with shorthand structure using tx and context free data', async () => {
transactionResponse = await tests.transactWithShorthandTxJsonContextFreeData();
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
});
it('transacts with elliptic p256/KeyType.R1 keys and signatures', async () => {
transactionResponse = await tests.transactWithConfig({
blocksBehind: 3,
expireSeconds: 30
}, 'transactWithR1KeySignature', 'bobr1', 'alicer1');
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
it('confirms an action\'s return value can be verified', async () => {
if (process.env.NODZSW_VER && process.env.NODZSW_VER === 'release/2.0.x') return;
const expectedValue = 10;
transactionResponse = await tests.transactWithReturnValue();
expect(transactionResponse.processed.action_traces[0].return_value_data).toEqual(expectedValue);
});
it('transacts with resource payer', async () => {
if (process.env.NODZSW_VER && (process.env.NODZSW_VER === 'release/2.0.x' || process.env.NODZSW_VER === 'release/2.1.x')) return;
transactionResponse = await tests.transactWithResourcePayer();
expect(Object.keys(transactionResponse)).toContain('transaction_id');
});
it('confirms the return value of the read-only query', async () => {
if (process.env.NODZSW_VER && (process.env.NODZSW_VER === 'release/2.0.x' || process.env.NODZSW_VER === 'release/2.1.x')) return;
const expectedValue = [
{'age': 25, 'gender': 1, 'id': 1, 'name': 'Bob Smith'},
{'age': 42, 'gender': 1, 'id': 3, 'name': 'John Smith'},
{'age': 27, 'gender': 1, 'id': 4, 'name': 'Jack Smith'},
{'age': 20, 'gender': 0, 'id': 2, 'name': 'Alice Smith'},
{'age': 26, 'gender': 0, 'id': 5, 'name': 'Youko Niihara'},
{'age': 18, 'gender': 0, 'id': 6, 'name': 'Rose Lee'},
{'age': 25, 'gender': 0, 'id': 7, 'name': 'Youko Kawakami'},
{'age': 24, 'gender': 0, 'id': 8, 'name': 'Yuu Yamada'}
];
transactionResponse = await tests.readOnlyQuery();
expect(transactionResponse.result.action_traces[0].return_value_data).toEqual(expectedValue);
});
it('returns failure trace for failed transaction', async () => {
if (process.env.NODZSW_VER && (process.env.NODZSW_VER === 'release/2.0.x' || process.env.NODZSW_VER === 'release/2.1.x')) return;
try {
await tests.readOnlyFailureTrace();
} catch (e) {
expect(e.details.code).toEqual(3090004);
expect(e.details.stack[0].format).toEqual('missing authority of ${account}');
}
});
it('throws appropriate error message without configuration object or TAPOS in place', async () => {
try {
failedAsPlanned = true;
await tests.transactShouldFail();
failedAsPlanned = false;
} catch (e) {
if (e.message !== 'Required configuration or TAPOS fields are not present') {
failedAsPlanned = false;
}
}
expect(failedAsPlanned).toEqual(true);
});
it('throws an an error with RpcError structure for invalid RPC calls', async () => {
try {
failedAsPlanned = true;
await tests.rpcShouldFail();
failedAsPlanned = false;
} catch (e) {
if (!e.json || !e.json.error || !(e.json.error.hasOwnProperty('details'))) {
failedAsPlanned = false;
}
}
expect(failedAsPlanned).toEqual(true);
});
*/
});
global.fetch = require('jest-fetch-mock');
import { JsonRpc } from '../zswjs-jsonrpc';
import { JsSignatureProvider } from '../zswjs-jssig';
import { Api } from '../zswjs-api';
import * as ser from '../zswjs-serialize';
import fetch from 'node-fetch';
const { TextEncoder, TextDecoder } = require('util');
import {
AbiJsonToBinResult,
GetAbiResult,
GetAccountResult,
GetAccountsByAuthorizersResult,
GetActivatedProtocolFeaturesResult,
GetBlockHeaderStateResult,
GetBlockInfoResult,
GetBlockResult,
GetCodeResult,
GetCodeHashResult,
GetCurrencyStatsResult,
GetInfoResult,
GetProducerScheduleResult,
GetProducersResult,
GetRawCodeAndAbiResult,
GetRawAbiResult,
GetScheduledTransactionsResult,
GetTableRowsResult,
GetTableByScopeResult,
PushTransactionArgs,
ReadOnlyTransactResult,
AbiBinToJsonResult,
TraceApiGetBlockResult,
DBSizeGetResult,
} from '../zswjs-rpc-interfaces';
import { Transaction, TransactResult } from '../zswjs-api-interfaces';
import 'jest-extended';
const privateKey = '5JuH9fCXmU3xbj8nRmhPZaVrxxXrdPaRmZLW1cznNTmTQR2Kg5Z';
const rpc = new JsonRpc('http://localhost:8888', { fetch });
const signatureProvider = new JsSignatureProvider([privateKey]);
const api = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() });
/** Checking types with verifyType/complexOrPrimitive
* To ensure that the data structure coming from zsw matches the declared types in zswjs for developers and documentation
* Since typescript is not a runtime language, it's required to test with javascript format
* Create an object matching the typescript type with some requirements:
* nullable: make the key a string and add a `&` character to the end
* optional: make the key a string and add a `?` character to the end (same as typescript)
* []: remove array symbols from simple/complex types, use arrays for std::pair
* Map<>: use Map<> in the value field
* |: operates the same as typescript but does not work for complex types
*/
describe('Chain API Plugin Endpoints', () => {
it('type check tests not required for this suite, see official type check tests',async()=>{
expect(1).toBe(1);
})
/*
it('validates return type of abi_bin_to_json', async () => {
const result: AbiBinToJsonResult = await rpc.abi_bin_to_json('returnvalue', 'sum', '0500000005000000');
const abiBinToJsonResult: any = {
args: 'any'
};
verifyType(result, abiBinToJsonResult);
});
it('validates return type of abi_json_to_bin', async () => {
const result: AbiJsonToBinResult = await rpc.abi_json_to_bin('returnvalue', 'sum', [5, 5]);
const abiJsonToBinResult: any = {
binargs: 'string'
};
verifyType(result, abiJsonToBinResult);
});
it('validates return type of get_abi', async () => {
const result: GetAbiResult = await rpc.get_abi('todo');
const getAbiResult: any = {
account_name: 'string',
'abi?': {
version: 'string',
types: {
new_type_name: 'string',
type: 'string',
},
structs: {
name: 'string',
base: 'string',
fields: {
name: 'string',
type: 'string',
},
},
actions: {
name: 'string',
type: 'string',
ricardian_contract: 'string',
},
tables: {
name: 'string',
type: 'string',
index_type: 'string',
key_names: 'string',
key_types: 'string',
},
ricardian_clauses: {
id: 'string',
body: 'string',
},
error_messages: {
error_code: 'number',
error_msg: 'string',
},
abi_extensions: {
tag: 'number',
value: 'string',
},
'variants?': {
name: 'string',
types: 'string',
},
'action_results?': {
name: 'string',
result_type: 'string',
},
'kv_tables?': {
todo: { // key is dynamic, using result from todo account
type: 'string',
primary_index: {
name: 'string',
type: 'string',
},
secondary_indices: {
'todo?': { // key is dynamic
type: 'string',
},
},
},
},
},
};
verifyType(result, getAbiResult);
});
it('validates return type of get_account', async () => {
const result: GetAccountResult = await rpc.get_account('zswchain');
const getAccountResult: any = {
account_name: 'string',
head_block_num: 'number',
head_block_time: 'string',
privileged: 'boolean',
last_code_update: 'string',
created: 'string',
'core_liquid_balance?': 'string',
ram_quota: 'number',
net_weight: 'number',
cpu_weight: 'number',
net_limit: {
used: 'number',
available: 'number',
max: 'number',
'last_usage_update_time?': 'string',
'current_used?': 'number',
},
cpu_limit: {
used: 'number',
available: 'number',
max: 'number',
'last_usage_update_time?': 'string',
'current_used?': 'number',
},
ram_usage: 'number',
permissions: {
perm_name: 'string',
parent: 'string',
required_auth: {
threshold: 'number',
keys: {
key: 'string',
weight: 'number',
},
accounts: {
permission: {
actor: 'string',
permission: 'string',
},
weight: 'number',
},
waits: {
wait_sec: 'number',
weight: 'number',
}
}
},
'total_resources&': {
owner: 'string',
ram_bytes: 'number',
net_weight: 'string',
cpu_weight: 'string',
},
'self_delegated_bandwidth&': {
from: 'string',
to: 'string',
net_weight: 'string',
cpu_weight: 'string',
},
'refund_request&': {
owner: 'string',
request_time: 'string',
net_amount: 'string',
cpu_amount: 'string',
},
'voter_info&': {
owner: 'string',
proxy: 'string',
producers: 'string',
staked: 'number',
last_vote_weight: 'string',
proxied_vote_weight: 'string',
is_proxy: 'number',
flags1: 'number',
reserved2: 'number',
reserved3: 'string',
},
'rex_info&': {
version: 'number',
owner: 'string',
vote_stake: 'string',
rex_balance: 'string',
matured_rex: 'number',
rex_maturities: 'any',
},
};
verifyType(result, getAccountResult);
});
it('validates return type of get_accounts_by_authorizers', async () => {
const result: GetAccountsByAuthorizersResult = await rpc.get_accounts_by_authorizers([
{ actor: 'bob', permission: 'active' },
{ actor: 'cfhello', permission: 'active' }
], ['EOS7bxrQUTbQ4mqcoefhWPz1aFieN4fA9RQAiozRz7FrUChHZ7Rb8', 'EOS6nVrBASwwviMy3CntKsb1cD5Ai2gRZnyrxJDqypL3JLL7KCKrK']);
const getAccountsByAuthorizersResult: any = {
accounts: {
account_name: 'string',
permission_name: 'string',
'authorizing_key?': 'string',
'authorizing_account?': {
actor: 'string',
permission: 'string',
},
weight: 'number',
threshold: 'number',
}
};
verifyType(result, getAccountsByAuthorizersResult);
});
it('validates return type of get_activated_protocol_features', async () => {
const result: GetActivatedProtocolFeaturesResult = await rpc.get_activated_protocol_features({});
const getActivatedProtocolFeaturesResult: any = {
activated_protocol_features: {
feature_digest: 'string',
activation_ordinal: 'number',
activation_block_num: 'number',
description_digest: 'string',
dependencies: 'string',
protocol_feature_type: 'string',
specification: {
name: 'string',
value: 'string',
},
},
'more?': 'number',
};
verifyType(result, getActivatedProtocolFeaturesResult);
});
it('validates return type of get_block_header_state', async () => {
const info: GetInfoResult = await rpc.get_info();
const result: GetBlockHeaderStateResult = await rpc.get_block_header_state(info.head_block_id);
const getBlockHeaderStateResult: any = {
id: 'string',
header: {
timestamp: 'string',
producer: 'string',
confirmed: 'number',
previous: 'string',
transaction_mroot: 'string',
action_mroot: 'string',
schedule_version: 'number',
'new_producers?': {
version: 'number',
producers: {
producer_name: 'string',
block_signing_key: 'string',
},
},
header_extensions: 'any',
producer_signature: 'string',
},
pending_schedule: {
schedule_lib_num: 'number',
schedule_hash: 'string',
schedule: {
version: 'number',
producers: {
producer_name: 'string',
block_signing_key: 'string',
},
},
},
activated_protocol_features: {
protocol_features: 'string',
},
additional_signatures: 'string',
block_num: 'number',
dpos_proposed_irreversible_blocknum: 'number',
dpos_irreversible_blocknum: 'number',
active_schedule: {
version: 'number',
producers: {
producer_name: 'string',
authority: [ 'number|string', {
threshold: 'number',
keys: {
key: 'string',
weight: 'number',
},
}],
},
},
blockroot_merkle: {
_active_nodes: 'string',
_node_count: 'number',
},
producer_to_last_produced: 'Map<string, number>',
producer_to_last_implied_irb: 'Map<string, number>',
valid_block_signing_authority: [ 'number|string', {
threshold: 'number',
keys: {
key: 'string',
weight: 'number',
},
}],
confirm_count: 'number',
state_extension: [ 'number', {
security_group_info: {
version: 'number',
participants: 'string',
},
}]
};
verifyType(result, getBlockHeaderStateResult);
});
it('validates return type of get_block_info', async () => {
const info: GetInfoResult = await rpc.get_info();
const result: GetBlockInfoResult = await rpc.get_block_info(info.last_irreversible_block_num);
const getBlockInfoResult: any = {
timestamp: 'string',
producer: 'string',
confirmed: 'number',
previous: 'string',
transaction_mroot: 'string',
action_mroot: 'string',
schedule_version: 'number',
producer_signature: 'string',
id: 'string',
block_num: 'number',
ref_block_num: 'number',
ref_block_prefix: 'number',
};
verifyType(result, getBlockInfoResult);
});
it('validates return type of get_block', async () => {
const info: GetInfoResult = await rpc.get_info();
const result: GetBlockResult = await rpc.get_block(info.last_irreversible_block_num);
const getBlockResult: any = {
timestamp: 'string',
producer: 'string',
confirmed: 'number',
previous: 'string',
transaction_mroot: 'string',
action_mroot: 'string',
schedule_version: 'number',
'new_producers&': {
version: 'number',
producers: {
producer_name: 'string',
block_signing_key: 'string'
}
},
producer_signature: 'string',
transactions: {
status: 'string',
cpu_usage_us: 'number',
net_usage_words: 'number',
trx: {
id: 'string',
signatures: 'string',
compression: 'number|string',
packed_context_free_data: 'string',
context_free_data: 'string',
packed_trx: 'string',
transaction: {
'expiration?': 'string',
'ref_block_num?': 'number',
'ref_block_prefix?': 'number',
'max_net_usage_words?': 'number',
'max_cpu_usage_ms?': 'number',
'delay_sec?': 'number',
'context_free_actions?': {
account: 'string',
name: 'string',
authorization: {
actor: 'string',
permission: 'string',
},
'data?': 'any',
'hex_data?': 'string',
},
'context_free_data?': 'number',
actions: {
account: 'string',
name: 'string',
authorization: {
actor: 'string',
permission: 'string',
},
'data?': 'any',
'hex_data?': 'string',
},
'transaction_extensions?': '[number, string]',
},
},
},
id: 'string',
block_num: 'number',
ref_block_prefix: 'number',
};
verifyType(result, getBlockResult);
});
it('validates return type of get_code', async () => {
const result: GetCodeResult = await rpc.get_code('todo');
const getCodeResult: any = {
account_name: 'string',
code_hash: 'string',
wast: 'string',
wasm: 'string',
'abi?': {
version: 'string',
types: {
new_type_name: 'string',
type: 'string',
},
structs: {
name: 'string',
base: 'string',
fields: {
name: 'string',
type: 'string',
},
},
actions: {
name: 'string',
type: 'string',
ricardian_contract: 'string',
},
tables: {
name: 'string',
type: 'string',
index_type: 'string',
key_names: 'string',
key_types: 'string',
},
ricardian_clauses: {
id: 'string',
body: 'string',
},
error_messages: {
error_code: 'number',
error_msg: 'string',
},
abi_extensions: {
tag: 'number',
value: 'string',
},
'variants?': {
name: 'string',
types: 'string',
},
'action_results?': {
name: 'string',
result_type: 'string',
},
'kv_tables?': {
todo: { // key is dynamic, using result from todo account
type: 'string',
primary_index: {
name: 'string',
type: 'string',
},
secondary_indices: {
'todo?': { // key is dynamic
type: 'string',
},
},
},
},
},
};
verifyType(result, getCodeResult);
});
it('validates return type of get_code_hash', async () => {
const result: GetCodeHashResult = await rpc.get_code_hash('todo');
const getCodeHashResult: any = {
account_name: 'string',
code_hash: 'string',
};
verifyType(result, getCodeHashResult);
});
it('validates return type of get_currency_balance', async () => {
const result: string[] = await rpc.get_currency_balance('zsw.token', 'bob', 'SYS');
result.forEach((element: any) => {
expect(typeof element).toEqual('string');
});
});
it('validates return type of get_currency_stats', async () => {
const result: GetCurrencyStatsResult = await rpc.get_currency_stats('zsw.token', 'SYS');
const getCurrencyStatsResult: any = {
SYS: {
supply: 'string',
max_supply: 'string',
issuer: 'string',
}
};
verifyType(result, getCurrencyStatsResult);
});
it('validates return type of get_info', async () => {
const result: GetInfoResult = await rpc.get_info();
const getInfoResult: any = {
server_version: 'string',
chain_id: 'string',
head_block_num: 'number',
last_irreversible_block_num: 'number',
last_irreversible_block_id: 'string',
'last_irreversible_block_time?': 'string',
head_block_id: 'string',
head_block_time: 'string',
head_block_producer: 'string',
virtual_block_cpu_limit: 'number',
virtual_block_net_limit: 'number',
block_cpu_limit: 'number',
block_net_limit: 'number',
'server_version_string?': 'string',
'fork_db_head_block_num?': 'number',
'fork_db_head_block_id?': 'string',
'server_full_version_string?': 'string',
'first_block_num?': 'number',
};
verifyType(result, getInfoResult);
});
it('validates return type of get_producer_schedule', async () => {
const result: GetProducerScheduleResult = await rpc.get_producer_schedule();
const getProducerScheduleResult: any = {
'active&': {
version: 'number',
producers: {
producer_name: 'string',
authority: [ 'number|string', {
threshold: 'number',
keys: {
key: 'string',
weight: 'number',
},
}],
},
},
'pending&': {
version: 'number',
producers: {
producer_name: 'string',
authority: [ 'number|string', {
threshold: 'number',
keys: {
key: 'string',
weight: 'number',
},
}],
},
},
'proposed&': {
version: 'number',
producers: {
producer_name: 'string',
authority: [ 'number|string', {
threshold: 'number',
keys: {
key: 'string',
weight: 'number',
},
}],
},
},
};
verifyType(result, getProducerScheduleResult);
});
it('validates return type of get_producers', async () => {
const result: GetProducersResult = await rpc.get_producers();
const getProducersResult: any = {
rows: {
owner: 'string',
'producer_authority?': [ 'number|string', {
threshold: 'number',
keys: {
key: 'string',
weight: 'number',
},
}],
url: 'string',
'is_active?': 'number',
total_votes: 'string',
producer_key: 'string',
'unpaid_blocks?': 'number',
'last_claim_time?': 'string',
'location?': 'number',
},
total_producer_vote_weight: 'string',
more: 'string',
};
verifyType(result, getProducersResult);
});
it('validates return type of get_raw_code_and_abi', async () => {
const result: GetRawCodeAndAbiResult = await rpc.get_raw_code_and_abi('zswchain');
const getRawCodeAndAbiResult: any = {
account_name: 'string',
wasm: 'string',
abi: 'string',
};
verifyType(result, getRawCodeAndAbiResult);
});
it('validates return type of get_raw_abi', async () => {
const result: GetRawAbiResult = await rpc.get_raw_abi('zswchain');
const getRawAbiResult: any = {
account_name: 'string',
code_hash: 'string',
abi_hash: 'string',
abi: 'string',
};
verifyType(result, getRawAbiResult);
});
it('validates return type of get_scheduled_transactions', async () => {
const result: GetScheduledTransactionsResult = await rpc.get_scheduled_transactions();
const getScheduledTransactionsResult: any = {
transactions: {
trx_id: 'string',
sender: 'string',
sender_id: 'string',
payer: 'string',
delay_until: 'string',
expiration: 'string',
published: 'string',
'packed_trx?': 'string',
'transaction?': {
'expiration?': 'string',
'ref_block_num?': 'number',
'ref_block_prefix?': 'number',
'max_net_usage_words?': 'number',
'max_cpu_usage_ms?': 'number',
'delay_sec?': 'number',
'context_free_actions?': {
account: 'string',
name: 'string',
authorization: {
actor: 'string',
permission: 'string',
},
'data?': 'any',
'hex_data?': 'string',
},
'context_free_data?': 'number',
'actions': {
account: 'string',
name: 'string',
authorization: {
actor: 'string',
permission: 'string',
},
'data?': 'any',
'hex_data?': 'string',
},
'transaction_extensions?': '[number, string]',
'deferred_transaction_generation?': {
sender_trx_id: 'string',
sender_id: 'string',
sender: 'string',
},
},
},
more: 'string',
};
verifyType(result, getScheduledTransactionsResult);
});
it('validates return type of get_table_rows', async () => {
const result: GetTableRowsResult = await rpc.get_table_rows({
code: 'zsw.token',
scope: 'zsw.token',
table: 'accounts',
});
const getTableRowsResult: any = {
rows: 'any',
more: 'boolean',
next_key: 'string',
next_key_bytes: 'string',
};
verifyType(result, getTableRowsResult);
});
it('validates return type of get_kv_table_rows', async () => {
const result: GetTableRowsResult = await rpc.get_kv_table_rows({
code: 'todo',
table: 'todo',
index_name: 'map.index',
encode_type: 'string',
});
const getTableRowsResult: any = {
rows: 'any',
more: 'boolean',
next_key: 'string',
next_key_bytes: 'string',
};
verifyType(result, getTableRowsResult);
});
it('validates return type of get_table_by_scope', async () => {
const result: GetTableByScopeResult = await rpc.get_table_by_scope({
code: 'zsw.token',
table: 'accounts',
});
const getTableByScopeResult: any = {
rows: 'any',
more: 'string',
};
verifyType(result, getTableByScopeResult);
});
it('validates return type of get_required_keys', async () => {
const info = await rpc.get_info();
let transaction: Transaction = {
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}],
data: {
from: 'bob',
to: 'alice',
quantity: '0.0001 SYS',
memo: '',
},
}],
context_free_actions: []
};
transaction = {
...ser.transactionHeader({
block_num: info.last_irreversible_block_num,
id: info.last_irreversible_block_id,
timestamp: info.last_irreversible_block_time,
}, 30),
context_free_actions: await api.serializeActions(transaction.context_free_actions || []),
actions: await api.serializeActions(transaction.actions),
...transaction,
};
const availableKeys = await signatureProvider.getAvailableKeys();
const result: string[] = await rpc.getRequiredKeys({ transaction, availableKeys });
result.forEach((element: any) => {
expect(typeof element).toEqual('string');
});
});
it('validates return type of push_transaction', async () => {
const transaction: PushTransactionArgs = await api.transact({
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}],
data: {
from: 'bob',
to: 'alice',
quantity: '0.0001 SYS',
memo: '',
},
}],
}, {
sign: true,
broadcast: false,
useLastIrreversible: true,
expireSeconds: 30,
}) as PushTransactionArgs;
const result: TransactResult = await rpc.push_transaction(transaction);
const transactResult = {
transaction_id: 'string',
processed: {
id: 'string',
block_num: 'number',
block_time: 'string',
'producer_block_id&': 'string',
'receipt&': {
status: 'string',
cpu_usage_us: 'number',
net_usage_words: 'number',
},
elapsed: 'number',
net_usage: 'number',
scheduled: 'boolean',
action_traces: {
action_ordinal: 'number',
creator_action_ordinal: 'number',
closest_unnotified_ancestor_action_ordinal: 'number',
receipt: {
receiver: 'string',
act_digest: 'string',
global_sequence: 'number',
recv_sequence: 'number',
auth_sequence: [ 'string', 'number' ],
code_sequence: 'number',
abi_sequence: 'number',
},
receiver: 'string',
act: {
account: 'string',
name: 'string',
authorization: {
actor: 'string',
permission: 'string',
},
'data?': 'any',
'hex_data?': 'string',
},
context_free: 'boolean',
elapsed: 'number',
console: 'string',
trx_id: 'string',
block_num: 'number',
block_time: 'string',
'producer_block_id&': 'string',
account_ram_deltas: {
account: 'string',
delta: 'number',
},
account_disk_deltas: {
account: 'string',
delta: 'number',
},
except: 'any',
'error_code&': 'number',
'return_value?': 'any',
'return_value_hex_data?': 'string',
'return_value_data?': 'any',
'inline_traces?': 'any', // ActionTrace, recursive?
},
'account_ram_delta&': {
account: 'string',
delta: 'number',
},
'except&': 'string',
'error_code&': 'number',
bill_to_accounts: 'string',
},
};
verifyType(result, transactResult);
});
it('validates return type of push_ro_transaction', async () => {
const transaction: PushTransactionArgs = await api.transact({
actions: [{
account: 'readonly',
name: 'get',
authorization: [{
actor: 'readonly',
permission: 'active',
}],
data: {},
}],
}, {
sign: true,
broadcast: false,
useLastIrreversible: true,
expireSeconds: 30,
}) as PushTransactionArgs;
const result: ReadOnlyTransactResult = await rpc.push_ro_transaction(transaction);
const readOnlyTransactResult: any = {
head_block_num: 'number',
head_block_id: 'string',
last_irreversible_block_num: 'number',
last_irreversible_block_id: 'string',
code_hash: 'string',
pending_transactions: 'string',
result: {
id: 'string',
block_num: 'number',
block_time: 'string',
'producer_block_id&': 'string',
'receipt&': {
status: 'string',
cpu_usage_us: 'number',
net_usage_words: 'number',
},
elapsed: 'number',
net_usage: 'number',
scheduled: 'boolean',
action_traces: {
action_ordinal: 'number',
creator_action_ordinal: 'number',
closest_unnotified_ancestor_action_ordinal: 'number',
receipt: {
receiver: 'string',
act_digest: 'string',
global_sequence: 'number',
recv_sequence: 'number',
auth_sequence: [ 'string', 'number' ],
code_sequence: 'number',
abi_sequence: 'number',
},
receiver: 'string',
act: {
account: 'string',
name: 'string',
authorization: {
actor: 'string',
permission: 'string',
},
'data?': 'any',
'hex_data?': 'string',
},
context_free: 'boolean',
elapsed: 'number',
console: 'string',
trx_id: 'string',
block_num: 'number',
block_time: 'string',
'producer_block_id&': 'string',
account_ram_deltas: {
account: 'string',
delta: 'number',
},
account_disk_deltas: {
account: 'string',
delta: 'number',
},
except: 'any',
'error_code&': 'number',
'return_value?': 'any',
'return_value_hex_data?': 'string',
'return_value_data?': 'any',
'inline_traces?': 'any', // ActionTrace, recursive?
},
'account_ram_delta&': {
account: 'string',
delta: 'number',
},
'except&': 'string',
'error_code&': 'number',
bill_to_accounts: 'string',
}
};
verifyType(result, readOnlyTransactResult);
});
it('validates return type of push_transactions', async () => {
const transactionA: PushTransactionArgs = await api.transact({
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}],
data: {
from: 'bob',
to: 'alice',
quantity: '0.0001 SYS',
memo: 'A',
},
}],
}, {
sign: true,
broadcast: false,
useLastIrreversible: true,
expireSeconds: 30,
}) as PushTransactionArgs;
const transactionB: PushTransactionArgs = await api.transact({
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}],
data: {
from: 'bob',
to: 'alice',
quantity: '0.0001 SYS',
memo: 'B',
},
}],
}, {
sign: true,
broadcast: false,
useLastIrreversible: true,
expireSeconds: 30,
}) as PushTransactionArgs;
const result: TransactResult[] = await rpc.push_transactions([ transactionA, transactionB ]);
const transactResult = {
transaction_id: 'string',
processed: {
id: 'string',
block_num: 'number',
block_time: 'string',
'producer_block_id&': 'string',
'receipt&': {
status: 'string',
cpu_usage_us: 'number',
net_usage_words: 'number',
},
elapsed: 'number',
net_usage: 'number',
scheduled: 'boolean',
action_traces: {
action_ordinal: 'number',
creator_action_ordinal: 'number',
closest_unnotified_ancestor_action_ordinal: 'number',
receipt: {
receiver: 'string',
act_digest: 'string',
global_sequence: 'number',
recv_sequence: 'number',
auth_sequence: [ 'string', 'number' ],
code_sequence: 'number',
abi_sequence: 'number',
},
receiver: 'string',
act: {
account: 'string',
name: 'string',
authorization: {
actor: 'string',
permission: 'string',
},
'data?': 'any',
'hex_data?': 'string',
},
context_free: 'boolean',
elapsed: 'number',
console: 'string',
trx_id: 'string',
block_num: 'number',
block_time: 'string',
'producer_block_id&': 'string',
account_ram_deltas: {
account: 'string',
delta: 'number',
},
account_disk_deltas: {
account: 'string',
delta: 'number',
},
except: 'any',
'error_code&': 'number',
'return_value?': 'any',
'return_value_hex_data?': 'string',
'return_value_data?': 'any',
'inline_traces?': 'any', // ActionTrace, recursive?
},
'account_ram_delta&': {
account: 'string',
delta: 'number',
},
'except&': 'string',
'error_code&': 'number',
bill_to_accounts: 'string',
},
};
result.forEach((transaction: TransactResult) => {
verifyType(transaction, transactResult);
});
});
it('validates return type of send_transaction', async () => {
const transaction: PushTransactionArgs = await api.transact({
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'alice',
permission: 'active',
}],
data: {
from: 'alice',
to: 'bob',
quantity: '0.0001 SYS',
memo: '',
},
}],
}, {
sign: true,
broadcast: false,
useLastIrreversible: true,
expireSeconds: 30,
}) as PushTransactionArgs;
const result: TransactResult = await rpc.send_transaction(transaction);
const transactResult = {
transaction_id: 'string',
processed: {
id: 'string',
block_num: 'number',
block_time: 'string',
'producer_block_id&': 'string',
'receipt&': {
status: 'string',
cpu_usage_us: 'number',
net_usage_words: 'number',
},
elapsed: 'number',
net_usage: 'number',
scheduled: 'boolean',
action_traces: {
action_ordinal: 'number',
creator_action_ordinal: 'number',
closest_unnotified_ancestor_action_ordinal: 'number',
receipt: {
receiver: 'string',
act_digest: 'string',
global_sequence: 'number',
recv_sequence: 'number',
auth_sequence: [ 'string', 'number' ],
code_sequence: 'number',
abi_sequence: 'number',
},
receiver: 'string',
act: {
account: 'string',
name: 'string',
authorization: {
actor: 'string',
permission: 'string',
},
'data?': 'any',
'hex_data?': 'string',
},
context_free: 'boolean',
elapsed: 'number',
console: 'string',
trx_id: 'string',
block_num: 'number',
block_time: 'string',
'producer_block_id&': 'string',
account_ram_deltas: {
account: 'string',
delta: 'number',
},
account_disk_deltas: {
account: 'string',
delta: 'number',
},
except: 'any',
'error_code&': 'number',
'return_value?': 'any',
'return_value_hex_data?': 'string',
'return_value_data?': 'any',
'inline_traces?': 'any', // ActionTrace, recursive?
},
'account_ram_delta&': {
account: 'string',
delta: 'number',
},
'except&': 'string',
'error_code&': 'number',
bill_to_accounts: 'string',
},
};
verifyType(result, transactResult);
});
});
describe('DB Size API Plugin Endpoints', () => {
it('validates return type of get', async () => {
const result: DBSizeGetResult = await rpc.db_size_get();
const dbSizeGetResult: any = {
free_bytes: 'number',
used_bytes: 'number',
size: 'number',
indices: {
index: 'string',
row_count: 'number',
},
};
verifyType(result, dbSizeGetResult);
});
});
describe('Trace API Plugin Endpoints', () => {
it('validates return type of get_block', async () => {
const info: GetInfoResult = await rpc.get_info();
const result: TraceApiGetBlockResult = await rpc.trace_get_block(info.last_irreversible_block_num);
const traceApiGetBlockResult: any = {
id: 'string',
number: 'number',
previous_id: 'string',
status: 'string',
timestamp: 'string',
producer: 'string',
transaction_mroot: 'string',
action_mroot: 'string',
schedule_version: 'number',
transactions: {
id: 'string',
actions: {
global_sequence: 'number',
receiver: 'string',
account: 'string',
action: 'string',
authorization: {
account: 'string',
permission: 'string'
},
data: 'string',
return_value: 'string',
},
status: 'string',
cpu_usage_us: 'number',
net_usage_words: 'number',
signatures: 'string',
transaction_header: 'any',
bill_to_accounts: 'string',
},
};
verifyType(result, traceApiGetBlockResult);
});
*/
});
const verifyType = (data: any, type: any): void => {
const verifiedKeys: string[] = Object.keys(type).filter((key: string) => {
const formattedKey = key.replace('?', '').replace('&', '');
if (key.includes('?')) {
if (!data.hasOwnProperty(formattedKey)) return false;
}
return true;
}).map((key: string) => {
const formattedKey = key.replace('?', '').replace('&', '');
if (Array.isArray(data[formattedKey])) {
if (Array.isArray(type[key])) {
data[formattedKey].forEach((element: any, index: number) => {
if (Array.isArray(element)) {
element.forEach((secondElement: any, secondIndex: number) => {
complexOrPrimitive(secondElement, type[key][secondIndex], formattedKey);
});
} else {
complexOrPrimitive(element, type[key][index], formattedKey);
}
});
} else {
data[formattedKey].forEach((element: any) => {
complexOrPrimitive(element, type[key], formattedKey);
});
}
} else if (key.includes('&')) {
if (data[formattedKey] !== null) {
complexOrPrimitive(data[formattedKey], type[key], formattedKey);
}
} else {
complexOrPrimitive(data[formattedKey], type[key], formattedKey);
}
return formattedKey;
});
expect(data).toContainAllKeys(verifiedKeys);
};
const complexOrPrimitive = (data: any, type: any, formattedKey: any): void => {
if (typeof type === 'object') {
verifyType(data, type);
} else if (type.includes('Map')) {
const types = type.replace('Map<', '').replace('>', '').split(', ');
data.forEach((value: any, index: number) => {
complexOrPrimitive(value, types[index], formattedKey);
});
} else if (type.includes('|')) {
const types = type.split('|');
expect(typeof data).toBeOneOf(types);
} else if (type !== 'any') {
expect(typeof data).toEqual(type);
}
};
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
font-family: 'Source Sans Pro', sans-serif;
}
button {
font-family: 'Source Sans Pro', sans-serif;
font-size: .9rem;
font-weight: 600;
cursor: pointer;
}
button:active, button:focus {
outline: none;
}
.header-container {
margin: 0;
background-color: #F6F6F8;
}
.header {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
max-width: 1200px;
margin : 0 auto;
padding: 2rem;
}
.header > img {
max-width: 8rem;
}
.header > h1 {
font-size: 3.5rem;
font-weight: lighter;
color: #202035;
margin: 2rem 0;
}
.header > button {
padding: 1rem 6rem;
border-radius: .4rem;
color: white;
background: #15087E;
letter-spacing: 1px;
}
.header > button:hover {
background: #15089E;
}
.tests {
max-width: 1200px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 auto;
padding: 0 1rem;
}
.tests > div {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
text-align: center;
width: 30%;
margin: 1rem auto;
}
.tests > div > h2 {
font-weight: normal;
margin: 1.5rem 0;
font-size: .9rem;
}
.tests > div > button {
width: 6.5rem;
height: 6.5rem;
margin: 1rem;;
padding: 0;
border: .15rem solid #15087E;
border-radius: 5rem;
}
.tests > div > button:hover {
background: #F6F6F8;
}
.tests > div > button.success {
color: #7ED321;
border: .15rem solid #7ED321;
}
.tests > div > button.failed {
color: #FF6363;
border: .15rem solid #FF6363;
}
@media (min-width: 1200px) {
html, body {
font-size: 20px;
}
}
@media (max-width: 800px) {
html, body {
font-size: 14px;
}
}
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' type='text/css' href='web.css'>
<script src='../../dist-web/externals.min.js'></script>
<script src='../../dist-web/zswjs-api.min.js'></script>
<script src='../../dist-web/zswjs-jsonrpc.min.js'></script>
<script src='../../dist-web/zswjs-jssig.min.js'></script>
<script>
const privateKey = '5JuH9fCXmU3xbj8nRmhPZaVrxxXrdPaRmZLW1cznNTmTQR2Kg5Z'; // replace with 'bob' account private key
const r1PrivateKey = 'PVT_R1_GrfEfbv5at9kbeHcGagQmvbFLdm6jqEpgE1wsGbrfbZNjpVgT'
const cfactorPrivateKey = '5K8Sm2bB2b7ZC8tJMefrk1GFa4jgtHxxHRcjX49maMk9AEwq8hN';
/* new accounts for testing can be created by unlocking a clzsw wallet then calling:
* 1) clzsw create key --to-console (copy this privateKey & publicKey)
* 2) clzsw wallet import
* 3) clzsw create account bob publicKey
* 4) clzsw create account alice publicKey
*/
const rpc = new zswjs_jsonrpc.JsonRpc('http://localhost:8888');
const signatureProvider = new zswjs_jssig.JsSignatureProvider([privateKey, r1PrivateKey, cfactorPrivateKey]);
const api = new zswjs_api.Api({ rpc, signatureProvider });
const EXECUTING = 'Executing Test', SUCCESS = 'Success', FAILED = 'Failed';
let resultsLabel, transactionResponse, transactionSignatures, failedAsPlanned;
const transactWithConfig = async (config, memo, from = 'bob', to = 'alice') => {
return api.transact({
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: from,
permission: 'active',
}],
data: {
from,
to,
quantity: '0.0001 SYS',
memo,
},
}]
}, config);
}
const testTransactWithConfigBlocksBehind = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await transactWithConfig({ blocksBehind: 3, expireSeconds: 30 }, 'transactWithBlocksBehind');
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact With Config Blocks Behind Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const testTransactWithConfigUseLastIrreversible = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await transactWithConfig({ useLastIrreversible: true, expireSeconds: 30 }, 'transactWithUseLastIrreversible');
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact With Config Use Last Irreversible Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const transactWithoutConfig = async () => {
const transactionResponse = await transactWithConfig({ blocksBehind: 3, expireSeconds: 30 }, 'transactWithoutConfig');
const blockInfo = await rpc.get_block_info(transactionResponse.processed.block_num - 3);
const currentDate = new Date();
const timePlusTen = currentDate.getTime() + 10000;
const timeInISOString = (new Date(timePlusTen)).toISOString();
const expiration = timeInISOString.substr(0, timeInISOString.length - 1);
return await api.transact({
expiration,
ref_block_num: blockInfo.block_num & 0xffff,
ref_block_prefix: blockInfo.ref_block_prefix,
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}],
data: {
from: 'bob',
to: 'alice',
quantity: '0.0001 SYS',
memo: 'transactWithoutConfig2',
},
}]
});
};
const testTransactWithoutConfig = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await transactWithoutConfig();
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact without Config Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
};
const testTransactWithCompression = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse =
await transactWithConfig({ blocksBehind: 3, expireSeconds: 30, compression: true }, 'transactWithCompression');
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact With Config Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const transactWithContextFree = async (user, context_free_data) => {
return api.transact({
actions: [{
account: 'cfhello',
name: 'normal',
authorization: [{
actor: 'cfactor',
permission: 'active'
}],
data: {
user
}
}],
context_free_actions: [{
account: 'cfhello',
name: 'contextfree',
authorization: [],
data: {}
}],
context_free_data
}, {
blocksBehind: 3,
expireSeconds: 30
});
}
const testTransactWithContextFreeAction = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse =
await transactWithContextFree('test', []);
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact With Context Free Action Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const testTransactWithContextFreeData = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse =
await transactWithContextFree('test2', [[ '74657374', '7465737464617461' ]]);
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact With Context Free Data Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const testTransactWithoutBroadcast = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionSignatures =
await transactWithConfig({ broadcast: false, blocksBehind: 3, expireSeconds: 30 }, 'transactWithoutBroadcast');
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact without Broadcast Test Failure: ', error.message);
return false;
}
if(transactionSignatures.signatures && transactionSignatures.serializedTransaction) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
};
const broadcastResult = async (signaturesAndPackedTransaction) => await api.pushSignedTransaction(signaturesAndPackedTransaction);
const testBroadcastResult = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionSignatures =
await transactWithConfig({ broadcast: false, blocksBehind: 3, expireSeconds: 30 }, 'transactWithoutBroadcast2');
transactionResponse = await broadcastResult(transactionSignatures);
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Broadcast Transaction Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const shorthandWithApiJson = async () => {
await api.getAbi('zsw.token');
return api.transact({
actions: [
api.with('zsw.token').as('bob').transfer('bob', 'alice', '0.0001 SYS', 'transactWithShorthandApiJson')
]
}, {
blocksBehind: 3,
expireSeconds: 30
});
}
const testShorthandWithApiJson = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await shorthandWithApiJson();
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact with .with() Using Api and Json Abi Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
};
const shorthandWithTxJson = async () => {
await api.getAbi('zsw.token');
const tx = api.buildTransaction();
tx.with('zsw.token').as('bob').transfer('bob', 'alice', '0.0001 SYS', 'transactWithShorthandTxJson');
return tx.send({
blocksBehind: 3,
expireSeconds: 30
});
}
const testShorthandWithTxJson = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await shorthandWithTxJson();
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact with .with() Using Tx and Json Abi Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
};
const shorthandWithTxJsonContextFree = async (user, context_free_data) => {
await api.getAbi('cfhello');
const tx = api.buildTransaction();
tx.associateContextFree(() => ({
contextFreeAction: tx.with('cfhello').as().contextfree(),
action: tx.with('cfhello').as('cfactor').normal(user),
context_free_data
}));
return await tx.send({
blocksBehind: 3,
expireSeconds: 30
});
}
const testShorthandWithTxJsonContextFreeAction = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await shorthandWithTxJsonContextFree('test');
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact with .with() Using Tx and Json Abi with Context Free Action: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const testShorthandWithTxJsonContextFreeData = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await shorthandWithTxJsonContextFree('test2', [ '74657374', '7465737464617461' ]);
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact with .with() Using Tx and Json Abi with Context Free Data: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const testWithP256EllipticCurve = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await transactWithConfig({ blocksBehind: 3, expireSeconds: 30 }, 'transactWithR1KeySignature', 'bobr1', 'alicer1');
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact elliptic p256/KeyType.r1 Keys and Signatures Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
};
const returnValueTx = async () => {
await api.getAbi('returnvalue');
const tx = api.buildTransaction();
tx.with('returnvalue').as('bob').sum(5, 5);
return tx.send({
blocksBehind: 3,
expireSeconds: 30
});
}
const testWithReturnValueTx = async (e) => {
const expectedValue = 10;
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await returnValueTx();
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact Return Values Test Failure: ', error.message);
return false;
}
if (transactionResponse.processed.action_traces[0].return_value_data === expectedValue) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const resourcePayerTx = async () => {
return await api.transact({
resource_payer: {
payer: 'alice',
max_net_bytes: 4096,
max_cpu_us: 400,
max_memory_bytes: 0
},
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}, {
actor: 'alice',
permission: 'active',
}],
data: {
from: 'bob',
to: 'alice',
quantity: '0.0001 SYS',
memo: 'resource payer',
},
}]
}, {
blocksBehind: 3,
expireSeconds: 30
});
}
const testWithResourcePayerTx = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await resourcePayerTx();
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact Resource Payer Test Failure: ', error.message);
return false;
}
if (transactionResponse.transaction_id) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const readOnlyQuery = async () => {
return await api.transact({
actions: [{
account: 'readonly',
name: 'get',
authorization: [{
actor: 'readonly',
permission: 'active',
}],
data: {},
}],
}, {
blocksBehind: 3,
expireSeconds: 30,
compression: true,
readOnlyTrx: true,
});
}
const testWithReadOnlyQuery = async (e) => {
const expectedValue = [
{'id': 1, 'name': 'Bob Smith', 'gender': 1, 'age': 25},
{'id': 3, 'name': 'John Smith', 'gender': 1, 'age': 42},
{'id': 4, 'name': 'Jack Smith', 'gender': 1, 'age': 27},
{'id': 2, 'name': 'Alice Smith', 'gender': 0, 'age': 20,},
{'id': 5, 'name': 'Youko Niihara', 'gender': 0, 'age': 26},
{'id': 6, 'name': 'Rose Lee', 'gender': 0, 'age': 18},
{'id': 7, 'name': 'Youko Kawakami', 'gender': 0, 'age': 25},
{'id': 8, 'name': 'Yuu Yamada', 'gender': 0, 'age': 24}
];
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
transactionResponse = await readOnlyQuery();
} catch (error) {
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
console.error('Transact Read Only Query Test Failure: ', error.message);
return false;
}
if (JSON.stringify(transactionResponse.result.action_traces[0].return_value_data) === JSON.stringify(expectedValue)) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const readOnlyFailureTrace = async () => {
return await api.transact({
actions: [{
account: 'zswchain',
name: 'setpriv',
authorization: [{
actor: 'bob',
permission: 'active',
}],
data: {
account: 'bob',
is_priv: '1'
},
}]
}, {
blocksBehind: 3,
expireSeconds: 30,
readOnlyTrx: true,
returnFailureTraces: true,
});
}
const testWithReadOnlyFailureTrace = async (e) => {
let err;
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
await readOnlyFailureTrace();
} catch (err) {
if (err.details.code === 3090004 && err.details.stack[0].format === 'missing authority of ${account}') {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const transactShouldFail = async () => await api.transact({
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}],
data: {
from: 'bob',
to: 'alice',
quantity: '0.0001 SYS',
memo: '',
},
}]
});
const testTransactShouldFail = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
await transactShouldFail();
} catch (e) {
if (e.message === 'Required configuration or TAPOS fields are not present') {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const rpcShouldFail = async () => await rpc.get_block_info(-1);
const testRpcShouldFail = async (e) => {
resultsLabel = e.target;
resultsLabel.innerText = EXECUTING;
try {
await rpcShouldFail();
} catch (e) {
if (e instanceof zswjs_jsonrpc.RpcError) {
resultsLabel.className = "success";
resultsLabel.innerText = SUCCESS;
return true;
}
}
resultsLabel.className = 'failed';
resultsLabel.innerText = FAILED;
return false;
}
const runAllTests = async () => {
const buttons = document.getElementsByTagName('button');
for (var i = 1; i < buttons.length; i++) {
var button = buttons[i];
button.click();
await new Promise(resolve => setTimeout(resolve, 150))
}
return;
}
</script>
</head>
<body>
<div class='header-container'>
<div class='header'>
<img src='logo.svg'/>
<h1>Web Build Integration Tests</h1>
<button onClick='runAllTests();'>Run All Tests</button>
</div>
</div>
<div class='tests'>
<div><h2>Transact with blocksBehind Configuration Parameter</h2><button id='testTransactWithConfigBlocksBehind' onClick='testTransactWithConfigBlocksBehind(event);'>Test</button></div>
<div><h2>Transact with useLastIrreversible Configuration Parameter</h2><button id='testTransactWithConfigUseLastIrreversible' onClick='testTransactWithConfigUseLastIrreversible(event);'>Test</button></div>
<div><h2>Transact with Manually Configured TAPOS</h2><button id='testTransactWithoutConfig' onClick='testTransactWithoutConfig(event);'>Test</button></div>
<div><h2>Transact with Compression</h2><button id='testTransactWithCompression' onClick='testTransactWithCompression(event);'>Test</button></div>
<div><h2>Transact with Context Free Action</h2><button id='testTransactWithContextFreeAction' onClick='testTransactWithContextFreeAction(event);'>Test</button></div>
<div><h2>Transact with Context Free Data</h2><button id='testTransactWithContextFreeData' onClick='testTransactWithContextFreeData(event);'>Test</button></div>
<div><h2>Transact without Broadcasting</h2><button id='testTransactWithoutBroadcast' onClick='testTransactWithoutBroadcast(event);'>Test</button></div>
<div><h2>Broadcast Transaction</h2><button id='testBroadcastResult' onClick='testBroadcastResult(event);'>Test</button></div>
<div><h2>Transact with .with() Using Api and Json Abi</h2><button id='testShorthandWithApiJson' onClick='testShorthandWithApiJson(event);'>Test</button></div>
<div><h2>Transact with .with() Using Tx and Json Abi</h2><button id='testShorthandWithTxJson' onClick='testShorthandWithTxJson(event);'>Test</button></div>
<div><h2>Transact with .with() Using Tx and Json Abi with Context Free Action</h2><button id='testShorthandWithTxJsonContextFreeAction' onClick='testShorthandWithTxJsonContextFreeAction(event);'>Test</button></div>
<div><h2>Transact with .with() Using Tx and Json Abi with Context Free Data</h2><button id='testShorthandWithTxJsonContextFreeData' onClick='testShorthandWithTxJsonContextFreeData(event);'>Test</button></div>
<div><h2>Transact elliptic p256/KeyType.r1 Keys and Signatures</h2><button id='testWithP256EllipticCurve' onClick='testWithP256EllipticCurve(event);'>Test</button></div>
<div><h2>Transact Return Values</h2><button id='testWithReturnValueTx' onClick='testWithReturnValueTx(event);'>Test</button></div>
<div><h2>Resource Payer</h2><button id='testWithResourcePayerTx' onClick='testWithResourcePayerTx(event);'>Test</button></div>
<div><h2>Read Only Query</h2><button id='testWithReadOnlyQuery' onClick='testWithReadOnlyQuery(event);'>Test</button></div>
<div><h2>Read Only Failure Trace</h2><button id='testWithReadOnlyFailureTrace' onClick='testWithReadOnlyFailureTrace(event);'>Test</button></div>
<div><h2>Invalid Transaction Throws Error</h2><button id='testTransactShouldFail' onClick='testTransactShouldFail(event);'>Test</button></div>
<div><h2>Invalid Rpc Call Throws Rpc Error</h2><button id='testRpcShouldFail' onClick='testRpcShouldFail(event);'>Test</button></div>
</div>
</body>
</html>
const ecc = require('zsw-crypto');
import { ecc as eccMigration } from '../zsw-crypto-migration';
import { PrivateKey } from '../zswjs-key-conversions';
describe('ecc Migration', () => {
const privateKeys = [
'5Juww5SS6aLWxopXBAWzwqrwadiZKz7XpKAiktXTKcfBGi1DWg8',
'5JnHjSFwe4r7xyqAUAaVs51G7HmzE86DWGa3VAA5VvQriGYnSUr',
'5K4XZH5XR2By7Q5KTcZnPAmUMU5yjUNBdoKzzXyrLfmiEZJqoKE',
];
const legacyPublicKeys = [
'EOS7tgwU6E7pAUQJgqEJt66Yi8cWvanTUW8ZfBjeXeJBQvhTU9ypi',
'EOS8VaY5CiTexYqgQZyPTJkc3qvWuZUi12QrZL9ssjqW2es6aQk2F',
'EOS7VGhqctkKprW1VUj19DZZiiZLX3YcJqUJCuEcahJmUCw3wJEMu',
];
it('verifies `initialize` returns console.error message', () => {
console.error = jest.fn();
eccMigration.initialize();
expect(console.error).toHaveBeenCalledWith('Method deprecated');
});
it('verifies `unsafeRandomKey` returns console.error message', () => {
console.error = jest.fn();
eccMigration.unsafeRandomKey();
expect(console.error).toHaveBeenCalledWith('Method deprecated');
});
it('verifies `randomKey` calls generateKeyPair', async () => {
console.warn = jest.fn();
const privateKey = await eccMigration.randomKey(0, { secureEnv: true });
expect(console.warn).toHaveBeenCalledWith('Argument `cpuEntropyBits` is deprecated, ' +
'use the options argument instead');
expect(typeof privateKey).toEqual('string');
expect(PrivateKey.fromString(privateKey).isValid()).toBeTruthy();
});
it('verifies `seedPrivate` returns console.error message', () => {
console.error = jest.fn();
eccMigration.seedPrivate();
expect(console.error).toHaveBeenCalledWith('Method deprecated');
});
it('verifies `privateToPublic` function is consistent between ecc objects', () => {
console.warn = jest.fn();
const eccPublicKey = ecc.privateToPublic(privateKeys[0], 'EOS');
const eccMigrationPublicKey = eccMigration.privateToPublic(privateKeys[0], 'EOS');
expect(console.warn).toHaveBeenCalledWith('Argument `pubkey_prefix` is deprecated, ' +
'keys prefixed with PUB_K1_/PUB_R1_/PUB_WA_ going forward');
expect(eccPublicKey).toEqual(eccMigrationPublicKey);
});
it('verifies `isValidPublic` function is consistent between ecc objects', () => {
console.warn = jest.fn();
const eccValid = ecc.isValidPublic(legacyPublicKeys[0], 'EOS');
const eccMigrationValid = eccMigration.isValidPublic(legacyPublicKeys[0], 'EOS');
expect(console.warn).toHaveBeenCalledWith('Argument `pubkey_prefix` is deprecated, ' +
'keys prefixed with PUB_K1_/PUB_R1_/PUB_WA_ going forward');
expect(eccValid).toEqual(eccMigrationValid);
expect(eccValid).toBeTruthy();
expect(eccMigrationValid).toBeTruthy();
});
it('verifies `isValidPublic` function is consistent during an error', () => {
console.warn = jest.fn();
const eccValid = ecc.isValidPublic('publickey', 'EOS');
const eccMigrationValid = eccMigration.isValidPublic('publickey', 'EOS');
expect(console.warn).toHaveBeenCalledWith('Argument `pubkey_prefix` is deprecated, ' +
'keys prefixed with PUB_K1_/PUB_R1_/PUB_WA_ going forward');
expect(eccValid).toEqual(eccMigrationValid);
expect(eccValid).toBeFalsy();
expect(eccMigrationValid).toBeFalsy();
});
it('verifies `isValidPrivate` function is consistent between ecc objects', () => {
const eccValid = ecc.isValidPrivate(privateKeys[0]);
const eccMigrationValid = eccMigration.isValidPrivate(privateKeys[0]);
expect(eccValid).toEqual(eccMigrationValid);
expect(eccValid).toBeTruthy();
expect(eccMigrationValid).toBeTruthy();
});
it('verifies `isValidPrivate` function is consistent during an error', () => {
const eccValid = ecc.isValidPrivate('privatekey');
const eccMigrationValid = eccMigration.isValidPrivate('privatekey');
expect(eccValid).toEqual(eccMigrationValid);
expect(eccValid).toBeFalsy();
expect(eccMigrationValid).toBeFalsy();
});
it('verifies `sign`, `recover`, and `verify` functions are consistent between ecc objects', () => {
const dataAsString = 'some string';
const eccSig = ecc.sign(dataAsString, privateKeys[0], 'utf8');
const eccMigrationSig = eccMigration.sign(dataAsString, privateKeys[0], 'utf8');
// signatures are different
expect(eccSig).not.toEqual(eccMigrationSig);
const eccKPub = ecc.recover(eccSig, dataAsString, 'utf8');
const eccMigrationKPub = eccMigration.recover(eccMigrationSig, dataAsString, 'utf8');
expect(eccKPub).toEqual(eccMigrationKPub);
});
it('verifies `signHash`, `recoverHash`, and `sha256` functions are consistent between ecc objects', () => {
console.warn = jest.fn();
const dataAsString = 'some string';
const eccHash = Buffer.from(ecc.sha256(dataAsString), 'hex');
const eccMigrationHash = Buffer.from(eccMigration.sha256(dataAsString, 'hex', 'utf8') as string, 'hex');
expect(console.warn).toBeCalledWith('Argument `encoding` is deprecated');
expect(console.warn).toBeCalledWith('Argument `resultEncoding` is deprecated');
expect(eccHash).toEqual(eccMigrationHash);
const eccSig = ecc.signHash(eccHash, privateKeys[0], 'utf8');
const eccMigrationSig = eccMigration.signHash(eccMigrationHash, privateKeys[0], 'utf8');
// signatures are different
expect(eccSig).not.toEqual(eccMigrationSig);
const eccKPub = ecc.recoverHash(eccSig, eccHash, 'utf8');
const eccMigrationKPub = eccMigration.recoverHash(eccSig, eccMigrationHash, 'utf8');
expect(eccKPub).toEqual(eccMigrationKPub);
});
});
// zsw-crypto stuff
const ecc = require('zsw-crypto');
const { ec } = require('elliptic');
const { Signature, PrivateKey, PublicKey, sha256 } = require('../zswjs-key-conversions');
const {
JsSignatureProvider,
} = require('../zswjs-jssig');
const { KeyType } = require('../zswjs-numeric');
const { SignatureProviderArgs } = require('../zswjs-api-interfaces');
describe('JsSignatureProvider', () => {
const privateKeys = [
'5Juww5SS6aLWxopXBAWzwqrwadiZKz7XpKAiktXTKcfBGi1DWg8',
'5JnHjSFwe4r7xyqAUAaVs51G7HmzE86DWGa3VAA5VvQriGYnSUr',
'5K4XZH5XR2By7Q5KTcZnPAmUMU5yjUNBdoKzzXyrLfmiEZJqoKE',
];
const legacyPublicKeys = [
'EOS7tgwU6E7pAUQJgqEJt66Yi8cWvanTUW8ZfBjeXeJBQvhTU9ypi',
'EOS8VaY5CiTexYqgQZyPTJkc3qvWuZUi12QrZL9ssjqW2es6aQk2F',
'EOS7VGhqctkKprW1VUj19DZZiiZLX3YcJqUJCuEcahJmUCw3wJEMu',
];
const k1FormatPublicKeys = [
'PUB_K1_7tgwU6E7pAUQJgqEJt66Yi8cWvanTUW8ZfBjeXeJBQvhYTBFvY',
'PUB_K1_8VaY5CiTexYqgQZyPTJkc3qvWuZUi12QrZL9ssjqW2es7e7bRJ',
'PUB_K1_7VGhqctkKprW1VUj19DZZiiZLX3YcJqUJCuEcahJmUCw9RT8v2',
];
const signatures = [
'SIG_K1_HKkqi3zray76i63ZQwAHWMjoLk3wTa1ajZWPcUnrhgmSWQYEHDJsxkny6VDTWEmVdfktxpGoTA81qe6QuCrDmazeQndmxh',
'SIG_K1_HCaY9Y9qdjnkRhE9hokAyp3pFtkMmjpxF6xTd514Vo8vLVSWKek5m5aHfCaka9TqZUbajkhhd4BfBLxSwCwZUEmy8cvt1x',
'SIG_K1_GrZqp9ZkuhBeNpeQ5b2L2UWUUrNU1gHbTyMzkyWRhiXNkxPP84Aq9eziU399eBf9xJw8MqHHjz7R2wMTMXhXjHLgpZYFeA',
];
const eccSignatures = [
'SIG_K1_KeEyJFpkp63Qq5E1zRD9aNZtTjpStvdkdnL31Z7wVmhYtrKGtpVdMBJnXyEUXNkNEyo4d4i4Q79qmRpCUsCRdFqhV6KAeF',
'SIG_K1_JvgMmFSDhipS1SeBLNBMdAxayAsWS3GuVGSHS7YQth5Z5ZpijxnZgaa23dYD1efQhpEgtEggdRfHMmp31RDXjmJdZYoKLm',
'SIG_K1_JwMqV2nbEntHSq9AuG3Zq1JBc5YqD2SftMHCTGK4A8DYGn1VPQ8QAduwCNksT5JhYgAmGMzPyJdZ2Ws4p8TCvQ16LeNhrw',
];
// These are simplified tests simply to verify a refactor didn't mess with existing code
it('(NOTE: sigs are different): ensure elliptic does what zsw-crypto used to do', () => {
const ellipticEc = new ec('secp256k1');
for (let idx=0; idx<privateKeys.length; idx++) {
const KPriv = privateKeys[idx];
const KPrivElliptic = PrivateKey.fromString(KPriv).toElliptic();
const KPubK1 = new JsSignatureProvider([KPriv]).availableKeys[0];
const dataAsString = 'some string';
const eccHashedString = Buffer.from(ecc.sha256(dataAsString), 'hex');
const ellipticHashedStringAsBuffer = Buffer.from(ellipticEc.hash().update(dataAsString).digest(), 'hex');
expect(eccHashedString).toEqual(ellipticHashedStringAsBuffer);
const eccSig = ecc.sign(dataAsString, KPriv, 'utf8');
const ellipticSig = KPrivElliptic.sign(ellipticHashedStringAsBuffer, 'utf8');
const eccKPub = ecc.recover(eccSig, dataAsString, 'utf8');
const ellipticRecoveredKPub = ellipticEc.recoverPubKey(
ellipticHashedStringAsBuffer,
ellipticSig,
ellipticSig.recoveryParam,
'utf8'
);
const ellipticKPub = ellipticEc.keyFromPublic(ellipticRecoveredKPub);
expect(PublicKey.fromElliptic(ellipticKPub, KeyType.k1).toString()).toEqual(k1FormatPublicKeys[idx]);
const eccValid = ecc.verify(eccSig, dataAsString, eccKPub, 'utf8');
const ellipticValid = ellipticEc.verify(
ellipticHashedStringAsBuffer,
ellipticSig,
ellipticEc.keyFromPublic(ellipticKPub),
'utf8'
);
expect(eccValid).toEqual(true);
expect(ellipticValid).toEqual(true);
}
});
it('ensure elliptic verifies zsw-crypto\'s Sigs', () => {
const ellipticEc = new ec('secp256k1');
for (let idx=0; idx<privateKeys.length; idx++) {
const KPriv = privateKeys[idx];
const KPrivElliptic = PrivateKey.fromString(KPriv).toElliptic();
const KPubK1 = new JsSignatureProvider([KPriv]).availableKeys[0];
const dataAsString = 'some string';
const eccHashedString = Buffer.from(ecc.sha256(dataAsString), 'hex');
const ellipticHashedStringAsBuffer = Buffer.from(ellipticEc.hash().update(dataAsString).digest(), 'hex');
expect(eccHashedString).toEqual(ellipticHashedStringAsBuffer);
const eccSig = ecc.sign(dataAsString, KPriv, 'utf8');
const ellipticSig = Signature.fromString(eccSig).toElliptic();
const recoveredKPub = ecc.recover(eccSig, dataAsString, 'utf8');
const ellipticRecoveredKPub = ellipticEc.recoverPubKey(
ellipticHashedStringAsBuffer,
ellipticSig,
ellipticSig.recoveryParam,
'utf8'
);
const ellipticKPub = ellipticEc.keyFromPublic(ellipticRecoveredKPub);
expect(PublicKey.fromElliptic(ellipticKPub, KeyType.k1).toString()).toEqual(PublicKey.fromString(recoveredKPub).toString());
expect(PublicKey.fromElliptic(ellipticKPub, KeyType.k1).toString()).toEqual(k1FormatPublicKeys[idx]);
const ellipticValid = ellipticEc.verify(
ellipticHashedStringAsBuffer,
ellipticSig,
ellipticEc.keyFromPublic(ellipticKPub),
'utf8'
);
expect(ellipticValid).toEqual(true);
}
});
it('ensure ecc verifies elliptic\'s Sigs', () => {
const ellipticEc = new ec('secp256k1');
for (let idx=0; idx<privateKeys.length; idx++) {
const KPriv = privateKeys[idx];
const KPrivElliptic = PrivateKey.fromString(KPriv).toElliptic();
const KPubK1 = new JsSignatureProvider([KPriv]).availableKeys[0];
const dataAsString = 'some string';
const ellipticHashedStringAsBuffer = Buffer.from(ellipticEc.hash().update(dataAsString).digest(), 'hex');
const ellipticSig = KPrivElliptic.sign(ellipticHashedStringAsBuffer, 'utf8');
const ellipticSigAsString = Signature.fromElliptic(ellipticSig, KeyType.k1).toString();
const recoveredKPub = ecc.recover(ellipticSigAsString, dataAsString, 'utf8');
const ellipticRecoveredKPub = ellipticEc.recoverPubKey(
ellipticHashedStringAsBuffer,
ellipticSig,
ellipticSig.recoveryParam,
'utf8'
);
const ellipticKPub = ellipticEc.keyFromPublic(ellipticRecoveredKPub);
expect(PublicKey.fromElliptic(ellipticKPub, KeyType.k1).toString()).toEqual(k1FormatPublicKeys[idx]);
const eccValid = ecc.verify(ellipticSigAsString, dataAsString, recoveredKPub, 'utf8');
expect(eccValid).toEqual(true);
}
});
it('ensure zswjs verifies zsw-crypto\'s Sigs', () => {
for (let idx=0; idx<privateKeys.length; idx++) {
const KPriv = privateKeys[idx];
const privateKey = PrivateKey.fromString(KPriv);
const dataAsString = 'some string';
const eccHashedString = Buffer.from(ecc.sha256(dataAsString), 'hex');
const zswjsHashedStringAsBuffer = Buffer.from(sha256(dataAsString), 'hex');
expect(eccHashedString).toEqual(zswjsHashedStringAsBuffer);
const eccSig = ecc.sign(dataAsString, KPriv, 'utf8');
const zswjsSig = Signature.fromString(eccSig);
const recoveredKPub = ecc.recover(eccSig, dataAsString, 'utf8');
const zswjsRecoveredKPub = zswjsSig.recover(dataAsString, true, 'utf8');
expect(zswjsRecoveredKPub.toLegacyString()).toEqual(recoveredKPub);
expect(zswjsRecoveredKPub.toString()).toEqual(k1FormatPublicKeys[idx]);
const zswjsValid = zswjsSig.verify(dataAsString, zswjsRecoveredKPub, true, 'utf8');
expect(zswjsValid).toEqual(true);
}
});
it('ensure ecc verifies zswjs\'s Sigs', () => {
for (let idx=0; idx<privateKeys.length; idx++) {
const KPriv = privateKeys[idx];
const privateKey = PrivateKey.fromString(KPriv);
const dataAsString = 'some string';
const zswjsHashedStringAsBuffer = Buffer.from(sha256(dataAsString), 'hex');
const zswjsSig = privateKey.sign(zswjsHashedStringAsBuffer, false, 'utf8');
const zswjsSigAsString = zswjsSig.toString();
const recoveredKPub = ecc.recover(zswjsSigAsString, dataAsString, 'utf8');
const zswjsRecoveredKPub = zswjsSig.recover(dataAsString, true, 'utf8');
expect(zswjsRecoveredKPub.toLegacyString()).toEqual(recoveredKPub);
expect(zswjsRecoveredKPub.toString()).toEqual(k1FormatPublicKeys[idx]);
const eccValid = ecc.verify(zswjsSigAsString, dataAsString, recoveredKPub, 'utf8');
expect(eccValid).toEqual(true);
}
});
});
const { TextEncoder, TextDecoder } = require('util');
import { Api } from '../zswjs-api';
import { JsonRpc } from '../zswjs-jsonrpc';
import { JsSignatureProvider } from '../zswjs-jssig';
import * as path from 'path';
import * as fs from 'fs';
import { Action } from '../zswjs-serialize';
import { base64ToBinary } from '../zswjs-numeric';
const transaction = {
expiration: '2018-09-04T18:42:49',
ref_block_num: 38096,
ref_block_prefix: 505360011,
max_net_usage_words: 0,
max_cpu_usage_ms: 0,
delay_sec: 0,
context_free_actions: [] as Action[],
actions: [
{
account: 'tstzswcredit',
name: 'transfer',
authorization: [
{
actor: 'thegazelle',
permission: 'active',
},
],
data: {
from: 'thegazelle',
to: 'remasteryoda',
quantity: '1.0000 ZSWCC',
memo: 'For a secure future.',
},
hex_data: `00808A517DC354CB6012F557656CA4BA1027000000000000045A53574343000014466F722
06120736563757265206675747572652E`,
},
{
account: 'tstzswcredit',
name: 'transfer',
authorization: [
{
actor: 'thegazelle',
permission: 'active',
},
],
data: {
from: 'thegazelle',
to: 'remasteryoda',
quantity: '2.0000 ZSWCC',
memo: 'For a second secure future (multiverse?)',
},
hex_data: `00808A517DC354CB6012F557656CA4BA204E000000000000045A53574343000028466F722061207365636F6E642073656
37572652066757475726520286D756C746976657273653F29`,
},
],
transaction_extensions: [] as any,
};
const serializedTx = [
41, 210, 142, 91, 208, 148, 139, 46, 31, 30, 0, 0, 0, 0, 2, 144, 93, 82,
23, 113, 252, 51, 206, 0, 0, 0, 87, 45, 60, 205, 205, 1, 0, 128, 138, 81,
125, 195, 84, 203, 0, 0, 0, 0, 168, 237, 50, 50, 0, 144, 93, 82, 23, 113,
252, 51, 206, 0, 0, 0, 87, 45, 60, 205, 205, 1, 0, 128, 138, 81, 125, 195,
84, 203, 0, 0, 0, 0, 168, 237, 50, 50, 0, 0
];
const deserializedTx = {
actions: [
{
account: 'tstzswcredit',
authorization: [{ actor: 'thegazelle', permission: 'active' }],
data: '',
name: 'transfer',
},
{
account: 'tstzswcredit',
authorization: [{ actor: 'thegazelle', permission: 'active' }],
data: '',
name: 'transfer',
},
],
context_free_actions: [] as any,
delay_sec: 0,
expiration: '2018-09-04T18:42:49.000',
max_cpu_usage_ms: 0,
max_net_usage_words: 0,
ref_block_num: 38096,
ref_block_prefix: 505360011,
transaction_extensions: [] as any,
};
const serializedActions = [
{
account: 'tstzswcredit',
authorization: [{ actor: 'thegazelle', permission: 'active' }],
data: '00808A517DC354CB6012F557656CA4BA1027000000000000045A53574343000014466F72206120736563757265206675747572652E', // eslint-disable-line
name: 'transfer',
},
{
account: 'tstzswcredit',
authorization: [{ actor: 'thegazelle', permission: 'active' }],
data: '00808A517DC354CB6012F557656CA4BA204E000000000000045A53574343000028466F722061207365636F6E64207365637572652066757475726520286D756C746976657273653F29', // eslint-disable-line
name: 'transfer',
},
];
const deserializedActions = [
{
account: 'tstzswcredit',
authorization: [{ actor: 'thegazelle', permission: 'active' }],
data: {
from: 'thegazelle',
memo: 'For a secure future.',
quantity: '1.0000 ZSWCC',
to: 'remasteryoda',
},
name: 'transfer',
},
{
account: 'tstzswcredit',
authorization: [{ actor: 'thegazelle', permission: 'active' }],
data: {
from: 'thegazelle',
memo: 'For a second secure future (multiverse?)',
quantity: '2.0000 ZSWCC',
to: 'remasteryoda',
},
name: 'transfer',
},
];
describe('zswjs-api', () => {
let api: any;
let rpc: any;
const fetch = async (input: any, init: any): Promise<any> => ({
ok: true,
json: async () => {
if (input === '/v1/chain/get_raw_abi') {
return {
account_name: 'tstzswcredit',
abi: 'DmVvc2lvOjphYmkvMS4wAQxhY2NvdW50X25hbWUEbmFtZQUIdHJhbnNmZXIABARmcm9tDGFjY291bnRfbmFtZQJ0bwxhY2NvdW50X25hbWUIcXVhbnRpdHkFYXNzZXQEbWVtbwZzdHJpbmcGY3JlYXRlAAIGaXNzdWVyDGFjY291bnRfbmFtZQ5tYXhpbXVtX3N1cHBseQVhc3NldAVpc3N1ZQADAnRvDGFjY291bnRfbmFtZQhxdWFudGl0eQVhc3NldARtZW1vBnN0cmluZwdhY2NvdW50AAEHYmFsYW5jZQVhc3NldA5jdXJyZW5jeV9zdGF0cwADBnN1cHBseQVhc3NldAptYXhfc3VwcGx5BWFzc2V0Bmlzc3VlcgxhY2NvdW50X25hbWUDAAAAVy08zc0IdHJhbnNmZXLnBSMjIFRyYW5zZmVyIFRlcm1zICYgQ29uZGl0aW9ucwoKSSwge3tmcm9tfX0sIGNlcnRpZnkgdGhlIGZvbGxvd2luZyB0byBiZSB0cnVlIHRvIHRoZSBiZXN0IG9mIG15IGtub3dsZWRnZToKCjEuIEkgY2VydGlmeSB0aGF0IHt7cXVhbnRpdHl9fSBpcyBub3QgdGhlIHByb2NlZWRzIG9mIGZyYXVkdWxlbnQgb3IgdmlvbGVudCBhY3Rpdml0aWVzLgoyLiBJIGNlcnRpZnkgdGhhdCwgdG8gdGhlIGJlc3Qgb2YgbXkga25vd2xlZGdlLCB7e3RvfX0gaXMgbm90IHN1cHBvcnRpbmcgaW5pdGlhdGlvbiBvZiB2aW9sZW5jZSBhZ2FpbnN0IG90aGVycy4KMy4gSSBoYXZlIGRpc2Nsb3NlZCBhbnkgY29udHJhY3R1YWwgdGVybXMgJiBjb25kaXRpb25zIHdpdGggcmVzcGVjdCB0byB7e3F1YW50aXR5fX0gdG8ge3t0b319LgoKSSB1bmRlcnN0YW5kIHRoYXQgZnVuZHMgdHJhbnNmZXJzIGFyZSBub3QgcmV2ZXJzaWJsZSBhZnRlciB0aGUge3t0cmFuc2FjdGlvbi5kZWxheX19IHNlY29uZHMgb3Igb3RoZXIgZGVsYXkgYXMgY29uZmlndXJlZCBieSB7e2Zyb219fSdzIHBlcm1pc3Npb25zLgoKSWYgdGhpcyBhY3Rpb24gZmFpbHMgdG8gYmUgaXJyZXZlcnNpYmx5IGNvbmZpcm1lZCBhZnRlciByZWNlaXZpbmcgZ29vZHMgb3Igc2VydmljZXMgZnJvbSAne3t0b319JywgSSBhZ3JlZSB0byBlaXRoZXIgcmV0dXJuIHRoZSBnb29kcyBvciBzZXJ2aWNlcyBvciByZXNlbmQge3txdWFudGl0eX19IGluIGEgdGltZWx5IG1hbm5lci4KAAAAAAClMXYFaXNzdWUAAAAAAKhs1EUGY3JlYXRlAAIAAAA4T00RMgNpNjQBCGN1cnJlbmN5AQZ1aW50NjQHYWNjb3VudAAAAAAAkE3GA2k2NAEIY3VycmVuY3kBBnVpbnQ2NA5jdXJyZW5jeV9zdGF0cwAAAA===', // eslint-disable-line
};
}
return transaction;
},
});
beforeEach(() => {
rpc = new JsonRpc('', { fetch });
const signatureProvider = new JsSignatureProvider(['5JtUScZK2XEp3g9gh7F8bwtPTRAkASmNrrftmx4AxDKD5K4zDnr']);
const chainId = '038f4b0fc8ff18a4f0842a8f0564611f6e96e8535901dd45e43ac8691a1c4dca';
api = new Api({
rpc,
signatureProvider,
chainId,
textDecoder: new TextDecoder(),
textEncoder: new TextEncoder()
});
});
it('Doesnt crash', () => {
expect(api).toBeTruthy();
});
it('getAbi returns an abi', async () => {
const response = await api.getAbi('tstzswcredit');
expect(response).toBeTruthy();
});
it('getTransactionAbis returns abis by transactions', async () => {
const response = await api.getTransactionAbis(transaction);
expect(response[0].abi.length).toBeGreaterThan(0);
});
it('getContract returns a contract', async () => {
const response = await api.getContract('tstzswcredit');
expect(response.actions).toBeTruthy();
});
it('deserializeTransaction converts tx from binary', () => {
const tx = api.deserializeTransaction(serializedTx);
expect(tx).toEqual(deserializedTx);
});
it('serializeActions converts actions to hex', async () => {
const response = await api.serializeActions(transaction.actions);
expect(response).toEqual(serializedActions);
});
it('deserializeActions converts actions from hex', async () => {
const response = await api.deserializeActions(serializedActions);
expect(response).toEqual(deserializedActions);
});
it('hasRequiredTaposFields returns true, if required fields are present', () => {
const response = api.hasRequiredTaposFields(transaction);
expect(response).toEqual(true);
});
it('rawAbiToJson returns correct Json from raw Abi', async () => {
const expected = await api.getAbi('tstzswcredit');
const response = await rpc.getRawAbi('tstzswcredit');
const actual = api.rawAbiToJson(response.abi);
expect(actual).toEqual(expected);
});
it('jsonToRawAbi returns correct raw Abi from Json', async () => {
const response = await rpc.getRawAbi('tstzswcredit');
const expected = response.abi;
const jsonAbi = await api.getAbi('tstzswcredit');
const actual = api.jsonToRawAbi(jsonAbi);
expect(actual).toEqual(expected);
});
it('confirms jsonToRawAbi and rawAbiToJson are reciprocal', async () => {
const expectedJsonAbi = await api.getAbi('tstzswcredit');
const response = await rpc.getRawAbi('tstzswcredit');
const expectedRawAbi = response.abi;
const jsonAbi = api.rawAbiToJson(api.jsonToRawAbi(expectedJsonAbi));
const rawAbi = api.jsonToRawAbi(api.rawAbiToJson(expectedRawAbi));
expect(rawAbi).toEqual(expectedRawAbi);
expect(jsonAbi).toEqual(expectedJsonAbi);
});
describe('Abi Serialization', () => {
const serializedAbis = {
'1.0': 'DmVvc2lvOjphYmkvMS4wAQxhY2NvdW50X25hbWUEbmFtZQUIdHJhbnNmZXIABARmcm9tDGFjY291bnRfbmFtZQJ0bwxhY2NvdW50X25hbWUIcXVhbnRpdHkFYXNzZXQEbWVtbwZzdHJpbmcGY3JlYXRlAAIGaXNzdWVyDGFjY291bnRfbmFtZQ5tYXhpbXVtX3N1cHBseQVhc3NldAVpc3N1ZQADAnRvDGFjY291bnRfbmFtZQhxdWFudGl0eQVhc3NldARtZW1vBnN0cmluZwdhY2NvdW50AAEHYmFsYW5jZQVhc3NldA5jdXJyZW5jeV9zdGF0cwADBnN1cHBseQVhc3NldAptYXhfc3VwcGx5BWFzc2V0Bmlzc3VlcgxhY2NvdW50X25hbWUDAAAAVy08zc0IdHJhbnNmZXLnBSMjIFRyYW5zZmVyIFRlcm1zICYgQ29uZGl0aW9ucwoKSSwge3tmcm9tfX0sIGNlcnRpZnkgdGhlIGZvbGxvd2luZyB0byBiZSB0cnVlIHRvIHRoZSBiZXN0IG9mIG15IGtub3dsZWRnZToKCjEuIEkgY2VydGlmeSB0aGF0IHt7cXVhbnRpdHl9fSBpcyBub3QgdGhlIHByb2NlZWRzIG9mIGZyYXVkdWxlbnQgb3IgdmlvbGVudCBhY3Rpdml0aWVzLgoyLiBJIGNlcnRpZnkgdGhhdCwgdG8gdGhlIGJlc3Qgb2YgbXkga25vd2xlZGdlLCB7e3RvfX0gaXMgbm90IHN1cHBvcnRpbmcgaW5pdGlhdGlvbiBvZiB2aW9sZW5jZSBhZ2FpbnN0IG90aGVycy4KMy4gSSBoYXZlIGRpc2Nsb3NlZCBhbnkgY29udHJhY3R1YWwgdGVybXMgJiBjb25kaXRpb25zIHdpdGggcmVzcGVjdCB0byB7e3F1YW50aXR5fX0gdG8ge3t0b319LgoKSSB1bmRlcnN0YW5kIHRoYXQgZnVuZHMgdHJhbnNmZXJzIGFyZSBub3QgcmV2ZXJzaWJsZSBhZnRlciB0aGUge3t0cmFuc2FjdGlvbi5kZWxheX19IHNlY29uZHMgb3Igb3RoZXIgZGVsYXkgYXMgY29uZmlndXJlZCBieSB7e2Zyb219fSdzIHBlcm1pc3Npb25zLgoKSWYgdGhpcyBhY3Rpb24gZmFpbHMgdG8gYmUgaXJyZXZlcnNpYmx5IGNvbmZpcm1lZCBhZnRlciByZWNlaXZpbmcgZ29vZHMgb3Igc2VydmljZXMgZnJvbSAne3t0b319JywgSSBhZ3JlZSB0byBlaXRoZXIgcmV0dXJuIHRoZSBnb29kcyBvciBzZXJ2aWNlcyBvciByZXNlbmQge3txdWFudGl0eX19IGluIGEgdGltZWx5IG1hbm5lci4KAAAAAAClMXYFaXNzdWUAAAAAAKhs1EUGY3JlYXRlAAIAAAA4T00RMgNpNjQBCGN1cnJlbmN5AQZ1aW50NjQHYWNjb3VudAAAAAAAkE3GA2k2NAEIY3VycmVuY3kBBnVpbnQ2NA5jdXJyZW5jeV9zdGF0cwAAAA===',
'1.1': 'DmVvc2lvOjphYmkvMS4xABgIYWJpX2hhc2gAAgVvd25lcgRuYW1lBGhhc2gLY2hlY2tzdW0yNTYIYWN0aXZhdGUAAQ5mZWF0dXJlX2RpZ2VzdAtjaGVja3N1bTI1NglhdXRob3JpdHkABAl0aHJlc2hvbGQGdWludDMyBGtleXMMa2V5X3dlaWdodFtdCGFjY291bnRzGXBlcm1pc3Npb25fbGV2ZWxfd2VpZ2h0W10Fd2FpdHMNd2FpdF93ZWlnaHRbXRVibG9ja2NoYWluX3BhcmFtZXRlcnMAERNtYXhfYmxvY2tfbmV0X3VzYWdlBnVpbnQ2NBp0YXJnZXRfYmxvY2tfbmV0X3VzYWdlX3BjdAZ1aW50MzIZbWF4X3RyYW5zYWN0aW9uX25ldF91c2FnZQZ1aW50MzIeYmFzZV9wZXJfdHJhbnNhY3Rpb25fbmV0X3VzYWdlBnVpbnQzMhBuZXRfdXNhZ2VfbGVld2F5BnVpbnQzMiNjb250ZXh0X2ZyZWVfZGlzY291bnRfbmV0X3VzYWdlX251bQZ1aW50MzIjY29udGV4dF9mcmVlX2Rpc2NvdW50X25ldF91c2FnZV9kZW4GdWludDMyE21heF9ibG9ja19jcHVfdXNhZ2UGdWludDMyGnRhcmdldF9ibG9ja19jcHVfdXNhZ2VfcGN0BnVpbnQzMhltYXhfdHJhbnNhY3Rpb25fY3B1X3VzYWdlBnVpbnQzMhltaW5fdHJhbnNhY3Rpb25fY3B1X3VzYWdlBnVpbnQzMhhtYXhfdHJhbnNhY3Rpb25fbGlmZXRpbWUGdWludDMyHmRlZmVycmVkX3RyeF9leHBpcmF0aW9uX3dpbmRvdwZ1aW50MzIVbWF4X3RyYW5zYWN0aW9uX2RlbGF5BnVpbnQzMhZtYXhfaW5saW5lX2FjdGlvbl9zaXplBnVpbnQzMhdtYXhfaW5saW5lX2FjdGlvbl9kZXB0aAZ1aW50MTYTbWF4X2F1dGhvcml0eV9kZXB0aAZ1aW50MTYLY2FuY2VsZGVsYXkAAg5jYW5jZWxpbmdfYXV0aBBwZXJtaXNzaW9uX2xldmVsBnRyeF9pZAtjaGVja3N1bTI1NgpkZWxldGVhdXRoAAIHYWNjb3VudARuYW1lCnBlcm1pc3Npb24EbmFtZQprZXlfd2VpZ2h0AAIDa2V5CnB1YmxpY19rZXkGd2VpZ2h0BnVpbnQxNghsaW5rYXV0aAAEB2FjY291bnQEbmFtZQRjb2RlBG5hbWUEdHlwZQRuYW1lC3JlcXVpcmVtZW50BG5hbWUKbmV3YWNjb3VudAAEB2NyZWF0b3IEbmFtZQRuYW1lBG5hbWUFb3duZXIJYXV0aG9yaXR5BmFjdGl2ZQlhdXRob3JpdHkHb25lcnJvcgACCXNlbmRlcl9pZAd1aW50MTI4CHNlbnRfdHJ4BWJ5dGVzEHBlcm1pc3Npb25fbGV2ZWwAAgVhY3RvcgRuYW1lCnBlcm1pc3Npb24EbmFtZRdwZXJtaXNzaW9uX2xldmVsX3dlaWdodAACCnBlcm1pc3Npb24QcGVybWlzc2lvbl9sZXZlbAZ3ZWlnaHQGdWludDE2DHByb2R1Y2VyX2tleQACDXByb2R1Y2VyX25hbWUEbmFtZRFibG9ja19zaWduaW5nX2tleQpwdWJsaWNfa2V5DHJlcWFjdGl2YXRlZAABDmZlYXR1cmVfZGlnZXN0C2NoZWNrc3VtMjU2B3JlcWF1dGgAAQRmcm9tBG5hbWUGc2V0YWJpAAIHYWNjb3VudARuYW1lA2FiaQVieXRlcwpzZXRhbGltaXRzAAQHYWNjb3VudARuYW1lCXJhbV9ieXRlcwVpbnQ2NApuZXRfd2VpZ2h0BWludDY0CmNwdV93ZWlnaHQFaW50NjQHc2V0Y29kZQAEB2FjY291bnQEbmFtZQZ2bXR5cGUFdWludDgJdm12ZXJzaW9uBXVpbnQ4BGNvZGUFYnl0ZXMJc2V0cGFyYW1zAAEGcGFyYW1zFWJsb2NrY2hhaW5fcGFyYW1ldGVycwdzZXRwcml2AAIHYWNjb3VudARuYW1lB2lzX3ByaXYFdWludDgIc2V0cHJvZHMAAQhzY2hlZHVsZQ5wcm9kdWNlcl9rZXlbXQp1bmxpbmthdXRoAAMHYWNjb3VudARuYW1lBGNvZGUEbmFtZQR0eXBlBG5hbWUKdXBkYXRlYXV0aAAEB2FjY291bnQEbmFtZQpwZXJtaXNzaW9uBG5hbWUGcGFyZW50BG5hbWUEYXV0aAlhdXRob3JpdHkLd2FpdF93ZWlnaHQAAgh3YWl0X3NlYwZ1aW50MzIGd2VpZ2h0BnVpbnQxNhAAAAAqm+0yMghhY3RpdmF0Zd8CLS0tCnNwZWNfdmVyc2lvbjogIjAuMi4wIgp0aXRsZTogQWN0aXZhdGUgUHJvdG9jb2wgRmVhdHVyZQpzdW1tYXJ5OiAnQWN0aXZhdGUgcHJvdG9jb2wgZmVhdHVyZSB7e25vd3JhcCBmZWF0dXJlX2RpZ2VzdH19JwppY29uOiBodHRwOi8vMTI3LjAuMC4xL3JpY2FyZGlhbl9hc3NldHMvZW9zaW8uY29udHJhY3RzL2ljb25zL2FkbWluLnBuZyM5YmYxY2VjNjY0ODYzYmQ2YWFhYzBmODE0YjIzNWY4Nzk5ZmIwMmM4NTBlOWFhNWRhMzRlOGEwMDRiZDY1MThlCi0tLQoKe3skYWN0aW9uLmFjY291bnR9fSBhY3RpdmF0ZXMgdGhlIHByb3RvY29sIGZlYXR1cmUgd2l0aCBhIGRpZ2VzdCBvZiB7e2ZlYXR1cmVfZGlnZXN0fX0uALyJKkWFpkELY2FuY2VsZGVsYXnhAi0tLQpzcGVjX3ZlcnNpb246ICIwLjIuMCIKdGl0bGU6IENhbmNlbCBEZWxheWVkIFRyYW5zYWN0aW9uCnN1bW1hcnk6ICd7e25vd3JhcCBjYW5jZWxpbmdfYXV0aC5hY3Rvcn19IGNhbmNlbHMgYSBkZWxheWVkIHRyYW5zYWN0aW9uJwppY29uOiBodHRwOi8vMTI3LjAuMC4xL3JpY2FyZGlhbl9hc3NldHMvZW9zaW8uY29udHJhY3RzL2ljb25zL2FjY291bnQucG5nIzNkNTVhMmZjM2E1YzIwYjQ1NmY1NjU3ZmFmNjY2YmMyNWZmZDA2ZjQ4MzZjNWU4MjU2Zjc0MTE0OWIwYjI5NGYKLS0tCgp7e2NhbmNlbGluZ19hdXRoLmFjdG9yfX0gY2FuY2VscyB0aGUgZGVsYXllZCB0cmFuc2FjdGlvbiB3aXRoIGlkIHt7dHJ4X2lkfX0uAEDL2qisokoKZGVsZXRlYXV0aMwCLS0tCnNwZWNfdmVyc2lvbjogIjAuMi4wIgp0aXRsZTogRGVsZXRlIEFjY291bnQgUGVybWlzc2lvbgpzdW1tYXJ5OiAnRGVsZXRlIHRoZSB7e25vd3JhcCBwZXJtaXNzaW9ufX0gcGVybWlzc2lvbiBvZiB7e25vd3JhcCBhY2NvdW50fX0nCmljb246IGh0dHA6Ly8xMjcuMC4wLjEvcmljYXJkaWFuX2Fzc2V0cy9lb3Npby5jb250cmFjdHMvaWNvbnMvYWNjb3VudC5wbmcjM2Q1NWEyZmMzYTVjMjBiNDU2ZjU2NTdmYWY2NjZiYzI1ZmZkMDZmNDgzNmM1ZTgyNTZmNzQxMTQ5YjBiMjk0ZgotLS0KCkRlbGV0ZSB0aGUge3twZXJtaXNzaW9ufX0gcGVybWlzc2lvbiBvZiB7e2FjY291bnR9fS4AAAAtawOniwhsaW5rYXV0aPQELS0tCnNwZWNfdmVyc2lvbjogIjAuMi4wIgp0aXRsZTogTGluayBBY3Rpb24gdG8gUGVybWlzc2lvbgpzdW1tYXJ5OiAne3tub3dyYXAgYWNjb3VudH19IHNldHMgdGhlIG1pbmltdW0gcmVxdWlyZWQgcGVybWlzc2lvbiBmb3IgdGhlIHt7I2lmIHR5cGV9fXt7bm93cmFwIHR5cGV9fSBhY3Rpb24gb2YgdGhle3svaWZ9fSB7e25vd3JhcCBjb2RlfX0gY29udHJhY3QgdG8ge3tub3dyYXAgcmVxdWlyZW1lbnR9fScKaWNvbjogaHR0cDovLzEyNy4wLjAuMS9yaWNhcmRpYW5fYXNzZXRzL2Vvc2lvLmNvbnRyYWN0cy9pY29ucy9hY2NvdW50LnBuZyMzZDU1YTJmYzNhNWMyMGI0NTZmNTY1N2ZhZjY2NmJjMjVmZmQwNmY0ODM2YzVlODI1NmY3NDExNDliMGIyOTRmCi0tLQoKe3thY2NvdW50fX0gc2V0cyB0aGUgbWluaW11bSByZXF1aXJlZCBwZXJtaXNzaW9uIGZvciB0aGUge3sjaWYgdHlwZX19e3t0eXBlfX0gYWN0aW9uIG9mIHRoZXt7L2lmfX0ge3tjb2RlfX0gY29udHJhY3QgdG8ge3tyZXF1aXJlbWVudH19LgoKe3sjaWYgdHlwZX19e3tlbHNlfX1BbnkgbGlua3MgZXhwbGljaXRseSBhc3NvY2lhdGVkIHRvIHNwZWNpZmljIGFjdGlvbnMgb2Yge3tjb2RlfX0gd2lsbCB0YWtlIHByZWNlZGVuY2Uue3svaWZ9fQBAnpoiZLiaCm5ld2FjY291bnTXAy0tLQpzcGVjX3ZlcnNpb246ICIwLjIuMCIKdGl0bGU6IENyZWF0ZSBOZXcgQWNjb3VudApzdW1tYXJ5OiAne3tub3dyYXAgY3JlYXRvcn19IGNyZWF0ZXMgYSBuZXcgYWNjb3VudCB3aXRoIHRoZSBuYW1lIHt7bm93cmFwIG5hbWV9fScKaWNvbjogaHR0cDovLzEyNy4wLjAuMS9yaWNhcmRpYW5fYXNzZXRzL2Vvc2lvLmNvbnRyYWN0cy9pY29ucy9hY2NvdW50LnBuZyMzZDU1YTJmYzNhNWMyMGI0NTZmNTY1N2ZhZjY2NmJjMjVmZmQwNmY0ODM2YzVlODI1NmY3NDExNDliMGIyOTRmCi0tLQoKe3tjcmVhdG9yfX0gY3JlYXRlcyBhIG5ldyBhY2NvdW50IHdpdGggdGhlIG5hbWUge3tuYW1lfX0gYW5kIHRoZSBmb2xsb3dpbmcgcGVybWlzc2lvbnM6Cgpvd25lciBwZXJtaXNzaW9uIHdpdGggYXV0aG9yaXR5Ogp7e3RvX2pzb24gb3duZXJ9fQoKYWN0aXZlIHBlcm1pc3Npb24gd2l0aCBhdXRob3JpdHk6Cnt7dG9fanNvbiBhY3RpdmV9fQAAAODSe9WkB29uZXJyb3IAkFQ222VkrLoMcmVxYWN0aXZhdGVk/wItLS0Kc3BlY192ZXJzaW9uOiAiMC4yLjAiCnRpdGxlOiBBc3NlcnQgUHJvdG9jb2wgRmVhdHVyZSBBY3RpdmF0aW9uCnN1bW1hcnk6ICdBc3NlcnQgdGhhdCBwcm90b2NvbCBmZWF0dXJlIHt7bm93cmFwIGZlYXR1cmVfZGlnZXN0fX0gaGFzIGJlZW4gYWN0aXZhdGVkJwppY29uOiBodHRwOi8vMTI3LjAuMC4xL3JpY2FyZGlhbl9hc3NldHMvZW9zaW8uY29udHJhY3RzL2ljb25zL2FkbWluLnBuZyM5YmYxY2VjNjY0ODYzYmQ2YWFhYzBmODE0YjIzNWY4Nzk5ZmIwMmM4NTBlOWFhNWRhMzRlOGEwMDRiZDY1MThlCi0tLQoKQXNzZXJ0IHRoYXQgdGhlIHByb3RvY29sIGZlYXR1cmUgd2l0aCBhIGRpZ2VzdCBvZiB7e2ZlYXR1cmVfZGlnZXN0fX0gaGFzIGJlZW4gYWN0aXZhdGVkLgAAAKBlbay6B3JlcWF1dGi8Ai0tLQpzcGVjX3ZlcnNpb246ICIwLjIuMCIKdGl0bGU6IEFzc2VydCBBdXRob3JpemF0aW9uCnN1bW1hcnk6ICdBc3NlcnQgdGhhdCBhdXRob3JpemF0aW9uIGJ5IHt7bm93cmFwIGZyb219fSBpcyBwcm92aWRlZCcKaWNvbjogaHR0cDovLzEyNy4wLjAuMS9yaWNhcmRpYW5fYXNzZXRzL2Vvc2lvLmNvbnRyYWN0cy9pY29ucy9hY2NvdW50LnBuZyMzZDU1YTJmYzNhNWMyMGI0NTZmNTY1N2ZhZjY2NmJjMjVmZmQwNmY0ODM2YzVlODI1NmY3NDExNDliMGIyOTRmCi0tLQoKQXNzZXJ0IHRoYXQgYXV0aG9yaXphdGlvbiBieSB7e2Zyb219fSBpcyBwcm92aWRlZC4AAAAAuGOywgZzZXRhYmnKAi0tLQpzcGVjX3ZlcnNpb246ICIwLjIuMCIKdGl0bGU6IERlcGxveSBDb250cmFjdCBBQkkKc3VtbWFyeTogJ0RlcGxveSBjb250cmFjdCBBQkkgb24gYWNjb3VudCB7e25vd3JhcCBhY2NvdW50fX0nCmljb246IGh0dHA6Ly8xMjcuMC4wLjEvcmljYXJkaWFuX2Fzc2V0cy9lb3Npby5jb250cmFjdHMvaWNvbnMvYWNjb3VudC5wbmcjM2Q1NWEyZmMzYTVjMjBiNDU2ZjU2NTdmYWY2NjZiYzI1ZmZkMDZmNDgzNmM1ZTgyNTZmNzQxMTQ5YjBiMjk0ZgotLS0KCkRlcGxveSB0aGUgQUJJIGZpbGUgYXNzb2NpYXRlZCB3aXRoIHRoZSBjb250cmFjdCBvbiBhY2NvdW50IHt7YWNjb3VudH19LgAAzk66aLLCCnNldGFsaW1pdHPNAy0tLQpzcGVjX3ZlcnNpb246ICIwLjIuMCIKdGl0bGU6IEFkanVzdCBSZXNvdXJjZSBMaW1pdHMgb2YgQWNjb3VudApzdW1tYXJ5OiAnQWRqdXN0IHJlc291cmNlIGxpbWl0cyBvZiBhY2NvdW50IHt7bm93cmFwIGFjY291bnR9fScKaWNvbjogaHR0cDovLzEyNy4wLjAuMS9yaWNhcmRpYW5fYXNzZXRzL2Vvc2lvLmNvbnRyYWN0cy9pY29ucy9hZG1pbi5wbmcjOWJmMWNlYzY2NDg2M2JkNmFhYWMwZjgxNGIyMzVmODc5OWZiMDJjODUwZTlhYTVkYTM0ZThhMDA0YmQ2NTE4ZQotLS0KCnt7JGFjdGlvbi5hY2NvdW50fX0gdXBkYXRlcyB7e2FjY291bnR9feKAmXMgcmVzb3VyY2UgbGltaXRzIHRvIGhhdmUgYSBSQU0gcXVvdGEgb2Yge3tyYW1fYnl0ZXN9fSBieXRlcywgYSBORVQgYmFuZHdpZHRoIHF1b3RhIG9mIHt7bmV0X3dlaWdodH19IGFuZCBhIENQVSBiYW5kd2lkdGggcXVvdGEgb2Yge3tjcHVfd2VpZ2h0fX0uAAAAQCWKssIHc2V0Y29kZb0CLS0tCnNwZWNfdmVyc2lvbjogIjAuMi4wIgp0aXRsZTogRGVwbG95IENvbnRyYWN0IENvZGUKc3VtbWFyeTogJ0RlcGxveSBjb250cmFjdCBjb2RlIG9uIGFjY291bnQge3tub3dyYXAgYWNjb3VudH19JwppY29uOiBodHRwOi8vMTI3LjAuMC4xL3JpY2FyZGlhbl9hc3NldHMvZW9zaW8uY29udHJhY3RzL2ljb25zL2FjY291bnQucG5nIzNkNTVhMmZjM2E1YzIwYjQ1NmY1NjU3ZmFmNjY2YmMyNWZmZDA2ZjQ4MzZjNWU4MjU2Zjc0MTE0OWIwYjI5NGYKLS0tCgpEZXBsb3kgY29tcGlsZWQgY29udHJhY3QgY29kZSB0byB0aGUgYWNjb3VudCB7e2FjY291bnR9fS4AAMDSXFOzwglzZXRwYXJhbXOnAi0tLQpzcGVjX3ZlcnNpb246ICIwLjIuMCIKdGl0bGU6IFNldCBTeXN0ZW0gUGFyYW1ldGVycwpzdW1tYXJ5OiAnU2V0IHN5c3RlbSBwYXJhbWV0ZXJzJwppY29uOiBodHRwOi8vMTI3LjAuMC4xL3JpY2FyZGlhbl9hc3NldHMvZW9zaW8uY29udHJhY3RzL2ljb25zL2FkbWluLnBuZyM5YmYxY2VjNjY0ODYzYmQ2YWFhYzBmODE0YjIzNWY4Nzk5ZmIwMmM4NTBlOWFhNWRhMzRlOGEwMDRiZDY1MThlCi0tLQoKe3skYWN0aW9uLmFjY291bnR9fSBzZXRzIHN5c3RlbSBwYXJhbWV0ZXJzIHRvOgp7e3RvX2pzb24gcGFyYW1zfX0AAABgu1uzwgdzZXRwcml25AMtLS0Kc3BlY192ZXJzaW9uOiAiMC4yLjAiCnRpdGxlOiBNYWtlIGFuIEFjY291bnQgUHJpdmlsZWdlZCBvciBVbnByaXZpbGVnZWQKc3VtbWFyeTogJ3t7I2lmIGlzX3ByaXZ9fU1ha2Uge3tub3dyYXAgYWNjb3VudH19IHByaXZpbGVnZWR7e2Vsc2V9fVJlbW92ZSBwcml2aWxlZ2VkIHN0YXR1cyBvZiB7e25vd3JhcCBhY2NvdW50fX17ey9pZn19JwppY29uOiBodHRwOi8vMTI3LjAuMC4xL3JpY2FyZGlhbl9hc3NldHMvZW9zaW8uY29udHJhY3RzL2ljb25zL2FkbWluLnBuZyM5YmYxY2VjNjY0ODYzYmQ2YWFhYzBmODE0YjIzNWY4Nzk5ZmIwMmM4NTBlOWFhNWRhMzRlOGEwMDRiZDY1MThlCi0tLQoKe3sjaWYgaXNfcHJpdn19Cnt7JGFjdGlvbi5hY2NvdW50fX0gbWFrZXMge3thY2NvdW50fX0gcHJpdmlsZWdlZC4Ke3tlbHNlfX0Ke3skYWN0aW9uLmFjY291bnR9fSByZW1vdmVzIHByaXZpbGVnZWQgc3RhdHVzIG9mIHt7YWNjb3VudH19Lgp7ey9pZn19AAAAONFbs8IIc2V0cHJvZHOUAy0tLQpzcGVjX3ZlcnNpb246ICIwLjIuMCIKdGl0bGU6IFNldCBCbG9jayBQcm9kdWNlcnMKc3VtbWFyeTogJ1NldCBibG9jayBwcm9kdWNlciBzY2hlZHVsZScKaWNvbjogaHR0cDovLzEyNy4wLjAuMS9yaWNhcmRpYW5fYXNzZXRzL2Vvc2lvLmNvbnRyYWN0cy9pY29ucy9hZG1pbi5wbmcjOWJmMWNlYzY2NDg2M2JkNmFhYWMwZjgxNGIyMzVmODc5OWZiMDJjODUwZTlhYTVkYTM0ZThhMDA0YmQ2NTE4ZQotLS0KCnt7JGFjdGlvbi5hY2NvdW50fX0gcHJvcG9zZXMgYSBibG9jayBwcm9kdWNlciBzY2hlZHVsZSBvZjoKe3sjZWFjaCBzY2hlZHVsZX19CiAgMS4ge3t0aGlzLnByb2R1Y2VyX25hbWV9fSB3aXRoIGEgYmxvY2sgc2lnbmluZyBrZXkgb2Yge3t0aGlzLmJsb2NrX3NpZ25pbmdfa2V5fX0Ke3svZWFjaH19AEDL2sDp4tQKdW5saW5rYXV0aOgELS0tCnNwZWNfdmVyc2lvbjogIjAuMi4wIgp0aXRsZTogVW5saW5rIEFjdGlvbiBmcm9tIFBlcm1pc3Npb24Kc3VtbWFyeTogJ3t7bm93cmFwIGFjY291bnR9fSB1bnNldHMgdGhlIG1pbmltdW0gcmVxdWlyZWQgcGVybWlzc2lvbiBmb3IgdGhlIHt7I2lmIHR5cGV9fXt7bm93cmFwIHR5cGV9fSBhY3Rpb24gb2YgdGhle3svaWZ9fSB7e25vd3JhcCBjb2RlfX0gY29udHJhY3QnCmljb246IGh0dHA6Ly8xMjcuMC4wLjEvcmljYXJkaWFuX2Fzc2V0cy9lb3Npby5jb250cmFjdHMvaWNvbnMvYWNjb3VudC5wbmcjM2Q1NWEyZmMzYTVjMjBiNDU2ZjU2NTdmYWY2NjZiYzI1ZmZkMDZmNDgzNmM1ZTgyNTZmNzQxMTQ5YjBiMjk0ZgotLS0KCnt7YWNjb3VudH19IHJlbW92ZXMgdGhlIGFzc29jaWF0aW9uIGJldHdlZW4gdGhlIHt7I2lmIHR5cGV9fXt7dHlwZX19IGFjdGlvbiBvZiB0aGV7ey9pZn19IHt7Y29kZX19IGNvbnRyYWN0IGFuZCBpdHMgbWluaW11bSByZXF1aXJlZCBwZXJtaXNzaW9uLgoKe3sjaWYgdHlwZX19e3tlbHNlfX1UaGlzIHdpbGwgbm90IHJlbW92ZSBhbnkgbGlua3MgZXhwbGljaXRseSBhc3NvY2lhdGVkIHRvIHNwZWNpZmljIGFjdGlvbnMgb2Yge3tjb2RlfX0ue3svaWZ9fQBAy9qobFLVCnVwZGF0ZWF1dGjEAy0tLQpzcGVjX3ZlcnNpb246ICIwLjIuMCIKdGl0bGU6IE1vZGlmeSBBY2NvdW50IFBlcm1pc3Npb24Kc3VtbWFyeTogJ0FkZCBvciB1cGRhdGUgdGhlIHt7bm93cmFwIHBlcm1pc3Npb259fSBwZXJtaXNzaW9uIG9mIHt7bm93cmFwIGFjY291bnR9fScKaWNvbjogaHR0cDovLzEyNy4wLjAuMS9yaWNhcmRpYW5fYXNzZXRzL2Vvc2lvLmNvbnRyYWN0cy9pY29ucy9hY2NvdW50LnBuZyMzZDU1YTJmYzNhNWMyMGI0NTZmNTY1N2ZhZjY2NmJjMjVmZmQwNmY0ODM2YzVlODI1NmY3NDExNDliMGIyOTRmCi0tLQoKTW9kaWZ5LCBhbmQgY3JlYXRlIGlmIG5lY2Vzc2FyeSwgdGhlIHt7cGVybWlzc2lvbn19IHBlcm1pc3Npb24gb2Yge3thY2NvdW50fX0gdG8gaGF2ZSBhIHBhcmVudCBwZXJtaXNzaW9uIG9mIHt7cGFyZW50fX0gYW5kIHRoZSBmb2xsb3dpbmcgYXV0aG9yaXR5Ogp7e3RvX2pzb24gYXV0aH19AQAAAKBh09wxA2k2NAAACGFiaV9oYXNoAAAAAAAA=',
'1.2': 'DmVvc2lvOjphYmkvMS4yAAcDZGVsAAEEdXVpZAZzdHJpbmcMZ2V0YnlhY2NuYW1lAAEMYWNjb3VudF9uYW1lBG5hbWUKdG9kb19lbnRyeQAEBHV1aWQGc3RyaW5nDGFjY291bnRfbmFtZRF0dXBsZV9zdHJpbmdfbmFtZQR0YXNrE3R1cGxlX3N0cmluZ19zdHJpbmcHY2hlY2tlZBF0dXBsZV9zdHJpbmdfYm9vbBF0dXBsZV9zdHJpbmdfYm9vbAACB2ZpZWxkXzAGc3RyaW5nB2ZpZWxkXzEEYm9vbBF0dXBsZV9zdHJpbmdfbmFtZQACB2ZpZWxkXzAGc3RyaW5nB2ZpZWxkXzEEbmFtZRN0dXBsZV9zdHJpbmdfc3RyaW5nAAIHZmllbGRfMAZzdHJpbmcHZmllbGRfMQZzdHJpbmcGdXBzZXJ0AAQEdXVpZAZzdHJpbmcMYWNjb3VudF9uYW1lBG5hbWUEdGFzawZzdHJpbmcHY2hlY2tlZARib29sAwAAAAAAAKJKA2RlbACgpJkIGX+yYgxnZXRieWFjY25hbWUAAAAAAOSrcNUGdXBzZXJ0AAAAAAAAAqCkmQgZf7JiDHRvZG9fZW50cnlbXQAAAADkq3DVCnRvZG9fZW50cnkBAAAAANBE84YKdG9kb19lbnRyeQAAAAAAkJzWBnN0cmluZwMAAABASTMRMhF0dXBsZV9zdHJpbmdfbmFtZQAAACApiFRDEXR1cGxlX3N0cmluZ19ib29sAAAAAAAAsckTdHVwbGVfc3RyaW5nX3N0cmluZw===',
};
it('deserializes/serializes the 1.0 abi', () => {
const raw = base64ToBinary(serializedAbis['1.0']);
const deserializedAbi = api.rawAbiToJson(raw);
const serializedAbi = api.jsonToRawAbi(deserializedAbi);
expect(serializedAbi).toEqual(raw);
});
it('deserializes/serializes the 1.1 abi', () => {
const raw = base64ToBinary(serializedAbis['1.1']);
const deserializedAbi = api.rawAbiToJson(raw);
const serializedAbi = api.jsonToRawAbi(deserializedAbi);
expect(serializedAbi).toEqual(raw);
});
it('deserializes/serializes the 1.2 abi', () => {
const raw = base64ToBinary(serializedAbis['1.2']);
const deserializedAbi = api.rawAbiToJson(raw);
const serializedAbi = api.jsonToRawAbi(deserializedAbi);
expect(serializedAbi).toEqual(raw);
});
});
describe('Api shorthand design (JsonAbi)', () => {
it('errors if abi is not cached', () => {
const abiCheck = () => {
api.with('tstzswcredit').as('bob')
.transfer('thegazelle', 'remasteryoda', '1.0000 ZSWCC', 'For a secure future.');
};
expect(abiCheck).toThrowError('ABI must be cached before using ActionBuilder, run api.getAbi()');
});
it('generates a valid serialized action using api.with()', async () => {
await api.getAbi('tstzswcredit');
const serializedAction = api.with('tstzswcredit').as('thegazelle')
.transfer('thegazelle', 'remasteryoda', '1.0000 ZSWCC', 'For a secure future.');
expect(serializedAction).toEqual(serializedActions[0]);
});
it('generates a valid serialized action using tx.with()', async () => {
await api.getAbi('tstzswcredit');
const tx = api.buildTransaction();
const serializedAction = tx.with('tstzswcredit').as('thegazelle')
.transfer('thegazelle', 'remasteryoda', '2.0000 ZSWCC', 'For a second secure future (multiverse?)');
expect(serializedAction).toEqual(serializedActions[1]);
});
it('confirms serializeActions and ActionBuilder return same serialized data', async () => {
const response = await api.serializeActions(transaction.actions);
const firstAction = api.with('tstzswcredit').as('thegazelle')
.transfer('thegazelle', 'remasteryoda', '1.0000 ZSWCC', 'For a secure future.');
const secondAction = api.with('tstzswcredit').as('thegazelle')
.transfer('thegazelle', 'remasteryoda', '2.0000 ZSWCC', 'For a second secure future (multiverse?)');
expect([firstAction, secondAction]).toEqual(response);
});
it('generates the same serialized data using the longer authorization', async () => {
await api.getAbi('tstzswcredit');
const firstSerializedAction =
api.with('tstzswcredit').as('thegazelle')
.transfer('thegazelle', 'remasteryoda', '1.0000 ZSWCC', 'For a secure future.');
const secondSerializedAction =
api.with('tstzswcredit').as([{ actor: 'thegazelle', permission: 'active'}])
.transfer('thegazelle', 'remasteryoda', '1.0000 ZSWCC', 'For a secure future.');
expect(firstSerializedAction).toEqual(secondSerializedAction);
});
it('confirms the transaction extension serialization is reciprocal', async () => {
const resourcePayerTrx = {
expiration: '2021-06-28T15:55:37.000',
ref_block_num: 2097,
ref_block_prefix: 1309445478,
actions: [{
account: 'zsw.token',
name: 'transfer',
authorization: [{
actor: 'bob',
permission: 'active',
}, {
actor: 'alice',
permission: 'active',
}],
data: '0000000000000E3D0000000000855C34010000000000000004535953000000000E7265736F75726365207061796572'
}],
context_free_actions: [] as any,
resource_payer: {
payer: 'payer',
max_net_bytes: 4096,
max_cpu_us: 250,
max_memory_bytes: 0
}
};
const serialized = [[1, '0000000080ABBCA90010000000000000FA000000000000000000000000000000']];
const deserialized = {
resource_payer: {
payer: 'payer',
max_net_bytes: 4096,
max_cpu_us: 250,
max_memory_bytes: 0
}
};
const serializedTransactionExtensions = api.serializeTransactionExtensions(resourcePayerTrx);
expect(serializedTransactionExtensions).toEqual(serialized);
const deserializedTransactionExtensions = api.deserializeTransactionExtensions(serialized);
expect(deserializedTransactionExtensions).toEqual(deserialized);
});
});
});
import { JsonRpc } from '../zswjs-jsonrpc';
import { RpcError } from '../zswjs-rpcerror';
describe('JSON RPC', () => {
const endpointExtraSlash = 'http://localhost/';
const endpoint = 'http://localhost';
const fetchMock = fetch as any;
let jsonRpc: JsonRpc;
beforeEach(() => {
fetchMock.resetMocks();
jsonRpc = new JsonRpc(endpointExtraSlash);
});
it('throws error bad status', async () => {
let actMessage = '';
const expMessage = 'Not Found';
const accountName = 'myaccountaaa';
const expReturn = { data: '12345', message: expMessage };
fetchMock.once(JSON.stringify(expReturn), { status: 404 });
// async / await don't play well with expect().toThrow()
try {
await jsonRpc.get_abi(accountName);
} catch (e) {
expect(e).toBeInstanceOf(RpcError);
actMessage = e.message;
}
expect(actMessage).toEqual(expMessage);
});
it('throws error unprocessed', async () => {
let actMessage = '';
const expMessage = 'Not Processed';
const accountName = 'myaccountaaa';
const expReturn = {
data: '12345',
processed: {
except: {
message: expMessage,
},
},
};
fetchMock.once(JSON.stringify(expReturn));
try {
await jsonRpc.get_abi(accountName);
} catch (e) {
expect(e).toBeInstanceOf(RpcError);
actMessage = e.message;
}
expect(actMessage).toEqual(expMessage);
});
it('calls provided fetch instead of default', async () => {
const expPath = '/v1/chain/get_abi';
const accountName = 'myaccountaaa';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
account_name: accountName,
}),
method: 'POST',
};
const mockResp = {
json: () => {
return expReturn;
},
ok: true,
};
const myFetch = jest.fn();
myFetch.mockReturnValue(mockResp);
jsonRpc = new JsonRpc(endpoint, { fetch: myFetch });
await jsonRpc.get_abi(accountName);
expect(myFetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_abi', async () => {
const expPath = '/v1/chain/get_abi';
const accountName = 'myaccountaaa';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
account_name: accountName,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_abi(accountName);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_account', async () => {
const expPath = '/v1/chain/get_account';
const accountName = 'myaccountaaa';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
account_name: accountName,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_account(accountName);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_block_header_state', async () => {
const expPath = '/v1/chain/get_block_header_state';
const blockNumOrId = 1234;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
block_num_or_id: blockNumOrId,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_block_header_state(blockNumOrId);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_block_info', async () => {
const expPath = '/v1/chain/get_block_info';
const blockNum = 1234;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
block_num: blockNum,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_block_info(blockNum);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_block', async () => {
const expPath = '/v1/chain/get_block';
const blockNumOrId = 1234;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
block_num_or_id: blockNumOrId,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_block(blockNumOrId);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_code', async () => {
const expPath = '/v1/chain/get_code';
const accountName = 'myaccountaaa';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
account_name: accountName,
code_as_wasm: true,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_code(accountName);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_currency_balance with all params', async () => {
const expPath = '/v1/chain/get_currency_balance';
const code = 'morse';
const account = 'myaccountaaa';
const symbol = 'EOS';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
code,
account,
symbol,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_currency_balance(code, account, symbol);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_currency_balance with default params', async () => {
const expPath = '/v1/chain/get_currency_balance';
const code = 'morse';
const account = 'myaccountaaa';
const symbol: string = null;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
code,
account,
symbol,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_currency_balance(code, account);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_currency_stats with all params', async () => {
const expPath = '/v1/chain/get_currency_stats';
const code = 'morse';
const symbol = 'EOS';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
code,
symbol,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_currency_stats(code, symbol);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_info', async () => {
const expPath = '/v1/chain/get_info';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_info();
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_producer_schedule', async () => {
const expPath = '/v1/chain/get_producer_schedule';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_producer_schedule();
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_producers with all params', async () => {
const expPath = '/v1/chain/get_producers';
const json = false;
const lowerBound = 'zero';
const limit = 10;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
json,
lower_bound: lowerBound,
limit,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_producers(json, lowerBound, limit);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_producers with default params', async () => {
const expPath = '/v1/chain/get_producers';
const json = true;
const lowerBound = '';
const limit = 50;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
json,
lower_bound: lowerBound,
limit,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_producers();
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_raw_code_and_abi', async () => {
const expPath = '/v1/chain/get_raw_code_and_abi';
const accountName = 'myaccountaaa';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
account_name: accountName,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_raw_code_and_abi(accountName);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_scheduled_transactions', async () => {
const expPath = '/v1/chain/get_scheduled_transactions';
const json = true;
const lowerBound = '';
const limit = 50;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
json,
lower_bound: lowerBound,
limit,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_scheduled_transactions();
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_table_rows with all params', async () => {
const expPath = '/v1/chain/get_table_rows';
const json = false;
const code = 'morse';
const scope = 'minty';
const table = 'coffee';
const lowerBound = 'zero';
const upperBound = 'five';
const limit = 20;
const indexPosition = 2;
const keyType = 'str';
const expReturn = { data: '12345' };
const reverse = false;
const showPayer = false;
const callParams = {
json,
code,
scope,
table,
lower_bound: lowerBound,
upper_bound: upperBound,
index_position: indexPosition,
key_type: keyType,
limit,
reverse,
show_payer: showPayer,
};
const expParams = {
body: JSON.stringify(callParams),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_table_rows(callParams);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_table_rows with default params', async () => {
const expPath = '/v1/chain/get_table_rows';
const json = true;
const code = 'morse';
const scope = 'minty';
const table = 'coffee';
const lowerBound = '';
const upperBound = '';
const limit = 10;
const indexPosition = 1;
const keyType = '';
const reverse = false;
const showPayer = false;
const expReturn = { data: '12345' };
const callParams = {
code,
scope,
table,
};
const expParams = {
body: JSON.stringify({
json,
code,
scope,
table,
lower_bound: lowerBound,
upper_bound: upperBound,
index_position: indexPosition,
key_type: keyType,
limit,
reverse,
show_payer: showPayer,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_table_rows(callParams);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_kv_table_rows with all params', async () => {
const expPath = '/v1/chain/get_kv_table_rows';
const json = false;
const code = 'morse';
const table = 'coffee';
const indexName = 'type';
const encodeType = 'bytes';
const indexValue = 'minty';
const lowerBound = 'zero';
const upperBound = 'five';
const limit = 10;
const reverse = false;
const showPayer = false;
const expReturn = { data: '12345' };
const callParams = {
json,
code,
table,
index_name: indexName,
encode_type: encodeType,
index_value: indexValue,
lower_bound: lowerBound,
upper_bound: upperBound,
limit,
reverse,
show_payer: showPayer,
};
const expParams = {
body: JSON.stringify(callParams),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_kv_table_rows(callParams);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_kv_table_rows with default params', async () => {
const expPath = '/v1/chain/get_kv_table_rows';
const json = true;
const code = 'morse';
const table = 'coffee';
const indexName = 'type';
const encodeType = 'bytes';
const limit = 10;
const reverse = false;
const showPayer = false;
const expReturn = { data: '12345' };
const callParams = {
code,
table,
index_name: indexName,
};
const expParams = {
body: JSON.stringify({
json,
code,
table,
index_name: indexName,
encode_type: encodeType,
limit,
reverse,
show_payer: showPayer,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_kv_table_rows(callParams);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_table_by_scope with all params', async () => {
const expPath = '/v1/chain/get_table_by_scope';
const code = 'morse';
const table = 'coffee';
const lowerBound = 'minty';
const upperBound = 'minty';
const limit = 20;
const expReturn = { data: '12345' };
const callParams = {
code,
table,
lower_bound: lowerBound,
upper_bound: upperBound,
limit,
};
const expParams = {
body: JSON.stringify(callParams),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_table_by_scope(callParams);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls get_table_by_scope with default params', async () => {
const expPath = '/v1/chain/get_table_by_scope';
const code = 'morse';
const table = 'coffee';
const lowerBound = '';
const upperBound = '';
const limit = 10;
const expReturn = { data: '12345' };
const callParams = {
code,
table,
};
const expParams = {
body: JSON.stringify({
code,
table,
lower_bound: lowerBound,
upper_bound: upperBound,
limit,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.get_table_by_scope(callParams);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls getRequiredKeys', async () => {
const expPath = '/v1/chain/get_required_keys';
const keys = ['key1', 'key2', 'key3'];
const expReturn = { required_keys: keys };
const callParams = {
transaction: 'mytxn',
availableKeys: keys,
};
const expParams = {
body: JSON.stringify({
transaction: callParams.transaction,
available_keys: callParams.availableKeys,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.getRequiredKeys(callParams);
expect(response).toEqual(expReturn.required_keys);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls push_transaction', async () => {
const expPath = '/v1/chain/push_transaction';
const signatures = [
'George Washington',
'John Hancock',
'Abraham Lincoln',
];
const serializedTransaction = new Uint8Array([
0, 16, 32, 128, 255,
]);
const limit = 50;
const expReturn = { data: '12345' };
const callParams = {
signatures,
serializedTransaction,
};
const expParams = {
body: JSON.stringify({
signatures,
compression: 0,
packed_context_free_data: '',
packed_trx: '00102080ff',
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.push_transaction(callParams);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls push_ro_transaction', async () => {
const expPath = '/v1/chain/push_ro_transaction';
const signatures = [
'George Washington',
'John Hancock',
'Abraham Lincoln',
];
const serializedTransaction = new Uint8Array([
0, 16, 32, 128, 255,
]);
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
transaction: {
signatures,
compression: 0,
packed_context_free_data: '',
packed_trx: '00102080ff'
},
return_failure_traces: false
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.push_ro_transaction({ signatures, serializedTransaction });
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls send_transaction', async () => {
const expPath = '/v1/chain/send_transaction';
const signatures = [
'George Washington',
'John Hancock',
'Abraham Lincoln',
];
const serializedTransaction = new Uint8Array([
0, 16, 32, 128, 255,
]);
const limit = 50;
const expReturn = { data: '12345' };
const callParams = {
signatures,
serializedTransaction,
};
const expParams = {
body: JSON.stringify({
signatures,
compression: 0,
packed_context_free_data: '',
packed_trx: '00102080ff',
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.send_transaction(callParams);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls db_size_get', async () => {
const expPath = '/v1/db_size/get';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.db_size_get();
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls history_get_actions with all params', async () => {
const expPath = '/v1/history/get_actions';
const accountName = 'myaccountaaa';
const pos = 5;
const offset = 10;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
account_name: accountName,
pos,
offset,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.history_get_actions(accountName, pos, offset);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls history_get_actions with default params', async () => {
const expPath = '/v1/history/get_actions';
const accountName = 'myaccountaaa';
const pos: number = null;
const offset: number = null;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
account_name: accountName,
pos,
offset,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.history_get_actions(accountName);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls history_get_transaction with all params', async () => {
const expPath = '/v1/history/get_transaction';
const id = 'myaccountaaa';
const blockNumHint = 20;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
id,
block_num_hint: blockNumHint,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.history_get_transaction(id, blockNumHint);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls history_get_transaction with default params', async () => {
const expPath = '/v1/history/get_transaction';
const id = 'myaccountaaa';
const blockNumHint: number = null;
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
id,
block_num_hint: blockNumHint,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.history_get_transaction(id);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls history_get_key_accounts', async () => {
const expPath = '/v1/history/get_key_accounts';
const publicKey = 'key12345';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
public_key: publicKey,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.history_get_key_accounts(publicKey);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
it('calls history_get_controlled_accounts', async () => {
const expPath = '/v1/history/get_controlled_accounts';
const controllingAccount = 'key12345';
const expReturn = { data: '12345' };
const expParams = {
body: JSON.stringify({
controlling_account: controllingAccount,
}),
method: 'POST',
};
fetchMock.once(JSON.stringify(expReturn));
const response = await jsonRpc.history_get_controlled_accounts(controllingAccount);
expect(response).toEqual(expReturn);
expect(fetch).toBeCalledWith(endpoint + expPath, expParams);
});
});
import {ec} from 'elliptic';
import {generateKeyPair, PrivateKey, PublicKey, sha256, Signature} from '../zswjs-key-conversions';
import {digestFromSerializedData, JsSignatureProvider} from '../zswjs-jssig';
import {KeyType} from '../zswjs-numeric';
import {SignatureProviderArgs} from '../zswjs-api-interfaces';
describe('JsSignatureProvider', () => {
const privateKeys = [
'5Juww5SS6aLWxopXBAWzwqrwadiZKz7XpKAiktXTKcfBGi1DWg8',
'5JnHjSFwe4r7xyqAUAaVs51G7HmzE86DWGa3VAA5VvQriGYnSUr',
'5K4XZH5XR2By7Q5KTcZnPAmUMU5yjUNBdoKzzXyrLfmiEZJqoKE',
];
const privateKeysK1 = [
'PVT_K1_26fMPzM27mXhoSF8y56ro7pN2te7rFT6W6wXiUi5joY79NHfZf',
'PVT_K1_y19korZcH8hyStRy8bn2G8tgx51zE8nTWGFz7LG3ZDYkaELTY',
'PVT_K1_2FEybdSLZcyrPh3RR7tJ82M8sG4XLW6uzGDmMw76nv54xk8FLu',
];
const privateKeysR1 = [
'PVT_R1_GrfEfbv5at9kbeHcGagQmvbFLdm6jqEpgE1wsGbrfbZNjpVgT',
'PVT_R1_wCpPsaY9o8NU9ZsuwaYVQUDkCfj1aWJZGVcmMM6XyYHJVqvqp',
];
const legacyPublicKeys = [
'EOS7tgwU6E7pAUQJgqEJt66Yi8cWvanTUW8ZfBjeXeJBQvhTU9ypi',
'EOS8VaY5CiTexYqgQZyPTJkc3qvWuZUi12QrZL9ssjqW2es6aQk2F',
'EOS7VGhqctkKprW1VUj19DZZiiZLX3YcJqUJCuEcahJmUCw3wJEMu',
];
const k1FormatPublicKeys = [
'PUB_K1_7tgwU6E7pAUQJgqEJt66Yi8cWvanTUW8ZfBjeXeJBQvhYTBFvY',
'PUB_K1_8VaY5CiTexYqgQZyPTJkc3qvWuZUi12QrZL9ssjqW2es7e7bRJ',
'PUB_K1_7VGhqctkKprW1VUj19DZZiiZLX3YcJqUJCuEcahJmUCw9RT8v2',
];
const r1FormatPublicKeys = [
'PUB_R1_4ztaVy8L9zbmzTdpfq5GcaFYwGwXTNmN3qW7qcgHMmfUZhpzQQ',
'PUB_R1_5xawnnr3mWayv2wkiqBGWqu4RQLNJffLSXHiL3BofdY7ortMy4',
];
const signatures = [
'SIG_K1_HKkqi3zray76i63ZQwAHWMjoLk3wTa1ajZWPcUnrhgmSWQYEHDJsxkny6VDTWEmVdfktxpGoTA81qe6QuCrDmazeQndmxh',
'SIG_K1_HCaY9Y9qdjnkRhE9hokAyp3pFtkMmjpxF6xTd514Vo8vLVSWKek5m5aHfCaka9TqZUbajkhhd4BfBLxSwCwZUEmy8cvt1x',
'SIG_K1_GrZqp9ZkuhBeNpeQ5b2L2UWUUrNU1gHbTyMzkyWRhiXNkxPP84Aq9eziU399eBf9xJw8MqHHjz7R2wMTMXhXjHLgpZYFeA',
];
const eccSignatures = [
'SIG_K1_KeEyJFpkp63Qq5E1zRD9aNZtTjpStvdkdnL31Z7wVmhYtrKGtpVdMBJnXyEUXNkNEyo4d4i4Q79qmRpCUsCRdFqhV6KAeF',
'SIG_K1_JvgMmFSDhipS1SeBLNBMdAxayAsWS3GuVGSHS7YQth5Z5ZpijxnZgaa23dYD1efQhpEgtEggdRfHMmp31RDXjmJdZYoKLm',
'SIG_K1_JwMqV2nbEntHSq9AuG3Zq1JBc5YqD2SftMHCTGK4A8DYGn1VPQ8QAduwCNksT5JhYgAmGMzPyJdZ2Ws4p8TCvQ16LeNhrw',
];
// These are simplified tests simply to verify a refactor didn't mess with existing code
describe('secp256k1 elliptic', () => {
it('generates a private and public key pair', () => {
const {privateKey, publicKey} = generateKeyPair(KeyType.k1, { secureEnv: true });
expect(privateKey).toBeInstanceOf(PrivateKey);
expect(privateKey.isValid()).toBeTruthy();
expect(publicKey).toBeInstanceOf(PublicKey);
expect(publicKey.isValid()).toBeTruthy();
});
it('throws error with no options.secureEnv variable', () => {
expect(() => generateKeyPair(KeyType.k1)).toThrowError();
});
it('Retrieves the public key from a private key', () => {
const privateKey = PrivateKey.fromString(privateKeys[0]);
const publicKey = privateKey.getPublicKey();
expect(publicKey.toString()).toEqual(k1FormatPublicKeys[0]);
});
it('builds public keys from private when constructed', async () => {
const provider = new JsSignatureProvider(privateKeys);
const actualPublicKeys = await provider.getAvailableKeys();
expect(actualPublicKeys).toEqual(k1FormatPublicKeys);
});
it('signs a transaction', async () => {
const provider = new JsSignatureProvider(privateKeys);
const chainId = '12345';
const requiredKeys = k1FormatPublicKeys;
const serializedTransaction = new Uint8Array([
0, 16, 32, 128, 255,
]);
const signOutput = await provider.sign(
{ chainId, requiredKeys, serializedTransaction } as SignatureProviderArgs
);
expect(signOutput).toEqual({
signatures: expect.any(Array),
serializedTransaction,
serializedContextFreeData: undefined
});
});
it('confirm elliptic conversion functions are actually reciprocal', async () => {
const provider = new JsSignatureProvider(privateKeys);
const chainId = '12345';
const requiredKeys = k1FormatPublicKeys;
const serializedTransaction = new Uint8Array([
0, 16, 32, 128, 255,
]);
const signOutput = await provider.sign(
{ chainId, requiredKeys, serializedTransaction } as SignatureProviderArgs
);
const sig: Signature = Signature.fromString(signOutput.signatures[0]);
const ellipticSig: ec.Signature = sig.toElliptic();
const zswSig = Signature.fromElliptic(ellipticSig, KeyType.k1);
expect(sig).toEqual(zswSig);
});
it('verify a transaction', async () => {
const provider = new JsSignatureProvider([privateKeys[0]]);
const chainId = '12345';
const requiredKeys = [k1FormatPublicKeys[0]];
const serializedTransaction = new Uint8Array([
0, 16, 32, 128, 255,
]);
const signOutput = await provider.sign(
{ chainId, requiredKeys, serializedTransaction } as SignatureProviderArgs
);
const signature = Signature.fromString(signOutput.signatures[0]);
expect(
signature.verify(
digestFromSerializedData(chainId, serializedTransaction),
PublicKey.fromString(k1FormatPublicKeys[0]),
false
)
).toEqual(true);
});
it('ensure public key functions are actual inverses of each other', async () => {
const zswchainPubKey = PublicKey.fromString(k1FormatPublicKeys[0]);
const ellipticPubKey = zswchainPubKey.toElliptic();
const finalZSWChainKeyAsK1String = PublicKey.fromElliptic(ellipticPubKey, KeyType.k1).toString();
expect(finalZSWChainKeyAsK1String).toEqual(k1FormatPublicKeys[0]);
});
it('verify that PUB_K1_ and Legacy pub formats are consistent', () => {
const zswchainLegacyPubKey = legacyPublicKeys[0];
const ellipticPubKey = PublicKey.fromString(zswchainLegacyPubKey).toElliptic();
expect(PublicKey.fromElliptic(ellipticPubKey, KeyType.k1).toString()).toEqual(k1FormatPublicKeys[0]);
});
it('verify that privateKey toLegacyString() and toString() are consistent', () => {
const privKeyFromK1 = PrivateKey.fromString(privateKeysK1[0]);
const privKeyFromLegacy = PrivateKey.fromString(privateKeys[0]);
expect(privKeyFromK1.toLegacyString()).toEqual(privateKeys[0]);
expect(privKeyFromLegacy.toString()).toEqual(privateKeysK1[0]);
});
it('verify that publicKey toLegacyString() and toString() are consistent', () => {
const pubKeyFromK1 = PublicKey.fromString(k1FormatPublicKeys[0]);
const pubKeyFromLegacy = PublicKey.fromString(legacyPublicKeys[0]);
expect(pubKeyFromK1.toLegacyString()).toEqual(legacyPublicKeys[0]);
expect(pubKeyFromLegacy.toString()).toEqual(k1FormatPublicKeys[0]);
});
it('ensure private key functions are actual inverses of each other', async () => {
const priv = privateKeys[0];
const privZSWChainKey = PrivateKey.fromString(priv);
const privEllipticKey = privZSWChainKey.toElliptic();
const finalZSWChainKeyAsString = PrivateKey.fromElliptic(privEllipticKey, KeyType.k1).toString();
expect(privZSWChainKey.toString()).toEqual(finalZSWChainKeyAsString);
});
it('verify that public key validate function correctly assesses public keys', () => {
const publicKey = PublicKey.fromString(k1FormatPublicKeys[0]);
expect(publicKey.isValid()).toEqual(true);
});
it('Ensure elliptic sign, recover, verify flow works', () => {
const KPrivStr = privateKeys[0];
const KPriv = PrivateKey.fromString(KPrivStr);
const dataAsString = 'some string';
const ellipticHashedString = sha256(dataAsString);
const sig = KPriv.sign(ellipticHashedString);
const KPub = sig.recover(ellipticHashedString);
expect(KPub.toString()).toEqual(k1FormatPublicKeys[0]);
const valid = sig.verify(ellipticHashedString, KPub);
expect(valid).toEqual(true);
});
it('Ensure elliptic sign, recover, verify flow works with shouldHash', () => {
const KPrivStr = privateKeys[0];
const KPriv = PrivateKey.fromString(KPrivStr);
const dataAsString = 'some string';
const sig = KPriv.sign(dataAsString, true);
const KPub = sig.recover(dataAsString, true);
expect(KPub.toString()).toEqual(k1FormatPublicKeys[0]);
const valid = sig.verify(dataAsString, KPub, true);
expect(valid).toEqual(true);
});
it('Ensure elliptic sign, recover, verify flow works with shouldHash and encoding', () => {
const KPrivStr = privateKeys[0];
const KPriv = PrivateKey.fromString(KPrivStr);
const dataAsString = 'some string';
const sig = KPriv.sign(dataAsString, true, 'utf8');
const KPub = sig.recover(dataAsString, true, 'utf8');
expect(KPub.toString()).toEqual(k1FormatPublicKeys[0]);
const valid = sig.verify(dataAsString, KPub, true, 'utf8');
expect(valid).toEqual(true);
});
});
describe('p256 elliptic', () => {
it('generates a private and public key pair', () => {
const {privateKey, publicKey} = generateKeyPair(KeyType.r1, { secureEnv: true });
expect(privateKey).toBeInstanceOf(PrivateKey);
expect(privateKey.isValid()).toBeTruthy();
expect(publicKey).toBeInstanceOf(PublicKey);
expect(publicKey.isValid()).toBeTruthy();
});
it('throws error with no options.secureEnv variable', () => {
expect(() => generateKeyPair(KeyType.r1)).toThrowError();
});
it('throws error when attempting a legacy private key from r1 format', () => {
const privateKey = PrivateKey.fromString(privateKeysR1[0]);
expect(() => privateKey.toLegacyString()).toThrowError('Key format not supported in legacy conversion');
});
it('throws error when attempting a legacy public key from r1 format', () => {
const publicKey = PublicKey.fromString(r1FormatPublicKeys[0]);
expect(() => publicKey.toLegacyString()).toThrowError('Key format not supported in legacy conversion');
});
it('Retrieves the public key from a private key', () => {
const privateKey = PrivateKey.fromString(privateKeysR1[0]);
const publicKey = privateKey.getPublicKey();
expect(publicKey.toString()).toEqual(r1FormatPublicKeys[0]);
});
it('builds public keys from private when constructed', async () => {
const provider = new JsSignatureProvider(privateKeysR1);
const actualPublicKeys = await provider.getAvailableKeys();
expect(actualPublicKeys).toEqual(r1FormatPublicKeys);
});
it('signs a transaction', async () => {
const provider = new JsSignatureProvider(privateKeysR1);
const chainId = '12345';
const requiredKeys = r1FormatPublicKeys;
const serializedTransaction = new Uint8Array([
0, 16, 32, 128, 255,
]);
const signOutput = await provider.sign(
{ chainId, requiredKeys, serializedTransaction } as SignatureProviderArgs
);
expect(signOutput).toEqual({
signatures: expect.any(Array),
serializedTransaction,
serializedContextFreeData: undefined
});
});
it('confirm elliptic conversion functions are actually reciprocal', async () => {
const provider = new JsSignatureProvider(privateKeysR1);
const chainId = '12345';
const requiredKeys = r1FormatPublicKeys;
const serializedTransaction = new Uint8Array([
0, 16, 32, 128, 255,
]);
const signOutput = await provider.sign(
{ chainId, requiredKeys, serializedTransaction } as SignatureProviderArgs
);
const sig: Signature = Signature.fromString(signOutput.signatures[0]);
const ellipticSig: ec.Signature = sig.toElliptic();
const zswSig = Signature.fromElliptic(ellipticSig, KeyType.r1);
expect(sig).toEqual(zswSig);
});
it('verify a transaction', async () => {
const provider = new JsSignatureProvider([privateKeysR1[0]]);
const chainId = '12345';
const requiredKeys = [r1FormatPublicKeys[0]];
const serializedTransaction = new Uint8Array([
0, 16, 32, 128, 255,
]);
const signOutput = await provider.sign(
{ chainId, requiredKeys, serializedTransaction } as SignatureProviderArgs
);
const signature = Signature.fromString(signOutput.signatures[0]);
expect(
signature.verify(
digestFromSerializedData(chainId, serializedTransaction),
PublicKey.fromString(r1FormatPublicKeys[0]),
false
)
).toEqual(true);
});
it('ensure public key functions using p256 format are actual inverses of each other', async () => {
const zswchainPubKey = PublicKey.fromString(r1FormatPublicKeys[0]);
const ellipticPubKey = zswchainPubKey.toElliptic();
const finalZSWChainKeyAsR1String = PublicKey.fromElliptic(ellipticPubKey, KeyType.r1).toString();
expect(finalZSWChainKeyAsR1String).toEqual(r1FormatPublicKeys[0]);
});
it('ensure private key functions using p256 format are actual inverses of each other', async () => {
const priv = privateKeysR1[0];
const privZSWChainKey = PrivateKey.fromString(priv);
const privEllipticKey = privZSWChainKey.toElliptic();
const finalZSWChainKeyAsString = PrivateKey.fromElliptic(privEllipticKey, KeyType.r1).toString();
expect(privZSWChainKey.toString()).toEqual(finalZSWChainKeyAsString);
});
it('verify that public key validate function correctly assesses public keys', () => {
const publicKey = PublicKey.fromString(r1FormatPublicKeys[0]);
expect(publicKey.isValid()).toEqual(true);
});
it('Ensure elliptic sign, recover, verify flow works', () => {
const KPrivStr = privateKeysR1[0];
const KPriv = PrivateKey.fromString(KPrivStr);
const dataAsString = 'some string';
const ellipticHashedString = sha256(dataAsString);
const sig = KPriv.sign(ellipticHashedString);
const KPub = sig.recover(ellipticHashedString);
expect(KPub.toString()).toEqual(r1FormatPublicKeys[0]);
const valid = sig.verify(ellipticHashedString, KPub);
expect(valid).toEqual(true);
});
it('Ensure elliptic sign, recover, verify flow works with shouldHash', () => {
const KPrivStr = privateKeysR1[0];
const KPriv = PrivateKey.fromString(KPrivStr);
const dataAsString = 'some string';
const sig = KPriv.sign(dataAsString, true);
const KPub = sig.recover(dataAsString, true);
expect(KPub.toString()).toEqual(r1FormatPublicKeys[0]);
const valid = sig.verify(dataAsString, KPub, true);
expect(valid).toEqual(true);
});
it('Ensure elliptic sign, recover, verify flow works with shouldHash and encoding', () => {
const KPrivStr = privateKeysR1[0];
const KPriv = PrivateKey.fromString(KPrivStr);
const dataAsString = 'some string';
const sig = KPriv.sign(dataAsString, true, 'utf8');
const KPub = sig.recover(dataAsString, true, 'utf8');
expect(KPub.toString()).toEqual(r1FormatPublicKeys[0]);
const valid = sig.verify(dataAsString, KPub, true, 'utf8');
expect(valid).toEqual(true);
});
});
});
const { TextEncoder, TextDecoder } = require('util');
import { ec } from 'elliptic';
import { createInitialTypes, Type, SerialBuffer } from '../zswjs-serialize';
describe('Serialize', () => {
let types: Map<string, Type>;
beforeAll(() => {
types = createInitialTypes();
});
it('should be able to createInitialTypes', () => {
expect(types).toBeTruthy();
});
describe('pushAsset', () => {
let serialBuffer: SerialBuffer;
const genericValidSymbolCharacter = 'A';
const invalidSymbolErrorMessage = 'Expected symbol to be A-Z and between one and seven characters';
beforeEach(() => {
serialBuffer = new SerialBuffer({
textEncoder: new TextEncoder(),
textDecoder: new TextDecoder()
});
});
const expectSuccessForICharactersSymbol = (i: number) => {
const symbol = genericValidSymbolCharacter.repeat(i);
const asset = `10.000 ${symbol}`;
serialBuffer.pushAsset(asset);
expect(serialBuffer.length).not.toBe(0);
};
const expectExceptionThrown = (asset: string) => {
let exceptionCaught = false;
try {
serialBuffer.pushAsset(asset);
} catch (e) {
expect(e.message).toBe(invalidSymbolErrorMessage);
exceptionCaught = true;
}
expect(exceptionCaught).toBeTruthy();
};
for (let i = 1; i <= 7; i++) {
it(`should be able to push asset with valid symbol of ${i} character(s)`, () => {
expectSuccessForICharactersSymbol(i);
});
}
it('should be able to push asset with valid ZSW symbol "10.000 ZSW"', () => {
const asset = '10.000 ZSW';
serialBuffer.pushAsset(asset);
expect(serialBuffer.length).not.toBe(0);
});
it('should not be able to push no symbol "10.000 "', () => {
const asset = '10.000 ';
expectExceptionThrown(asset);
});
it('should not be able to push symbol with 8 or more characters "10.000 AAAAAAAA"', () => {
const asset = '10.000 AAAAAAAA';
expectExceptionThrown(asset);
});
it('should not be able to push invalid lowercase symbol "10.000 zsw"', () => {
const asset = '10.000 zsw';
expectExceptionThrown(asset);
});
it('should not be able to push two symbols "10.000 ZSW blah"', () => {
const asset = '10.000 ZSW blah';
expectExceptionThrown(asset);
});
});
describe('name', () => {
let serialBuffer: SerialBuffer;
const invalidNameErrorMessage = 'Name should be less than 13 characters, or less than 14 if last character is between 1-5 or a-j, and only contain the following symbols .12345abcdefghijklmnopqrstuvwxyz';
beforeEach(() => {
serialBuffer = new SerialBuffer({
textEncoder: new TextEncoder(),
textDecoder: new TextDecoder()
});
});
it('should be able to push name with a valid account name', () => {
const name = '.12345abcdefg';
serialBuffer.pushName(name);
expect(serialBuffer.getName()).toEqual(name);
});
it('should remove the `.` character from the end of the account name', () => {
const name = 'abcd......';
const expectedName = 'abcd';
serialBuffer.pushName(name);
expect(serialBuffer.getName()).toEqual(expectedName);
});
it('should not be able to push name with an account name too long', () => {
const name = 'abcdabcdabcdab';
const shouldFail = () => serialBuffer.pushName(name);
expect(shouldFail).toThrowError(invalidNameErrorMessage);
});
it('should not be able to push name with an account name with invalid characters', () => {
const name = '6789$/,';
const shouldFail = () => serialBuffer.pushName(name);
expect(shouldFail).toThrowError(invalidNameErrorMessage);
});
});
describe('bool', () => {
let boolType: Type;
let mockedBuffer: SerialBuffer;
const shouldThrowErrorForValue = (value: any) => {
try {
boolType.serialize(mockedBuffer, value);
} catch (e) {
expect(e.message).toBe('Expected boolean or number equal to 1 or 0');
}
};
const shouldNotThrowErrorForValue = (value: any) => {
expect(() => {
boolType.serialize(mockedBuffer, value);
}).not.toThrow();
};
beforeAll(() => {
boolType = types.get('bool');
mockedBuffer = Object.create(SerialBuffer);
mockedBuffer.push = jest.fn().mockImplementation((value) => {
return;
});
});
it('should be able to create bool type', () => {
expect(boolType).toBeTruthy();
});
it('should throw error when calling serialize when type is not boolean or number', () => {
const dataValue = 'string';
shouldThrowErrorForValue(dataValue);
});
it('should throw error when calling serialize when number that is not 1 or 0', () => {
const dataValue = 10;
shouldThrowErrorForValue(dataValue);
});
it('should not throw error when calling serialize with false', () => {
const dataValue = false;
shouldNotThrowErrorForValue(dataValue);
});
it('should not throw error when calling serialize with true', () => {
const dataValue = true;
shouldNotThrowErrorForValue(dataValue);
});
it('should not throw error when calling serialize with 0', () => {
const dataValue = 0;
shouldNotThrowErrorForValue(dataValue);
});
it('should not throw error when calling serialize with 1', () => {
const dataValue = 1;
shouldNotThrowErrorForValue(dataValue);
});
});
});
......@@ -87,9 +87,9 @@ export class Api {
this.authorityProvider = args.authorityProvider || args.rpc;
this.abiProvider = args.abiProvider || args.rpc;
this.signatureProvider = args.signatureProvider;
this.chainId = args.chainId;
this.textEncoder = args.textEncoder;
this.textDecoder = args.textDecoder;
this.chainId = args.chainId!;
this.textEncoder = args.textEncoder!;
this.textDecoder = args.textDecoder!;
this.abiTypes = ser.getTypesFromAbi(ser.createAbiTypes());
this.transactionTypes = ser.getTypesFromAbi(ser.createTransactionTypes());
......@@ -106,7 +106,7 @@ export class Api {
throw new Error('Unsupported abi version');
}
buffer.restartRead();
return this.abiTypes.get('abi_def').deserialize(buffer);
return this.abiTypes.get('abi_def')!.deserialize(buffer);
}
/** Encodes a json abi as Uint8Array. */
......@@ -115,7 +115,7 @@ export class Api {
textEncoder: this.textEncoder,
textDecoder: this.textDecoder,
});
this.abiTypes.get('abi_def').serialize(buffer, jsonAbi);
this.abiTypes.get('abi_def')!.serialize(buffer, jsonAbi);
if (!ser.supportedAbiVersion(buffer.getString())) {
throw new Error('Unsupported abi version');
}
......@@ -125,14 +125,14 @@ export class Api {
/** Get abi in both binary and structured forms. Fetch when needed. */
public async getCachedAbi(accountName: string, reload = false): Promise<CachedAbi> {
if (!reload && this.cachedAbis.get(accountName)) {
return this.cachedAbis.get(accountName);
return this.cachedAbis.get(accountName)!;
}
let cachedAbi: CachedAbi;
try {
const rawAbi = (await this.abiProvider.getRawAbi(accountName)).abi;
const abi = this.rawAbiToJson(rawAbi);
cachedAbi = { rawAbi, abi };
} catch (e) {
} catch (e: any) {
e.message = `fetching abi for ${accountName}: ${e.message}`;
throw e;
}
......@@ -163,7 +163,7 @@ export class Api {
/** Get data needed to serialize actions in a contract */
public async getContract(accountName: string, reload = false): Promise<ser.Contract> {
if (!reload && this.contracts.get(accountName)) {
return this.contracts.get(accountName);
return this.contracts.get(accountName)!;
}
const abi = await this.getAbi(accountName, reload);
const types = ser.getTypesFromAbi(ser.createInitialTypes(), abi);
......@@ -178,12 +178,12 @@ export class Api {
/** Convert `value` to binary form. `type` must be a built-in abi type or in `transaction.abi.json`. */
public serialize(buffer: ser.SerialBuffer, type: string, value: any): void {
this.transactionTypes.get(type).serialize(buffer, value);
this.transactionTypes.get(type)!.serialize(buffer, value);
}
/** Convert data in `buffer` to structured form. `type` must be a built-in abi type or in `transaction.abi.json`. */
public deserialize(buffer: ser.SerialBuffer, type: string): any {
return this.transactionTypes.get(type).deserialize(buffer);
return this.transactionTypes.get(type)!.deserialize(buffer);
}
/** Convert a transaction to binary */
......@@ -196,7 +196,7 @@ export class Api {
context_free_actions: [],
actions: [],
transaction_extensions: [],
...transaction,
...(transaction as any),
});
return buffer.asUint8Array();
}
......@@ -204,7 +204,7 @@ export class Api {
/** Serialize context-free data */
public serializeContextFreeData(contextFreeData: Uint8Array[]): Uint8Array {
if (!contextFreeData || !contextFreeData.length) {
return null;
return null!;
}
const buffer = new ser.SerialBuffer({ textEncoder: this.textEncoder, textDecoder: this.textDecoder });
buffer.pushVaruint32(contextFreeData.length);
......@@ -231,7 +231,7 @@ export class Api {
if (transaction.resource_payer) {
const extensionBuffer = new ser.SerialBuffer({ textEncoder: this.textEncoder, textDecoder: this.textDecoder });
const types = ser.getTypesFromAbi(ser.createTransactionExtensionTypes());
types.get('resource_payer').serialize(extensionBuffer, transaction.resource_payer);
types.get('resource_payer')!.serialize(extensionBuffer, transaction.resource_payer);
transaction_extensions = [...transaction_extensions, [1, ser.arrayToHex(extensionBuffer.asUint8Array())]];
}
return transaction_extensions;
......@@ -248,7 +248,7 @@ export class Api {
const types = ser.getTypesFromAbi(ser.createTransactionExtensionTypes());
const extensionBuffer = new ser.SerialBuffer({ textEncoder: this.textEncoder, textDecoder: this.textDecoder });
extensionBuffer.pushArray(ser.hexToUint8Array(extensionData[1]));
const deserializedObj = types.get(transactionExtension.type).deserialize(extensionBuffer);
const deserializedObj = types.get(transactionExtension.type)!.deserialize(extensionBuffer);
if (extensionData[0] === 1) {
deserializedObj.max_net_bytes = Number(deserializedObj.max_net_bytes);
deserializedObj.max_cpu_us = Number(deserializedObj.max_cpu_us);
......@@ -293,7 +293,7 @@ export class Api {
transaction = ser.hexToUint8Array(transaction);
}
const deserializedTransaction = this.deserializeTransaction(transaction);
const deserializedCFActions = await this.deserializeActions(deserializedTransaction.context_free_actions);
const deserializedCFActions = await this.deserializeActions(deserializedTransaction.context_free_actions!);
const deserializedActions = await this.deserializeActions(deserializedTransaction.actions);
return {
...deserializedTransaction, context_free_actions: deserializedCFActions, actions: deserializedActions
......@@ -357,7 +357,7 @@ export class Api {
}
if ((typeof blocksBehind === 'number' || useLastIrreversible) && expireSeconds) {
transaction = await this.generateTapos(info, transaction, blocksBehind, useLastIrreversible, expireSeconds);
transaction = await this.generateTapos(info!, transaction, blocksBehind, useLastIrreversible, expireSeconds);
}
if (!this.hasRequiredTaposFields(transaction)) {
......@@ -373,7 +373,7 @@ export class Api {
};
transaction = this.deleteTransactionExtensionObjects(transaction);
const serializedTransaction = this.serializeTransaction(transaction);
const serializedContextFreeData = this.serializeContextFreeData(transaction.context_free_data);
const serializedContextFreeData = this.serializeContextFreeData(transaction.context_free_data!);
let pushTransactionArgs: PushTransactionArgs = {
serializedTransaction, serializedContextFreeData, signatures: []
};
......@@ -442,7 +442,7 @@ export class Api {
chainId: this.chainId,
requiredKeys,
serializedTransaction,
serializedContextFreeData: null,
serializedContextFreeData: null!,
abis,
});
......@@ -527,7 +527,7 @@ export class Api {
return { ...ser.transactionHeader(block, expireSeconds), ...transaction };
}
const taposBlockNumber: number = info.head_block_num - blocksBehind;
const taposBlockNumber: number = info.head_block_num - blocksBehind!;
const refBlock: GetBlockHeaderStateResult | GetBlockResult | GetBlockInfoResult =
taposBlockNumber <= info.last_irreversible_block_num
......@@ -646,7 +646,7 @@ export class TransactionBuilder {
export class ActionBuilder {
private api: Api;
private readonly accountName: string;
public serializedData: ser.SerializedAction;
public serializedData: ser.SerializedAction = {} as any;
constructor(api: Api, accountName: string) {
this.api = api;
......
......@@ -89,7 +89,7 @@ export class JsonRpc implements AuthorityProvider, AbiProvider {
} else if (json.result && json.result.except) {
throw new RpcError(json);
}
} catch (e) {
} catch (e: any) {
e.isFetchError = true;
throw e;
}
......@@ -135,8 +135,8 @@ export class JsonRpc implements AuthorityProvider, AbiProvider {
limit = 10,
search_by_block_num = false,
reverse = false,
lower_bound = null,
upper_bound = null,
lower_bound = null as any,
upper_bound = null as any,
}: GetActivatedProtocolFeaturesParams): Promise<GetActivatedProtocolFeaturesResult> {
return await this.fetch('/v1/chain/get_activated_protocol_features', { lower_bound, upper_bound, limit, search_by_block_num, reverse });
}
......@@ -170,7 +170,7 @@ export class JsonRpc implements AuthorityProvider, AbiProvider {
}
/** Raw call to `/v1/chain/get_currency_balance` */
public async get_currency_balance(code: string, account: string, symbol: string = null): Promise<string[]> {
public async get_currency_balance(code: string, account: string, symbol: string = null as any): Promise<string[]> {
return await this.fetch('/v1/chain/get_currency_balance', { code, account, symbol });
}
......@@ -361,12 +361,12 @@ export class JsonRpc implements AuthorityProvider, AbiProvider {
}
/** Raw call to `/v1/history/get_actions` */
public async history_get_actions(accountName: string, pos: number = null, offset: number = null): Promise<GetActionsResult> {
public async history_get_actions(accountName: string, pos: number = null as any, offset: number = null as any): Promise<GetActionsResult> {
return await this.fetch('/v1/history/get_actions', { account_name: accountName, pos, offset });
}
/** Raw call to `/v1/history/get_transaction` */
public async history_get_transaction(id: string, blockNumHint: number = null): Promise<GetTransactionResult> {
public async history_get_transaction(id: string, blockNumHint: number = null as any): Promise<GetTransactionResult> {
return await this.fetch('/v1/history/get_transaction', { id, block_num_hint: blockNumHint });
}
......
......@@ -69,7 +69,7 @@ class JsSignatureProvider implements SignatureProvider {
for (const key of requiredKeys) {
const publicKey = PublicKey.fromString(key);
const ellipticPrivateKey = this.keys.get(convertLegacyPublicKey(key));
const privateKey = PrivateKey.fromElliptic(ellipticPrivateKey, publicKey.getType());
const privateKey = PrivateKey.fromElliptic(ellipticPrivateKey!, publicKey.getType());
const signature = privateKey.sign(digest, false);
signatures.push(signature.toString());
}
......
......@@ -682,11 +682,11 @@ export const hexToUint8Array = (hex: string): Uint8Array => {
return result;
};
function serializeUnknown(buffer: SerialBuffer, data: any): SerialBuffer {
function serializeUnknown(this: any, buffer: SerialBuffer, data: any): SerialBuffer {
throw new Error('Don\'t know how to serialize ' + this.name);
}
function deserializeUnknown(buffer: SerialBuffer): SerialBuffer {
function deserializeUnknown(this: any, buffer: SerialBuffer): SerialBuffer {
throw new Error('Don\'t know how to deserialize ' + this.name);
}
......@@ -796,11 +796,11 @@ function deserializeOptional(this: Type, buffer: SerialBuffer, state?: Serialize
function serializeExtension(
this: Type, buffer: SerialBuffer, data: any, state?: SerializerState, allowExtensions?: boolean
): void {
this.extensionOf.serialize(buffer, data, state, allowExtensions);
this.extensionOf!.serialize(buffer, data, state, allowExtensions);
}
function deserializeExtension(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean): any {
return this.extensionOf.deserialize(buffer, state, allowExtensions);
return this.extensionOf!.deserialize(buffer, state, allowExtensions);
}
function serializeObject(
......@@ -874,7 +874,7 @@ const createType = (attrs: CreateTypeArgs): Type => {
serialize: serializeUnknown,
deserialize: deserializeUnknown,
...attrs,
};
} as Type;
};
const checkRange = (orig: number, converted: number): number => {
......@@ -1077,7 +1077,7 @@ export const createInitialTypes = (): Map<string, Type> => {
fields: [
{ name: 'quantity', typeName: 'asset', type: result.get('asset') },
{ name: 'contract', typeName: 'name', type: result.get('name') },
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1093,7 +1093,7 @@ export const createAbiTypes = (): Map<string, Type> => {
fields: [
{ name: 'tag', typeName: 'uint16', type: null },
{ name: 'value', typeName: 'bytes', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1103,7 +1103,7 @@ export const createAbiTypes = (): Map<string, Type> => {
fields: [
{ name: 'new_type_name', typeName: 'string', type: null },
{ name: 'type', typeName: 'string', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1113,7 +1113,7 @@ export const createAbiTypes = (): Map<string, Type> => {
fields: [
{ name: 'name', typeName: 'string', type: null },
{ name: 'type', typeName: 'string', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1124,7 +1124,7 @@ export const createAbiTypes = (): Map<string, Type> => {
{ name: 'name', typeName: 'string', type: null },
{ name: 'base', typeName: 'string', type: null },
{ name: 'fields', typeName: 'field_def[]', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1135,7 +1135,7 @@ export const createAbiTypes = (): Map<string, Type> => {
{ name: 'name', typeName: 'name', type: null },
{ name: 'type', typeName: 'string', type: null },
{ name: 'ricardian_contract', typeName: 'string', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1148,7 +1148,7 @@ export const createAbiTypes = (): Map<string, Type> => {
{ name: 'key_names', typeName: 'string[]', type: null },
{ name: 'key_types', typeName: 'string[]', type: null },
{ name: 'type', typeName: 'string', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1158,7 +1158,7 @@ export const createAbiTypes = (): Map<string, Type> => {
fields: [
{ name: 'id', typeName: 'string', type: null },
{ name: 'body', typeName: 'string', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1168,7 +1168,7 @@ export const createAbiTypes = (): Map<string, Type> => {
fields: [
{ name: 'error_code', typeName: 'uint64', type: null },
{ name: 'error_msg', typeName: 'string', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1178,7 +1178,7 @@ export const createAbiTypes = (): Map<string, Type> => {
fields: [
{ name: 'name', typeName: 'string', type: null },
{ name: 'types', typeName: 'string[]', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1188,7 +1188,7 @@ export const createAbiTypes = (): Map<string, Type> => {
fields: [
{ name: 'name', typeName: 'name', type: null },
{ name: 'result_type', typeName: 'string', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1198,7 +1198,7 @@ export const createAbiTypes = (): Map<string, Type> => {
fields: [
{ name: 'name', typeName: 'name', type: null },
{ name: 'type', typeName: 'string', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1207,7 +1207,7 @@ export const createAbiTypes = (): Map<string, Type> => {
baseName: '',
fields: [
{ name: 'type', typeName: 'string', type: null },
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1217,7 +1217,7 @@ export const createAbiTypes = (): Map<string, Type> => {
fields: [
{ name: 'name', typeName: 'name', type: null },
{ name: 'secondary_index_def', typeName: 'secondary_index_def', type: null }
],
] as any[],
serialize: serializeObject,
deserialize: deserializeObject,
}));
......@@ -1228,7 +1228,7 @@ export const createAbiTypes = (): Map<string, Type> => {
{ name: 'type', typeName: 'string', type: null },
{ name: 'primary_index', typeName: 'primary_key_index_def', type: null },
{ name: 'secondary_indices', typeName: 'secondary_indices', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1238,7 +1238,7 @@ export const createAbiTypes = (): Map<string, Type> => {
fields: [
{ name: 'name', typeName: 'name', type: null },
{ name: 'kv_table_entry_def', typeName: 'kv_table_entry_def', type: null }
],
] as any[],
serialize: serializeObject,
deserialize: deserializeObject
}));
......@@ -1257,7 +1257,7 @@ export const createAbiTypes = (): Map<string, Type> => {
{ name: 'variants', typeName: 'variant_def[]$', type: null },
{ name: 'action_results', typeName: 'action_result[]$', type: null },
{ name: 'kv_tables', typeName: 'kv_table$', type: null },
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1274,7 +1274,7 @@ export const createTransactionExtensionTypes = (): Map<string, Type> => {
{ name: 'max_net_bytes', typeName: 'uint64', type: null },
{ name: 'max_cpu_us', typeName: 'uint64', type: null },
{ name: 'max_memory_bytes', typeName: 'uint64', type: null },
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1289,7 +1289,7 @@ export const createTransactionTypes = (): Map<string, Type> => {
fields: [
{ name: 'actor', typeName: 'name', type: null },
{ name: 'permission', typeName: 'name', type: null },
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1301,7 +1301,7 @@ export const createTransactionTypes = (): Map<string, Type> => {
{ name: 'name', typeName: 'name', type: null },
{ name: 'authorization', typeName: 'permission_level[]', type: null },
{ name: 'data', typeName: 'bytes', type: null },
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1311,7 +1311,7 @@ export const createTransactionTypes = (): Map<string, Type> => {
fields: [
{ name: 'type', typeName: 'uint16', type: null },
{ name: 'data', typeName: 'bytes', type: null },
],
] as any[],
serialize: serializePair,
deserialize: deserializePair,
}));
......@@ -1325,7 +1325,7 @@ export const createTransactionTypes = (): Map<string, Type> => {
{ name: 'max_net_usage_words', typeName: 'varuint32', type: null },
{ name: 'max_cpu_usage_ms', typeName: 'uint8', type: null },
{ name: 'delay_sec', typeName: 'varuint32', type: null },
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1336,7 +1336,7 @@ export const createTransactionTypes = (): Map<string, Type> => {
{ name: 'context_free_actions', typeName: 'action[]', type: null },
{ name: 'actions', typeName: 'action[]', type: null },
{ name: 'transaction_extensions', typeName: 'extension', type: null }
],
] as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1398,7 +1398,7 @@ export const getTypesFromAbi = (initialTypes: Map<string, Type>, abi?: Abi): Map
types.set(name, createType({
name,
baseName: base,
fields: fields.map(({ name: n, type }) => ({ name: n, typeName: type, type: null })),
fields: fields.map(({ name: n, type }) => ({ name: n, typeName: type, type: null })) as any[],
serialize: serializeStruct,
deserialize: deserializeStruct,
}));
......@@ -1408,7 +1408,7 @@ export const getTypesFromAbi = (initialTypes: Map<string, Type>, abi?: Abi): Map
for (const { name, types: t } of abi.variants) {
types.set(name, createType({
name,
fields: t.map((s) => ({ name: s, typeName: s, type: null })),
fields: t.map((s) => ({ name: s, typeName: s, type: null })) as any[],
serialize: serializeVariant,
deserialize: deserializeVariant,
}));
......@@ -1435,7 +1435,7 @@ export const transactionHeader = (refBlock: BlockTaposInfo, expireSeconds: numbe
const prefix = parseInt(reverseHex(refBlock.id.substr(16, 8)), 16);
return {
expiration: timePointSecToDate(dateToTimePointSec(timestamp) + expireSeconds),
expiration: timePointSecToDate(dateToTimePointSec(timestamp!) + expireSeconds),
ref_block_num: refBlock.block_num & 0xffff,
ref_block_prefix: prefix,
};
......@@ -1501,19 +1501,19 @@ export const serializeAnyvar = (buffer: SerialBuffer, anyvar: Anyvar): void => {
let def: AnyvarDef;
let value: any;
if (anyvar === null) {
[def, value] = [anyvarDefs.null_t, anyvar];
[def, value] = [anyvarDefs.null_t as any, anyvar];
} else if (typeof anyvar === 'string') {
[def, value] = [anyvarDefs.string, anyvar];
[def, value] = [anyvarDefs.string as any, anyvar];
} else if (typeof anyvar === 'number') {
[def, value] = [anyvarDefs.int32, anyvar];
[def, value] = [anyvarDefs.int32 as any, anyvar];
} else if (anyvar instanceof Uint8Array) {
[def, value] = [anyvarDefs.bytes, anyvar];
[def, value] = [anyvarDefs.bytes as any, anyvar];
} else if (Array.isArray(anyvar)) {
[def, value] = [anyvarDefs.any_array, anyvar];
[def, value] = [anyvarDefs.any_array as any, anyvar];
} else if (Object.keys(anyvar).length === 2 && anyvar.hasOwnProperty('type') && anyvar.hasOwnProperty('value')) {
[def, value] = [(anyvarDefs as any)[(anyvar as any).type] as AnyvarDef, (anyvar as any).value];
} else {
[def, value] = [anyvarDefs.any_object, anyvar];
[def, value] = [anyvarDefs.any_object as any, anyvar];
}
buffer.pushVaruint32(def.index);
def.type.serialize(buffer, value);
......@@ -1525,11 +1525,11 @@ export const deserializeAnyvar = (buffer: SerialBuffer, state?: SerializerState)
throw new Error('Tried to deserialize unknown anyvar type');
}
const def = anyvarDefsByIndex[defIndex];
const value = def.type.deserialize(buffer, state);
const value = def.type!.deserialize(buffer, state);
if (state && (state.options as any).useShortForm || def.useShortForm) {
return value;
} else {
return { type: def.type.name, value };
return { type: def.type!.name, value };
}
};
......@@ -1656,18 +1656,18 @@ export const serializeQuery = (buffer: SerialBuffer, query: Query): void => {
} else if (Array.isArray(query) && query.length === 3) {
[method, arg, filter] = query;
} else {
[method, arg, filter] = [query.method, query.arg, query.filter];
[method, arg, filter] = [query.method, query.arg!, query.filter!];
}
buffer.pushString(method);
if (arg === undefined) {
if (arg! === undefined) {
buffer.push(0);
} else {
buffer.push(1);
serializeAnyvar(buffer, arg);
}
if (filter === undefined) {
if (filter! === undefined) {
buffer.push(0);
} else {
buffer.pushVaruint32(filter.length);
......
......@@ -35,7 +35,7 @@ export class WebAuthnSignatureProvider implements SignatureProvider {
const signatures = [] as string[];
for (const key of requiredKeys) {
const id = ser.hexToUint8Array(this.keys.get(key));
const id = ser.hexToUint8Array(this.keys.get(key)!);
const assertion = await (navigator as any).credentials.get({
publicKey: {
timeout: 60000,
......
{
"compilerOptions": {
"target": "es5",
"module": "CommonJS",
"outDir": "dist",
"alwaysStrict": true,
"sourceMap": true,
"noImplicitAny": true,
"moduleResolution": "node",
"declaration": true,
"downlevelIteration": true,
"skipLibCheck": true,
"lib": [
"es2017",
"dom"
]
},
"include": [
"src/**/*.ts",
"src/**/*.js"
]
"compileOnSave": true,
"compilerOptions": {
"module": "commonjs",
"target": "ES2018",
"moduleResolution": "node",
"sourceMap": false,
"outDir": "out/zswjs",
"rootDir": ".",
"baseUrl": ".",
"declaration": true,
"alwaysStrict": true,
"allowJs": true,
"checkJs": false,
"strict": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"emitDecoratorMetadata": false,
"experimentalDecorators": true,
"removeComments": true,
"resolveJsonModule": true,
},
"include": [
"src/*",
"src/*/**",
],
"exclude": [
"out",
"var",
".git",
"node_modules",
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment