-
Notifications
You must be signed in to change notification settings - Fork 808
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
wallet rpc: add getdescriptorinfo rpc command
- Loading branch information
Showing
10 changed files
with
667 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
/* eslint-disable max-len */ | ||
'use strict'; | ||
|
||
const assert = require('assert'); | ||
const common = require('./common'); | ||
const bio = require('bufio'); | ||
const hash160 = require('bcrypto/lib/hash160'); | ||
const WalletKey = require('../wallet/walletkey'); | ||
|
||
/** | ||
* Helper class to represent hd key path for arbitrary wallets. | ||
* @property {Number} fingerPrint - master key fingerprint (uint32) | ||
* @property {Array} path - bip32 derivation path in uint32 array | ||
*/ | ||
class KeyOriginInfo { | ||
constructor(options) { | ||
this.fingerPrint = -1; | ||
this.path = []; | ||
if (options) { | ||
this.fromOptions(options); | ||
} | ||
} | ||
|
||
fromOptions(options) { | ||
assert(options, 'requires options'); | ||
|
||
if (options.fingerPrint !== undefined) { | ||
assert( | ||
(options.fingerPrint >>> 0) === options.fingerPrint && | ||
options.fingerPrint <= 0xFFFFFFFF, | ||
'fingerPrint must be uint32' | ||
); | ||
this.fingerPrint = options.fingerPrint; | ||
} | ||
|
||
if (options.path !== undefined) { | ||
if (Array.isArray(options.path)) { | ||
assert( | ||
options.path.every(p => (p >>> 0) === p && p <= 0xFFFFFFFF), | ||
'all path index must be uint32' | ||
); | ||
this.path = options.path; | ||
} else { | ||
this.path = common.parsePath(options.path, true); | ||
} | ||
} | ||
return this; | ||
} | ||
|
||
static fromOptions(options) { | ||
return new this().fromOptions(options); | ||
} | ||
|
||
equals(keyInfo) { | ||
assert(KeyOriginInfo.isKeyOriginInfo(keyInfo)); | ||
if (this.fingerPrint !== keyInfo.fingerPrint) | ||
return false; | ||
assert(this.path.length === keyInfo.path.length); | ||
for (let i = 0; i < this.path.length; i++) { | ||
if (this.path[i] !== keyInfo.path[i]) | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
inspect() { | ||
return this.format(); | ||
} | ||
|
||
format() { | ||
let path = 'm'; | ||
for (const p of this.path) { | ||
const hardened = (p & common.HARDENED) ? '\'' : ''; | ||
path += `/${p & 0x7fffffff}${hardened}`; | ||
} | ||
return { | ||
fingerPrint: this.fingerPrint, | ||
path | ||
}; | ||
} | ||
|
||
static fromRaw(data) { | ||
return new this().fromRaw(data); | ||
} | ||
|
||
fromRaw(data) { | ||
return this.fromReader(bio.read(data)); | ||
}; | ||
|
||
static fromReader(br) { | ||
return new this().fromReader(br); | ||
} | ||
|
||
fromReader(br) { | ||
this.fingerPrint = br.readU32BE(); | ||
while (br.left()) { | ||
this.path.push(br.readU32()); | ||
} | ||
return this; | ||
} | ||
|
||
toRaw() { | ||
const size = this.getSize(); | ||
return this.toWriter(bio.write(size)).render(); | ||
} | ||
|
||
toWriter(bw) { | ||
bw.writeU32BE(this.fingerPrint); | ||
for (const p of this.path) { | ||
bw.writeU32(p); | ||
} | ||
return bw; | ||
} | ||
|
||
toJSON() { | ||
return { | ||
fingerPrint: this.fingerPrint.toString(16), | ||
path: this.format().path | ||
}; | ||
} | ||
|
||
static fromJSON(json) { | ||
return new this().fromJSON(json); | ||
} | ||
|
||
fromJSON(json) { | ||
if (json.fingerPrint) { | ||
assert((json.fingerPrint >>> 0) === json.fingerPrint); | ||
this.fingerPrint = json.fingerPrint; | ||
} | ||
|
||
if (json.path) { | ||
if (Array.isArray(json.path) && json.path.length > 0) { | ||
for (const p of json.path) { | ||
assert((p >>> 0) === p); | ||
this.path.push(p); | ||
} | ||
} else { | ||
this.path = common.parsePath(json.path, true); | ||
} | ||
} | ||
|
||
return this; | ||
} | ||
|
||
static isKeyOriginInfo(obj) { | ||
return obj instanceof KeyOriginInfo; | ||
} | ||
|
||
clone() { | ||
const path = this.path.slice(); | ||
return new KeyOriginInfo({ fingerPrint: this.fingerPrint, path }); | ||
} | ||
|
||
clear() { | ||
this.fingerPrint = -1; | ||
this.path = []; | ||
} | ||
|
||
getSize() { | ||
return 4 + this.path.length * 4; | ||
} | ||
|
||
static fromWalletKey(wk) { | ||
return new this().fromWalletKey(wk); | ||
} | ||
|
||
fromWalletKey(wk) { | ||
assert(WalletKey.isWalletKey(wk)); | ||
const fp = hash160.digest(wk.publicKey); | ||
this.fingerPrint = fp.readUInt32BE(0, true); | ||
this.path.push(wk.account | common.HARDENED); | ||
this.path.push(wk.branch); | ||
this.path.push(wk.index); | ||
return this; | ||
} | ||
} | ||
|
||
module.exports = KeyOriginInfo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* eslint-disable max-len */ | ||
'use strict'; | ||
const secp256k1 = require('bcrypto/lib/secp256k1'); | ||
const assert = require('bsert'); | ||
|
||
exports.checkScriptType = function checkScriptType(str, s) { | ||
if (s.length >= str.length + 2 && s[str.length] === '(' && s[s.length - 1] === ')' && str === s.substring(0, str.length)) { | ||
// s = s.substring(str.length + 1, s.length - 1); | ||
return true; | ||
} | ||
return false; | ||
}; | ||
|
||
exports.stringAfterScriptType = function stringAfterScriptType(str, s) { | ||
s = s.substring(str.length + 1, s.length - 1); | ||
return s; | ||
}; | ||
|
||
exports.giveExpr = function giveExpr(str) { | ||
let level = 0; | ||
let it = 0; | ||
|
||
while (it < str.length) { | ||
if (str[it] === '(' || str[it] === '{') { | ||
level++; | ||
} else if (level && (str[it] === ')' || str[it] === '}')) { | ||
level--; | ||
} else if (level === 0 && (str[it] === ')' || str[it] === '}' || str[it] === ',')) { | ||
break; | ||
} | ||
it++; | ||
} | ||
|
||
const ret = str.slice(0, it); | ||
// str = str.slice(it); | ||
return ret; | ||
}; | ||
|
||
// eslint-disable-next-line valid-jsdoc | ||
/** | ||
* @TODO write tests | ||
*/ | ||
exports.isHex = function isHex(str) { | ||
const regexp = /^[0-9a-fA-F]+$/; | ||
return regexp.test(str); | ||
}; | ||
|
||
exports.isCompressed = function isCompressed(key) { | ||
assert(secp256k1.publicKeyVerify(key), 'Invalid public key'); | ||
const iscompressed = key.length === 33 && key[0] === 0x02 || key[0] === 0x03; | ||
return iscompressed; | ||
}; |
Oops, something went wrong.