Browse Source

all classes working with soop and test passing

patch-2
Matias Alejo Garcia 11 years ago
parent
commit
c0c325dabd
  1. 23
      Address.js
  2. 195
      Block.js
  3. 57
      Bloom.js
  4. 139
      Connection.js
  5. 25
      Opcode.js
  6. 35
      Peer.js
  7. 93
      PeerManager.js
  8. 37
      PrivateKey.js
  9. 42
      RpcClient.js
  10. 45
      SIN.js
  11. 30
      SINKey.js
  12. 298
      Script.js
  13. 180
      ScriptInterpreter.js
  14. 218
      Transaction.js
  15. 64
      Wallet.js
  16. 33
      WalletKey.js
  17. 2
      test/test.Address.js
  18. 2
      test/test.Block.js
  19. 2
      test/test.Bloom.js
  20. 2
      test/test.Connection.js
  21. 2
      test/test.EncodedData.js
  22. 2
      test/test.Opcode.js
  23. 2
      test/test.Peer.js
  24. 2
      test/test.PeerManager.js
  25. 2
      test/test.PrivateKey.js
  26. 2
      test/test.RpcClient.js
  27. 2
      test/test.SIN.js
  28. 2
      test/test.SINKey.js
  29. 4
      test/test.Script.js
  30. 2
      test/test.ScriptInterpreter.js
  31. 2
      test/test.Transaction.js
  32. 2
      test/test.VersionedData.js
  33. 2
      test/test.Wallet.js
  34. 2
      test/test.WalletKey.js
  35. 4
      test/test.basic.js
  36. 72
      util/BinaryParser.js
  37. 109
      util/EncodedData.js
  38. 32
      util/VersionedData.js

23
Address.js

@ -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();
function Address() {
Address.super(this, arguments); 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);

195
Block.js

@ -1,25 +1,24 @@
require('classtool'); var imports = require('soop').imports();
function spec(b) { var util = imports.util || require('./util/util');
var util = b.util || require('./util/util'); var Debug1 = imports.Debug1 || function() {};
var Debug1 = b.Debug1 || function() {}; var Script = imports.Script || require('./Script');
var Script = b.Script || require('./Script').class(); var Bignum = imports.Bignum || require('bignum');
var Bignum = b.Bignum || require('bignum'); var Binary = imports.Binary || require('binary');
var Binary = b.Binary || require('binary'); var Step = imports.Step || require('step');
var Step = b.Step || require('step'); var buffertools = imports.buffertools || require('buffertools');
var buffertools = b.buffertools || require('buffertools'); var Transaction = imports.Transaction || require('./Transaction');
var Transaction = b.Transaction || require('./Transaction').class(); var TransactionIn = Transaction.In;
var TransactionIn = Transaction.In; var TransactionOut = Transaction.Out;
var TransactionOut = Transaction.Out; var COINBASE_OP = Transaction.COINBASE_OP;
var COINBASE_OP = Transaction.COINBASE_OP; var VerificationError = imports.VerificationError || require('./util/error').VerificationError;
var VerificationError = b.VerificationError || require('./util/error').VerificationError; var BlockRules = {
var BlockRules = {
maxTimeOffset: 2 * 60 * 60, // How far block timestamps can be into the future maxTimeOffset: 2 * 60 * 60, // How far block timestamps can be into the future
largestHash: Bignum(2).pow(256) largestHash: Bignum(2).pow(256)
}; };
function Block(data) function Block(data)
{ {
if ("object" !== typeof data) { if ("object" !== typeof data) {
data = {}; data = {};
} }
@ -35,9 +34,9 @@ function spec(b) {
this.active = data.active || false; this.active = data.active || false;
this.chainWork = data.chainWork || util.EMPTY_BUFFER; this.chainWork = data.chainWork || util.EMPTY_BUFFER;
this.txs = data.txs || []; this.txs = data.txs || [];
} }
Block.prototype.getHeader = function getHeader() { Block.prototype.getHeader = function getHeader() {
var buf = new Buffer(80); var buf = new Buffer(80);
var ofs = 0; var ofs = 0;
buf.writeUInt32LE(this.version, ofs); ofs += 4; buf.writeUInt32LE(this.version, ofs); ofs += 4;
@ -47,9 +46,9 @@ function spec(b) {
buf.writeUInt32LE(this.bits, ofs); ofs += 4; buf.writeUInt32LE(this.bits, ofs); ofs += 4;
buf.writeUInt32LE(this.nonce, ofs); ofs += 4; buf.writeUInt32LE(this.nonce, ofs); ofs += 4;
return buf; return buf;
}; };
Block.prototype.parse = function parse(parser, headerOnly) { Block.prototype.parse = function parse(parser, headerOnly) {
this.version = parser.word32le(); this.version = parser.word32le();
this.prev_hash = parser.buffer(32); this.prev_hash = parser.buffer(32);
this.merkle_root = parser.buffer(32); this.merkle_root = parser.buffer(32);
@ -70,26 +69,26 @@ function spec(b) {
tx.parse(parser); tx.parse(parser);
this.txs.push(tx); this.txs.push(tx);
} }
}; };
Block.prototype.calcHash = function calcHash() { Block.prototype.calcHash = function calcHash() {
var header = this.getHeader(); var header = this.getHeader();
return util.twoSha256(header); return util.twoSha256(header);
}; };
Block.prototype.checkHash = function checkHash() { Block.prototype.checkHash = function checkHash() {
if (!this.hash || !this.hash.length) return false; if (!this.hash || !this.hash.length) return false;
return buffertools.compare(this.calcHash(), this.hash) == 0; return buffertools.compare(this.calcHash(), this.hash) == 0;
}; };
Block.prototype.getHash = function getHash() { Block.prototype.getHash = function getHash() {
if (!this.hash || !this.hash.length) this.hash = this.calcHash(); if (!this.hash || !this.hash.length) this.hash = this.calcHash();
return this.hash; return this.hash;
}; };
Block.prototype.checkProofOfWork = function checkProofOfWork() { Block.prototype.checkProofOfWork = function checkProofOfWork() {
var target = util.decodeDiffBits(this.bits); var target = util.decodeDiffBits(this.bits);
// TODO: Create a compare method in node-buffertools that uses the correct // TODO: Create a compare method in node-buffertools that uses the correct
@ -104,30 +103,30 @@ function spec(b) {
buffertools.reverse(this.hash); buffertools.reverse(this.hash);
return true; return true;
}; };
/** /**
* Returns the amount of work that went into this block. * Returns the amount of work that went into this block.
* *
* Work is defined as the average number of tries required to meet this * Work is defined as the average number of tries required to meet this
* block's difficulty target. For example a target that is greater than 5% * block's difficulty target. For example a target that is greater than 5%
* of all possible hashes would mean that 20 "work" is required to meet it. * of all possible hashes would mean that 20 "work" is required to meet it.
*/ */
Block.prototype.getWork = function getWork() { Block.prototype.getWork = function getWork() {
var target = util.decodeDiffBits(this.bits, true); var target = util.decodeDiffBits(this.bits, true);
return BlockRules.largestHash.div(target.add(1)); return BlockRules.largestHash.div(target.add(1));
}; };
Block.prototype.checkTimestamp = function checkTimestamp() { Block.prototype.checkTimestamp = function checkTimestamp() {
var currentTime = new Date().getTime() / 1000; var currentTime = new Date().getTime() / 1000;
if (this.timestamp > currentTime + BlockRules.maxTimeOffset) { if (this.timestamp > currentTime + BlockRules.maxTimeOffset) {
throw new VerificationError('Timestamp too far into the future'); throw new VerificationError('Timestamp too far into the future');
} }
return true; return true;
}; };
Block.prototype.checkTransactions = function checkTransactions(txs) { Block.prototype.checkTransactions = function checkTransactions(txs) {
if (!Array.isArray(txs) || txs.length <= 0) { if (!Array.isArray(txs) || txs.length <= 0) {
throw new VerificationError('No transactions'); throw new VerificationError('No transactions');
} }
@ -141,15 +140,15 @@ function spec(b) {
} }
return true; return true;
}; };
/** /**
* Build merkle tree. * Build merkle tree.
* *
* Ported from Java. Original code: BitcoinJ by Mike Hearn * Ported from Java. Original code: BitcoinJ by Mike Hearn
* Copyright (c) 2011 Google Inc. * Copyright (c) 2011 Google Inc.
*/ */
Block.prototype.getMerkleTree = function getMerkleTree(txs) { Block.prototype.getMerkleTree = function getMerkleTree(txs) {
// The merkle hash is based on a tree of hashes calculated from the transactions: // The merkle hash is based on a tree of hashes calculated from the transactions:
// //
// merkleHash // merkleHash
@ -188,14 +187,14 @@ function spec(b) {
} }
return tree; return tree;
}; };
Block.prototype.calcMerkleRoot = function calcMerkleRoot(txs) { Block.prototype.calcMerkleRoot = function calcMerkleRoot(txs) {
var tree = this.getMerkleTree(txs); var tree = this.getMerkleTree(txs);
return tree[tree.length - 1]; return tree[tree.length - 1];
}; };
Block.prototype.checkMerkleRoot = function checkMerkleRoot(txs) { Block.prototype.checkMerkleRoot = function checkMerkleRoot(txs) {
if (!this.merkle_root || !this.merkle_root.length) { if (!this.merkle_root || !this.merkle_root.length) {
throw new VerificationError('No merkle root'); throw new VerificationError('No merkle root');
} }
@ -205,9 +204,9 @@ function spec(b) {
} }
return true; return true;
}; };
Block.prototype.checkBlock = function checkBlock(txs) { Block.prototype.checkBlock = function checkBlock(txs) {
if (!this.checkHash()) { if (!this.checkHash()) {
throw new VerificationError("Block hash invalid"); throw new VerificationError("Block hash invalid");
} }
@ -221,31 +220,31 @@ function spec(b) {
} }
} }
return true; return true;
}; };
Block.getBlockValue = function getBlockValue(height) { Block.getBlockValue = function getBlockValue(height) {
var subsidy = Bignum(50).mul(util.COIN); var subsidy = Bignum(50).mul(util.COIN);
subsidy = subsidy.div(Bignum(2).pow(Math.floor(height / 210000))); subsidy = subsidy.div(Bignum(2).pow(Math.floor(height / 210000)));
return subsidy; return subsidy;
}; };
Block.prototype.getBlockValue = function getBlockValue() { Block.prototype.getBlockValue = function getBlockValue() {
return Block.getBlockValue(this.height); return Block.getBlockValue(this.height);
}; };
Block.prototype.toString = function toString() { Block.prototype.toString = function toString() {
return "<Block " + util.formatHashAlt(this.hash) + " height="+this.height+">"; return "<Block " + util.formatHashAlt(this.hash) + " height="+this.height+">";
}; };
/** /**
* Initializes some properties based on information from the parent block. * Initializes some properties based on information from the parent block.
*/ */
Block.prototype.attachTo = function attachTo(parent) { Block.prototype.attachTo = function attachTo(parent) {
this.height = parent.height + 1; this.height = parent.height + 1;
this.setChainWork(parent.getChainWork().add(this.getWork())); this.setChainWork(parent.getChainWork().add(this.getWork()));
}; };
Block.prototype.setChainWork = function setChainWork(chainWork) { Block.prototype.setChainWork = function setChainWork(chainWork) {
if (Buffer.isBuffer(chainWork)) { if (Buffer.isBuffer(chainWork)) {
// Nothing to do // Nothing to do
} else if ("function" === typeof chainWork.toBuffer) { // duck-typing bignum } else if ("function" === typeof chainWork.toBuffer) { // duck-typing bignum
@ -255,24 +254,24 @@ function spec(b) {
} }
this.chainWork = chainWork; this.chainWork = chainWork;
}; };
Block.prototype.getChainWork = function getChainWork() { Block.prototype.getChainWork = function getChainWork() {
return Bignum.fromBuffer(this.chainWork); return Bignum.fromBuffer(this.chainWork);
}; };
/** /**
* Compares the chainWork of two blocks. * Compares the chainWork of two blocks.
*/ */
Block.prototype.moreWorkThan = function moreWorkThan(otherBlock) { Block.prototype.moreWorkThan = function moreWorkThan(otherBlock) {
return this.getChainWork().cmp(otherBlock.getChainWork()) > 0; return this.getChainWork().cmp(otherBlock.getChainWork()) > 0;
}; };
/** /**
* Returns the difficulty target for the next block after this one. * Returns the difficulty target for the next block after this one.
*/ */
Block.prototype.getNextWork = Block.prototype.getNextWork =
function getNextWork(blockChain, nextBlock, callback) { function getNextWork(blockChain, nextBlock, callback) {
var self = this; var self = this;
var powLimit = blockChain.getMinDiff(); var powLimit = blockChain.getMinDiff();
@ -371,13 +370,13 @@ function spec(b) {
} }
); );
} }
}; };
var medianTimeSpan = 11; var medianTimeSpan = 11;
Block.prototype.getMedianTimePast = Block.prototype.getMedianTimePast =
function getMedianTimePast(blockChain, callback) function getMedianTimePast(blockChain, callback)
{ {
var self = this; var self = this;
Step( Step(
@ -407,11 +406,11 @@ function spec(b) {
}, },
callback callback
); );
}; };
Block.prototype.verifyChild = Block.prototype.verifyChild =
function verifyChild(blockChain, child, callback) function verifyChild(blockChain, child, callback)
{ {
var self = this; var self = this;
Step( Step(
@ -444,11 +443,11 @@ function spec(b) {
}, },
callback callback
); );
}; };
Block.prototype.createCoinbaseTx = Block.prototype.createCoinbaseTx =
function createCoinbaseTx(beneficiary) function createCoinbaseTx(beneficiary)
{ {
var tx = new Transaction(); var tx = new Transaction();
tx.ins.push(new TransactionIn({ tx.ins.push(new TransactionIn({
s: util.EMPTY_BUFFER, s: util.EMPTY_BUFFER,
@ -460,11 +459,11 @@ function spec(b) {
s: Script.createPubKeyOut(beneficiary).getBuffer() s: Script.createPubKeyOut(beneficiary).getBuffer()
})); }));
return tx; return tx;
}; };
Block.prototype.prepareNextBlock = Block.prototype.prepareNextBlock =
function prepareNextBlock(blockChain, beneficiary, time, callback) function prepareNextBlock(blockChain, beneficiary, time, callback)
{ {
var self = this; var self = this;
var newBlock = new Block(); var newBlock = new Block();
@ -512,11 +511,11 @@ function spec(b) {
}, },
callback callback
); );
}; };
Block.prototype.mineNextBlock = Block.prototype.mineNextBlock =
function mineNextBlock(blockChain, beneficiary, time, miner, callback) function mineNextBlock(blockChain, beneficiary, time, miner, callback)
{ {
this.prepareNextBlock(blockChain, beneficiary, time, function (err, data) { this.prepareNextBlock(blockChain, beneficiary, time, function (err, data) {
try { try {
if (err) throw err; if (err) throw err;
@ -539,20 +538,20 @@ function spec(b) {
callback(e); callback(e);
} }
}); });
}; };
Block.prototype.solve = function solve(miner, callback) { Block.prototype.solve = function solve(miner, callback) {
var header = this.getHeader(); var header = this.getHeader();
var target = util.decodeDiffBits(this.bits); var target = util.decodeDiffBits(this.bits);
miner.solve(header, target, callback); miner.solve(header, target, callback);
}; };
/** /**
* Returns an object with the same field names as jgarzik's getblock patch. * Returns an object with the same field names as jgarzik's getblock patch.
*/ */
Block.prototype.getStandardizedObject = Block.prototype.getStandardizedObject =
function getStandardizedObject(txs) function getStandardizedObject(txs)
{ {
var block = { var block = {
hash: util.formatHashFull(this.getHash()), hash: util.formatHashFull(this.getHash()),
version: this.version, version: this.version,
@ -587,8 +586,6 @@ function spec(b) {
block.size = this.size; block.size = this.size;
} }
return block; return block;
};
return Block;
}; };
module.defineClass(spec);
module.exports = require('soop')(Block);

57
Bloom.js

@ -1,31 +1,28 @@
require('classtool'); var MAX_BLOOM_FILTER_SIZE = 36000; // bytes
var MAX_HASH_FUNCS = 50;
var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455;
var LN2 = 0.6931471805599453094172321214581765680755001343602552;
var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80];
function ClassSpec(b) { function Bloom() {
var MAX_BLOOM_FILTER_SIZE = 36000; // bytes
var MAX_HASH_FUNCS = 50;
var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455;
var LN2 = 0.6931471805599453094172321214581765680755001343602552;
var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80];
function Bloom() {
this.data = ''; this.data = '';
this.hashFuncs = 0; this.hashFuncs = 0;
}; };
function ROTL32(x, r) { function ROTL32(x, r) {
return (x << r) | (x >> (32 - r)); return (x << r) | (x >> (32 - r));
}; };
function getBlockU32(blockIdx, data) { function getBlockU32(blockIdx, data) {
var idx = blockIdx * 4; var idx = blockIdx * 4;
var v = (data[idx + 0] << (0 * 8)) | var v = (data[idx + 0] << (0 * 8)) |
(data[idx + 1] << (1 * 8)) | (data[idx + 1] << (1 * 8)) |
(data[idx + 2] << (2 * 8)) | (data[idx + 2] << (2 * 8)) |
(data[idx + 3] << (3 * 8)); (data[idx + 3] << (3 * 8));
return v; return v;
}; };
Bloom.prototype.hash = function(hashNum, data) { Bloom.prototype.hash = function(hashNum, data) {
var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1)); var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1));
var c1 = 0xcc9e2d51; var c1 = 0xcc9e2d51;
var c2 = 0x1b873593; var c2 = 0x1b873593;
@ -68,16 +65,16 @@ function ClassSpec(b) {
h1 ^= h1 >> 16; h1 ^= h1 >> 16;
return h1 % (this.data.length * 8); return h1 % (this.data.length * 8);
}; };
Bloom.prototype.insert = function(data) { Bloom.prototype.insert = function(data) {
for (var i = 0; i < this.hashFuncs; i++) { for (var i = 0; i < this.hashFuncs; i++) {
var index = this.hash(i, data); var index = this.hash(i, data);
this.data[index >> 3] |= bit_mask[7 & index]; this.data[index >> 3] |= bit_mask[7 & index];
} }
}; };
Bloom.prototype.contains = function(data) { Bloom.prototype.contains = function(data) {
for (var i = 0; i < this.hashFuncs; i++) { for (var i = 0; i < this.hashFuncs; i++) {
var index = this.hash(i, data); var index = this.hash(i, data);
if (!(this.data[index >> 3] & bit_mask[7 & index])) if (!(this.data[index >> 3] & bit_mask[7 & index]))
@ -85,32 +82,30 @@ function ClassSpec(b) {
} }
return true; return true;
}; };
Bloom.prototype.sizeOk = function() { Bloom.prototype.sizeOk = function() {
return this.data.length <= MAX_BLOOM_FILTER_SIZE && return this.data.length <= MAX_BLOOM_FILTER_SIZE &&
this.hashFuncs <= MAX_HASH_FUNCS; this.hashFuncs <= MAX_HASH_FUNCS;
}; };
function toInt(v) { function toInt(v) {
return ~~v; return ~~v;
} }
function min(a, b) { function min(a, b) {
if (a < b) if (a < b)
return a; return a;
return b; return b;
} }
Bloom.prototype.init = function(elements, FPRate) { Bloom.prototype.init = function(elements, FPRate) {
var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)), var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)),
MAX_BLOOM_FILTER_SIZE * 8) / 8; MAX_BLOOM_FILTER_SIZE * 8) / 8;
this.data[filterSize] = 0; this.data[filterSize] = 0;
this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2), this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2),
MAX_HASH_FUNCS); MAX_HASH_FUNCS);
};
return Bloom;
}; };
module.defineClass(ClassSpec);
module.exports = require('soop')(Bloom);

139
Connection.js

@ -1,29 +1,28 @@
require('classtool'); var imports = require('soop').imports();
function spec(b) { var config = imports.config || require('./config');
var config = b.config || require('./config'); var log = imports.log || require('./util/log');
var log = b.log || require('./util/log'); var network = imports.network || require('./networks')[config.network];
var network = b.network || require('./networks')[config.network];
var MAX_RECEIVE_BUFFER = 10000000;
var MAX_RECEIVE_BUFFER = 10000000; var PROTOCOL_VERSION = 70000;
var PROTOCOL_VERSION = 70000;
var Binary = imports.Binary || require('binary');
var Binary = b.Binary || require('binary'); var Put = imports.Put || require('bufferput');
var Put = b.Put || require('bufferput'); var Buffers = imports.Buffers || require('buffers');
var Buffers = b.Buffers || require('buffers'); require('./Buffers.monkey').patch(Buffers);
require('./Buffers.monkey').patch(Buffers);
var noop = function() {}; var Block = require('./Block');
var Block = require('./Block').class(); var Transaction = require('./Transaction');
var Transaction = require('./Transaction').class(); var util = imports.util || require('./util/util');
var util = b.util || require('./util/util'); var Parser = imports.Parser || require('./util/BinaryParser');
var Parser = b.Parser || require('./util/BinaryParser').class(); var buffertools = imports.buffertools || require('buffertools');
var buffertools = b.buffertools || require('buffertools'); var doubleSha256 = imports.doubleSha256 || util.twoSha256;
var doubleSha256 = b.doubleSha256 || util.twoSha256; var nonce = util.generateNonce();
var nonce = util.generateNonce();
var BIP0031_VERSION = 60000;
var BIP0031_VERSION = 60000;
function Connection(socket, peer) {
function Connection(socket, peer) {
Connection.super(this, arguments); Connection.super(this, arguments);
this.socket = socket; this.socket = socket;
this.peer = peer; this.peer = peer;
@ -52,10 +51,10 @@ function spec(b) {
} }
this.setupHandlers(); this.setupHandlers();
} }
Connection.superclass = b.superclass || require('events').EventEmitter; Connection.parent = imports.parent || require('events').EventEmitter;
Connection.prototype.setupHandlers = function () { Connection.prototype.setupHandlers = function () {
this.socket.addListener('connect', this.handleConnect.bind(this)); this.socket.addListener('connect', this.handleConnect.bind(this));
this.socket.addListener('error', this.handleError.bind(this)); this.socket.addListener('error', this.handleError.bind(this));
this.socket.addListener('end', this.handleDisconnect.bind(this)); this.socket.addListener('end', this.handleDisconnect.bind(this));
@ -68,9 +67,9 @@ function spec(b) {
(data.length > dumpLen ? '...' : '')); (data.length > dumpLen ? '...' : ''));
}).bind(this)); }).bind(this));
this.socket.addListener('data', this.handleData.bind(this)); this.socket.addListener('data', this.handleData.bind(this));
}; };
Connection.prototype.handleConnect = function () { Connection.prototype.handleConnect = function () {
if (!this.inbound) { if (!this.inbound) {
this.sendVersion(); this.sendVersion();
} }
@ -79,9 +78,9 @@ function spec(b) {
socket: this.socket, socket: this.socket,
peer: this.peer peer: this.peer
}); });
}; };
Connection.prototype.handleError = function(err) { Connection.prototype.handleError = function(err) {
if (err.errno == 110 || err.errno == 'ETIMEDOUT') { if (err.errno == 110 || err.errno == 'ETIMEDOUT') {
log.info('connection timed out for '+this.peer); log.info('connection timed out for '+this.peer);
} else if (err.errno == 111 || err.errno == 'ECONNREFUSED') { } else if (err.errno == 111 || err.errno == 'ECONNREFUSED') {
@ -95,17 +94,17 @@ function spec(b) {
peer: this.peer, peer: this.peer,
err: err err: err
}); });
}; };
Connection.prototype.handleDisconnect = function () { Connection.prototype.handleDisconnect = function () {
this.emit('disconnect', { this.emit('disconnect', {
conn: this, conn: this,
socket: this.socket, socket: this.socket,
peer: this.peer peer: this.peer
}); });
}; };
Connection.prototype.handleMessage = function(message) { Connection.prototype.handleMessage = function(message) {
if (!message) { if (!message) {
// Parser was unable to make sense of the message, drop it // Parser was unable to make sense of the message, drop it
return; return;
@ -163,13 +162,13 @@ function spec(b) {
peer: this.peer, peer: this.peer,
message: message message: message
}); });
}; };
Connection.prototype.sendPong = function (nonce) { Connection.prototype.sendPong = function (nonce) {
this.sendMessage('pong', nonce); this.sendMessage('pong', nonce);
}; };
Connection.prototype.sendVersion = function () { Connection.prototype.sendVersion = function () {
var subversion = '/BitcoinX:0.1/'; var subversion = '/BitcoinX:0.1/';
var put = new Put(); var put = new Put();
@ -184,9 +183,9 @@ function spec(b) {
put.word32le(0); put.word32le(0);
this.sendMessage('version', put.buffer()); this.sendMessage('version', put.buffer());
}; };
Connection.prototype.sendGetBlocks = function (starts, stop, wantHeaders) { Connection.prototype.sendGetBlocks = function (starts, stop, wantHeaders) {
var put = new Put(); var put = new Put();
put.word32le(this.sendVer); put.word32le(this.sendVer);
@ -210,13 +209,13 @@ function spec(b) {
if (wantHeaders) if (wantHeaders)
command = 'getheaders'; command = 'getheaders';
this.sendMessage(command, put.buffer()); this.sendMessage(command, put.buffer());
}; };
Connection.prototype.sendGetHeaders = function(starts, stop) { Connection.prototype.sendGetHeaders = function(starts, stop) {
this.sendGetBlocks(starts, stop, true); this.sendGetBlocks(starts, stop, true);
}; };
Connection.prototype.sendGetData = function (invs) { Connection.prototype.sendGetData = function (invs) {
var put = new Put(); var put = new Put();
put.varint(invs.length); put.varint(invs.length);
for (var i = 0; i < invs.length; i++) { for (var i = 0; i < invs.length; i++) {
@ -224,14 +223,14 @@ function spec(b) {
put.put(invs[i].hash); put.put(invs[i].hash);
} }
this.sendMessage('getdata', put.buffer()); this.sendMessage('getdata', put.buffer());
}; };
Connection.prototype.sendGetAddr = function (invs) { Connection.prototype.sendGetAddr = function (invs) {
var put = new Put(); var put = new Put();
this.sendMessage('getaddr', put.buffer()); this.sendMessage('getaddr', put.buffer());
}; };
Connection.prototype.sendInv = function(data) { Connection.prototype.sendInv = function(data) {
if(!Array.isArray(data)) data = [data]; if(!Array.isArray(data)) data = [data];
var put = new Put(); var put = new Put();
put.varint(data.length); put.varint(data.length);
@ -246,9 +245,9 @@ function spec(b) {
put.put(value.getHash()); put.put(value.getHash());
}); });
this.sendMessage('inv', put.buffer()); this.sendMessage('inv', put.buffer());
}; };
Connection.prototype.sendHeaders = function (headers) { Connection.prototype.sendHeaders = function (headers) {
var put = new Put(); var put = new Put();
put.varint(headers.length); put.varint(headers.length);
headers.forEach(function (header) { headers.forEach(function (header) {
@ -258,13 +257,13 @@ function spec(b) {
put.word8(0); put.word8(0);
}); });
this.sendMessage('headers', put.buffer()); this.sendMessage('headers', put.buffer());
}; };
Connection.prototype.sendTx = function (tx) { Connection.prototype.sendTx = function (tx) {
this.sendMessage('tx', tx.serialize()); this.sendMessage('tx', tx.serialize());
}; };
Connection.prototype.sendBlock = function (block, txs) { Connection.prototype.sendBlock = function (block, txs) {
var put = new Put(); var put = new Put();
// Block header // Block header
@ -277,9 +276,9 @@ function spec(b) {
}); });
this.sendMessage('block', put.buffer()); this.sendMessage('block', put.buffer());
}; };
Connection.prototype.sendMessage = function (command, payload) { Connection.prototype.sendMessage = function (command, payload) {
try { try {
var magic = network.magic; var magic = network.magic;
var commandBuf = new Buffer(command, 'ascii'); var commandBuf = new Buffer(command, 'ascii');
@ -313,9 +312,9 @@ function spec(b) {
log.err("Error while sending message to peer "+this.peer+": "+ log.err("Error while sending message to peer "+this.peer+": "+
(err.stack ? err.stack : err.toString())); (err.stack ? err.stack : err.toString()));
} }
}; };
Connection.prototype.handleData = function (data) { Connection.prototype.handleData = function (data) {
this.buffers.push(data); this.buffers.push(data);
if (this.buffers.length > MAX_RECEIVE_BUFFER) { if (this.buffers.length > MAX_RECEIVE_BUFFER) {
@ -326,9 +325,9 @@ function spec(b) {
} }
this.processData(); this.processData();
}; };
Connection.prototype.processData = function () { Connection.prototype.processData = function () {
// If there are less than 20 bytes there can't be a message yet. // If there are less than 20 bytes there can't be a message yet.
if (this.buffers.length < 20) return; if (this.buffers.length < 20) return;
@ -402,9 +401,9 @@ function spec(b) {
this.buffers.skip(endPos); this.buffers.skip(endPos);
this.processData(); this.processData();
}; };
Connection.prototype.parseMessage = function (command, payload) { Connection.prototype.parseMessage = function (command, payload) {
var parser = new Parser(payload); var parser = new Parser(payload);
var data = { var data = {
@ -444,8 +443,8 @@ function spec(b) {
data.headers = []; data.headers = [];
for (i = 0; i < data.count; i++) { for (i = 0; i < data.count; i++) {
var header = new Block(); var header = new Block();
header.parse(parser); header.parse(parser);
data.headers.push(header); data.headers.push(header);
} }
break; break;
@ -540,8 +539,6 @@ function spec(b) {
} }
return data; return data;
};
return Connection;
}; };
module.defineClass(spec);
module.exports = require('soop')(Connection);

25
Opcode.js

@ -1,15 +1,14 @@
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 = { Opcode.map = {
// push value // push value
OP_FALSE : 0, OP_FALSE : 0,
OP_0 : 0, OP_0 : 0,
@ -146,16 +145,14 @@ function spec(b) {
OP_PUBKEYHASH : 253, OP_PUBKEYHASH : 253,
OP_PUBKEY : 254, OP_PUBKEY : 254,
OP_INVALIDOPCODE : 255 OP_INVALIDOPCODE : 255
}; };
Opcode.reverseMap = []; Opcode.reverseMap = [];
for (var k in Opcode.map) { for (var k in Opcode.map) {
if(Opcode.map.hasOwnProperty(k)) { if(Opcode.map.hasOwnProperty(k)) {
Opcode.reverseMap[Opcode.map[k]] = k.substr(3); Opcode.reverseMap[Opcode.map[k]] = k.substr(3);
} }
} }
return Opcode; module.exports = require('soop')(Opcode);
};
module.defineClass(spec);

35
Peer.js

@ -1,11 +1,10 @@
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(':');
@ -30,32 +29,30 @@ function spec(b) {
this.services = (services) ? services : null; this.services = (services) ? services : null;
this.lastSeen = 0; this.lastSeen = 0;
}; };
Peer.IPV6_IPV4_PADDING = new Buffer([0,0,0,0,0,0,0,0,0,0,255,255]); Peer.IPV6_IPV4_PADDING = new Buffer([0,0,0,0,0,0,0,0,0,0,255,255]);
Peer.prototype.createConnection = function () { Peer.prototype.createConnection = function () {
var c = Net.createConnection(this.port, this.host); var c = Net.createConnection(this.port, this.host);
return c; return c;
}; };
Peer.prototype.getHostAsBuffer = function () { Peer.prototype.getHostAsBuffer = function () {
return new Buffer(this.host.split('.')); return new Buffer(this.host.split('.'));
}; };
Peer.prototype.toString = function () { Peer.prototype.toString = function () {
return this.host + ":" + this.port; return this.host + ":" + this.port;
}; };
Peer.prototype.toBuffer = function () { Peer.prototype.toBuffer = function () {
var put = Binary.put(); var put = Binary.put();
put.word32le(this.lastSeen); put.word32le(this.lastSeen);
put.word64le(this.services); put.word64le(this.services);
put.put(this.getHostAsBuffer()); put.put(this.getHostAsBuffer());
put.word16be(this.port); put.word16be(this.port);
return put.buffer(); return put.buffer();
};
return Peer;
}; };
module.defineClass(spec);
module.exports = require('soop')(Peer);

93
PeerManager.js

@ -1,20 +1,19 @@
require('classtool');
var imports = require('soop').imports();
function spec(b) { var config = imports.config || require('./config');
var config = b.config || require('./config'); var log = imports.log || require('./util/log');
var log = b.log || require('./util/log'); var network = imports.network || require('./networks')[config.network];
var network = b.network || require('./networks')[config.network]; var Connection = imports.Connection ||
var Connection = b.Connection || require('./Connection').createClass( require('soop').load('./Connection', {config: config, network: network});
{config: config, network: network});
var Peer = b.Peer || require('./Peer').class(); var Peer = imports.Peer || require('./Peer');
var noop = function() {};
GetAdjustedTime = imports.GetAdjustedTime || function () {
GetAdjustedTime = b.GetAdjustedTime || function () {
// TODO: Implement actual adjustment // TODO: Implement actual adjustment
return Math.floor(new Date().getTime() / 1000); return Math.floor(new Date().getTime() / 1000);
}; };
function PeerManager() { function PeerManager() {
this.active = false; this.active = false;
this.timer = null; this.timer = null;
@ -27,19 +26,19 @@ function spec(b) {
this.interval = 5000; this.interval = 5000;
this.minConnections = 8; this.minConnections = 8;
this.minKnownPeers = 10; this.minKnownPeers = 10;
}; };
PeerManager.superclass = b.superclass || require('events').EventEmitter;
PeerManager.Connection = Connection; PeerManager.parent = imports.parent || require('events').EventEmitter;
PeerManager.Connection = Connection;
PeerManager.prototype.start = function() { PeerManager.prototype.start = function() {
this.active = true; this.active = true;
if(!this.timer) { if(!this.timer) {
this.timer = setInterval(this.checkStatus.bind(this), this.interval); this.timer = setInterval(this.checkStatus.bind(this), this.interval);
} }
}; };
PeerManager.prototype.stop = function() { PeerManager.prototype.stop = function() {
this.active = false; this.active = false;
if(this.timer) { if(this.timer) {
clearInterval(this.timer); clearInterval(this.timer);
@ -48,9 +47,9 @@ function spec(b) {
for(var i=0; i<this.connections.length; i++) { for(var i=0; i<this.connections.length; i++) {
this.connections[i].socket.end(); this.connections[i].socket.end();
}; };
}; };
PeerManager.prototype.addPeer = function(peer, port) { PeerManager.prototype.addPeer = function(peer, port) {
if(peer instanceof Peer) { if(peer instanceof Peer) {
this.peers.push(peer); this.peers.push(peer);
} else if ("string" == typeof peer) { } else if ("string" == typeof peer) {
@ -60,9 +59,9 @@ function spec(b) {
{val: peer}); {val: peer});
throw 'Node.addPeer(): Invalid value provided for peer.'; throw 'Node.addPeer(): Invalid value provided for peer.';
} }
}; };
PeerManager.prototype.checkStatus = function checkStatus() { PeerManager.prototype.checkStatus = function checkStatus() {
// Make sure we are connected to all forcePeers // Make sure we are connected to all forcePeers
if(this.peers.length) { if(this.peers.length) {
var peerIndex = {}; var peerIndex = {};
@ -82,9 +81,9 @@ function spec(b) {
this.connectTo(peerIndex[i]); this.connectTo(peerIndex[i]);
}.bind(this)); }.bind(this));
} }
}; };
PeerManager.prototype.connectTo = function(peer) { PeerManager.prototype.connectTo = function(peer) {
log.info('connecting to '+peer); log.info('connecting to '+peer);
try { try {
return this.addConnection(peer.createConnection(), peer); return this.addConnection(peer.createConnection(), peer);
@ -92,9 +91,9 @@ function spec(b) {
log.err('creating connection',e); log.err('creating connection',e);
return null; return null;
} }
}; };
PeerManager.prototype.addConnection = function(socketConn, peer) { PeerManager.prototype.addConnection = function(socketConn, peer) {
var conn = new Connection(socketConn, peer); var conn = new Connection(socketConn, peer);
this.connections.push(conn); this.connections.push(conn);
this.emit('connection', conn); this.emit('connection', conn);
@ -107,9 +106,9 @@ function spec(b) {
conn.addListener('disconnect', this.handleDisconnect.bind(this)); conn.addListener('disconnect', this.handleDisconnect.bind(this));
return conn; return conn;
}; };
PeerManager.prototype.handleVersion = function(e) { PeerManager.prototype.handleVersion = function(e) {
if (!e.conn.inbound) { if (!e.conn.inbound) {
// TODO: Advertise our address (if listening) // TODO: Advertise our address (if listening)
} }
@ -119,9 +118,9 @@ function spec(b) {
e.conn.sendGetAddr(); e.conn.sendGetAddr();
e.conn.getaddr = true; e.conn.getaddr = true;
} }
}; };
PeerManager.prototype.handleReady = function (e) { PeerManager.prototype.handleReady = function (e) {
log.info('connected to '+e.conn.peer.host+':'+e.conn.peer.port); log.info('connected to '+e.conn.peer.host+':'+e.conn.peer.port);
this.emit('connect', { this.emit('connect', {
pm: this, pm: this,
@ -134,9 +133,9 @@ function spec(b) {
this.emit('netConnected'); this.emit('netConnected');
this.isConnected = true; 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();
@ -160,18 +159,18 @@ function spec(b) {
if (e.message.addrs.length < 1000 ) { if (e.message.addrs.length < 1000 ) {
e.conn.getaddr = false; 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);
@ -180,9 +179,9 @@ function spec(b) {
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;
}); });
@ -204,12 +203,10 @@ function spec(b) {
} else { } else {
return null; return null;
} }
}; };
PeerManager.prototype.getActiveConnections = function () { PeerManager.prototype.getActiveConnections = function () {
return this.connections.slice(0); return this.connections.slice(0);
};
return PeerManager;
}; };
module.defineClass(spec);
module.exports = require('soop')(PeerManager);

37
PrivateKey.js

@ -1,29 +1,28 @@
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;
@ -33,10 +32,10 @@ function ClassSpec(b) {
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; var len=1+32+1;
@ -60,8 +59,6 @@ function ClassSpec(b) {
else else
throw new Error('invalid private key'); throw new Error('invalid private key');
} }
};
return PrivateKey;
}; };
module.defineClass(ClassSpec);
module.exports = require('soop')(PrivateKey);

42
RpcClient.js

@ -1,14 +1,13 @@
// 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');
var https = b.https || require('https');
var log = b.log || require('./util/log');
function RpcClient(opts) {
opts = opts || {}; opts = opts || {};
this.host = opts.host || '127.0.0.1'; this.host = opts.host || '127.0.0.1';
this.port = opts.port || 8332; this.port = opts.port || 8332;
@ -17,16 +16,16 @@ function ClassSpec(b) {
this.protocol = (opts.protocol == 'http') ? http : https; this.protocol = (opts.protocol == 'http') ? http : https;
this.batchedCalls = null; this.batchedCalls = null;
this.disableAgent = opts.disableAgent || false; this.disableAgent = opts.disableAgent || false;
} }
RpcClient.prototype.batch = function(batchCallback, resultCallback) { RpcClient.prototype.batch = function(batchCallback, resultCallback) {
this.batchedCalls = []; this.batchedCalls = [];
batchCallback(); batchCallback();
rpc.call(this, this.batchedCalls, resultCallback); rpc.call(this, this.batchedCalls, resultCallback);
this.batchedCalls = null; this.batchedCalls = null;
} }
var callspec = { var callspec = {
addMultiSigAddress: '', addMultiSigAddress: '',
addNode: '', addNode: '',
backupWallet: '', backupWallet: '',
@ -93,13 +92,13 @@ function ClassSpec(b) {
walletLock: '', walletLock: '',
walletPassPhrase: 'string int', walletPassPhrase: 'string int',
walletPassphraseChange: '', 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;
@ -137,9 +136,9 @@ function ClassSpec(b) {
constructor.prototype[methodName] = constructor.prototype[k]; 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);
@ -201,10 +200,9 @@ function ClassSpec(b) {
req.setHeader('Authorization', 'Basic ' + auth); req.setHeader('Authorization', 'Basic ' + auth);
req.write(request); req.write(request);
req.end(); req.end();
};
generateRPCMethods(RpcClient, callspec, rpc);
return RpcClient;
}; };
module.defineClass(ClassSpec);
generateRPCMethods(RpcClient, callspec, rpc);
module.exports = require('soop')(RpcClient);

45
SIN.js

@ -1,10 +1,7 @@
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();
function SIN(type, payload) {
if (typeof type != 'number') { if (typeof type != 'number') {
SIN.super(this, arguments); SIN.super(this, arguments);
return; return;
@ -14,47 +11,45 @@ function ClassSpec(b) {
this.prefix(0x0F); // SIN magic number, in numberspace this.prefix(0x0F); // SIN magic number, in numberspace
this.type(type); this.type(type);
this.payload(payload); this.payload(payload);
}; };
SIN.superclass = superclass; SIN.parent = parent;
superclass.applyEncodingsTo(SIN); parent.applyEncodingsTo(SIN);
SIN.SIN_PERSIST_MAINNET = 0x01; // associated with sacrifice TX SIN.SIN_PERSIST_MAINNET = 0x01; // associated with sacrifice TX
SIN.SIN_PERSIST_TESTNET = 0x11; // associated with sacrifice TX SIN.SIN_PERSIST_TESTNET = 0x11; // associated with sacrifice TX
SIN.SIN_EPHEM = 0x02; // generate off-net at any time SIN.SIN_EPHEM = 0x02; // generate off-net at any time
// get or set the prefix data (the first byte of the address) // get or set the prefix data (the first byte of the address)
SIN.prototype.prefix = function(num) { SIN.prototype.prefix = function(num) {
if(num || (num === 0)) { if(num || (num === 0)) {
this.doAsBinary(function() {this.data.writeUInt8(num, 0);}); this.doAsBinary(function() {this.data.writeUInt8(num, 0);});
return num; return num;
} }
return this.as('binary').readUInt8(0); 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);

30
SINKey.js

@ -1,29 +1,26 @@
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');
var timeUtil = require('./util/time');
var KeyModule = require('./Key');
var SIN = require('./SIN').class();
function SINKey(cfg) {
if (typeof cfg != 'object') if (typeof cfg != 'object')
cfg = {}; cfg = {};
this.created = cfg.created; this.created = cfg.created;
this.privKey = cfg.privKey; this.privKey = cfg.privKey;
}; };
SINKey.prototype.generate = function() { SINKey.prototype.generate = function() {
this.privKey = KeyModule.Key.generateSync(); this.privKey = KeyModule.Key.generateSync();
this.created = timeUtil.curtime(); this.created = timeUtil.curtime();
}; };
SINKey.prototype.pubkeyHash = function() { SINKey.prototype.pubkeyHash = function() {
return coinUtil.sha256ripe160(this.privKey.public); return coinUtil.sha256ripe160(this.privKey.public);
}; };
SINKey.prototype.storeObj = function() { SINKey.prototype.storeObj = function() {
var pubKey = this.privKey.public.toString('hex'); var pubKey = this.privKey.public.toString('hex');
var pubKeyHash = this.pubkeyHash(); var pubKeyHash = this.pubkeyHash();
var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash); var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash);
@ -35,9 +32,6 @@ function ClassSpec(b) {
}; };
return obj; return obj;
};
return SINKey;
}; };
module.defineClass(ClassSpec);
module.exports = require('soop')(SINKey);

298
Script.js

@ -1,36 +1,33 @@
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 util = b.util || require('./util/util');
var Parser = b.Parser || require('./util/BinaryParser').class(); var TX_UNKNOWN = 0;
var Put = b.Put || require('bufferput'); var TX_PUBKEY = 1;
var TX_PUBKEYHASH = 2;
var TX_UNKNOWN = 0; var TX_MULTISIG = 3;
var TX_PUBKEY = 1; var TX_SCRIPTHASH = 4;
var TX_PUBKEYHASH = 2;
var TX_MULTISIG = 3; var TX_TYPES = [
var TX_SCRIPTHASH = 4;
var TX_TYPES = [
'unknown', 'unknown',
'pubkey', 'pubkey',
'pubkeyhash', 'pubkeyhash',
'multisig', 'multisig',
'scripthash' 'scripthash'
]; ];
function Script(buffer) { function Script(buffer) {
if(buffer) { if(buffer) {
this.buffer = buffer; this.buffer = buffer;
} else { } else {
@ -38,16 +35,16 @@ function spec(b) {
} }
this.chunks = []; this.chunks = [];
this.parse(); this.parse();
}; };
this.class = Script; this.class = Script;
Script.TX_UNKNOWN=TX_UNKNOWN; Script.TX_UNKNOWN=TX_UNKNOWN;
Script.TX_PUBKEY=TX_PUBKEY; Script.TX_PUBKEY=TX_PUBKEY;
Script.TX_PUBKEYHASH=TX_PUBKEYHASH; Script.TX_PUBKEYHASH=TX_PUBKEYHASH;
Script.TX_MULTISIG=TX_MULTISIG; Script.TX_MULTISIG=TX_MULTISIG;
Script.TX_SCRIPTHASH=TX_SCRIPTHASH; Script.TX_SCRIPTHASH=TX_SCRIPTHASH;
Script.prototype.parse = function () { Script.prototype.parse = function () {
this.chunks = []; this.chunks = [];
var parser = new Parser(this.buffer); var parser = new Parser(this.buffer);
@ -71,35 +68,35 @@ function spec(b) {
this.chunks.push(opcode); 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 () Script.prototype.isPubkey = function ()
{ {
return (this.chunks.length == 2 && return (this.chunks.length == 2 &&
Buffer.isBuffer(this.chunks[0]) && Buffer.isBuffer(this.chunks[0]) &&
this.chunks[1] == OP_CHECKSIG); this.chunks[1] == OP_CHECKSIG);
}; };
Script.prototype.isPubkeyHash = function () Script.prototype.isPubkeyHash = function ()
{ {
return (this.chunks.length == 5 && return (this.chunks.length == 5 &&
this.chunks[0] == OP_DUP && this.chunks[0] == OP_DUP &&
this.chunks[1] == OP_HASH160 && this.chunks[1] == OP_HASH160 &&
@ -107,24 +104,24 @@ function spec(b) {
this.chunks[2].length == 20 && this.chunks[2].length == 20 &&
this.chunks[3] == OP_EQUALVERIFY && this.chunks[3] == OP_EQUALVERIFY &&
this.chunks[4] == OP_CHECKSIG); this.chunks[4] == OP_CHECKSIG);
}; };
function isSmallIntOp(opcode) function isSmallIntOp(opcode)
{ {
return ((opcode == OP_0) || return ((opcode == OP_0) ||
((opcode >= OP_1) && (opcode <= OP_16))); ((opcode >= OP_1) && (opcode <= OP_16)));
}; };
Script.prototype.isMultiSig = function () Script.prototype.isMultiSig = function ()
{ {
return (this.chunks.length > 3 && return (this.chunks.length > 3 &&
isSmallIntOp(this.chunks[0]) && isSmallIntOp(this.chunks[0]) &&
isSmallIntOp(this.chunks[this.chunks.length-2]) && isSmallIntOp(this.chunks[this.chunks.length-2]) &&
this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG); this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG);
}; };
Script.prototype.finishedMultiSig = function() Script.prototype.finishedMultiSig = function()
{ {
var nsigs = 0; var nsigs = 0;
for (var i = 0; i < this.chunks.length-1; i++) for (var i = 0; i < this.chunks.length-1; i++)
if (this.chunks[i] !== 0) if (this.chunks[i] !== 0)
@ -138,10 +135,10 @@ function spec(b) {
return true; return true;
else else
return false; return false;
} }
Script.prototype.removePlaceHolders = function() Script.prototype.removePlaceHolders = function()
{ {
var chunks = []; var chunks = [];
for (var i in this.chunks) for (var i in this.chunks)
{ {
@ -154,10 +151,10 @@ function spec(b) {
this.chunks = chunks; this.chunks = chunks;
this.updateBuffer(); this.updateBuffer();
return this; return this;
} }
Script.prototype.prependOp0 = function() Script.prototype.prependOp0 = function()
{ {
var chunks = [0]; var chunks = [0];
for (i in this.chunks) { for (i in this.chunks) {
if (this.chunks.hasOwnProperty(i)) { if (this.chunks.hasOwnProperty(i)) {
@ -167,74 +164,74 @@ function spec(b) {
this.chunks = chunks; this.chunks = chunks;
this.updateBuffer(); this.updateBuffer();
return this; return this;
} }
// is this a script form we know? // is this a script form we know?
Script.prototype.classify = function () Script.prototype.classify = function ()
{ {
if (this.isPubkeyHash()) if (this.isPubkeyHash())
return TX_PUBKEYHASH; return TX_PUBKEYHASH;
if (this.isP2SH()) if (this.isP2SH())
return TX_SCRIPTHASH; return TX_SCRIPTHASH;
if (this.isMultiSig()) if (this.isMultiSig())
return TX_MULTISIG; return TX_MULTISIG;
if (this.isPubkey()) if (this.isPubkey())
return TX_PUBKEY; return TX_PUBKEY;
return TX_UNKNOWN; return TX_UNKNOWN;
}; };
// extract useful data items from known scripts // extract useful data items from known scripts
Script.prototype.capture = function () Script.prototype.capture = function ()
{ {
var txType = this.classify(); var txType = this.classify();
var res = []; var res = [];
switch (txType) { switch (txType) {
case TX_PUBKEY: case TX_PUBKEY:
res.push(this.chunks[0]); res.push(this.chunks[0]);
break; break;
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
res.push(this.chunks[2]); res.push(this.chunks[2]);
break; break;
case TX_MULTISIG: case TX_MULTISIG:
for (var i = 1; i < (this.chunks.length - 2); i++) for (var i = 1; i < (this.chunks.length - 2); i++)
res.push(this.chunks[i]); res.push(this.chunks[i]);
break; break;
case TX_SCRIPTHASH: case TX_SCRIPTHASH:
res.push(this.chunks[1]); res.push(this.chunks[1]);
break; break;
case TX_UNKNOWN: case TX_UNKNOWN:
default: default:
// do nothing // do nothing
break; break;
} }
return res; return res;
}; };
// return first extracted data item from script // return first extracted data item from script
Script.prototype.captureOne = function () Script.prototype.captureOne = function ()
{ {
var arr = this.capture(); var arr = this.capture();
return arr[0]; return arr[0];
}; };
Script.prototype.getOutType = function () Script.prototype.getOutType = function ()
{ {
var txType = this.classify(); var txType = this.classify();
switch (txType) { switch (txType) {
case TX_PUBKEY: return 'Pubkey'; case TX_PUBKEY: return 'Pubkey';
case TX_PUBKEYHASH: return 'Address'; case TX_PUBKEYHASH: return 'Address';
default: return 'Strange'; default: return 'Strange';
} }
}; };
Script.prototype.getRawOutType = function() { Script.prototype.getRawOutType = function() {
return TX_TYPES[this.classify()]; return TX_TYPES[this.classify()];
}; };
Script.prototype.simpleOutHash = function () Script.prototype.simpleOutHash = function ()
{ {
switch (this.getOutType()) { switch (this.getOutType()) {
case 'Address': case 'Address':
return this.chunks[2]; return this.chunks[2];
@ -245,10 +242,10 @@ function spec(b) {
log.debug("Strange script was: " + this.toString()); log.debug("Strange script was: " + this.toString());
return null; return null;
} }
}; };
Script.prototype.getInType = function () Script.prototype.getInType = function ()
{ {
if (this.chunks.length == 1) { if (this.chunks.length == 1) {
// Direct IP to IP transactions only have the public key in their scriptSig. // Direct IP to IP transactions only have the public key in their scriptSig.
return 'Pubkey'; return 'Pubkey';
@ -259,10 +256,10 @@ function spec(b) {
} else { } else {
return 'Strange'; return 'Strange';
} }
}; };
Script.prototype.simpleInPubKey = function () Script.prototype.simpleInPubKey = function ()
{ {
switch (this.getInType()) { switch (this.getInType()) {
case 'Address': case 'Address':
return this.chunks[1]; return this.chunks[1];
@ -273,15 +270,15 @@ function spec(b) {
log.debug("Strange script was: " + this.toString()); log.debug("Strange script was: " + this.toString());
return null; return null;
} }
}; };
Script.prototype.getBuffer = function () Script.prototype.getBuffer = function ()
{ {
return this.buffer; return this.buffer;
}; };
Script.prototype.getStringContent = function (truncate, maxEl) Script.prototype.getStringContent = function (truncate, maxEl)
{ {
if (truncate === null) { if (truncate === null) {
truncate = true; truncate = true;
} }
@ -310,19 +307,19 @@ function spec(b) {
} }
} }
return script; return script;
}; };
Script.prototype.toString = function (truncate, maxEl) Script.prototype.toString = function (truncate, maxEl)
{ {
var script = "<Script "; var script = "<Script ";
script += this.getStringContent(truncate, maxEl); script += this.getStringContent(truncate, maxEl);
script += ">"; script += ">";
return script; return script;
}; };
Script.prototype.writeOp = function (opcode) Script.prototype.writeOp = function (opcode)
{ {
var buf = Buffer(this.buffer.length + 1); var buf = Buffer(this.buffer.length + 1);
this.buffer.copy(buf); this.buffer.copy(buf);
buf.writeUInt8(opcode, this.buffer.length); buf.writeUInt8(opcode, this.buffer.length);
@ -330,10 +327,10 @@ function spec(b) {
this.buffer = buf; this.buffer = buf;
this.chunks.push(opcode); this.chunks.push(opcode);
}; };
Script.prototype.writeN = function (n) Script.prototype.writeN = function (n)
{ {
if (n < 0 || n > 16) if (n < 0 || n > 16)
throw new Error("writeN: out of range value " + n); throw new Error("writeN: out of range value " + n);
@ -341,10 +338,10 @@ function spec(b) {
this.writeOp(OP_0); this.writeOp(OP_0);
else else
this.writeOp(OP_1 + n - 1); this.writeOp(OP_1 + n - 1);
}; };
function prefixSize(data_length) function prefixSize(data_length)
{ {
if (data_length < OP_PUSHDATA1) { if (data_length < OP_PUSHDATA1) {
return 1; return 1;
} else if (data_length <= 0xff) { } else if (data_length <= 0xff) {
@ -354,9 +351,9 @@ function spec(b) {
} else { } else {
return 1 + 4; return 1 + 4;
} }
}; };
function encodeLen(data_length) { function encodeLen(data_length) {
var buf = undefined; var buf = undefined;
if (data_length < OP_PUSHDATA1) { if (data_length < OP_PUSHDATA1) {
buf = new Buffer(1); buf = new Buffer(1);
@ -382,22 +379,22 @@ function spec(b) {
} }
return buf; return buf;
}; };
Script.prototype.writeBytes = function (data) Script.prototype.writeBytes = function (data)
{ {
var newSize = this.buffer.length + prefixSize(data.length) + data.length; var newSize = this.buffer.length + prefixSize(data.length) + data.length;
this.buffer = Buffer.concat([this.buffer, encodeLen(data.length), data]); this.buffer = Buffer.concat([this.buffer, encodeLen(data.length), data]);
this.chunks.push(data); this.chunks.push(data);
}; };
Script.prototype.updateBuffer = function () Script.prototype.updateBuffer = function ()
{ {
this.buffer = Script.chunksToBuffer(this.chunks); this.buffer = Script.chunksToBuffer(this.chunks);
}; };
Script.prototype.findAndDelete = function (chunk) Script.prototype.findAndDelete = function (chunk)
{ {
var dirty = false; var dirty = false;
if (Buffer.isBuffer(chunk)) { if (Buffer.isBuffer(chunk)) {
for (var i = 0, l = this.chunks.length; i < l; i++) { for (var i = 0, l = this.chunks.length; i < l; i++) {
@ -420,25 +417,25 @@ function spec(b) {
if (dirty) { if (dirty) {
this.updateBuffer(); this.updateBuffer();
} }
}; };
/** /**
* Creates a simple OP_CHECKSIG with pubkey output script. * Creates a simple OP_CHECKSIG with pubkey output script.
* *
* These are used for coinbase transactions and at some point were used for * These are used for coinbase transactions and at some point were used for
* IP-based transactions as well. * IP-based transactions as well.
*/ */
Script.createPubKeyOut = function (pubkey) { Script.createPubKeyOut = function (pubkey) {
var script = new Script(); var script = new Script();
script.writeBytes(pubkey); script.writeBytes(pubkey);
script.writeOp(OP_CHECKSIG); script.writeOp(OP_CHECKSIG);
return script; return script;
}; };
/** /**
* Creates a standard txout script. * Creates a standard txout script.
*/ */
Script.createPubKeyHashOut = function (pubKeyHash) { Script.createPubKeyHashOut = function (pubKeyHash) {
var script = new Script(); var script = new Script();
script.writeOp(OP_DUP); script.writeOp(OP_DUP);
script.writeOp(OP_HASH160); script.writeOp(OP_HASH160);
@ -446,9 +443,9 @@ function spec(b) {
script.writeOp(OP_EQUALVERIFY); script.writeOp(OP_EQUALVERIFY);
script.writeOp(OP_CHECKSIG); script.writeOp(OP_CHECKSIG);
return script; return script;
}; };
Script.createMultisig = function(n_required, keys) { Script.createMultisig = function(n_required, keys) {
var script = new Script(); var script = new Script();
script.writeN(n_required); script.writeN(n_required);
keys.forEach(function(key) { keys.forEach(function(key) {
@ -457,17 +454,17 @@ function spec(b) {
script.writeN(keys.length); script.writeN(keys.length);
script.writeOp(OP_CHECKMULTISIG); script.writeOp(OP_CHECKMULTISIG);
return script; return script;
}; };
Script.createP2SH = function(scriptHash) { Script.createP2SH = function(scriptHash) {
var script = new Script(); var script = new Script();
script.writeOp(OP_HASH160); script.writeOp(OP_HASH160);
script.writeBytes(scriptHash); script.writeBytes(scriptHash);
script.writeOp(OP_EQUAL); script.writeOp(OP_EQUAL);
return script; return script;
}; };
Script.fromTestData = function (testData) { Script.fromTestData = function (testData) {
testData = testData.map(function (chunk) { testData = testData.map(function (chunk) {
if ("string" === typeof chunk) { if ("string" === typeof chunk) {
return new Buffer(chunk, 'hex'); return new Buffer(chunk, 'hex');
@ -480,16 +477,16 @@ function spec(b) {
script.chunks = testData; script.chunks = testData;
script.updateBuffer(); script.updateBuffer();
return script; return script;
}; };
Script.fromChunks = function (chunks) { Script.fromChunks = function (chunks) {
var script = new Script(); var script = new Script();
script.chunks = chunks; script.chunks = chunks;
script.updateBuffer(); script.updateBuffer();
return script; return script;
}; };
Script.chunksToBuffer = function (chunks) { Script.chunksToBuffer = function (chunks) {
var buf = new Put(); var buf = new Put();
for (var i = 0, l = chunks.length; i < l; i++) { for (var i = 0, l = chunks.length; i < l; i++) {
@ -515,9 +512,6 @@ function spec(b) {
} }
} }
return buf.buffer(); return buf.buffer();
};
return Script;
}; };
module.defineClass(spec);
module.exports = require('soop')(Script);

180
ScriptInterpreter.js

@ -1,29 +1,27 @@
require('classtool'); var imports = require('soop').imports();
function spec(b) { var assert = require('assert');
var assert = require('assert'); var config = imports.config || require('./config');
var config = b.config || require('./config'); var log = imports.log || require('./util/log');
var log = b.log || require('./util/log'); var Opcode = imports.Opcode || require('./Opcode');
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) {
// Make opcodes available as pseudo-constants
for (var i in Opcode.map) {
eval(i + " = " + Opcode.map[i] + ";"); eval(i + " = " + Opcode.map[i] + ";");
} }
var bignum = b.bignum || require('bignum'); var bignum = imports.bignum || require('bignum');
var Util = b.Util || require('./util/util'); var Util = imports.Util || require('./util/util');
var Script = require('./Script').class(); var Script = require('./Script');
function ScriptInterpreter() { function ScriptInterpreter() {
this.stack = []; this.stack = [];
this.disableUnsafeOpcodes = true; this.disableUnsafeOpcodes = true;
}; };
ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, callback) ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType, callback)
{ {
if ("function" !== typeof callback) { if ("function" !== typeof callback) {
throw new Error("ScriptInterpreter.eval() requires a callback"); throw new Error("ScriptInterpreter.eval() requires a callback");
} }
@ -726,11 +724,11 @@ function spec(b) {
cb(e); cb(e);
} }
} }
}; };
ScriptInterpreter.prototype.evalTwo = ScriptInterpreter.prototype.evalTwo =
function evalTwo(scriptSig, scriptPubkey, tx, n, hashType, callback) function evalTwo(scriptSig, scriptPubkey, tx, n, hashType, callback)
{ {
var self = this; var self = this;
self.eval(scriptSig, tx, n, hashType, function (e) { self.eval(scriptSig, tx, n, hashType, function (e) {
@ -741,15 +739,15 @@ function spec(b) {
self.eval(scriptPubkey, tx, n, hashType, callback); self.eval(scriptPubkey, tx, n, hashType, callback);
}); });
}; };
/** /**
* Get the top element of the stack. * Get the top element of the stack.
* *
* Using the offset parameter this function can also access lower elements * Using the offset parameter this function can also access lower elements
* from the stack. * from the stack.
*/ */
ScriptInterpreter.prototype.stackTop = function stackTop(offset) { ScriptInterpreter.prototype.stackTop = function stackTop(offset) {
offset = +offset || 1; offset = +offset || 1;
if (offset < 1) offset = 1; if (offset < 1) offset = 1;
@ -758,25 +756,25 @@ function spec(b) {
} }
return this.stack[this.stack.length-offset]; return this.stack[this.stack.length-offset];
}; };
ScriptInterpreter.prototype.stackBack = function stackBack() ScriptInterpreter.prototype.stackBack = function stackBack()
{ {
return this.stack[-1]; return this.stack[-1];
}; };
/** /**
* Pop the top element off the stack and return it. * Pop the top element off the stack and return it.
*/ */
ScriptInterpreter.prototype.stackPop = function stackPop() { ScriptInterpreter.prototype.stackPop = function stackPop() {
if (this.stack.length < 1) { if (this.stack.length < 1) {
throw new Error('ScriptInterpreter.stackTop(): Stack underrun'); throw new Error('ScriptInterpreter.stackTop(): Stack underrun');
} }
return this.stack.pop(); return this.stack.pop();
}; };
ScriptInterpreter.prototype.stackSwap = function stackSwap(a, b) { ScriptInterpreter.prototype.stackSwap = function stackSwap(a, b) {
if (this.stack.length < a || this.stack.length < b) { if (this.stack.length < a || this.stack.length < b) {
throw new Error('ScriptInterpreter.stackTop(): Stack underrun'); throw new Error('ScriptInterpreter.stackTop(): Stack underrun');
} }
@ -787,15 +785,15 @@ function spec(b) {
var tmp = s[l - a]; var tmp = s[l - a];
s[l - a] = s[l - b]; s[l - a] = s[l - b];
s[l - b] = tmp; s[l - b] = tmp;
}; };
/** /**
* Returns a version of the stack with only primitive types. * Returns a version of the stack with only primitive types.
* *
* The return value is an array. Any single byte buffer is converted to an * The return value is an array. Any single byte buffer is converted to an
* integer. Any longer Buffer is converted to a hex string. * integer. Any longer Buffer is converted to a hex string.
*/ */
ScriptInterpreter.prototype.getPrimitiveStack = function getPrimitiveStack() { ScriptInterpreter.prototype.getPrimitiveStack = function getPrimitiveStack() {
return this.stack.map(function (entry) { return this.stack.map(function (entry) {
if (entry.length > 2) { if (entry.length > 2) {
return buffertools.toHex(entry.slice(0)); return buffertools.toHex(entry.slice(0));
@ -807,9 +805,9 @@ function spec(b) {
return buffertools.toHex(entry.slice(0)); return buffertools.toHex(entry.slice(0));
} }
}); });
}; };
var castBool = ScriptInterpreter.castBool = function castBool(v) { var castBool = ScriptInterpreter.castBool = function castBool(v) {
for (var i = 0, l = v.length; i < l; i++) { for (var i = 0, l = v.length; i < l; i++) {
if (v[i] != 0) { if (v[i] != 0) {
// Negative zero is still zero // Negative zero is still zero
@ -820,11 +818,11 @@ function spec(b) {
} }
} }
return false; return false;
}; };
var castInt = ScriptInterpreter.castInt = function castInt(v) { var castInt = ScriptInterpreter.castInt = function castInt(v) {
return castBigint(v).toNumber(); return castBigint(v).toNumber();
}; };
var castBigint = ScriptInterpreter.castBigint = function castBigint(v) { var castBigint = ScriptInterpreter.castBigint = function castBigint(v) {
if (!v.length) { if (!v.length) {
return bignum(0); return bignum(0);
} }
@ -844,8 +842,8 @@ function spec(b) {
// Positive number // Positive number
return bignum.fromBuffer(w); return bignum.fromBuffer(w);
} }
}; };
var bigintToBuffer = ScriptInterpreter.bigintToBuffer = function bigintToBuffer(v) { var bigintToBuffer = ScriptInterpreter.bigintToBuffer = function bigintToBuffer(v) {
if ("number" === typeof v) { if ("number" === typeof v) {
v = bignum(v); v = bignum(v);
} }
@ -877,19 +875,19 @@ function spec(b) {
return buffertools.reverse(b); return buffertools.reverse(b);
} }
} }
}; };
ScriptInterpreter.prototype.getResult = function getResult() { ScriptInterpreter.prototype.getResult = function getResult() {
if (this.stack.length === 0) { if (this.stack.length === 0) {
throw new Error("Empty stack after script evaluation"); throw new Error("Empty stack after script evaluation");
} }
return castBool(this.stack[this.stack.length-1]); return castBool(this.stack[this.stack.length-1]);
}; };
ScriptInterpreter.verify = ScriptInterpreter.verify =
function verify(scriptSig, scriptPubKey, txTo, n, hashType, callback) function verify(scriptSig, scriptPubKey, txTo, n, hashType, callback)
{ {
if ("function" !== typeof callback) { if ("function" !== typeof callback) {
throw new Error("ScriptInterpreter.verify() requires a callback"); throw new Error("ScriptInterpreter.verify() requires a callback");
} }
@ -916,92 +914,92 @@ function spec(b) {
}); });
return si; return si;
}; };
function verifyStep4(scriptSig, scriptPubKey, txTo, nIn, function verifyStep4(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy) hashType, opts, callback, si, siCopy)
{ {
if (siCopy.stack.length == 0) { if (siCopy.stack.length == 0) {
callback(null, false); callback(null, false);
return; return;
} }
callback(null, castBool(siCopy.stackBack())); callback(null, castBool(siCopy.stackBack()));
} }
function verifyStep3(scriptSig, scriptPubKey, txTo, nIn, function verifyStep3(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy) hashType, opts, callback, si, siCopy)
{ {
if (si.stack.length == 0) { if (si.stack.length == 0) {
callback(null, false); callback(null, false);
return; return;
} }
if (castBool(si.stackBack()) == false) { if (castBool(si.stackBack()) == false) {
callback(null, false); callback(null, false);
return; return;
} }
// if not P2SH, we're done // if not P2SH, we're done
if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) { if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) {
callback(null, true); callback(null, true);
return; return;
} }
if (!scriptSig.isPushOnly()) { if (!scriptSig.isPushOnly()) {
callback(null, false); callback(null, false);
return; return;
} }
assert.notEqual(siCopy.length, 0); assert.notEqual(siCopy.length, 0);
var subscript = new Script(siCopy.stackPop()); var subscript = new Script(siCopy.stackPop());
ok = true; ok = true;
siCopy.eval(subscript, txTo, nIn, hashType, function (err) { siCopy.eval(subscript, txTo, nIn, hashType, function (err) {
if (err) if (err)
callback(err); callback(err);
else else
verifyStep4(scriptSig, scriptPubKey, txTo, nIn, verifyStep4(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy); hashType, opts, callback, si, siCopy);
}); });
} }
function verifyStep2(scriptSig, scriptPubKey, txTo, nIn, function verifyStep2(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy) hashType, opts, callback, si, siCopy)
{ {
if (opts.verifyP2SH) { if (opts.verifyP2SH) {
si.stack.forEach(function(item) { si.stack.forEach(function(item) {
siCopy.stack.push(item); siCopy.stack.push(item);
}); });
} }
si.eval(scriptPubKey, txTo, nIn, hashType, function (err) { si.eval(scriptPubKey, txTo, nIn, hashType, function (err) {
if (err) if (err)
callback(err); callback(err);
else else
verifyStep3(scriptSig, scriptPubKey, txTo, nIn, verifyStep3(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy); hashType, opts, callback, si, siCopy);
}); });
} }
ScriptInterpreter.verifyFull = ScriptInterpreter.verifyFull =
function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType,
opts, callback) opts, callback)
{ {
var si = new ScriptInterpreter(); var si = new ScriptInterpreter();
var siCopy = new ScriptInterpreter(); var siCopy = new ScriptInterpreter();
si.eval(scriptSig, txTo, nIn, hashType, function (err) { si.eval(scriptSig, txTo, nIn, hashType, function (err) {
if (err) if (err)
callback(err); callback(err);
else else
verifyStep2(scriptSig, scriptPubKey, txTo, nIn, verifyStep2(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy); hashType, opts, callback, si, siCopy);
}); });
}; };
var checkSig = ScriptInterpreter.checkSig = var checkSig = ScriptInterpreter.checkSig =
function (sig, pubkey, scriptCode, tx, n, hashType, callback) { function (sig, pubkey, scriptCode, tx, n, hashType, callback) {
if (!sig.length) { if (!sig.length) {
callback(null, false); callback(null, false);
return; return;
@ -1026,8 +1024,6 @@ function spec(b) {
} catch (err) { } catch (err) {
callback(null, false); callback(null, false);
} }
};
return ScriptInterpreter;
}; };
module.defineClass(spec);
module.exports = require('soop')(ScriptInterpreter);

218
Transaction.js

@ -1,25 +1,23 @@
require('classtool'); var imports = require('soop').imports();
function spec(b) { var config = imports.config || require('./config');
var config = b.config || require('./config'); var log = imports.log || require('./util/log');
var log = b.log || require('./util/log'); var Address = imports.Address || require('./Address');
var Address = b.Address || require('./Address').class(); var Script = imports.Script || require('./Script');
var Script = b.Script || require('./Script').class(); var ScriptInterpreter = imports.ScriptInterpreter || require('./ScriptInterpreter');
var ScriptInterpreter = b.ScriptInterpreter || require('./ScriptInterpreter').class(); var util = imports.util || require('./util/util');
var util = b.util || require('./util/util'); var bignum = imports.bignum || require('bignum');
var bignum = b.bignum || require('bignum'); var Put = imports.Put || require('bufferput');
var Put = b.Put || require('bufferput'); var Parser = imports.Parser || require('./util/BinaryParser');
var Parser = b.Parser || require('./util/BinaryParser').class(); var Step = imports.Step || require('step');
var Step = b.Step || require('step'); var buffertools = imports.buffertools || require('buffertools');
var buffertools = b.buffertools || require('buffertools'); var error = imports.error || require('./util/error');
var VerificationError = error.VerificationError;
var error = b.error || require('./util/error'); var MissingSourceError = error.MissingSourceError;
var VerificationError = error.VerificationError;
var MissingSourceError = error.MissingSourceError; var COINBASE_OP = Buffer.concat([util.NULL_HASH, new Buffer('FFFFFFFF', 'hex')]);
var COINBASE_OP = Buffer.concat([util.NULL_HASH, new Buffer('FFFFFFFF', 'hex')]); function TransactionIn(data) {
function TransactionIn(data) {
if ("object" !== typeof data) { if ("object" !== typeof data) {
data = {}; data = {};
} }
@ -29,69 +27,69 @@ function spec(b) {
this.s = Buffer.isBuffer(data.s) ? data.s : this.s = Buffer.isBuffer(data.s) ? data.s :
Buffer.isBuffer(data.script) ? data.script : util.EMPTY_BUFFER; Buffer.isBuffer(data.script) ? data.script : util.EMPTY_BUFFER;
this.q = data.q ? data.q : data.sequence; this.q = data.q ? data.q : data.sequence;
} }
TransactionIn.prototype.getScript = function getScript() { TransactionIn.prototype.getScript = function getScript() {
return new Script(this.s); return new Script(this.s);
}; };
TransactionIn.prototype.isCoinBase = function isCoinBase() { TransactionIn.prototype.isCoinBase = function isCoinBase() {
return buffertools.compare(this.o, COINBASE_OP) === 0; return buffertools.compare(this.o, COINBASE_OP) === 0;
}; };
TransactionIn.prototype.serialize = function serialize() { TransactionIn.prototype.serialize = function serialize() {
var slen = util.varIntBuf(this.s.length); var slen = util.varIntBuf(this.s.length);
var qbuf = new Buffer(4); var qbuf = new Buffer(4);
qbuf.writeUInt32LE(this.q, 0); qbuf.writeUInt32LE(this.q, 0);
return Buffer.concat([this.o, slen, this.s, qbuf]); return Buffer.concat([this.o, slen, this.s, qbuf]);
}; };
TransactionIn.prototype.getOutpointHash = function getOutpointHash() { TransactionIn.prototype.getOutpointHash = function getOutpointHash() {
if ("undefined" !== typeof this.o.outHashCache) { if ("undefined" !== typeof this.o.outHashCache) {
return this.o.outHashCache; return this.o.outHashCache;
} }
return this.o.outHashCache = this.o.slice(0, 32); return this.o.outHashCache = this.o.slice(0, 32);
}; };
TransactionIn.prototype.getOutpointIndex = function getOutpointIndex() { TransactionIn.prototype.getOutpointIndex = function getOutpointIndex() {
return (this.o[32] ) + return (this.o[32] ) +
(this.o[33] << 8) + (this.o[33] << 8) +
(this.o[34] << 16) + (this.o[34] << 16) +
(this.o[35] << 24); (this.o[35] << 24);
}; };
TransactionIn.prototype.setOutpointIndex = function setOutpointIndex(n) { TransactionIn.prototype.setOutpointIndex = function setOutpointIndex(n) {
this.o[32] = n & 0xff; this.o[32] = n & 0xff;
this.o[33] = n >> 8 & 0xff; this.o[33] = n >> 8 & 0xff;
this.o[34] = n >> 16 & 0xff; this.o[34] = n >> 16 & 0xff;
this.o[35] = n >> 24 & 0xff; this.o[35] = n >> 24 & 0xff;
}; };
function TransactionOut(data) { function TransactionOut(data) {
if ("object" !== typeof data) { if ("object" !== typeof data) {
data = {}; data = {};
} }
this.v = data.v ? data.v : data.value; this.v = data.v ? data.v : data.value;
this.s = data.s ? data.s : data.script; this.s = data.s ? data.s : data.script;
}; };
TransactionOut.prototype.getValue = function getValue() { TransactionOut.prototype.getValue = function getValue() {
return new Parser(this.v).word64lu(); return new Parser(this.v).word64lu();
}; };
TransactionOut.prototype.getScript = function getScript() { TransactionOut.prototype.getScript = function getScript() {
return new Script(this.s); return new Script(this.s);
}; };
TransactionOut.prototype.serialize = function serialize() { TransactionOut.prototype.serialize = function serialize() {
var slen = util.varIntBuf(this.s.length); var slen = util.varIntBuf(this.s.length);
return Buffer.concat([this.v, slen, this.s]); return Buffer.concat([this.v, slen, this.s]);
}; };
function Transaction(data) { function Transaction(data) {
if ("object" !== typeof data) { if ("object" !== typeof data) {
data = {}; data = {};
} }
@ -112,16 +110,16 @@ function spec(b) {
return txout; return txout;
}) : []; }) : [];
if (data.buffer) this._buffer = data.buffer; if (data.buffer) this._buffer = data.buffer;
}; };
this.class = Transaction; this.class = Transaction;
Transaction.In = TransactionIn; Transaction.In = TransactionIn;
Transaction.Out = TransactionOut; Transaction.Out = TransactionOut;
Transaction.prototype.isCoinBase = function () { Transaction.prototype.isCoinBase = function () {
return this.ins.length == 1 && this.ins[0].isCoinBase(); return this.ins.length == 1 && this.ins[0].isCoinBase();
}; };
Transaction.prototype.isStandard = function isStandard() { Transaction.prototype.isStandard = function isStandard() {
var i; var i;
for (i = 0; i < this.ins.length; i++) { for (i = 0; i < this.ins.length; i++) {
if (this.ins[i].getScript().getInType() == "Strange") { if (this.ins[i].getScript().getInType() == "Strange") {
@ -134,9 +132,9 @@ function spec(b) {
} }
} }
return true; return true;
}; };
Transaction.prototype.serialize = function serialize() { Transaction.prototype.serialize = function serialize() {
var bufs = []; var bufs = [];
var buf = new Buffer(4); var buf = new Buffer(4);
@ -159,34 +157,34 @@ function spec(b) {
this._buffer = Buffer.concat(bufs); this._buffer = Buffer.concat(bufs);
return this._buffer; return this._buffer;
}; };
Transaction.prototype.getBuffer = function getBuffer() { Transaction.prototype.getBuffer = function getBuffer() {
if (this._buffer) return this._buffer; if (this._buffer) return this._buffer;
return this.serialize(); return this.serialize();
}; };
Transaction.prototype.calcHash = function calcHash() { Transaction.prototype.calcHash = function calcHash() {
this.hash = util.twoSha256(this.getBuffer()); this.hash = util.twoSha256(this.getBuffer());
return this.hash; return this.hash;
}; };
Transaction.prototype.checkHash = function checkHash() { Transaction.prototype.checkHash = function checkHash() {
if (!this.hash || !this.hash.length) return false; if (!this.hash || !this.hash.length) return false;
return buffertools.compare(this.calcHash(), this.hash) === 0; return buffertools.compare(this.calcHash(), this.hash) === 0;
}; };
Transaction.prototype.getHash = function getHash() { Transaction.prototype.getHash = function getHash() {
if (!this.hash || !this.hash.length) { if (!this.hash || !this.hash.length) {
this.hash = this.calcHash(); this.hash = this.calcHash();
} }
return this.hash; return this.hash;
}; };
// convert encoded list of inputs to easy-to-use JS list-of-lists // convert encoded list of inputs to easy-to-use JS list-of-lists
Transaction.prototype.inputs = function inputs() { Transaction.prototype.inputs = function inputs() {
var res = []; var res = [];
for (var i = 0; i < this.ins.length; i++) { for (var i = 0; i < this.ins.length; i++) {
var txin = this.ins[i]; var txin = this.ins[i];
@ -196,9 +194,9 @@ function spec(b) {
} }
return res; return res;
} }
/** /**
* Load and cache transaction inputs. * Load and cache transaction inputs.
* *
* This function will try to load the inputs for a transaction. * This function will try to load the inputs for a transaction.
@ -209,15 +207,15 @@ function spec(b) {
* met (or a timeout occurs.) * met (or a timeout occurs.)
* @param {Function} callback Function to call on completion. * @param {Function} callback Function to call on completion.
*/ */
Transaction.prototype.cacheInputs = Transaction.prototype.cacheInputs =
function cacheInputs(blockChain, txStore, wait, callback) { function cacheInputs(blockChain, txStore, wait, callback) {
var self = this; var self = this;
var txCache = new TransactionInputsCache(this); var txCache = new TransactionInputsCache(this);
txCache.buffer(blockChain, txStore, wait, callback); txCache.buffer(blockChain, txStore, wait, callback);
}; };
Transaction.prototype.verify = function verify(txCache, blockChain, callback) { Transaction.prototype.verify = function verify(txCache, blockChain, callback) {
var self = this; var self = this;
var txIndex = txCache.txIndex; var txIndex = txCache.txIndex;
@ -341,22 +339,22 @@ function spec(b) {
}, },
callback callback
); );
}; };
Transaction.prototype.verifyInput = function verifyInput(n, scriptPubKey, callback) { Transaction.prototype.verifyInput = function verifyInput(n, scriptPubKey, callback) {
return ScriptInterpreter.verify(this.ins[n].getScript(), return ScriptInterpreter.verify(this.ins[n].getScript(),
scriptPubKey, scriptPubKey,
this, n, 0, this, n, 0,
callback); callback);
}; };
/** /**
* Returns an object containing all pubkey hashes affected by this transaction. * Returns an object containing all pubkey hashes affected by this transaction.
* *
* The return object contains the base64-encoded pubKeyHash values as keys * The return object contains the base64-encoded pubKeyHash values as keys
* and the original pubKeyHash buffers as values. * and the original pubKeyHash buffers as values.
*/ */
Transaction.prototype.getAffectedKeys = function getAffectedKeys(txCache) { Transaction.prototype.getAffectedKeys = function getAffectedKeys(txCache) {
// TODO: Function won't consider results cached if there are no affected // TODO: Function won't consider results cached if there are no affected
// accounts. // accounts.
if (!(this.affects && this.affects.length)) { if (!(this.affects && this.affects.length)) {
@ -423,22 +421,22 @@ function spec(b) {
}); });
return affectedKeys; return affectedKeys;
}; };
var OP_CODESEPARATOR = 171; var OP_CODESEPARATOR = 171;
var SIGHASH_ALL = 1; var SIGHASH_ALL = 1;
var SIGHASH_NONE = 2; var SIGHASH_NONE = 2;
var SIGHASH_SINGLE = 3; var SIGHASH_SINGLE = 3;
var SIGHASH_ANYONECANPAY = 80; var SIGHASH_ANYONECANPAY = 80;
Transaction.SIGHASH_ALL=SIGHASH_ALL; Transaction.SIGHASH_ALL=SIGHASH_ALL;
Transaction.SIGHASH_NONE=SIGHASH_NONE; Transaction.SIGHASH_NONE=SIGHASH_NONE;
Transaction.SIGHASH_SINGLE=SIGHASH_SINGLE; Transaction.SIGHASH_SINGLE=SIGHASH_SINGLE;
Transaction.SIGHASH_ANYONECANPAY=SIGHASH_ANYONECANPAY; Transaction.SIGHASH_ANYONECANPAY=SIGHASH_ANYONECANPAY;
Transaction.prototype.hashForSignature = Transaction.prototype.hashForSignature =
function hashForSignature(script, inIndex, hashType) { function hashForSignature(script, inIndex, hashType) {
if (+inIndex !== inIndex || if (+inIndex !== inIndex ||
inIndex < 0 || inIndex >= this.ins.length) { inIndex < 0 || inIndex >= this.ins.length) {
throw new Error("Input index '"+inIndex+"' invalid or out of bounds "+ throw new Error("Input index '"+inIndex+"' invalid or out of bounds "+
@ -540,12 +538,12 @@ function spec(b) {
buffer = Buffer.concat([buffer, new Buffer([parseInt(hashType), 0, 0, 0])]); buffer = Buffer.concat([buffer, new Buffer([parseInt(hashType), 0, 0, 0])]);
return util.twoSha256(buffer); return util.twoSha256(buffer);
}; };
/** /**
* Returns an object with the same field names as jgarzik's getblock patch. * Returns an object with the same field names as jgarzik's getblock patch.
*/ */
Transaction.prototype.getStandardizedObject = function getStandardizedObject() { Transaction.prototype.getStandardizedObject = function getStandardizedObject() {
var tx = { var tx = {
hash: util.formatHashFull(this.getHash()), hash: util.formatHashFull(this.getHash()),
version: this.version, version: this.version,
@ -587,14 +585,14 @@ function spec(b) {
tx["out"] = outs; tx["out"] = outs;
return tx; return tx;
}; };
// Add some Mongoose compatibility functions to the plain object // Add some Mongoose compatibility functions to the plain object
Transaction.prototype.toObject = function toObject() { Transaction.prototype.toObject = function toObject() {
return this; return this;
}; };
Transaction.prototype.fromObj = function fromObj(obj) { Transaction.prototype.fromObj = function fromObj(obj) {
var txobj = {}; var txobj = {};
txobj.version = obj.version || 1; txobj.version = obj.version || 1;
txobj.lock_time = obj.lock_time || 0; txobj.lock_time = obj.lock_time || 0;
@ -636,9 +634,9 @@ function spec(b) {
this.version = txobj.version; this.version = txobj.version;
this.ins = txobj.ins; this.ins = txobj.ins;
this.outs = txobj.outs; this.outs = txobj.outs;
} }
Transaction.prototype.parse = function (parser) { Transaction.prototype.parse = function (parser) {
if (Buffer.isBuffer(parser)) { if (Buffer.isBuffer(parser)) {
parser = new Parser(parser); parser = new Parser(parser);
} }
@ -672,11 +670,11 @@ function spec(b) {
this.lock_time = parser.word32le(); this.lock_time = parser.word32le();
this.calcHash(); this.calcHash();
}; };
var TransactionInputsCache = exports.TransactionInputsCache = var TransactionInputsCache = exports.TransactionInputsCache =
function TransactionInputsCache(tx) function TransactionInputsCache(tx)
{ {
var txList = []; var txList = [];
var txList64 = []; var txList64 = [];
var reqOuts = {}; var reqOuts = {};
@ -703,10 +701,10 @@ function spec(b) {
this.txIndex = {}; this.txIndex = {};
this.requiredOuts = reqOuts; this.requiredOuts = reqOuts;
this.callbacks = []; this.callbacks = [];
}; };
TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, wait, callback) TransactionInputsCache.prototype.buffer = function buffer(blockChain, txStore, wait, callback)
{ {
var self = this; var self = this;
var complete = false; var complete = false;
@ -789,11 +787,11 @@ function spec(b) {
}, },
self.callback.bind(self) self.callback.bind(self)
); );
}; };
TransactionInputsCache.prototype.callback = function callback(err) TransactionInputsCache.prototype.callback = function callback(err)
{ {
var args = Array.prototype.slice.apply(arguments); var args = Array.prototype.slice.apply(arguments);
// Empty the callback array first (because downstream functions could add new // Empty the callback array first (because downstream functions could add new
@ -809,8 +807,6 @@ function spec(b) {
log.err("Callback error after connecting tx inputs: "+ log.err("Callback error after connecting tx inputs: "+
(err.stack ? err.stack : err.toString())); (err.stack ? err.stack : err.toString()));
} }
};
return Transaction;
}; };
module.defineClass(spec);
module.exports = require('soop')(Transaction);

64
Wallet.js

@ -1,15 +1,15 @@
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',
@ -19,9 +19,9 @@ function ClassSpec(b) {
keys: [], keys: [],
sin: {}, sin: {},
scripts: {}, scripts: {},
}; };
function Wallet(cfg) { function Wallet(cfg) {
if (typeof cfg !== 'object') if (typeof cfg !== 'object')
cfg = {}; cfg = {};
@ -33,15 +33,15 @@ function ClassSpec(b) {
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,
@ -49,9 +49,9 @@ function ClassSpec(b) {
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;
@ -70,19 +70,19 @@ function ClassSpec(b) {
// 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) { Wallet.prototype.addSIN = function(sinObj) {
this.datastore.sin[sinObj.sin] = sinObj; this.datastore.sin[sinObj.sin] = sinObj;
this.dirty = true; this.dirty = true;
}; };
Wallet.prototype.findKeyHash = function(pubKeyHash) { Wallet.prototype.findKeyHash = function(pubKeyHash) {
var pkhStr = pubKeyHash.toString(); var pkhStr = pubKeyHash.toString();
for (var i = 0; i < this.datastore.keys.length; i++) { for (var i = 0; i < this.datastore.keys.length; i++) {
@ -94,9 +94,9 @@ function ClassSpec(b) {
} }
return undefined; return undefined;
}; };
Wallet.prototype.expandKey = function(key) { Wallet.prototype.expandKey = function(key) {
var addr = new Address(key); var addr = new Address(key);
var isAddr = true; var isAddr = true;
@ -113,9 +113,9 @@ function ClassSpec(b) {
if (!key.match(re)) if (!key.match(re))
throw new Error("Unknown key type"); throw new Error("Unknown key type");
return hex(key); return hex(key);
}; };
Wallet.prototype.expandKeys = function(keys) { Wallet.prototype.expandKeys = function(keys) {
var res = []; var res = [];
var us = this; var us = this;
keys.forEach(function(key) { keys.forEach(function(key) {
@ -123,9 +123,9 @@ function ClassSpec(b) {
res.push(expKey); res.push(expKey);
}); });
return res; return res;
}; };
Wallet.prototype.addScript = function(script) { Wallet.prototype.addScript = function(script) {
var buf = script.getBuffer(); var buf = script.getBuffer();
var hash = util.sha256ripe160(buf); var hash = util.sha256ripe160(buf);
var addr = new Address(this.network.addressScript, hash); var addr = new Address(this.network.addressScript, hash);
@ -134,9 +134,7 @@ function ClassSpec(b) {
this.dirty = true; this.dirty = true;
return addrStr; return addrStr;
};
return Wallet;
}; };
module.defineClass(ClassSpec);
module.exports = require('soop')(Wallet);

33
WalletKey.js

@ -1,26 +1,25 @@
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() { WalletKey.prototype.generate = function() {
this.privKey = KeyModule.Key.generateSync(); this.privKey = KeyModule.Key.generateSync();
this.created = timeUtil.curtime(); this.created = timeUtil.curtime();
}; };
WalletKey.prototype.storeObj = function() { WalletKey.prototype.storeObj = function() {
var pubKey = this.privKey.public.toString('hex'); var pubKey = this.privKey.public.toString('hex');
var pubKeyHash = coinUtil.sha256ripe160(this.privKey.public); var pubKeyHash = coinUtil.sha256ripe160(this.privKey.public);
var addr = new Address(this.network.addressPubkey, pubKeyHash); var addr = new Address(this.network.addressPubkey, pubKeyHash);
@ -33,9 +32,9 @@ function ClassSpec(b) {
}; };
return obj; return obj;
}; };
WalletKey.prototype.fromObj = function(obj) { WalletKey.prototype.fromObj = function(obj) {
this.created = obj.created; this.created = obj.created;
this.privKey = new KeyModule.Key(); this.privKey = new KeyModule.Key();
if (obj.priv.length==64) { if (obj.priv.length==64) {
@ -48,8 +47,6 @@ function ClassSpec(b) {
this.privKey.compressed = priv.compressed(); this.privKey.compressed = priv.compressed();
} }
this.privKey.regenerateSync(); this.privKey.regenerateSync();
};
return WalletKey;
}; };
module.defineClass(ClassSpec);
module.exports = require('soop')(WalletKey);

2
test/test.Address.js

@ -13,7 +13,7 @@ describe('Address', function() {
should.exist(AddressModule); should.exist(AddressModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Address = AddressModule.class(); Address = AddressModule;
should.exist(Address); should.exist(Address);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.Block.js

@ -12,7 +12,7 @@ describe('Block', function() {
should.exist(BlockModule); should.exist(BlockModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Block = BlockModule.class(); Block = BlockModule;
should.exist(Block); should.exist(Block);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.Bloom.js

@ -13,7 +13,7 @@ describe('Bloom', function() {
should.exist(BloomModule); should.exist(BloomModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Bloom = BloomModule.class(); Bloom = BloomModule;
should.exist(Bloom); should.exist(Bloom);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.Connection.js

@ -14,7 +14,7 @@ describe('Connection', function() {
should.exist(ConnectionModule); should.exist(ConnectionModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Connection = ConnectionModule.class(); Connection = ConnectionModule;
should.exist(Connection); should.exist(Connection);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.EncodedData.js

@ -13,7 +13,7 @@ describe('EncodedData', function() {
should.exist(EncodedDataModule); should.exist(EncodedDataModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
EncodedData = EncodedDataModule.class(); EncodedData = EncodedDataModule;
should.exist(EncodedData); should.exist(EncodedData);
}); });
it('should be able to create an instance', function() { it('should be able to create an instance', function() {

2
test/test.Opcode.js

@ -13,7 +13,7 @@ describe('Opcode', function() {
should.exist(OpcodeModule); should.exist(OpcodeModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Opcode = OpcodeModule.class(); Opcode = OpcodeModule;
should.exist(Opcode); should.exist(Opcode);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.Peer.js

@ -13,7 +13,7 @@ describe('Peer', function() {
should.exist(PeerModule); should.exist(PeerModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Peer = PeerModule.class(); Peer = PeerModule;
should.exist(Peer); should.exist(Peer);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.PeerManager.js

@ -13,7 +13,7 @@ describe('PeerManager', function() {
should.exist(PeerManagerModule); should.exist(PeerManagerModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
PeerManager = PeerManagerModule.class(); PeerManager = PeerManagerModule;
should.exist(PeerManager); should.exist(PeerManager);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.PrivateKey.js

@ -15,7 +15,7 @@ describe('PrivateKey', function() {
should.exist(PrivateKeyModule); should.exist(PrivateKeyModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
PrivateKey = PrivateKeyModule.class(); PrivateKey = PrivateKeyModule;
should.exist(PrivateKey); should.exist(PrivateKey);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.RpcClient.js

@ -7,7 +7,7 @@ var should = chai.should();
var RpcClientModule = bitcore.RpcClient; var RpcClientModule = bitcore.RpcClient;
var RpcClient; var RpcClient;
RpcClient = RpcClientModule.class(); RpcClient = RpcClientModule;
describe('RpcClient', function() { describe('RpcClient', function() {
it('should initialze the main object', function() { it('should initialze the main object', function() {

2
test/test.SIN.js

@ -13,7 +13,7 @@ describe('SIN', function() {
should.exist(SINModule); should.exist(SINModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
SIN = SINModule.class(); SIN = SINModule;
should.exist(SIN); should.exist(SIN);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.SINKey.js

@ -16,7 +16,7 @@ describe('SINKey', function() {
should.exist(SINKeyModule); should.exist(SINKeyModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
SINKey = SINKeyModule.class(); SINKey = SINKeyModule;
should.exist(SINKey); should.exist(SINKey);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

4
test/test.Script.js

@ -6,7 +6,7 @@ var bitcore = require('../bitcore');
var should = chai.should(); var should = chai.should();
var ScriptModule = bitcore.Script; var ScriptModule = bitcore.Script;
var Address = bitcore.Address.class(); var Address = bitcore.Address;
var networks = bitcore.networks; var networks = bitcore.networks;
var Script; var Script;
@ -15,7 +15,7 @@ describe('Script', function() {
should.exist(ScriptModule); should.exist(ScriptModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Script = ScriptModule.class(); Script = ScriptModule;
should.exist(Script); should.exist(Script);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.ScriptInterpreter.js

@ -13,7 +13,7 @@ describe('ScriptInterpreter', function() {
should.exist(ScriptInterpreterModule); should.exist(ScriptInterpreterModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
ScriptInterpreter = ScriptInterpreterModule.class(); ScriptInterpreter = ScriptInterpreterModule;
should.exist(ScriptInterpreter); should.exist(ScriptInterpreter);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.Transaction.js

@ -13,7 +13,7 @@ describe('Transaction', function() {
should.exist(TransactionModule); should.exist(TransactionModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Transaction = TransactionModule.class(); Transaction = TransactionModule;
should.exist(Transaction); should.exist(Transaction);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.VersionedData.js

@ -13,7 +13,7 @@ describe('VersionedData', function() {
should.exist(VersionedDataModule); should.exist(VersionedDataModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
VersionedData = VersionedDataModule.class(); VersionedData = VersionedDataModule;
should.exist(VersionedData); should.exist(VersionedData);
}); });
it('should be able to create an instance', function() { it('should be able to create an instance', function() {

2
test/test.Wallet.js

@ -13,7 +13,7 @@ describe('Wallet', function() {
should.exist(WalletModule); should.exist(WalletModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Wallet = WalletModule.class(); Wallet = WalletModule;
should.exist(Wallet); should.exist(Wallet);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

2
test/test.WalletKey.js

@ -14,7 +14,7 @@ describe('WalletKey', function() {
should.exist(WalletKeyModule); should.exist(WalletKeyModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
WalletKey = WalletKeyModule.class(); WalletKey = WalletKeyModule;
should.exist(WalletKey); should.exist(WalletKey);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

4
test/test.basic.js

@ -4,8 +4,8 @@ var chai = require('chai');
var bitcore = require('../bitcore'); var bitcore = require('../bitcore');
var should = chai.should(); var should = chai.should();
var Address = bitcore.Address.class(); var Address = bitcore.Address;
var PrivateKey = bitcore.PrivateKey.class(); var PrivateKey = bitcore.PrivateKey;
var networks = bitcore.networks; var networks = bitcore.networks;
var KeyModule = bitcore.KeyModule; var KeyModule = bitcore.KeyModule;

72
util/BinaryParser.js

@ -2,20 +2,20 @@
* Simple synchronous parser based on node-binary. * Simple synchronous parser based on node-binary.
*/ */
function spec(b) { var imports = require('soop').imports();
function Parser(buffer) function Parser(buffer)
{ {
this.subject = buffer; this.subject = buffer;
this.pos = 0; this.pos = 0;
}; };
Parser.prototype.buffer = function buffer(len) { Parser.prototype.buffer = function buffer(len) {
var buf = this.subject.slice(this.pos, this.pos+len); var buf = this.subject.slice(this.pos, this.pos+len);
this.pos += len; this.pos += len;
return buf; return buf;
}; };
Parser.prototype.search = function search(needle) { Parser.prototype.search = function search(needle) {
var len; var len;
if ("string" === typeof needle || Buffer.isBuffer(needle)) { if ("string" === typeof needle || Buffer.isBuffer(needle)) {
@ -38,12 +38,12 @@ function spec(b) {
} }
return -1; return -1;
} }
}; };
/** /**
* Like search(), but returns the skipped bytes * Like search(), but returns the skipped bytes
*/ */
Parser.prototype.scan = function scan(needle) { Parser.prototype.scan = function scan(needle) {
var startPos = this.pos; var startPos = this.pos;
var len = this.search(needle); var len = this.search(needle);
if (len !== -1) { if (len !== -1) {
@ -51,55 +51,55 @@ function spec(b) {
} else { } else {
throw new Error('No match'); throw new Error('No match');
} }
}; };
Parser.prototype.eof = function eof() { Parser.prototype.eof = function eof() {
return this.pos >= this.subject.length; return this.pos >= this.subject.length;
}; };
// convert byte strings to unsigned little endian numbers // convert byte strings to unsigned little endian numbers
function decodeLEu (bytes) { function decodeLEu (bytes) {
var acc = 0; var acc = 0;
for (var i = 0; i < bytes.length; i++) { for (var i = 0; i < bytes.length; i++) {
acc += Math.pow(256,i) * bytes[i]; acc += Math.pow(256,i) * bytes[i];
} }
return acc; return acc;
} }
// convert byte strings to unsigned big endian numbers // convert byte strings to unsigned big endian numbers
function decodeBEu (bytes) { function decodeBEu (bytes) {
var acc = 0; var acc = 0;
for (var i = 0; i < bytes.length; i++) { for (var i = 0; i < bytes.length; i++) {
acc += Math.pow(256, bytes.length - i - 1) * bytes[i]; acc += Math.pow(256, bytes.length - i - 1) * bytes[i];
} }
return acc; return acc;
} }
// convert byte strings to signed big endian numbers // convert byte strings to signed big endian numbers
function decodeBEs (bytes) { function decodeBEs (bytes) {
var val = decodeBEu(bytes); var val = decodeBEu(bytes);
if ((bytes[0] & 0x80) == 0x80) { if ((bytes[0] & 0x80) == 0x80) {
val -= Math.pow(256, bytes.length); val -= Math.pow(256, bytes.length);
} }
return val; return val;
} }
// convert byte strings to signed little endian numbers // convert byte strings to signed little endian numbers
function decodeLEs (bytes) { function decodeLEs (bytes) {
var val = decodeLEu(bytes); var val = decodeLEu(bytes);
if ((bytes[bytes.length - 1] & 0x80) == 0x80) { if ((bytes[bytes.length - 1] & 0x80) == 0x80) {
val -= Math.pow(256, bytes.length); val -= Math.pow(256, bytes.length);
} }
return val; return val;
} }
function getDecoder(len, fn) { function getDecoder(len, fn) {
return function () { return function () {
var buf = this.buffer(len); var buf = this.buffer(len);
return fn(buf); return fn(buf);
}; };
}; };
[ 1, 2, 4, 8 ].forEach(function (bytes) { [ 1, 2, 4, 8 ].forEach(function (bytes) {
var bits = bytes * 8; var bits = bytes * 8;
Parser.prototype['word' + bits + 'le'] Parser.prototype['word' + bits + 'le']
@ -118,10 +118,10 @@ function spec(b) {
Parser.prototype.word8 = Parser.prototype.word8u = Parser.prototype.word8be; Parser.prototype.word8 = Parser.prototype.word8u = Parser.prototype.word8be;
Parser.prototype.word8s = Parser.prototype.word8bs; Parser.prototype.word8s = Parser.prototype.word8bs;
}); });
Parser.prototype.varInt = function () Parser.prototype.varInt = function ()
{ {
var firstByte = this.word8(); var firstByte = this.word8();
switch (firstByte) { switch (firstByte) {
case 0xFD: case 0xFD:
@ -136,13 +136,11 @@ function spec(b) {
default: default:
return firstByte; return firstByte;
} }
}; };
Parser.prototype.varStr = function () { Parser.prototype.varStr = function () {
var len = this.varInt(); var len = this.varInt();
return this.buffer(len); return this.buffer(len);
};
return Parser;
}; };
module.defineClass(spec);
module.exports = require('soop')(Parser);

109
util/EncodedData.js

@ -1,92 +1,91 @@
require('classtool'); var imports = require('soop').imports();
var base58 = imports.base58 || require('base58-native').base58Check;
function ClassSpec(b) {
var base58 = b.base58 || require('base58-native').base58Check;
// Constructor. Takes the following forms: // Constructor. Takes the following forms:
// new EncodedData(<base58_address_string>) // new EncodedData(<base58_address_string>)
// new EncodedData(<binary_buffer>) // new EncodedData(<binary_buffer>)
// new EncodedData(<data>, <encoding>) // new EncodedData(<data>, <encoding>)
// new EncodedData(<version>, <20-byte-hash>) // new EncodedData(<version>, <20-byte-hash>)
function EncodedData(data, encoding) { function EncodedData(data, encoding) {
this.data = data; this.data = data;
if(!encoding && (typeof data == 'string')) { if(!encoding && (typeof data == 'string')) {
this.__proto__ = this.encodings['base58']; this.__proto__ = this.encodings['base58'];
} else { } else {
this.__proto__ = this.encodings[encoding || 'binary']; this.__proto__ = this.encodings[encoding || 'binary'];
} }
}; };
// get or set the encoding used (transforms data) // get or set the encoding used (transforms data)
EncodedData.prototype.encoding = function(encoding) { EncodedData.prototype.encoding = function(encoding) {
if(encoding && (encoding != this._encoding)) { if(encoding && (encoding != this._encoding)) {
this.data = this.as(encoding); this.data = this.as(encoding);
this.__proto__ = this.encodings[encoding]; this.__proto__ = this.encodings[encoding];
} }
return this._encoding; return this._encoding;
}; };
// answer a new instance having the given encoding // answer a new instance having the given encoding
EncodedData.prototype.withEncoding = function(encoding) { EncodedData.prototype.withEncoding = function(encoding) {
return new EncodedData(this.as(encoding), encoding); return new EncodedData(this.as(encoding), encoding);
}; };
// answer the data in the given encoding // answer the data in the given encoding
EncodedData.prototype.as = function(encoding) { EncodedData.prototype.as = function(encoding) {
if(!encodings[encoding]) throw new Error('invalid encoding'); if(!encodings[encoding]) throw new Error('invalid encoding');
return this.converters[encoding].call(this); return this.converters[encoding].call(this);
}; };
// validate that we can convert to binary // validate that we can convert to binary
EncodedData.prototype._validate = function() { EncodedData.prototype._validate = function() {
this.withEncoding('binary'); this.withEncoding('binary');
}; };
// Boolean protocol for testing if valid // Boolean protocol for testing if valid
EncodedData.prototype.isValid = function() { EncodedData.prototype.isValid = function() {
try { try {
this.validate(); this.validate();
return true; return true;
} catch(e) { } catch(e) {
return false; return false;
} }
}; };
// subclasses can override to do more stuff // subclasses can override to do more stuff
EncodedData.prototype.validate = function() { EncodedData.prototype.validate = function() {
this._validate(); this._validate();
}; };
// Boolean protocol for testing if valid // Boolean protocol for testing if valid
EncodedData.prototype.isValid = function() { EncodedData.prototype.isValid = function() {
try { try {
this.validate(); this.validate();
return true; return true;
} catch(e) { } catch(e) {
return false; return false;
} }
}; };
// convert to a string (in base58 form) // convert to a string (in base58 form)
EncodedData.prototype.toString = function() { EncodedData.prototype.toString = function() {
return this.as('base58'); return this.as('base58');
}; };
// utility // utility
EncodedData.prototype.doAsBinary = function(callback) { EncodedData.prototype.doAsBinary = function(callback) {
var oldEncoding = this.encoding(); var oldEncoding = this.encoding();
this.encoding('binary'); this.encoding('binary');
callback.apply(this); callback.apply(this);
this.encoding(oldEncoding); this.encoding(oldEncoding);
}; };
// Setup support for various address encodings. The object for // Setup support for various address encodings. The object for
// each encoding inherits from the EncodedData prototype. This // each encoding inherits from the EncodedData prototype. This
// allows any encoding to override any method...changing the encoding // allows any encoding to override any method...changing the encoding
// for an instance will change the encoding it inherits from. Note, // for an instance will change the encoding it inherits from. Note,
// this will present some problems for anyone wanting to inherit from // this will present some problems for anyone wanting to inherit from
// EncodedData (we'll deal with that when needed). // EncodedData (we'll deal with that when needed).
var encodings = { var encodings = {
'binary': { 'binary': {
converters: { converters: {
'binary': function() { 'binary': function() {
@ -128,18 +127,18 @@ function ClassSpec(b) {
}, },
}, },
}, },
}; };
var no_conversion = function() {return this.data;}; var no_conversion = function() {return this.data;};
for(var k in encodings) { for(var k in encodings) {
if(encodings.hasOwnProperty(k)){ if(encodings.hasOwnProperty(k)){
if(!encodings[k].converters[k]) if(!encodings[k].converters[k])
encodings[k].converters[k] = no_conversion; encodings[k].converters[k] = no_conversion;
encodings[k]._encoding = k; encodings[k]._encoding = k;
} }
} }
EncodedData.applyEncodingsTo = function(aClass) { EncodedData.applyEncodingsTo = function(aClass) {
var tmp = {}; var tmp = {};
for(var k in encodings) { for(var k in encodings) {
var enc = encodings[k]; var enc = encodings[k];
@ -151,9 +150,9 @@ function ClassSpec(b) {
tmp[k] = obj; tmp[k] = obj;
} }
aClass.prototype.encodings = tmp; aClass.prototype.encodings = tmp;
}; };
EncodedData.applyEncodingsTo(EncodedData);
module.exports = require('soop')(EncodedData);
EncodedData.applyEncodingsTo(EncodedData);
return EncodedData;
}
module.defineClass(ClassSpec);

32
util/VersionedData.js

@ -1,9 +1,8 @@
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();
function VersionedData(version, payload) {
if(typeof version != 'number') { if(typeof version != 'number') {
VersionedData.super(this, arguments); VersionedData.super(this, arguments);
return; return;
@ -12,28 +11,27 @@ function ClassSpec(b) {
this.__proto__ = this.encodings['binary']; this.__proto__ = this.encodings['binary'];
this.version(version); this.version(version);
this.payload(payload); this.payload(payload);
}; };
VersionedData.superclass = superclass;
superclass.applyEncodingsTo(VersionedData);
// 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);
// get or set the version data (the first byte of the address)
VersionedData.prototype.version = function(num) {
if(num || (num === 0)) { if(num || (num === 0)) {
this.doAsBinary(function() {this.data.writeUInt8(num, 0);}); this.doAsBinary(function() {this.data.writeUInt8(num, 0);});
return num; return num;
} }
return this.as('binary').readUInt8(0); return this.as('binary').readUInt8(0);
}; };
// get or set the payload data (as a Buffer object) // get or set the payload data (as a Buffer object)
VersionedData.prototype.payload = function(data) { VersionedData.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;
} }
return this.as('binary').slice(1); return this.as('binary').slice(1);
};
return VersionedData;
}; };
module.defineClass(ClassSpec);
module.exports = require('soop')(VersionedData);

Loading…
Cancel
Save