Matias Alejo Garcia
11 years ago
38 changed files with 4339 additions and 4402 deletions
@ -1,22 +1,19 @@ |
|||||
require('classtool'); |
'use strict'; |
||||
|
var imports = require('soop').imports(); |
||||
|
var parent = imports.parent || require('./util/VersionedData'); |
||||
|
|
||||
function ClassSpec(b) { |
function Address() { |
||||
var superclass = b.superclass || require('./util/VersionedData').class(); |
Address.super(this, arguments); |
||||
|
} |
||||
function Address() { |
|
||||
Address.super(this, arguments); |
|
||||
} |
|
||||
|
|
||||
Address.superclass = superclass; |
Address.parent = parent; |
||||
superclass.applyEncodingsTo(Address); |
parent.applyEncodingsTo(Address); |
||||
|
|
||||
Address.prototype.validate = function() { |
Address.prototype.validate = function() { |
||||
this.doAsBinary(function() { |
this.doAsBinary(function() { |
||||
Address.super(this, 'validate', arguments); |
Address.super(this, 'validate', arguments); |
||||
if(this.data.length !== 21) throw new Error('invalid data length'); |
if(this.data.length !== 21) throw new Error('invalid data length'); |
||||
}); |
}); |
||||
}; |
}; |
||||
|
|
||||
return Address; |
module.exports = require('soop')(Address); |
||||
} |
|
||||
module.defineClass(ClassSpec); |
|
||||
|
File diff suppressed because it is too large
@ -1,116 +1,111 @@ |
|||||
require('classtool'); |
var MAX_BLOOM_FILTER_SIZE = 36000; // bytes
|
||||
|
var MAX_HASH_FUNCS = 50; |
||||
function ClassSpec(b) { |
var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455; |
||||
var MAX_BLOOM_FILTER_SIZE = 36000; // bytes
|
var LN2 = 0.6931471805599453094172321214581765680755001343602552; |
||||
var MAX_HASH_FUNCS = 50; |
var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]; |
||||
var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455; |
|
||||
var LN2 = 0.6931471805599453094172321214581765680755001343602552; |
function Bloom() { |
||||
var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]; |
this.data = ''; |
||||
|
this.hashFuncs = 0; |
||||
function Bloom() { |
}; |
||||
this.data = ''; |
|
||||
this.hashFuncs = 0; |
function ROTL32(x, r) { |
||||
}; |
return (x << r) | (x >> (32 - r)); |
||||
|
}; |
||||
function ROTL32(x, r) { |
|
||||
return (x << r) | (x >> (32 - r)); |
function getBlockU32(blockIdx, data) { |
||||
}; |
var idx = blockIdx * 4; |
||||
|
var v = (data[idx + 0] << (0 * 8)) | |
||||
function getBlockU32(blockIdx, data) { |
(data[idx + 1] << (1 * 8)) | |
||||
var idx = blockIdx * 4; |
(data[idx + 2] << (2 * 8)) | |
||||
var v = (data[idx + 0] << (0 * 8)) | |
(data[idx + 3] << (3 * 8)); |
||||
(data[idx + 1] << (1 * 8)) | |
return v; |
||||
(data[idx + 2] << (2 * 8)) | |
}; |
||||
(data[idx + 3] << (3 * 8)); |
|
||||
return v; |
Bloom.prototype.hash = function(hashNum, data) { |
||||
}; |
var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1)); |
||||
|
var c1 = 0xcc9e2d51; |
||||
Bloom.prototype.hash = function(hashNum, data) { |
var c2 = 0x1b873593; |
||||
var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1)); |
var nBlocks = data.length / 4; |
||||
var c1 = 0xcc9e2d51; |
|
||||
var c2 = 0x1b873593; |
// data body
|
||||
var nBlocks = data.length / 4; |
for (var i = -nBlocks; i; i++) { |
||||
|
var k1 = getBlockU32(i); |
||||
// data body
|
|
||||
for (var i = -nBlocks; i; i++) { |
k1 *= c1; |
||||
var k1 = getBlockU32(i); |
k1 = ROTLF32(k1, 15); |
||||
|
k1 *= c2; |
||||
k1 *= c1; |
|
||||
k1 = ROTLF32(k1, 15); |
h1 ^= k1; |
||||
k1 *= c2; |
h1 = ROTFL(h1, 13); |
||||
|
h1 = h1 * 5 + 0xe6546b64; |
||||
h1 ^= k1; |
|
||||
h1 = ROTFL(h1, 13); |
|
||||
h1 = h1 * 5 + 0xe6546b64; |
|
||||
} |
|
||||
|
|
||||
// tail (trailing 1-3 bytes)
|
|
||||
var tail = data.slice(nBlocks * 4); |
|
||||
|
|
||||
var k1 = 0; |
|
||||
|
|
||||
switch (data.length & 3) { |
|
||||
case 3: k1 ^= tail[2] << 16; |
|
||||
case 2: k1 ^= tail[1] << 8; |
|
||||
case 1: k1 ^= tail[0]; |
|
||||
k1 *= c1; |
|
||||
k1 = ROTL32(k1, 15); |
|
||||
k1 *= c2; |
|
||||
h1 ^= k1; |
|
||||
} |
|
||||
|
|
||||
// finalize
|
|
||||
h1 ^= data.length; |
|
||||
h1 ^= h1 >> 16; |
|
||||
h1 *= 0x85ebca6b; |
|
||||
h1 ^= h1 >> 13; |
|
||||
h1 *= 0xc2b2ae35; |
|
||||
h1 ^= h1 >> 16; |
|
||||
|
|
||||
return h1 % (this.data.length * 8); |
|
||||
}; |
|
||||
|
|
||||
Bloom.prototype.insert = function(data) { |
|
||||
for (var i = 0; i < this.hashFuncs; i++) { |
|
||||
var index = this.hash(i, data); |
|
||||
this.data[index >> 3] |= bit_mask[7 & index]; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
Bloom.prototype.contains = function(data) { |
|
||||
for (var i = 0; i < this.hashFuncs; i++) { |
|
||||
var index = this.hash(i, data); |
|
||||
if (!(this.data[index >> 3] & bit_mask[7 & index])) |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
return true; |
|
||||
}; |
|
||||
|
|
||||
Bloom.prototype.sizeOk = function() { |
|
||||
return this.data.length <= MAX_BLOOM_FILTER_SIZE && |
|
||||
this.hashFuncs <= MAX_HASH_FUNCS; |
|
||||
}; |
|
||||
|
|
||||
function toInt(v) { |
|
||||
return ~~v; |
|
||||
} |
} |
||||
|
|
||||
function min(a, b) { |
// tail (trailing 1-3 bytes)
|
||||
if (a < b) |
var tail = data.slice(nBlocks * 4); |
||||
return a; |
|
||||
return b; |
var k1 = 0; |
||||
|
|
||||
|
switch (data.length & 3) { |
||||
|
case 3: k1 ^= tail[2] << 16; |
||||
|
case 2: k1 ^= tail[1] << 8; |
||||
|
case 1: k1 ^= tail[0]; |
||||
|
k1 *= c1; |
||||
|
k1 = ROTL32(k1, 15); |
||||
|
k1 *= c2; |
||||
|
h1 ^= k1; |
||||
} |
} |
||||
|
|
||||
Bloom.prototype.init = function(elements, FPRate) { |
// finalize
|
||||
var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)), |
h1 ^= data.length; |
||||
MAX_BLOOM_FILTER_SIZE * 8) / 8; |
h1 ^= h1 >> 16; |
||||
this.data[filterSize] = 0; |
h1 *= 0x85ebca6b; |
||||
this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2), |
h1 ^= h1 >> 13; |
||||
MAX_HASH_FUNCS); |
h1 *= 0xc2b2ae35; |
||||
}; |
h1 ^= h1 >> 16; |
||||
|
|
||||
return Bloom; |
return h1 % (this.data.length * 8); |
||||
}; |
}; |
||||
module.defineClass(ClassSpec); |
|
||||
|
|
||||
|
Bloom.prototype.insert = function(data) { |
||||
|
for (var i = 0; i < this.hashFuncs; i++) { |
||||
|
var index = this.hash(i, data); |
||||
|
this.data[index >> 3] |= bit_mask[7 & index]; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Bloom.prototype.contains = function(data) { |
||||
|
for (var i = 0; i < this.hashFuncs; i++) { |
||||
|
var index = this.hash(i, data); |
||||
|
if (!(this.data[index >> 3] & bit_mask[7 & index])) |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
}; |
||||
|
|
||||
|
Bloom.prototype.sizeOk = function() { |
||||
|
return this.data.length <= MAX_BLOOM_FILTER_SIZE && |
||||
|
this.hashFuncs <= MAX_HASH_FUNCS; |
||||
|
}; |
||||
|
|
||||
|
function toInt(v) { |
||||
|
return ~~v; |
||||
|
} |
||||
|
|
||||
|
function min(a, b) { |
||||
|
if (a < b) |
||||
|
return a; |
||||
|
return b; |
||||
|
} |
||||
|
|
||||
|
Bloom.prototype.init = function(elements, FPRate) { |
||||
|
var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)), |
||||
|
MAX_BLOOM_FILTER_SIZE * 8) / 8; |
||||
|
this.data[filterSize] = 0; |
||||
|
this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2), |
||||
|
MAX_HASH_FUNCS); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
module.exports = require('soop')(Bloom); |
||||
|
File diff suppressed because it is too large
@ -1,161 +1,158 @@ |
|||||
require('classtool'); |
var imports = require('soop').imports(); |
||||
|
|
||||
function spec(b) { |
function Opcode(num) { |
||||
function Opcode(num) { |
this.code = num; |
||||
this.code = num; |
}; |
||||
}; |
|
||||
|
Opcode.prototype.toString = function () { |
||||
Opcode.prototype.toString = function () { |
return Opcode.reverseMap[this.code]; |
||||
return Opcode.reverseMap[this.code]; |
}; |
||||
}; |
|
||||
|
|
||||
Opcode.map = { |
|
||||
// push value
|
|
||||
OP_FALSE : 0, |
|
||||
OP_0 : 0, |
|
||||
OP_PUSHDATA1 : 76, |
|
||||
OP_PUSHDATA2 : 77, |
|
||||
OP_PUSHDATA4 : 78, |
|
||||
OP_1NEGATE : 79, |
|
||||
OP_RESERVED : 80, |
|
||||
OP_TRUE : 81, |
|
||||
OP_1 : 81, |
|
||||
OP_2 : 82, |
|
||||
OP_3 : 83, |
|
||||
OP_4 : 84, |
|
||||
OP_5 : 85, |
|
||||
OP_6 : 86, |
|
||||
OP_7 : 87, |
|
||||
OP_8 : 88, |
|
||||
OP_9 : 89, |
|
||||
OP_10 : 90, |
|
||||
OP_11 : 91, |
|
||||
OP_12 : 92, |
|
||||
OP_13 : 93, |
|
||||
OP_14 : 94, |
|
||||
OP_15 : 95, |
|
||||
OP_16 : 96, |
|
||||
|
|
||||
// control
|
|
||||
OP_NOP : 97, |
|
||||
OP_VER : 98, |
|
||||
OP_IF : 99, |
|
||||
OP_NOTIF : 100, |
|
||||
OP_VERIF : 101, |
|
||||
OP_VERNOTIF : 102, |
|
||||
OP_ELSE : 103, |
|
||||
OP_ENDIF : 104, |
|
||||
OP_VERIFY : 105, |
|
||||
OP_RETURN : 106, |
|
||||
|
|
||||
// stack ops
|
|
||||
OP_TOALTSTACK : 107, |
|
||||
OP_FROMALTSTACK : 108, |
|
||||
OP_2DROP : 109, |
|
||||
OP_2DUP : 110, |
|
||||
OP_3DUP : 111, |
|
||||
OP_2OVER : 112, |
|
||||
OP_2ROT : 113, |
|
||||
OP_2SWAP : 114, |
|
||||
OP_IFDUP : 115, |
|
||||
OP_DEPTH : 116, |
|
||||
OP_DROP : 117, |
|
||||
OP_DUP : 118, |
|
||||
OP_NIP : 119, |
|
||||
OP_OVER : 120, |
|
||||
OP_PICK : 121, |
|
||||
OP_ROLL : 122, |
|
||||
OP_ROT : 123, |
|
||||
OP_SWAP : 124, |
|
||||
OP_TUCK : 125, |
|
||||
|
|
||||
// splice ops
|
|
||||
OP_CAT : 126, |
|
||||
OP_SUBSTR : 127, |
|
||||
OP_LEFT : 128, |
|
||||
OP_RIGHT : 129, |
|
||||
OP_SIZE : 130, |
|
||||
|
|
||||
// bit logic
|
|
||||
OP_INVERT : 131, |
|
||||
OP_AND : 132, |
|
||||
OP_OR : 133, |
|
||||
OP_XOR : 134, |
|
||||
OP_EQUAL : 135, |
|
||||
OP_EQUALVERIFY : 136, |
|
||||
OP_RESERVED1 : 137, |
|
||||
OP_RESERVED2 : 138, |
|
||||
|
|
||||
// numeric
|
|
||||
OP_1ADD : 139, |
|
||||
OP_1SUB : 140, |
|
||||
OP_2MUL : 141, |
|
||||
OP_2DIV : 142, |
|
||||
OP_NEGATE : 143, |
|
||||
OP_ABS : 144, |
|
||||
OP_NOT : 145, |
|
||||
OP_0NOTEQUAL : 146, |
|
||||
|
|
||||
OP_ADD : 147, |
|
||||
OP_SUB : 148, |
|
||||
OP_MUL : 149, |
|
||||
OP_DIV : 150, |
|
||||
OP_MOD : 151, |
|
||||
OP_LSHIFT : 152, |
|
||||
OP_RSHIFT : 153, |
|
||||
|
|
||||
OP_BOOLAND : 154, |
|
||||
OP_BOOLOR : 155, |
|
||||
OP_NUMEQUAL : 156, |
|
||||
OP_NUMEQUALVERIFY : 157, |
|
||||
OP_NUMNOTEQUAL : 158, |
|
||||
OP_LESSTHAN : 159, |
|
||||
OP_GREATERTHAN : 160, |
|
||||
OP_LESSTHANOREQUAL : 161, |
|
||||
OP_GREATERTHANOREQUAL : 162, |
|
||||
OP_MIN : 163, |
|
||||
OP_MAX : 164, |
|
||||
|
|
||||
OP_WITHIN : 165, |
|
||||
|
|
||||
// crypto
|
|
||||
OP_RIPEMD160 : 166, |
|
||||
OP_SHA1 : 167, |
|
||||
OP_SHA256 : 168, |
|
||||
OP_HASH160 : 169, |
|
||||
OP_HASH256 : 170, |
|
||||
OP_CODESEPARATOR : 171, |
|
||||
OP_CHECKSIG : 172, |
|
||||
OP_CHECKSIGVERIFY : 173, |
|
||||
OP_CHECKMULTISIG : 174, |
|
||||
OP_CHECKMULTISIGVERIFY : 175, |
|
||||
|
|
||||
// expansion
|
|
||||
OP_NOP1 : 176, |
|
||||
OP_NOP2 : 177, |
|
||||
OP_NOP3 : 178, |
|
||||
OP_NOP4 : 179, |
|
||||
OP_NOP5 : 180, |
|
||||
OP_NOP6 : 181, |
|
||||
OP_NOP7 : 182, |
|
||||
OP_NOP8 : 183, |
|
||||
OP_NOP9 : 184, |
|
||||
OP_NOP10 : 185, |
|
||||
|
|
||||
// template matching params
|
|
||||
OP_PUBKEYHASH : 253, |
|
||||
OP_PUBKEY : 254, |
|
||||
OP_INVALIDOPCODE : 255 |
|
||||
}; |
|
||||
|
|
||||
Opcode.reverseMap = []; |
|
||||
|
|
||||
for (var k in Opcode.map) { |
|
||||
if(Opcode.map.hasOwnProperty(k)) { |
|
||||
Opcode.reverseMap[Opcode.map[k]] = k.substr(3); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return Opcode; |
Opcode.map = { |
||||
|
// push value
|
||||
|
OP_FALSE : 0, |
||||
|
OP_0 : 0, |
||||
|
OP_PUSHDATA1 : 76, |
||||
|
OP_PUSHDATA2 : 77, |
||||
|
OP_PUSHDATA4 : 78, |
||||
|
OP_1NEGATE : 79, |
||||
|
OP_RESERVED : 80, |
||||
|
OP_TRUE : 81, |
||||
|
OP_1 : 81, |
||||
|
OP_2 : 82, |
||||
|
OP_3 : 83, |
||||
|
OP_4 : 84, |
||||
|
OP_5 : 85, |
||||
|
OP_6 : 86, |
||||
|
OP_7 : 87, |
||||
|
OP_8 : 88, |
||||
|
OP_9 : 89, |
||||
|
OP_10 : 90, |
||||
|
OP_11 : 91, |
||||
|
OP_12 : 92, |
||||
|
OP_13 : 93, |
||||
|
OP_14 : 94, |
||||
|
OP_15 : 95, |
||||
|
OP_16 : 96, |
||||
|
|
||||
|
// control
|
||||
|
OP_NOP : 97, |
||||
|
OP_VER : 98, |
||||
|
OP_IF : 99, |
||||
|
OP_NOTIF : 100, |
||||
|
OP_VERIF : 101, |
||||
|
OP_VERNOTIF : 102, |
||||
|
OP_ELSE : 103, |
||||
|
OP_ENDIF : 104, |
||||
|
OP_VERIFY : 105, |
||||
|
OP_RETURN : 106, |
||||
|
|
||||
|
// stack ops
|
||||
|
OP_TOALTSTACK : 107, |
||||
|
OP_FROMALTSTACK : 108, |
||||
|
OP_2DROP : 109, |
||||
|
OP_2DUP : 110, |
||||
|
OP_3DUP : 111, |
||||
|
OP_2OVER : 112, |
||||
|
OP_2ROT : 113, |
||||
|
OP_2SWAP : 114, |
||||
|
OP_IFDUP : 115, |
||||
|
OP_DEPTH : 116, |
||||
|
OP_DROP : 117, |
||||
|
OP_DUP : 118, |
||||
|
OP_NIP : 119, |
||||
|
OP_OVER : 120, |
||||
|
OP_PICK : 121, |
||||
|
OP_ROLL : 122, |
||||
|
OP_ROT : 123, |
||||
|
OP_SWAP : 124, |
||||
|
OP_TUCK : 125, |
||||
|
|
||||
|
// splice ops
|
||||
|
OP_CAT : 126, |
||||
|
OP_SUBSTR : 127, |
||||
|
OP_LEFT : 128, |
||||
|
OP_RIGHT : 129, |
||||
|
OP_SIZE : 130, |
||||
|
|
||||
|
// bit logic
|
||||
|
OP_INVERT : 131, |
||||
|
OP_AND : 132, |
||||
|
OP_OR : 133, |
||||
|
OP_XOR : 134, |
||||
|
OP_EQUAL : 135, |
||||
|
OP_EQUALVERIFY : 136, |
||||
|
OP_RESERVED1 : 137, |
||||
|
OP_RESERVED2 : 138, |
||||
|
|
||||
|
// numeric
|
||||
|
OP_1ADD : 139, |
||||
|
OP_1SUB : 140, |
||||
|
OP_2MUL : 141, |
||||
|
OP_2DIV : 142, |
||||
|
OP_NEGATE : 143, |
||||
|
OP_ABS : 144, |
||||
|
OP_NOT : 145, |
||||
|
OP_0NOTEQUAL : 146, |
||||
|
|
||||
|
OP_ADD : 147, |
||||
|
OP_SUB : 148, |
||||
|
OP_MUL : 149, |
||||
|
OP_DIV : 150, |
||||
|
OP_MOD : 151, |
||||
|
OP_LSHIFT : 152, |
||||
|
OP_RSHIFT : 153, |
||||
|
|
||||
|
OP_BOOLAND : 154, |
||||
|
OP_BOOLOR : 155, |
||||
|
OP_NUMEQUAL : 156, |
||||
|
OP_NUMEQUALVERIFY : 157, |
||||
|
OP_NUMNOTEQUAL : 158, |
||||
|
OP_LESSTHAN : 159, |
||||
|
OP_GREATERTHAN : 160, |
||||
|
OP_LESSTHANOREQUAL : 161, |
||||
|
OP_GREATERTHANOREQUAL : 162, |
||||
|
OP_MIN : 163, |
||||
|
OP_MAX : 164, |
||||
|
|
||||
|
OP_WITHIN : 165, |
||||
|
|
||||
|
// crypto
|
||||
|
OP_RIPEMD160 : 166, |
||||
|
OP_SHA1 : 167, |
||||
|
OP_SHA256 : 168, |
||||
|
OP_HASH160 : 169, |
||||
|
OP_HASH256 : 170, |
||||
|
OP_CODESEPARATOR : 171, |
||||
|
OP_CHECKSIG : 172, |
||||
|
OP_CHECKSIGVERIFY : 173, |
||||
|
OP_CHECKMULTISIG : 174, |
||||
|
OP_CHECKMULTISIGVERIFY : 175, |
||||
|
|
||||
|
// expansion
|
||||
|
OP_NOP1 : 176, |
||||
|
OP_NOP2 : 177, |
||||
|
OP_NOP3 : 178, |
||||
|
OP_NOP4 : 179, |
||||
|
OP_NOP5 : 180, |
||||
|
OP_NOP6 : 181, |
||||
|
OP_NOP7 : 182, |
||||
|
OP_NOP8 : 183, |
||||
|
OP_NOP9 : 184, |
||||
|
OP_NOP10 : 185, |
||||
|
|
||||
|
// template matching params
|
||||
|
OP_PUBKEYHASH : 253, |
||||
|
OP_PUBKEY : 254, |
||||
|
OP_INVALIDOPCODE : 255 |
||||
}; |
}; |
||||
module.defineClass(spec); |
|
||||
|
Opcode.reverseMap = []; |
||||
|
|
||||
|
for (var k in Opcode.map) { |
||||
|
if(Opcode.map.hasOwnProperty(k)) { |
||||
|
Opcode.reverseMap[Opcode.map[k]] = k.substr(3); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = require('soop')(Opcode); |
||||
|
@ -1,61 +1,58 @@ |
|||||
require('classtool'); |
var imports = require('soop').imports(); |
||||
|
|
||||
function spec(b) { |
var Net = imports.Net || require('net'); |
||||
var Net = b.Net || require('net'); |
var Binary = imports.Binary || require('binary'); |
||||
var Binary = b.Binary || require('binary'); |
var buffertools = imports.buffertools || require('buffertools'); |
||||
var buffertools = b.buffertools || require('buffertools'); |
|
||||
|
function Peer(host, port, services) { |
||||
function Peer(host, port, services) { |
if ("string" === typeof host) { |
||||
if ("string" === typeof host) { |
if (host.indexOf(':') && !port) { |
||||
if (host.indexOf(':') && !port) { |
var parts = host.split(':'); |
||||
var parts = host.split(':'); |
host = parts[0]; |
||||
host = parts[0]; |
port = parts[1]; |
||||
port = parts[1]; |
|
||||
} |
|
||||
this.host = host; |
|
||||
this.port = +port || 8333; |
|
||||
} else if (host instanceof Peer) { |
|
||||
this.host = host.host; |
|
||||
this.port = host.port; |
|
||||
} else if (Buffer.isBuffer(host)) { |
|
||||
if (buffertools.compare(Peer.IPV6_IPV4_PADDING, host.slice(0, 12)) != 0) { |
|
||||
throw new Error('IPV6 not supported yet! Cannot instantiate host.'); |
|
||||
} |
|
||||
this.host = Array.prototype.slice.apply(host.slice(12)).join('.'); |
|
||||
this.port = +port || 8333; |
|
||||
} else { |
|
||||
throw new Error('Could not instantiate peer, invalid parameter type: ' + |
|
||||
typeof host); |
|
||||
} |
} |
||||
|
this.host = host; |
||||
|
this.port = +port || 8333; |
||||
|
} else if (host instanceof Peer) { |
||||
|
this.host = host.host; |
||||
|
this.port = host.port; |
||||
|
} else if (Buffer.isBuffer(host)) { |
||||
|
if (buffertools.compare(Peer.IPV6_IPV4_PADDING, host.slice(0, 12)) != 0) { |
||||
|
throw new Error('IPV6 not supported yet! Cannot instantiate host.'); |
||||
|
} |
||||
|
this.host = Array.prototype.slice.apply(host.slice(12)).join('.'); |
||||
|
this.port = +port || 8333; |
||||
|
} else { |
||||
|
throw new Error('Could not instantiate peer, invalid parameter type: ' + |
||||
|
typeof host); |
||||
|
} |
||||
|
|
||||
|
this.services = (services) ? services : null; |
||||
|
this.lastSeen = 0; |
||||
|
}; |
||||
|
|
||||
this.services = (services) ? services : null; |
Peer.IPV6_IPV4_PADDING = new Buffer([0,0,0,0,0,0,0,0,0,0,255,255]); |
||||
this.lastSeen = 0; |
|
||||
}; |
|
||||
|
|
||||
Peer.IPV6_IPV4_PADDING = new Buffer([0,0,0,0,0,0,0,0,0,0,255,255]); |
|
||||
|
|
||||
Peer.prototype.createConnection = function () { |
|
||||
var c = Net.createConnection(this.port, this.host); |
|
||||
return c; |
|
||||
}; |
|
||||
|
|
||||
Peer.prototype.getHostAsBuffer = function () { |
Peer.prototype.createConnection = function () { |
||||
return new Buffer(this.host.split('.')); |
var c = Net.createConnection(this.port, this.host); |
||||
}; |
return c; |
||||
|
}; |
||||
|
|
||||
Peer.prototype.toString = function () { |
Peer.prototype.getHostAsBuffer = function () { |
||||
return this.host + ":" + this.port; |
return new Buffer(this.host.split('.')); |
||||
}; |
}; |
||||
|
|
||||
Peer.prototype.toBuffer = function () { |
Peer.prototype.toString = function () { |
||||
var put = Binary.put(); |
return this.host + ":" + this.port; |
||||
put.word32le(this.lastSeen); |
}; |
||||
put.word64le(this.services); |
|
||||
put.put(this.getHostAsBuffer()); |
|
||||
put.word16be(this.port); |
|
||||
return put.buffer(); |
|
||||
}; |
|
||||
|
|
||||
return Peer; |
Peer.prototype.toBuffer = function () { |
||||
|
var put = Binary.put(); |
||||
|
put.word32le(this.lastSeen); |
||||
|
put.word64le(this.services); |
||||
|
put.put(this.getHostAsBuffer()); |
||||
|
put.word16be(this.port); |
||||
|
return put.buffer(); |
||||
}; |
}; |
||||
module.defineClass(spec); |
|
||||
|
module.exports = require('soop')(Peer); |
||||
|
@ -1,215 +1,212 @@ |
|||||
require('classtool'); |
|
||||
|
|
||||
function spec(b) { |
|
||||
var config = b.config || require('./config'); |
|
||||
var log = b.log || require('./util/log'); |
|
||||
var network = b.network || require('./networks')[config.network]; |
|
||||
var Connection = b.Connection || require('./Connection').createClass( |
|
||||
{config: config, network: network}); |
|
||||
var Peer = b.Peer || require('./Peer').class(); |
|
||||
var noop = function() {}; |
|
||||
|
|
||||
GetAdjustedTime = b.GetAdjustedTime || function () { |
|
||||
// TODO: Implement actual adjustment
|
|
||||
return Math.floor(new Date().getTime() / 1000); |
|
||||
}; |
|
||||
|
|
||||
function PeerManager() { |
var imports = require('soop').imports(); |
||||
this.active = false; |
var config = imports.config || require('./config'); |
||||
this.timer = null; |
var log = imports.log || require('./util/log'); |
||||
|
var network = imports.network || require('./networks')[config.network]; |
||||
|
var Connection = imports.Connection || |
||||
|
require('soop').load('./Connection', {config: config, network: network}); |
||||
|
|
||||
this.peers = []; |
var Peer = imports.Peer || require('./Peer'); |
||||
this.connections = []; |
|
||||
this.isConnected = false; |
|
||||
this.peerDiscovery = false; |
|
||||
|
|
||||
// Move these to the Node's settings object
|
GetAdjustedTime = imports.GetAdjustedTime || function () { |
||||
this.interval = 5000; |
// TODO: Implement actual adjustment
|
||||
this.minConnections = 8; |
return Math.floor(new Date().getTime() / 1000); |
||||
this.minKnownPeers = 10; |
}; |
||||
}; |
|
||||
PeerManager.superclass = b.superclass || require('events').EventEmitter; |
|
||||
|
|
||||
PeerManager.Connection = Connection; |
function PeerManager() { |
||||
|
this.active = false; |
||||
|
this.timer = null; |
||||
|
|
||||
PeerManager.prototype.start = function() { |
this.peers = []; |
||||
this.active = true; |
this.connections = []; |
||||
if(!this.timer) { |
this.isConnected = false; |
||||
this.timer = setInterval(this.checkStatus.bind(this), this.interval); |
this.peerDiscovery = false; |
||||
} |
|
||||
}; |
|
||||
|
|
||||
PeerManager.prototype.stop = function() { |
// Move these to the Node's settings object
|
||||
this.active = false; |
this.interval = 5000; |
||||
if(this.timer) { |
this.minConnections = 8; |
||||
clearInterval(this.timer); |
this.minKnownPeers = 10; |
||||
this.timer = null; |
}; |
||||
} |
|
||||
for(var i=0; i<this.connections.length; i++) { |
|
||||
this.connections[i].socket.end(); |
|
||||
}; |
|
||||
}; |
|
||||
|
|
||||
PeerManager.prototype.addPeer = function(peer, port) { |
PeerManager.parent = imports.parent || require('events').EventEmitter; |
||||
if(peer instanceof Peer) { |
PeerManager.Connection = Connection; |
||||
this.peers.push(peer); |
|
||||
} else if ("string" == typeof peer) { |
|
||||
this.addPeer(new Peer(peer, port)); |
|
||||
} else { |
|
||||
log.err('Node.addPeer(): Invalid value provided for peer', |
|
||||
{val: peer}); |
|
||||
throw 'Node.addPeer(): Invalid value provided for peer.'; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
PeerManager.prototype.checkStatus = function checkStatus() { |
PeerManager.prototype.start = function() { |
||||
// Make sure we are connected to all forcePeers
|
this.active = true; |
||||
if(this.peers.length) { |
if(!this.timer) { |
||||
var peerIndex = {}; |
this.timer = setInterval(this.checkStatus.bind(this), this.interval); |
||||
this.peers.forEach(function(peer) { |
} |
||||
peerIndex[peer.toString()] = peer; |
}; |
||||
}); |
|
||||
|
|
||||
// Ignore the ones we're already connected to
|
|
||||
this.connections.forEach(function(conn) { |
|
||||
var peerName = conn.peer.toString(); |
|
||||
if("undefined" !== peerIndex[peerName]) { |
|
||||
delete peerIndex[peerName]; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
Object.keys(peerIndex).forEach(function(i) { |
|
||||
this.connectTo(peerIndex[i]); |
|
||||
}.bind(this)); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
PeerManager.prototype.connectTo = function(peer) { |
PeerManager.prototype.stop = function() { |
||||
log.info('connecting to '+peer); |
this.active = false; |
||||
try { |
if(this.timer) { |
||||
return this.addConnection(peer.createConnection(), peer); |
clearInterval(this.timer); |
||||
} catch (e) { |
this.timer = null; |
||||
log.err('creating connection',e); |
} |
||||
return null; |
for(var i=0; i<this.connections.length; i++) { |
||||
} |
this.connections[i].socket.end(); |
||||
}; |
}; |
||||
|
}; |
||||
|
|
||||
PeerManager.prototype.addConnection = function(socketConn, peer) { |
PeerManager.prototype.addPeer = function(peer, port) { |
||||
var conn = new Connection(socketConn, peer); |
if(peer instanceof Peer) { |
||||
this.connections.push(conn); |
this.peers.push(peer); |
||||
this.emit('connection', conn); |
} else if ("string" == typeof peer) { |
||||
|
this.addPeer(new Peer(peer, port)); |
||||
|
} else { |
||||
|
log.err('Node.addPeer(): Invalid value provided for peer', |
||||
|
{val: peer}); |
||||
|
throw 'Node.addPeer(): Invalid value provided for peer.'; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
conn.addListener('version', this.handleVersion.bind(this)); |
PeerManager.prototype.checkStatus = function checkStatus() { |
||||
conn.addListener('verack', this.handleReady.bind(this)); |
// Make sure we are connected to all forcePeers
|
||||
conn.addListener('addr', this.handleAddr.bind(this)); |
if(this.peers.length) { |
||||
conn.addListener('getaddr', this.handleGetAddr.bind(this)); |
var peerIndex = {}; |
||||
conn.addListener('error', this.handleError.bind(this)); |
this.peers.forEach(function(peer) { |
||||
conn.addListener('disconnect', this.handleDisconnect.bind(this)); |
peerIndex[peer.toString()] = peer; |
||||
|
}); |
||||
|
|
||||
return conn; |
// Ignore the ones we're already connected to
|
||||
}; |
this.connections.forEach(function(conn) { |
||||
|
var peerName = conn.peer.toString(); |
||||
|
if("undefined" !== peerIndex[peerName]) { |
||||
|
delete peerIndex[peerName]; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
PeerManager.prototype.handleVersion = function(e) { |
Object.keys(peerIndex).forEach(function(i) { |
||||
if (!e.conn.inbound) { |
this.connectTo(peerIndex[i]); |
||||
// TODO: Advertise our address (if listening)
|
}.bind(this)); |
||||
} |
} |
||||
// Get recent addresses
|
}; |
||||
if(this.peerDiscovery && |
|
||||
(e.message.version >= 31402 || this.peers.length < 1000)) { |
|
||||
e.conn.sendGetAddr(); |
|
||||
e.conn.getaddr = true; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
PeerManager.prototype.handleReady = function (e) { |
PeerManager.prototype.connectTo = function(peer) { |
||||
log.info('connected to '+e.conn.peer.host+':'+e.conn.peer.port); |
log.info('connecting to '+peer); |
||||
this.emit('connect', { |
try { |
||||
pm: this, |
return this.addConnection(peer.createConnection(), peer); |
||||
conn: e.conn, |
} catch (e) { |
||||
socket: e.socket, |
log.err('creating connection',e); |
||||
peer: e.peer |
return null; |
||||
}); |
} |
||||
|
}; |
||||
|
|
||||
if(this.isConnected == false) { |
PeerManager.prototype.addConnection = function(socketConn, peer) { |
||||
this.emit('netConnected'); |
var conn = new Connection(socketConn, peer); |
||||
this.isConnected = true; |
this.connections.push(conn); |
||||
} |
this.emit('connection', conn); |
||||
}; |
|
||||
|
conn.addListener('version', this.handleVersion.bind(this)); |
||||
|
conn.addListener('verack', this.handleReady.bind(this)); |
||||
|
conn.addListener('addr', this.handleAddr.bind(this)); |
||||
|
conn.addListener('getaddr', this.handleGetAddr.bind(this)); |
||||
|
conn.addListener('error', this.handleError.bind(this)); |
||||
|
conn.addListener('disconnect', this.handleDisconnect.bind(this)); |
||||
|
|
||||
|
return conn; |
||||
|
}; |
||||
|
|
||||
|
PeerManager.prototype.handleVersion = function(e) { |
||||
|
if (!e.conn.inbound) { |
||||
|
// TODO: Advertise our address (if listening)
|
||||
|
} |
||||
|
// Get recent addresses
|
||||
|
if(this.peerDiscovery && |
||||
|
(e.message.version >= 31402 || this.peers.length < 1000)) { |
||||
|
e.conn.sendGetAddr(); |
||||
|
e.conn.getaddr = true; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
PeerManager.prototype.handleReady = function (e) { |
||||
|
log.info('connected to '+e.conn.peer.host+':'+e.conn.peer.port); |
||||
|
this.emit('connect', { |
||||
|
pm: this, |
||||
|
conn: e.conn, |
||||
|
socket: e.socket, |
||||
|
peer: e.peer |
||||
|
}); |
||||
|
|
||||
|
if(this.isConnected == false) { |
||||
|
this.emit('netConnected'); |
||||
|
this.isConnected = true; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
PeerManager.prototype.handleAddr = function (e) { |
PeerManager.prototype.handleAddr = function (e) { |
||||
if(!this.peerDiscovery) return; |
if(!this.peerDiscovery) return; |
||||
|
|
||||
var now = GetAdjustedTime(); |
var now = GetAdjustedTime(); |
||||
e.message.addrs.forEach(function (addr) { |
e.message.addrs.forEach(function (addr) { |
||||
try { |
try { |
||||
// In case of an invalid time, assume "5 days ago"
|
// In case of an invalid time, assume "5 days ago"
|
||||
if (addr.time <= 100000000 || addr.time > (now + 10 * 60)) { |
if (addr.time <= 100000000 || addr.time > (now + 10 * 60)) { |
||||
addr.time = now - 5 * 24 * 60 * 60; |
addr.time = now - 5 * 24 * 60 * 60; |
||||
} |
|
||||
var peer = new Peer(addr.ip, addr.port, addr.services); |
|
||||
peer.lastSeen = addr.time; |
|
||||
|
|
||||
// TODO: Handle duplicate peers
|
|
||||
this.peers.push(peer); |
|
||||
|
|
||||
// TODO: Handle addr relay
|
|
||||
} catch(e) { |
|
||||
log.warn("Invalid addr received: "+e.message); |
|
||||
} |
} |
||||
}.bind(this)); |
var peer = new Peer(addr.ip, addr.port, addr.services); |
||||
if (e.message.addrs.length < 1000 ) { |
peer.lastSeen = addr.time; |
||||
e.conn.getaddr = false; |
|
||||
|
// TODO: Handle duplicate peers
|
||||
|
this.peers.push(peer); |
||||
|
|
||||
|
// TODO: Handle addr relay
|
||||
|
} catch(e) { |
||||
|
log.warn("Invalid addr received: "+e.message); |
||||
} |
} |
||||
}; |
}.bind(this)); |
||||
|
if (e.message.addrs.length < 1000 ) { |
||||
|
e.conn.getaddr = false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
PeerManager.prototype.handleGetAddr = function(e) { |
PeerManager.prototype.handleGetAddr = function(e) { |
||||
// TODO: Reply with addr message.
|
// TODO: Reply with addr message.
|
||||
}; |
}; |
||||
|
|
||||
PeerManager.prototype.handleError = function(e) { |
PeerManager.prototype.handleError = function(e) { |
||||
log.err('unkown error with peer '+e.peer+' (disconnecting): '+e.err); |
log.err('unkown error with peer '+e.peer+' (disconnecting): '+e.err); |
||||
this.handleDisconnect.apply(this, [].slice.call(arguments)); |
this.handleDisconnect.apply(this, [].slice.call(arguments)); |
||||
}; |
}; |
||||
|
|
||||
PeerManager.prototype.handleDisconnect = function(e) { |
PeerManager.prototype.handleDisconnect = function(e) { |
||||
log.info('disconnected from peer '+e.peer); |
log.info('disconnected from peer '+e.peer); |
||||
var i = this.connections.indexOf(e.conn); |
var i = this.connections.indexOf(e.conn); |
||||
if(i != -1) this.connections.splice(i, 1); |
if(i != -1) this.connections.splice(i, 1); |
||||
|
|
||||
if(!this.connections.length) { |
if(!this.connections.length) { |
||||
this.emit('netDisconnected'); |
this.emit('netDisconnected'); |
||||
this.isConnected = false; |
this.isConnected = false; |
||||
} |
} |
||||
}; |
}; |
||||
|
|
||||
PeerManager.prototype.getActiveConnection = function () { |
PeerManager.prototype.getActiveConnection = function () { |
||||
var activeConnections = this.connections.filter(function (conn) { |
var activeConnections = this.connections.filter(function (conn) { |
||||
return conn.active; |
return conn.active; |
||||
}); |
}); |
||||
|
|
||||
if (activeConnections.length) { |
if (activeConnections.length) { |
||||
var randomIndex = Math.floor(Math.random()*activeConnections.length); |
var randomIndex = Math.floor(Math.random()*activeConnections.length); |
||||
var candidate = activeConnections[randomIndex]; |
var candidate = activeConnections[randomIndex]; |
||||
if (candidate.socket.writable) { |
if (candidate.socket.writable) { |
||||
return candidate; |
return candidate; |
||||
} else { |
|
||||
// Socket is not writable, remove it from active connections
|
|
||||
activeConnections.splice(randomIndex, 1); |
|
||||
|
|
||||
// Then try again
|
|
||||
// TODO: This causes an infinite recursion when all connections are dead,
|
|
||||
// although it shouldn't.
|
|
||||
return this.getActiveConnection(); |
|
||||
} |
|
||||
} else { |
} else { |
||||
return null; |
// Socket is not writable, remove it from active connections
|
||||
} |
activeConnections.splice(randomIndex, 1); |
||||
}; |
|
||||
|
|
||||
PeerManager.prototype.getActiveConnections = function () { |
// Then try again
|
||||
return this.connections.slice(0); |
// TODO: This causes an infinite recursion when all connections are dead,
|
||||
}; |
// although it shouldn't.
|
||||
|
return this.getActiveConnection(); |
||||
|
} |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
return PeerManager; |
PeerManager.prototype.getActiveConnections = function () { |
||||
|
return this.connections.slice(0); |
||||
}; |
}; |
||||
module.defineClass(spec); |
|
||||
|
module.exports = require('soop')(PeerManager); |
||||
|
@ -1,67 +1,64 @@ |
|||||
require('classtool'); |
var imports = require('soop').imports(); |
||||
|
|
||||
function ClassSpec(b) { |
var parent = imports.parent || require('./util/VersionedData'); |
||||
var superclass = b.superclass || require('./util/VersionedData').class(); |
|
||||
|
|
||||
//compressed is true if public key is compressed; false otherwise
|
//compressed is true if public key is compressed; false otherwise
|
||||
function PrivateKey(version, buf, compressed) { |
function PrivateKey(version, buf, compressed) { |
||||
PrivateKey.super(this, arguments); |
PrivateKey.super(this, arguments); |
||||
if (compressed !== undefined) |
if (compressed !== undefined) |
||||
this.compressed(compressed); |
this.compressed(compressed); |
||||
}; |
}; |
||||
|
|
||||
PrivateKey.superclass = superclass; |
PrivateKey.parent = parent; |
||||
superclass.applyEncodingsTo(PrivateKey); |
parent.applyEncodingsTo(PrivateKey); |
||||
|
|
||||
PrivateKey.prototype.validate = function() { |
PrivateKey.prototype.validate = function() { |
||||
this.doAsBinary(function() { |
this.doAsBinary(function() { |
||||
PrivateKey.super(this, 'validate', arguments); |
PrivateKey.super(this, 'validate', arguments); |
||||
if (this.data.length < 32 || (this.data.length > 1+32 && !this.compressed()) || (this.data.length==1+32+1 && this.data[1+32+1-1]!=1) || this.data.length>1+32+1) |
if (this.data.length < 32 || (this.data.length > 1+32 && !this.compressed()) || (this.data.length==1+32+1 && this.data[1+32+1-1]!=1) || this.data.length>1+32+1) |
||||
throw new Error('invalid data length'); |
throw new Error('invalid data length'); |
||||
}); |
}); |
||||
}; |
}; |
||||
|
|
||||
// get or set the payload data (as a Buffer object)
|
// get or set the payload data (as a Buffer object)
|
||||
// overloaded from VersionedData
|
// overloaded from VersionedData
|
||||
PrivateKey.prototype.payload = function(data) { |
PrivateKey.prototype.payload = function(data) { |
||||
if(data) { |
if(data) { |
||||
this.doAsBinary(function() {data.copy(this.data,1);}); |
this.doAsBinary(function() {data.copy(this.data,1);}); |
||||
return data; |
return data; |
||||
} |
} |
||||
var buf=this.as('binary'); |
var buf=this.as('binary'); |
||||
if (buf.length==1+32+1) |
if (buf.length==1+32+1) |
||||
return buf.slice(1,1+32); |
return buf.slice(1,1+32); |
||||
else if (buf.length==1+32) |
else if (buf.length==1+32) |
||||
return buf.slice(1); |
return buf.slice(1); |
||||
}; |
}; |
||||
|
|
||||
// get or set whether the corresponding public key is compressed
|
// get or set whether the corresponding public key is compressed
|
||||
PrivateKey.prototype.compressed = function(compressed) { |
PrivateKey.prototype.compressed = function(compressed) { |
||||
if (compressed !== undefined) { |
if (compressed !== undefined) { |
||||
this.doAsBinary(function(){ |
this.doAsBinary(function(){ |
||||
var len=1+32+1; |
|
||||
if (compressed) { |
|
||||
var data=new Buffer(len); |
|
||||
this.data.copy(data); |
|
||||
this.data=data; |
|
||||
this.data[len-1]=1; |
|
||||
} else { |
|
||||
this.data=this.data.slice(0,len-1); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
else { |
|
||||
var len=1+32+1; |
var len=1+32+1; |
||||
var data=this.as('binary'); |
if (compressed) { |
||||
if (data.length==len && data[len-1]==1) |
var data=new Buffer(len); |
||||
return true; |
this.data.copy(data); |
||||
else if (data.length==len-1) |
this.data=data; |
||||
return false; |
this.data[len-1]=1; |
||||
else |
} else { |
||||
throw new Error('invalid private key'); |
this.data=this.data.slice(0,len-1); |
||||
} |
} |
||||
}; |
}); |
||||
|
} |
||||
return PrivateKey; |
else { |
||||
|
var len=1+32+1; |
||||
|
var data=this.as('binary'); |
||||
|
if (data.length==len && data[len-1]==1) |
||||
|
return true; |
||||
|
else if (data.length==len-1) |
||||
|
return false; |
||||
|
else |
||||
|
throw new Error('invalid private key'); |
||||
|
} |
||||
}; |
}; |
||||
module.defineClass(ClassSpec); |
|
||||
|
module.exports = require('soop')(PrivateKey); |
||||
|
@ -1,210 +1,208 @@ |
|||||
// RpcClient.js
|
// RpcClient.js
|
||||
// MIT/X11-like license. See LICENSE.txt.
|
// MIT/X11-like license. See LICENSE.txt.
|
||||
// Copyright 2013 BitPay, Inc.
|
// Copyright 2013 BitPay, Inc.
|
||||
require('classtool'); |
//
|
||||
|
var imports = require('soop').imports(); |
||||
|
var http = imports.http || require('http'); |
||||
|
var https = imports.https || require('https'); |
||||
|
var log = imports.log || require('./util/log'); |
||||
|
|
||||
function ClassSpec(b) { |
function RpcClient(opts) { |
||||
var http = b.http || require('http'); |
opts = opts || {}; |
||||
var https = b.https || require('https'); |
this.host = opts.host || '127.0.0.1'; |
||||
var log = b.log || require('./util/log'); |
this.port = opts.port || 8332; |
||||
|
this.user = opts.user || 'user'; |
||||
|
this.pass = opts.pass || 'pass'; |
||||
|
this.protocol = (opts.protocol == 'http') ? http : https; |
||||
|
this.batchedCalls = null; |
||||
|
this.disableAgent = opts.disableAgent || false; |
||||
|
} |
||||
|
|
||||
|
RpcClient.prototype.batch = function(batchCallback, resultCallback) { |
||||
|
this.batchedCalls = []; |
||||
|
batchCallback(); |
||||
|
rpc.call(this, this.batchedCalls, resultCallback); |
||||
|
this.batchedCalls = null; |
||||
|
} |
||||
|
|
||||
function RpcClient(opts) { |
var callspec = { |
||||
opts = opts || {}; |
addMultiSigAddress: '', |
||||
this.host = opts.host || '127.0.0.1'; |
addNode: '', |
||||
this.port = opts.port || 8332; |
backupWallet: '', |
||||
this.user = opts.user || 'user'; |
createMultiSig: '', |
||||
this.pass = opts.pass || 'pass'; |
createRawTransaction: '', |
||||
this.protocol = (opts.protocol == 'http') ? http : https; |
decodeRawTransaction: '', |
||||
this.batchedCalls = null; |
dumpPrivKey: '', |
||||
this.disableAgent = opts.disableAgent || false; |
encryptWallet: '', |
||||
} |
getAccount: '', |
||||
|
getAccountAddress: 'str', |
||||
RpcClient.prototype.batch = function(batchCallback, resultCallback) { |
getAddedNodeInfo: '', |
||||
this.batchedCalls = []; |
getAddressesByAccount: '', |
||||
batchCallback(); |
getBalance: 'str int', |
||||
rpc.call(this, this.batchedCalls, resultCallback); |
getBestBlockHash: '', |
||||
this.batchedCalls = null; |
getBlock: '', |
||||
} |
getBlockCount: '', |
||||
|
getBlockHash: 'int', |
||||
var callspec = { |
getBlockNumber: '', |
||||
addMultiSigAddress: '', |
getBlockTemplate: '', |
||||
addNode: '', |
getConnectionCount: '', |
||||
backupWallet: '', |
getDifficulty: '', |
||||
createMultiSig: '', |
getGenerate: '', |
||||
createRawTransaction: '', |
getHashesPerSec: '', |
||||
decodeRawTransaction: '', |
getInfo: '', |
||||
dumpPrivKey: '', |
getMemoryPool: '', |
||||
encryptWallet: '', |
getMiningInfo: '', |
||||
getAccount: '', |
getNewAddress: '', |
||||
getAccountAddress: 'str', |
getPeerInfo: '', |
||||
getAddedNodeInfo: '', |
getRawMemPool: '', |
||||
getAddressesByAccount: '', |
getRawTransaction: 'str int', |
||||
getBalance: 'str int', |
getReceivedByAccount: 'str int', |
||||
getBestBlockHash: '', |
getReceivedByAddress: 'str int', |
||||
getBlock: '', |
getTransaction: '', |
||||
getBlockCount: '', |
getTxOut: 'str int bool', |
||||
getBlockHash: 'int', |
getTxOutSetInfo: '', |
||||
getBlockNumber: '', |
getWork: '', |
||||
getBlockTemplate: '', |
help: '', |
||||
getConnectionCount: '', |
importAddress: 'str str bool', |
||||
getDifficulty: '', |
importPrivKey: 'str str bool', |
||||
getGenerate: '', |
keyPoolRefill: '', |
||||
getHashesPerSec: '', |
listAccounts: 'int', |
||||
getInfo: '', |
listAddressGroupings: '', |
||||
getMemoryPool: '', |
listReceivedByAccount: 'int bool', |
||||
getMiningInfo: '', |
listReceivedByAddress: 'int bool', |
||||
getNewAddress: '', |
listSinceBlock: 'str int', |
||||
getPeerInfo: '', |
listTransactions: 'str int int', |
||||
getRawMemPool: '', |
listUnspent: 'int int', |
||||
getRawTransaction: 'str int', |
listLockUnspent: 'bool', |
||||
getReceivedByAccount: 'str int', |
lockUnspent: '', |
||||
getReceivedByAddress: 'str int', |
move: 'str str float int str', |
||||
getTransaction: '', |
sendFrom: 'str str float int str str', |
||||
getTxOut: 'str int bool', |
sendMany: 'str str int str', //not sure this is will work
|
||||
getTxOutSetInfo: '', |
sendRawTransaction: '', |
||||
getWork: '', |
sendToAddress: 'str float str str', |
||||
help: '', |
setAccount: '', |
||||
importAddress: 'str str bool', |
setGenerate: 'bool int', |
||||
importPrivKey: 'str str bool', |
setTxFee: 'float', |
||||
keyPoolRefill: '', |
signMessage: '', |
||||
listAccounts: 'int', |
signRawTransaction: '', |
||||
listAddressGroupings: '', |
stop: '', |
||||
listReceivedByAccount: 'int bool', |
submitBlock: '', |
||||
listReceivedByAddress: 'int bool', |
validateAddress: '', |
||||
listSinceBlock: 'str int', |
verifyMessage: '', |
||||
listTransactions: 'str int int', |
walletLock: '', |
||||
listUnspent: 'int int', |
walletPassPhrase: 'string int', |
||||
listLockUnspent: 'bool', |
walletPassphraseChange: '', |
||||
lockUnspent: '', |
}; |
||||
move: 'str str float int str', |
|
||||
sendFrom: 'str str float int str str', |
|
||||
sendMany: 'str str int str', //not sure this is will work
|
|
||||
sendRawTransaction: '', |
|
||||
sendToAddress: 'str float str str', |
|
||||
setAccount: '', |
|
||||
setGenerate: 'bool int', |
|
||||
setTxFee: 'float', |
|
||||
signMessage: '', |
|
||||
signRawTransaction: '', |
|
||||
stop: '', |
|
||||
submitBlock: '', |
|
||||
validateAddress: '', |
|
||||
verifyMessage: '', |
|
||||
walletLock: '', |
|
||||
walletPassPhrase: 'string int', |
|
||||
walletPassphraseChange: '', |
|
||||
}; |
|
||||
|
|
||||
var slice = function(arr, start, end) { |
var slice = function(arr, start, end) { |
||||
return Array.prototype.slice.call(arr, start, end); |
return Array.prototype.slice.call(arr, start, end); |
||||
}; |
}; |
||||
|
|
||||
function generateRPCMethods(constructor, apiCalls, rpc) { |
function generateRPCMethods(constructor, apiCalls, rpc) { |
||||
function createRPCMethod(methodName, argMap) { |
function createRPCMethod(methodName, argMap) { |
||||
return function() { |
return function() { |
||||
var limit = arguments.length - 1; |
var limit = arguments.length - 1; |
||||
if(this.batchedCalls) var limit = arguments.length; |
if(this.batchedCalls) var limit = arguments.length; |
||||
for (var i=0; i<limit; i++) { |
for (var i=0; i<limit; i++) { |
||||
if(argMap[i]) arguments[i] = argMap[i](arguments[i]); |
if(argMap[i]) arguments[i] = argMap[i](arguments[i]); |
||||
}; |
|
||||
if(this.batchedCalls) { |
|
||||
this.batchedCalls.push({jsonrpc: '2.0', method: methodName, params: slice(arguments)}); |
|
||||
} else { |
|
||||
rpc.call(this, {method: methodName, params: slice(arguments, 0, arguments.length - 1)}, arguments[arguments.length - 1]); |
|
||||
} |
|
||||
}; |
}; |
||||
|
if(this.batchedCalls) { |
||||
|
this.batchedCalls.push({jsonrpc: '2.0', method: methodName, params: slice(arguments)}); |
||||
|
} else { |
||||
|
rpc.call(this, {method: methodName, params: slice(arguments, 0, arguments.length - 1)}, arguments[arguments.length - 1]); |
||||
|
} |
||||
}; |
}; |
||||
|
}; |
||||
|
|
||||
var types = { |
var types = { |
||||
str: function(arg) {return arg.toString();}, |
str: function(arg) {return arg.toString();}, |
||||
int: function(arg) {return parseFloat(arg);}, |
int: function(arg) {return parseFloat(arg);}, |
||||
float: function(arg) {return parseFloat(arg);}, |
float: function(arg) {return parseFloat(arg);}, |
||||
bool: function(arg) {return (arg === true || arg == '1' || arg == 'true' || arg.toString().toLowerCase() == 'true');}, |
bool: function(arg) {return (arg === true || arg == '1' || arg == 'true' || arg.toString().toLowerCase() == 'true');}, |
||||
}; |
}; |
||||
|
|
||||
for(var k in apiCalls) { |
for(var k in apiCalls) { |
||||
if (apiCalls.hasOwnProperty(k)) { |
if (apiCalls.hasOwnProperty(k)) { |
||||
var spec = apiCalls[k].split(' '); |
var spec = apiCalls[k].split(' '); |
||||
for (var i = 0; i < spec.length; i++) { |
for (var i = 0; i < spec.length; i++) { |
||||
if(types[spec[i]]) { |
if(types[spec[i]]) { |
||||
spec[i] = types[spec[i]]; |
spec[i] = types[spec[i]]; |
||||
} else { |
} else { |
||||
spec[i] = types.string; |
spec[i] = types.string; |
||||
} |
|
||||
} |
} |
||||
var methodName = k.toLowerCase(); |
|
||||
constructor.prototype[k] = createRPCMethod(methodName, spec); |
|
||||
constructor.prototype[methodName] = constructor.prototype[k]; |
|
||||
} |
} |
||||
|
var methodName = k.toLowerCase(); |
||||
|
constructor.prototype[k] = createRPCMethod(methodName, spec); |
||||
|
constructor.prototype[methodName] = constructor.prototype[k]; |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
|
|
||||
function rpc(request, callback) { |
function rpc(request, callback) { |
||||
var self = this; |
var self = this; |
||||
var request; |
var request; |
||||
request = JSON.stringify(request); |
request = JSON.stringify(request); |
||||
var auth = Buffer(self.user + ':' + self.pass).toString('base64'); |
var auth = Buffer(self.user + ':' + self.pass).toString('base64'); |
||||
|
|
||||
var options = { |
var options = { |
||||
host: self.host, |
host: self.host, |
||||
path: '/', |
path: '/', |
||||
method: 'POST', |
method: 'POST', |
||||
port: self.port, |
port: self.port, |
||||
agent: self.disableAgent ? false : undefined, |
agent: self.disableAgent ? false : undefined, |
||||
}; |
}; |
||||
if(self.httpOptions) { |
if(self.httpOptions) { |
||||
for(var k in self.httpOptions) { |
for(var k in self.httpOptions) { |
||||
options[k] = self.httpOptions[k]; |
options[k] = self.httpOptions[k]; |
||||
} |
|
||||
} |
} |
||||
var err = null; |
} |
||||
var req = this.protocol.request(options, function(res) { |
var err = null; |
||||
|
var req = this.protocol.request(options, function(res) { |
||||
|
|
||||
var buf = ''; |
var buf = ''; |
||||
res.on('data', function(data) { |
res.on('data', function(data) { |
||||
buf += data; |
buf += data; |
||||
}); |
|
||||
res.on('end', function() { |
|
||||
if(res.statusCode == 401) { |
|
||||
callback(new Error('bitcoin JSON-RPC connection rejected: 401 unauthorized')); |
|
||||
return; |
|
||||
} |
|
||||
if(res.statusCode == 403) { |
|
||||
callback(new Error('bitcoin JSON-RPC connection rejected: 403 forbidden')); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if(err) { |
|
||||
callback(err); |
|
||||
return; |
|
||||
} |
|
||||
try { |
|
||||
var parsedBuf = JSON.parse(buf); |
|
||||
} catch(e) { |
|
||||
log.err(e.stack); |
|
||||
log.err(buf); |
|
||||
log.err('HTTP Status code:' + res.statusCode); |
|
||||
callback(e); |
|
||||
return; |
|
||||
} |
|
||||
callback(parsedBuf.error, parsedBuf); |
|
||||
}); |
|
||||
}); |
}); |
||||
req.on('error', function(e) { |
res.on('end', function() { |
||||
var err = new Error('Could not connect to bitcoin via RPC: '+e.message); |
if(res.statusCode == 401) { |
||||
log.err(err); |
callback(new Error('bitcoin JSON-RPC connection rejected: 401 unauthorized')); |
||||
callback(err); |
return; |
||||
}); |
} |
||||
|
if(res.statusCode == 403) { |
||||
req.setHeader('Content-Length', request.length); |
callback(new Error('bitcoin JSON-RPC connection rejected: 403 forbidden')); |
||||
req.setHeader('Content-Type', 'application/json'); |
return; |
||||
req.setHeader('Authorization', 'Basic ' + auth); |
} |
||||
req.write(request); |
|
||||
req.end(); |
|
||||
}; |
|
||||
|
|
||||
generateRPCMethods(RpcClient, callspec, rpc); |
if(err) { |
||||
return RpcClient; |
callback(err); |
||||
|
return; |
||||
|
} |
||||
|
try { |
||||
|
var parsedBuf = JSON.parse(buf); |
||||
|
} catch(e) { |
||||
|
log.err(e.stack); |
||||
|
log.err(buf); |
||||
|
log.err('HTTP Status code:' + res.statusCode); |
||||
|
callback(e); |
||||
|
return; |
||||
|
} |
||||
|
callback(parsedBuf.error, parsedBuf); |
||||
|
}); |
||||
|
}); |
||||
|
req.on('error', function(e) { |
||||
|
var err = new Error('Could not connect to bitcoin via RPC: '+e.message); |
||||
|
log.err(err); |
||||
|
callback(err); |
||||
|
}); |
||||
|
|
||||
|
req.setHeader('Content-Length', request.length); |
||||
|
req.setHeader('Content-Type', 'application/json'); |
||||
|
req.setHeader('Authorization', 'Basic ' + auth); |
||||
|
req.write(request); |
||||
|
req.end(); |
||||
}; |
}; |
||||
module.defineClass(ClassSpec); |
|
||||
|
generateRPCMethods(RpcClient, callspec, rpc); |
||||
|
|
||||
|
module.exports = require('soop')(RpcClient); |
||||
|
|
||||
|
@ -1,60 +1,55 @@ |
|||||
require('classtool'); |
var imports = require('soop').imports(); |
||||
|
var parent = imports.parent || require('./util/VersionedData'); |
||||
|
|
||||
function ClassSpec(b) { |
function SIN(type, payload) { |
||||
var superclass = b.superclass || require('./util/VersionedData').class(); |
if (typeof type != 'number') { |
||||
|
SIN.super(this, arguments); |
||||
|
return; |
||||
function SIN(type, payload) { |
|
||||
if (typeof type != 'number') { |
|
||||
SIN.super(this, arguments); |
|
||||
return; |
|
||||
}; |
|
||||
this.data = new Buffer(1 + 1 + payload.length); |
|
||||
this.__proto__ = this.encodings['binary']; |
|
||||
this.prefix(0x0F); // SIN magic number, in numberspace
|
|
||||
this.type(type); |
|
||||
this.payload(payload); |
|
||||
}; |
|
||||
SIN.superclass = superclass; |
|
||||
superclass.applyEncodingsTo(SIN); |
|
||||
|
|
||||
SIN.SIN_PERSIST_MAINNET = 0x01; // associated with sacrifice TX
|
|
||||
SIN.SIN_PERSIST_TESTNET = 0x11; // associated with sacrifice TX
|
|
||||
SIN.SIN_EPHEM = 0x02; // generate off-net at any time
|
|
||||
|
|
||||
// get or set the prefix data (the first byte of the address)
|
|
||||
SIN.prototype.prefix = function(num) { |
|
||||
if(num || (num === 0)) { |
|
||||
this.doAsBinary(function() {this.data.writeUInt8(num, 0);}); |
|
||||
return num; |
|
||||
} |
|
||||
return this.as('binary').readUInt8(0); |
|
||||
}; |
}; |
||||
|
this.data = new Buffer(1 + 1 + payload.length); |
||||
|
this.__proto__ = this.encodings['binary']; |
||||
|
this.prefix(0x0F); // SIN magic number, in numberspace
|
||||
|
this.type(type); |
||||
|
this.payload(payload); |
||||
|
}; |
||||
|
SIN.parent = parent; |
||||
|
parent.applyEncodingsTo(SIN); |
||||
|
|
||||
|
SIN.SIN_PERSIST_MAINNET = 0x01; // associated with sacrifice TX
|
||||
|
SIN.SIN_PERSIST_TESTNET = 0x11; // associated with sacrifice TX
|
||||
|
SIN.SIN_EPHEM = 0x02; // generate off-net at any time
|
||||
|
|
||||
|
// get or set the prefix data (the first byte of the address)
|
||||
|
SIN.prototype.prefix = function(num) { |
||||
|
if(num || (num === 0)) { |
||||
|
this.doAsBinary(function() {this.data.writeUInt8(num, 0);}); |
||||
|
return num; |
||||
|
} |
||||
|
return this.as('binary').readUInt8(0); |
||||
|
}; |
||||
|
|
||||
// get or set the SIN-type data (the second byte of the address)
|
// get or set the SIN-type data (the second byte of the address)
|
||||
SIN.prototype.type = function(num) { |
SIN.prototype.type = function(num) { |
||||
if(num || (num === 0)) { |
if(num || (num === 0)) { |
||||
this.doAsBinary(function() {this.data.writeUInt8(num, 1);}); |
this.doAsBinary(function() {this.data.writeUInt8(num, 1);}); |
||||
return num; |
return num; |
||||
} |
} |
||||
return this.as('binary').readUInt8(1); |
return this.as('binary').readUInt8(1); |
||||
}; |
}; |
||||
|
|
||||
// get or set the payload data (as a Buffer object)
|
// get or set the payload data (as a Buffer object)
|
||||
SIN.prototype.payload = function(data) { |
SIN.prototype.payload = function(data) { |
||||
if(data) { |
if(data) { |
||||
this.doAsBinary(function() {data.copy(this.data, 2);}); |
this.doAsBinary(function() {data.copy(this.data, 2);}); |
||||
return data; |
return data; |
||||
} |
} |
||||
return this.as('binary').slice(1); |
return this.as('binary').slice(1); |
||||
}; |
}; |
||||
|
|
||||
SIN.prototype.validate = function() { |
SIN.prototype.validate = function() { |
||||
this.doAsBinary(function() { |
this.doAsBinary(function() { |
||||
SIN.super(this, 'validate', arguments); |
SIN.super(this, 'validate', arguments); |
||||
if (this.data.length != 22) throw new Error('invalid data length'); |
if (this.data.length != 22) throw new Error('invalid data length'); |
||||
}); |
}); |
||||
}; |
|
||||
return SIN; |
|
||||
}; |
}; |
||||
module.defineClass(ClassSpec); |
module.exports = require('soop')(SIN); |
||||
|
@ -1,43 +1,37 @@ |
|||||
require('classtool'); |
var coinUtil = require('./util/util'); |
||||
|
var timeUtil = require('./util/time'); |
||||
|
var KeyModule = require('./Key'); |
||||
|
var SIN = require('./SIN'); |
||||
|
|
||||
function ClassSpec(b) { |
function SINKey(cfg) { |
||||
var coinUtil = require('./util/util'); |
if (typeof cfg != 'object') |
||||
var timeUtil = require('./util/time'); |
cfg = {}; |
||||
var KeyModule = require('./Key'); |
|
||||
var SIN = require('./SIN').class(); |
|
||||
|
|
||||
function SINKey(cfg) { |
this.created = cfg.created; |
||||
if (typeof cfg != 'object') |
this.privKey = cfg.privKey; |
||||
cfg = {}; |
}; |
||||
|
|
||||
this.created = cfg.created; |
|
||||
this.privKey = cfg.privKey; |
|
||||
}; |
|
||||
|
|
||||
SINKey.prototype.generate = function() { |
|
||||
this.privKey = KeyModule.Key.generateSync(); |
|
||||
this.created = timeUtil.curtime(); |
|
||||
}; |
|
||||
|
|
||||
SINKey.prototype.pubkeyHash = function() { |
SINKey.prototype.generate = function() { |
||||
return coinUtil.sha256ripe160(this.privKey.public); |
this.privKey = KeyModule.Key.generateSync(); |
||||
}; |
this.created = timeUtil.curtime(); |
||||
|
}; |
||||
|
|
||||
SINKey.prototype.storeObj = function() { |
SINKey.prototype.pubkeyHash = function() { |
||||
var pubKey = this.privKey.public.toString('hex'); |
return coinUtil.sha256ripe160(this.privKey.public); |
||||
var pubKeyHash = this.pubkeyHash(); |
}; |
||||
var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash); |
|
||||
var obj = { |
|
||||
created: this.created, |
|
||||
priv: this.privKey.private.toString('hex'), |
|
||||
pub: pubKey, |
|
||||
sin: sin.toString(), |
|
||||
}; |
|
||||
|
|
||||
return obj; |
SINKey.prototype.storeObj = function() { |
||||
|
var pubKey = this.privKey.public.toString('hex'); |
||||
|
var pubKeyHash = this.pubkeyHash(); |
||||
|
var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash); |
||||
|
var obj = { |
||||
|
created: this.created, |
||||
|
priv: this.privKey.private.toString('hex'), |
||||
|
pub: pubKey, |
||||
|
sin: sin.toString(), |
||||
}; |
}; |
||||
|
|
||||
return SINKey; |
return obj; |
||||
}; |
}; |
||||
module.defineClass(ClassSpec); |
|
||||
|
|
||||
|
module.exports = require('soop')(SINKey); |
||||
|
@ -1,523 +1,517 @@ |
|||||
require('classtool'); |
var imports = require('soop').imports(); |
||||
|
var config = imports.config || require('./config'); |
||||
function spec(b) { |
var log = imports.log || require('./util/log'); |
||||
var config = b.config || require('./config'); |
var Opcode = imports.Opcode || require('./Opcode'); |
||||
var log = b.log || require('./util/log'); |
var buffertools = imports.buffertools || require('buffertools'); |
||||
|
|
||||
var Opcode = b.Opcode || require('./Opcode').class(); |
// Make opcodes available as pseudo-constants
|
||||
var buffertools = b.buffertools || require('buffertools'); |
for (var i in Opcode.map) { |
||||
|
eval(i + ' = ' + Opcode.map[i] + ';'); |
||||
// Make opcodes available as pseudo-constants
|
} |
||||
for (var i in Opcode.map) { |
|
||||
eval(i + " = " + Opcode.map[i] + ";"); |
var util = imports.util || require('./util/util'); |
||||
|
var Parser = imports.Parser || require('./util/BinaryParser'); |
||||
|
var Put = imports.Put || require('bufferput'); |
||||
|
|
||||
|
var TX_UNKNOWN = 0; |
||||
|
var TX_PUBKEY = 1; |
||||
|
var TX_PUBKEYHASH = 2; |
||||
|
var TX_MULTISIG = 3; |
||||
|
var TX_SCRIPTHASH = 4; |
||||
|
|
||||
|
var TX_TYPES = [ |
||||
|
'unknown', |
||||
|
'pubkey', |
||||
|
'pubkeyhash', |
||||
|
'multisig', |
||||
|
'scripthash' |
||||
|
]; |
||||
|
|
||||
|
function Script(buffer) { |
||||
|
if(buffer) { |
||||
|
this.buffer = buffer; |
||||
|
} else { |
||||
|
this.buffer = util.EMPTY_BUFFER; |
||||
} |
} |
||||
|
this.chunks = []; |
||||
var util = b.util || require('./util/util'); |
this.parse(); |
||||
var Parser = b.Parser || require('./util/BinaryParser').class(); |
}; |
||||
var Put = b.Put || require('bufferput'); |
this.class = Script; |
||||
|
|
||||
var TX_UNKNOWN = 0; |
Script.TX_UNKNOWN=TX_UNKNOWN; |
||||
var TX_PUBKEY = 1; |
Script.TX_PUBKEY=TX_PUBKEY; |
||||
var TX_PUBKEYHASH = 2; |
Script.TX_PUBKEYHASH=TX_PUBKEYHASH; |
||||
var TX_MULTISIG = 3; |
Script.TX_MULTISIG=TX_MULTISIG; |
||||
var TX_SCRIPTHASH = 4; |
Script.TX_SCRIPTHASH=TX_SCRIPTHASH; |
||||
|
|
||||
var TX_TYPES = [ |
Script.prototype.parse = function () { |
||||
'unknown', |
this.chunks = []; |
||||
'pubkey', |
|
||||
'pubkeyhash', |
var parser = new Parser(this.buffer); |
||||
'multisig', |
while (!parser.eof()) { |
||||
'scripthash' |
var opcode = parser.word8(); |
||||
]; |
|
||||
|
var len; |
||||
function Script(buffer) { |
if (opcode > 0 && opcode < OP_PUSHDATA1) { |
||||
if(buffer) { |
// Read some bytes of data, opcode value is the length of data
|
||||
this.buffer = buffer; |
this.chunks.push(parser.buffer(opcode)); |
||||
|
} else if (opcode == OP_PUSHDATA1) { |
||||
|
len = parser.word8(); |
||||
|
this.chunks.push(parser.buffer(len)); |
||||
|
} else if (opcode == OP_PUSHDATA2) { |
||||
|
len = parser.word16le(); |
||||
|
this.chunks.push(parser.buffer(len)); |
||||
|
} else if (opcode == OP_PUSHDATA4) { |
||||
|
len = parser.word32le(); |
||||
|
this.chunks.push(parser.buffer(len)); |
||||
} else { |
} else { |
||||
this.buffer = util.EMPTY_BUFFER; |
this.chunks.push(opcode); |
||||
} |
} |
||||
this.chunks = []; |
} |
||||
this.parse(); |
}; |
||||
}; |
|
||||
this.class = Script; |
|
||||
|
|
||||
Script.TX_UNKNOWN=TX_UNKNOWN; |
|
||||
Script.TX_PUBKEY=TX_PUBKEY; |
|
||||
Script.TX_PUBKEYHASH=TX_PUBKEYHASH; |
|
||||
Script.TX_MULTISIG=TX_MULTISIG; |
|
||||
Script.TX_SCRIPTHASH=TX_SCRIPTHASH; |
|
||||
|
|
||||
Script.prototype.parse = function () { |
|
||||
this.chunks = []; |
|
||||
|
|
||||
var parser = new Parser(this.buffer); |
|
||||
while (!parser.eof()) { |
|
||||
var opcode = parser.word8(); |
|
||||
|
|
||||
var len; |
|
||||
if (opcode > 0 && opcode < OP_PUSHDATA1) { |
|
||||
// Read some bytes of data, opcode value is the length of data
|
|
||||
this.chunks.push(parser.buffer(opcode)); |
|
||||
} else if (opcode == OP_PUSHDATA1) { |
|
||||
len = parser.word8(); |
|
||||
this.chunks.push(parser.buffer(len)); |
|
||||
} else if (opcode == OP_PUSHDATA2) { |
|
||||
len = parser.word16le(); |
|
||||
this.chunks.push(parser.buffer(len)); |
|
||||
} else if (opcode == OP_PUSHDATA4) { |
|
||||
len = parser.word32le(); |
|
||||
this.chunks.push(parser.buffer(len)); |
|
||||
} else { |
|
||||
this.chunks.push(opcode); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
Script.prototype.isPushOnly = function () |
Script.prototype.isPushOnly = function () |
||||
{ |
{ |
||||
for (var i = 0; i < this.chunks.length; i++) |
for (var i = 0; i < this.chunks.length; i++) |
||||
if (!Buffer.isBuffer(this.chunks[i])) |
if (!Buffer.isBuffer(this.chunks[i])) |
||||
return false; |
return false; |
||||
|
|
||||
return true; |
return true; |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.isP2SH = function () |
Script.prototype.isP2SH = function () |
||||
{ |
{ |
||||
return (this.chunks.length == 3 && |
return (this.chunks.length == 3 && |
||||
this.chunks[0] == OP_HASH160 && |
this.chunks[0] == OP_HASH160 && |
||||
Buffer.isBuffer(this.chunks[1]) && |
Buffer.isBuffer(this.chunks[1]) && |
||||
this.chunks[1].length == 20 && |
this.chunks[1].length == 20 && |
||||
this.chunks[2] == OP_EQUAL); |
this.chunks[2] == OP_EQUAL); |
||||
}; |
}; |
||||
|
|
||||
Script.prototype.isPubkey = function () |
|
||||
{ |
|
||||
return (this.chunks.length == 2 && |
|
||||
Buffer.isBuffer(this.chunks[0]) && |
|
||||
this.chunks[1] == OP_CHECKSIG); |
|
||||
}; |
|
||||
|
|
||||
Script.prototype.isPubkeyHash = function () |
Script.prototype.isPubkey = function () |
||||
{ |
{ |
||||
return (this.chunks.length == 5 && |
return (this.chunks.length == 2 && |
||||
this.chunks[0] == OP_DUP && |
Buffer.isBuffer(this.chunks[0]) && |
||||
this.chunks[1] == OP_HASH160 && |
this.chunks[1] == OP_CHECKSIG); |
||||
Buffer.isBuffer(this.chunks[2]) && |
}; |
||||
this.chunks[2].length == 20 && |
|
||||
this.chunks[3] == OP_EQUALVERIFY && |
|
||||
this.chunks[4] == OP_CHECKSIG); |
|
||||
}; |
|
||||
|
|
||||
function isSmallIntOp(opcode) |
|
||||
{ |
|
||||
return ((opcode == OP_0) || |
|
||||
((opcode >= OP_1) && (opcode <= OP_16))); |
|
||||
}; |
|
||||
|
|
||||
Script.prototype.isMultiSig = function () |
Script.prototype.isPubkeyHash = function () |
||||
{ |
{ |
||||
return (this.chunks.length > 3 && |
return (this.chunks.length == 5 && |
||||
isSmallIntOp(this.chunks[0]) && |
this.chunks[0] == OP_DUP && |
||||
isSmallIntOp(this.chunks[this.chunks.length-2]) && |
this.chunks[1] == OP_HASH160 && |
||||
this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG); |
Buffer.isBuffer(this.chunks[2]) && |
||||
}; |
this.chunks[2].length == 20 && |
||||
|
this.chunks[3] == OP_EQUALVERIFY && |
||||
|
this.chunks[4] == OP_CHECKSIG); |
||||
|
}; |
||||
|
|
||||
Script.prototype.finishedMultiSig = function() |
function isSmallIntOp(opcode) |
||||
{ |
{ |
||||
var nsigs = 0; |
return ((opcode == OP_0) || |
||||
for (var i = 0; i < this.chunks.length-1; i++) |
((opcode >= OP_1) && (opcode <= OP_16))); |
||||
if (this.chunks[i] !== 0) |
}; |
||||
nsigs++; |
|
||||
|
Script.prototype.isMultiSig = function () |
||||
var serializedScript = this.chunks[this.chunks.length-1]; |
{ |
||||
var script = new Script(serializedScript); |
return (this.chunks.length > 3 && |
||||
var nreq = script.chunks[0] - 80; //see OP_2-OP_16
|
isSmallIntOp(this.chunks[0]) && |
||||
|
isSmallIntOp(this.chunks[this.chunks.length-2]) && |
||||
if (nsigs == nreq) |
this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG); |
||||
return true; |
}; |
||||
else |
|
||||
return false; |
Script.prototype.finishedMultiSig = function() |
||||
} |
{ |
||||
|
var nsigs = 0; |
||||
|
for (var i = 0; i < this.chunks.length-1; i++) |
||||
|
if (this.chunks[i] !== 0) |
||||
|
nsigs++; |
||||
|
|
||||
Script.prototype.removePlaceHolders = function() |
var serializedScript = this.chunks[this.chunks.length-1]; |
||||
|
var script = new Script(serializedScript); |
||||
|
var nreq = script.chunks[0] - 80; //see OP_2-OP_16
|
||||
|
|
||||
|
if (nsigs == nreq) |
||||
|
return true; |
||||
|
else |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
Script.prototype.removePlaceHolders = function() |
||||
|
{ |
||||
|
var chunks = []; |
||||
|
for (var i in this.chunks) |
||||
{ |
{ |
||||
var chunks = []; |
if (this.chunks.hasOwnProperty(i)) { |
||||
for (var i in this.chunks) |
var chunk = this.chunks[i]; |
||||
{ |
if (chunk != 0) |
||||
if (this.chunks.hasOwnProperty(i)) { |
chunks.push(chunk); |
||||
var chunk = this.chunks[i]; |
|
||||
if (chunk != 0) |
|
||||
chunks.push(chunk); |
|
||||
} |
|
||||
} |
} |
||||
this.chunks = chunks; |
|
||||
this.updateBuffer(); |
|
||||
return this; |
|
||||
} |
} |
||||
|
this.chunks = chunks; |
||||
Script.prototype.prependOp0 = function() |
this.updateBuffer(); |
||||
{ |
return this; |
||||
var chunks = [0]; |
} |
||||
for (i in this.chunks) { |
|
||||
if (this.chunks.hasOwnProperty(i)) { |
Script.prototype.prependOp0 = function() |
||||
chunks.push(this.chunks[i]); |
{ |
||||
} |
var chunks = [0]; |
||||
|
for (i in this.chunks) { |
||||
|
if (this.chunks.hasOwnProperty(i)) { |
||||
|
chunks.push(this.chunks[i]); |
||||
} |
} |
||||
this.chunks = chunks; |
|
||||
this.updateBuffer(); |
|
||||
return this; |
|
||||
} |
} |
||||
|
this.chunks = chunks; |
||||
|
this.updateBuffer(); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
// is this a script form we know?
|
||||
|
Script.prototype.classify = function () |
||||
|
{ |
||||
|
if (this.isPubkeyHash()) |
||||
|
return TX_PUBKEYHASH; |
||||
|
if (this.isP2SH()) |
||||
|
return TX_SCRIPTHASH; |
||||
|
if (this.isMultiSig()) |
||||
|
return TX_MULTISIG; |
||||
|
if (this.isPubkey()) |
||||
|
return TX_PUBKEY; |
||||
|
return TX_UNKNOWN; |
||||
|
}; |
||||
|
|
||||
// is this a script form we know?
|
// extract useful data items from known scripts
|
||||
Script.prototype.classify = function () |
Script.prototype.capture = function () |
||||
{ |
{ |
||||
if (this.isPubkeyHash()) |
var txType = this.classify(); |
||||
return TX_PUBKEYHASH; |
var res = []; |
||||
if (this.isP2SH()) |
switch (txType) { |
||||
return TX_SCRIPTHASH; |
case TX_PUBKEY: |
||||
if (this.isMultiSig()) |
res.push(this.chunks[0]); |
||||
return TX_MULTISIG; |
break; |
||||
if (this.isPubkey()) |
case TX_PUBKEYHASH: |
||||
return TX_PUBKEY; |
res.push(this.chunks[2]); |
||||
return TX_UNKNOWN; |
break; |
||||
}; |
case TX_MULTISIG: |
||||
|
for (var i = 1; i < (this.chunks.length - 2); i++) |
||||
// extract useful data items from known scripts
|
res.push(this.chunks[i]); |
||||
Script.prototype.capture = function () |
break; |
||||
{ |
case TX_SCRIPTHASH: |
||||
var txType = this.classify(); |
res.push(this.chunks[1]); |
||||
var res = []; |
break; |
||||
|
|
||||
|
case TX_UNKNOWN: |
||||
|
default: |
||||
|
// do nothing
|
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return res; |
||||
|
}; |
||||
|
|
||||
|
// return first extracted data item from script
|
||||
|
Script.prototype.captureOne = function () |
||||
|
{ |
||||
|
var arr = this.capture(); |
||||
|
return arr[0]; |
||||
|
}; |
||||
|
|
||||
|
Script.prototype.getOutType = function () |
||||
|
{ |
||||
|
var txType = this.classify(); |
||||
switch (txType) { |
switch (txType) { |
||||
case TX_PUBKEY: |
case TX_PUBKEY: return 'Pubkey'; |
||||
res.push(this.chunks[0]); |
case TX_PUBKEYHASH: return 'Address'; |
||||
break; |
default: return 'Strange'; |
||||
case TX_PUBKEYHASH: |
} |
||||
res.push(this.chunks[2]); |
}; |
||||
break; |
|
||||
case TX_MULTISIG: |
Script.prototype.getRawOutType = function() { |
||||
for (var i = 1; i < (this.chunks.length - 2); i++) |
return TX_TYPES[this.classify()]; |
||||
res.push(this.chunks[i]); |
}; |
||||
break; |
|
||||
case TX_SCRIPTHASH: |
Script.prototype.simpleOutHash = function () |
||||
res.push(this.chunks[1]); |
{ |
||||
break; |
switch (this.getOutType()) { |
||||
|
case 'Address': |
||||
case TX_UNKNOWN: |
return this.chunks[2]; |
||||
|
case 'Pubkey': |
||||
|
return util.sha256ripe160(this.chunks[0]); |
||||
default: |
default: |
||||
// do nothing
|
log.debug("Encountered non-standard scriptPubKey"); |
||||
break; |
log.debug("Strange script was: " + this.toString()); |
||||
|
return null; |
||||
} |
} |
||||
|
}; |
||||
|
|
||||
return res; |
Script.prototype.getInType = function () |
||||
}; |
{ |
||||
|
if (this.chunks.length == 1) { |
||||
|
// Direct IP to IP transactions only have the public key in their scriptSig.
|
||||
|
return 'Pubkey'; |
||||
|
} else if (this.chunks.length == 2 && |
||||
|
Buffer.isBuffer(this.chunks[0]) && |
||||
|
Buffer.isBuffer(this.chunks[1])) { |
||||
|
return 'Address'; |
||||
|
} else { |
||||
|
return 'Strange'; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
// return first extracted data item from script
|
Script.prototype.simpleInPubKey = function () |
||||
Script.prototype.captureOne = function () |
{ |
||||
{ |
switch (this.getInType()) { |
||||
var arr = this.capture(); |
case 'Address': |
||||
return arr[0]; |
return this.chunks[1]; |
||||
}; |
case 'Pubkey': |
||||
|
return null; |
||||
|
default: |
||||
|
log.debug("Encountered non-standard scriptSig"); |
||||
|
log.debug("Strange script was: " + this.toString()); |
||||
|
return null; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
Script.prototype.getOutType = function () |
Script.prototype.getBuffer = function () |
||||
{ |
{ |
||||
var txType = this.classify(); |
return this.buffer; |
||||
switch (txType) { |
}; |
||||
case TX_PUBKEY: return 'Pubkey'; |
|
||||
case TX_PUBKEYHASH: return 'Address'; |
|
||||
default: return 'Strange'; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
Script.prototype.getRawOutType = function() { |
Script.prototype.getStringContent = function (truncate, maxEl) |
||||
return TX_TYPES[this.classify()]; |
{ |
||||
}; |
if (truncate === null) { |
||||
|
truncate = true; |
||||
|
} |
||||
|
|
||||
Script.prototype.simpleOutHash = function () |
if ("undefined" === typeof maxEl) { |
||||
{ |
maxEl = 15; |
||||
switch (this.getOutType()) { |
} |
||||
case 'Address': |
|
||||
return this.chunks[2]; |
|
||||
case 'Pubkey': |
|
||||
return util.sha256ripe160(this.chunks[0]); |
|
||||
default: |
|
||||
log.debug("Encountered non-standard scriptPubKey"); |
|
||||
log.debug("Strange script was: " + this.toString()); |
|
||||
return null; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
Script.prototype.getInType = function () |
var script = ''; |
||||
{ |
for (var i = 0, l = this.chunks.length; i < l; i++) { |
||||
if (this.chunks.length == 1) { |
var chunk = this.chunks[i]; |
||||
// Direct IP to IP transactions only have the public key in their scriptSig.
|
|
||||
return 'Pubkey'; |
|
||||
} else if (this.chunks.length == 2 && |
|
||||
Buffer.isBuffer(this.chunks[0]) && |
|
||||
Buffer.isBuffer(this.chunks[1])) { |
|
||||
return 'Address'; |
|
||||
} else { |
|
||||
return 'Strange'; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
Script.prototype.simpleInPubKey = function () |
if (i > 0) { |
||||
{ |
script += " "; |
||||
switch (this.getInType()) { |
|
||||
case 'Address': |
|
||||
return this.chunks[1]; |
|
||||
case 'Pubkey': |
|
||||
return null; |
|
||||
default: |
|
||||
log.debug("Encountered non-standard scriptSig"); |
|
||||
log.debug("Strange script was: " + this.toString()); |
|
||||
return null; |
|
||||
} |
} |
||||
}; |
|
||||
|
|
||||
Script.prototype.getBuffer = function () |
if (Buffer.isBuffer(chunk)) { |
||||
{ |
script += "0x"+util.formatBuffer(chunk, truncate ? null : 0); |
||||
return this.buffer; |
} else { |
||||
}; |
script += Opcode.reverseMap[chunk]; |
||||
|
|
||||
Script.prototype.getStringContent = function (truncate, maxEl) |
|
||||
{ |
|
||||
if (truncate === null) { |
|
||||
truncate = true; |
|
||||
} |
} |
||||
|
|
||||
if ("undefined" === typeof maxEl) { |
if (maxEl && i > maxEl) { |
||||
maxEl = 15; |
script += " ..."; |
||||
|
break; |
||||
} |
} |
||||
|
} |
||||
|
return script; |
||||
|
}; |
||||
|
|
||||
var script = ''; |
Script.prototype.toString = function (truncate, maxEl) |
||||
for (var i = 0, l = this.chunks.length; i < l; i++) { |
{ |
||||
var chunk = this.chunks[i]; |
var script = "<Script "; |
||||
|
script += this.getStringContent(truncate, maxEl); |
||||
|
script += ">"; |
||||
|
return script; |
||||
|
}; |
||||
|
|
||||
if (i > 0) { |
|
||||
script += " "; |
|
||||
} |
|
||||
|
|
||||
if (Buffer.isBuffer(chunk)) { |
Script.prototype.writeOp = function (opcode) |
||||
script += "0x"+util.formatBuffer(chunk, truncate ? null : 0); |
{ |
||||
} else { |
var buf = Buffer(this.buffer.length + 1); |
||||
script += Opcode.reverseMap[chunk]; |
this.buffer.copy(buf); |
||||
} |
buf.writeUInt8(opcode, this.buffer.length); |
||||
|
|
||||
if (maxEl && i > maxEl) { |
this.buffer = buf; |
||||
script += " ..."; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
return script; |
|
||||
}; |
|
||||
|
|
||||
Script.prototype.toString = function (truncate, maxEl) |
this.chunks.push(opcode); |
||||
{ |
}; |
||||
var script = "<Script "; |
|
||||
script += this.getStringContent(truncate, maxEl); |
|
||||
script += ">"; |
|
||||
return script; |
|
||||
}; |
|
||||
|
|
||||
|
Script.prototype.writeN = function (n) |
||||
|
{ |
||||
|
if (n < 0 || n > 16) |
||||
|
throw new Error("writeN: out of range value " + n); |
||||
|
|
||||
Script.prototype.writeOp = function (opcode) |
if (n == 0) |
||||
{ |
this.writeOp(OP_0); |
||||
var buf = Buffer(this.buffer.length + 1); |
else |
||||
this.buffer.copy(buf); |
this.writeOp(OP_1 + n - 1); |
||||
buf.writeUInt8(opcode, this.buffer.length); |
}; |
||||
|
|
||||
this.buffer = buf; |
function prefixSize(data_length) |
||||
|
{ |
||||
|
if (data_length < OP_PUSHDATA1) { |
||||
|
return 1; |
||||
|
} else if (data_length <= 0xff) { |
||||
|
return 1 + 1; |
||||
|
} else if (data_length <= 0xffff) { |
||||
|
return 1 + 2; |
||||
|
} else { |
||||
|
return 1 + 4; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
this.chunks.push(opcode); |
function encodeLen(data_length) { |
||||
}; |
var buf = undefined; |
||||
|
if (data_length < OP_PUSHDATA1) { |
||||
|
buf = new Buffer(1); |
||||
|
buf.writeUInt8(data_length, 0); |
||||
|
} |
||||
|
|
||||
|
else if (data_length <= 0xff) { |
||||
|
buf = new Buffer(1 + 1); |
||||
|
buf.writeUInt8(OP_PUSHDATA1, 0); |
||||
|
buf.writeUInt8(data_length, 1); |
||||
|
} |
||||
|
|
||||
|
else if (data_length <= 0xffff) { |
||||
|
buf = new Buffer(1 + 2); |
||||
|
buf.writeUInt8(OP_PUSHDATA2, 0); |
||||
|
buf.writeUInt16LE(data_length, 1); |
||||
|
} |
||||
|
|
||||
|
else { |
||||
|
buf = new Buffer(1 + 4); |
||||
|
buf.writeUInt8(OP_PUSHDATA4, 0); |
||||
|
buf.writeUInt32LE(data_length, 1); |
||||
|
} |
||||
|
|
||||
Script.prototype.writeN = function (n) |
return buf; |
||||
{ |
}; |
||||
if (n < 0 || n > 16) |
|
||||
throw new Error("writeN: out of range value " + n); |
|
||||
|
|
||||
if (n == 0) |
Script.prototype.writeBytes = function (data) |
||||
this.writeOp(OP_0); |
{ |
||||
else |
var newSize = this.buffer.length + prefixSize(data.length) + data.length; |
||||
this.writeOp(OP_1 + n - 1); |
this.buffer = Buffer.concat([this.buffer, encodeLen(data.length), data]); |
||||
}; |
this.chunks.push(data); |
||||
|
}; |
||||
|
|
||||
function prefixSize(data_length) |
Script.prototype.updateBuffer = function () |
||||
{ |
{ |
||||
if (data_length < OP_PUSHDATA1) { |
this.buffer = Script.chunksToBuffer(this.chunks); |
||||
return 1; |
}; |
||||
} else if (data_length <= 0xff) { |
|
||||
return 1 + 1; |
|
||||
} else if (data_length <= 0xffff) { |
|
||||
return 1 + 2; |
|
||||
} else { |
|
||||
return 1 + 4; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
function encodeLen(data_length) { |
Script.prototype.findAndDelete = function (chunk) |
||||
var buf = undefined; |
{ |
||||
if (data_length < OP_PUSHDATA1) { |
var dirty = false; |
||||
buf = new Buffer(1); |
if (Buffer.isBuffer(chunk)) { |
||||
buf.writeUInt8(data_length, 0); |
for (var i = 0, l = this.chunks.length; i < l; i++) { |
||||
} |
if (Buffer.isBuffer(this.chunks[i]) && |
||||
|
buffertools.compare(this.chunks[i], chunk) === 0) { |
||||
else if (data_length <= 0xff) { |
this.chunks.splice(i, 1); |
||||
buf = new Buffer(1 + 1); |
dirty = true; |
||||
buf.writeUInt8(OP_PUSHDATA1, 0); |
} |
||||
buf.writeUInt8(data_length, 1); |
|
||||
} |
|
||||
|
|
||||
else if (data_length <= 0xffff) { |
|
||||
buf = new Buffer(1 + 2); |
|
||||
buf.writeUInt8(OP_PUSHDATA2, 0); |
|
||||
buf.writeUInt16LE(data_length, 1); |
|
||||
} |
} |
||||
|
} else if ("number" === typeof chunk) { |
||||
else { |
for (var i = 0, l = this.chunks.length; i < l; i++) { |
||||
buf = new Buffer(1 + 4); |
if (this.chunks[i] === chunk) { |
||||
buf.writeUInt8(OP_PUSHDATA4, 0); |
this.chunks.splice(i, 1); |
||||
buf.writeUInt32LE(data_length, 1); |
dirty = true; |
||||
|
} |
||||
} |
} |
||||
|
} else { |
||||
|
throw new Error("Invalid chunk datatype."); |
||||
|
} |
||||
|
if (dirty) { |
||||
|
this.updateBuffer(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
return buf; |
/** |
||||
}; |
* Creates a simple OP_CHECKSIG with pubkey output script. |
||||
|
* |
||||
|
* These are used for coinbase transactions and at some point were used for |
||||
|
* IP-based transactions as well. |
||||
|
*/ |
||||
|
Script.createPubKeyOut = function (pubkey) { |
||||
|
var script = new Script(); |
||||
|
script.writeBytes(pubkey); |
||||
|
script.writeOp(OP_CHECKSIG); |
||||
|
return script; |
||||
|
}; |
||||
|
|
||||
Script.prototype.writeBytes = function (data) |
/** |
||||
{ |
* Creates a standard txout script. |
||||
var newSize = this.buffer.length + prefixSize(data.length) + data.length; |
*/ |
||||
this.buffer = Buffer.concat([this.buffer, encodeLen(data.length), data]); |
Script.createPubKeyHashOut = function (pubKeyHash) { |
||||
this.chunks.push(data); |
var script = new Script(); |
||||
}; |
script.writeOp(OP_DUP); |
||||
|
script.writeOp(OP_HASH160); |
||||
|
script.writeBytes(pubKeyHash); |
||||
|
script.writeOp(OP_EQUALVERIFY); |
||||
|
script.writeOp(OP_CHECKSIG); |
||||
|
return script; |
||||
|
}; |
||||
|
|
||||
Script.prototype.updateBuffer = function () |
Script.createMultisig = function(n_required, keys) { |
||||
{ |
var script = new Script(); |
||||
this.buffer = Script.chunksToBuffer(this.chunks); |
script.writeN(n_required); |
||||
}; |
keys.forEach(function(key) { |
||||
|
script.writeBytes(key); |
||||
|
}); |
||||
|
script.writeN(keys.length); |
||||
|
script.writeOp(OP_CHECKMULTISIG); |
||||
|
return script; |
||||
|
}; |
||||
|
|
||||
Script.prototype.findAndDelete = function (chunk) |
Script.createP2SH = function(scriptHash) { |
||||
{ |
var script = new Script(); |
||||
var dirty = false; |
script.writeOp(OP_HASH160); |
||||
if (Buffer.isBuffer(chunk)) { |
script.writeBytes(scriptHash); |
||||
for (var i = 0, l = this.chunks.length; i < l; i++) { |
script.writeOp(OP_EQUAL); |
||||
if (Buffer.isBuffer(this.chunks[i]) && |
return script; |
||||
buffertools.compare(this.chunks[i], chunk) === 0) { |
}; |
||||
this.chunks.splice(i, 1); |
|
||||
dirty = true; |
Script.fromTestData = function (testData) { |
||||
} |
testData = testData.map(function (chunk) { |
||||
} |
if ("string" === typeof chunk) { |
||||
} else if ("number" === typeof chunk) { |
return new Buffer(chunk, 'hex'); |
||||
for (var i = 0, l = this.chunks.length; i < l; i++) { |
|
||||
if (this.chunks[i] === chunk) { |
|
||||
this.chunks.splice(i, 1); |
|
||||
dirty = true; |
|
||||
} |
|
||||
} |
|
||||
} else { |
} else { |
||||
throw new Error("Invalid chunk datatype."); |
return chunk; |
||||
} |
|
||||
if (dirty) { |
|
||||
this.updateBuffer(); |
|
||||
} |
} |
||||
}; |
}); |
||||
|
|
||||
/** |
var script = new Script(); |
||||
* Creates a simple OP_CHECKSIG with pubkey output script. |
script.chunks = testData; |
||||
* |
script.updateBuffer(); |
||||
* These are used for coinbase transactions and at some point were used for |
return script; |
||||
* IP-based transactions as well. |
}; |
||||
*/ |
|
||||
Script.createPubKeyOut = function (pubkey) { |
Script.fromChunks = function (chunks) { |
||||
var script = new Script(); |
var script = new Script(); |
||||
script.writeBytes(pubkey); |
script.chunks = chunks; |
||||
script.writeOp(OP_CHECKSIG); |
script.updateBuffer(); |
||||
return script; |
return script; |
||||
}; |
}; |
||||
|
|
||||
/** |
Script.chunksToBuffer = function (chunks) { |
||||
* Creates a standard txout script. |
var buf = new Put(); |
||||
*/ |
|
||||
Script.createPubKeyHashOut = function (pubKeyHash) { |
for (var i = 0, l = chunks.length; i < l; i++) { |
||||
var script = new Script(); |
var data = chunks[i]; |
||||
script.writeOp(OP_DUP); |
if (Buffer.isBuffer(data)) { |
||||
script.writeOp(OP_HASH160); |
if (data.length < OP_PUSHDATA1) { |
||||
script.writeBytes(pubKeyHash); |
buf.word8(data.length); |
||||
script.writeOp(OP_EQUALVERIFY); |
} else if (data.length <= 0xff) { |
||||
script.writeOp(OP_CHECKSIG); |
buf.word8(OP_PUSHDATA1); |
||||
return script; |
buf.word8(data.length); |
||||
}; |
} else if (data.length <= 0xffff) { |
||||
|
buf.word8(OP_PUSHDATA2); |
||||
Script.createMultisig = function(n_required, keys) { |
buf.word16le(data.length); |
||||
var script = new Script(); |
|
||||
script.writeN(n_required); |
|
||||
keys.forEach(function(key) { |
|
||||
script.writeBytes(key); |
|
||||
}); |
|
||||
script.writeN(keys.length); |
|
||||
script.writeOp(OP_CHECKMULTISIG); |
|
||||
return script; |
|
||||
}; |
|
||||
|
|
||||
Script.createP2SH = function(scriptHash) { |
|
||||
var script = new Script(); |
|
||||
script.writeOp(OP_HASH160); |
|
||||
script.writeBytes(scriptHash); |
|
||||
script.writeOp(OP_EQUAL); |
|
||||
return script; |
|
||||
}; |
|
||||
|
|
||||
Script.fromTestData = function (testData) { |
|
||||
testData = testData.map(function (chunk) { |
|
||||
if ("string" === typeof chunk) { |
|
||||
return new Buffer(chunk, 'hex'); |
|
||||
} else { |
|
||||
return chunk; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
var script = new Script(); |
|
||||
script.chunks = testData; |
|
||||
script.updateBuffer(); |
|
||||
return script; |
|
||||
}; |
|
||||
|
|
||||
Script.fromChunks = function (chunks) { |
|
||||
var script = new Script(); |
|
||||
script.chunks = chunks; |
|
||||
script.updateBuffer(); |
|
||||
return script; |
|
||||
}; |
|
||||
|
|
||||
Script.chunksToBuffer = function (chunks) { |
|
||||
var buf = new Put(); |
|
||||
|
|
||||
for (var i = 0, l = chunks.length; i < l; i++) { |
|
||||
var data = chunks[i]; |
|
||||
if (Buffer.isBuffer(data)) { |
|
||||
if (data.length < OP_PUSHDATA1) { |
|
||||
buf.word8(data.length); |
|
||||
} else if (data.length <= 0xff) { |
|
||||
buf.word8(OP_PUSHDATA1); |
|
||||
buf.word8(data.length); |
|
||||
} else if (data.length <= 0xffff) { |
|
||||
buf.word8(OP_PUSHDATA2); |
|
||||
buf.word16le(data.length); |
|
||||
} else { |
|
||||
buf.word8(OP_PUSHDATA4); |
|
||||
buf.word32le(data.length); |
|
||||
} |
|
||||
buf.put(data); |
|
||||
} else if ("number" === typeof data) { |
|
||||
buf.word8(data); |
|
||||
} else { |
} else { |
||||
throw new Error("Script.chunksToBuffer(): Invalid chunk datatype"); |
buf.word8(OP_PUSHDATA4); |
||||
|
buf.word32le(data.length); |
||||
} |
} |
||||
|
buf.put(data); |
||||
|
} else if ("number" === typeof data) { |
||||
|
buf.word8(data); |
||||
|
} else { |
||||
|
throw new Error("Script.chunksToBuffer(): Invalid chunk datatype"); |
||||
} |
} |
||||
return buf.buffer(); |
} |
||||
}; |
return buf.buffer(); |
||||
|
|
||||
return Script; |
|
||||
}; |
}; |
||||
module.defineClass(spec); |
|
||||
|
|
||||
|
module.exports = require('soop')(Script); |
||||
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,142 +1,140 @@ |
|||||
require('classtool'); |
var imports = require('soop').imports(); |
||||
|
|
||||
var hex = function(hex) {return new Buffer(hex, 'hex');}; |
var hex = function(hex) {return new Buffer(hex, 'hex');}; |
||||
|
|
||||
function ClassSpec(b) { |
var fs = require('fs'); |
||||
var fs = require('fs'); |
var EncFile = require('./util/EncFile'); |
||||
var EncFile = require('./util/EncFile'); |
var Address = require('./Address'); |
||||
var Address = require('./Address').class(); |
var networks = require('./networks'); |
||||
var networks = require('./networks'); |
var util = imports.util || require('./util/util'); |
||||
var util = b.util || require('./util/util'); |
var ENC_METHOD = 'aes-256-cbc'; |
||||
var ENC_METHOD = 'aes-256-cbc'; |
|
||||
|
var skeleton = { |
||||
var skeleton = { |
client: 'libcoin', |
||||
client: 'libcoin', |
client_version: '0.0.1', |
||||
client_version: '0.0.1', |
network: 'testnet', |
||||
network: 'testnet', |
version: 1, |
||||
version: 1, |
best_hash: null, |
||||
best_hash: null, |
best_height: -1, |
||||
best_height: -1, |
keys: [], |
||||
keys: [], |
sin: {}, |
||||
sin: {}, |
scripts: {}, |
||||
scripts: {}, |
}; |
||||
}; |
|
||||
|
function Wallet(cfg) { |
||||
function Wallet(cfg) { |
if (typeof cfg !== 'object') |
||||
if (typeof cfg !== 'object') |
cfg = {}; |
||||
cfg = {}; |
|
||||
|
// deep copy (no references)
|
||||
// deep copy (no references)
|
if (cfg.datastore) |
||||
if (cfg.datastore) |
this.datastore = JSON.parse(JSON.stringify(cfg.datastore)); |
||||
this.datastore = JSON.parse(JSON.stringify(cfg.datastore)); |
else |
||||
else |
this.datastore = JSON.parse(JSON.stringify(skeleton)); |
||||
this.datastore = JSON.parse(JSON.stringify(skeleton)); |
|
||||
|
this.network = undefined; |
||||
this.network = undefined; |
this.dirty = cfg.dirty || true; |
||||
this.dirty = cfg.dirty || true; |
}; |
||||
}; |
|
||||
|
Wallet.prototype.readSync = function(filename, passphrase) { |
||||
Wallet.prototype.readSync = function(filename, passphrase) { |
this.datastore = EncFile.readJFileSync(ENC_METHOD, |
||||
this.datastore = EncFile.readJFileSync(ENC_METHOD, |
passphrase, filename); |
||||
passphrase, filename); |
this.dirty = false; |
||||
this.dirty = false; |
}; |
||||
}; |
|
||||
|
Wallet.prototype.writeSync = function(filename, passphrase) { |
||||
Wallet.prototype.writeSync = function(filename, passphrase) { |
var tmp_fn = filename + ".tmp"; |
||||
var tmp_fn = filename + ".tmp"; |
|
||||
|
EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn, |
||||
EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn, |
this.datastore); |
||||
this.datastore); |
fs.renameSync(tmp_fn, filename); |
||||
fs.renameSync(tmp_fn, filename); |
|
||||
|
this.dirty = false; |
||||
this.dirty = false; |
}; |
||||
}; |
|
||||
|
Wallet.prototype.setNetwork = function(netname) { |
||||
Wallet.prototype.setNetwork = function(netname) { |
if (!netname) |
||||
if (!netname) |
netname = this.datastore.network; |
||||
netname = this.datastore.network; |
|
||||
|
switch (netname) { |
||||
switch (netname) { |
case "mainnet": |
||||
case "mainnet": |
case "livenet": |
||||
case "livenet": |
this.network = networks.livenet; |
||||
this.network = networks.livenet; |
break; |
||||
break; |
case "testnet": |
||||
case "testnet": |
this.network = networks.testnet; |
||||
this.network = networks.testnet; |
break; |
||||
break; |
default: |
||||
default: |
throw new Error("Unsupported network"); |
||||
throw new Error("Unsupported network"); |
} |
||||
} |
|
||||
|
// store+canonicalize name
|
||||
// store+canonicalize name
|
this.datastore['network'] = this.network.name; |
||||
this.datastore['network'] = this.network.name; |
this.dirty = true; |
||||
this.dirty = true; |
}; |
||||
}; |
|
||||
|
Wallet.prototype.addKey = function(wkey) { |
||||
Wallet.prototype.addKey = function(wkey) { |
this.datastore.keys.push(wkey); |
||||
this.datastore.keys.push(wkey); |
this.dirty = true; |
||||
this.dirty = true; |
|
||||
}; |
|
||||
|
|
||||
Wallet.prototype.addSIN = function(sinObj) { |
|
||||
this.datastore.sin[sinObj.sin] = sinObj; |
|
||||
this.dirty = true; |
|
||||
}; |
|
||||
|
|
||||
Wallet.prototype.findKeyHash = function(pubKeyHash) { |
|
||||
var pkhStr = pubKeyHash.toString(); |
|
||||
|
|
||||
for (var i = 0; i < this.datastore.keys.length; i++) { |
|
||||
var obj = this.datastore.keys[i]; |
|
||||
var addrStr = obj.addr; |
|
||||
var addr = new Address(addrStr); |
|
||||
if (addr.payload().toString() == pkhStr) |
|
||||
return obj; |
|
||||
} |
|
||||
|
|
||||
return undefined; |
|
||||
}; |
|
||||
|
|
||||
Wallet.prototype.expandKey = function(key) { |
|
||||
var addr = new Address(key); |
|
||||
var isAddr = true; |
|
||||
|
|
||||
try { |
|
||||
addr.validate(); |
|
||||
var b = addr.payload(); |
|
||||
var obj = this.findKeyHash(b); |
|
||||
key = obj.pub; |
|
||||
} catch(e) { |
|
||||
// do nothing
|
|
||||
} |
|
||||
|
|
||||
var re = /^[a-fA-F0-9]+$/; |
|
||||
if (!key.match(re)) |
|
||||
throw new Error("Unknown key type"); |
|
||||
return hex(key); |
|
||||
}; |
|
||||
|
|
||||
Wallet.prototype.expandKeys = function(keys) { |
|
||||
var res = []; |
|
||||
var us = this; |
|
||||
keys.forEach(function(key) { |
|
||||
var expKey = us.expandKey(key); |
|
||||
res.push(expKey); |
|
||||
}); |
|
||||
return res; |
|
||||
}; |
|
||||
|
|
||||
Wallet.prototype.addScript = function(script) { |
|
||||
var buf = script.getBuffer(); |
|
||||
var hash = util.sha256ripe160(buf); |
|
||||
var addr = new Address(this.network.addressScript, hash); |
|
||||
var addrStr = addr.as('base58'); |
|
||||
this.datastore.scripts[addrStr] = buf.toString('hex'); |
|
||||
this.dirty = true; |
|
||||
|
|
||||
return addrStr; |
|
||||
}; |
|
||||
|
|
||||
return Wallet; |
|
||||
}; |
}; |
||||
module.defineClass(ClassSpec); |
|
||||
|
Wallet.prototype.addSIN = function(sinObj) { |
||||
|
this.datastore.sin[sinObj.sin] = sinObj; |
||||
|
this.dirty = true; |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.findKeyHash = function(pubKeyHash) { |
||||
|
var pkhStr = pubKeyHash.toString(); |
||||
|
|
||||
|
for (var i = 0; i < this.datastore.keys.length; i++) { |
||||
|
var obj = this.datastore.keys[i]; |
||||
|
var addrStr = obj.addr; |
||||
|
var addr = new Address(addrStr); |
||||
|
if (addr.payload().toString() == pkhStr) |
||||
|
return obj; |
||||
|
} |
||||
|
|
||||
|
return undefined; |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.expandKey = function(key) { |
||||
|
var addr = new Address(key); |
||||
|
var isAddr = true; |
||||
|
|
||||
|
try { |
||||
|
addr.validate(); |
||||
|
var b = addr.payload(); |
||||
|
var obj = this.findKeyHash(b); |
||||
|
key = obj.pub; |
||||
|
} catch(e) { |
||||
|
// do nothing
|
||||
|
} |
||||
|
|
||||
|
var re = /^[a-fA-F0-9]+$/; |
||||
|
if (!key.match(re)) |
||||
|
throw new Error("Unknown key type"); |
||||
|
return hex(key); |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.expandKeys = function(keys) { |
||||
|
var res = []; |
||||
|
var us = this; |
||||
|
keys.forEach(function(key) { |
||||
|
var expKey = us.expandKey(key); |
||||
|
res.push(expKey); |
||||
|
}); |
||||
|
return res; |
||||
|
}; |
||||
|
|
||||
|
Wallet.prototype.addScript = function(script) { |
||||
|
var buf = script.getBuffer(); |
||||
|
var hash = util.sha256ripe160(buf); |
||||
|
var addr = new Address(this.network.addressScript, hash); |
||||
|
var addrStr = addr.as('base58'); |
||||
|
this.datastore.scripts[addrStr] = buf.toString('hex'); |
||||
|
this.dirty = true; |
||||
|
|
||||
|
return addrStr; |
||||
|
}; |
||||
|
|
||||
|
module.exports = require('soop')(Wallet); |
||||
|
|
||||
|
@ -1,55 +1,52 @@ |
|||||
require('classtool'); |
var imports = require('soop').imports(); |
||||
|
|
||||
function ClassSpec(b) { |
var coinUtil = require('./util/util'); |
||||
var coinUtil = require('./util/util'); |
var timeUtil = require('./util/time'); |
||||
var timeUtil = require('./util/time'); |
var KeyModule = require('./Key'); |
||||
var KeyModule = require('./Key'); |
var PrivateKey = require('./PrivateKey'); |
||||
var PrivateKey = require('./PrivateKey').class(); |
var Address = require('./Address'); |
||||
var Address = require('./Address').class(); |
|
||||
|
|
||||
function WalletKey(cfg) { |
function WalletKey(cfg) { |
||||
if (!cfg) cfg = {}; |
if (!cfg) cfg = {}; |
||||
if (!cfg.network) throw new Error('network parameter is required'); |
if (!cfg.network) throw new Error('network parameter is required'); |
||||
this.network = cfg.network; // required
|
this.network = cfg.network; // required
|
||||
this.created = cfg.created; |
this.created = cfg.created; |
||||
this.privKey = cfg.privKey; |
this.privKey = cfg.privKey; |
||||
}; |
}; |
||||
|
|
||||
WalletKey.prototype.generate = function() { |
|
||||
this.privKey = KeyModule.Key.generateSync(); |
|
||||
this.created = timeUtil.curtime(); |
|
||||
}; |
|
||||
|
|
||||
WalletKey.prototype.storeObj = function() { |
WalletKey.prototype.generate = function() { |
||||
var pubKey = this.privKey.public.toString('hex'); |
this.privKey = KeyModule.Key.generateSync(); |
||||
var pubKeyHash = coinUtil.sha256ripe160(this.privKey.public); |
this.created = timeUtil.curtime(); |
||||
var addr = new Address(this.network.addressPubkey, pubKeyHash); |
}; |
||||
var priv = new PrivateKey(this.network.keySecret, this.privKey.private, this.privKey.compressed); |
|
||||
var obj = { |
|
||||
created: this.created, |
|
||||
priv: priv.toString(), |
|
||||
pub: pubKey, |
|
||||
addr: addr.toString(), |
|
||||
}; |
|
||||
|
|
||||
return obj; |
WalletKey.prototype.storeObj = function() { |
||||
|
var pubKey = this.privKey.public.toString('hex'); |
||||
|
var pubKeyHash = coinUtil.sha256ripe160(this.privKey.public); |
||||
|
var addr = new Address(this.network.addressPubkey, pubKeyHash); |
||||
|
var priv = new PrivateKey(this.network.keySecret, this.privKey.private, this.privKey.compressed); |
||||
|
var obj = { |
||||
|
created: this.created, |
||||
|
priv: priv.toString(), |
||||
|
pub: pubKey, |
||||
|
addr: addr.toString(), |
||||
}; |
}; |
||||
|
|
||||
WalletKey.prototype.fromObj = function(obj) { |
return obj; |
||||
this.created = obj.created; |
}; |
||||
this.privKey = new KeyModule.Key(); |
|
||||
if (obj.priv.length==64) { |
|
||||
this.privKey.private = new Buffer(obj.priv,'hex'); |
|
||||
this.privKey.compressed = true; |
|
||||
} |
|
||||
else { |
|
||||
var priv = new PrivateKey(obj.priv); |
|
||||
this.privKey.private = new Buffer(priv.payload()); |
|
||||
this.privKey.compressed = priv.compressed(); |
|
||||
} |
|
||||
this.privKey.regenerateSync(); |
|
||||
}; |
|
||||
|
|
||||
return WalletKey; |
WalletKey.prototype.fromObj = function(obj) { |
||||
|
this.created = obj.created; |
||||
|
this.privKey = new KeyModule.Key(); |
||||
|
if (obj.priv.length==64) { |
||||
|
this.privKey.private = new Buffer(obj.priv,'hex'); |
||||
|
this.privKey.compressed = true; |
||||
|
} |
||||
|
else { |
||||
|
var priv = new PrivateKey(obj.priv); |
||||
|
this.privKey.private = new Buffer(priv.payload()); |
||||
|
this.privKey.compressed = priv.compressed(); |
||||
|
} |
||||
|
this.privKey.regenerateSync(); |
||||
}; |
}; |
||||
module.defineClass(ClassSpec); |
|
||||
|
module.exports = require('soop')(WalletKey); |
||||
|
@ -1,39 +1,37 @@ |
|||||
require('classtool'); |
var imports = require('soop').imports(); |
||||
|
var base58 = imports.base58 || require('base58-native').base58Check; |
||||
|
var superclass = imports.parent || require('./EncodedData'); |
||||
|
|
||||
function ClassSpec(b) { |
function VersionedData(version, payload) { |
||||
var superclass = b.superclass || require('./EncodedData').class(); |
if(typeof version != 'number') { |
||||
|
VersionedData.super(this, arguments); |
||||
function VersionedData(version, payload) { |
return; |
||||
if(typeof version != 'number') { |
|
||||
VersionedData.super(this, arguments); |
|
||||
return; |
|
||||
}; |
|
||||
this.data = new Buffer(payload.length + 1); |
|
||||
this.__proto__ = this.encodings['binary']; |
|
||||
this.version(version); |
|
||||
this.payload(payload); |
|
||||
}; |
}; |
||||
VersionedData.superclass = superclass; |
this.data = new Buffer(payload.length + 1); |
||||
superclass.applyEncodingsTo(VersionedData); |
this.__proto__ = this.encodings['binary']; |
||||
|
this.version(version); |
||||
|
this.payload(payload); |
||||
|
}; |
||||
|
|
||||
// get or set the version data (the first byte of the address)
|
VersionedData.parent = superclass || require('./Person'); |
||||
VersionedData.prototype.version = function(num) { |
superclass.applyEncodingsTo(VersionedData); |
||||
if(num || (num === 0)) { |
|
||||
this.doAsBinary(function() {this.data.writeUInt8(num, 0);}); |
|
||||
return num; |
|
||||
} |
|
||||
return this.as('binary').readUInt8(0); |
|
||||
}; |
|
||||
|
|
||||
// get or set the payload data (as a Buffer object)
|
// get or set the version data (the first byte of the address)
|
||||
VersionedData.prototype.payload = function(data) { |
VersionedData.prototype.version = function(num) { |
||||
if(data) { |
if(num || (num === 0)) { |
||||
this.doAsBinary(function() {data.copy(this.data,1);}); |
this.doAsBinary(function() {this.data.writeUInt8(num, 0);}); |
||||
return data; |
return num; |
||||
} |
} |
||||
return this.as('binary').slice(1); |
return this.as('binary').readUInt8(0); |
||||
}; |
}; |
||||
|
|
||||
return VersionedData; |
// get or set the payload data (as a Buffer object)
|
||||
|
VersionedData.prototype.payload = function(data) { |
||||
|
if(data) { |
||||
|
this.doAsBinary(function() {data.copy(this.data,1);}); |
||||
|
return data; |
||||
|
} |
||||
|
return this.as('binary').slice(1); |
||||
}; |
}; |
||||
module.defineClass(ClassSpec); |
|
||||
|
module.exports = require('soop')(VersionedData); |
||||
|
Loading…
Reference in new issue