diff --git a/js/scripts/build-rpc-markdown.js b/js/scripts/build-rpc-markdown.js index 2407cf208..7a78e1ce9 100644 --- a/js/scripts/build-rpc-markdown.js +++ b/js/scripts/build-rpc-markdown.js @@ -37,7 +37,11 @@ Object.keys(rustMethods).forEach((group) => { }); }); -function printType (type) { +function printType (type, obj) { + if (!type) { + throw new Error(`Invalid type in ${JSON.stringify(obj)}`); + } + return type.print || `\`${type.name}\``; } @@ -45,7 +49,7 @@ function formatDescription (obj, prefix = '', indent = '') { const optional = obj.optional ? '(optional) ' : ''; const defaults = obj.default ? `(default: \`${obj.default}\`) ` : ''; - return `${indent}${prefix}${printType(obj.type)} - ${optional}${defaults}${obj.desc}`; + return `${indent}${prefix}${printType(obj.type, obj)} - ${optional}${defaults}${obj.desc}`; } function formatType (obj) { diff --git a/js/src/api/format/input.js b/js/src/api/format/input.js index 3a9cfee06..0ccd10604 100644 --- a/js/src/api/format/input.js +++ b/js/src/api/format/input.js @@ -165,10 +165,6 @@ export function inOptions (_options = {}) { options[key] = inNumber16((new BigNumber(options[key])).round()); break; - case 'minBlock': - options[key] = options[key] ? inNumber16(options[key]) : null; - break; - case 'value': case 'nonce': options[key] = inNumber16(options[key]); @@ -211,3 +207,36 @@ export function inTraceType (whatTrace) { return whatTrace; } + +function inDeriveType (derive) { + return derive && derive.type === 'hard' ? 'hard' : 'soft'; +} + +export function inDeriveHash (derive) { + const hash = derive && derive.hash ? derive.hash : derive; + const type = inDeriveType(derive); + + return { + hash: inHex(hash), + type + }; +} + +export function inDeriveIndex (derive) { + if (!derive) { + return []; + } + + if (!isArray(derive)) { + derive = [derive]; + } + + return derive.map(item => { + const index = inNumber10(item && item.index ? item.index : item); + + return { + index, + type: inDeriveType(item) + }; + }); +} diff --git a/js/src/api/format/input.spec.js b/js/src/api/format/input.spec.js index 4b82bd1ef..eb74b7bd6 100644 --- a/js/src/api/format/input.spec.js +++ b/js/src/api/format/input.spec.js @@ -16,7 +16,11 @@ import BigNumber from 'bignumber.js'; -import { inAddress, inBlockNumber, inData, inFilter, inHex, inNumber10, inNumber16, inOptions, inTraceType } from './input'; +import { + inAddress, inBlockNumber, inData, inFilter, inHex, + inNumber10, inNumber16, inOptions, inTraceType, + inDeriveHash, inDeriveIndex +} from './input'; import { isAddress } from '../../../test/types'; describe('api/format/input', () => { @@ -215,7 +219,7 @@ describe('api/format/input', () => { expect(formatted.to).to.equal(''); }); - ['gas', 'gasPrice', 'value', 'minBlock', 'nonce'].forEach((input) => { + ['gas', 'gasPrice', 'value', 'nonce'].forEach((input) => { it(`formats ${input} number as hexnumber`, () => { const block = {}; @@ -226,8 +230,8 @@ describe('api/format/input', () => { }); }); - it('passes minBlock as null when specified as such', () => { - expect(inOptions({ minBlock: null })).to.deep.equal({ minBlock: null }); + it('passes condition as null when specified as such', () => { + expect(inOptions({ condition: null })).to.deep.equal({ condition: null }); }); it('ignores and passes through unknown keys', () => { @@ -272,4 +276,66 @@ describe('api/format/input', () => { expect(inTraceType(type)).to.deep.equal([type]); }); }); + + describe('inDeriveHash', () => { + it('returns derive hash', () => { + expect(inDeriveHash(1)).to.deep.equal({ + hash: '0x1', + type: 'soft' + }); + + expect(inDeriveHash(null)).to.deep.equal({ + hash: '0x', + type: 'soft' + }); + + expect(inDeriveHash({ + hash: 5 + })).to.deep.equal({ + hash: '0x5', + type: 'soft' + }); + + expect(inDeriveHash({ + hash: 5, + type: 'hard' + })).to.deep.equal({ + hash: '0x5', + type: 'hard' + }); + }); + }); + + describe('inDeriveIndex', () => { + it('returns derive hash', () => { + expect(inDeriveIndex(null)).to.deep.equal([]); + expect(inDeriveIndex([])).to.deep.equal([]); + + expect(inDeriveIndex([1])).to.deep.equal([{ + index: 1, + type: 'soft' + }]); + + expect(inDeriveIndex({ + index: 1 + })).to.deep.equal([{ + index: 1, + type: 'soft' + }]); + + expect(inDeriveIndex([{ + index: 1, + type: 'hard' + }, 5])).to.deep.equal([ + { + index: 1, + type: 'hard' + }, + { + index: 5, + type: 'soft' + } + ]); + }); + }); }); diff --git a/js/src/api/format/output.js b/js/src/api/format/output.js index 7348487e7..8f375e210 100644 --- a/js/src/api/format/output.js +++ b/js/src/api/format/output.js @@ -284,12 +284,6 @@ export function outTransaction (tx) { tx[key] = outTransactionCondition(tx[key]); break; - case 'minBlock': - tx[key] = tx[key] - ? outNumber(tx[key]) - : null; - break; - case 'creates': case 'from': case 'to': diff --git a/js/src/api/format/output.spec.js b/js/src/api/format/output.spec.js index fabe30b32..6ffefaedc 100644 --- a/js/src/api/format/output.spec.js +++ b/js/src/api/format/output.spec.js @@ -392,7 +392,7 @@ describe('api/format/output', () => { }); }); - ['blockNumber', 'gasPrice', 'gas', 'minBlock', 'nonce', 'transactionIndex', 'value'].forEach((input) => { + ['blockNumber', 'gasPrice', 'gas', 'nonce', 'transactionIndex', 'value'].forEach((input) => { it(`formats ${input} number as hexnumber`, () => { const block = {}; @@ -404,8 +404,8 @@ describe('api/format/output', () => { }); }); - it('passes minBlock as null when null', () => { - expect(outTransaction({ minBlock: null })).to.deep.equal({ minBlock: null }); + it('passes condition as null when null', () => { + expect(outTransaction({ condition: null })).to.deep.equal({ condition: null }); }); it('ignores and passes through unknown keys', () => { diff --git a/js/src/api/rpc/parity/parity.js b/js/src/api/rpc/parity/parity.js index 752557d80..f38e29e7b 100644 --- a/js/src/api/rpc/parity/parity.js +++ b/js/src/api/rpc/parity/parity.js @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { inAddress, inAddresses, inData, inHex, inNumber16, inOptions, inBlockNumber } from '../../format/input'; +import { + inAddress, inAddresses, inData, inHex, inNumber16, inOptions, inBlockNumber, inDeriveHash, inDeriveIndex +} from '../../format/input'; import { outAccountInfo, outAddress, outAddresses, outChainStatus, outHistogram, outHwAccountInfo, outNodeKind, outNumber, outPeers, outRecentDapps, outTransaction, outVaultMeta } from '../../format/output'; export default class Parity { @@ -117,6 +119,18 @@ export default class Parity { .execute('parity_devLogsLevels'); } + deriveAddressHash (address, password, hash, shouldSave) { + return this._transport + .execute('parity_deriveAddressHash', inAddress(address), password, inDeriveHash(hash), !!shouldSave) + .then(outAddress); + } + + deriveAddressIndex (address, password, index, shouldSave) { + return this._transport + .execute('parity_deriveAddressIndex', inAddress(address), password, inDeriveIndex(index), !!shouldSave) + .then(outAddress); + } + dropNonReservedPeers () { return this._transport .execute('parity_dropNonReservedPeers'); @@ -137,6 +151,11 @@ export default class Parity { .execute('parity_executeUpgrade'); } + exportAccount (address, password) { + return this._transport + .execute('parity_exportAccount', inAddress(address), password); + } + extraData () { return this._transport .execute('parity_extraData'); @@ -401,6 +420,12 @@ export default class Parity { .execute('parity_removeReservedPeer', encode); } + removeTransaction (hash) { + return this._transport + .execute('parity_removeTransaction', inHex(hash)) + .then(outTransaction); + } + rpcSettings () { return this._transport .execute('parity_rpcSettings'); diff --git a/js/src/jsonrpc/interfaces/parity.js b/js/src/jsonrpc/interfaces/parity.js index e9ac256e3..3e6fa8475 100644 --- a/js/src/jsonrpc/interfaces/parity.js +++ b/js/src/jsonrpc/interfaces/parity.js @@ -379,7 +379,9 @@ export default { gasPrice: '0x2d20cff33', hash: '0x09e64eb1ae32bb9ac415ce4ddb3dbad860af72d9377bb5f073c9628ab413c532', input: '0x', - minBlock: null, + condition: { + block: 1 + }, networkId: null, nonce: '0x0', publicKey: '0x3fa8c08c65a83f6b4ea3e04e1cc70cbe3cd391499e3e05ab7dedf28aff9afc538200ff93e3f2b2cb5029f03c7ebee820d63a4c5a9541c83acebe293f54cacf0e', @@ -435,17 +437,17 @@ export default { type: String, desc: 'Capability, either `full` or `light`.' } + }, + example: { + availability: 'personal', + capability: 'light' } - }, - example: { - availability: 'personal', - capability: 'light' } }, netChain: { section: SECTION_NET, - desc: 'Returns the name of the connected chain.', + desc: 'Returns the name of the connected chain. DEPRECATED use `parity_chain` instead.', params: [], returns: { type: String, @@ -454,6 +456,17 @@ export default { } }, + chain: { + section: SECTION_NET, + desc: 'Returns the name of the connected chain. ', + params: [], + returns: { + type: String, + desc: 'chain name, one of: "foundation", "kovan", &c. of a filename.', + example: 'homestead' + } + }, + netPeers: { section: SECTION_NET, desc: 'Returns number of peers.', @@ -589,7 +602,9 @@ export default { gasPrice: '0xba43b7400', hash: '0x160b3c30ab1cf5871083f97ee1cee3901cfba3b0a2258eb337dd20a7e816b36e', input: '0x095ea7b3000000000000000000000000bf4ed7b27f1d666546e30d74d50d173d20bca75400000000000000000000000000002643c948210b4bd99244ccd64d5555555555', - minBlock: null, + condition: { + block: 1 + }, networkId: 1, nonce: '0x5', publicKey: '0x96157302dade55a1178581333e57d60ffe6fdf5a99607890456a578b4e6b60e335037d61ed58aa4180f9fd747dc50d44a7924aa026acbfb988b5062b629d6c36', @@ -609,6 +624,7 @@ export default { }, pendingTransactionsStats: { + section: SECTION_NET, desc: 'Returns propagation stats for transactions in the queue.', params: [], returns: { @@ -626,6 +642,49 @@ export default { } }, + removeTransaction: { + section: SECTION_NET, + desc: 'Removes transaction from local transaction pool. Scheduled transactions and not-propagated transactions are safe to remove, removal of other transactions may have no effect though.', + params: [{ + type: Hash, + desc: 'Hash of transaction to remove.', + example: '0x2547ea3382099c7c76d33dd468063b32d41016aacb02cbd51ebc14ff5d2b6a43' + }], + returns: { + type: Object, + desc: 'Removed transaction or `null`.', + details: TransactionResponse.details, + example: [ + { + blockHash: null, + blockNumber: null, + creates: null, + from: '0xee3ea02840129123d5397f91be0391283a25bc7d', + gas: '0x23b58', + gasPrice: '0xba43b7400', + hash: '0x160b3c30ab1cf5871083f97ee1cee3901cfba3b0a2258eb337dd20a7e816b36e', + input: '0x095ea7b3000000000000000000000000bf4ed7b27f1d666546e30d74d50d173d20bca75400000000000000000000000000002643c948210b4bd99244ccd64d5555555555', + condition: { + block: 1 + }, + networkId: 1, + nonce: '0x5', + publicKey: '0x96157302dade55a1178581333e57d60ffe6fdf5a99607890456a578b4e6b60e335037d61ed58aa4180f9fd747dc50d44a7924aa026acbfb988b5062b629d6c36', + r: '0x92e8beb19af2bad0511d516a86e77fa73004c0811b2173657a55797bdf8558e1', + raw: '0xf8aa05850ba43b740083023b5894bb9bc244d798123fde783fcc1c72d3bb8c18941380b844095ea7b3000000000000000000000000bf4ed7b27f1d666546e30d74d50d173d20bca75400000000000000000000000000002643c948210b4bd99244ccd64d555555555526a092e8beb19af2bad0511d516a86e77fa73004c0811b2173657a55797bdf8558e1a062b4d4d125bbcb9c162453bc36ca156537543bb4414d59d1805d37fb63b351b8', + s: '0x62b4d4d125bbcb9c162453bc36ca156537543bb4414d59d1805d37fb63b351b8', + standardV: '0x1', + to: '0xbb9bc244d798123fde783fcc1c72d3bb8c189413', + transactionIndex: null, + v: '0x26', + value: '0x0' + }, + new Dummy('{ ... }'), + new Dummy('{ ... }') + ] + } + }, + phraseToAddress: { section: SECTION_ACCOUNTS, desc: 'Converts a secret phrase into the corresponding address.', @@ -916,7 +975,9 @@ export default { v: '0x25', r: '0xb40c6967a7e8bbdfd99a25fd306b9ef23b80e719514aeb7ddd19e2303d6fc139', s: '0x6bf770ab08119e67dc29817e1412a0e3086f43da308c314db1b3bca9fb6d32bd', - minBlock: null + condition: { + block: 1 + } }, new Dummy('{ ... }, { ... }, ...') ] @@ -1307,12 +1368,14 @@ export default { params: [ { type: Array, - desc: 'List of the Geth addresses to import.' + desc: 'List of the Geth addresses to import.', + example: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1'] } ], returns: { type: Array, - desc: 'Array of the imported addresses.' + desc: 'Array of the imported addresses.', + example: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1'] } }, @@ -1322,7 +1385,114 @@ export default { params: [], returns: { type: Array, - desc: '20 Bytes addresses owned by the client.' + desc: '20 Bytes addresses owned by the client.', + example: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1'] + } + }, + + deriveAddressHash: { + subdoc: SUBDOC_ACCOUNTS, + desc: 'Derive new address from given account address using specific hash.', + params: [ + { + type: Address, + desc: 'Account address to derive from.', + example: '0x407d73d8a49eeb85d32cf465507dd71d507100c1' + }, + { + type: String, + desc: 'Password to the account.', + example: 'hunter2' + }, + { + type: Object, + desc: 'Derivation hash and type (`soft` or `hard`). E.g. `{ hash: "0x123..123", type: "hard" }`.', + example: { + hash: '0x2547ea3382099c7c76d33dd468063b32d41016aacb02cbd51ebc14ff5d2b6a43', + type: 'hard' + } + }, + { + type: Boolean, + desc: 'Flag indicating if the account should be saved.', + example: false + } + ], + returns: { + type: Address, + desc: '20 Bytes new derived address.', + example: '0x407d73d8a49eeb85d32cf465507dd71d507100c1' + } + }, + + deriveAddressIndex: { + subdoc: SUBDOC_ACCOUNTS, + desc: 'Derive new address from given account address using hierarchical derivation (sequence of 32-bit integer indices).', + params: [ + { + type: Address, + desc: 'Account address to export.', + example: '0x407d73d8a49eeb85d32cf465507dd71d507100c1' + }, + { + type: String, + desc: 'Password to the account.', + example: 'hunter2' + }, + { + type: Array, + desc: 'Hierarchical derivation sequence of index and type (`soft` or `hard`). E.g. `[{index:1,type:"hard"},{index:2,type:"soft"}]`.', + example: [ + { index: 1, type: 'hard' }, + { index: 2, type: 'soft' } + ] + }, + { + type: Boolean, + desc: 'Flag indicating if the account should be saved.', + example: false + } + ], + returns: { + type: Address, + desc: '20 Bytes new derived address.', + example: '0x407d73d8a49eeb85d32cf465507dd71d507100c1' + } + }, + + exportAccount: { + subdoc: SUBDOC_ACCOUNTS, + desc: 'Returns a standard wallet file for given account if password matches.', + params: [ + { + type: Address, + desc: 'Account address to export.', + example: '0x407d73d8a49eeb85d32cf465507dd71d507100c1' + }, + { + type: String, + desc: 'Password to the account.', + example: 'hunter2' + } + ], + returns: { + type: Object, + desc: 'Standard wallet JSON.', + example: { + 'address': '0042e5d2a662eeaca8a7e828c174f98f35d8925b', + 'crypto': { + 'cipher': 'aes-128-ctr', + 'cipherparams': { 'iv': 'a1c6ff99070f8032ca1c4e8add006373' }, + 'ciphertext': 'df27e3db64aa18d984b6439443f73660643c2d119a6f0fa2fa9a6456fc802d75', + 'kdf': 'pbkdf2', + 'kdfparams': { 'c': 10240, 'dklen': 32, 'prf': 'hmac-sha256', 'salt': 'ddc325335cda5567a1719313e73b4842511f3e4a837c9658eeb78e51ebe8c815' }, + 'mac': '3dc888ae79cbb226ff9c455669f6cf2d79be72120f2298f6cb0d444fddc0aa3d' + }, + 'id': '6a186c80-7797-cff2-bc2e-7c1d6a6cc76e', + 'meta': '{"passwordHint":"parity-export-test","timestamp":1490017814987}', + 'name': 'parity-export-test', + 'version': 3 + } } }, @@ -1529,6 +1699,23 @@ export default { } }, + setChain: { + subdoc: SUBDOC_SET, + desc: 'Sets the network spec file Parity is using.', + params: [ + { + type: String, + desc: 'Chain spec name, one of: "foundation", "ropsten", "morden", "kovan", "olympic", "classic", "dev", "expanse" or a filename.', + example: 'foundation' + } + ], + returns: { + type: Boolean, + desc: '`true` if the call succeeded.', + example: true + } + }, + setMode: { subdoc: SUBDOC_SET, desc: 'Changes the operating mode of Parity.', diff --git a/js/src/jsonrpc/interfaces/signer.js b/js/src/jsonrpc/interfaces/signer.js index e38bad68f..495b8e0e8 100644 --- a/js/src/jsonrpc/interfaces/signer.js +++ b/js/src/jsonrpc/interfaces/signer.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { Quantity, Data, BlockNumber } from '../types'; +import { Quantity, Data } from '../types'; import { fromDecimal, Dummy } from '../helpers'; export default { @@ -71,9 +71,9 @@ export default { 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.', + condition: { + type: Object, + desc: 'Condition for scheduled transaction. Can be either an integer block number `{ block: 1 }` or UTC timestamp (in seconds) `{ timestamp: 1491290692 }`.', optional: true } }, @@ -114,7 +114,7 @@ export default { }, confirmRequestWithToken: { - desc: 'Confirm specific request with token.', + desc: 'Confirm specific request with rolling token.', params: [ { type: Quantity, @@ -135,9 +135,9 @@ export default { 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.', + condition: { + type: Object, + desc: 'Conditional submission of the transaction. Can be either an integer block number `{ block: 1 }` or UTC timestamp (in seconds) `{ time: 1491290692 }` or `null`.', optional: true } }, @@ -145,7 +145,7 @@ export default { }, { type: String, - desc: 'Password.', + desc: 'Password (initially) or a token returned by the previous call.', example: 'hunter2' } ], @@ -159,7 +159,7 @@ export default { }, token: { type: String, - desc: 'Token used to authenticate the request.' + desc: 'Token used to authenticate the next request.' } }, example: { diff --git a/js/src/jsonrpc/types.js b/js/src/jsonrpc/types.js index 8803fdd5c..52e79019e 100644 --- a/js/src/jsonrpc/types.js +++ b/js/src/jsonrpc/types.js @@ -102,9 +102,9 @@ export class TransactionRequest { 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.', + condition: { + type: Object, + desc: 'Conditional submission of the transaction. Can be either an integer block number `{ block: 1 }` or UTC timestamp (in seconds) `{ time: 1491290692 }` or `null`.', optional: true } }