Merge pull request #173 from dcousens/scriptclean
Cleanup of network in Transaction/Script
This commit is contained in:
commit
631f7257ef
16 changed files with 354 additions and 383 deletions
|
@ -1,5 +1,21 @@
|
|||
var assert = require('assert')
|
||||
var base58check = require('./base58check')
|
||||
var networks = require('./networks')
|
||||
var Script = require('./script')
|
||||
|
||||
function findScriptTypeByVersion(queryVersion) {
|
||||
for (var networkName in networks) {
|
||||
var network = networks[networkName]
|
||||
|
||||
for (var versionName in network) {
|
||||
var version = network[versionName]
|
||||
|
||||
if (version === queryVersion) {
|
||||
return versionName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Address(hash, version) {
|
||||
assert(Buffer.isBuffer(hash), 'First argument must be a Buffer')
|
||||
|
@ -10,18 +26,55 @@ function Address(hash, version) {
|
|||
this.version = version
|
||||
}
|
||||
|
||||
Address.Error = function(message) {
|
||||
this.name = 'AddressError'
|
||||
this.message = message
|
||||
}
|
||||
Address.Error.prototype = new Error()
|
||||
Address.Error.prototype.constructor = Address.Error
|
||||
|
||||
// Import functions
|
||||
Address.fromBase58Check = function(string) {
|
||||
var decode = base58check.decode(string)
|
||||
|
||||
return new Address(decode.payload, decode.version)
|
||||
}
|
||||
Address.prototype.fromString = Address.prototype.fromBase58Check
|
||||
|
||||
Address.fromScriptPubKey = function(script, network) {
|
||||
network = network || networks.bitcoin
|
||||
|
||||
var type = script.getOutType()
|
||||
|
||||
if (type === 'pubkeyhash') {
|
||||
return new Address(new Buffer(script.chunks[2]), network.pubKeyHash)
|
||||
}
|
||||
|
||||
else if (type === 'scripthash') {
|
||||
return new Address(new Buffer(script.chunks[1]), network.scriptHash)
|
||||
}
|
||||
|
||||
throw new Address.Error(type + ' has no matching Address')
|
||||
}
|
||||
|
||||
// Export functions
|
||||
Address.prototype.toBase58Check = function () {
|
||||
return base58check.encode(this.hash, this.version)
|
||||
}
|
||||
|
||||
Address.prototype.toScriptPubKey = function() {
|
||||
var scriptType = findScriptTypeByVersion(this.version)
|
||||
|
||||
if (scriptType === 'pubKeyHash') {
|
||||
return Script.createPubKeyHashScriptPubKey(this.hash)
|
||||
}
|
||||
|
||||
else if (scriptType === 'scriptHash') {
|
||||
return Script.createP2SHScriptPubKey(this.hash)
|
||||
}
|
||||
|
||||
throw new Address.Error(this + ' has no matching script')
|
||||
}
|
||||
|
||||
Address.prototype.toString = Address.prototype.toBase58Check
|
||||
|
||||
module.exports = Address
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
var assert = require('assert')
|
||||
var base58check = require('./base58check')
|
||||
var ecdsa = require('./ecdsa')
|
||||
var network = require('./network')
|
||||
var networks = require('./networks')
|
||||
var secureRandom = require('secure-random')
|
||||
|
||||
var Address = require('./address')
|
||||
|
@ -72,7 +72,7 @@ ECKey.prototype.toHex = function() {
|
|||
}
|
||||
|
||||
ECKey.prototype.toWIF = function(version) {
|
||||
version = version || network.bitcoin.wif
|
||||
version = version || networks.bitcoin.wif
|
||||
|
||||
var buffer = this.toBuffer()
|
||||
if (this.pub.compressed) {
|
||||
|
@ -115,7 +115,7 @@ ECPubKey.prototype.verify = function(hash, sig) {
|
|||
}
|
||||
|
||||
ECPubKey.prototype.getAddress = function(version) {
|
||||
version = version || network.bitcoin.pubKeyHash
|
||||
version = version || networks.bitcoin.pubKeyHash
|
||||
|
||||
return new Address(crypto.hash160(this.toBuffer()), version)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ var CJS = require('crypto-js')
|
|||
var crypto = require('./crypto')
|
||||
var ECKey = require('./eckey').ECKey
|
||||
var ECPubKey = require('./eckey').ECPubKey
|
||||
var Network = require('./network')
|
||||
var networks = require('./networks')
|
||||
|
||||
var sec = require('./sec')
|
||||
var ecparams = sec("secp256k1")
|
||||
|
@ -27,7 +27,7 @@ function HDWallet(seed, networkString) {
|
|||
this.chaincode = I.slice(32)
|
||||
this.network = networkString || 'bitcoin'
|
||||
|
||||
if(!Network.hasOwnProperty(this.network)) {
|
||||
if(!networks.hasOwnProperty(this.network)) {
|
||||
throw new Error("Unknown network: " + this.network)
|
||||
}
|
||||
|
||||
|
@ -70,8 +70,8 @@ HDWallet.fromBuffer = function(input) {
|
|||
var version = input.readUInt32BE(0)
|
||||
|
||||
var type
|
||||
for(var name in Network) {
|
||||
var network = Network[name]
|
||||
for(var name in networks) {
|
||||
var network = networks[name]
|
||||
|
||||
for(var t in network.bip32) {
|
||||
if (version != network.bip32[t]) continue
|
||||
|
@ -128,7 +128,7 @@ HDWallet.prototype.getAddress = function() {
|
|||
|
||||
HDWallet.prototype.toBuffer = function(priv) {
|
||||
// Version
|
||||
var version = Network[this.network].bip32[priv ? 'priv' : 'pub']
|
||||
var version = networks[this.network].bip32[priv ? 'priv' : 'pub']
|
||||
var buffer = new Buffer(HDWallet.LENGTH)
|
||||
|
||||
// 4 bytes: version bytes
|
||||
|
@ -245,7 +245,7 @@ HDWallet.prototype.derivePrivate = function(index) {
|
|||
}
|
||||
|
||||
HDWallet.prototype.getKeyVersion = function() {
|
||||
return Network[this.network].pubKeyHash
|
||||
return networks[this.network].pubKeyHash
|
||||
}
|
||||
|
||||
HDWallet.prototype.toString = HDWallet.prototype.toBase58
|
||||
|
|
|
@ -22,6 +22,6 @@ module.exports = {
|
|||
Transaction: T.Transaction,
|
||||
TransactionIn: T.TransactionIn,
|
||||
TransactionOut: T.TransactionOut,
|
||||
network: require('./network'),
|
||||
networks: require('./networks'),
|
||||
Wallet: require('./wallet')
|
||||
}
|
||||
|
|
188
src/script.js
188
src/script.js
|
@ -1,18 +1,17 @@
|
|||
var assert = require('assert')
|
||||
var Address = require('./address')
|
||||
var assert = require('assert')
|
||||
var crypto = require('./crypto')
|
||||
var convert = require('./convert')
|
||||
var Network = require('./network')
|
||||
var Opcode = require('./opcode')
|
||||
|
||||
function Script(data) {
|
||||
this.buffer = data || []
|
||||
if(!Array.isArray(this.buffer)) {
|
||||
throw new Error('expect Script to be initialized with Array, but got ' + data)
|
||||
}
|
||||
data = data || []
|
||||
assert(Array.isArray(data), 'Expected Array, got ' + data)
|
||||
|
||||
this.buffer = data
|
||||
this.parse()
|
||||
}
|
||||
|
||||
// Import operations
|
||||
Script.fromBuffer = function(buffer) {
|
||||
assert(Buffer.isBuffer(buffer)) // FIXME: transitionary
|
||||
|
||||
|
@ -23,30 +22,13 @@ Script.fromHex = function(hex) {
|
|||
return Script.fromBuffer(new Buffer(hex, 'hex'))
|
||||
}
|
||||
|
||||
Script.fromPubKey = function(str) {
|
||||
var script = new Script()
|
||||
var s = str.split(' ')
|
||||
for (var i in s) {
|
||||
if (Opcode.map.hasOwnProperty(s[i])) {
|
||||
script.writeOp(Opcode.map[s[i]])
|
||||
} else {
|
||||
script.writeBytes(convert.hexToBytes(s[i]))
|
||||
}
|
||||
}
|
||||
return script
|
||||
// Export operations
|
||||
Script.prototype.toBuffer = function() {
|
||||
return new Buffer(this.buffer)
|
||||
}
|
||||
|
||||
Script.fromScriptSig = function(str) {
|
||||
var script = new Script()
|
||||
var s = str.split(' ')
|
||||
for (var i in s) {
|
||||
if (Opcode.map.hasOwnProperty(s[i])) {
|
||||
script.writeOp(Opcode.map[s[i]])
|
||||
} else {
|
||||
script.writeBytes(convert.hexToBytes(s[i]))
|
||||
}
|
||||
}
|
||||
return script
|
||||
Script.prototype.toHex = function() {
|
||||
return this.toBuffer().toString('hex')
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -202,40 +184,10 @@ function isSmallIntOp(opcode) {
|
|||
((opcode >= Opcode.map.OP_1) && (opcode <= Opcode.map.OP_16)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address corresponding to this output in hash160 form.
|
||||
* Assumes strange scripts are P2SH
|
||||
*/
|
||||
Script.prototype.toScriptHash = function() {
|
||||
if(isPubkeyhash.call(this)) {
|
||||
return this.chunks[2]
|
||||
}
|
||||
|
||||
if(isScripthash.call(this)) {
|
||||
return crypto.hash160(this.buffer)
|
||||
}
|
||||
|
||||
Script.prototype.getHash = function() {
|
||||
return crypto.hash160(this.buffer)
|
||||
}
|
||||
|
||||
Script.prototype.getToAddress = function(network) {
|
||||
network = network || Network.bitcoin
|
||||
|
||||
if(isPubkeyhash.call(this)) {
|
||||
return new Address(new Buffer(this.chunks[2]), network.pubKeyHash)
|
||||
}
|
||||
|
||||
assert(isScripthash.call(this))
|
||||
|
||||
return new Address(new Buffer(this.chunks[1]), network.scriptHash)
|
||||
}
|
||||
|
||||
Script.prototype.getFromAddress = function(version) {
|
||||
version = version || Network.bitcoin.pubKeyHash
|
||||
|
||||
return new Address(this.simpleInHash(), version)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the script to known templates of scriptSig.
|
||||
*
|
||||
|
@ -281,58 +233,6 @@ Script.prototype.getInType = function() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the affected public key for this input.
|
||||
*
|
||||
* This currently only works with payToPubKeyHash transactions. It will also
|
||||
* work in the future for standard payToScriptHash transactions that use a
|
||||
* single public key.
|
||||
*
|
||||
* However for multi-key and other complex transactions, this will only return
|
||||
* one of the keys or raise an error. Therefore, it is recommended for indexing
|
||||
* purposes to use Script#simpleInHash or Script#simpleOutHash instead.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
Script.prototype.simpleInPubKey = function() {
|
||||
switch (this.getInType()) {
|
||||
case 'pubkeyhash':
|
||||
return this.chunks[1]
|
||||
case 'pubkey':
|
||||
// TODO: Theoretically, we could recover the pubkey from the sig here.
|
||||
// See https://bitcointalk.org/?topic=6430.0
|
||||
throw new Error('Script does not contain pubkey')
|
||||
default:
|
||||
throw new Error('Encountered non-standard scriptSig')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the affected address hash for this input.
|
||||
*
|
||||
* For standard transactions, this will return the hash of the pubKey that
|
||||
* can spend this output.
|
||||
*
|
||||
* In the future, for standard payToScriptHash inputs, this will return the
|
||||
* scriptHash.
|
||||
*
|
||||
* Note: This function provided for convenience. If you have the corresponding
|
||||
* scriptPubKey available, you are urged to use Script#simpleOutHash instead
|
||||
* as it is more reliable for non-standard payToScriptHash transactions.
|
||||
*
|
||||
* This method is useful for indexing transactions.
|
||||
*/
|
||||
Script.prototype.simpleInHash = function() {
|
||||
return crypto.hash160(this.simpleInPubKey())
|
||||
}
|
||||
|
||||
/**
|
||||
* Old name for Script#simpleInHash.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
Script.prototype.simpleInPubKeyHash = Script.prototype.simpleInHash
|
||||
|
||||
/**
|
||||
* Add an op code to the script.
|
||||
*/
|
||||
|
@ -347,7 +247,7 @@ Script.prototype.writeOp = function(opcode) {
|
|||
Script.prototype.writeBytes = function(data) {
|
||||
// FIXME: Script module doesn't support buffers yet
|
||||
if (Buffer.isBuffer(data)) data = Array.prototype.slice.call(data);
|
||||
assert(Array.isArray(data), "Expect a byte array. Got" + data)
|
||||
assert(Array.isArray(data), "Expected a byte array. Got " + data)
|
||||
|
||||
if (data.length < Opcode.map.OP_PUSHDATA1) {
|
||||
this.buffer.push(data.length)
|
||||
|
@ -369,48 +269,32 @@ Script.prototype.writeBytes = function(data) {
|
|||
this.chunks.push(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an output for an address
|
||||
*/
|
||||
Script.createOutputScript = function(address, network) {
|
||||
assert(address instanceof Address)
|
||||
network = network || Network.bitcoin
|
||||
|
||||
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
|
||||
Script.createPubKeyHashScriptPubKey = function(hash) {
|
||||
var script = new Script()
|
||||
|
||||
// Standard pay-to-script-hash
|
||||
if (address.version === network.scriptHash) {
|
||||
script.writeOp(Opcode.map.OP_HASH160)
|
||||
script.writeBytes(address.hash)
|
||||
script.writeOp(Opcode.map.OP_EQUAL)
|
||||
|
||||
return script
|
||||
}
|
||||
|
||||
assert.strictEqual(address.version, network.pubKeyHash, 'Unknown address type')
|
||||
|
||||
// Standard pay-to-pubkey-hash
|
||||
script.writeOp(Opcode.map.OP_DUP)
|
||||
script.writeOp(Opcode.map.OP_HASH160)
|
||||
script.writeBytes(address.hash)
|
||||
script.writeBytes(hash)
|
||||
script.writeOp(Opcode.map.OP_EQUALVERIFY)
|
||||
script.writeOp(Opcode.map.OP_CHECKSIG)
|
||||
|
||||
return script
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract pubkeys from a multisig script
|
||||
*/
|
||||
// OP_HASH160 {scriptHash} OP_EQUAL
|
||||
Script.createP2SHScriptPubKey = function(hash) {
|
||||
var script = new Script()
|
||||
|
||||
Script.prototype.extractPubkeys = function() {
|
||||
return this.chunks.filter(function(chunk) {
|
||||
return(chunk[0] == 4 && chunk.length == 65 || chunk[0] < 4 && chunk.length == 33)
|
||||
})
|
||||
script.writeOp(Opcode.map.OP_HASH160)
|
||||
script.writeBytes(hash)
|
||||
script.writeOp(Opcode.map.OP_EQUAL)
|
||||
|
||||
return script
|
||||
}
|
||||
|
||||
// m [pubKeys ...] n OP_CHECKMULTISIG
|
||||
Script.createMultisigOutputScript = function(m, pubKeys) {
|
||||
Script.createMultisigScriptPubKey = function(m, pubKeys) {
|
||||
var script = new Script()
|
||||
pubKeys = pubKeys.sort()
|
||||
|
||||
|
@ -433,7 +317,15 @@ Script.createPubKeyHashScriptSig = function(signature, pubKey) {
|
|||
}
|
||||
|
||||
// OP_0 [signatures ...]
|
||||
Script.createMultisigScriptSig = function(signatures) {
|
||||
Script.createMultisigScriptSig = function(signatures, scriptPubKey) {
|
||||
if (scriptPubKey) {
|
||||
assert(isMultisig.call(scriptPubKey))
|
||||
|
||||
var m = scriptPubKey.chunks[0]
|
||||
var k = m - (Opcode.map.OP_1 - 1)
|
||||
assert(k <= signatures.length, 'Not enough signatures provided')
|
||||
}
|
||||
|
||||
var inScript = new Script()
|
||||
|
||||
inScript.writeOp(Opcode.map.OP_0)
|
||||
|
@ -451,18 +343,6 @@ Script.createP2SHScriptSig = function(scriptSig, scriptPubKey) {
|
|||
return inScript
|
||||
}
|
||||
|
||||
// [signatures ...] {m [pubKeys ...] n OP_CHECKSIG}
|
||||
Script.createP2SHMultisigScriptSig = function(signatures, scriptPubKey) {
|
||||
assert(isMultisig.call(scriptPubKey))
|
||||
|
||||
var m = scriptPubKey.chunks[0]
|
||||
var k = m - (Opcode.map.OP_1 - 1)
|
||||
assert(k <= signatures.length, 'Not enough signatures provided')
|
||||
|
||||
var scriptSig = Script.createMultisigScriptSig(signatures)
|
||||
return Script.createP2SHScriptSig(scriptSig, scriptPubKey)
|
||||
}
|
||||
|
||||
Script.prototype.clone = function() {
|
||||
return new Script(this.buffer)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ var convert = require('./convert')
|
|||
var crypto = require('./crypto')
|
||||
var ECKey = require('./eckey').ECKey
|
||||
var ecdsa = require('./ecdsa')
|
||||
var Network = require('./network')
|
||||
|
||||
var Transaction = function (doc) {
|
||||
if (!(this instanceof Transaction)) { return new Transaction(doc) }
|
||||
|
@ -90,11 +89,10 @@ Transaction.prototype.addInput = function (tx, outIndex) {
|
|||
* i) An existing TransactionOut object
|
||||
* ii) An address object or a string address, and a value
|
||||
* iii) An address:value string
|
||||
* iv) Either ii), iii) with an optional network argument
|
||||
*
|
||||
* FIXME: This is a bit convoluted
|
||||
*/
|
||||
Transaction.prototype.addOutput = function (address, value, network) {
|
||||
Transaction.prototype.addOutput = function (address, value) {
|
||||
if (arguments[0] instanceof TransactionOut) {
|
||||
this.outs.push(arguments[0])
|
||||
return
|
||||
|
@ -105,19 +103,15 @@ Transaction.prototype.addOutput = function (address, value, network) {
|
|||
var args = arguments[0].split(':')
|
||||
address = args[0]
|
||||
value = parseInt(args[1])
|
||||
|
||||
network = arguments[1]
|
||||
}
|
||||
|
||||
address = Address.fromBase58Check(address)
|
||||
}
|
||||
|
||||
network = network || Network.bitcoin
|
||||
|
||||
this.outs.push(new TransactionOut({
|
||||
value: value,
|
||||
script: Script.createOutputScript(address, network),
|
||||
network: network
|
||||
script: address.toScriptPubKey(),
|
||||
address: address // TODO: Remove me
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -364,56 +358,18 @@ Transaction.deserialize = function(buffer) {
|
|||
|
||||
/**
|
||||
* Signs a standard output at some index with the given key
|
||||
* FIXME: network support is ugly
|
||||
*/
|
||||
Transaction.prototype.sign = function(index, key, type, network) {
|
||||
Transaction.prototype.sign = function(index, key, type) {
|
||||
assert(key instanceof ECKey)
|
||||
network = network || Network.bitcoin
|
||||
|
||||
var address = key.pub.getAddress(network.pubKeyHash)
|
||||
|
||||
// FIXME: Assumed prior TX was pay-to-pubkey-hash
|
||||
var script = Script.createOutputScript(address, network)
|
||||
var script = key.pub.getAddress().toScriptPubKey()
|
||||
var signature = this.signScriptSig(index, script, key, type)
|
||||
|
||||
// FIXME: Assumed prior TX was pay-to-pubkey-hash
|
||||
var scriptSig = Script.createPubKeyHashScriptSig(signature, key.pub)
|
||||
this.setScriptSig(index, scriptSig)
|
||||
}
|
||||
|
||||
// Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...]
|
||||
Transaction.prototype.signWithKeys = function(keys, outputs, type) {
|
||||
type = type || SIGHASH_ALL
|
||||
|
||||
var addrdata = keys.map(function(key) {
|
||||
assert(key instanceof ECKey)
|
||||
|
||||
return {
|
||||
key: key,
|
||||
address: key.getAddress().toString()
|
||||
}
|
||||
})
|
||||
|
||||
var hmap = {}
|
||||
outputs.forEach(function(o) {
|
||||
hmap[o.output] = o
|
||||
})
|
||||
|
||||
for (var i = 0; i < this.ins.length; i++) {
|
||||
var outpoint = this.ins[i].outpoint.hash + ':' + this.ins[i].outpoint.index
|
||||
var histItem = hmap[outpoint]
|
||||
|
||||
if (!histItem) continue;
|
||||
|
||||
var thisInputAddrdata = addrdata.filter(function(a) {
|
||||
return a.address == histItem.address
|
||||
})
|
||||
|
||||
if (thisInputAddrdata.length === 0) continue;
|
||||
|
||||
this.sign(i,thisInputAddrdata[0].key)
|
||||
}
|
||||
}
|
||||
|
||||
Transaction.prototype.signScriptSig = function(index, script, key, type) {
|
||||
type = type || SIGHASH_ALL
|
||||
|
||||
|
@ -458,14 +414,8 @@ var TransactionIn = function (data) {
|
|||
this.outpoint = { hash: data.hash, index: data.index }
|
||||
}
|
||||
|
||||
if (data.scriptSig) {
|
||||
this.script = Script.fromScriptSig(data.scriptSig)
|
||||
} else if (data.script) {
|
||||
this.script = data.script
|
||||
} else {
|
||||
this.script = new Script(data.script)
|
||||
}
|
||||
|
||||
assert(data.script, 'Invalid TxIn parameters')
|
||||
this.script = data.script
|
||||
this.sequence = data.sequence || this.defaultSequence
|
||||
}
|
||||
|
||||
|
@ -480,38 +430,20 @@ TransactionIn.prototype.clone = function () {
|
|||
})
|
||||
}
|
||||
|
||||
// FIXME: Support for alternate networks
|
||||
var TransactionOut = function (data) {
|
||||
this.script =
|
||||
data.script instanceof Script ? data.script.clone()
|
||||
: Array.isArray(data.script) ? new Script(data.script)
|
||||
: typeof data.script == "string" ? new Script(convert.hexToBytes(data.script))
|
||||
: data.scriptPubKey ? Script.fromScriptSig(data.scriptPubKey)
|
||||
: data.address ? Script.createOutputScript(data.address)
|
||||
: new Script()
|
||||
function TransactionOut(data) {
|
||||
this.script = data.script
|
||||
this.value = data.value
|
||||
this.address = data.address
|
||||
|
||||
var network = data.network || Network.bitcoin
|
||||
if (this.script.buffer.length > 0) {
|
||||
this.address = this.script.getToAddress(network)
|
||||
}
|
||||
|
||||
this.value =
|
||||
Array.isArray(data.value) ? convert.bytesToNum(data.value)
|
||||
: "string" == typeof data.value ? parseInt(data.value)
|
||||
: data.value instanceof BigInteger ? parseInt(data.value.toString())
|
||||
: data.value
|
||||
if (data.address) this.address = data.address
|
||||
}
|
||||
|
||||
TransactionOut.prototype.clone = function() {
|
||||
var newTxout = new TransactionOut({
|
||||
return new TransactionOut({
|
||||
script: this.script.clone(),
|
||||
value: this.value
|
||||
value: this.value,
|
||||
address: this.address
|
||||
})
|
||||
return newTxout
|
||||
}
|
||||
|
||||
TransactionOut.prototype.scriptPubKey = function() {
|
||||
return convert.bytesToHex(this.script.buffer)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
var Address = require('./address')
|
||||
var convert = require('./convert')
|
||||
var Transaction = require('./transaction').Transaction
|
||||
var HDNode = require('./hdwallet.js')
|
||||
var networks = require('./networks')
|
||||
var rng = require('secure-random')
|
||||
var Network = require('./network')
|
||||
var Transaction = require('./transaction').Transaction
|
||||
|
||||
function Wallet(seed, options) {
|
||||
if (!(this instanceof Wallet)) { return new Wallet(seed, options); }
|
||||
|
@ -42,7 +43,6 @@ function Wallet(seed, options) {
|
|||
}
|
||||
this.newMasterKey(seed, network)
|
||||
|
||||
|
||||
this.generateAddress = function() {
|
||||
var key = externalAccount.derive(this.addresses.length)
|
||||
this.addresses.push(key.getAddress().toString())
|
||||
|
@ -150,9 +150,17 @@ function Wallet(seed, options) {
|
|||
var txhash = tx.getHash()
|
||||
|
||||
tx.outs.forEach(function(txOut, i){
|
||||
var address = txOut.address.toString()
|
||||
var address
|
||||
|
||||
try {
|
||||
address = Address.fromScriptPubKey(txOut.script, networks[network]).toString()
|
||||
} catch(e) {
|
||||
if (!(e instanceof Address.Error)) throw e
|
||||
}
|
||||
|
||||
if (isMyAddress(address)) {
|
||||
var output = txhash+':'+i
|
||||
var output = txhash + ':' + i
|
||||
|
||||
me.outputs[output] = {
|
||||
receive: output,
|
||||
value: txOut.value,
|
||||
|
@ -165,7 +173,7 @@ function Wallet(seed, options) {
|
|||
var op = txIn.outpoint
|
||||
var o = me.outputs[op.hash+':'+op.index]
|
||||
if (o) {
|
||||
o.spend = txhash+':'+i
|
||||
o.spend = txhash + ':' +i
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -174,7 +182,7 @@ function Wallet(seed, options) {
|
|||
checkDust(value)
|
||||
|
||||
var tx = new Transaction()
|
||||
tx.addOutput(to, value, Network[network])
|
||||
tx.addOutput(to, value)
|
||||
|
||||
var utxo = getCandidateOutputs(value)
|
||||
var totalInValue = 0
|
||||
|
@ -190,7 +198,7 @@ function Wallet(seed, options) {
|
|||
|
||||
var change = totalInValue - value - fee
|
||||
if(change > 0 && !isDust(change)) {
|
||||
tx.addOutput(changeAddress || getChangeAddress(), change, Network[network])
|
||||
tx.addOutput(changeAddress || getChangeAddress(), change)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -246,7 +254,7 @@ function Wallet(seed, options) {
|
|||
|
||||
function estimateFeePadChangeOutput(tx){
|
||||
var tmpTx = tx.clone()
|
||||
tmpTx.addOutput(getChangeAddress(), 0, Network[network])
|
||||
tmpTx.addOutput(getChangeAddress(), 0)
|
||||
return tmpTx.estimateFee()
|
||||
}
|
||||
|
||||
|
@ -266,7 +274,7 @@ function Wallet(seed, options) {
|
|||
tx.ins.forEach(function(inp,i) {
|
||||
var output = me.outputs[inp.outpoint.hash + ':' + inp.outpoint.index]
|
||||
if (output) {
|
||||
tx.sign(i, me.getPrivateKeyForAddress(output.address), false, Network[network])
|
||||
tx.sign(i, me.getPrivateKeyForAddress(output.address), false)
|
||||
}
|
||||
})
|
||||
return tx
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
var assert = require('assert')
|
||||
var Address = require('..').Address
|
||||
var networks = require('..').networks
|
||||
var Script = require('..').Script
|
||||
|
||||
var b58fixtures = require('./fixtures/base58')
|
||||
var fixtures = require('./fixtures/address')
|
||||
|
||||
describe('Address', function() {
|
||||
var bothVectors = fixtures.pubKeyHash.concat(fixtures.scriptHash)
|
||||
function h2b(h) { return new Buffer(h, 'hex') }
|
||||
|
||||
describe('Address', function() {
|
||||
describe('Constructor', function() {
|
||||
it('does not mutate the input', function() {
|
||||
bothVectors.forEach(function(f) {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
var hash = new Buffer(f.hex, 'hex')
|
||||
var addr = new Address(hash, f.version)
|
||||
|
||||
|
@ -28,8 +30,8 @@ describe('Address', function() {
|
|||
})
|
||||
})
|
||||
|
||||
bothVectors.forEach(function(f) {
|
||||
it('imports ' + f.description + ' correctly', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
it('imports ' + f.description + '(' + f.network + ') correctly', function() {
|
||||
var addr = Address.fromBase58Check(f.base58check)
|
||||
|
||||
assert.equal(addr.version, f.version)
|
||||
|
@ -38,9 +40,21 @@ describe('Address', function() {
|
|||
})
|
||||
})
|
||||
|
||||
describe('fromScriptPubKey', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
it('imports ' + f.description + '(' + f.network + ') correctly', function() {
|
||||
var script = Script.fromHex(f.script)
|
||||
var addr = Address.fromScriptPubKey(script, networks[f.network])
|
||||
|
||||
assert.equal(addr.version, f.version)
|
||||
assert.equal(addr.hash.toString('hex'), f.hex)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('toBase58Check', function() {
|
||||
bothVectors.forEach(function(f) {
|
||||
it('exports ' + f.description + ' correctly', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
it('exports ' + f.description + '(' + f.network + ') correctly', function() {
|
||||
var addr = Address.fromBase58Check(f.base58check)
|
||||
var result = addr.toBase58Check()
|
||||
|
||||
|
@ -48,4 +62,25 @@ describe('Address', function() {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('toScriptPubKey', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
it('imports ' + f.description + '(' + f.network + ') correctly', function() {
|
||||
var addr = Address.fromBase58Check(f.base58check)
|
||||
var script = addr.toScriptPubKey()
|
||||
|
||||
assert.equal(script.toHex(), f.script)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.toScriptPubKey.forEach(function(f) {
|
||||
it('throws on ' + f.description, function() {
|
||||
var addr = new Address(h2b(f.hex), f.version)
|
||||
|
||||
assert.throws(function() {
|
||||
addr.toScriptPubKey()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
47
test/fixtures/address.js
vendored
47
test/fixtures/address.js
vendored
|
@ -1,30 +1,45 @@
|
|||
module.exports = {
|
||||
pubKeyHash: [
|
||||
valid: [
|
||||
{
|
||||
description: 'pubKeyHash (bitcoin)',
|
||||
description: 'pubKeyHash',
|
||||
network: 'bitcoin',
|
||||
version: 0,
|
||||
hex: '751e76e8199196d454941c45d1b3a323f1433bd6',
|
||||
base58check: '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH'
|
||||
base58check: '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH',
|
||||
script: '76a914751e76e8199196d454941c45d1b3a323f1433bd688ac'
|
||||
},
|
||||
{
|
||||
description: 'pubKeyHash (testnet)',
|
||||
description: 'pubKeyHash',
|
||||
network: 'testnet',
|
||||
version: 111,
|
||||
hex: '751e76e8199196d454941c45d1b3a323f1433bd6',
|
||||
base58check: 'mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r'
|
||||
}
|
||||
],
|
||||
scriptHash: [
|
||||
{
|
||||
description: 'scriptHash (bitcoin)',
|
||||
version: 5,
|
||||
hex: 'cd7b44d0b03f2d026d1e586d7ae18903b0d385f6',
|
||||
base58check: '3LRW7jeCvQCRdPF8S3yUCfRAx4eqXFmdcr'
|
||||
base58check: 'mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r',
|
||||
script: '76a914751e76e8199196d454941c45d1b3a323f1433bd688ac'
|
||||
},
|
||||
{
|
||||
description: 'scriptHash (testnet)',
|
||||
description: 'scriptHash',
|
||||
network: 'bitcoin',
|
||||
version: 5,
|
||||
hex: 'cd7b44d0b03f2d026d1e586d7ae18903b0d385f6',
|
||||
base58check: '3LRW7jeCvQCRdPF8S3yUCfRAx4eqXFmdcr',
|
||||
script: 'a914cd7b44d0b03f2d026d1e586d7ae18903b0d385f687'
|
||||
},
|
||||
{
|
||||
description: 'scriptHash',
|
||||
network: 'testnet',
|
||||
version: 196,
|
||||
hex: 'cd7b44d0b03f2d026d1e586d7ae18903b0d385f6',
|
||||
base58check: '2NByiBUaEXrhmqAsg7BbLpcQSAQs1EDwt5w'
|
||||
base58check: '2NByiBUaEXrhmqAsg7BbLpcQSAQs1EDwt5w',
|
||||
script: 'a914cd7b44d0b03f2d026d1e586d7ae18903b0d385f687'
|
||||
}
|
||||
]
|
||||
],
|
||||
invalid: {
|
||||
toScriptPubKey: [
|
||||
{
|
||||
description: 'Unknown Address version',
|
||||
version: 0x99,
|
||||
hex: 'cd7b44d0b03f2d026d1e586d7ae18903b0d385f6'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
54
test/fixtures/script.js
vendored
Normal file
54
test/fixtures/script.js
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
module.exports = {
|
||||
valid: [
|
||||
{
|
||||
description: 'P2SH ScriptPubKey',
|
||||
hex: 'a914e8c300c87986efa84c37c0519929019ef86eb5b487',
|
||||
type: 'scripthash',
|
||||
hash: '0ba47b56a573bab4b430ad6ed3ec79270e04b066',
|
||||
scriptPubKey: true
|
||||
},
|
||||
{
|
||||
description: 'PubKeyHash ScriptPubKey',
|
||||
hex: '76a9145a3acbc7bbcc97c5ff16f5909c9d7d3fadb293a888ac',
|
||||
type: 'pubkeyhash',
|
||||
hash: 'a5313f33d5c7b81674b35f7f3febc3522ef234db',
|
||||
scriptPubKey: true
|
||||
},
|
||||
{
|
||||
description: 'pubKeyHash scriptSig',
|
||||
hex: '48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f30141040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8',
|
||||
type: 'pubkeyhash',
|
||||
hash: 'b9bac2a5c5c29bb27c382d41fa3d179c646c78fd',
|
||||
scriptPubKey: false
|
||||
},
|
||||
{
|
||||
description: 'Valid multisig script',
|
||||
hex: '5121032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca330162102308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a52ae',
|
||||
type: 'multisig',
|
||||
hash: 'f1c98f0b74ecabcf78ae20dfa224bb6666051fbe',
|
||||
scriptPubKey: true
|
||||
},
|
||||
{
|
||||
description: 'OP_RETURN script',
|
||||
hex:'6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474',
|
||||
type: 'nulldata',
|
||||
hash: 'ec88f016655477663455fe6a8e83508c348ea145',
|
||||
scriptPubKey: true
|
||||
},
|
||||
{
|
||||
description: 'Non standard script',
|
||||
hex: 'aa206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000087',
|
||||
type: 'nonstandard',
|
||||
hash: '3823382e70d1930989813d3459988e0d7c2861d8',
|
||||
scriptPubKey: true
|
||||
},
|
||||
{
|
||||
description: 'Invalid multisig script',
|
||||
asm: '0 0 0 OP_CHECKmulTISIG',
|
||||
hex: '000000ae',
|
||||
type: 'nonstandard',
|
||||
hash: '62ede8963f9387544935f168745262f703dab1fb',
|
||||
scriptPubKey: true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -5,7 +5,7 @@ var ECKey = require('../../src/eckey').ECKey;
|
|||
var T = require('../../src/transaction');
|
||||
var Transaction = T.Transaction;
|
||||
var Script = require('../../src/script');
|
||||
var network = require('../../src/network');
|
||||
var networks = require('../../src/networks');
|
||||
var crypto = require('../../src/crypto');
|
||||
|
||||
var helloblock = require('helloblock-js')({
|
||||
|
@ -31,7 +31,7 @@ describe('p2sh', function() {
|
|||
})
|
||||
var redeemScript = Script.createMultisigOutputScript(2, pubKeyBuffers)
|
||||
var hash160 = crypto.hash160(redeemScript.buffer)
|
||||
var multisigAddress = new Address(hash160, network.testnet.scriptHash)
|
||||
var multisigAddress = new Address(hash160, networks.testnet.scriptHash)
|
||||
|
||||
// Check what our target address's starting value is
|
||||
var targetAddress = 'mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r';
|
||||
|
@ -50,7 +50,7 @@ describe('p2sh', function() {
|
|||
var tx = new Transaction()
|
||||
var unspent = resource[0];
|
||||
tx.addInput(unspent.txHash, unspent.index)
|
||||
tx.addOutput(targetAddress, 100000, network.testnet)
|
||||
tx.addOutput(targetAddress, 100000, networks.testnet)
|
||||
|
||||
var signatures = privKeys.map(function(privKey) {
|
||||
return tx.signScriptSig(0, redeemScript, privKey)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
var assert = require('assert')
|
||||
var ECKey = require('../src/eckey').ECKey
|
||||
var Message = require('../').Message
|
||||
var network = require('../').network
|
||||
var networks = require('../').networks
|
||||
|
||||
var fixtures = require('./fixtures/message')
|
||||
|
||||
|
@ -86,7 +86,7 @@ describe('Message', function() {
|
|||
var key = ECKey.makeRandom()
|
||||
var sig = Message.sign(key, msg)
|
||||
|
||||
var addr = key.pub.getAddress(network.testnet.pubKeyHash)
|
||||
var addr = key.pub.getAddress(networks.testnet.pubKeyHash)
|
||||
assert(Message.verify(addr, sig, msg))
|
||||
})
|
||||
})
|
||||
|
|
132
test/script.js
132
test/script.js
|
@ -1,34 +1,16 @@
|
|||
var assert = require('assert')
|
||||
var crypto = require('..').crypto
|
||||
var network = require('..').network
|
||||
var networks = require('..').networks
|
||||
|
||||
var Address = require('../src/address.js')
|
||||
var Script = require('../src/script.js')
|
||||
|
||||
var fixtures = require('./fixtures/script')
|
||||
|
||||
function b2h(b) { return new Buffer(b).toString('hex') }
|
||||
function h2b(h) { return new Buffer(h, 'hex') }
|
||||
|
||||
describe('Script', function() {
|
||||
var p2shScriptPubKey, pubkeyScriptPubkey, addressScriptSig
|
||||
|
||||
beforeEach(function(){
|
||||
p2shScriptPubKey = "a914e8c300c87986efa84c37c0519929019ef86eb5b487"
|
||||
pubkeyScriptPubKey = "76a9145a3acbc7bbcc97c5ff16f5909c9d7d3fadb293a888ac"
|
||||
addressScriptSig = "48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f30141040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8"
|
||||
|
||||
// txid: 09dd94f2c85262173da87a745a459007bb1eed6eeb6bfa238a0cd91a16cf7790
|
||||
validMultisigScript = '5121032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca330162102308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a52ae'
|
||||
|
||||
// txid: 5e9be7fb36ee49ce84bee4c8ef38ad0efc0608b78dae1c2c99075297ef527890
|
||||
opreturnScript = '6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474'
|
||||
|
||||
// asm: "0 0 0 OP_CHECKMULTISIG"
|
||||
invalidMultisigScript = '000000ae'
|
||||
|
||||
// txid: a4bfa8ab6435ae5f25dae9d89e4eb67dfa94283ca751f393c1ddc5a837bbc31b
|
||||
nonStandardScript = 'aa206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000087'
|
||||
})
|
||||
|
||||
describe('constructor', function() {
|
||||
it('works for a byte array', function() {
|
||||
assert.ok(new Script([]))
|
||||
|
@ -43,65 +25,69 @@ describe('Script', function() {
|
|||
})
|
||||
})
|
||||
|
||||
describe('getOutType', function() {
|
||||
it('supports p2sh', function() {
|
||||
var script = Script.fromHex(p2shScriptPubKey)
|
||||
assert.equal(script.getOutType(), 'scripthash')
|
||||
describe('fromHex/toHex', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
it('decodes/encodes ' + f.description, function() {
|
||||
assert.equal(Script.fromHex(f.hex).toHex(), f.hex)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('supports pubkeyhash', function() {
|
||||
var script = Script.fromHex(pubkeyScriptPubKey)
|
||||
assert.equal(script.getOutType(), 'pubkeyhash')
|
||||
})
|
||||
describe('getHash', function() {
|
||||
it('matches the test vectors', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
var script = Script.fromHex(f.hex)
|
||||
|
||||
it('supports multisig', function() {
|
||||
var script = Script.fromHex(validMultisigScript)
|
||||
assert.equal(script.getOutType(), 'multisig')
|
||||
})
|
||||
|
||||
it('supports null_data', function() {
|
||||
var script = Script.fromHex(opreturnScript)
|
||||
assert.equal(script.getOutType(), 'nulldata')
|
||||
})
|
||||
|
||||
it('supports nonstandard script', function() {
|
||||
var script = Script.fromHex(nonStandardScript)
|
||||
assert.equal(script.getOutType(), 'nonstandard')
|
||||
})
|
||||
|
||||
it('identifies invalid multisig script as nonstandard', function() {
|
||||
var script = Script.fromHex(invalidMultisigScript)
|
||||
assert.equal(script.getOutType(), 'nonstandard')
|
||||
assert.equal(script.getHash().toString('hex'), f.hash)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getInType', function() {
|
||||
it('works for address', function() {
|
||||
var script = Script.fromHex(addressScriptSig)
|
||||
assert.equal(script.getInType(), 'pubkeyhash')
|
||||
fixtures.valid.forEach(function(f) {
|
||||
if (!f.scriptPubKey) {
|
||||
it('supports ' + f.description, function() {
|
||||
var script = Script.fromHex(f.hex)
|
||||
|
||||
assert.equal(script.getInType(), f.type)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('getToAddress', function() {
|
||||
it('works for p2sh type output', function() {
|
||||
var script = Script.fromHex(p2shScriptPubKey)
|
||||
assert.equal(script.getToAddress().toString(), '3NukJ6fYZJ5Kk8bPjycAnruZkE5Q7UW7i8')
|
||||
})
|
||||
describe('getOutType', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
if (f.scriptPubKey) {
|
||||
it('supports ' + f.description, function() {
|
||||
var script = Script.fromHex(f.hex)
|
||||
|
||||
it('works for pubkey type output', function() {
|
||||
var script = Script.fromHex(pubkeyScriptPubKey)
|
||||
assert.equal(script.getToAddress().toString(), '19E6FV3m3kEPoJD5Jz6dGKdKwTVvjsWUvu')
|
||||
assert.equal(script.getOutType(), f.type)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('getFromAddress', function() {
|
||||
it('works for address type input', function() {
|
||||
var script = Script.fromHex(addressScriptSig)
|
||||
assert.equal(script.getFromAddress().toString(), '1BBjuhF2jHxu7tPinyQGCuaNhEs6f5u59u')
|
||||
describe('pay-to-pubKeyHash', function() {
|
||||
it('matches the test data', function() {
|
||||
var address = Address.fromBase58Check('19E6FV3m3kEPoJD5Jz6dGKdKwTVvjsWUvu')
|
||||
var script = Script.createPubKeyHashScriptPubKey(address.hash)
|
||||
|
||||
// FIXME: not good TDD
|
||||
assert.equal(script.toHex(), fixtures.valid[1].hex)
|
||||
})
|
||||
})
|
||||
|
||||
describe('2-of-3 Multi-Signature', function() {
|
||||
describe('pay-to-scriptHash', function() {
|
||||
it('matches the test data', function() {
|
||||
var hash = new Buffer('e8c300c87986efa84c37c0519929019ef86eb5b4', 'hex')
|
||||
var script = Script.createP2SHScriptPubKey(hash)
|
||||
|
||||
// FIXME: not good TDD
|
||||
assert.equal(script.toHex(), fixtures.valid[0].hex)
|
||||
})
|
||||
})
|
||||
|
||||
describe('2-of-3 Multi-Signature scriptPubKey', function() {
|
||||
var pubKeys
|
||||
|
||||
beforeEach(function() {
|
||||
|
@ -113,10 +99,10 @@ describe('Script', function() {
|
|||
})
|
||||
|
||||
it('should create valid redeemScript', function() {
|
||||
var redeemScript = Script.createMultisigOutputScript(2, pubKeys)
|
||||
var redeemScript = Script.createMultisigScriptPubKey(2, pubKeys)
|
||||
|
||||
var hash160 = crypto.hash160(redeemScript.buffer)
|
||||
var multisigAddress = new Address(hash160, network.bitcoin.scriptHash)
|
||||
var multisigAddress = new Address(hash160, networks.bitcoin.scriptHash)
|
||||
|
||||
assert.equal(multisigAddress.toString(), '32vYjxBb7pHJJyXgNk8UoK3BdRDxBzny2v')
|
||||
})
|
||||
|
@ -134,10 +120,20 @@ describe('Script', function() {
|
|||
var expected = '0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae'
|
||||
|
||||
it('should create a valid P2SH multisig scriptSig', function() {
|
||||
var redeemScript = Script.createMultisigOutputScript(2, pubKeys)
|
||||
var actual = Script.createP2SHMultisigScriptSig(signatures, redeemScript)
|
||||
var redeemScript = Script.createMultisigScriptPubKey(2, pubKeys)
|
||||
var redeemScriptSig = Script.createMultisigScriptSig(signatures)
|
||||
|
||||
assert.equal(b2h(actual.buffer), expected)
|
||||
var scriptSig = Script.createP2SHScriptSig(redeemScriptSig, redeemScript)
|
||||
|
||||
assert.equal(b2h(scriptSig.buffer), expected)
|
||||
})
|
||||
|
||||
it('should throw on not enough signatures', function() {
|
||||
var redeemScript = Script.createMultisigScriptPubKey(2, pubKeys)
|
||||
|
||||
assert.throws(function() {
|
||||
Script.createMultisigScriptSig(signatures.slice(1), redeemScript)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -2,11 +2,11 @@ var assert = require('assert')
|
|||
|
||||
var Address = require('../src/address')
|
||||
var ECKey = require('../src/eckey').ECKey
|
||||
var networks = require('..').networks
|
||||
var T = require('../src/transaction')
|
||||
var Transaction = T.Transaction
|
||||
var TransactionOut = T.TransactionOut
|
||||
var Script = require('../src/script')
|
||||
var network = require('..').network
|
||||
|
||||
var fixtureTxes = require('./fixtures/mainnet_tx')
|
||||
var fixtureTx1Hex = fixtureTxes.prevTx
|
||||
|
@ -72,9 +72,7 @@ describe('Transaction', function() {
|
|||
var output = tx.outs[0]
|
||||
|
||||
assert.equal(output.value, 5000000000)
|
||||
assert.equal(b2h(output.script.toScriptHash()), "dd40dedd8f7e37466624c4dacc6362d8e7be23dd")
|
||||
// assert.equal(output.address.toString(), "n1gqLjZbRH1biT5o4qiVMiNig8wcCPQeB9")
|
||||
// TODO: address is wrong because it's a testnet transaction. Transaction needs to support testnet
|
||||
assert.deepEqual(output.script, Address.fromBase58Check('n1gqLjZbRH1biT5o4qiVMiNig8wcCPQeB9').toScriptPubKey())
|
||||
})
|
||||
|
||||
it('assigns hash to deserialized object', function(){
|
||||
|
@ -176,7 +174,7 @@ describe('Transaction', function() {
|
|||
it('supports alternative networks', function(){
|
||||
var addr = 'mkHJaNR7uuwRG1JrmTZsV4MszaTKjCBvCR'
|
||||
|
||||
tx.addOutput(addr, 40000, network.testnet)
|
||||
tx.addOutput(addr, 40000)
|
||||
verifyTransactionOut()
|
||||
|
||||
assert.equal(tx.outs[0].address.toString(), addr)
|
||||
|
@ -250,7 +248,7 @@ describe('Transaction', function() {
|
|||
it('works for multi-sig redeem script', function() {
|
||||
var tx = new Transaction()
|
||||
tx.addInput('d6f72aab8ff86ff6289842a0424319bf2ddba85dc7c52757912297f948286389', 0)
|
||||
tx.addOutput('mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r', 1, network.testnet)
|
||||
tx.addOutput('mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r', 1)
|
||||
|
||||
var privKeys = [
|
||||
'5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf',
|
||||
|
@ -260,13 +258,14 @@ describe('Transaction', function() {
|
|||
})
|
||||
var pubKeys = privKeys.map(function(eck) { return eck.pub })
|
||||
var pubKeyBuffers = pubKeys.map(function(q) { return q.toBuffer() })
|
||||
var redeemScript = Script.createMultisigOutputScript(2, pubKeyBuffers)
|
||||
var redeemScript = Script.createMultisigScriptPubKey(2, pubKeyBuffers)
|
||||
|
||||
var signatures = privKeys.map(function(privKey) {
|
||||
return tx.signScriptSig(0, redeemScript, privKey)
|
||||
})
|
||||
|
||||
var scriptSig = Script.createP2SHMultisigScriptSig(signatures, redeemScript)
|
||||
var redeemScriptSig = Script.createMultisigScriptSig(signatures)
|
||||
var scriptSig = Script.createP2SHScriptSig(redeemScriptSig, redeemScript)
|
||||
tx.setScriptSig(0, scriptSig)
|
||||
|
||||
signatures.forEach(function(sig, i){
|
||||
|
@ -279,18 +278,6 @@ describe('Transaction', function() {
|
|||
})
|
||||
|
||||
describe('TransactionOut', function() {
|
||||
describe('scriptPubKey', function() {
|
||||
it('returns hex string', function() {
|
||||
var address = Address.fromBase58Check("1AZpKpcfCzKDUeTFBQUL4MokQai3m3HMXv")
|
||||
|
||||
var txOut = new TransactionOut({
|
||||
value: 50000,
|
||||
script: Script.createOutputScript(address)
|
||||
})
|
||||
|
||||
assert.equal(txOut.scriptPubKey(), "76a91468edf28474ee22f68dfe7e56e76c017c1701b84f88ac")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
var Wallet = require('../src/wallet.js')
|
||||
var assert = require('assert')
|
||||
var crypto = require('../').crypto
|
||||
var sinon = require('sinon')
|
||||
|
||||
var Address = require('..').Address
|
||||
var HDNode = require('../src/hdwallet.js')
|
||||
var T = require('../src/transaction.js')
|
||||
var Transaction = T.Transaction
|
||||
var TransactionOut = T.TransactionOut
|
||||
var Script = require('../src/script.js')
|
||||
var assert = require('assert')
|
||||
var sinon = require('sinon')
|
||||
var crypto = require('../').crypto
|
||||
var Wallet = require('../src/wallet.js')
|
||||
|
||||
var fixtureTxes = require('./fixtures/mainnet_tx')
|
||||
var fixtureTx1Hex = fixtureTxes.prevTx
|
||||
|
@ -308,17 +310,24 @@ describe('Wallet', function() {
|
|||
})
|
||||
|
||||
describe('processTx', function(){
|
||||
var addresses
|
||||
var tx
|
||||
|
||||
beforeEach(function(){
|
||||
addresses = [
|
||||
'115qa7iPZqn6as57hxLL8E9VUnhmGQxKWi',
|
||||
'1Bu3bhwRmevHLAy1JrRB6AfcxfgDG2vXRd',
|
||||
'1BBjuhF2jHxu7tPinyQGCuaNhEs6f5u59u'
|
||||
]
|
||||
|
||||
tx = Transaction.deserialize(fixtureTx1Hex)
|
||||
})
|
||||
|
||||
describe("when tx outs contains an address owned by the wallet, an 'output' gets added to wallet.outputs", function(){
|
||||
it("works for receive address", function(){
|
||||
var totalOuts = outputCount()
|
||||
wallet.addresses = [tx.outs[0].address.toString()]
|
||||
|
||||
wallet.addresses = [addresses[0]]
|
||||
wallet.processTx(tx)
|
||||
|
||||
assert.equal(outputCount(), totalOuts + 1)
|
||||
|
@ -327,7 +336,7 @@ describe('Wallet', function() {
|
|||
|
||||
it("works for change address", function(){
|
||||
var totalOuts = outputCount()
|
||||
wallet.changeAddresses = [tx.outs[1].address.toString()]
|
||||
wallet.changeAddresses = [addresses[1]]
|
||||
|
||||
wallet.processTx(tx)
|
||||
|
||||
|
@ -345,13 +354,15 @@ describe('Wallet', function() {
|
|||
var output = wallet.outputs[key]
|
||||
assert.equal(output.receive, key)
|
||||
assert.equal(output.value, txOut.value)
|
||||
assert.equal(output.address, txOut.address)
|
||||
|
||||
var txOutAddress = Address.fromScriptPubKey(txOut.script).toString()
|
||||
assert.equal(output.address, txOutAddress)
|
||||
}
|
||||
})
|
||||
|
||||
describe("when tx ins outpoint contains a known txhash:i, the corresponding 'output' gets updated", function(){
|
||||
beforeEach(function(){
|
||||
wallet.addresses = [tx.outs[0].address.toString()] // the address fixtureTx2 used as input
|
||||
wallet.addresses = [addresses[0]] // the address fixtureTx2 used as input
|
||||
wallet.processTx(tx)
|
||||
|
||||
tx = Transaction.deserialize(fixtureTx2Hex)
|
||||
|
|
Loading…
Reference in a new issue