chore: replace jest with mocha
This commit is contained in:
parent
612958ece0
commit
c675c67c26
|
@ -7,4 +7,4 @@
|
|||
/module.flow.js
|
||||
/.eslintrc.js
|
||||
/test/.eslintrc.js
|
||||
/jest-environment.js
|
||||
/test/dist
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
// Configure Node.js tests
|
||||
module.exports = {
|
||||
require: ['@babel/register', 'esm'],
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
test/dist
|
|
@ -15,3 +15,4 @@ npm run codecov
|
|||
make -C examples/bpf-c-noop/
|
||||
cargo build-bpf --manifest-path examples/bpf-rust-noop/Cargo.toml
|
||||
npm run test:live-with-test-validator
|
||||
npm run test:browser-with-test-validator
|
||||
|
|
|
@ -24,7 +24,7 @@ eslint and flow-type are used.
|
|||
Helpful link: https://www.saltycrane.com/flow-type-cheat-sheet/latest/
|
||||
|
||||
### Testing Framework
|
||||
https://jestjs.io/
|
||||
https://mochajs.org/
|
||||
|
||||
### API Documentation
|
||||
ESDoc is used to document the public API. See
|
||||
|
|
|
@ -5,11 +5,5 @@
|
|||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": ["@babel/plugin-transform-runtime"],
|
||||
"presets": ["@babel/preset-flow"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,27 +0,0 @@
|
|||
const NodeEnvironment = require('jest-environment-node');
|
||||
|
||||
class CustomEnvironment extends NodeEnvironment {
|
||||
constructor(config, context) {
|
||||
Object.assign(config.globals, {
|
||||
Uint8Array,
|
||||
ArrayBuffer,
|
||||
});
|
||||
super(config, context);
|
||||
this.testPath = context.testPath;
|
||||
this.docblockPragmas = context.docblockPragmas;
|
||||
}
|
||||
|
||||
async setup() {
|
||||
await super.setup();
|
||||
}
|
||||
|
||||
async teardown() {
|
||||
await super.teardown();
|
||||
}
|
||||
|
||||
runScript(script) {
|
||||
return super.runScript(script);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CustomEnvironment;
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"collectCoverage": true,
|
||||
"collectCoverageFrom": ["src/**"],
|
||||
"coverageReporters": ["json", "lcov", "text-summary", "html"]
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Mocha Tests</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./node_modules/mocha/mocha.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
<script src="./node_modules/mocha/mocha.js"></script>
|
||||
<script class="mocha-init">
|
||||
mocha.setup('bdd');
|
||||
mocha.checkLeaks();
|
||||
</script>
|
||||
<script type="module" src="./test/dist/bundle.js"></script>
|
||||
<script type="module" class="mocha-exec">
|
||||
mocha.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -46,6 +46,7 @@
|
|||
"bpf-sdk:install": "npm run clean:fixtures; bin/bpf-sdk-install.sh .",
|
||||
"bpf-sdk:remove-symlinks": "find bpf-sdk -type l -print -exec cp {} {}.tmp \\; -exec mv {}.tmp {} \\;",
|
||||
"build": "cross-env NODE_ENV=production rollup -c",
|
||||
"build:browser-test": "rollup -c test/rollup.config.js",
|
||||
"build:fixtures": "set -ex; ./test/fixtures/noop-c/build.sh; ./test/fixtures/noop-rust/build.sh",
|
||||
"clean:fixtures": "make -C examples/bpf-c-noop clean ",
|
||||
"clean": "rimraf ./coverage ./lib",
|
||||
|
@ -70,14 +71,13 @@
|
|||
"pretty": "prettier --check '{,{examples,src,test}/**/}*.{j,t}s'",
|
||||
"pretty:fix": "prettier --write '{,{examples,src,test}/**/}*.{j,t}s'",
|
||||
"re": "semantic-release --repository-url git@github.com:solana-labs/solana-web3.js.git",
|
||||
"test": "npm run build:fixtures && cross-env NODE_ENV=test jest --useStderr",
|
||||
"test:cover": "npm run build:fixtures && cross-env NODE_ENV=test jest --coverage --useStderr",
|
||||
"test:live": "npm run build:fixtures && cross-env NODE_ENV=test TEST_LIVE=1 jest --useStderr",
|
||||
"test": "npm run build:fixtures && mocha './test/**/*.test.js'",
|
||||
"test:browser": "TEST_LIVE=1 npm run build:fixtures && npm run build:browser-test && mocha-headless-chrome -f http://localhost:8080/mocha.html --timeout 180000",
|
||||
"test:browser-with-server": "start-server-and-test 'http-server -p 8080' 8080 test:browser",
|
||||
"test:browser-with-test-validator": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health test:browser-with-server",
|
||||
"test:live": "TEST_LIVE=1 npm run test",
|
||||
"test:live-with-test-validator": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health test:live",
|
||||
"test:watch": "npm run build:fixtures && cross-env NODE_ENV=test jest --watch --useStderr"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "./jest-environment"
|
||||
"test:watch": "npm run build:fixtures && npm run test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
|
@ -95,15 +95,19 @@
|
|||
"tweetnacl": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.13",
|
||||
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
||||
"@babel/plugin-transform-runtime": "^7.12.10",
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@babel/preset-flow": "^7.12.1",
|
||||
"@babel/register": "^7.12.13",
|
||||
"@commitlint/config-conventional": "^11.0.0",
|
||||
"@commitlint/travis-cli": "^11.0.0",
|
||||
"@rollup/plugin-alias": "^3.1.2",
|
||||
"@rollup/plugin-babel": "^5.2.3",
|
||||
"@rollup/plugin-commonjs": "^17.1.0",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-multi-entry": "^4.0.0",
|
||||
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||
"@rollup/plugin-replace": "^2.3.4",
|
||||
"@solana/spl-token": "^0.0.13",
|
||||
|
@ -111,6 +115,8 @@
|
|||
"@typescript-eslint/parser": "^4.14.2",
|
||||
"acorn": "^8.0.1",
|
||||
"babel-eslint": "^11.0.0-beta.2",
|
||||
"chai": "^4.3.0",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"codecov": "^3.0.4",
|
||||
"cross-env": "7.0.3",
|
||||
"elfy": "^1.0.0",
|
||||
|
@ -121,22 +127,21 @@
|
|||
"esdoc-importpath-plugin": "^1.0.2",
|
||||
"esdoc-inject-style-plugin": "^1.0.0",
|
||||
"esdoc-standard-plugin": "^1.0.0",
|
||||
<<<<<<< HEAD
|
||||
"eslint": "7.19.0",
|
||||
=======
|
||||
"eslint": "^7.19.0",
|
||||
>>>>>>> 37215dbc2... feat: add support for browser es modules
|
||||
"eslint-config-prettier": "^7.0.0",
|
||||
"eslint-plugin-flowtype": "^5.2.0",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-jest": "22.19.0",
|
||||
"eslint-plugin-prettier": "^3.0.0",
|
||||
"esm": "^3.2.25",
|
||||
"flow-bin": "0.130.0",
|
||||
"flow-remove-types": "^2.143.1",
|
||||
"flow-typed": "3.2.1",
|
||||
"fs-file-tree": "1.1.1",
|
||||
"jest": "26.6.3",
|
||||
"http-server": "^0.12.3",
|
||||
"marked": "^1.1.0",
|
||||
"mocha": "^8.2.1",
|
||||
"mocha-headless-chrome": "^3.1.0",
|
||||
"mockttp": "^1.1.0",
|
||||
"mz": "^2.7.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.0.0",
|
||||
|
@ -146,8 +151,11 @@
|
|||
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"semantic-release": "^17.0.2",
|
||||
"start-server-and-test": "^1.11.6",
|
||||
"sinon": "^9.2.4",
|
||||
"start-server-and-test": "^1.12.0",
|
||||
"typescript": "^4.1.3",
|
||||
"watch": "^1.0.2"
|
||||
"watch": "^1.0.2",
|
||||
"webpack": "^5.21.0",
|
||||
"webpack-cli": "^4.5.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,11 @@ function generateConfig(configType, format) {
|
|||
plugins: [
|
||||
flow(),
|
||||
commonjs(),
|
||||
nodeResolve({browser, preferBuiltins: !browser, dedupe: ['bn.js']}),
|
||||
nodeResolve({
|
||||
browser,
|
||||
preferBuiltins: !browser,
|
||||
dedupe: ['bn.js', 'buffer'],
|
||||
}),
|
||||
babel({
|
||||
exclude: '**/node_modules/**',
|
||||
babelHelpers: bundle ? 'bundled' : 'runtime',
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
// eslint-disable-line import/no-commonjs
|
||||
extends: ['plugin:jest/recommended', '../.eslintrc.js'],
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
dist
|
|
@ -1,86 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
type RpcRequest = {
|
||||
method: string,
|
||||
params?: Array<any>,
|
||||
};
|
||||
|
||||
type RpcResponseError = {
|
||||
message: string,
|
||||
};
|
||||
type RpcResponseResult = any;
|
||||
type RpcResponse = {
|
||||
error: ?RpcResponseError,
|
||||
result: ?RpcResponseResult,
|
||||
};
|
||||
|
||||
export const mockRpc: Array<[string, RpcRequest, RpcResponse]> = [];
|
||||
|
||||
// Define TEST_LIVE in the environment to test against the real full node
|
||||
// identified by `url` instead of using the mock
|
||||
export const mockRpcEnabled = !process.env.TEST_LIVE;
|
||||
|
||||
let mockNotice = true;
|
||||
|
||||
// Suppress lint: 'JestMockFn' is not defined
|
||||
// eslint-disable-next-line no-undef
|
||||
const mock: JestMockFn<any, any> = jest.fn((fetchUrl, fetchOptions) => {
|
||||
if (!mockRpcEnabled) {
|
||||
if (mockNotice) {
|
||||
console.log(
|
||||
`Note: node-fetch mock is disabled, testing live against ${fetchUrl}`,
|
||||
);
|
||||
mockNotice = false;
|
||||
}
|
||||
return fetch(fetchUrl, fetchOptions);
|
||||
}
|
||||
|
||||
expect(mockRpc.length).toBeGreaterThanOrEqual(1);
|
||||
const [mockUrl, mockRequest, mockResponse] = mockRpc.shift();
|
||||
|
||||
expect(fetchUrl).toBe(mockUrl);
|
||||
expect(fetchOptions).toMatchObject({
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
expect(fetchOptions.body).toBeDefined();
|
||||
|
||||
const body = JSON.parse(fetchOptions.body);
|
||||
expect(body).toMatchObject(
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
jsonrpc: '2.0',
|
||||
method: 'invalid',
|
||||
},
|
||||
mockRequest,
|
||||
),
|
||||
);
|
||||
|
||||
const response = Object.assign(
|
||||
{},
|
||||
{
|
||||
jsonrpc: '2.0',
|
||||
id: body.id,
|
||||
error: {
|
||||
message: 'invalid error message',
|
||||
},
|
||||
result: 'invalid response',
|
||||
},
|
||||
mockResponse,
|
||||
);
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
text: () => {
|
||||
return Promise.resolve(JSON.stringify(response));
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default mock;
|
|
@ -1,77 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import {Client as LiveClient} from 'rpc-websockets';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
type RpcRequest = {
|
||||
method: string,
|
||||
params?: Array<any>,
|
||||
};
|
||||
|
||||
type RpcResponse = {
|
||||
context: {
|
||||
slot: number,
|
||||
},
|
||||
value: any,
|
||||
};
|
||||
|
||||
// Define TEST_LIVE in the environment to test against the real full node
|
||||
// identified by `url` instead of using the mock
|
||||
export const mockRpcEnabled = !process.env.TEST_LIVE;
|
||||
|
||||
export const mockRpcSocket: Array<[RpcRequest, RpcResponse]> = [];
|
||||
|
||||
class MockClient extends EventEmitter {
|
||||
mockOpen = false;
|
||||
subscriptionCounter = 0;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connect() {
|
||||
if (!this.mockOpen) {
|
||||
this.mockOpen = true;
|
||||
this.emit('open');
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.mockOpen) {
|
||||
this.mockOpen = false;
|
||||
this.emit('close');
|
||||
}
|
||||
}
|
||||
|
||||
notify(): Promise<any> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
on(event: string, callback: Function): this {
|
||||
return super.on(event, callback);
|
||||
}
|
||||
|
||||
call(method: string, params: Array<any>): Promise<Object> {
|
||||
expect(mockRpcSocket.length).toBeGreaterThanOrEqual(1);
|
||||
const [mockRequest, mockResponse] = mockRpcSocket.shift();
|
||||
|
||||
expect(method).toBe(mockRequest.method);
|
||||
expect(params).toMatchObject(mockRequest.params);
|
||||
|
||||
let id = this.subscriptionCounter++;
|
||||
const response = {
|
||||
subscription: id,
|
||||
result: mockResponse,
|
||||
};
|
||||
|
||||
setImmediate(() => {
|
||||
const eventName = method.replace('Subscribe', 'Notification');
|
||||
this.emit(eventName, response);
|
||||
});
|
||||
|
||||
return Promise.resolve(id);
|
||||
}
|
||||
}
|
||||
|
||||
const Client = mockRpcEnabled ? MockClient : LiveClient;
|
||||
export {Client};
|
|
@ -1,80 +1,84 @@
|
|||
// @flow
|
||||
import {Account} from '../src/account';
|
||||
import {expect} from 'chai';
|
||||
import {Buffer} from 'buffer';
|
||||
|
||||
test('generate new account', () => {
|
||||
const account = new Account();
|
||||
expect(account.secretKey).toHaveLength(64);
|
||||
});
|
||||
describe('Account', () => {
|
||||
it('generate new account', () => {
|
||||
const account = new Account();
|
||||
expect(account.secretKey).to.have.length(64);
|
||||
});
|
||||
|
||||
test('account from secret key', () => {
|
||||
const secretKey = Buffer.from([
|
||||
153,
|
||||
218,
|
||||
149,
|
||||
89,
|
||||
225,
|
||||
94,
|
||||
145,
|
||||
62,
|
||||
233,
|
||||
171,
|
||||
46,
|
||||
83,
|
||||
227,
|
||||
223,
|
||||
173,
|
||||
87,
|
||||
93,
|
||||
163,
|
||||
59,
|
||||
73,
|
||||
190,
|
||||
17,
|
||||
37,
|
||||
187,
|
||||
146,
|
||||
46,
|
||||
51,
|
||||
73,
|
||||
79,
|
||||
73,
|
||||
136,
|
||||
40,
|
||||
27,
|
||||
47,
|
||||
73,
|
||||
9,
|
||||
110,
|
||||
62,
|
||||
93,
|
||||
189,
|
||||
15,
|
||||
207,
|
||||
169,
|
||||
192,
|
||||
192,
|
||||
205,
|
||||
146,
|
||||
217,
|
||||
171,
|
||||
59,
|
||||
33,
|
||||
84,
|
||||
75,
|
||||
52,
|
||||
213,
|
||||
221,
|
||||
74,
|
||||
101,
|
||||
217,
|
||||
139,
|
||||
135,
|
||||
139,
|
||||
153,
|
||||
34,
|
||||
]);
|
||||
const account = new Account(secretKey);
|
||||
expect(account.publicKey.toBase58()).toBe(
|
||||
'2q7pyhPwAwZ3QMfZrnAbDhnh9mDUqycszcpf86VgQxhF',
|
||||
);
|
||||
it('account from secret key', () => {
|
||||
const secretKey = Buffer.from([
|
||||
153,
|
||||
218,
|
||||
149,
|
||||
89,
|
||||
225,
|
||||
94,
|
||||
145,
|
||||
62,
|
||||
233,
|
||||
171,
|
||||
46,
|
||||
83,
|
||||
227,
|
||||
223,
|
||||
173,
|
||||
87,
|
||||
93,
|
||||
163,
|
||||
59,
|
||||
73,
|
||||
190,
|
||||
17,
|
||||
37,
|
||||
187,
|
||||
146,
|
||||
46,
|
||||
51,
|
||||
73,
|
||||
79,
|
||||
73,
|
||||
136,
|
||||
40,
|
||||
27,
|
||||
47,
|
||||
73,
|
||||
9,
|
||||
110,
|
||||
62,
|
||||
93,
|
||||
189,
|
||||
15,
|
||||
207,
|
||||
169,
|
||||
192,
|
||||
192,
|
||||
205,
|
||||
146,
|
||||
217,
|
||||
171,
|
||||
59,
|
||||
33,
|
||||
84,
|
||||
75,
|
||||
52,
|
||||
213,
|
||||
221,
|
||||
74,
|
||||
101,
|
||||
217,
|
||||
139,
|
||||
135,
|
||||
139,
|
||||
153,
|
||||
34,
|
||||
]);
|
||||
const account = new Account(secretKey);
|
||||
expect(account.publicKey.toBase58()).to.eq(
|
||||
'2q7pyhPwAwZ3QMfZrnAbDhnh9mDUqycszcpf86VgQxhF',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,40 +1,41 @@
|
|||
// @flow
|
||||
|
||||
import {AgentManager, DESTROY_TIMEOUT_MS} from '../src/agent-manager';
|
||||
import {expect} from 'chai';
|
||||
import {sleep} from '../src/util/sleep';
|
||||
|
||||
jest.setTimeout(10 * 1000);
|
||||
describe('AgentManager', () => {
|
||||
it('works', async () => {
|
||||
const manager = new AgentManager();
|
||||
const agent = manager._agent;
|
||||
expect(manager._activeRequests).to.eq(0);
|
||||
expect(manager._destroyTimeout).to.be.null;
|
||||
|
||||
test('agent manager', async () => {
|
||||
const manager = new AgentManager();
|
||||
const agent = manager._agent;
|
||||
expect(manager._activeRequests).toBe(0);
|
||||
expect(manager._destroyTimeout).toBeNull();
|
||||
manager.requestStart();
|
||||
|
||||
manager.requestStart();
|
||||
expect(manager._activeRequests).to.eq(1);
|
||||
expect(manager._destroyTimeout).to.be.null;
|
||||
|
||||
expect(manager._activeRequests).toBe(1);
|
||||
expect(manager._destroyTimeout).toBeNull();
|
||||
manager.requestEnd();
|
||||
|
||||
manager.requestEnd();
|
||||
expect(manager._activeRequests).to.eq(0);
|
||||
expect(manager._destroyTimeout).not.to.be.null;
|
||||
|
||||
expect(manager._activeRequests).toBe(0);
|
||||
expect(manager._destroyTimeout).not.toBeNull();
|
||||
manager.requestStart();
|
||||
manager.requestStart();
|
||||
|
||||
manager.requestStart();
|
||||
manager.requestStart();
|
||||
expect(manager._activeRequests).to.eq(2);
|
||||
expect(manager._destroyTimeout).to.be.null;
|
||||
|
||||
expect(manager._activeRequests).toBe(2);
|
||||
expect(manager._destroyTimeout).toBeNull();
|
||||
manager.requestEnd();
|
||||
manager.requestEnd();
|
||||
|
||||
manager.requestEnd();
|
||||
manager.requestEnd();
|
||||
expect(manager._activeRequests).to.eq(0);
|
||||
expect(manager._destroyTimeout).not.to.be.null;
|
||||
expect(manager._agent).to.eq(agent);
|
||||
|
||||
expect(manager._activeRequests).toBe(0);
|
||||
expect(manager._destroyTimeout).not.toBeNull();
|
||||
expect(manager._agent).toBe(agent);
|
||||
await sleep(DESTROY_TIMEOUT_MS);
|
||||
|
||||
await sleep(DESTROY_TIMEOUT_MS);
|
||||
|
||||
expect(manager._agent).not.toBe(agent);
|
||||
expect(manager._agent).not.to.eq(agent);
|
||||
}).timeout(2 * DESTROY_TIMEOUT_MS);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// @flow
|
||||
|
||||
import fs from 'mz/fs';
|
||||
import {expect, use} from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
|
||||
import {
|
||||
Connection,
|
||||
|
@ -9,263 +11,278 @@ import {
|
|||
sendAndConfirmTransaction,
|
||||
Account,
|
||||
} from '../src';
|
||||
import {mockRpcEnabled} from './__mocks__/node-fetch';
|
||||
import {url} from './url';
|
||||
import {newAccountWithLamports} from './new-account-with-lamports';
|
||||
import {BPF_LOADER_PROGRAM_ID} from '../src/bpf-loader';
|
||||
import {helpers} from './mocks/rpc-http';
|
||||
|
||||
if (!mockRpcEnabled) {
|
||||
// The default of 5 seconds is too slow for live testing sometimes
|
||||
jest.setTimeout(240000);
|
||||
}
|
||||
use(chaiAsPromised);
|
||||
|
||||
test('load BPF C program', async () => {
|
||||
if (mockRpcEnabled) {
|
||||
console.log('non-live test skipped');
|
||||
return;
|
||||
}
|
||||
if (process.env.TEST_LIVE) {
|
||||
describe('BPF Loader', () => {
|
||||
it('load BPF C program', async () => {
|
||||
const data = await fs.readFile('test/fixtures/noop-c/noop.so');
|
||||
|
||||
const data = await fs.readFile('test/fixtures/noop-c/noop.so');
|
||||
const connection = new Connection(url, 'singleGossip');
|
||||
const {feeCalculator} = await connection.getRecentBlockhash();
|
||||
const fees =
|
||||
feeCalculator.lamportsPerSignature *
|
||||
BpfLoader.getMinNumSignatures(data.length);
|
||||
const payerBalance = await connection.getMinimumBalanceForRentExemption(
|
||||
0,
|
||||
);
|
||||
const executableBalance = await connection.getMinimumBalanceForRentExemption(
|
||||
data.length,
|
||||
);
|
||||
|
||||
const connection = new Connection(url, 'singleGossip');
|
||||
const {feeCalculator} = await connection.getRecentBlockhash();
|
||||
const fees =
|
||||
feeCalculator.lamportsPerSignature *
|
||||
BpfLoader.getMinNumSignatures(data.length);
|
||||
const payerBalance = await connection.getMinimumBalanceForRentExemption(0);
|
||||
const executableBalance = await connection.getMinimumBalanceForRentExemption(
|
||||
data.length,
|
||||
);
|
||||
const from = await newAccountWithLamports(
|
||||
connection,
|
||||
payerBalance + fees + executableBalance,
|
||||
);
|
||||
const from = new Account();
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: from.publicKey,
|
||||
amount: payerBalance + fees + executableBalance,
|
||||
});
|
||||
|
||||
const program = new Account();
|
||||
await BpfLoader.load(connection, from, program, data, BPF_LOADER_PROGRAM_ID);
|
||||
|
||||
// Check that program loading costed exactly `fees + executableBalance`
|
||||
const fromBalance = await connection.getBalance(from.publicKey);
|
||||
expect(fromBalance).toEqual(payerBalance);
|
||||
|
||||
const transaction = new Transaction().add({
|
||||
keys: [{pubkey: from.publicKey, isSigner: true, isWritable: true}],
|
||||
programId: program.publicKey,
|
||||
});
|
||||
await sendAndConfirmTransaction(connection, transaction, [from], {
|
||||
commitment: 'singleGossip',
|
||||
preflightCommitment: 'singleGossip',
|
||||
});
|
||||
});
|
||||
|
||||
describe('load BPF Rust program', () => {
|
||||
if (mockRpcEnabled) {
|
||||
console.log('non-live test skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
const connection = new Connection(url, 'singleGossip');
|
||||
|
||||
let program: Account;
|
||||
let payerAccount: Account;
|
||||
let programData: Buffer;
|
||||
|
||||
beforeAll(async () => {
|
||||
programData = await fs.readFile(
|
||||
'test/fixtures/noop-rust/solana_bpf_rust_noop.so',
|
||||
);
|
||||
|
||||
const {feeCalculator} = await connection.getRecentBlockhash();
|
||||
const fees =
|
||||
feeCalculator.lamportsPerSignature *
|
||||
BpfLoader.getMinNumSignatures(programData.length);
|
||||
const payerBalance = await connection.getMinimumBalanceForRentExemption(0);
|
||||
const executableBalance = await connection.getMinimumBalanceForRentExemption(
|
||||
programData.length,
|
||||
);
|
||||
|
||||
payerAccount = await newAccountWithLamports(
|
||||
connection,
|
||||
payerBalance + fees + executableBalance,
|
||||
);
|
||||
|
||||
// Create program account with low balance
|
||||
program = await newAccountWithLamports(connection, executableBalance - 1);
|
||||
|
||||
// First load will fail part way due to lack of funds
|
||||
const insufficientPayerAccount = await newAccountWithLamports(
|
||||
connection,
|
||||
2 * feeCalculator.lamportsPerSignature * 8,
|
||||
);
|
||||
|
||||
const failedLoad = BpfLoader.load(
|
||||
connection,
|
||||
insufficientPayerAccount,
|
||||
program,
|
||||
programData,
|
||||
BPF_LOADER_PROGRAM_ID,
|
||||
);
|
||||
await expect(failedLoad).rejects.toThrow();
|
||||
|
||||
// Second load will succeed
|
||||
await BpfLoader.load(
|
||||
connection,
|
||||
payerAccount,
|
||||
program,
|
||||
programData,
|
||||
BPF_LOADER_PROGRAM_ID,
|
||||
);
|
||||
});
|
||||
|
||||
test('get confirmed transaction', async () => {
|
||||
const transaction = new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: payerAccount.publicKey, isSigner: true, isWritable: true},
|
||||
],
|
||||
programId: program.publicKey,
|
||||
});
|
||||
|
||||
const signature = await sendAndConfirmTransaction(
|
||||
connection,
|
||||
transaction,
|
||||
[payerAccount],
|
||||
{
|
||||
commitment: 'max', // `getParsedConfirmedTransaction` requires max commitment
|
||||
preflightCommitment: connection.commitment || 'max',
|
||||
},
|
||||
);
|
||||
|
||||
const parsedTx = await connection.getParsedConfirmedTransaction(signature);
|
||||
if (parsedTx === null) {
|
||||
expect(parsedTx).not.toBeNull();
|
||||
return;
|
||||
}
|
||||
const {signatures, message} = parsedTx.transaction;
|
||||
expect(signatures[0]).toEqual(signature);
|
||||
const ix = message.instructions[0];
|
||||
if (ix.parsed) {
|
||||
expect('parsed' in ix).toBe(false);
|
||||
} else {
|
||||
expect(ix.programId.equals(program.publicKey)).toBe(true);
|
||||
expect(ix.data).toEqual('');
|
||||
}
|
||||
});
|
||||
|
||||
test('simulate transaction', async () => {
|
||||
const simulatedTransaction = new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: payerAccount.publicKey, isSigner: true, isWritable: true},
|
||||
],
|
||||
programId: program.publicKey,
|
||||
});
|
||||
|
||||
const {err, logs} = (
|
||||
await connection.simulateTransaction(simulatedTransaction, [payerAccount])
|
||||
).value;
|
||||
expect(err).toBeNull();
|
||||
|
||||
if (logs === null) {
|
||||
expect(logs).not.toBeNull();
|
||||
return;
|
||||
}
|
||||
|
||||
expect(logs.length).toBeGreaterThanOrEqual(2);
|
||||
expect(logs[0]).toEqual(
|
||||
`Program ${program.publicKey.toBase58()} invoke [1]`,
|
||||
);
|
||||
expect(logs[logs.length - 1]).toEqual(
|
||||
`Program ${program.publicKey.toBase58()} success`,
|
||||
);
|
||||
});
|
||||
|
||||
test('deprecated - simulate transaction without signature verification', async () => {
|
||||
const simulatedTransaction = new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: payerAccount.publicKey, isSigner: true, isWritable: true},
|
||||
],
|
||||
programId: program.publicKey,
|
||||
});
|
||||
|
||||
simulatedTransaction.setSigners(payerAccount.publicKey);
|
||||
const {err, logs} = (
|
||||
await connection.simulateTransaction(simulatedTransaction)
|
||||
).value;
|
||||
expect(err).toBeNull();
|
||||
|
||||
if (logs === null) {
|
||||
expect(logs).not.toBeNull();
|
||||
return;
|
||||
}
|
||||
|
||||
expect(logs.length).toBeGreaterThanOrEqual(2);
|
||||
expect(logs[0]).toEqual(
|
||||
`Program ${program.publicKey.toBase58()} invoke [1]`,
|
||||
);
|
||||
expect(logs[logs.length - 1]).toEqual(
|
||||
`Program ${program.publicKey.toBase58()} success`,
|
||||
);
|
||||
});
|
||||
|
||||
test('simulate transaction without signature verification', async () => {
|
||||
const simulatedTransaction = new Transaction({
|
||||
feePayer: payerAccount.publicKey,
|
||||
}).add({
|
||||
keys: [
|
||||
{pubkey: payerAccount.publicKey, isSigner: true, isWritable: true},
|
||||
],
|
||||
programId: program.publicKey,
|
||||
});
|
||||
|
||||
const {err, logs} = (
|
||||
await connection.simulateTransaction(simulatedTransaction)
|
||||
).value;
|
||||
expect(err).toBeNull();
|
||||
|
||||
if (logs === null) {
|
||||
expect(logs).not.toBeNull();
|
||||
return;
|
||||
}
|
||||
|
||||
expect(logs.length).toBeGreaterThanOrEqual(2);
|
||||
expect(logs[0]).toEqual(
|
||||
`Program ${program.publicKey.toBase58()} invoke [1]`,
|
||||
);
|
||||
expect(logs[logs.length - 1]).toEqual(
|
||||
`Program ${program.publicKey.toBase58()} success`,
|
||||
);
|
||||
});
|
||||
|
||||
test('simulate transaction with bad programId', async () => {
|
||||
const simulatedTransaction = new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: payerAccount.publicKey, isSigner: true, isWritable: true},
|
||||
],
|
||||
programId: new Account().publicKey,
|
||||
});
|
||||
|
||||
simulatedTransaction.setSigners(payerAccount.publicKey);
|
||||
const {err, logs} = (
|
||||
await connection.simulateTransaction(simulatedTransaction)
|
||||
).value;
|
||||
expect(err).toEqual('ProgramAccountNotFound');
|
||||
|
||||
if (logs === null) {
|
||||
expect(logs).not.toBeNull();
|
||||
return;
|
||||
}
|
||||
|
||||
expect(logs.length).toEqual(0);
|
||||
});
|
||||
|
||||
test('reload program', async () => {
|
||||
expect(
|
||||
const program = new Account();
|
||||
await BpfLoader.load(
|
||||
connection,
|
||||
payerAccount,
|
||||
from,
|
||||
program,
|
||||
programData,
|
||||
data,
|
||||
BPF_LOADER_PROGRAM_ID,
|
||||
),
|
||||
).toBe(false);
|
||||
);
|
||||
|
||||
// Check that program loading costed exactly `fees + executableBalance`
|
||||
const fromBalance = await connection.getBalance(from.publicKey);
|
||||
expect(fromBalance).to.eq(payerBalance);
|
||||
|
||||
const transaction = new Transaction().add({
|
||||
keys: [{pubkey: from.publicKey, isSigner: true, isWritable: true}],
|
||||
programId: program.publicKey,
|
||||
});
|
||||
await sendAndConfirmTransaction(connection, transaction, [from], {
|
||||
commitment: 'singleGossip',
|
||||
preflightCommitment: 'singleGossip',
|
||||
});
|
||||
}).timeout(5000);
|
||||
|
||||
describe('load BPF Rust program', () => {
|
||||
const connection = new Connection(url, 'singleGossip');
|
||||
|
||||
let program = new Account();
|
||||
let payerAccount = new Account();
|
||||
let programData: Buffer;
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60_000);
|
||||
programData = await fs.readFile(
|
||||
'test/fixtures/noop-rust/solana_bpf_rust_noop.so',
|
||||
);
|
||||
|
||||
const {feeCalculator} = await connection.getRecentBlockhash();
|
||||
const fees =
|
||||
feeCalculator.lamportsPerSignature *
|
||||
BpfLoader.getMinNumSignatures(programData.length);
|
||||
const payerBalance = await connection.getMinimumBalanceForRentExemption(
|
||||
0,
|
||||
);
|
||||
const executableBalance = await connection.getMinimumBalanceForRentExemption(
|
||||
programData.length,
|
||||
);
|
||||
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: payerAccount.publicKey,
|
||||
amount: payerBalance + fees + executableBalance,
|
||||
});
|
||||
|
||||
// Create program account with low balance
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: program.publicKey,
|
||||
amount: executableBalance - 1,
|
||||
});
|
||||
|
||||
// First load will fail part way due to lack of funds
|
||||
const insufficientPayerAccount = new Account();
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: insufficientPayerAccount.publicKey,
|
||||
amount: 2 * feeCalculator.lamportsPerSignature * 8,
|
||||
});
|
||||
|
||||
const failedLoad = BpfLoader.load(
|
||||
connection,
|
||||
insufficientPayerAccount,
|
||||
program,
|
||||
programData,
|
||||
BPF_LOADER_PROGRAM_ID,
|
||||
);
|
||||
await expect(failedLoad).to.be.rejected;
|
||||
|
||||
// Second load will succeed
|
||||
await BpfLoader.load(
|
||||
connection,
|
||||
payerAccount,
|
||||
program,
|
||||
programData,
|
||||
BPF_LOADER_PROGRAM_ID,
|
||||
);
|
||||
});
|
||||
|
||||
it('get confirmed transaction', async () => {
|
||||
const transaction = new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: payerAccount.publicKey, isSigner: true, isWritable: true},
|
||||
],
|
||||
programId: program.publicKey,
|
||||
});
|
||||
|
||||
const signature = await sendAndConfirmTransaction(
|
||||
connection,
|
||||
transaction,
|
||||
[payerAccount],
|
||||
{
|
||||
commitment: 'max', // `getParsedConfirmedTransaction` requires max commitment
|
||||
preflightCommitment: connection.commitment || 'max',
|
||||
},
|
||||
);
|
||||
|
||||
const parsedTx = await connection.getParsedConfirmedTransaction(
|
||||
signature,
|
||||
);
|
||||
if (parsedTx === null) {
|
||||
expect(parsedTx).not.to.be.null;
|
||||
return;
|
||||
}
|
||||
const {signatures, message} = parsedTx.transaction;
|
||||
expect(signatures[0]).to.eq(signature);
|
||||
const ix = message.instructions[0];
|
||||
if (ix.parsed) {
|
||||
expect('parsed' in ix).to.eq(false);
|
||||
} else {
|
||||
expect(ix.programId).to.eql(program.publicKey);
|
||||
expect(ix.data).to.eq('');
|
||||
}
|
||||
}).timeout(30000);
|
||||
|
||||
it('simulate transaction', async () => {
|
||||
const simulatedTransaction = new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: payerAccount.publicKey, isSigner: true, isWritable: true},
|
||||
],
|
||||
programId: program.publicKey,
|
||||
});
|
||||
|
||||
const {err, logs} = (
|
||||
await connection.simulateTransaction(simulatedTransaction, [
|
||||
payerAccount,
|
||||
])
|
||||
).value;
|
||||
expect(err).to.be.null;
|
||||
|
||||
if (logs === null) {
|
||||
expect(logs).not.to.be.null;
|
||||
return;
|
||||
}
|
||||
|
||||
expect(logs.length).to.be.at.least(2);
|
||||
expect(logs[0]).to.eq(
|
||||
`Program ${program.publicKey.toBase58()} invoke [1]`,
|
||||
);
|
||||
expect(logs[logs.length - 1]).to.eq(
|
||||
`Program ${program.publicKey.toBase58()} success`,
|
||||
);
|
||||
});
|
||||
|
||||
it('deprecated - simulate transaction without signature verification', async () => {
|
||||
const simulatedTransaction = new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: payerAccount.publicKey, isSigner: true, isWritable: true},
|
||||
],
|
||||
programId: program.publicKey,
|
||||
});
|
||||
|
||||
simulatedTransaction.setSigners(payerAccount.publicKey);
|
||||
const {err, logs} = (
|
||||
await connection.simulateTransaction(simulatedTransaction)
|
||||
).value;
|
||||
expect(err).to.be.null;
|
||||
|
||||
if (logs === null) {
|
||||
expect(logs).not.to.be.null;
|
||||
return;
|
||||
}
|
||||
|
||||
expect(logs.length).to.be.at.least(2);
|
||||
expect(logs[0]).to.eq(
|
||||
`Program ${program.publicKey.toBase58()} invoke [1]`,
|
||||
);
|
||||
expect(logs[logs.length - 1]).to.eq(
|
||||
`Program ${program.publicKey.toBase58()} success`,
|
||||
);
|
||||
});
|
||||
|
||||
it('simulate transaction without signature verification', async () => {
|
||||
const simulatedTransaction = new Transaction({
|
||||
feePayer: payerAccount.publicKey,
|
||||
}).add({
|
||||
keys: [
|
||||
{pubkey: payerAccount.publicKey, isSigner: true, isWritable: true},
|
||||
],
|
||||
programId: program.publicKey,
|
||||
});
|
||||
|
||||
const {err, logs} = (
|
||||
await connection.simulateTransaction(simulatedTransaction)
|
||||
).value;
|
||||
expect(err).to.be.null;
|
||||
|
||||
if (logs === null) {
|
||||
expect(logs).not.to.be.null;
|
||||
return;
|
||||
}
|
||||
|
||||
expect(logs.length).to.be.at.least(2);
|
||||
expect(logs[0]).to.eq(
|
||||
`Program ${program.publicKey.toBase58()} invoke [1]`,
|
||||
);
|
||||
expect(logs[logs.length - 1]).to.eq(
|
||||
`Program ${program.publicKey.toBase58()} success`,
|
||||
);
|
||||
});
|
||||
|
||||
it('simulate transaction with bad programId', async () => {
|
||||
const simulatedTransaction = new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: payerAccount.publicKey, isSigner: true, isWritable: true},
|
||||
],
|
||||
programId: new Account().publicKey,
|
||||
});
|
||||
|
||||
simulatedTransaction.setSigners(payerAccount.publicKey);
|
||||
const {err, logs} = (
|
||||
await connection.simulateTransaction(simulatedTransaction)
|
||||
).value;
|
||||
expect(err).to.eq('ProgramAccountNotFound');
|
||||
|
||||
if (logs === null) {
|
||||
expect(logs).not.to.be.null;
|
||||
return;
|
||||
}
|
||||
|
||||
expect(logs).to.have.length(0);
|
||||
});
|
||||
|
||||
it('reload program', async () => {
|
||||
expect(
|
||||
await BpfLoader.load(
|
||||
connection,
|
||||
payerAccount,
|
||||
program,
|
||||
programData,
|
||||
BPF_LOADER_PROGRAM_ID,
|
||||
),
|
||||
).to.eq(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
// @flow
|
||||
import {clusterApiUrl} from '../src/util/cluster';
|
||||
import {expect} from 'chai';
|
||||
|
||||
test('invalid', () => {
|
||||
expect(() => {
|
||||
// $FlowExpectedError
|
||||
clusterApiUrl('abc123');
|
||||
}).toThrow();
|
||||
});
|
||||
describe('Cluster Util', () => {
|
||||
it('invalid', () => {
|
||||
expect(() => {
|
||||
// $FlowExpectedError
|
||||
clusterApiUrl('abc123');
|
||||
}).to.throw();
|
||||
});
|
||||
|
||||
test('devnet', () => {
|
||||
expect(clusterApiUrl()).toEqual('https://devnet.solana.com');
|
||||
expect(clusterApiUrl('devnet')).toEqual('https://devnet.solana.com');
|
||||
expect(clusterApiUrl('devnet', true)).toEqual('https://devnet.solana.com');
|
||||
expect(clusterApiUrl('devnet', false)).toEqual('http://devnet.solana.com');
|
||||
it('devnet', () => {
|
||||
expect(clusterApiUrl()).to.eq('https://devnet.solana.com');
|
||||
expect(clusterApiUrl('devnet')).to.eq('https://devnet.solana.com');
|
||||
expect(clusterApiUrl('devnet', true)).to.eq('https://devnet.solana.com');
|
||||
expect(clusterApiUrl('devnet', false)).to.eq('http://devnet.solana.com');
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
make -C ../../../examples/bpf-c-noop/
|
||||
cp ../../../examples/bpf-c-noop/out/noop.so .
|
|
@ -1,7 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
cargo build-bpf --manifest-path=../../../examples/bpf-rust-noop/Cargo.toml
|
||||
cp ../../../examples/bpf-rust-noop/target/deploy/solana_bpf_rust_noop.so .
|
|
@ -1,83 +0,0 @@
|
|||
// @flow
|
||||
import {Account} from '../src/account';
|
||||
import {SystemProgram} from '../src/system-program';
|
||||
import {Transaction} from '../src/transaction';
|
||||
|
||||
test('verify getConfirmedBlock', () => {
|
||||
const account0 = new Account();
|
||||
const account1 = new Account();
|
||||
const account2 = new Account();
|
||||
const account3 = new Account();
|
||||
const recentBlockhash = account1.publicKey.toBase58(); // Fake recentBlockhash
|
||||
|
||||
// Create a couple signed transactions
|
||||
const transfer0 = SystemProgram.transfer({
|
||||
fromPubkey: account0.publicKey,
|
||||
toPubkey: account1.publicKey,
|
||||
lamports: 123,
|
||||
});
|
||||
|
||||
const transaction0 = new Transaction({recentBlockhash}).add(transfer0);
|
||||
transaction0.sign(account0);
|
||||
const transfer1 = SystemProgram.transfer({
|
||||
fromPubkey: account2.publicKey,
|
||||
toPubkey: account3.publicKey,
|
||||
lamports: 456,
|
||||
});
|
||||
|
||||
let transaction1 = new Transaction({recentBlockhash}).add(transfer1);
|
||||
transaction1.sign(account2);
|
||||
|
||||
// Build ConfirmedBlock, with dummy data for blockhashes, balances
|
||||
const confirmedBlock = {
|
||||
blockhash: recentBlockhash,
|
||||
previousBlockhash: recentBlockhash,
|
||||
transactions: [
|
||||
{
|
||||
transaction: transaction0,
|
||||
meta: {
|
||||
fee: 0,
|
||||
preBalances: [100000, 100000, 1, 1, 1],
|
||||
postBalances: [99877, 100123, 1, 1, 1],
|
||||
status: {Ok: null},
|
||||
err: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
transaction: transaction1,
|
||||
meta: {
|
||||
fee: 0,
|
||||
preBalances: [100000, 100000, 1, 1, 1],
|
||||
postBalances: [99544, 100456, 1, 1, 1],
|
||||
status: {Ok: null},
|
||||
err: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
rewards: [],
|
||||
};
|
||||
|
||||
// Verify signatures in ConfirmedBlock
|
||||
for (const transactionWithMeta of confirmedBlock.transactions) {
|
||||
expect(transactionWithMeta.transaction.verifySignatures()).toBe(true);
|
||||
}
|
||||
|
||||
const bogusSignature = {
|
||||
signature: Buffer.alloc(64, 9),
|
||||
publicKey: account2.publicKey,
|
||||
};
|
||||
transaction1.signatures[0] = bogusSignature;
|
||||
|
||||
let badConfirmedBlock = confirmedBlock;
|
||||
badConfirmedBlock.transactions[1].transaction = transaction1;
|
||||
|
||||
// Verify signatures in ConfirmedBlock
|
||||
const verifications = badConfirmedBlock.transactions.map(
|
||||
transactionWithMeta => transactionWithMeta.transaction.verifySignatures(),
|
||||
);
|
||||
expect(
|
||||
verifications.reduce(
|
||||
(accumulator, currentValue) => accumulator && currentValue,
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import type {TransactionSignature} from '../../src/transaction';
|
||||
import {mockRpcSocket} from '../__mocks__/rpc-websockets';
|
||||
|
||||
export function mockConfirmTransaction(signature: TransactionSignature) {
|
||||
mockRpcSocket.push([
|
||||
{
|
||||
method: 'signatureSubscribe',
|
||||
params: [signature, {commitment: 'singleGossip'}],
|
||||
},
|
||||
{
|
||||
context: {
|
||||
slot: 11,
|
||||
},
|
||||
value: {err: null},
|
||||
},
|
||||
]);
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import {Account} from '../../src';
|
||||
import type {Commitment} from '../../src/connection';
|
||||
import {url} from '../url';
|
||||
import {mockRpc} from '../__mocks__/node-fetch';
|
||||
|
||||
export function mockGetRecentBlockhash(commitment: ?Commitment) {
|
||||
const recentBlockhash = new Account();
|
||||
const params = [];
|
||||
if (commitment) {
|
||||
params.push({commitment});
|
||||
}
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'getRecentBlockhash',
|
||||
params,
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: {
|
||||
context: {
|
||||
slot: 11,
|
||||
},
|
||||
value: {
|
||||
blockhash: recentBlockhash.publicKey.toBase58(),
|
||||
feeCalculator: {
|
||||
lamportsPerSignature: 42,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
// @flow
|
||||
|
||||
import bs58 from 'bs58';
|
||||
import BN from 'bn.js';
|
||||
import * as mockttp from 'mockttp';
|
||||
import {mockRpcMessage} from './rpc-websockets';
|
||||
|
||||
import {Connection} from '../../src';
|
||||
import type {Commitment} from '../../src/connection';
|
||||
|
||||
export const mockServer = process.env.TEST_LIVE || mockttp.getLocal();
|
||||
|
||||
let uniqueCounter = 0;
|
||||
export const uniqueSignature = () => {
|
||||
return bs58.encode(new BN(++uniqueCounter).toArray(null, 64));
|
||||
};
|
||||
export const uniqueBlockhash = () => {
|
||||
return bs58.encode(new BN(++uniqueCounter).toArray(null, 32));
|
||||
};
|
||||
|
||||
export const mockErrorMessage = 'Invalid';
|
||||
export const mockErrorResponse = {
|
||||
code: -32602,
|
||||
message: mockErrorMessage,
|
||||
};
|
||||
|
||||
export const mockRpcResponse = async ({
|
||||
method,
|
||||
params,
|
||||
value,
|
||||
error,
|
||||
withContext,
|
||||
}: {
|
||||
method: string,
|
||||
params: Array<any>,
|
||||
value: any,
|
||||
error: any,
|
||||
withContext?: boolean,
|
||||
}) => {
|
||||
if (process.env.TEST_LIVE) return;
|
||||
let result = withContext
|
||||
? {
|
||||
context: {
|
||||
slot: 11,
|
||||
},
|
||||
value,
|
||||
}
|
||||
: value;
|
||||
await mockServer
|
||||
.post('/')
|
||||
.withJsonBodyIncluding({
|
||||
jsonrpc: '2.0',
|
||||
method,
|
||||
params,
|
||||
})
|
||||
.thenReply(
|
||||
200,
|
||||
JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
id: '',
|
||||
error,
|
||||
result,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const recentBlockhash = async ({
|
||||
connection,
|
||||
commitment,
|
||||
}: {
|
||||
connection: Connection,
|
||||
commitment: ?Commitment,
|
||||
}) => {
|
||||
const blockhash = uniqueBlockhash();
|
||||
const params = [];
|
||||
if (commitment) {
|
||||
params.push({commitment});
|
||||
}
|
||||
|
||||
await mockRpcResponse({
|
||||
method: 'getRecentBlockhash',
|
||||
params,
|
||||
value: {
|
||||
blockhash,
|
||||
feeCalculator: {
|
||||
lamportsPerSignature: 42,
|
||||
},
|
||||
},
|
||||
withContext: true,
|
||||
});
|
||||
|
||||
return await connection.getRecentBlockhash(commitment);
|
||||
};
|
||||
|
||||
const processTransaction = async ({
|
||||
connection,
|
||||
transaction,
|
||||
signers,
|
||||
commitment,
|
||||
err,
|
||||
}: {
|
||||
connection: Connection,
|
||||
transaction: Transaction,
|
||||
signers: Array<Account>,
|
||||
commitment: Commitment,
|
||||
err?: any,
|
||||
}) => {
|
||||
const blockhash = (await recentBlockhash({connection})).blockhash;
|
||||
transaction.recentBlockhash = blockhash;
|
||||
transaction.sign(...signers);
|
||||
|
||||
const encoded = transaction.serialize().toString('base64');
|
||||
const signature = bs58.encode(transaction.signature);
|
||||
await mockRpcResponse({
|
||||
method: 'sendTransaction',
|
||||
params: [encoded],
|
||||
value: signature,
|
||||
});
|
||||
|
||||
const sendOptions = err
|
||||
? {
|
||||
skipPreflight: true,
|
||||
}
|
||||
: {
|
||||
preflightCommitment: commitment,
|
||||
};
|
||||
|
||||
await connection.sendEncodedTransaction(encoded, sendOptions);
|
||||
|
||||
await mockRpcMessage({
|
||||
method: 'signatureSubscribe',
|
||||
params: [signature, {commitment}],
|
||||
result: {err: err || null},
|
||||
});
|
||||
|
||||
return await connection.confirmTransaction(signature, commitment);
|
||||
};
|
||||
|
||||
const airdrop = async ({
|
||||
connection,
|
||||
address,
|
||||
amount,
|
||||
}: {
|
||||
connection: Connection,
|
||||
address: PublicKey,
|
||||
amount: number,
|
||||
}) => {
|
||||
await mockRpcResponse({
|
||||
method: 'requestAirdrop',
|
||||
params: [address.toBase58(), amount],
|
||||
value: uniqueSignature(),
|
||||
});
|
||||
|
||||
const signature = await connection.requestAirdrop(address, amount);
|
||||
|
||||
await mockRpcMessage({
|
||||
method: 'signatureSubscribe',
|
||||
params: [signature, {commitment: 'singleGossip'}],
|
||||
result: {err: null},
|
||||
});
|
||||
|
||||
await connection.confirmTransaction(signature, 'singleGossip');
|
||||
return signature;
|
||||
};
|
||||
|
||||
export const helpers = {
|
||||
airdrop,
|
||||
processTransaction,
|
||||
recentBlockhash,
|
||||
};
|
|
@ -0,0 +1,106 @@
|
|||
// @flow
|
||||
|
||||
import {Client as LiveClient} from 'rpc-websockets';
|
||||
import {expect} from 'chai';
|
||||
import sinon from 'sinon';
|
||||
import {Connection} from '../../src';
|
||||
|
||||
type RpcRequest = {
|
||||
method: string,
|
||||
params?: Array<any>,
|
||||
};
|
||||
|
||||
type RpcResponse = {
|
||||
context: {
|
||||
slot: number,
|
||||
},
|
||||
value: any,
|
||||
};
|
||||
|
||||
const mockRpcSocket: Array<[RpcRequest, RpcResponse]> = [];
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
export const mockRpcMessage = ({
|
||||
method,
|
||||
params,
|
||||
result,
|
||||
}: {
|
||||
method: string,
|
||||
params: Array<any>,
|
||||
result: any,
|
||||
}) => {
|
||||
mockRpcSocket.push([
|
||||
{method, params},
|
||||
{
|
||||
context: {slot: 11},
|
||||
value: result,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
export const stubRpcWebSocket = (connection: Connection) => {
|
||||
const rpcWebSocket = connection._rpcWebSocket;
|
||||
const mockClient = new MockClient(rpcWebSocket);
|
||||
sandbox.stub(rpcWebSocket, 'connect').callsFake(() => {
|
||||
mockClient.connect();
|
||||
});
|
||||
sandbox.stub(rpcWebSocket, 'close').callsFake(() => {
|
||||
mockClient.close();
|
||||
});
|
||||
sandbox.stub(rpcWebSocket, 'call').callsFake((method, params) => {
|
||||
return mockClient.call(method, params);
|
||||
});
|
||||
};
|
||||
|
||||
export const restoreRpcWebSocket = (connection: Connection) => {
|
||||
connection._rpcWebSocket.close();
|
||||
if (connection._rpcWebSocketIdleTimeout !== null) {
|
||||
clearTimeout(connection._rpcWebSocketIdleTimeout);
|
||||
connection._rpcWebSocketIdleTimeout = null;
|
||||
}
|
||||
sandbox.restore();
|
||||
};
|
||||
|
||||
class MockClient {
|
||||
mockOpen = false;
|
||||
subscriptionCounter = 0;
|
||||
|
||||
constructor(rpcWebSocket: LiveClient) {
|
||||
this.client = rpcWebSocket;
|
||||
}
|
||||
|
||||
connect() {
|
||||
if (!this.mockOpen) {
|
||||
this.mockOpen = true;
|
||||
this.client.emit('open');
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.mockOpen) {
|
||||
this.mockOpen = false;
|
||||
this.client.emit('close');
|
||||
}
|
||||
}
|
||||
|
||||
call(method: string, params: Array<any>): Promise<Object> {
|
||||
expect(mockRpcSocket.length).to.be.at.least(1);
|
||||
const [mockRequest, mockResponse] = mockRpcSocket.shift();
|
||||
|
||||
expect(method).to.eq(mockRequest.method);
|
||||
expect(params).to.eql(mockRequest.params);
|
||||
|
||||
let id = ++this.subscriptionCounter;
|
||||
const response = {
|
||||
subscription: id,
|
||||
result: mockResponse,
|
||||
};
|
||||
|
||||
setImmediate(() => {
|
||||
const eventName = method.replace('Subscribe', 'Notification');
|
||||
this.client.emit(eventName, response);
|
||||
});
|
||||
|
||||
return Promise.resolve(id);
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import {Account, Connection} from '../src';
|
||||
import {mockRpc} from './__mocks__/node-fetch';
|
||||
import {url} from './url';
|
||||
|
||||
export async function newAccountWithLamports(
|
||||
connection: Connection,
|
||||
lamports: number = 1000000,
|
||||
): Promise<Account> {
|
||||
const account = new Account();
|
||||
|
||||
{
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'requestAirdrop',
|
||||
params: [account.publicKey.toBase58(), lamports],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
// Signature doesn't matter
|
||||
result:
|
||||
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
const signature = await connection.requestAirdrop(
|
||||
account.publicKey,
|
||||
lamports,
|
||||
);
|
||||
await connection.confirmTransaction(signature, 'singleGossip');
|
||||
|
||||
return account;
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
// @flow
|
||||
|
||||
import bs58 from 'bs58';
|
||||
import {Buffer} from 'buffer';
|
||||
import {expect} from 'chai';
|
||||
|
||||
import {
|
||||
Account,
|
||||
|
@ -10,15 +12,9 @@ import {
|
|||
PublicKey,
|
||||
} from '../src';
|
||||
import {NONCE_ACCOUNT_LENGTH} from '../src/nonce-account';
|
||||
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
|
||||
import {mockGetRecentBlockhash} from './mockrpc/get-recent-blockhash';
|
||||
import {url} from './url';
|
||||
import {mockConfirmTransaction} from './mockrpc/confirm-transaction';
|
||||
|
||||
if (!mockRpcEnabled) {
|
||||
// Testing max commitment level takes around 20s to complete
|
||||
jest.setTimeout(30000);
|
||||
}
|
||||
import {MOCK_PORT, url} from './url';
|
||||
import {helpers, mockRpcResponse, mockServer} from './mocks/rpc-http';
|
||||
import {stubRpcWebSocket, restoreRpcWebSocket} from './mocks/rpc-websockets';
|
||||
|
||||
const expectedData = (authorizedPubkey: PublicKey): [string, string] => {
|
||||
const expectedData = Buffer.alloc(NONCE_ACCOUNT_LENGTH);
|
||||
|
@ -31,257 +27,154 @@ const expectedData = (authorizedPubkey: PublicKey): [string, string] => {
|
|||
return [expectedData.toString('base64'), 'base64'];
|
||||
};
|
||||
|
||||
test('create and query nonce account', async () => {
|
||||
const from = new Account();
|
||||
const nonceAccount = new Account();
|
||||
const connection = new Connection(url, 'singleGossip');
|
||||
describe('Nonce', () => {
|
||||
let connection: Connection;
|
||||
beforeEach(() => {
|
||||
connection = new Connection(url);
|
||||
});
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
if (!process.env.TEST_LIVE) {
|
||||
beforeEach(() => {
|
||||
mockServer.start(MOCK_PORT);
|
||||
stubRpcWebSocket(connection);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockServer.stop();
|
||||
restoreRpcWebSocket(connection);
|
||||
});
|
||||
}
|
||||
|
||||
it('create and query nonce account', async () => {
|
||||
const from = new Account();
|
||||
const nonceAccount = new Account();
|
||||
|
||||
await mockRpcResponse({
|
||||
method: 'getMinimumBalanceForRentExemption',
|
||||
params: [NONCE_ACCOUNT_LENGTH, {commitment: 'singleGossip'}],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: 50,
|
||||
},
|
||||
]);
|
||||
params: [NONCE_ACCOUNT_LENGTH],
|
||||
value: 50,
|
||||
});
|
||||
|
||||
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
|
||||
NONCE_ACCOUNT_LENGTH,
|
||||
);
|
||||
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
|
||||
NONCE_ACCOUNT_LENGTH,
|
||||
);
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'requestAirdrop',
|
||||
params: [from.publicKey.toBase58(), minimumAmount * 2],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result:
|
||||
'1WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
||||
},
|
||||
]);
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: from.publicKey,
|
||||
amount: minimumAmount * 2,
|
||||
});
|
||||
|
||||
const signature = await connection.requestAirdrop(
|
||||
from.publicKey,
|
||||
minimumAmount * 2,
|
||||
);
|
||||
mockConfirmTransaction(signature);
|
||||
await connection.confirmTransaction(signature, 'singleGossip');
|
||||
const transaction = new Transaction().add(
|
||||
SystemProgram.createNonceAccount({
|
||||
fromPubkey: from.publicKey,
|
||||
noncePubkey: nonceAccount.publicKey,
|
||||
authorizedPubkey: from.publicKey,
|
||||
lamports: minimumAmount,
|
||||
}),
|
||||
);
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'getBalance',
|
||||
params: [from.publicKey.toBase58(), {commitment: 'singleGossip'}],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: {
|
||||
context: {
|
||||
slot: 11,
|
||||
},
|
||||
value: minimumAmount * 2,
|
||||
},
|
||||
},
|
||||
]);
|
||||
await helpers.processTransaction({
|
||||
connection,
|
||||
transaction,
|
||||
signers: [from, nonceAccount],
|
||||
commitment: 'singleGossip',
|
||||
});
|
||||
|
||||
const balance = await connection.getBalance(from.publicKey);
|
||||
expect(balance).toBe(minimumAmount * 2);
|
||||
|
||||
mockGetRecentBlockhash('max');
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'sendTransaction',
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result:
|
||||
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
||||
},
|
||||
]);
|
||||
|
||||
const transaction = new Transaction().add(
|
||||
SystemProgram.createNonceAccount({
|
||||
fromPubkey: from.publicKey,
|
||||
noncePubkey: nonceAccount.publicKey,
|
||||
authorizedPubkey: from.publicKey,
|
||||
lamports: minimumAmount,
|
||||
}),
|
||||
);
|
||||
const nonceSignature = await connection.sendTransaction(
|
||||
transaction,
|
||||
[from, nonceAccount],
|
||||
{
|
||||
skipPreflight: true,
|
||||
},
|
||||
);
|
||||
mockConfirmTransaction(nonceSignature);
|
||||
await connection.confirmTransaction(nonceSignature, 'singleGossip');
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
await mockRpcResponse({
|
||||
method: 'getAccountInfo',
|
||||
params: [
|
||||
nonceAccount.publicKey.toBase58(),
|
||||
{encoding: 'base64', commitment: 'singleGossip'},
|
||||
],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: {
|
||||
context: {
|
||||
slot: 11,
|
||||
},
|
||||
value: {
|
||||
owner: '11111111111111111111111111111111',
|
||||
lamports: minimumAmount,
|
||||
data: expectedData(from.publicKey),
|
||||
executable: false,
|
||||
},
|
||||
value: {
|
||||
owner: '11111111111111111111111111111111',
|
||||
lamports: minimumAmount,
|
||||
data: expectedData(from.publicKey),
|
||||
executable: false,
|
||||
},
|
||||
},
|
||||
]);
|
||||
//
|
||||
const nonceAccountData = await connection.getNonce(nonceAccount.publicKey);
|
||||
if (nonceAccountData === null) {
|
||||
expect(nonceAccountData).not.toBeNull();
|
||||
return;
|
||||
}
|
||||
expect(nonceAccountData.authorizedPubkey).toEqual(from.publicKey);
|
||||
expect(bs58.decode(nonceAccountData.nonce).length).toBeGreaterThan(30);
|
||||
});
|
||||
withContext: true,
|
||||
});
|
||||
|
||||
test('create and query nonce account with seed', async () => {
|
||||
const from = new Account();
|
||||
const seed = 'seed';
|
||||
const noncePubkey = await PublicKey.createWithSeed(
|
||||
from.publicKey,
|
||||
seed,
|
||||
SystemProgram.programId,
|
||||
);
|
||||
const connection = new Connection(url, 'singleGossip');
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'getMinimumBalanceForRentExemption',
|
||||
params: [NONCE_ACCOUNT_LENGTH, {commitment: 'singleGossip'}],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: 50,
|
||||
},
|
||||
]);
|
||||
|
||||
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
|
||||
NONCE_ACCOUNT_LENGTH,
|
||||
);
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'requestAirdrop',
|
||||
params: [from.publicKey.toBase58(), minimumAmount * 2],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result:
|
||||
'1WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
||||
},
|
||||
]);
|
||||
|
||||
const signature = await connection.requestAirdrop(
|
||||
from.publicKey,
|
||||
minimumAmount * 2,
|
||||
);
|
||||
mockConfirmTransaction(signature);
|
||||
await connection.confirmTransaction(signature, 'singleGossip');
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'getBalance',
|
||||
params: [from.publicKey.toBase58(), {commitment: 'singleGossip'}],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: {
|
||||
context: {
|
||||
slot: 11,
|
||||
},
|
||||
value: minimumAmount * 2,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const balance = await connection.getBalance(from.publicKey);
|
||||
expect(balance).toBe(minimumAmount * 2);
|
||||
|
||||
mockGetRecentBlockhash('max');
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'sendTransaction',
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result:
|
||||
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
||||
},
|
||||
]);
|
||||
|
||||
const transaction = new Transaction().add(
|
||||
SystemProgram.createNonceAccount({
|
||||
fromPubkey: from.publicKey,
|
||||
noncePubkey: noncePubkey,
|
||||
basePubkey: from.publicKey,
|
||||
seed,
|
||||
authorizedPubkey: from.publicKey,
|
||||
lamports: minimumAmount,
|
||||
}),
|
||||
);
|
||||
const nonceSignature = await connection.sendTransaction(transaction, [from], {
|
||||
skipPreflight: true,
|
||||
const nonceAccountData = await connection.getNonce(
|
||||
nonceAccount.publicKey,
|
||||
'singleGossip',
|
||||
);
|
||||
if (nonceAccountData === null) {
|
||||
expect(nonceAccountData).not.to.be.null;
|
||||
return;
|
||||
}
|
||||
expect(nonceAccountData.authorizedPubkey).to.eql(from.publicKey);
|
||||
expect(bs58.decode(nonceAccountData.nonce).length).to.be.greaterThan(30);
|
||||
});
|
||||
mockConfirmTransaction(nonceSignature);
|
||||
await connection.confirmTransaction(nonceSignature, 'singleGossip');
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
it('create and query nonce account with seed', async () => {
|
||||
const from = new Account();
|
||||
const seed = 'seed';
|
||||
const noncePubkey = await PublicKey.createWithSeed(
|
||||
from.publicKey,
|
||||
seed,
|
||||
SystemProgram.programId,
|
||||
);
|
||||
|
||||
await mockRpcResponse({
|
||||
method: 'getMinimumBalanceForRentExemption',
|
||||
params: [NONCE_ACCOUNT_LENGTH],
|
||||
value: 50,
|
||||
});
|
||||
|
||||
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
|
||||
NONCE_ACCOUNT_LENGTH,
|
||||
);
|
||||
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: from.publicKey,
|
||||
amount: minimumAmount * 2,
|
||||
});
|
||||
|
||||
const transaction = new Transaction().add(
|
||||
SystemProgram.createNonceAccount({
|
||||
fromPubkey: from.publicKey,
|
||||
noncePubkey: noncePubkey,
|
||||
basePubkey: from.publicKey,
|
||||
seed,
|
||||
authorizedPubkey: from.publicKey,
|
||||
lamports: minimumAmount,
|
||||
}),
|
||||
);
|
||||
|
||||
await helpers.processTransaction({
|
||||
connection,
|
||||
transaction,
|
||||
signers: [from],
|
||||
commitment: 'singleGossip',
|
||||
});
|
||||
|
||||
await mockRpcResponse({
|
||||
method: 'getAccountInfo',
|
||||
params: [
|
||||
noncePubkey.toBase58(),
|
||||
{encoding: 'base64', commitment: 'singleGossip'},
|
||||
],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: {
|
||||
context: {
|
||||
slot: 11,
|
||||
},
|
||||
value: {
|
||||
owner: '11111111111111111111111111111111',
|
||||
lamports: minimumAmount,
|
||||
data: expectedData(from.publicKey),
|
||||
executable: false,
|
||||
},
|
||||
value: {
|
||||
owner: '11111111111111111111111111111111',
|
||||
lamports: minimumAmount,
|
||||
data: expectedData(from.publicKey),
|
||||
executable: false,
|
||||
},
|
||||
},
|
||||
]);
|
||||
//
|
||||
const nonceAccountData = await connection.getNonce(noncePubkey);
|
||||
if (nonceAccountData === null) {
|
||||
expect(nonceAccountData).not.toBeNull();
|
||||
return;
|
||||
}
|
||||
expect(nonceAccountData.authorizedPubkey).toEqual(from.publicKey);
|
||||
expect(bs58.decode(nonceAccountData.nonce).length).toBeGreaterThan(30);
|
||||
withContext: true,
|
||||
});
|
||||
|
||||
const nonceAccountData = await connection.getNonce(
|
||||
noncePubkey,
|
||||
'singleGossip',
|
||||
);
|
||||
if (nonceAccountData === null) {
|
||||
expect(nonceAccountData).not.to.be.null;
|
||||
return;
|
||||
}
|
||||
expect(nonceAccountData.authorizedPubkey).to.eql(from.publicKey);
|
||||
expect(bs58.decode(nonceAccountData.nonce).length).to.be.greaterThan(30);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,11 +1,77 @@
|
|||
// @flow
|
||||
import BN from 'bn.js';
|
||||
|
||||
import {Buffer} from 'buffer';
|
||||
import {PublicKey, MAX_SEED_LENGTH} from '../src/publickey';
|
||||
import {expect, use} from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
|
||||
test('invalid', () => {
|
||||
expect(() => {
|
||||
new PublicKey([
|
||||
use(chaiAsPromised);
|
||||
|
||||
describe('PublicKey', function () {
|
||||
it('invalid', () => {
|
||||
expect(() => {
|
||||
new PublicKey([
|
||||
3,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]);
|
||||
}).to.throw();
|
||||
|
||||
expect(() => {
|
||||
new PublicKey(
|
||||
'0x300000000000000000000000000000000000000000000000000000000000000000000',
|
||||
);
|
||||
}).to.throw();
|
||||
|
||||
expect(() => {
|
||||
new PublicKey(
|
||||
'0x300000000000000000000000000000000000000000000000000000000000000',
|
||||
);
|
||||
}).to.throw();
|
||||
|
||||
expect(() => {
|
||||
new PublicKey(
|
||||
'135693854574979916511997248057056142015550763280047535983739356259273198796800000',
|
||||
);
|
||||
}).to.throw();
|
||||
|
||||
expect(() => {
|
||||
new PublicKey('12345');
|
||||
}).to.throw();
|
||||
});
|
||||
|
||||
it('equals', () => {
|
||||
const arrayKey = new PublicKey([
|
||||
3,
|
||||
0,
|
||||
0,
|
||||
|
@ -38,286 +104,228 @@ test('invalid', () => {
|
|||
0,
|
||||
0,
|
||||
0,
|
||||
]);
|
||||
const base58Key = new PublicKey(
|
||||
'CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3',
|
||||
);
|
||||
|
||||
expect(arrayKey.equals(base58Key)).to.be.true;
|
||||
});
|
||||
|
||||
it('toBase58', () => {
|
||||
const key = new PublicKey('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
|
||||
expect(key.toBase58()).to.eq('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
|
||||
expect(key.toString()).to.eq('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
|
||||
|
||||
const key2 = new PublicKey('1111111111111111111111111111BukQL');
|
||||
expect(key2.toBase58()).to.eq('1111111111111111111111111111BukQL');
|
||||
expect(key2.toString()).to.eq('1111111111111111111111111111BukQL');
|
||||
|
||||
const key3 = new PublicKey('11111111111111111111111111111111');
|
||||
expect(key3.toBase58()).to.eq('11111111111111111111111111111111');
|
||||
|
||||
const key4 = new PublicKey([
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]);
|
||||
}).toThrow();
|
||||
expect(key4.toBase58()).to.eq('11111111111111111111111111111111');
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
new PublicKey(
|
||||
'0x300000000000000000000000000000000000000000000000000000000000000000000',
|
||||
it('toBuffer', () => {
|
||||
const key = new PublicKey('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
|
||||
expect(key.toBuffer()).to.have.length(32);
|
||||
expect(key.toBase58()).to.eq('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
|
||||
|
||||
const key2 = new PublicKey('11111111111111111111111111111111');
|
||||
expect(key2.toBuffer()).to.have.length(32);
|
||||
expect(key2.toBase58()).to.eq('11111111111111111111111111111111');
|
||||
|
||||
const key3 = new PublicKey(0);
|
||||
expect(key3.toBuffer()).to.have.length(32);
|
||||
expect(key3.toBase58()).to.eq('11111111111111111111111111111111');
|
||||
});
|
||||
|
||||
it('equals (II)', () => {
|
||||
const key1 = new PublicKey([
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
]);
|
||||
const key2 = new PublicKey(key1.toBuffer());
|
||||
|
||||
expect(key1.equals(key2)).to.be.true;
|
||||
});
|
||||
|
||||
it('createWithSeed', async () => {
|
||||
const defaultPublicKey = new PublicKey('11111111111111111111111111111111');
|
||||
const derivedKey = await PublicKey.createWithSeed(
|
||||
defaultPublicKey,
|
||||
'limber chicken: 4/45',
|
||||
defaultPublicKey,
|
||||
);
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
new PublicKey(
|
||||
'0x300000000000000000000000000000000000000000000000000000000000000',
|
||||
expect(
|
||||
derivedKey.equals(
|
||||
new PublicKey('9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq'),
|
||||
),
|
||||
).to.be.true;
|
||||
});
|
||||
|
||||
it('createProgramAddress', async () => {
|
||||
const programId = new PublicKey(
|
||||
'BPFLoader1111111111111111111111111111111111',
|
||||
);
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
new PublicKey(
|
||||
'135693854574979916511997248057056142015550763280047535983739356259273198796800000',
|
||||
const publicKey = new PublicKey(
|
||||
'SeedPubey1111111111111111111111111111111111',
|
||||
);
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
new PublicKey('12345');
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test('equals', () => {
|
||||
const arrayKey = new PublicKey([
|
||||
3,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]);
|
||||
const base58Key = new PublicKey(
|
||||
'CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3',
|
||||
);
|
||||
|
||||
expect(arrayKey.equals(base58Key)).toBe(true);
|
||||
});
|
||||
|
||||
test('toBase58', () => {
|
||||
const key = new PublicKey('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
|
||||
expect(key.toBase58()).toBe('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
|
||||
expect(key.toString()).toBe('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
|
||||
|
||||
const key2 = new PublicKey('1111111111111111111111111111BukQL');
|
||||
expect(key2.toBase58()).toBe('1111111111111111111111111111BukQL');
|
||||
expect(key2.toString()).toBe('1111111111111111111111111111BukQL');
|
||||
|
||||
const key3 = new PublicKey('11111111111111111111111111111111');
|
||||
expect(key3.toBase58()).toBe('11111111111111111111111111111111');
|
||||
|
||||
const key4 = new PublicKey([
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]);
|
||||
expect(key4.toBase58()).toBe('11111111111111111111111111111111');
|
||||
});
|
||||
|
||||
test('toBuffer', () => {
|
||||
const key = new PublicKey('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
|
||||
expect(key.toBuffer()).toHaveLength(32);
|
||||
expect(key.toBase58()).toBe('CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3');
|
||||
|
||||
const key2 = new PublicKey('11111111111111111111111111111111');
|
||||
expect(key2.toBuffer()).toHaveLength(32);
|
||||
expect(key2.toBase58()).toBe('11111111111111111111111111111111');
|
||||
|
||||
const key3 = new PublicKey(0);
|
||||
expect(key3.toBuffer()).toHaveLength(32);
|
||||
expect(key3.toBase58()).toBe('11111111111111111111111111111111');
|
||||
});
|
||||
|
||||
test('equals (II)', () => {
|
||||
const key1 = new PublicKey([
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
]);
|
||||
const key2 = new PublicKey(key1.toBuffer());
|
||||
|
||||
expect(key1.equals(key2)).toBe(true);
|
||||
});
|
||||
|
||||
test('createWithSeed', async () => {
|
||||
const defaultPublicKey = new PublicKey('11111111111111111111111111111111');
|
||||
const derivedKey = await PublicKey.createWithSeed(
|
||||
defaultPublicKey,
|
||||
'limber chicken: 4/45',
|
||||
defaultPublicKey,
|
||||
);
|
||||
|
||||
expect(
|
||||
derivedKey.equals(
|
||||
new PublicKey('9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq'),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('createProgramAddress', async () => {
|
||||
const programId = new PublicKey(
|
||||
'BPFLoader1111111111111111111111111111111111',
|
||||
);
|
||||
const publicKey = new PublicKey(
|
||||
'SeedPubey1111111111111111111111111111111111',
|
||||
);
|
||||
|
||||
let programAddress = await PublicKey.createProgramAddress(
|
||||
[Buffer.from('', 'utf8'), Buffer.from([1])],
|
||||
programId,
|
||||
);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
new PublicKey('3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT'),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
programAddress = await PublicKey.createProgramAddress(
|
||||
[Buffer.from('☉', 'utf8')],
|
||||
programId,
|
||||
);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
new PublicKey('7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7'),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
programAddress = await PublicKey.createProgramAddress(
|
||||
[Buffer.from('Talking', 'utf8'), Buffer.from('Squirrels', 'utf8')],
|
||||
programId,
|
||||
);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
new PublicKey('HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds'),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
programAddress = await PublicKey.createProgramAddress(
|
||||
[publicKey.toBuffer()],
|
||||
programId,
|
||||
);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
new PublicKey('GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K'),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
const programAddress2 = await PublicKey.createProgramAddress(
|
||||
[Buffer.from('Talking', 'utf8')],
|
||||
programId,
|
||||
);
|
||||
expect(programAddress.equals(programAddress2)).toBe(false);
|
||||
|
||||
await expect(
|
||||
PublicKey.createProgramAddress(
|
||||
[Buffer.alloc(MAX_SEED_LENGTH + 1)],
|
||||
let programAddress = await PublicKey.createProgramAddress(
|
||||
[Buffer.from('', 'utf8'), Buffer.from([1])],
|
||||
programId,
|
||||
),
|
||||
).rejects.toThrow('Max seed length exceeded');
|
||||
|
||||
// https://github.com/solana-labs/solana/issues/11950
|
||||
{
|
||||
let seeds = [
|
||||
new PublicKey('H4snTKK9adiU15gP22ErfZYtro3aqR9BTMXiH3AwiUTQ').toBuffer(),
|
||||
new BN(2).toArrayLike(Buffer, 'le', 8),
|
||||
];
|
||||
let programId = new PublicKey(
|
||||
'4ckmDgGdxQoPDLUkDT3vHgSAkzA3QRdNq5ywwY4sUSJn',
|
||||
);
|
||||
programAddress = await PublicKey.createProgramAddress(seeds, programId);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
new PublicKey('12rqwuEgBYiGhBrDJStCiqEtzQpTTiZbh7teNVLuYcFA'),
|
||||
new PublicKey('3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT'),
|
||||
),
|
||||
).toBe(true);
|
||||
}
|
||||
});
|
||||
).to.be.true;
|
||||
|
||||
test('findProgramAddress', async () => {
|
||||
const programId = new PublicKey(
|
||||
'BPFLoader1111111111111111111111111111111111',
|
||||
);
|
||||
let [programAddress, nonce] = await PublicKey.findProgramAddress(
|
||||
[Buffer.from('', 'utf8')],
|
||||
programId,
|
||||
);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
await PublicKey.createProgramAddress(
|
||||
[Buffer.from('', 'utf8'), Buffer.from([nonce])],
|
||||
programAddress = await PublicKey.createProgramAddress(
|
||||
[Buffer.from('☉', 'utf8')],
|
||||
programId,
|
||||
);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
new PublicKey('7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7'),
|
||||
),
|
||||
).to.be.true;
|
||||
|
||||
programAddress = await PublicKey.createProgramAddress(
|
||||
[Buffer.from('Talking', 'utf8'), Buffer.from('Squirrels', 'utf8')],
|
||||
programId,
|
||||
);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
new PublicKey('HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds'),
|
||||
),
|
||||
).to.be.true;
|
||||
|
||||
programAddress = await PublicKey.createProgramAddress(
|
||||
[publicKey.toBuffer()],
|
||||
programId,
|
||||
);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
new PublicKey('GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K'),
|
||||
),
|
||||
).to.be.true;
|
||||
|
||||
const programAddress2 = await PublicKey.createProgramAddress(
|
||||
[Buffer.from('Talking', 'utf8')],
|
||||
programId,
|
||||
);
|
||||
expect(programAddress.equals(programAddress2)).to.eq(false);
|
||||
|
||||
await expect(
|
||||
PublicKey.createProgramAddress(
|
||||
[Buffer.alloc(MAX_SEED_LENGTH + 1)],
|
||||
programId,
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
).to.be.rejectedWith('Max seed length exceeded');
|
||||
|
||||
// https://github.com/solana-labs/solana/issues/11950
|
||||
{
|
||||
let seeds = [
|
||||
new PublicKey(
|
||||
'H4snTKK9adiU15gP22ErfZYtro3aqR9BTMXiH3AwiUTQ',
|
||||
).toBuffer(),
|
||||
new BN(2).toArrayLike(Buffer, 'le', 8),
|
||||
];
|
||||
let programId = new PublicKey(
|
||||
'4ckmDgGdxQoPDLUkDT3vHgSAkzA3QRdNq5ywwY4sUSJn',
|
||||
);
|
||||
programAddress = await PublicKey.createProgramAddress(seeds, programId);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
new PublicKey('12rqwuEgBYiGhBrDJStCiqEtzQpTTiZbh7teNVLuYcFA'),
|
||||
),
|
||||
).to.be.true;
|
||||
}
|
||||
});
|
||||
|
||||
it('findProgramAddress', async () => {
|
||||
const programId = new PublicKey(
|
||||
'BPFLoader1111111111111111111111111111111111',
|
||||
);
|
||||
let [programAddress, nonce] = await PublicKey.findProgramAddress(
|
||||
[Buffer.from('', 'utf8')],
|
||||
programId,
|
||||
);
|
||||
expect(
|
||||
programAddress.equals(
|
||||
await PublicKey.createProgramAddress(
|
||||
[Buffer.from('', 'utf8'), Buffer.from([nonce])],
|
||||
programId,
|
||||
),
|
||||
),
|
||||
).to.be.true;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
import alias from '@rollup/plugin-alias';
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import flowRemoveTypes from 'flow-remove-types';
|
||||
import json from '@rollup/plugin-json';
|
||||
import multi from '@rollup/plugin-multi-entry';
|
||||
import nodeResolve from '@rollup/plugin-node-resolve';
|
||||
import nodePolyfills from 'rollup-plugin-node-polyfills';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
|
||||
export default {
|
||||
input: {
|
||||
// include: [
|
||||
// 'test/account.test.js',
|
||||
// 'test/cluster.test.js',
|
||||
// 'test/stake-program.test.js',
|
||||
// ],
|
||||
include: ['test/**/*.test.js'],
|
||||
exclude: ['test/agent-manager.test.js', 'test/bpf-loader.test.js'],
|
||||
},
|
||||
external: ['node-forge', 'http2', '_stream_wrap'],
|
||||
output: {
|
||||
file: 'test/dist/bundle.js',
|
||||
format: 'es',
|
||||
sourcemap: true,
|
||||
},
|
||||
plugins: [
|
||||
flow(),
|
||||
multi(),
|
||||
commonjs(),
|
||||
nodeResolve({
|
||||
browser: true,
|
||||
preferBuiltins: false,
|
||||
dedupe: ['bn.js', 'buffer'],
|
||||
}),
|
||||
babel({
|
||||
exclude: '**/node_modules/**',
|
||||
babelHelpers: 'runtime',
|
||||
plugins: ['@babel/plugin-transform-runtime'],
|
||||
}),
|
||||
nodePolyfills(),
|
||||
replace({
|
||||
'process.env.BROWSER': 'true',
|
||||
'process.env.TEST_LIVE': 'true',
|
||||
}),
|
||||
alias({
|
||||
entries: [
|
||||
{
|
||||
find: /^\.\.\/src\/.*\.js$/,
|
||||
replacement: './lib/index.browser.esm.js',
|
||||
},
|
||||
],
|
||||
}),
|
||||
json(),
|
||||
],
|
||||
onwarn: function (warning, rollupWarn) {
|
||||
if (warning.code !== 'CIRCULAR_DEPENDENCY' && warning.code !== 'EVAL') {
|
||||
rollupWarn(warning);
|
||||
}
|
||||
},
|
||||
treeshake: {
|
||||
moduleSideEffects: path => path.endsWith('test.js'),
|
||||
},
|
||||
};
|
||||
|
||||
// Using this instead of rollup-plugin-flow due to
|
||||
// https://github.com/leebyron/rollup-plugin-flow/issues/5
|
||||
function flow() {
|
||||
return {
|
||||
name: 'flow-remove-types',
|
||||
transform: code => ({
|
||||
code: flowRemoveTypes(code).toString(),
|
||||
map: null,
|
||||
}),
|
||||
};
|
||||
}
|
|
@ -1,88 +1,82 @@
|
|||
// @flow
|
||||
|
||||
import {keccak_256} from 'js-sha3';
|
||||
import secp256k1 from 'secp256k1';
|
||||
import {randomBytes} from 'crypto';
|
||||
import {Buffer} from 'buffer';
|
||||
import createKeccakHash from 'keccak';
|
||||
import {privateKeyVerify, ecdsaSign, publicKeyCreate} from 'secp256k1';
|
||||
|
||||
import {Secp256k1Program} from '../src/secp256k1-program';
|
||||
import {mockRpcEnabled} from './__mocks__/node-fetch';
|
||||
import {url} from './url';
|
||||
import {
|
||||
Connection,
|
||||
Account,
|
||||
sendAndConfirmTransaction,
|
||||
LAMPORTS_PER_SOL,
|
||||
Transaction,
|
||||
Secp256k1Program,
|
||||
} from '../src';
|
||||
import {url} from './url';
|
||||
import {helpers} from './mocks/rpc-http';
|
||||
|
||||
const {privateKeyVerify, ecdsaSign, publicKeyCreate} = secp256k1;
|
||||
const randomPrivateKey = () => {
|
||||
let privateKey;
|
||||
do {
|
||||
privateKey = new Account().secretKey.slice(0, 32);
|
||||
} while (!privateKeyVerify(privateKey));
|
||||
return privateKey;
|
||||
};
|
||||
|
||||
if (!mockRpcEnabled) {
|
||||
jest.setTimeout(20000);
|
||||
if (process.env.TEST_LIVE) {
|
||||
describe('secp256k1', () => {
|
||||
it('create secp256k1 instruction with public key', async () => {
|
||||
const privateKey = randomPrivateKey();
|
||||
const publicKey = publicKeyCreate(privateKey, false);
|
||||
const message = Buffer.from('This is a message');
|
||||
const messageHash = createKeccakHash('keccak256')
|
||||
.update(message)
|
||||
.digest();
|
||||
const {signature, recid: recoveryId} = ecdsaSign(messageHash, privateKey);
|
||||
const connection = new Connection(url, 'singleGossip');
|
||||
|
||||
const from = new Account();
|
||||
await connection.confirmTransaction(
|
||||
await connection.requestAirdrop(from.publicKey, 2 * LAMPORTS_PER_SOL),
|
||||
'singleGossip',
|
||||
);
|
||||
|
||||
const transaction = new Transaction().add(
|
||||
Secp256k1Program.createInstructionWithPublicKey({
|
||||
publicKey,
|
||||
message,
|
||||
signature,
|
||||
recoveryId,
|
||||
}),
|
||||
);
|
||||
|
||||
await sendAndConfirmTransaction(connection, transaction, [from], {
|
||||
commitment: 'singleGossip',
|
||||
preflightCommitment: 'singleGossip',
|
||||
});
|
||||
});
|
||||
|
||||
it('create secp256k1 instruction with private key', async () => {
|
||||
const privateKey = randomPrivateKey();
|
||||
const connection = new Connection(url, 'singleGossip');
|
||||
|
||||
const from = new Account();
|
||||
await connection.confirmTransaction(
|
||||
await connection.requestAirdrop(from.publicKey, 2 * LAMPORTS_PER_SOL),
|
||||
'singleGossip',
|
||||
);
|
||||
|
||||
const transaction = new Transaction().add(
|
||||
Secp256k1Program.createInstructionWithPrivateKey({
|
||||
privateKey,
|
||||
message: Buffer.from('Test 123'),
|
||||
}),
|
||||
);
|
||||
|
||||
await sendAndConfirmTransaction(connection, transaction, [from], {
|
||||
commitment: 'singleGossip',
|
||||
preflightCommitment: 'singleGossip',
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test('live create secp256k1 instruction with public key', async () => {
|
||||
if (mockRpcEnabled) {
|
||||
console.log('non-live test skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
const message = Buffer.from('This is a message');
|
||||
|
||||
let privateKey;
|
||||
do {
|
||||
privateKey = randomBytes(32);
|
||||
} while (!privateKeyVerify(privateKey));
|
||||
|
||||
const publicKey = publicKeyCreate(privateKey, false);
|
||||
const messageHash = Buffer.from(keccak_256.update(message).digest());
|
||||
const {signature, recid: recoveryId} = ecdsaSign(messageHash, privateKey);
|
||||
|
||||
const instruction = Secp256k1Program.createInstructionWithPublicKey({
|
||||
publicKey,
|
||||
message,
|
||||
signature,
|
||||
recoveryId,
|
||||
});
|
||||
|
||||
const transaction = new Transaction();
|
||||
transaction.add(instruction);
|
||||
|
||||
const connection = new Connection(url, 'recent');
|
||||
const from = new Account();
|
||||
await connection.requestAirdrop(from.publicKey, 2 * LAMPORTS_PER_SOL);
|
||||
|
||||
await sendAndConfirmTransaction(connection, transaction, [from], {
|
||||
commitment: 'single',
|
||||
skipPreflight: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('live create secp256k1 instruction with private key', async () => {
|
||||
if (mockRpcEnabled) {
|
||||
console.log('non-live test skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
let privateKey;
|
||||
do {
|
||||
privateKey = randomBytes(32);
|
||||
} while (!privateKeyVerify(privateKey));
|
||||
|
||||
const instruction = Secp256k1Program.createInstructionWithPrivateKey({
|
||||
privateKey,
|
||||
message: Buffer.from('Test 123'),
|
||||
});
|
||||
|
||||
const transaction = new Transaction();
|
||||
transaction.add(instruction);
|
||||
|
||||
const connection = new Connection(url, 'recent');
|
||||
const from = new Account();
|
||||
await connection.requestAirdrop(from.publicKey, 2 * LAMPORTS_PER_SOL);
|
||||
|
||||
await sendAndConfirmTransaction(connection, transaction, [from], {
|
||||
commitment: 'single',
|
||||
skipPreflight: true,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,38 +1,13 @@
|
|||
// @flow
|
||||
|
||||
import {expect} from 'chai';
|
||||
import {decodeLength, encodeLength} from '../src/util/shortvec-encoding';
|
||||
|
||||
function checkDecodedArray(array: Array<number>, expectedValue: number) {
|
||||
expect(decodeLength(array)).toEqual(expectedValue);
|
||||
expect(array.length).toEqual(0);
|
||||
expect(decodeLength(array)).to.eq(expectedValue);
|
||||
expect(array).to.have.length(0);
|
||||
}
|
||||
|
||||
test('shortvec decodeLength', () => {
|
||||
let array = [];
|
||||
checkDecodedArray(array, 0);
|
||||
|
||||
array = [5];
|
||||
checkDecodedArray(array, 5);
|
||||
|
||||
array = [0x7f];
|
||||
checkDecodedArray(array, 0x7f);
|
||||
|
||||
array = [0x80, 0x01];
|
||||
checkDecodedArray(array, 0x80);
|
||||
|
||||
array = [0xff, 0x01];
|
||||
checkDecodedArray(array, 0xff);
|
||||
|
||||
array = [0x80, 0x02];
|
||||
checkDecodedArray(array, 0x100);
|
||||
|
||||
array = [0xff, 0xff, 0x01];
|
||||
checkDecodedArray(array, 0x7fff);
|
||||
|
||||
array = [0x80, 0x80, 0x80, 0x01];
|
||||
checkDecodedArray(array, 0x200000);
|
||||
});
|
||||
|
||||
function checkEncodedArray(
|
||||
array: Array<number>,
|
||||
len: number,
|
||||
|
@ -41,31 +16,59 @@ function checkEncodedArray(
|
|||
expectedArray: Array<number>,
|
||||
) {
|
||||
encodeLength(array, len);
|
||||
expect(array.length).toEqual(prevLength);
|
||||
expect(array.slice(-addedLength)).toEqual(expectedArray);
|
||||
expect(array).to.have.length(prevLength);
|
||||
expect(array.slice(-addedLength)).to.eql(expectedArray);
|
||||
}
|
||||
|
||||
test('shortvec encodeLength', () => {
|
||||
let array = [];
|
||||
let prevLength = 1;
|
||||
checkEncodedArray(array, 0, prevLength, 1, [0]);
|
||||
describe('shortvec', () => {
|
||||
it('decodeLength', () => {
|
||||
let array = [];
|
||||
checkDecodedArray(array, 0);
|
||||
|
||||
checkEncodedArray(array, 5, (prevLength += 1), 1, [5]);
|
||||
array = [5];
|
||||
checkDecodedArray(array, 5);
|
||||
|
||||
checkEncodedArray(array, 0x7f, (prevLength += 1), 1, [0x7f]);
|
||||
array = [0x7f];
|
||||
checkDecodedArray(array, 0x7f);
|
||||
|
||||
checkEncodedArray(array, 0x80, (prevLength += 2), 2, [0x80, 0x01]);
|
||||
array = [0x80, 0x01];
|
||||
checkDecodedArray(array, 0x80);
|
||||
|
||||
checkEncodedArray(array, 0xff, (prevLength += 2), 2, [0xff, 0x01]);
|
||||
array = [0xff, 0x01];
|
||||
checkDecodedArray(array, 0xff);
|
||||
|
||||
checkEncodedArray(array, 0x100, (prevLength += 2), 2, [0x80, 0x02]);
|
||||
array = [0x80, 0x02];
|
||||
checkDecodedArray(array, 0x100);
|
||||
|
||||
checkEncodedArray(array, 0x7fff, (prevLength += 3), 3, [0xff, 0xff, 0x01]);
|
||||
array = [0xff, 0xff, 0x01];
|
||||
checkDecodedArray(array, 0x7fff);
|
||||
|
||||
prevLength = checkEncodedArray(array, 0x200000, (prevLength += 4), 4, [
|
||||
0x80,
|
||||
0x80,
|
||||
0x80,
|
||||
0x01,
|
||||
]);
|
||||
array = [0x80, 0x80, 0x80, 0x01];
|
||||
checkDecodedArray(array, 0x200000);
|
||||
});
|
||||
|
||||
it('encodeLength', () => {
|
||||
let array = [];
|
||||
let prevLength = 1;
|
||||
checkEncodedArray(array, 0, prevLength, 1, [0]);
|
||||
|
||||
checkEncodedArray(array, 5, (prevLength += 1), 1, [5]);
|
||||
|
||||
checkEncodedArray(array, 0x7f, (prevLength += 1), 1, [0x7f]);
|
||||
|
||||
checkEncodedArray(array, 0x80, (prevLength += 2), 2, [0x80, 0x01]);
|
||||
|
||||
checkEncodedArray(array, 0xff, (prevLength += 2), 2, [0xff, 0x01]);
|
||||
|
||||
checkEncodedArray(array, 0x100, (prevLength += 2), 2, [0x80, 0x02]);
|
||||
|
||||
checkEncodedArray(array, 0x7fff, (prevLength += 3), 3, [0xff, 0xff, 0x01]);
|
||||
|
||||
prevLength = checkEncodedArray(array, 0x200000, (prevLength += 4), 4, [
|
||||
0x80,
|
||||
0x80,
|
||||
0x80,
|
||||
0x01,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import {expect} from 'chai';
|
||||
|
||||
import {
|
||||
Account,
|
||||
Connection,
|
||||
|
@ -6,202 +9,136 @@ import {
|
|||
SystemProgram,
|
||||
LAMPORTS_PER_SOL,
|
||||
} from '../src';
|
||||
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
|
||||
import {mockGetRecentBlockhash} from './mockrpc/get-recent-blockhash';
|
||||
import {url} from './url';
|
||||
import {mockConfirmTransaction} from './mockrpc/confirm-transaction';
|
||||
import {MOCK_PORT, url} from './url';
|
||||
|
||||
if (!mockRpcEnabled) {
|
||||
// The default of 5 seconds is too slow for live testing sometimes
|
||||
jest.setTimeout(30000);
|
||||
}
|
||||
import {
|
||||
helpers,
|
||||
mockErrorMessage,
|
||||
mockErrorResponse,
|
||||
uniqueSignature,
|
||||
uniqueBlockhash,
|
||||
mockRpcResponse,
|
||||
mockServer,
|
||||
} from './mocks/rpc-http';
|
||||
import {
|
||||
stubRpcWebSocket,
|
||||
restoreRpcWebSocket,
|
||||
mockRpcMessage,
|
||||
} from './mocks/rpc-websockets';
|
||||
import base58 from 'bs58';
|
||||
|
||||
test('transaction-payer', async () => {
|
||||
const accountPayer = new Account();
|
||||
const accountFrom = new Account();
|
||||
const accountTo = new Account();
|
||||
const connection = new Connection(url, 'singleGossip');
|
||||
describe('Transaction Payer', () => {
|
||||
let connection: Connection;
|
||||
beforeEach(() => {
|
||||
connection = new Connection(url);
|
||||
});
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'getMinimumBalanceForRentExemption',
|
||||
params: [0, {commitment: 'singleGossip'}],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: 50,
|
||||
},
|
||||
]);
|
||||
if (!process.env.TEST_LIVE) {
|
||||
beforeEach(() => {
|
||||
mockServer.start(MOCK_PORT);
|
||||
stubRpcWebSocket(connection);
|
||||
});
|
||||
|
||||
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
|
||||
0,
|
||||
'singleGossip',
|
||||
);
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'requestAirdrop',
|
||||
params: [accountPayer.publicKey.toBase58(), LAMPORTS_PER_SOL],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result:
|
||||
'8WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
||||
},
|
||||
]);
|
||||
let signature = await connection.requestAirdrop(
|
||||
accountPayer.publicKey,
|
||||
LAMPORTS_PER_SOL,
|
||||
);
|
||||
mockConfirmTransaction(signature);
|
||||
await connection.confirmTransaction(signature, 'singleGossip');
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'requestAirdrop',
|
||||
params: [accountFrom.publicKey.toBase58(), minimumAmount + 12],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result:
|
||||
'8WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
||||
},
|
||||
]);
|
||||
signature = await connection.requestAirdrop(
|
||||
accountFrom.publicKey,
|
||||
minimumAmount + 12,
|
||||
);
|
||||
mockConfirmTransaction(signature);
|
||||
await connection.confirmTransaction(signature, 'singleGossip');
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'requestAirdrop',
|
||||
params: [accountTo.publicKey.toBase58(), minimumAmount + 21],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result:
|
||||
'8WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
||||
},
|
||||
]);
|
||||
signature = await connection.requestAirdrop(
|
||||
accountTo.publicKey,
|
||||
minimumAmount + 21,
|
||||
);
|
||||
mockConfirmTransaction(signature);
|
||||
await connection.confirmTransaction(signature, 'singleGossip');
|
||||
|
||||
mockGetRecentBlockhash('max');
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'sendTransaction',
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result:
|
||||
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
||||
},
|
||||
]);
|
||||
|
||||
const transaction = new Transaction().add(
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: accountFrom.publicKey,
|
||||
toPubkey: accountTo.publicKey,
|
||||
lamports: 10,
|
||||
}),
|
||||
);
|
||||
|
||||
signature = await connection.sendTransaction(
|
||||
transaction,
|
||||
[accountPayer, accountFrom],
|
||||
{skipPreflight: true},
|
||||
);
|
||||
|
||||
mockConfirmTransaction(signature);
|
||||
await connection.confirmTransaction(signature, 'singleGossip');
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
method: 'getSignatureStatuses',
|
||||
params: [
|
||||
[
|
||||
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: {
|
||||
context: {
|
||||
slot: 11,
|
||||
},
|
||||
value: [
|
||||
{
|
||||
slot: 0,
|
||||
confirmations: 11,
|
||||
status: {Ok: null},
|
||||
err: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
const {value} = await connection.getSignatureStatus(signature);
|
||||
if (value !== null) {
|
||||
expect(typeof value.slot).toEqual('number');
|
||||
expect(value.err).toBeNull();
|
||||
} else {
|
||||
expect(value).not.toBeNull();
|
||||
afterEach(() => {
|
||||
mockServer.stop();
|
||||
restoreRpcWebSocket(connection);
|
||||
});
|
||||
}
|
||||
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
it('transaction-payer', async () => {
|
||||
const accountPayer = new Account();
|
||||
const accountFrom = new Account();
|
||||
const accountTo = new Account();
|
||||
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: accountPayer.publicKey,
|
||||
amount: LAMPORTS_PER_SOL,
|
||||
});
|
||||
|
||||
await mockRpcResponse({
|
||||
method: 'getMinimumBalanceForRentExemption',
|
||||
params: [0],
|
||||
value: 50,
|
||||
});
|
||||
|
||||
const minimumAmount = await connection.getMinimumBalanceForRentExemption(0);
|
||||
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: accountFrom.publicKey,
|
||||
amount: minimumAmount + 12,
|
||||
});
|
||||
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: accountTo.publicKey,
|
||||
amount: minimumAmount + 21,
|
||||
});
|
||||
|
||||
const transaction = new Transaction().add(
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: accountFrom.publicKey,
|
||||
toPubkey: accountTo.publicKey,
|
||||
lamports: 10,
|
||||
}),
|
||||
);
|
||||
|
||||
await helpers.processTransaction({
|
||||
connection,
|
||||
transaction,
|
||||
signers: [accountPayer, accountFrom],
|
||||
commitment: 'singleGossip',
|
||||
});
|
||||
|
||||
const signature = base58.encode(transaction.signature);
|
||||
|
||||
await mockRpcResponse({
|
||||
method: 'getSignatureStatuses',
|
||||
params: [[signature]],
|
||||
value: [
|
||||
{
|
||||
slot: 0,
|
||||
confirmations: 11,
|
||||
status: {Ok: null},
|
||||
err: null,
|
||||
},
|
||||
],
|
||||
withContext: true,
|
||||
});
|
||||
const {value} = await connection.getSignatureStatus(signature);
|
||||
if (value !== null) {
|
||||
expect(typeof value.slot).to.eq('number');
|
||||
expect(value.err).to.be.null;
|
||||
} else {
|
||||
expect(value).not.to.be.null;
|
||||
}
|
||||
|
||||
await mockRpcResponse({
|
||||
method: 'getBalance',
|
||||
params: [accountPayer.publicKey.toBase58(), {commitment: 'singleGossip'}],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: {
|
||||
context: {
|
||||
slot: 11,
|
||||
},
|
||||
value: LAMPORTS_PER_SOL - 1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
value: LAMPORTS_PER_SOL - 1,
|
||||
withContext: true,
|
||||
});
|
||||
|
||||
// accountPayer should be less than LAMPORTS_PER_SOL as it paid for the transaction
|
||||
// (exact amount less depends on the current cluster fees)
|
||||
const balance = await connection.getBalance(accountPayer.publicKey);
|
||||
expect(balance).toBeGreaterThan(0);
|
||||
expect(balance).toBeLessThanOrEqual(LAMPORTS_PER_SOL);
|
||||
// accountPayer should be less than LAMPORTS_PER_SOL as it paid for the transaction
|
||||
// (exact amount less depends on the current cluster fees)
|
||||
const balance = await connection.getBalance(
|
||||
accountPayer.publicKey,
|
||||
'singleGossip',
|
||||
);
|
||||
expect(balance).to.be.greaterThan(0);
|
||||
expect(balance).to.be.at.most(LAMPORTS_PER_SOL);
|
||||
|
||||
// accountFrom should have exactly 2, since it didn't pay for the transaction
|
||||
mockRpc.push([
|
||||
url,
|
||||
{
|
||||
// accountFrom should have exactly 2, since it didn't pay for the transaction
|
||||
await mockRpcResponse({
|
||||
method: 'getBalance',
|
||||
params: [accountFrom.publicKey.toBase58(), {commitment: 'singleGossip'}],
|
||||
},
|
||||
{
|
||||
error: null,
|
||||
result: {
|
||||
context: {
|
||||
slot: 11,
|
||||
},
|
||||
value: minimumAmount + 2,
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(await connection.getBalance(accountFrom.publicKey)).toBe(
|
||||
minimumAmount + 2,
|
||||
);
|
||||
value: minimumAmount + 2,
|
||||
withContext: true,
|
||||
});
|
||||
expect(
|
||||
await connection.getBalance(accountFrom.publicKey, 'singleGossip'),
|
||||
).to.eq(minimumAmount + 2);
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,10 @@
|
|||
* The connection url to use when running unit tests against a live cluster
|
||||
*/
|
||||
|
||||
export const url = 'http://localhost:8899/';
|
||||
export const MOCK_PORT = 9999;
|
||||
export const url = process.env.TEST_LIVE
|
||||
? 'http://localhost:8899/'
|
||||
: 'http://localhost:9999/';
|
||||
|
||||
//export const url = 'https://devnet.solana.com/';
|
||||
//export const url = 'http://devnet.solana.com/';
|
||||
|
|
|
@ -1,36 +1,40 @@
|
|||
// @flow
|
||||
import {Buffer} from 'buffer';
|
||||
import {expect} from 'chai';
|
||||
import nacl from 'tweetnacl';
|
||||
|
||||
import {PublicKey} from '../src/publickey';
|
||||
import {ValidatorInfo} from '../src/validator-info';
|
||||
|
||||
test('from config account data', () => {
|
||||
const keypair = nacl.sign.keyPair.fromSeed(
|
||||
Uint8Array.from(Array(32).fill(8)),
|
||||
);
|
||||
describe('ValidatorInfo', () => {
|
||||
it('from config account data', () => {
|
||||
const keypair = nacl.sign.keyPair.fromSeed(
|
||||
Uint8Array.from(Array(32).fill(8)),
|
||||
);
|
||||
|
||||
const expectedValidatorInfo = new ValidatorInfo(
|
||||
new PublicKey(keypair.publicKey),
|
||||
{
|
||||
name: 'Validator',
|
||||
keybaseUsername: 'validator_id',
|
||||
},
|
||||
);
|
||||
const expectedValidatorInfo = new ValidatorInfo(
|
||||
new PublicKey(keypair.publicKey),
|
||||
{
|
||||
name: 'Validator',
|
||||
keybaseUsername: 'validator_id',
|
||||
},
|
||||
);
|
||||
|
||||
// Config data string steps:
|
||||
// 1) Generate a keypair
|
||||
// 2) Airdrop lamports to the account
|
||||
// 3) Modify the `solana-validator-info` tool
|
||||
// a) Remove the keybase id verification step
|
||||
// b) Print base64 account data in the `get --all` codepath
|
||||
// c) Add `println!("Account data: {:?}", base64::encode(&account.data));`
|
||||
// 4) Use modified `solana-validator-info` tool to publish validator info
|
||||
// 5) And then use it again to fetch the data! (feel free to trim some A's)
|
||||
const configData = Buffer.from(
|
||||
'AgdRlwF0SPKsXcI8nrx6x4wKJyV6xhRFjeCk8W+AAAAAABOY9ixtGkV8UbpqS189vS9p/KkyFiGNyJl+QWvRfZPKATUAAAAAAAAAeyJrZXliYXNlVXNlcm5hbWUiOiJ2YWxpZGF0b3JfaWQiLCJuYW1lIjoiVmFsaWRhdG9yIn0',
|
||||
'base64',
|
||||
);
|
||||
const info = ValidatorInfo.fromConfigData(configData);
|
||||
// Config data string steps:
|
||||
// 1) Generate a keypair
|
||||
// 2) Airdrop lamports to the account
|
||||
// 3) Modify the `solana-validator-info` tool
|
||||
// a) Remove the keybase id verification step
|
||||
// b) Print base64 account data in the `get --all` codepath
|
||||
// c) Add `println!("Account data: {:?}", base64::encode(&account.data));`
|
||||
// 4) Use modified `solana-validator-info` tool to publish validator info
|
||||
// 5) And then use it again to fetch the data! (feel free to trim some A's)
|
||||
const configData = Buffer.from(
|
||||
'AgdRlwF0SPKsXcI8nrx6x4wKJyV6xhRFjeCk8W+AAAAAABOY9ixtGkV8UbpqS189vS9p/KkyFiGNyJl+QWvRfZPKATUAAAAAAAAAeyJrZXliYXNlVXNlcm5hbWUiOiJ2YWxpZGF0b3JfaWQiLCJuYW1lIjoiVmFsaWRhdG9yIn0',
|
||||
'base64',
|
||||
);
|
||||
const info = ValidatorInfo.fromConfigData(configData);
|
||||
|
||||
expect(info).toEqual(expectedValidatorInfo);
|
||||
expect(info).to.eql(expectedValidatorInfo);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,68 +1,68 @@
|
|||
// @flow
|
||||
import bs58 from 'bs58';
|
||||
import {Buffer} from 'buffer';
|
||||
import {expect, use} from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
|
||||
import {Connection} from '../src';
|
||||
import {url} from './url';
|
||||
import {mockRpcEnabled} from './__mocks__/node-fetch';
|
||||
import {sleep} from '../src/util/sleep';
|
||||
|
||||
describe('websocket', () => {
|
||||
if (mockRpcEnabled) {
|
||||
test('no-op', () => {});
|
||||
console.log('non-live test skipped');
|
||||
return;
|
||||
}
|
||||
use(chaiAsPromised);
|
||||
|
||||
const connection = new Connection(url);
|
||||
if (process.env.TEST_LIVE) {
|
||||
describe('websocket', () => {
|
||||
const connection = new Connection(url);
|
||||
|
||||
test('connect and disconnect', async () => {
|
||||
const testSignature = bs58.encode(Buffer.alloc(64));
|
||||
const id = connection.onSignature(testSignature, () => {});
|
||||
it('connect and disconnect', async () => {
|
||||
const testSignature = bs58.encode(Buffer.alloc(64));
|
||||
const id = connection.onSignature(testSignature, () => {});
|
||||
|
||||
// wait for websocket to connect
|
||||
await sleep(100);
|
||||
expect(connection._rpcWebSocketConnected).toBe(true);
|
||||
expect(connection._rpcWebSocketHeartbeat).not.toBe(null);
|
||||
// wait for websocket to connect
|
||||
await sleep(100);
|
||||
expect(connection._rpcWebSocketConnected).to.be.true;
|
||||
expect(connection._rpcWebSocketHeartbeat).not.to.eq(null);
|
||||
|
||||
// test if socket is open
|
||||
await connection._rpcWebSocket.notify('ping');
|
||||
// test if socket is open
|
||||
await connection._rpcWebSocket.notify('ping');
|
||||
|
||||
await connection.removeSignatureListener(id);
|
||||
expect(connection._rpcWebSocketConnected).toBe(false);
|
||||
expect(connection._rpcWebSocketHeartbeat).not.toBe(null);
|
||||
expect(connection._rpcWebSocketIdleTimeout).not.toBe(null);
|
||||
await connection.removeSignatureListener(id);
|
||||
expect(connection._rpcWebSocketConnected).to.eq(false);
|
||||
expect(connection._rpcWebSocketHeartbeat).not.to.eq(null);
|
||||
expect(connection._rpcWebSocketIdleTimeout).not.to.eq(null);
|
||||
|
||||
// wait for websocket to disconnect
|
||||
await sleep(1100);
|
||||
expect(connection._rpcWebSocketConnected).toBe(false);
|
||||
expect(connection._rpcWebSocketHeartbeat).toBe(null);
|
||||
expect(connection._rpcWebSocketIdleTimeout).toBe(null);
|
||||
// wait for websocket to disconnect
|
||||
await sleep(1100);
|
||||
expect(connection._rpcWebSocketConnected).to.eq(false);
|
||||
expect(connection._rpcWebSocketHeartbeat).to.eq(null);
|
||||
expect(connection._rpcWebSocketIdleTimeout).to.eq(null);
|
||||
|
||||
// test if socket is closed
|
||||
await expect(connection._rpcWebSocket.notify('ping')).rejects.toThrow(
|
||||
'socket not ready',
|
||||
);
|
||||
// test if socket is closed
|
||||
await expect(connection._rpcWebSocket.notify('ping')).to.be.rejectedWith(
|
||||
'socket not ready',
|
||||
);
|
||||
});
|
||||
|
||||
it('idle timeout', async () => {
|
||||
const testSignature = bs58.encode(Buffer.alloc(64));
|
||||
const id = connection.onSignature(testSignature, () => {});
|
||||
|
||||
// wait for websocket to connect
|
||||
await sleep(100);
|
||||
expect(connection._rpcWebSocketIdleTimeout).to.eq(null);
|
||||
|
||||
await connection.removeSignatureListener(id);
|
||||
expect(connection._rpcWebSocketIdleTimeout).not.to.eq(null);
|
||||
|
||||
const nextId = connection.onSignature(testSignature, () => {});
|
||||
expect(connection._rpcWebSocketIdleTimeout).to.eq(null);
|
||||
|
||||
await connection.removeSignatureListener(nextId);
|
||||
expect(connection._rpcWebSocketIdleTimeout).not.to.eq(null);
|
||||
|
||||
// wait for websocket to disconnect
|
||||
await sleep(1100);
|
||||
expect(connection._rpcWebSocketIdleTimeout).to.eq(null);
|
||||
});
|
||||
});
|
||||
|
||||
test('idle timeout', async () => {
|
||||
const testSignature = bs58.encode(Buffer.alloc(64));
|
||||
const id = connection.onSignature(testSignature, () => {});
|
||||
|
||||
// wait for websocket to connect
|
||||
await sleep(100);
|
||||
expect(connection._rpcWebSocketIdleTimeout).toBe(null);
|
||||
|
||||
await connection.removeSignatureListener(id);
|
||||
expect(connection._rpcWebSocketIdleTimeout).not.toBe(null);
|
||||
|
||||
const nextId = connection.onSignature(testSignature, () => {});
|
||||
expect(connection._rpcWebSocketIdleTimeout).toBe(null);
|
||||
|
||||
await connection.removeSignatureListener(nextId);
|
||||
expect(connection._rpcWebSocketIdleTimeout).not.toBe(null);
|
||||
|
||||
// wait for websocket to disconnect
|
||||
await sleep(1100);
|
||||
expect(connection._rpcWebSocketIdleTimeout).toBe(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue