Full JSON-RPC docs + sync tests. (#4335)

* Extended Markdown generator

* Synced and extended all JSON-RPC interfaces

* Fix linter errors

* Format `parity_listAccounts` output in API

* typo

* Check if interfaces are synced in JS spec tests

* Fixing missing interface errors

* Better #[rpc] attribute parsing

* Fixed RPC JS spec tests

* More examples

* Refactored how dummy data appears in examples

* Complete trace docs!

* fix typo

* Less copy-paste

* All the docs!

* Fix differences between CallRequest and TransactionRequest

* Fix differences between CallRequest and TransactionRequest

* Missing examples

* Grumble fixes
This commit is contained in:
Maciej Hirsz 2017-02-01 10:58:09 +01:00 committed by Jaco Greeff
parent b2ecf1c5a4
commit ed09a76c91
17 changed files with 791 additions and 299 deletions

View File

@ -17,9 +17,9 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import chalk from 'chalk'; import chalk from 'chalk';
import { isPlainObject } from 'lodash';
import { DUMMY } from '../src/jsonrpc/helpers'; import { Dummy } from '../src/jsonrpc/helpers';
import { BlockNumber } from '../src/jsonrpc/types';
import interfaces from '../src/jsonrpc'; import interfaces from '../src/jsonrpc';
const ROOT_DIR = path.join(__dirname, '../docs'); const ROOT_DIR = path.join(__dirname, '../docs');
@ -28,10 +28,6 @@ if (!fs.existsSync(ROOT_DIR)) {
fs.mkdirSync(ROOT_DIR); fs.mkdirSync(ROOT_DIR);
} }
const type2print = new WeakMap();
type2print.set(BlockNumber, 'Quantity|Tag');
// INFO Logging helper // INFO Logging helper
function info (log) { function info (log) {
console.log(chalk.blue(`INFO:\t${log}`)); console.log(chalk.blue(`INFO:\t${log}`));
@ -48,24 +44,26 @@ function error (log) {
} }
function printType (type) { function printType (type) {
return type2print.get(type) || type.name; return type.print || `\`${type.name}\``;
} }
function formatDescription (obj, prefix = '', indent = '') { function formatDescription (obj, prefix = '', indent = '') {
const optional = obj.optional ? '(optional) ' : ''; const optional = obj.optional ? '(optional) ' : '';
const defaults = obj.default ? `(default: \`${obj.default}\`) ` : ''; const defaults = obj.default ? `(default: \`${obj.default}\`) ` : '';
return `${indent}${prefix}\`${printType(obj.type)}\` - ${optional}${defaults}${obj.desc}`; return `${indent}${prefix}${printType(obj.type)} - ${optional}${defaults}${obj.desc}`;
} }
function formatType (obj) { function formatType (obj) {
if (obj == null) { if (obj == null || obj.type == null) {
return obj; return obj;
} }
if (obj.type === Object && obj.details) { const details = obj.details || obj.type.details;
const sub = Object.keys(obj.details).map((key) => {
return formatDescription(obj.details[key], `\`${key}\`: `, ' - '); if (details) {
const sub = Object.keys(details).map((key) => {
return formatDescription(details[key], `\`${key}\`: `, ' - ');
}).join('\n'); }).join('\n');
return `${formatDescription(obj)}\n${sub}`; return `${formatDescription(obj)}\n${sub}`;
@ -83,18 +81,9 @@ const rpcReqTemplate = {
jsonrpc: '2.0' jsonrpc: '2.0'
}; };
// Checks if the value passed in is a DUMMY object placeholder for `{ ... }`` const { isDummy } = Dummy;
function isDummy (val) {
return val === DUMMY;
}
const { isArray } = Array; const { isArray } = Array;
// Checks if the value passed is a plain old JS object
function isObject (val) {
return val != null && val.constructor === Object;
}
// Checks if a field definition has an example, // Checks if a field definition has an example,
// or describes an object with fields that recursively have examples of their own, // or describes an object with fields that recursively have examples of their own,
// or is optional. // or is optional.
@ -143,8 +132,8 @@ function getExample (obj) {
function stringifyExample (example, dent = '') { function stringifyExample (example, dent = '') {
const indent = `${dent} `; const indent = `${dent} `;
if (example === DUMMY) { if (isDummy(example)) {
return '{ ... }'; return example.toString();
} }
if (isArray(example)) { if (isArray(example)) {
@ -153,13 +142,13 @@ function stringifyExample (example, dent = '') {
// If all elements are dummies, print out a single line. // If all elements are dummies, print out a single line.
// Also covers empty arrays. // Also covers empty arrays.
if (example.every(isDummy)) { if (example.every(isDummy)) {
const dummies = example.map(_ => '{ ... }'); const dummies = example.map(d => d.toString());
return `[${dummies.join(', ')}]`; return `[${dummies.join(', ')}]`;
} }
// For arrays containing just one object or string, don't unwind the array to multiline // For arrays containing just one object or string, don't unwind the array to multiline
if (last === 0 && (isObject(example[0]) || typeof example[0] === 'string')) { if (last === 0 && (isPlainObject(example[0]) || typeof example[0] === 'string')) {
return `[${stringifyExample(example[0], dent)}]`; return `[${stringifyExample(example[0], dent)}]`;
} }
@ -173,7 +162,7 @@ function stringifyExample (example, dent = '') {
return `[\n${indent}${elements.join(`\n${indent}`)}\n${dent}]`; return `[\n${indent}${elements.join(`\n${indent}`)}\n${dent}]`;
} }
if (isObject(example)) { if (isPlainObject(example)) {
const keys = Object.keys(example); const keys = Object.keys(example);
const last = keys.length - 1; const last = keys.length - 1;
@ -193,7 +182,7 @@ function stringifyExample (example, dent = '') {
return `{\n${indent}${elements.join(`\n${indent}`)}\n${dent}}`; return `{\n${indent}${elements.join(`\n${indent}`)}\n${dent}}`;
} }
return JSON.stringify(example); // .replace(/"\$DUMMY\$"/g, '{ ... }'); return JSON.stringify(example);
} }
function buildExample (name, method) { function buildExample (name, method) {
@ -217,7 +206,7 @@ function buildExample (name, method) {
if (hasReqExample) { if (hasReqExample) {
const params = getExample(method.params); const params = getExample(method.params);
const req = JSON.stringify(Object.assign({}, rpcReqTemplate, { method: name, params })).replace(/"\$DUMMY\$"/g, '{ ... }'); const req = Dummy.stringifyJSON(Object.assign({}, rpcReqTemplate, { method: name, params }));
examples.push(`Request\n\`\`\`bash\ncurl --data '${req}' -H "Content-Type: application/json" -X POST localhost:8545\n\`\`\``); examples.push(`Request\n\`\`\`bash\ncurl --data '${req}' -H "Content-Type: application/json" -X POST localhost:8545\n\`\`\``);
} else { } else {
@ -250,7 +239,7 @@ function buildParameters (params) {
let md = `0. ${params.map(formatType).join('\n0. ')}`; let md = `0. ${params.map(formatType).join('\n0. ')}`;
if (params.length > 0 && params.every(hasExample) && params[0].example !== DUMMY) { if (params.length > 0 && params.every(hasExample) && !isDummy(params[0].example)) {
const example = getExample(params); const example = getExample(params);
md = `${md}\n\n\`\`\`js\nparams: ${stringifyExample(example)}\n\`\`\``; md = `${md}\n\n\`\`\`js\nparams: ${stringifyExample(example)}\n\`\`\``;
@ -292,7 +281,15 @@ Object.keys(interfaces).sort().forEach((group) => {
const tocMain = []; const tocMain = [];
const tocSections = {}; const tocSections = {};
Object.keys(spec).sort().forEach((iname) => { // Comparator that will sort by sections first, names second
function methodComparator (a, b) {
const sectionA = spec[a].section || '';
const sectionB = spec[b].section || '';
return sectionA.localeCompare(sectionB) || a.localeCompare(b);
}
Object.keys(spec).sort(methodComparator).forEach((iname) => {
const method = spec[iname]; const method = spec[iname];
const name = `${group.replace(/_.*$/, '')}_${iname}`; const name = `${group.replace(/_.*$/, '')}_${iname}`;

View File

@ -76,6 +76,11 @@ export default class Parity {
.execute('parity_dappsInterface'); .execute('parity_dappsInterface');
} }
decryptMessage (address, data) {
return this._transport
.execute('parity_decryptMessage', inAddress(address), inHex(data));
}
defaultExtraData () { defaultExtraData () {
return this._transport return this._transport
.execute('parity_defaultExtraData'); .execute('parity_defaultExtraData');
@ -292,6 +297,11 @@ export default class Parity {
.then(outAddress); .then(outAddress);
} }
postSign (address, hash) {
return this._transport
.execute('parity_postSign', inAddress(address), inHex(hash));
}
postTransaction (options) { postTransaction (options) {
return this._transport return this._transport
.execute('parity_postTransaction', inOptions(options)); .execute('parity_postTransaction', inOptions(options));

View File

@ -32,6 +32,11 @@ export default class Signer {
.execute('signer_confirmRequestRaw', inNumber16(requestId), inData(data)); .execute('signer_confirmRequestRaw', inNumber16(requestId), inData(data));
} }
confirmRequestWithToken (requestId, options, password) {
return this._transport
.execute('signer_confirmRequestWithToken', inNumber16(requestId), inOptions(options), password);
}
generateAuthorizationToken () { generateAuthorizationToken () {
return this._transport return this._transport
.execute('signer_generateAuthorizationToken'); .execute('signer_generateAuthorizationToken');

View File

@ -14,8 +14,38 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
// Placeholders for objects with undefined fields, will show up in docs as `{ ... }` // A dummy placeholder object that will stringify literally to anything
export const DUMMY = '$DUMMY$'; // in the example source.
//
// { {
// foo: new Dummy('{ ... }') -------> "foo": { ... }
// } {
//
export class Dummy {
constructor (value) {
this.value = value;
}
toString () {
return this.value;
}
toJSON () {
return `##${this.value}##`;
}
static fixJSON (json) {
return json.replace(/"##([^#]+)##"/g, '$1');
}
static isDummy (obj) {
return obj instanceof Dummy;
}
static stringifyJSON (any) {
return Dummy.fixJSON(JSON.stringify(any));
}
}
// Enrich the API spec by additional markdown-formatted preamble // Enrich the API spec by additional markdown-formatted preamble
export function withPreamble (preamble, spec) { export function withPreamble (preamble, spec) {

View File

@ -14,37 +14,104 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import fs from 'fs';
import path from 'path';
import interfaces from './'; import interfaces from './';
import { Address, BlockNumber, Data, Hash, Integer, Quantity } from './types'; import * as customTypes from './types';
const flatlist = {}; const allowedTypes = [Array, Boolean, Object, String].concat(Object.values(customTypes));
function verifyType (obj) { function verifyType (obj) {
if (typeof obj !== 'string') { if (typeof obj !== 'string') {
expect(obj).to.satisfy(() => { expect(obj).to.satisfy(() => allowedTypes.includes(obj.type));
return obj.type === Array ||
obj.type === Boolean ||
obj.type === Object ||
obj.type === String ||
obj.type === Address ||
obj.type === BlockNumber ||
obj.type === Data ||
obj.type === Hash ||
obj.type === Integer ||
obj.type === Quantity;
});
} }
} }
// Get a list of JSON-RPC from Rust trait source code
function parseMethodsFromRust (source) {
// Matching the custom `rpc` attribute with it's doc comment
const attributePattern = /((?:\s*\/\/\/.*$)*)\s*#\[rpc\(([^)]+)\)]/gm;
const commentPattern = /\s*\/\/\/\s*/g;
const separatorPattern = /\s*,\s*/g;
const assignPattern = /([\S]+)\s*=\s*"([^"]*)"/;
const ignorePattern = /@(ignore|deprecated|unimplemented|alias)\b/i;
const methods = [];
source.toString().replace(attributePattern, (match, comment, props) => {
comment = comment.replace(commentPattern, '\n').trim();
// Skip deprecated methods
if (ignorePattern.test(comment)) {
return match;
}
props.split(separatorPattern).forEach((prop) => {
const [, key, value] = prop.split(assignPattern) || [];
if (key === 'name' && value != null) {
methods.push(value);
}
});
return match;
});
return methods;
}
// Get a list of all JSON-RPC methods from all defined traits
function getMethodsFromRustTraits () {
const traitsDir = path.join(__dirname, '../../../rpc/src/v1/traits');
return fs.readdirSync(traitsDir)
.filter((name) => name !== 'mod.rs' && /\.rs$/.test(name))
.map((name) => fs.readFileSync(path.join(traitsDir, name)))
.map(parseMethodsFromRust)
.reduce((a, b) => a.concat(b));
}
const rustMethods = {};
getMethodsFromRustTraits().sort().forEach((method) => {
const [group, name] = method.split('_');
// Skip methods with malformed names
if (group == null || name == null) {
return;
}
rustMethods[group] = rustMethods[group] || {};
rustMethods[group][name] = true;
});
describe('jsonrpc/interfaces', () => { describe('jsonrpc/interfaces', () => {
describe('Rust trait methods', () => {
Object.keys(rustMethods).forEach((group) => {
describe(group, () => {
Object.keys(rustMethods[group]).forEach((name) => {
describe(name, () => {
it('has a defined JS interface', () => {
expect(interfaces[group][name]).to.exist;
});
});
});
});
});
});
Object.keys(interfaces).forEach((group) => { Object.keys(interfaces).forEach((group) => {
describe(group, () => { describe(group, () => {
Object.keys(interfaces[group]).forEach((name) => { Object.keys(interfaces[group]).forEach((name) => {
const method = interfaces[group][name]; const method = interfaces[group][name];
flatlist[`${group}_${name}`] = true;
describe(name, () => { describe(name, () => {
if (!method.nodoc) {
it('is present in Rust codebase', () => {
expect(rustMethods[group][name]).to.exist;
});
}
it('has the correct interface', () => { it('has the correct interface', () => {
expect(method.desc).to.be.a('string'); expect(method.desc).to.be.a('string');
expect(method.params).to.be.an('array'); expect(method.params).to.be.an('array');

View File

@ -18,6 +18,7 @@ import { Data } from '../types';
export default { export default {
getHex: { getHex: {
nodoc: 'Not present in Rust code',
desc: 'Returns binary data from the local database.', desc: 'Returns binary data from the local database.',
params: [ params: [
{ {
@ -37,6 +38,7 @@ export default {
}, },
getString: { getString: {
nodoc: 'Not present in Rust code',
desc: 'Returns string from the local database.', desc: 'Returns string from the local database.',
params: [ params: [
{ {
@ -56,6 +58,7 @@ export default {
}, },
putHex: { putHex: {
nodoc: 'Not present in Rust code',
desc: 'Stores binary data in the local database.', desc: 'Stores binary data in the local database.',
params: [ params: [
{ {
@ -79,6 +82,7 @@ export default {
}, },
putString: { putString: {
nodoc: 'Not present in Rust code',
desc: 'Stores a string in the local database.', desc: 'Stores a string in the local database.',
params: [ params: [
{ {

View File

@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { Address, BlockNumber, Data, Hash, Quantity } from '../types'; import { Address, BlockNumber, Data, Hash, Quantity, CallRequest, TransactionRequest } from '../types';
import { withPreamble, fromDecimal, withComment, DUMMY } from '../helpers'; import { withPreamble, fromDecimal, withComment, Dummy } from '../helpers';
export default withPreamble(` export default withPreamble(`
@ -64,40 +64,9 @@ The following options are possible for the \`defaultBlock\` parameter:
desc: 'Executes a new message call immediately without creating a transaction on the block chain.', desc: 'Executes a new message call immediately without creating a transaction on the block chain.',
params: [ params: [
{ {
type: Object, type: CallRequest,
desc: 'The transaction call object.', desc: 'The transaction call object.',
format: 'inputCallFormatter', format: 'inputCallFormatter',
details: {
from: {
type: Address,
desc: '20 Bytes - The address the transaction is send from.',
optional: true
},
to: {
type: Address,
desc: '20 Bytes - The address the transaction is directed to.'
},
gas: {
type: Quantity,
desc: 'Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.',
optional: true
},
gasPrice: {
type: Quantity,
desc: 'Integer of the gasPrice used for each paid gas.',
optional: true
},
value: {
type: Quantity,
desc: 'Integer of the value sent with this transaction.',
optional: true
},
data: {
type: Data,
desc: '4 byte hash of the method signature followed by encoded parameters. For details see [Ethereum Contract ABI](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI).',
optional: true
}
},
example: { example: {
from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1', from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b', to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
@ -180,10 +149,10 @@ The following options are possible for the \`defaultBlock\` parameter:
desc: 'Makes a call or transaction, which won\'t be added to the blockchain and returns the used gas, which can be used for estimating the used gas.', desc: 'Makes a call or transaction, which won\'t be added to the blockchain and returns the used gas, which can be used for estimating the used gas.',
params: [ params: [
{ {
type: Object, type: CallRequest,
desc: 'See [eth_call](#eth_call) parameters, expect that all properties are optional.', desc: 'Same as [eth_call](#eth_call) parameters, except that all properties are optional.',
format: 'inputCallFormatter', format: 'inputCallFormatter',
example: DUMMY // will be replaced with { ... } by the generator example: new Dummy('{ ... }')
}, },
{ {
type: BlockNumber, type: BlockNumber,
@ -368,7 +337,7 @@ The following options are possible for the \`defaultBlock\` parameter:
minGasPrice: fromDecimal(653145), minGasPrice: fromDecimal(653145),
gasUsed: fromDecimal(653145), gasUsed: fromDecimal(653145),
timestamp: fromDecimal(1424182926), timestamp: fromDecimal(1424182926),
transactions: [DUMMY, DUMMY], // will be replaced with [{ ... }, { ... }] by the generator transactions: [new Dummy('{ ... }, { ... }, ...')],
uncles: ['0x1606e5...', '0xd5145a9...'] uncles: ['0x1606e5...', '0xd5145a9...']
} }
} }
@ -479,7 +448,7 @@ The following options are possible for the \`defaultBlock\` parameter:
data: '0x0000000000000000000000000000000000000000000000000000000000000000', data: '0x0000000000000000000000000000000000000000000000000000000000000000',
topics: ['0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5'] topics: ['0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5']
}, },
DUMMY // will be replaced with { ... } by the generator new Dummy('...')
] ]
} }
}, },
@ -762,10 +731,7 @@ The following options are possible for the \`defaultBlock\` parameter:
cumulativeGasUsed: fromDecimal(13244), cumulativeGasUsed: fromDecimal(13244),
gasUsed: fromDecimal(1244), gasUsed: fromDecimal(1244),
contractAddress: withComment('0xb60e8dd61c5d32be8058bb8eb970870f07233155', 'or null, if none was created'), contractAddress: withComment('0xb60e8dd61c5d32be8058bb8eb970870f07233155', 'or null, if none was created'),
logs: withComment( logs: withComment([new Dummy('{ ... }, { ... }, ...]')], 'logs as returned by eth_getFilterLogs, etc.')
[DUMMY, DUMMY], // will be replaced with [{ ... }, { ... }] by the generator
'logs as returned by eth_getFilterLogs, etc.'
)
} }
} }
}, },
@ -1040,45 +1006,9 @@ The following options are possible for the \`defaultBlock\` parameter:
desc: 'Creates new message call transaction or a contract creation, if the data field contains code.', desc: 'Creates new message call transaction or a contract creation, if the data field contains code.',
params: [ params: [
{ {
type: Object, type: TransactionRequest,
desc: 'The transaction object.', desc: 'The transaction object.',
format: 'inputTransactionFormatter', format: 'inputTransactionFormatter',
details: {
from: {
type: Address,
desc: '20 Bytes - The address the transaction is send from.'
},
to: {
type: Address,
desc: '20 Bytes - (optional when creating new contract) The address the transaction is directed to.'
},
gas: {
type: Quantity,
desc: 'Integer of the gas provided for the transaction execution. It will return unused gas.',
optional: true,
default: 90000
},
gasPrice: {
type: Quantity,
desc: 'Integer of the gasPrice used for each paid gas.',
optional: true,
default: 'To-Be-Determined'
},
value: {
type: Quantity,
desc: 'Integer of the value sent with this transaction.',
optional: true
},
data: {
type: Data,
desc: 'The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see [Ethereum Contract ABI](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI)'
},
nonce: {
type: Quantity,
desc: 'Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce.',
optional: true
}
},
example: { example: {
from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155', from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155',
to: '0xd46e8dd67c5d32be8058bb8eb970870f072445675', to: '0xd46e8dd67c5d32be8058bb8eb970870f072445675',
@ -1122,10 +1052,10 @@ The following options are possible for the \`defaultBlock\` parameter:
desc: 'Signs transactions without dispatching it to the network. It can be later submitted using [eth_sendRawTransaction](#eth_sendrawtransaction).', desc: 'Signs transactions without dispatching it to the network. It can be later submitted using [eth_sendRawTransaction](#eth_sendrawtransaction).',
params: [ params: [
{ {
type: Object, type: TransactionRequest,
desc: 'see [eth_sendTransaction](#eth_sendTransaction).', desc: 'Transaction object, see [eth_sendTransaction](#eth_sendTransaction).',
format: 'inputCallFormatter', format: 'inputCallFormatter',
example: DUMMY // will be replaced with { ... } by the generator example: new Dummy('{ ... }')
} }
], ],
returns: { returns: {

View File

@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { Address, Data, Hash, Quantity, BlockNumber } from '../types'; import { Address, Data, Hash, Quantity, BlockNumber, TransactionRequest } from '../types';
import { fromDecimal, withComment, DUMMY } from '../helpers'; import { fromDecimal, withComment, Dummy } from '../helpers';
const SECTION_MINING = 'Block Authoring (aka "mining")'; const SECTION_MINING = 'Block Authoring (aka "mining")';
const SECTION_DEV = 'Development'; const SECTION_DEV = 'Development';
@ -23,6 +23,9 @@ const SECTION_NODE = 'Node Settings';
const SECTION_NET = 'Network Information'; const SECTION_NET = 'Network Information';
const SECTION_ACCOUNTS = 'Accounts (read-only) and Signatures'; const SECTION_ACCOUNTS = 'Accounts (read-only) and Signatures';
const SUBDOC_SET = 'set';
const SUBDOC_ACCOUNTS = 'accounts';
const transactionDetails = { const transactionDetails = {
hash: { hash: {
type: Hash, type: Hash,
@ -332,7 +335,7 @@ export default {
transactionIndex: null transactionIndex: null
} }
}, },
'0x...': DUMMY '0x...': new Dummy('{ ... }')
} }
} }
}, },
@ -400,7 +403,7 @@ export default {
active: 0, active: 0,
connected: 25, connected: 25,
max: 25, max: 25,
peers: [DUMMY, DUMMY, DUMMY, DUMMY] peers: [new Dummy('{ ... }, { ... }, { ... }, ...')]
} }
} }
}, },
@ -475,8 +478,8 @@ export default {
v: '0x26', v: '0x26',
value: '0x0' value: '0x0'
}, },
DUMMY, new Dummy('{ ... }'),
DUMMY new Dummy('{ ... }')
] ]
} }
}, },
@ -769,8 +772,7 @@ export default {
s: '0x6bf770ab08119e67dc29817e1412a0e3086f43da308c314db1b3bca9fb6d32bd', s: '0x6bf770ab08119e67dc29817e1412a0e3086f43da308c314db1b3bca9fb6d32bd',
minBlock: null minBlock: null
}, },
DUMMY, new Dummy('{ ... }, { ... }, ...')
DUMMY
] ]
} }
}, },
@ -780,7 +782,7 @@ export default {
* ================================ * ================================
*/ */
allAccountsInfo: { allAccountsInfo: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'returns a map of accounts as an object.', desc: 'returns a map of accounts as an object.',
params: [], params: [],
returns: { returns: {
@ -811,7 +813,7 @@ export default {
}, },
newAccountFromPhrase: { newAccountFromPhrase: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Creates a new account from a recovery phrase.', desc: 'Creates a new account from a recovery phrase.',
params: [ params: [
{ {
@ -833,7 +835,7 @@ export default {
}, },
newAccountFromSecret: { newAccountFromSecret: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Creates a new account from a private ethstore secret key.', desc: 'Creates a new account from a private ethstore secret key.',
params: [ params: [
{ {
@ -855,26 +857,29 @@ export default {
}, },
newAccountFromWallet: { newAccountFromWallet: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Creates a new account from a JSON import', desc: 'Creates a new account from a JSON import',
params: [ params: [
{ {
type: String, type: String,
desc: 'JSON' desc: 'Wallet JSON encoded to a string.',
example: '{"id": "9c62e86b-3cf9...", ...}'
}, },
{ {
type: String, type: String,
desc: 'Password' desc: 'Password.',
example: 'hunter2'
} }
], ],
returns: { returns: {
type: Address, type: Address,
desc: 'The created address' desc: 'The created address',
example: '0x407d73d8a49eeb85d32cf465507dd71d507100c1'
} }
}, },
setAccountName: { setAccountName: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Sets a name for the account', desc: 'Sets a name for the account',
params: [ params: [
{ {
@ -896,7 +901,7 @@ export default {
}, },
setAccountMeta: { setAccountMeta: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Sets metadata for the account', desc: 'Sets metadata for the account',
params: [ params: [
{ {
@ -918,7 +923,7 @@ export default {
}, },
testPassword: { testPassword: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Checks if a given password can unlock a given account, without actually unlocking it.', desc: 'Checks if a given password can unlock a given account, without actually unlocking it.',
params: [ params: [
{ {
@ -940,7 +945,7 @@ export default {
}, },
changePassword: { changePassword: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Change the password for a given account.', desc: 'Change the password for a given account.',
params: [ params: [
{ {
@ -967,7 +972,7 @@ export default {
}, },
killAccount: { killAccount: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Deletes an account.', desc: 'Deletes an account.',
params: [ params: [
{ {
@ -989,7 +994,7 @@ export default {
}, },
removeAddress: { removeAddress: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Removes an address from the addressbook.', desc: 'Removes an address from the addressbook.',
params: [ params: [
{ {
@ -1006,7 +1011,7 @@ export default {
}, },
setDappsAddresses: { setDappsAddresses: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Sets the available addresses for a dapp.', desc: 'Sets the available addresses for a dapp.',
params: [ params: [
{ {
@ -1028,7 +1033,7 @@ export default {
}, },
getDappsAddresses: { getDappsAddresses: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Returns the list of accounts available to a specific dapp.', desc: 'Returns the list of accounts available to a specific dapp.',
params: [ params: [
{ {
@ -1045,7 +1050,7 @@ export default {
}, },
setNewDappsWhitelist: { setNewDappsWhitelist: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Sets the list of accounts available to new dapps.', desc: 'Sets the list of accounts available to new dapps.',
params: [ params: [
{ {
@ -1062,7 +1067,7 @@ export default {
}, },
getNewDappsWhitelist: { getNewDappsWhitelist: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Returns the list of accounts available to a new dapps.', desc: 'Returns the list of accounts available to a new dapps.',
params: [], params: [],
returns: { returns: {
@ -1073,7 +1078,7 @@ export default {
}, },
listRecentDapps: { listRecentDapps: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Returns a list of the most recent active dapps.', desc: 'Returns a list of the most recent active dapps.',
params: [], params: [],
returns: { returns: {
@ -1084,7 +1089,7 @@ export default {
}, },
importGethAccounts: { importGethAccounts: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Imports a list of accounts from Geth.', desc: 'Imports a list of accounts from Geth.',
params: [ params: [
{ {
@ -1099,7 +1104,7 @@ export default {
}, },
listGethAccounts: { listGethAccounts: {
subdoc: 'accounts', subdoc: SUBDOC_ACCOUNTS,
desc: 'Returns a list of the accounts available from Geth.', desc: 'Returns a list of the accounts available from Geth.',
params: [], params: [],
returns: { returns: {
@ -1113,7 +1118,7 @@ export default {
* =========================== * ===========================
*/ */
setMinGasPrice: { setMinGasPrice: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Changes minimal gas price for transaction to be accepted to the queue.', desc: 'Changes minimal gas price for transaction to be accepted to the queue.',
params: [ params: [
{ {
@ -1131,7 +1136,7 @@ export default {
}, },
setGasFloorTarget: { setGasFloorTarget: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Sets a new gas floor target for mined blocks..', desc: 'Sets a new gas floor target for mined blocks..',
params: [ params: [
{ {
@ -1149,7 +1154,7 @@ export default {
}, },
setGasCeilTarget: { setGasCeilTarget: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Sets new gas ceiling target for mined blocks.', desc: 'Sets new gas ceiling target for mined blocks.',
params: [ params: [
{ {
@ -1167,7 +1172,7 @@ export default {
}, },
setExtraData: { setExtraData: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Changes extra data for newly mined blocks', desc: 'Changes extra data for newly mined blocks',
params: [ params: [
{ {
@ -1185,7 +1190,7 @@ export default {
}, },
setAuthor: { setAuthor: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Changes author (coinbase) for mined blocks.', desc: 'Changes author (coinbase) for mined blocks.',
params: [ params: [
{ {
@ -1203,7 +1208,7 @@ export default {
}, },
setMaxTransactionGas: { setMaxTransactionGas: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Sets the maximum amount of gas a single transaction may consume.', desc: 'Sets the maximum amount of gas a single transaction may consume.',
params: [ params: [
{ {
@ -1221,7 +1226,7 @@ export default {
}, },
setTransactionsLimit: { setTransactionsLimit: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Changes limit for transactions in queue.', desc: 'Changes limit for transactions in queue.',
params: [ params: [
{ {
@ -1239,7 +1244,7 @@ export default {
}, },
addReservedPeer: { addReservedPeer: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Add a reserved peer.', desc: 'Add a reserved peer.',
params: [ params: [
{ {
@ -1256,7 +1261,7 @@ export default {
}, },
removeReservedPeer: { removeReservedPeer: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Remove a reserved peer.', desc: 'Remove a reserved peer.',
params: [ params: [
{ {
@ -1273,7 +1278,7 @@ export default {
}, },
dropNonReservedPeers: { dropNonReservedPeers: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Set Parity to drop all non-reserved peers. To restore default behavior call [parity_acceptNonReservedPeers](#parity_acceptnonreservedpeers).', desc: 'Set Parity to drop all non-reserved peers. To restore default behavior call [parity_acceptNonReservedPeers](#parity_acceptnonreservedpeers).',
params: [], params: [],
returns: { returns: {
@ -1284,7 +1289,7 @@ export default {
}, },
acceptNonReservedPeers: { acceptNonReservedPeers: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Set Parity to accept non-reserved peers (default behavior).', desc: 'Set Parity to accept non-reserved peers (default behavior).',
params: [], params: [],
returns: { returns: {
@ -1295,7 +1300,7 @@ export default {
}, },
hashContent: { hashContent: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Creates a hash of a file at a given URL.', desc: 'Creates a hash of a file at a given URL.',
params: [ params: [
{ {
@ -1312,7 +1317,7 @@ export default {
}, },
setMode: { setMode: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Changes the operating mode of Parity.', desc: 'Changes the operating mode of Parity.',
params: [ params: [
{ {
@ -1329,7 +1334,7 @@ export default {
}, },
setEngineSigner: { setEngineSigner: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Sets an authority account for signing consensus messages. For more information check the [[Proof of Authority Chains]] page.', desc: 'Sets an authority account for signing consensus messages. For more information check the [[Proof of Authority Chains]] page.',
params: [ params: [
{ {
@ -1351,7 +1356,7 @@ export default {
}, },
upgradeReady: { upgradeReady: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Returns a ReleaseInfo object describing the release which is available for upgrade or `null` if none is available.', desc: 'Returns a ReleaseInfo object describing the release which is available for upgrade or `null` if none is available.',
params: [], params: [],
returns: { returns: {
@ -1381,7 +1386,7 @@ export default {
}, },
executeUpgrade: { executeUpgrade: {
subdoc: 'set', subdoc: SUBDOC_SET,
desc: 'Attempts to upgrade Parity to the version specified in [parity_upgradeReady](#parity_upgradeready).', desc: 'Attempts to upgrade Parity to the version specified in [parity_upgradeReady](#parity_upgradeready).',
params: [], params: [],
returns: { returns: {
@ -1389,5 +1394,92 @@ export default {
desc: 'returns `true` if the upgrade to the new release was successfully executed, `false` if not.', desc: 'returns `true` if the upgrade to the new release was successfully executed, `false` if not.',
example: true example: true
} }
},
/*
* `parity_signing` trait methods (rolled into `parity` module)
* ============================================================
*/
postSign: {
section: SECTION_ACCOUNTS,
desc: 'Request an arbitrary transaction to be signed by an account.',
params: [
{
type: Address,
desc: 'Account address.',
example: '0xb60e8dd61c5d32be8058bb8eb970870f07233155'
},
{
type: Hash,
desc: 'Transaction hash.',
example: '0x8cda01991ae267a539135736132f1f987e76868ce0269b7537d3aab37b7b185e'
}
],
returns: {
type: Quantity,
desc: 'The id of the request to the signer. If the account was already unlocked, returns `Hash` of the transaction instead.',
example: '0x1'
}
},
postTransaction: {
section: SECTION_ACCOUNTS,
desc: 'Posts a transaction to the signer without waiting for the signer response.',
params: [
{
type: TransactionRequest,
desc: 'see [`eth_sendTransaction`](JSONRPC-eth-module#eth_sendtransaction).',
format: 'inputCallFormatter',
example: {
from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155',
to: '0xd46e8dd67c5d32be8058bb8eb970870f072445675',
value: fromDecimal(2441406250)
}
}
],
returns: {
type: Quantity,
desc: 'The id of the request to the signer. If the account was already unlocked, returns `Hash` of the transaction instead.',
format: 'utils.toDecimal',
example: '0x1'
}
},
checkRequest: {
section: SECTION_ACCOUNTS,
desc: 'Get the the transaction hash of the request previously posted to [`parity_postTransaction`](#parity_posttransaction) or [`parity_postSign`](#parity_postsign). Will return a JSON-RPC error if the request was rejected.',
params: [
{
type: Quantity,
desc: 'The id of the request sent to the signer.',
example: '0x1'
}
],
returns: {
type: Hash,
desc: '32 Bytes - the transaction hash or `null` if the request hasn\'t been signed yet.',
example: '0xde8dfd9642f7eeef12402f2a560dbf40921b4f0bda01fb84709b9d71f6c181be'
}
},
decryptMessage: {
desc: 'Decrypt a message encrypted with a ECIES public key.',
params: [
{
type: Address,
desc: 'Account which can decrypt the message.',
example: '0x00a329c0648769a73afac7f9381e08fb43dbea72'
},
{
type: Data,
desc: 'Encrypted message.',
example: '0x0405afee7fa2ab3e48c27b00d543389270cb7267fc191ca1311f297255a83cbe8d77a4ba135b51560700a582924fa86d2b19029fcb50d2b68d60a7df1ba81df317a19c8def117f2b9cf8c2618be0e3f146a5272fb9e5528719d2d7a1bd91fa620901cffa756305c79c093e7af30fa3c1587029421351c34a7c1e5a2b'
}
],
returns: {
type: Data,
desc: 'Decrypted message.',
example: withComment('0x68656c6c6f20776f726c64', 'hello world')
}
} }
}; };

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { Address, Data, Quantity } from '../types'; import { Address, Data, Quantity, TransactionRequest } from '../types';
export default { export default {
listAccounts: { listAccounts: {
@ -50,44 +50,8 @@ export default {
desc: 'Sends transaction and signs it in a single call. The account does not need to be unlocked to make this call, and will not be left unlocked after.', desc: 'Sends transaction and signs it in a single call. The account does not need to be unlocked to make this call, and will not be left unlocked after.',
params: [ params: [
{ {
type: Object, type: TransactionRequest,
desc: 'The transaction object', desc: 'The transaction object',
details: {
from: {
type: Address,
desc: '20 Bytes - The address of the account to unlock and send the transaction from.'
},
to: {
type: Address,
desc: '20 Bytes - (optional when creating new contract) The address the transaction is directed to.'
},
gas: {
type: Quantity,
desc: 'Integer of the gas provided for the transaction execution. It will return unused gas.',
optional: true,
default: 90000
},
gasPrice: {
type: Quantity,
desc: 'Integer of the gasPrice used for each paid gas.',
optional: true,
default: 'To-Be-Determined'
},
value: {
type: Quantity,
desc: 'Integer of the value send with this transaction.',
optional: true
},
data: {
type: Data,
desc: 'The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see [Ethereum Contract ABI](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI).'
},
nonce: {
type: Quantity,
desc: 'Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce.',
optional: true
}
},
example: { example: {
from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1', from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b', to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',

View File

@ -18,6 +18,7 @@ import { Data, Quantity } from '../types';
export default { export default {
version: { version: {
nodoc: 'Not present in Rust code',
desc: 'Returns the current whisper protocol version.', desc: 'Returns the current whisper protocol version.',
params: [], params: [],
returns: { returns: {
@ -27,6 +28,7 @@ export default {
}, },
post: { post: {
nodoc: 'Not present in Rust code',
desc: 'Sends a whisper message.', desc: 'Sends a whisper message.',
params: [ params: [
{ {
@ -62,6 +64,7 @@ export default {
}, },
newIdentity: { newIdentity: {
nodoc: 'Not present in Rust code',
desc: 'Creates new whisper identity in the client.', desc: 'Creates new whisper identity in the client.',
params: [], params: [],
returns: { returns: {
@ -71,6 +74,7 @@ export default {
}, },
hasIdentity: { hasIdentity: {
nodoc: 'Not present in Rust code',
desc: 'Checks if the client hold the private keys for a given identity.', desc: 'Checks if the client hold the private keys for a given identity.',
params: [ params: [
{ {
@ -85,6 +89,7 @@ export default {
}, },
newGroup: { newGroup: {
nodoc: 'Not present in Rust code',
desc: '(?)', desc: '(?)',
params: [], params: [],
returns: { returns: {
@ -93,6 +98,7 @@ export default {
}, },
addToGroup: { addToGroup: {
nodoc: 'Not present in Rust code',
desc: '(?)', desc: '(?)',
params: [ params: [
{ {
@ -107,6 +113,7 @@ export default {
}, },
newFilter: { newFilter: {
nodoc: 'Not present in Rust code',
desc: 'Creates filter to notify, when client receives whisper message matching the filter options.', desc: 'Creates filter to notify, when client receives whisper message matching the filter options.',
params: [ params: [
{ {
@ -129,6 +136,7 @@ export default {
}, },
uninstallFilter: { uninstallFilter: {
nodoc: 'Not present in Rust code',
desc: 'Uninstalls a filter with given id. Should always be called when watch is no longer needed.\nAdditonally Filters timeout when they aren\'t requested with [shh_getFilterChanges](#shh_getfilterchanges) for a period of time.', desc: 'Uninstalls a filter with given id. Should always be called when watch is no longer needed.\nAdditonally Filters timeout when they aren\'t requested with [shh_getFilterChanges](#shh_getfilterchanges) for a period of time.',
params: [ params: [
{ {
@ -143,6 +151,7 @@ export default {
}, },
getFilterChanges: { getFilterChanges: {
nodoc: 'Not present in Rust code',
desc: 'Polling method for whisper filters. Returns new messages since the last call of this method.\n**Note** calling the [shh_getMessages](#shh_getmessages) method, will reset the buffer for this method, so that you won\'t receive duplicate messages.', desc: 'Polling method for whisper filters. Returns new messages since the last call of this method.\n**Note** calling the [shh_getMessages](#shh_getmessages) method, will reset the buffer for this method, so that you won\'t receive duplicate messages.',
params: [ params: [
{ {
@ -157,6 +166,7 @@ export default {
}, },
getMessages: { getMessages: {
nodoc: 'Not present in Rust code',
desc: 'Get all messages matching a filter. Unlike `shh_getFilterChanges` this returns all messages.', desc: 'Get all messages matching a filter. Unlike `shh_getFilterChanges` this returns all messages.',
params: [ params: [
{ {

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { Quantity, Data, BlockNumber } from '../types'; import { Quantity, Data, BlockNumber } from '../types';
import { fromDecimal } from '../helpers'; import { fromDecimal, Dummy } from '../helpers';
export default { export default {
generateAuthorizationToken: { generateAuthorizationToken: {
@ -45,7 +45,7 @@ export default {
// TODO: Types of the fields of transaction objects? Link to a transaction object in another page? // TODO: Types of the fields of transaction objects? Link to a transaction object in another page?
type: Array, type: Array,
desc: 'A list of the outstanding transactions.', desc: 'A list of the outstanding transactions.',
example: [] example: new Dummy('[ ... ]')
} }
}, },
@ -86,9 +86,9 @@ export default {
} }
], ],
returns: { returns: {
type: Boolean, type: Object,
desc: 'The status of the confirmation', desc: 'The status of the confirmation, depending on the request type.',
example: true example: {}
} }
}, },
@ -107,9 +107,65 @@ export default {
} }
], ],
returns: { returns: {
type: Boolean, type: Object,
desc: 'The status of the confirmation', desc: 'The status of the confirmation, depending on the request type.',
example: true example: {}
}
},
confirmRequestWithToken: {
desc: 'Confirm specific request with token.',
params: [
{
type: Quantity,
desc: 'The request id.',
example: fromDecimal(1)
},
{
type: Object,
desc: 'Modify the transaction before confirmation.',
details: {
gasPrice: {
type: Quantity,
desc: 'Modify the gas price provided by the sender in Wei.',
optional: true
},
gas: {
type: Quantity,
desc: 'Gas provided by the sender in Wei.',
optional: true
},
minBlock: {
type: BlockNumber,
desc: 'Integer block number, or the string `\'latest\'`, `\'earliest\'` or `\'pending\'`. Request will not be propagated till the given block is reached.',
optional: true
}
},
example: {}
},
{
type: String,
desc: 'Password.',
example: 'hunter2'
}
],
returns: {
type: Object,
desc: 'Status.',
details: {
result: {
type: Object,
desc: 'The status of the confirmation, depending on the request type.'
},
token: {
type: String,
desc: 'Token used to authenticate the request.'
}
},
example: {
result: new Dummy('{ ... }'),
token: 'cAF2w5LE7XUZ3v3N'
}
} }
}, },
@ -130,6 +186,7 @@ export default {
}, },
signerEnabled: { signerEnabled: {
nodoc: 'Not present in Rust code',
desc: 'Returns whether signer is enabled/disabled.', desc: 'Returns whether signer is enabled/disabled.',
params: [], params: [],
returns: { returns: {

View File

@ -14,124 +14,363 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { BlockNumber, Data, Hash, Integer } from '../types'; import { Address, BlockNumber, Data, Hash, CallRequest } from '../types';
import { withPreamble, Dummy, fromDecimal } from '../helpers';
export default { const SECTION_FILTERING = 'Transaction-Trace Filtering';
const SECTION_ADHOC = 'Ad-hoc Tracing';
export default withPreamble(`
The trace module is for getting a deeper insight into transaction processing.
It includes two sets of calls; the transaction trace filtering API and the ad-hoc tracing API.
**Note:** In order to use these API Parity must be fully synced with flags \`$ parity --tracing on\`.
## The Ad-hoc Tracing API
The ad-hoc tracing API allows you to perform a number of different diagnostics on calls or transactions,
either historical ones from the chain or hypothetical ones not yet mined. The diagnostics include:
- \`trace\` **Transaction trace**. An equivalent trace to that in the previous section.
- \`vmTrace\` **Virtual Machine execution trace**. Provides a full trace of the VM's state throughout the execution of the transaction, including for any subcalls.
- \`stateDiff\` **State difference**. Provides information detailing all altered portions of the Ethereum state made due to the execution of the transaction.
There are three means of providing a transaction to execute; either providing the same information as when making
a call using \`eth_call\` (see \`trace_call\`), through providing raw, signed, transaction data as when using
\`eth_sendRawTransaction\` (see \`trace_rawTransaction\`) or simply a transaction hash for a previously mined
transaction (see \`trace_replayTransaction\`). In the latter case, your node must be in archive mode or the
transaction should be within the most recent 1000 blocks.
## The Transaction-Trace Filtering API
These APIs allow you to get a full *externality* trace on any transaction executed throughout the Parity chain.
Unlike the log filtering API, you are able to search and filter based only upon address information.
Information returned includes the execution of all \`CREATE\`s, \`SUICIDE\`s and all variants of \`CALL\` together
with input data, output data, gas usage, amount transferred and the success status of each individual action.
### \`traceAddress\` field
The \`traceAddress\` field of all returned traces, gives the exact location in the call trace [index in root,
index in first \`CALL\`, index in second \`CALL\`, ...].
i.e. if the trace is:
\`\`\`
A
CALLs B
CALLs G
CALLs C
CALLs G
\`\`\`
then it should look something like:
\`[ {A: []}, {B: [0]}, {G: [0, 0]}, {C: [1]}, {G: [1, 0]} ]\`
`, {
block: { block: {
desc: 'Returns traces created at given block', section: SECTION_FILTERING,
desc: 'Returns traces created at given block.',
params: [ params: [
{ {
type: BlockNumber, type: BlockNumber,
desc: 'Integer block number, or \'latest\' for the last mined block or \'pending\', \'earliest\' for not yet mined transactions' desc: 'Integer of a block number, or the string `\'earliest\'`, `\'latest\'` or `\'pending\'`.',
example: fromDecimal(3068185)
} }
], ],
returns: { returns: {
type: Array, type: Array,
desc: 'Block traces' desc: 'Block traces.',
} example: [
}, {
action: {
call: { callType: 'call',
desc: 'Returns traces for a specific call', from: '0xaa7b131dc60b80d3cf5e59b5a21a666aa039c951',
params: [ gas: '0x0',
{ input: '0x',
type: Object, to: '0xd40aba8166a212d6892125f079c33e6f5ca19814',
desc: 'Call options' value: '0x4768d7effc3fbe'
}, },
{ blockHash: '0x7eb25504e4c202cf3d62fd585d3e238f592c780cca82dacb2ed3cb5b38883add',
type: BlockNumber, blockNumber: 3068185,
desc: 'The blockNumber' result: {
}, gasUsed: '0x0',
{ output: '0x'
type: Array, },
desc: 'Type of trace, one or more of \'vmTrace\', \'trace\' and/or \'stateDiff\'' subtraces: 0,
} traceAddress: [],
], transactionHash: '0x07da28d752aba3b9dd7060005e554719c6205c8a3aea358599fc9b245c52f1f6',
returns: { transactionPosition: 0,
type: Array, type: 'call'
desc: 'Block traces' },
new Dummy('...')
]
} }
}, },
filter: { filter: {
section: SECTION_FILTERING,
desc: 'Returns traces matching given filter', desc: 'Returns traces matching given filter',
params: [ params: [
{ {
type: Object, type: Object,
desc: 'The filter object' desc: 'The filter object',
details: {
fromBlock: {
type: BlockNumber,
desc: 'From this block.',
optional: true
},
toBlock: {
type: BlockNumber,
desc: 'To this block.',
optional: true
},
fromAddress: {
type: Array,
desc: 'Sent from these addresses.',
optional: true
},
toAddress: {
type: Address,
desc: 'Sent to these addresses.',
optional: true
}
},
example: {
fromBlock: fromDecimal(3068100),
toBlock: fromDecimal(3068200),
toAddress: ['0x8bbB73BCB5d553B5A556358d27625323Fd781D37']
}
} }
], ],
returns: { returns: {
type: Array, type: Array,
desc: 'Traces matching given filter' desc: 'Traces matching given filter',
example: [
{
action: {
callType: 'call',
from: '0x32be343b94f860124dc4fee278fdcbd38c102d88',
gas: '0x4c40d',
input: '0x',
to: '0x8bbb73bcb5d553b5a556358d27625323fd781d37',
value: '0x3f0650ec47fd240000'
},
blockHash: '0x86df301bcdd8248d982dbf039f09faf792684e1aeee99d5b58b77d620008b80f',
blockNumber: 3068183,
result: {
gasUsed: '0x0',
output: '0x'
},
subtraces: 0,
traceAddress: [],
transactionHash: '0x3321a7708b1083130bd78da0d62ead9f6683033231617c9d268e2c7e3fa6c104',
transactionPosition: 3,
type: 'call'
},
new Dummy('...')
]
} }
}, },
get: { get: {
section: SECTION_FILTERING,
desc: 'Returns trace at given position.', desc: 'Returns trace at given position.',
params: [ params: [
{ {
type: Hash, type: Hash,
desc: 'Transaction hash' desc: 'Transaction hash.',
example: '0x17104ac9d3312d8c136b7f44d4b8b47852618065ebfa534bd2d3b5ef218ca1f3'
}, },
{ {
type: Integer, type: Array,
desc: 'Trace position witing transaction' desc: 'Index positions of the traces.',
example: ['0x0']
} }
], ],
returns: { returns: {
type: Object, type: Object,
desc: 'Trace object' desc: 'Trace object',
} example: {
}, action: {
callType: 'call',
rawTransaction: { from: '0x1c39ba39e4735cb65978d4db400ddd70a72dc750',
desc: 'Traces a call to eth_sendRawTransaction without making the call, returning the traces', gas: '0x13e99',
params: [ input: '0x16c72721',
{ to: '0x2bd2326c993dfaef84f696526064ff22eba5b362',
type: Data, value: '0x0'
desc: 'Transaction data' },
}, blockHash: '0x7eb25504e4c202cf3d62fd585d3e238f592c780cca82dacb2ed3cb5b38883add',
{ blockNumber: 3068185,
type: Array, result: {
desc: 'Type of trace, one or more of \'vmTrace\', \'trace\' and/or \'stateDiff\'' gasUsed: '0x183',
output: '0x0000000000000000000000000000000000000000000000000000000000000001'
},
subtraces: 0,
traceAddress: [0],
transactionHash: '0x17104ac9d3312d8c136b7f44d4b8b47852618065ebfa534bd2d3b5ef218ca1f3',
transactionPosition: 2,
type: 'call'
} }
],
returns: {
type: Array,
desc: 'Block traces'
}
},
replayTransaction: {
desc: 'Replays a transaction, returning the traces',
params: [
{
type: Hash,
desc: 'Transaction hash'
},
{
type: Array,
desc: 'Type of trace, one or more of \'vmTrace\', \'trace\' and/or \'stateDiff\''
}
],
returns: {
type: Array,
desc: 'Block traces'
} }
}, },
transaction: { transaction: {
section: SECTION_FILTERING,
desc: 'Returns all traces of given transaction', desc: 'Returns all traces of given transaction',
params: [ params: [
{ {
type: Hash, type: Hash,
desc: 'Transaction hash' desc: 'Transaction hash',
example: '0x17104ac9d3312d8c136b7f44d4b8b47852618065ebfa534bd2d3b5ef218ca1f3'
} }
], ],
returns: { returns: {
type: Array, type: Array,
desc: 'Traces of given transaction' desc: 'Traces of given transaction',
example: [
{
action: {
callType: 'call',
from: '0x1c39ba39e4735cb65978d4db400ddd70a72dc750',
gas: '0x13e99',
input: '0x16c72721',
to: '0x2bd2326c993dfaef84f696526064ff22eba5b362',
value: '0x0'
},
blockHash: '0x7eb25504e4c202cf3d62fd585d3e238f592c780cca82dacb2ed3cb5b38883add',
blockNumber: 3068185,
result: {
gasUsed: '0x183',
output: '0x0000000000000000000000000000000000000000000000000000000000000001'
},
subtraces: 0,
traceAddress: [0],
transactionHash: '0x17104ac9d3312d8c136b7f44d4b8b47852618065ebfa534bd2d3b5ef218ca1f3',
transactionPosition: 2,
type: 'call'
},
new Dummy('...')
]
}
},
call: {
section: SECTION_ADHOC,
desc: 'Executes the given call and returns a number of possible traces for it.',
params: [
{
type: CallRequest,
desc: 'Call options, same as `eth_call`.',
example: new Dummy('{ ... }')
},
{
type: Array,
desc: 'Type of trace, one or more of: `"vmTrace"`, `"trace"`, `"stateDiff"`.',
example: ['trace']
},
{
type: BlockNumber,
optional: true,
desc: 'Integer of a block number, or the string `\'earliest\'`, `\'latest\'` or `\'pending\'`.'
}
],
returns: {
type: Array,
desc: 'Block traces',
example: {
output: '0x',
stateDiff: null,
trace: [
{
action: new Dummy('{ ... }'),
result: {
gasUsed: '0x0',
output: '0x'
},
subtraces: 0,
traceAddress: [],
type: 'call'
}
],
vmTrace: null
}
}
},
rawTransaction: {
section: SECTION_ADHOC,
desc: 'Traces a call to `eth_sendRawTransaction` without making the call, returning the traces',
params: [
{
type: Data,
desc: 'Raw transaction data.',
example: '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675'
},
{
type: Array,
desc: 'Type of trace, one or more of: `"vmTrace"`, `"trace"`, `"stateDiff"`.',
example: ['trace']
}
],
returns: {
type: Object,
desc: 'Block traces.',
example: {
output: '0x',
stateDiff: null,
trace: [
{
action: new Dummy('{ ... }'),
result: {
gasUsed: '0x0',
output: '0x'
},
subtraces: 0,
traceAddress: [],
type: 'call'
}
],
vmTrace: null
}
}
},
replayTransaction: {
section: SECTION_ADHOC,
desc: 'Replays a transaction, returning the traces.',
params: [
{
type: Hash,
desc: 'Transaction hash.',
example: '0x02d4a872e096445e80d05276ee756cefef7f3b376bcec14246469c0cd97dad8f'
},
{
type: Array,
desc: 'Type of trace, one or more of: `"vmTrace"`, `"trace"`, `"stateDiff"`.',
example: ['trace']
}
],
returns: {
type: Object,
desc: 'Block traces.',
example: {
output: '0x',
stateDiff: null,
trace: [
{
action: new Dummy('{ ... }'),
result: {
gasUsed: '0x0',
output: '0x'
},
subtraces: 0,
traceAddress: [],
type: 'call'
}
],
vmTrace: null
}
} }
} }
}; });

View File

@ -16,8 +16,6 @@
export class Address {} export class Address {}
export class BlockNumber {}
export class Data {} export class Data {}
export class Hash {} export class Hash {}
@ -25,3 +23,89 @@ export class Hash {}
export class Integer {} export class Integer {}
export class Quantity {} export class Quantity {}
export class BlockNumber {
static print = '`Quantity` | `Tag`';
}
export class CallRequest {
static print = '`Object`';
static details = {
from: {
type: Address,
desc: '20 Bytes - The address the transaction is send from.',
optional: true
},
to: {
type: Address,
desc: '(optional when creating new contract) 20 Bytes - The address the transaction is directed to.'
},
gas: {
type: Quantity,
desc: 'Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.',
optional: true
},
gasPrice: {
type: Quantity,
desc: 'Integer of the gas price used for each paid gas.',
optional: true
},
value: {
type: Quantity,
desc: 'Integer of the value sent with this transaction.',
optional: true
},
data: {
type: Data,
desc: '4 byte hash of the method signature followed by encoded parameters. For details see [Ethereum Contract ABI](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI).',
optional: true
}
}
}
export class TransactionRequest {
static print = '`Object`';
static details = {
from: {
type: Address,
desc: '20 Bytes - The address the transaction is send from.'
},
to: {
type: Address,
desc: '20 Bytes - The address the transaction is directed to.',
optional: true
},
gas: {
type: Quantity,
desc: 'Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.',
optional: true
},
gasPrice: {
type: Quantity,
desc: 'Integer of the gas price used for each paid gas.',
optional: true
},
value: {
type: Quantity,
desc: 'Integer of the value sent with this transaction.',
optional: true
},
data: {
type: Data,
desc: '4 byte hash of the method signature followed by encoded parameters. For details see [Ethereum Contract ABI](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI).',
optional: true
},
nonce: {
type: Quantity,
desc: 'Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce.',
optional: true
},
minBlock: {
type: BlockNumber,
desc: 'Delay until this block if specified.',
optional: true
}
}
}

View File

@ -105,7 +105,7 @@ build_rpc_trait! {
#[rpc(name = "eth_sendRawTransaction")] #[rpc(name = "eth_sendRawTransaction")]
fn send_raw_transaction(&self, Bytes) -> Result<H256, Error>; fn send_raw_transaction(&self, Bytes) -> Result<H256, Error>;
/// Alias of `eth_sendRawTransaction`. /// @alias of `eth_sendRawTransaction`.
#[rpc(name = "eth_submitTransaction")] #[rpc(name = "eth_submitTransaction")]
fn submit_transaction(&self, Bytes) -> Result<H256, Error>; fn submit_transaction(&self, Bytes) -> Result<H256, Error>;

View File

@ -70,7 +70,8 @@ build_rpc_trait! {
#[rpc(name = "parity_setAccountMeta")] #[rpc(name = "parity_setAccountMeta")]
fn set_account_meta(&self, H160, String) -> Result<bool, Error>; fn set_account_meta(&self, H160, String) -> Result<bool, Error>;
/// Sets account visibility /// Sets account visibility.
/// @unimplemented
#[rpc(name = "parity_setAccountVisiblity")] #[rpc(name = "parity_setAccountVisiblity")]
fn set_account_visibility(&self, H160, H256, bool) -> Result<bool, Error>; fn set_account_visibility(&self, H160, H256, bool) -> Result<bool, Error>;

View File

@ -74,13 +74,13 @@ build_rpc_trait! {
/// Start the network. /// Start the network.
/// ///
/// Deprecated. Use `set_mode("active")` instead. /// @deprecated - Use `set_mode("active")` instead.
#[rpc(name = "parity_startNetwork")] #[rpc(name = "parity_startNetwork")]
fn start_network(&self) -> Result<bool, Error>; fn start_network(&self) -> Result<bool, Error>;
/// Stop the network. /// Stop the network.
/// ///
/// Deprecated. Use `set_mode("offline")` instead. /// @deprecated - Use `set_mode("offline")` instead.
#[rpc(name = "parity_stopNetwork")] #[rpc(name = "parity_stopNetwork")]
fn stop_network(&self) -> Result<bool, Error>; fn stop_network(&self) -> Result<bool, Error>;

View File

@ -24,10 +24,12 @@ build_rpc_trait! {
/// RPC Interface. /// RPC Interface.
pub trait Rpc { pub trait Rpc {
/// Returns supported modules for Geth 1.3.6 /// Returns supported modules for Geth 1.3.6
/// @ignore
#[rpc(name = "modules")] #[rpc(name = "modules")]
fn modules(&self) -> Result<BTreeMap<String, String>, Error>; fn modules(&self) -> Result<BTreeMap<String, String>, Error>;
/// Returns supported modules for Geth 1.4.0 /// Returns supported modules for Geth 1.4.0
/// @ignore
#[rpc(name = "rpc_modules")] #[rpc(name = "rpc_modules")]
fn rpc_modules(&self) -> Result<BTreeMap<String, String>, Error>; fn rpc_modules(&self) -> Result<BTreeMap<String, String>, Error>;
} }