improve p2sh building. change wallet key/redeem methods.

This commit is contained in:
Christopher Jeffrey 2016-01-10 21:43:52 -08:00
parent 871e36c426
commit 442c984c12
5 changed files with 124 additions and 138 deletions

View File

@ -949,29 +949,29 @@ Pool.prototype.removeWallet = function removeWallet(w) {
Pool.prototype.watchWallet = function watchWallet(w) {
if (w.type === 'scripthash') {
// For the redeem script hash in outputs:
this.watch(w.getFullHash());
this.watch(w.getScriptHash());
// For the redeem script in inputs:
this.watch(w.getFullPublicKey());
this.watch(w.getScript());
}
// For the pubkey hash in outputs:
this.watch(w.getOwnHash());
this.watch(w.getKeyHash());
// For the pubkey in inputs:
this.watch(w.getOwnPublicKey());
this.watch(w.getPublicKey());
};
Pool.prototype.unwatchWallet = function unwatchWallet(w) {
if (w.type === 'scripthash') {
// For the redeem script hash in p2sh outputs:
this.unwatch(w.getFullHash());
this.unwatch(w.getScriptHash());
// For the redeem script in p2sh inputs:
this.unwatch(w.getFullPublicKey());
this.unwatch(w.getScript());
}
// For the pubkey hash in p2pk/multisig outputs:
this.unwatch(w.getOwnHash());
this.unwatch(w.getKeyHash());
// For the pubkey in p2pkh inputs:
this.unwatch(w.getOwnPublicKey());
this.unwatch(w.getPublicKey());
};
Pool.prototype.searchWallet = function(w) {

View File

@ -982,6 +982,26 @@ script.size = function size(s) {
return bcoin.script.encode(s).length;
};
script.normalize = function normalize(s) {
var bytes = true;
var i;
for (i = 0; bytes && i < s.length; i++) {
if (typeof s[i] !== 'number')
bytes = false;
}
if (bytes)
s = script.decode(s);
s = script.subscript(s);
if (script.lockTime(s))
s = s.slice(3);
return s;
};
script.lockTime = function lockTime(s) {
var lock = s[0];
var res = s.length > 3

View File

@ -157,8 +157,8 @@ TX.prototype._inputIndex = function _inputIndex(hash, index) {
};
// Build the scriptSigs for inputs, excluding the signatures
TX.prototype.scriptInput = function scriptInput(index, pub) {
var input, s, n, i, redeem;
TX.prototype.scriptInput = function scriptInput(index, pub, redeem) {
var input, s, standard, n, i;
if (typeof index !== 'number')
index = this.inputs.indexOf(index);
@ -174,26 +174,25 @@ TX.prototype.scriptInput = function scriptInput(index, pub) {
if (input.script.length)
return;
// P2PK
// P2SH
if (bcoin.script.isScripthash(s)) {
assert(redeem);
s = bcoin.script.normalize(redeem);
} else {
redeem = null;
}
if (bcoin.script.isPubkey(s)) {
// P2PK
input.script = [ [] ];
this._recalculateFee();
return;
}
// P2PKH
if (bcoin.script.isPubkeyhash(s)) {
} else if (bcoin.script.isPubkeyhash(s)) {
// P2PKH
input.script = [ [], pub ];
this._recalculateFee();
return;
}
// NOTE for multisig: Technically we should create m signature slots,
// but we create n signature slots so we can order the signatures properly.
// Multisig
// raw format: OP_FALSE [sig-1] [sig-2] ...
if (bcoin.script.isMultisig(s)) {
} else if (bcoin.script.isMultisig(s)) {
// Bare Multisig
// Technically we should create m signature slots,
// but we create n signature slots so we can order
// the signatures properly.
input.script = [ [] ];
n = s[s.length - 2];
// If using pushdata instead of OP_1-16:
@ -201,34 +200,19 @@ TX.prototype.scriptInput = function scriptInput(index, pub) {
n = n[0] || 0;
for (i = 0; i < n; i++)
input.script[i + 1] = [];
this._recalculateFee();
return;
}
// P2SH multisig
// p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
if (bcoin.script.isScripthash(s)) {
input.script = [ [] ];
redeem = bcoin.script.decode(pub);
n = redeem[redeem.length - 2];
// If using pushdata instead of OP_1-16:
if (Array.isArray(n))
n = n[0] || 0;
for (i = 0; i < n; i++)
input.script[i + 1] = [];
// P2SH requires the redeem script after signatures
input.script.push(pub);
this._recalculateFee();
return;
}
// P2SH requires the redeem script after signatures
if (redeem)
input.script.push(redeem);
throw new Error('scriptInput(): Could not identify prev_out type');
this._recalculateFee();
};
// Sign the now-built scriptSigs
TX.prototype.signInput = function signInput(index, key, type) {
var input, s, hash, signature;
var len, redeem, m, keys, pub, pubn, ki, totalSigs, i;
var len, redeem, m, keys, pub, pubn, ki, signatures, i;
if (typeof index !== 'number')
index = this.inputs.indexOf(index);
@ -262,33 +246,30 @@ TX.prototype.signInput = function signInput(index, key, type) {
// Add the sighash as a single byte to the signature
signature = signature.concat(type);
// P2PK
// Script length, needed for multisig
len = input.script.length;
// P2SH
if (bcoin.script.isScripthash(s)) {
s = bcoin.script.normalize(redeem);
// Decrement `len` to avoid the redeem script
len--;
}
if (bcoin.script.isPubkey(s)) {
// P2PK
input.script[0] = signature;
return;
}
// P2PKH
if (bcoin.script.isPubkeyhash(s)) {
} else if (bcoin.script.isPubkeyhash(s)) {
// P2PKH
input.script[0] = signature;
return;
}
// Multisig
// raw format: OP_FALSE [sig-1] [sig-2] ...
// p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
if (bcoin.script.isMultisig(s) || bcoin.script.isScripthash(s)) {
len = input.script.length;
if (bcoin.script.isScripthash(s))
len--;
m = redeem[0];
} else if (bcoin.script.isMultisig(s)) {
// Multisig
m = s[0];
// If using pushdata instead of OP_1-16:
if (Array.isArray(m))
m = m[0] || 0;
keys = redeem.slice(1, -2);
keys = s.slice(1, -2);
pub = key.getPublic(true, 'array');
pubn = key.getPublic(false, 'array');
@ -309,44 +290,35 @@ TX.prototype.signInput = function signInput(index, key, type) {
// Add our signature to the correct slot
// and count the total number of signatures.
totalSigs = 0;
signatures = 0;
for (i = 1; i < len; i++) {
if (Array.isArray(input.script[i]) && input.script[i].length) {
totalSigs++;
signatures++;
continue;
}
if (i - 1 === ki) {
if (totalSigs >= m)
if (signatures >= m)
continue;
input.script[i] = signature;
totalSigs++;
signatures++;
}
}
// All signatures added. Finalize by removing empty slots.
if (totalSigs >= m) {
if (signatures >= m) {
for (i = len - 1; i >= 1; i--) {
if (Array.isArray(input.script[i]) && !input.script[i].length)
input.script.splice(i, 1);
}
}
return;
}
throw new Error('signInput(): Could not identify prev_out type');
};
// Build the scriptSig and sign it
TX.prototype.scriptSig = function scriptSig(index, key, pub, type) {
TX.prototype.scriptSig = function scriptSig(index, key, pub, redeem, type) {
var input;
if (!utils.isBuffer(pub)) {
type = pub;
pub = key.getPublic(true, 'array');
}
if (typeof index !== 'number')
index = this.inputs.indexOf(index);
@ -355,7 +327,7 @@ TX.prototype.scriptSig = function scriptSig(index, key, pub, type) {
assert(input);
// Build script for input
this.scriptInput(index, pub);
this.scriptInput(index, pub, redeem);
// Sign input
this.signInput(index, key, type);
@ -523,7 +495,7 @@ TX.prototype.getSubscript = function getSubscript(index) {
TX.prototype.subscriptHash = function subscriptHash(index, s, type) {
var copy = this.clone();
var verifyStr, hash;
var msg, hash;
if (typeof index !== 'number')
index = this.inputs.indexOf(index);
@ -573,11 +545,11 @@ TX.prototype.subscriptHash = function subscriptHash(index, s, type) {
copy.inputs[0].script = s;
}
verifyStr = copy.render(true);
msg = copy.render(true);
utils.writeU32(verifyStr, type, verifyStr.length);
utils.writeU32(msg, type, msg.length);
hash = utils.dsha256(verifyStr);
hash = utils.dsha256(msg);
return hash;
};

View File

@ -81,7 +81,7 @@ function Wallet(options, passphrase) {
this.m = 1;
this.n = 1;
this.prefix = 'bt/wallet/' + this.getOwnAddress() + '/';
this.prefix = 'bt/wallet/' + this.getKeyAddress() + '/';
this.multisig(options.multisig || {});
@ -126,7 +126,7 @@ Wallet.prototype._init = function init() {
};
Wallet.prototype.multisig = function multisig(options) {
var pub = this.getOwnPublicKey();
var pub = this.getPublicKey();
options.type = options.type || options.prefix;
options.keys = options.keys || options.pubkeys || [];
@ -235,21 +235,21 @@ Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
return priv;
};
Wallet.prototype.getFullPublicKey = function getFullPublicKey(enc) {
var pub = this.getOwnPublicKey();
if (this.type === 'scripthash')
pub = bcoin.script.encode(bcoin.script.redeem(this.keys, this.m, this.n));
if (enc === 'base58')
return utils.toBase58(pub);
else if (enc === 'hex')
return utils.toHex(pub);
else
return pub;
Wallet.prototype.getScript = function getScript(enc) {
if (this.redeem)
return this.redeem.slice();
return bcoin.script.encode(bcoin.script.redeem(this.keys, this.m, this.n));
};
Wallet.prototype.getOwnPublicKey = function getOwnPublicKey(enc) {
Wallet.prototype.getScriptHash = function getScriptHash() {
return utils.ripesha(this.getScript());
};
Wallet.prototype.getScriptAddress = function getScriptAddress() {
return Wallet.hash2addr(this.getScriptHash(), this.type);
};
Wallet.prototype.getPublicKey = function getPublicKey(enc) {
var pub = this.key.getPublic(this.compressed, 'array');
if (enc === 'base58')
@ -260,32 +260,24 @@ Wallet.prototype.getOwnPublicKey = function getOwnPublicKey(enc) {
return pub;
};
Wallet.prototype.getPublicKey = function getPublicKey(enc) {
return this.getFullPublicKey(enc);
Wallet.prototype.getKeyHash = function getKeyHash() {
return Wallet.key2hash(this.getPublicKey());
};
Wallet.prototype.getFullHash = function getFullHash() {
return Wallet.key2hash(this.getFullPublicKey());
};
Wallet.prototype.getFullAddress = function getFullAddress() {
return Wallet.hash2addr(this.getFullHash(), this.type);
};
Wallet.prototype.getOwnHash = function getOwnHash() {
return Wallet.key2hash(this.getOwnPublicKey());
};
Wallet.prototype.getOwnAddress = function getOwnAddress() {
return Wallet.hash2addr(this.getOwnHash(), 'pubkeyhash');
Wallet.prototype.getKeyAddress = function getKeyAddress() {
return Wallet.hash2addr(this.getKeyHash(), 'pubkeyhash');
};
Wallet.prototype.getHash = function getHash() {
return Wallet.key2hash(this.getFullPublicKey());
if (this.type === 'scripthash')
return this.getScriptHash();
return this.getKeyHash();
};
Wallet.prototype.getAddress = function getAddress() {
return Wallet.hash2addr(this.getFullHash(), this.type);
if (this.type === 'scripthash')
return this.getScriptAddress();
return this.getKeyAddress();
};
Wallet.key2hash = function key2hash(key) {
@ -351,9 +343,9 @@ Wallet.validateAddress = function validateAddress(addr, prefix) {
};
Wallet.prototype.ownOutput = function ownOutput(tx, index) {
var scriptHash = this.getFullHash();
var hash = this.getOwnHash();
var key = this.getOwnPublicKey();
var scripthash = this.getScriptHash();
var hash = this.getKeyHash();
var key = this.getPublicKey();
var keys = this.keys;
var outputs = tx.outputs.filter(function(output, i) {
@ -371,7 +363,7 @@ Wallet.prototype.ownOutput = function ownOutput(tx, index) {
if (bcoin.script.isMultisig(s, keys))
return true;
if (bcoin.script.isScripthash(s, scriptHash))
if (bcoin.script.isScripthash(s, scripthash))
return true;
return false;
@ -384,10 +376,10 @@ Wallet.prototype.ownOutput = function ownOutput(tx, index) {
};
Wallet.prototype.ownInput = function ownInput(tx, index) {
var scriptHash = this.getFullHash();
var hash = this.getOwnHash();
var key = this.getOwnPublicKey();
var redeem = this.getFullPublicKey();
var scripthash = this.getScriptHash();
var hash = this.getKeyHash();
var key = this.getPublicKey();
var redeem = this.getScript();
var keys = this.keys;
var inputs = tx.inputs.filter(function(input, i) {
@ -425,7 +417,7 @@ Wallet.prototype.ownInput = function ownInput(tx, index) {
if (bcoin.script.isMultisig(s, keys))
return true;
if (bcoin.script.isScripthash(s, scriptHash))
if (bcoin.script.isScripthash(s, scripthash))
return true;
return false;
@ -445,12 +437,13 @@ Wallet.prototype.scriptOutputs = function scriptOutputs(tx, options) {
};
Wallet.prototype.fillUnspent = function fillUnspent(tx, changeAddress) {
changeAddress = changeAddress || this.changeAddress || this.getFullAddress();
changeAddress = changeAddress || this.changeAddress || this.getAddress();
return tx.fillUnspent(this.unspent(), changeAddress);
};
Wallet.prototype.scriptInputs = function scriptInputs(tx) {
var pub = this.getFullPublicKey();
var pub = this.getPublicKey();
var redeem = this.getScript();
var inputs = tx.inputs;
inputs = inputs.filter(function(input, i) {
@ -461,7 +454,7 @@ Wallet.prototype.scriptInputs = function scriptInputs(tx) {
if (!input.out.tx || !this.ownOutput(input.out.tx))
return false;
tx.scriptInput(i, pub);
tx.scriptInput(i, pub, redeem);
return true;
}, this);
@ -495,7 +488,8 @@ Wallet.prototype.sign = function sign(tx, type) {
if (!type)
type = 'all';
var pub = this.getFullPublicKey();
var pub = this.getPublicKey();
var redeem = this.getScript();
var key = this.key;
var inputs = tx.inputs;
@ -508,7 +502,7 @@ Wallet.prototype.sign = function sign(tx, type) {
if (!input.out.tx || !this.ownOutput(input.out.tx))
return false;
tx.scriptSig(i, key, pub, type);
tx.scriptSig(i, key, pub, redeem, type);
return true;
}, this);
@ -584,8 +578,8 @@ Wallet.prototype.toAddress = function toAddress() {
});
return {
address: this.getFullAddress(),
hash: utils.toHex(this.getFullHash()),
address: this.getAddress(),
hash: utils.toHex(this.getHash()),
received: received,
sent: sent,
balance: this.balance(),
@ -602,7 +596,7 @@ Wallet.prototype.toJSON = function toJSON(encrypt) {
label: this.label,
address: this.getAddress(),
balance: utils.toBTC(this.balance()),
pub: this.getOwnPublicKey('base58'),
pub: this.getPublicKey('base58'),
priv: encrypt
? encrypt(this.getPrivateKey('base58'))
: this.getPrivateKey('base58'),

View File

@ -81,7 +81,7 @@ describe('Wallet', function() {
});
var k2 = w.getPublicKey().concat(1);
w.addKey(k2);
assert.equal(w.getOwnAddress(), w.getFullAddress());
assert.equal(w.getKeyAddress(), w.getAddress());
// Input transcation
var src = bcoin.tx({