Browse Source

Merge branch 'master' of ../fullnode into v0.8

Conflicts:
	.gitignore
	.travis.yml
	README.md
patch-2
Eric Martindale 10 years ago
parent
commit
fb1a193893
  1. 14
      .gitignore
  2. 1
      .travis.yml
  3. 30
      LICENSE.md
  4. 4
      browser/build
  5. 25
      examples/blockreader.js
  6. 21
      examples/ecdsa.js
  7. 63
      examples/stealthmessage.js
  8. 56
      index.js
  9. 120
      lib/address.js
  10. 53
      lib/base58.js
  11. 73
      lib/base58check.js
  12. 326
      lib/bip32.js
  13. 108
      lib/block.js
  14. 88
      lib/blockheader.js
  15. 132
      lib/bn.js
  16. 133
      lib/bufferreader.js
  17. 135
      lib/bufferwriter.js
  18. 15
      lib/constants.js
  19. 216
      lib/ecdsa.js
  20. 47
      lib/expmt/aes.js
  21. 34
      lib/expmt/aescbc.js
  22. 138
      lib/expmt/cbc.js
  23. 55
      lib/expmt/ecies.js
  24. 127
      lib/expmt/stealthaddress.js
  25. 88
      lib/expmt/stealthkey.js
  26. 85
      lib/expmt/stealthmessage.js
  27. 69
      lib/expmt/stealthtx.js
  28. 89
      lib/hash.js
  29. 32
      lib/kdf.js
  30. 73
      lib/keypair.js
  31. 94
      lib/message.js
  32. 192
      lib/opcode.js
  33. 50
      lib/point.js
  34. 104
      lib/privkey.js
  35. 125
      lib/pubkey.js
  36. 54
      lib/random.js
  37. 281
      lib/script.js
  38. 169
      lib/signature.js
  39. 155
      lib/transaction.js
  40. 81
      lib/txin.js
  41. 71
      lib/txout.js
  42. 70
      lib/varint.js
  43. 58
      npm-shrinkwrap.json
  44. 79
      package.json
  45. 203
      test/address.js
  46. 85
      test/aes.js
  47. 73
      test/aescbc.js
  48. 98
      test/base58.js
  49. 114
      test/base58check.js
  50. 359
      test/bip32.js
  51. 151
      test/block.js
  52. 124
      test/blockheader.js
  53. 154
      test/bn.js
  54. 274
      test/bufferreader.js
  55. 186
      test/bufferwriter.js
  56. 317
      test/cbc.js
  57. 213
      test/ecdsa.js
  58. 52
      test/ecies.js
  59. 32
      test/examples.js
  60. 115
      test/hash.js
  61. 18
      test/index.html
  62. 39
      test/kdf.js
  63. 165
      test/keypair.js
  64. 107
      test/message.js
  65. 75
      test/opcode.js
  66. 117
      test/point.js
  67. 120
      test/privkey.js
  68. 205
      test/pubkey.js
  69. 61
      test/random.js
  70. 308
      test/script.js
  71. 149
      test/signature.js
  72. 149
      test/stealthaddress.js
  73. 138
      test/stealthkey.js
  74. 139
      test/stealthmessage.js
  75. 68
      test/stealthtx.js
  76. 195
      test/transaction.js
  77. 124
      test/txin.js
  78. 110
      test/txout.js
  79. 123
      test/varint.js

14
.gitignore

@ -1,13 +1,5 @@
build/
node_modules/
*.swp
*.swo
*~
.project
README.html
CONTRIBUTING.html
tags
coverage
.DS_Store
docs
browser/bitcore-dev.js
node_modules
browser/fullnode.js
browser/tests.js

1
.travis.yml

@ -9,4 +9,3 @@ notifications:
- '%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message} (<a href="%{build_url}">Details</a>/<a href="%{compare_url}">Change view</a>)'
format: html
on_success: never

30
LICENSE.md

@ -0,0 +1,30 @@
This software is licensed under the MIT License.
Copyright (c) 2014 Ryan X. Charles <ryanxcharles@gmail.com>
Parts of this software are based on bitcore
Copyright (c) 2014 BitPay Inc.
Parts of this software are based on BitcoinJS
Copyright (c) 2011 Stefan Thomas <justmoon@members.fsf.org>
Parts of this software are based on BitcoinJ
Copyright (c) 2011 Google Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

4
browser/build

@ -0,0 +1,4 @@
#!/bin/bash
browserify index.js -o browser/fullnode.js
ls test/*.js | xargs browserify -o browser/tests.js

25
examples/blockreader.js

@ -0,0 +1,25 @@
var Block = require('../lib/block');
var BufferReader = require('../lib/bufferreader');
var BufferWriter = require('../lib/bufferwriter');
//This example will parse the blocks in a block file.
//To use, pipe in a blk*****.dat file. e.g.:
//cat blk00000.dat | node blockreader.js
var head = null;
process.stdin.on('readable', function() {
if (!head) {
head = process.stdin.read(8);
if (!head)
return;
}
var body = process.stdin.read(head.slice(4).readUInt32LE(0));
if (!body)
return;
var blockbuf = BufferWriter().write(head).write(body).concat();
var block = Block().fromBuffer(blockbuf);
console.log(block.toJSON());
head = null;
process.stdin.unshift(process.stdin.read());
});

21
examples/ecdsa.js

@ -0,0 +1,21 @@
var ECDSA = require('../lib/ecdsa');
var Keypair = require('../lib/keypair');
var Hash = require('../lib/hash');
//ECDSA is the signature algorithm used in bitcoin
//start with a keypair that you will use for signing
var keypair = Keypair().fromRandom();
//a message to be signed (normally you would have the hash of a transaction)
var messagebuf = new Buffer('This is a message I would like to sign');
//calculate a 32 byte hash for use in ECDSA. one way to do that is sha256.
var hashbuf = Hash.sha256(messagebuf);
var sig = ECDSA.sign(hashbuf, keypair);
//Anyone with the public key can verify
var pubkey = keypair.pubkey;
console.log('Valid signature? ' + ECDSA.verify(hashbuf, sig, pubkey));

63
examples/stealthmessage.js

@ -0,0 +1,63 @@
var Pubkey = require('../lib/pubkey');
var Address = require('../lib/address');
var Stealthkey = require('../lib/expmt/stealthkey');
var StealthAddress = require('../lib/expmt/stealthaddress');
var StealthMessage = require('../lib/expmt/stealthmessage');
var Keypair = require('../lib/keypair')
//First, the person receiving must make a stealth key.
var sk = Stealthkey().fromRandom();
//It has an associated stealth address.
var sa = StealthAddress().fromStealthkey(sk);
console.log('Stealth address: ' + sa);
//Now make a message.
var messagebuf = new Buffer('Hello there. Only you know this message is to you, and only you know what it says.');
//Encrypt the message with the stealth address.
var encbuf = StealthMessage.encrypt(messagebuf, sa);
console.log('Hex of the encrypted message: ' + encbuf.toString('hex'));
//Note that the first 20 bytes are a pubkeyhash, which may be interpreted as a bitcoin address.
//This address has never been seen before in public.
var address = Address().set({hashbuf: encbuf.slice(0, 20)});
console.log('The randomly generated address the message is to: ' + address);
//And the next 33 bytes are a nonce public key, which the message is "from".
//It has never been seen before in public.
var pubkey = Pubkey().fromDER(encbuf.slice(20, 20 + 33));
console.log('Nonce public key: ' + pubkey);
//The owner of the stealth key can check to see if it is for them.
console.log('Is the message for me? ' + (StealthMessage.isForMe(encbuf, sk) ? "yes" : "no"));
//The owner can decrypt it.
var messagebuf2 = StealthMessage.decrypt(encbuf, sk);
console.log('Decrypted message: ' + messagebuf2.toString());
//If you do not have the payload privkey, you can still use isForMe.
sk.payloadKeypair.privkey = undefined;
console.log('Without payload privkey, is the message for me? ' + (StealthMessage.isForMe(encbuf, sk) ? "yes" : "no"));
//...but not decrypt
try {
StealthMessage.decrypt(encbuf, sk);
} catch (e) {
console.log("...but without the payload privkey, I can't decrypt.");
}

56
index.js

@ -0,0 +1,56 @@
var fullnode = module.exports;
//main bitcoin library
fullnode.Address = require('./lib/address');
fullnode.Base58 = require('./lib/base58');
fullnode.Base58Check = require('./lib/base58check');
fullnode.BIP32 = require('./lib/bip32');
fullnode.Block = require('./lib/block');
fullnode.Blockheader = require('./lib/blockheader');
fullnode.BN = require('./lib/bn');
fullnode.BufferReader = require('./lib/bufferreader');
fullnode.BufferWriter = require('./lib/bufferwriter');
fullnode.Constants = require('./lib/constants');
fullnode.ECDSA = require('./lib/ecdsa');
fullnode.Hash = require('./lib/hash');
fullnode.KDF = require('./lib/kdf');
fullnode.Keypair = require('./lib/keypair');
fullnode.Message = require('./lib/message');
fullnode.Opcode = require('./lib/opcode');
fullnode.Point = require('./lib/point');
fullnode.Privkey = require('./lib/privkey');
fullnode.Pubkey = require('./lib/pubkey');
fullnode.Random = require('./lib/random');
fullnode.Script = require('./lib/script');
fullnode.Signature = require('./lib/signature');
fullnode.Transaction = require('./lib/transaction');
fullnode.Txin = require('./lib/txin');
fullnode.Txout = require('./lib/txout');
fullnode.Varint = require('./lib/varint');
//experimental, nonstandard, or unstable features
fullnode.expmt = {};
fullnode.expmt.AES = require('./lib/expmt/aes');
fullnode.expmt.AESCBC = require('./lib/expmt/aescbc');
fullnode.expmt.CBC = require('./lib/expmt/cbc');
fullnode.expmt.ECIES = require('./lib/expmt/ecies');
fullnode.expmt.StealthAddress = require('./lib/expmt/stealthaddress');
fullnode.expmt.Stealthkey = require('./lib/expmt/stealthkey');
fullnode.expmt.StealthMessage = require('./lib/expmt/stealthmessage');
fullnode.expmt.StealthTx = require('./lib/expmt/stealthtx');
//dependencies, subject to change
fullnode.deps = {};
fullnode.deps.aes = require('aes');
fullnode.deps.bnjs = require('bn.js');
fullnode.deps.bs58 = require('bs58');
fullnode.deps.Buffer = Buffer;
fullnode.deps.elliptic = require('elliptic');
fullnode.deps.hashjs = require('hash.js');
fullnode.deps.sha512 = require('sha512');
//fullnode.scriptexec = require('lib/scriptexec');
//fullnode.tx = require('lib/tx');
//fullnode.txpartial = require('lib/txpartial');
//fullnode.bip70 = require('lib/bip70');

120
lib/address.js

@ -0,0 +1,120 @@
var base58check = require('./base58check');
var constants = require('./constants');
var Hash = require('./hash');
var Pubkey = require('./pubkey');
var Script = require('./script');
function Address(buf) {
if (!(this instanceof Address))
return new Address(buf);
if (Buffer.isBuffer(buf)) {
this.fromBuffer(buf);
} else if (typeof buf === 'string') {
var str = buf;
this.fromString(str);
} else if (buf) {
var obj = buf;
this.set(obj);
}
};
Address.prototype.set = function(obj) {
this.hashbuf = obj.hashbuf || this.hashbuf || null;
this.networkstr = obj.networkstr || this.networkstr || 'mainnet';
this.typestr = obj.typestr || this.typestr || 'pubkeyhash';
return this;
};
Address.prototype.fromBuffer = function(buf) {
if (buf.length !== 1 + 20)
throw new Error('Address buffers must be exactly 21 bytes');
var version = buf[0];
if (version === constants['mainnet']['pubkeyhash']) {
this.networkstr = 'mainnet';
this.typestr = 'pubkeyhash';
} else if (version === constants['mainnet']['scripthash']) {
this.networkstr = 'mainnet';
this.typestr = 'scripthash';
} else if (version === constants['testnet']['pubkeyhash']) {
this.networkstr = 'testnet';
this.typestr = 'pubkeyhash';
} else if (version === constants['testnet']['scripthash']) {
this.networkstr = 'testnet';
this.typestr = 'scripthash';
} else {
this.networkstr = 'unknown';
this.typestr = 'unknown';
}
this.hashbuf = buf.slice(1);
return this;
};
Address.prototype.fromHashbuf = function(hashbuf, networkstr, typestr) {
if (hashbuf.length !== 20)
throw new Error('hashbuf must be exactly 20 bytes');
this.hashbuf = hashbuf;
this.networkstr = networkstr || 'mainnet';
this.typestr = typestr || 'pubkeyhash';
return this;
};
Address.prototype.fromPubkey = function(pubkey, networkstr) {
this.hashbuf = Hash.sha256ripemd160(pubkey.toBuffer());
this.networkstr = networkstr || 'mainnet';
this.typestr = 'pubkeyhash';
return this;
};
Address.prototype.fromScript = function(script, networkstr) {
this.hashbuf = Hash.sha256ripemd160(script.toBuffer());
this.networkstr = networkstr || 'mainnet';
this.typestr = 'scripthash';
return this;
};
Address.prototype.fromString = function(str) {
var buf = base58check.decode(str);
return this.fromBuffer(buf);
}
Address.isValid = function(addrstr) {
try {
var address = new Address().fromString(addrstr);
} catch (e) {
return false;
}
return address.isValid();
};
Address.prototype.isValid = function() {
try {
this.validate();
return true;
} catch (e) {
return false;
}
};
Address.prototype.toBuffer = function() {
version = new Buffer([constants[this.networkstr][this.typestr]]);
var buf = Buffer.concat([version, this.hashbuf]);
return buf;
};
Address.prototype.toString = function() {
return base58check.encode(this.toBuffer());
};
Address.prototype.validate = function() {
if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 20)
throw new Error('hash must be a buffer of 20 bytes');
if (this.networkstr !== 'mainnet' && this.networkstr !== 'testnet')
throw new Error('networkstr must be "mainnet" or "testnet"');
if (this.typestr !== 'pubkeyhash' && this.typestr !== 'scripthash')
throw new Error('typestr must be "pubkeyhash" or "scripthash"');
return this;
};
module.exports = Address;

53
lib/base58.js

@ -0,0 +1,53 @@
var bs58 = require('bs58');
var Base58 = function Base58(obj) {
if (!(this instanceof Base58))
return new Base58(obj);
if (Buffer.isBuffer(obj)) {
var buf = obj;
this.fromBuffer(buf);
} else if (typeof obj === 'string') {
var str = obj;
this.fromString(str);
} else if (obj) {
this.set(obj);
}
};
Base58.prototype.set = function(obj) {
this.buf = obj.buf || this.buf || undefined;
return this;
};
Base58.encode = function(buf) {
if (!Buffer.isBuffer(buf))
throw new Error('Input should be a buffer');
return bs58.encode(buf);
};
Base58.decode = function(str) {
if (typeof str !== 'string')
throw new Error('Input should be a string');
return bs58.decode(str);
};
Base58.prototype.fromBuffer = function(buf) {
this.buf = buf;
return this;
};
Base58.prototype.fromString = function(str) {
var buf = Base58.decode(str);
this.buf = buf;
return this;
};
Base58.prototype.toBuffer = function() {
return this.buf;
};
Base58.prototype.toString = function() {
return Base58.encode(this.buf);
};
module.exports = Base58;

73
lib/base58check.js

@ -0,0 +1,73 @@
var base58 = require('./base58');
var sha256sha256 = require('./hash').sha256sha256;
var Base58Check = function Base58Check(obj) {
if (!(this instanceof Base58Check))
return new Base58Check(obj);
if (Buffer.isBuffer(obj)) {
var buf = obj;
this.fromBuffer(buf);
} else if (typeof obj === 'string') {
var str = obj;
this.fromString(str);
} else if (obj) {
this.set(obj);
}
};
Base58Check.prototype.set = function(obj) {
this.buf = obj.buf || this.buf || undefined;
return this;
};
Base58Check.decode = function(s) {
if (typeof s !== 'string')
throw new Error('Input must be a string');
var buf = base58.decode(s);
if (buf.length < 4)
throw new Error("Input string too short");
var data = buf.slice(0, -4);
var csum = buf.slice(-4);
var hash = sha256sha256(data);
var hash4 = hash.slice(0, 4);
if (csum.toString('hex') !== hash4.toString('hex'))
throw new Error("Checksum mismatch");
return data;
};
Base58Check.encode = function(buf) {
if (!Buffer.isBuffer(buf))
throw new Error('Input must be a buffer');
var checkedBuf = new Buffer(buf.length + 4);
var hash = sha256sha256(buf);
buf.copy(checkedBuf);
hash.copy(checkedBuf, buf.length);
return base58.encode(checkedBuf);
};
Base58Check.prototype.fromBuffer = function(buf) {
this.buf = buf;
return this;
};
Base58Check.prototype.fromString = function(str) {
var buf = Base58Check.decode(str);
this.buf = buf;
return this;
};
Base58Check.prototype.toBuffer = function() {
return this.buf;
};
Base58Check.prototype.toString = function() {
return Base58Check.encode(this.buf);
};
module.exports = Base58Check;

326
lib/bip32.js

@ -0,0 +1,326 @@
var Base58Check = require('./base58check');
var Hash = require('./hash');
var Keypair = require('./keypair');
var Pubkey = require('./pubkey');
var Privkey = require('./privkey');
var Point = require('./point');
var Random = require('./random');
var BN = require('./bn');
var constants = require('./constants');
var BIP32 = function BIP32(obj) {
if (!(this instanceof BIP32))
return new BIP32(obj);
if (typeof obj === 'string') {
var str = obj;
this.fromString(str);
} else if (obj ) {
this.set(obj);
}
}
BIP32.prototype.set = function(obj) {
this.version = typeof obj.version !== 'undefined' ? obj.version : this.version;
this.depth = typeof obj.depth !== 'undefined' ? obj.depth : this.depth;
this.parentfingerprint = obj.parentfingerprint || this.parentfingerprint;
this.childindex = obj.childindex || this.childindex;
this.chaincode = obj.chaincode || this.chaincode;
this.keypair = obj.keypair || this.keypair;
this.hasprivkey = typeof obj.hasprivkey !== 'undefined' ? obj.hasprivkey : this.hasprivkey;
this.pubkeyhash = obj.pubkeyhash || this.pubkeyhash;
this.xpubkey = obj.xpubkey || this.xpubkey;
this.xprivkey = obj.xprivkey || this.xprivkey;
return this;
};
BIP32.prototype.fromRandom = function(networkstr) {
if (!networkstr)
networkstr = 'mainnet';
this.version = constants[networkstr].bip32privkey;
this.depth = 0x00;
this.parentfingerprint = new Buffer([0, 0, 0, 0]);
this.childindex = new Buffer([0, 0, 0, 0]);
this.chaincode = Random.getRandomBuffer(32);
this.keypair = (new Keypair()).fromRandom();
this.hasprivkey = true;
this.pubkeyhash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer());
this.buildxpubkey();
this.buildxprivkey();
};
BIP32.prototype.fromString = function(str) {
var bytes = Base58Check.decode(str);
this.initFromBytes(bytes);
return this;
};
BIP32.prototype.fromSeed = function(bytes, networkstr) {
if (!networkstr)
networkstr = 'mainnet';
if (!Buffer.isBuffer(bytes))
throw new Error('bytes must be a buffer');
if (bytes.length < 128 / 8)
throw new Error('Need more than 128 bytes of entropy');
if (bytes.length > 512 / 8)
throw new Error('More than 512 bytes of entropy is nonstandard');
var hash = Hash.sha512hmac(bytes, new Buffer('Bitcoin seed'));
this.depth = 0x00;
this.parentfingerprint = new Buffer([0, 0, 0, 0]);
this.childindex = new Buffer([0, 0, 0, 0]);
this.chaincode = hash.slice(32, 64);
this.version = constants[networkstr].bip32privkey;
this.keypair = new Keypair();
this.keypair.privkey = new Privkey({bn: BN().fromBuffer(hash.slice(0, 32))});
this.keypair.privkey2pubkey();
this.hasprivkey = true;
this.pubkeyhash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer());
this.buildxpubkey();
this.buildxprivkey();
return this;
};
BIP32.prototype.initFromBytes = function(bytes) {
// Both pub and private extended keys are 78 bytes
if (bytes.length != 78)
throw new Error('not enough data');
this.version = bytes.slice(0, 4).readUInt32BE(0);
this.depth = bytes.slice(4, 5).readUInt8(0);
this.parentfingerprint = bytes.slice(5, 9);
this.childindex = bytes.slice(9, 13).readUInt32BE(0);
this.chaincode = bytes.slice(13, 45);
var keyBytes = bytes.slice(45, 78);
var isPrivate =
(this.version == constants.mainnet.bip32privkey ||
this.version == constants.testnet.bip32privkey);
var isPublic =
(this.version == constants.mainnet.bip32pubkey ||
this.version == constants.testnet.bip32pubkey);
if (isPrivate && keyBytes[0] == 0) {
this.keypair = new Keypair();
this.keypair.privkey = new Privkey({bn: BN().fromBuffer(keyBytes.slice(1, 33))});
this.keypair.privkey2pubkey();
this.pubkeyhash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer());
this.hasprivkey = true;
} else if (isPublic && (keyBytes[0] == 0x02 || keyBytes[0] == 0x03)) {
this.keypair = new Keypair();
this.keypair.pubkey = (new Pubkey()).fromDER(keyBytes);
this.pubkeyhash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer());
this.hasprivkey = false;
} else {
throw new Error('Invalid key');
}
this.buildxpubkey();
this.buildxprivkey();
}
BIP32.prototype.buildxpubkey = function() {
this.xpubkey = new Buffer([]);
var v = null;
switch (this.version) {
case constants.mainnet.bip32pubkey:
case constants.mainnet.bip32privkey:
v = constants.mainnet.bip32pubkey;
break;
case constants.testnet.bip32pubkey:
case constants.testnet.bip32privkey:
v = constants.testnet.bip32pubkey;
break;
default:
throw new Error('Unknown version');
}
// Version
this.xpubkey = Buffer.concat([
new Buffer([v >> 24]),
new Buffer([(v >> 16) & 0xff]),
new Buffer([(v >> 8) & 0xff]),
new Buffer([v & 0xff]),
new Buffer([this.depth]),
this.parentfingerprint,
new Buffer([this.childindex >>> 24]),
new Buffer([(this.childindex >>> 16) & 0xff]),
new Buffer([(this.childindex >>> 8) & 0xff]),
new Buffer([this.childindex & 0xff]),
this.chaincode,
this.keypair.pubkey.toBuffer()
]);
}
BIP32.prototype.xpubkeyString = function(format) {
if (format === undefined || format === 'base58') {
return Base58Check.encode(this.xpubkey);
} else if (format === 'hex') {
return this.xpubkey.toString('hex');
} else {
throw new Error('bad format');
}
}
BIP32.prototype.buildxprivkey = function() {
if (!this.hasprivkey) return;
this.xprivkey = new Buffer([]);
var v = this.version;
this.xprivkey = Buffer.concat([
new Buffer([v >> 24]),
new Buffer([(v >> 16) & 0xff]),
new Buffer([(v >> 8) & 0xff]),
new Buffer([v & 0xff]),
new Buffer([this.depth]),
this.parentfingerprint,
new Buffer([this.childindex >>> 24]),
new Buffer([(this.childindex >>> 16) & 0xff]),
new Buffer([(this.childindex >>> 8) & 0xff]),
new Buffer([this.childindex & 0xff]),
this.chaincode,
new Buffer([0]),
this.keypair.privkey.bn.toBuffer({size: 32})
]);
}
BIP32.prototype.xprivkeyString = function(format) {
if (format === undefined || format === 'base58') {
return Base58Check.encode(this.xprivkey);
} else if (format === 'hex') {
return this.xprivkey.toString('hex');
} else {
throw new Error('bad format');
}
}
BIP32.prototype.derive = function(path) {
var e = path.split('/');
// Special cases:
if (path == 'm' || path == 'M' || path == 'm\'' || path == 'M\'')
return this;
var bip32 = this;
for (var i in e) {
var c = e[i];
if (i == 0) {
if (c != 'm') throw new Error('invalid path');
continue;
}
if (parseInt(c.replace("'", "")).toString() !== c.replace("'", ""))
throw new Error('invalid path');
var usePrivate = (c.length > 1) && (c[c.length - 1] == '\'');
var childindex = parseInt(usePrivate ? c.slice(0, c.length - 1) : c) & 0x7fffffff;
if (usePrivate)
childindex += 0x80000000;
bip32 = bip32.deriveChild(childindex);
}
return bip32;
}
BIP32.prototype.deriveChild = function(i) {
if (typeof i !== 'number')
throw new Error('i must be a number');
var ib = [];
ib.push((i >> 24) & 0xff);
ib.push((i >> 16) & 0xff);
ib.push((i >> 8) & 0xff);
ib.push(i & 0xff);
ib = new Buffer(ib);
var usePrivate = (i & 0x80000000) != 0;
var isPrivate =
(this.version == constants.mainnet.bip32privkey ||
this.version == constants.testnet.bip32privkey);
if (usePrivate && (!this.hasprivkey || !isPrivate))
throw new Error('Cannot do private key derivation without private key');
var ret = null;
if (this.hasprivkey) {
var data = null;
if (usePrivate) {
data = Buffer.concat([new Buffer([0]), this.keypair.privkey.bn.toBuffer({size: 32}), ib]);
} else {
data = Buffer.concat([this.keypair.pubkey.toBuffer({size: 32}), ib]);
}
var hash = Hash.sha512hmac(data, this.chaincode);
var il = BN().fromBuffer(hash.slice(0, 32), {size: 32});
var ir = hash.slice(32, 64);
// ki = IL + kpar (mod n).
var k = il.add(this.keypair.privkey.bn).mod(Point.getN());
ret = new BIP32();
ret.chaincode = ir;
ret.keypair = new Keypair();
ret.keypair.privkey = new Privkey({bn: k});
ret.keypair.privkey2pubkey();
ret.hasprivkey = true;
} else {
var data = Buffer.concat([this.keypair.pubkey.toBuffer(), ib]);
var hash = Hash.sha512hmac(data, this.chaincode);
var il = BN().fromBuffer(hash.slice(0, 32));
var ir = hash.slice(32, 64);
// Ki = (IL + kpar)*G = IL*G + Kpar
var ilG = Point.getG().mul(il);
var Kpar = this.keypair.pubkey.point;
var Ki = ilG.add(Kpar);
var newpub = new Pubkey();
newpub.point = Ki;
ret = new BIP32();
ret.chaincode = ir;
var keypair = new Keypair();
keypair.pubkey = newpub;
ret.keypair = keypair;
ret.hasprivkey = false;
}
ret.childindex = i;
ret.parentfingerprint = this.pubkeyhash.slice(0, 4);
ret.version = this.version;
ret.depth = this.depth + 1;
ret.pubkeyhash = Hash.sha256ripemd160(ret.keypair.pubkey.toBuffer());
ret.buildxpubkey();
ret.buildxprivkey();
return ret;
}
BIP32.prototype.toString = function() {
var isPrivate =
(this.version == constants.mainnet.bip32privkey ||
this.version == constants.testnet.bip32privkey);
if (isPrivate)
return this.xprivkeyString();
else
return this.xpubkeyString();
};
module.exports = BIP32;

108
lib/block.js

@ -0,0 +1,108 @@
var Transaction = require('./transaction');
var BufferReader = require('./bufferreader');
var BufferWriter = require('./bufferwriter');
var Blockheader = require('./blockheader');
var Varint = require('./varint');
var Hash = require('./hash');
var Block = function Block(magicnum, blocksize, blockheader, txsvi, txs) {
if (!(this instanceof Block))
return new Block(magicnum, blocksize, blockheader, txsvi, txs);
if (typeof magicnum === 'number') {
this.set({
magicnum: magicnum,
blocksize: blocksize,
blockheader: blockheader,
txsvi: txsvi,
txs: txs
});
} else if (Buffer.isBuffer(magicnum)) {
var blockbuf = magicnum;
this.fromBuffer(blockbuf);
} else if (magicnum) {
var obj = magicnum;
}
};
Block.prototype.set = function(obj) {
this.magicnum = typeof obj.magicnum !== 'undefined' ? obj.magicnum : this.magicnum;
this.blocksize = typeof obj.blocksize !== 'undefined' ? obj.blocksize : this.blocksize;
this.blockheader = obj.blockheader || this.blockheader;
this.txsvi = obj.txsvi || this.txsvi;
this.txs = obj.txs || this.txs;
return this;
};
Block.prototype.fromJSON = function(json) {
var txs = [];
json.txs.forEach(function(tx) {
txs.push(Transaction().fromJSON(tx));
});
this.set({
magicnum: json.magicnum,
blocksize: json.blocksize,
blockheader: Blockheader().fromJSON(json.blockheader),
txsvi: Varint().fromJSON(json.txsvi),
txs: txs
});
return this;
};
Block.prototype.toJSON = function() {
var txs = [];
this.txs.forEach(function(tx) {
txs.push(tx.toJSON());
});
return {
magicnum: this.magicnum,
blocksize: this.blocksize,
blockheader: this.blockheader.toJSON(),
txsvi: this.txsvi.toJSON(),
txs: txs
};
};
Block.prototype.fromBuffer = function(buf) {
return this.fromBufferReader(BufferReader(buf));
};
Block.prototype.fromBufferReader = function(br) {
this.magicnum = br.readUInt32LE();
this.blocksize = br.readUInt32LE();
this.blockheader = Blockheader().fromBufferReader(br);
this.txsvi = Varint(br.readVarintBuf());
var txslen = this.txsvi.toNumber();
this.txs = [];
for (var i = 0; i < txslen; i++) {
this.txs.push(Transaction().fromBufferReader(br));
}
return this;
};
Block.prototype.toBuffer = function() {
return this.toBufferWriter().concat();
};
Block.prototype.toBufferWriter = function(bw) {
if (!bw)
bw = new BufferWriter();
bw.writeUInt32LE(this.magicnum);
bw.writeUInt32LE(this.blocksize);
bw.write(this.blockheader.toBuffer());
bw.write(this.txsvi.buf);
var txslen = this.txsvi.toNumber();
for (var i = 0; i < txslen; i++) {
this.txs[i].toBufferWriter(bw);
}
return bw;
};
Block.prototype.hash = function() {
return Hash.sha256sha256(this.blockheader.toBuffer());
};
Block.prototype.id = function() {
return BufferReader(this.hash()).reverse().read();
};
module.exports = Block;

88
lib/blockheader.js

@ -0,0 +1,88 @@
var BufferReader = require('./bufferreader');
var BufferWriter = require('./bufferwriter');
var Blockheader = function Blockheader(version, prevblockidbuf, merklerootbuf, time, bits, nonce) {
if (!(this instanceof Blockheader))
return new Blockheader(version, prevblockidbuf, merklerootbuf, time, bits, nonce);
if (typeof version === 'number') {
this.set({
version: version,
prevblockidbuf: prevblockidbuf,
merklerootbuf: merklerootbuf,
time: time,
bits: bits,
nonce: nonce
});
} else if (Buffer.isBuffer(version)) {
var bhbuf = version;
this.fromBuffer(bhbuf);
} else if (version) {
var obj = version;
this.set(obj);
}
}
Blockheader.prototype.set = function(obj) {
this.version = typeof obj.version !== 'undefined' ? obj.version : this.version;
this.prevblockidbuf = obj.prevblockidbuf || this.prevblockidbuf;
this.merklerootbuf = obj.merklerootbuf || this.merklerootbuf;
this.time = typeof obj.time !== 'undefined' ? obj.time : this.time;
this.bits = typeof obj.bits !== 'undefined' ? obj.bits : this.bits;
this.nonce = typeof obj.nonce !== 'undefined' ? obj.nonce : this.nonce;
return this;
};
Blockheader.prototype.fromJSON = function(json) {
this.set({
version: json.version,
prevblockidbuf: new Buffer(json.prevblockidbuf, 'hex'),
merklerootbuf: new Buffer(json.merklerootbuf, 'hex'),
time: json.time,
bits: json.bits,
nonce: json.nonce
});
return this;
};
Blockheader.prototype.toJSON = function() {
return {
version: this.version,
prevblockidbuf: this.prevblockidbuf.toString('hex'),
merklerootbuf: this.merklerootbuf.toString('hex'),
time: this.time,
bits: this.bits,
nonce: this.nonce
};
};
Blockheader.prototype.fromBuffer = function(buf) {
return this.fromBufferReader(BufferReader(buf));
};
Blockheader.prototype.fromBufferReader = function(br) {
this.version = br.readUInt32LE();
this.prevblockidbuf = br.read(32);
this.merklerootbuf = br.read(32);
this.time = br.readUInt32LE();
this.bits = br.readUInt32LE();
this.nonce = br.readUInt32LE();
return this;
};
Blockheader.prototype.toBuffer = function() {
return this.toBufferWriter().concat();
};
Blockheader.prototype.toBufferWriter = function(bw) {
if (!bw)
bw = new BufferWriter();
bw.writeUInt32LE(this.version);
bw.write(this.prevblockidbuf);
bw.write(this.merklerootbuf);
bw.writeUInt32LE(this.time);
bw.writeUInt32LE(this.bits);
bw.writeUInt32LE(this.nonce);
return bw;
};
module.exports = Blockheader;

132
lib/bn.js

@ -0,0 +1,132 @@
var _BN = require('bn.js');
var BN = function BN_extended(n) {
if (!(this instanceof BN_extended)) {
return new BN(n);
}
arguments[0] = n;
return _BN.apply(this, arguments);
};
module.exports = BN;
BN.prototype = _BN.prototype;
var reversebuf = function(buf, nbuf) {
for (var i = 0; i < buf.length; i++) {
nbuf[i] = buf[buf.length-1-i];
}
};
BN.prototype.toJSON = function() {
return this.toString();
};
BN.prototype.fromJSON = function(str) {
var bn = BN(str);
bn.copy(this);
return this;
};
BN.prototype.fromString = function(str) {
var bn = BN(str);
bn.copy(this);
return this;
};
BN.fromBuffer = function(buf, opts) {
if (typeof opts !== 'undefined' && opts.endian === 'little') {
var nbuf = new Buffer(buf.length);
reversebuf(buf, nbuf);
buf = nbuf;
}
var hex = buf.toString('hex');
if (hex.length % 2)
hex = "0" + hex;
var bn = new BN(hex, 16);
return bn;
};
BN.prototype.fromBuffer = function(buf, opts) {
var bn = BN.fromBuffer(buf, opts);
bn.copy(this);
return this;
};
BN.prototype.toBuffer = function(opts) {
var buf;
if (opts && opts.size) {
var hex = this.toString(16);
if (hex.length % 2)
hex = "0" + hex;
var natlen = hex.length/2;
buf = new Buffer(hex, 'hex');
if (natlen == opts.size)
buf = buf;
else if (natlen > opts.size) {
buf = buf.slice(natlen - buf.length, buf.length);
}
else if (natlen < opts.size) {
var rbuf = new Buffer(opts.size);
//rbuf.fill(0);
for (var i = 0; i < buf.length; i++)
rbuf[rbuf.length-1-i] = buf[buf.length-1-i];
for (var i = 0; i < opts.size - natlen; i++)
rbuf[i] = 0;
buf = rbuf;
}
}
else {
var hex = this.toString(16);
if (hex.length % 2)
hex = "0" + hex;
buf = new Buffer(hex, 'hex');
}
if (typeof opts !== 'undefined' && opts.endian === 'little') {
var nbuf = new Buffer(buf.length);
reversebuf(buf, nbuf);
buf = nbuf;
}
return buf;
};
function decorate(name) {
BN.prototype['_' + name] = _BN.prototype[name];
var f = function(b) {
if (typeof b === 'string')
b = new _BN(b);
else if (typeof b === 'number')
b = new _BN(b.toString());
return this['_' + name](b);
};
BN.prototype[name] = f;
};
_BN.prototype.gt = function(b) {
return this.cmp(b) > 0;
};
_BN.prototype.lt = function(b) {
return this.cmp(b) < 0;
};
decorate('add');
decorate('sub');
decorate('mul');
decorate('mod');
decorate('div');
decorate('cmp');
decorate('gt');
decorate('lt');
BN.prototype.toNumber = function() {
return parseInt(this['toString'](10), 10);
};
module.exports = BN;

133
lib/bufferreader.js

@ -0,0 +1,133 @@
var BN = require('./bn');
var BufferReader = function BufferReader(buf) {
if (!(this instanceof BufferReader))
return new BufferReader(buf);
if (Buffer.isBuffer(buf)) {
this.set({buf: buf});
}
else if (buf) {
var obj = buf;
this.set(obj);
}
};
BufferReader.prototype.set = function(obj) {
this.buf = obj.buf || this.buf || undefined;
this.pos = obj.pos || this.pos || 0;
return this;
};
BufferReader.prototype.eof = function() {
return this.pos >= this.buf.length;
};
BufferReader.prototype.read = function(len) {
if (!len)
var len = this.buf.length;
var buf = this.buf.slice(this.pos, this.pos + len);
this.pos = this.pos + len;
return buf;
};
BufferReader.prototype.readUInt8 = function() {
var val = this.buf.readUInt8(this.pos);
this.pos = this.pos + 1;
return val;
};
BufferReader.prototype.readUInt16BE = function() {
var val = this.buf.readUInt16BE(this.pos);
this.pos = this.pos + 2;
return val;
};
BufferReader.prototype.readUInt16LE = function() {
var val = this.buf.readUInt16LE(this.pos);
this.pos = this.pos + 2;
return val;
};
BufferReader.prototype.readUInt32BE = function() {
var val = this.buf.readUInt32BE(this.pos);
this.pos = this.pos + 4;
return val;
};
BufferReader.prototype.readUInt32LE = function() {
var val = this.buf.readUInt32LE(this.pos);
this.pos = this.pos + 4;
return val;
};
BufferReader.prototype.readUInt64BEBN = function() {
var buf = this.buf.slice(this.pos, this.pos + 8);
var bn = BN().fromBuffer(buf);
this.pos = this.pos + 8;
return bn;
};
BufferReader.prototype.readUInt64LEBN = function() {
var buf = this.buf.slice(this.pos, this.pos + 8);
var reversebuf = BufferReader({buf: buf}).reverse().read();
var bn = BN().fromBuffer(reversebuf);
this.pos = this.pos + 8;
return bn;
};
BufferReader.prototype.readVarintNum = function() {
var first = this.readUInt8();
switch (first) {
case 0xFD:
return this.readUInt16LE();
case 0xFE:
return this.readUInt32LE();
case 0xFF:
var bn = this.readUInt64LEBN();
var n = bn.toNumber();
if (n <= Math.pow(2, 53))
return n;
else
throw new Error('number too large to retain precision - use readVarintBN');
default:
return first;
}
};
BufferReader.prototype.readVarintBuf = function() {
var first = this.buf.readUInt8(this.pos);
switch (first) {
case 0xFD:
return this.read(1 + 2);
case 0xFE:
return this.read(1 + 4);
case 0xFF:
return this.read(1 + 8);
default:
return this.read(1);
}
};
BufferReader.prototype.readVarintBN = function() {
var first = this.readUInt8();
switch (first) {
case 0xFD:
return BN(this.readUInt16LE());
case 0xFE:
return BN(this.readUInt32LE());
case 0xFF:
return this.readUInt64LEBN();
default:
return BN(first);
}
};
BufferReader.prototype.reverse = function() {
var buf = new Buffer(this.buf.length);
for (var i = 0; i < buf.length; i++)
buf[i] = this.buf[this.buf.length - 1 - i];
this.buf = buf;
return this;
};
module.exports = BufferReader;

135
lib/bufferwriter.js

@ -0,0 +1,135 @@
var BN = require('./bn');
var BufferWriter = function BufferWriter(obj) {
if (!(this instanceof BufferWriter))
return new BufferWriter(obj);
if (obj)
this.set(obj);
else
this.bufs = [];
};
BufferWriter.prototype.set = function(obj) {
this.bufs = obj.bufs || this.bufs || [];
return this;
};
BufferWriter.prototype.toBuffer = function() {
return this.concat();
};
BufferWriter.prototype.concat = function() {
return Buffer.concat(this.bufs);
};
BufferWriter.prototype.write = function(buf) {
this.bufs.push(buf);
return this;
};
BufferWriter.prototype.writeUInt8 = function(n) {
var buf = new Buffer(1);
buf.writeUInt8(n, 0);
this.write(buf);
return this;
};
BufferWriter.prototype.writeUInt16BE = function(n) {
var buf = new Buffer(2);
buf.writeUInt16BE(n, 0);
this.write(buf);
return this;
};
BufferWriter.prototype.writeUInt16LE = function(n) {
var buf = new Buffer(2);
buf.writeUInt16LE(n, 0);
this.write(buf);
return this;
};
BufferWriter.prototype.writeUInt32BE = function(n) {
var buf = new Buffer(4);
buf.writeUInt32BE(n, 0);
this.write(buf);
return this;
};
BufferWriter.prototype.writeUInt32LE = function(n) {
var buf = new Buffer(4);
buf.writeUInt32LE(n, 0);
this.write(buf);
return this;
};
BufferWriter.prototype.writeUInt64BEBN = function(bn) {
var buf = bn.toBuffer({size: 8});
this.write(buf);
return this;
};
BufferWriter.prototype.writeUInt64LEBN = function(bn) {
var buf = bn.toBuffer({size: 8});
var reversebuf = new Buffer(Array.apply(new Array(), buf).reverse());
this.write(reversebuf);
return this;
};
BufferWriter.prototype.writeVarintNum = function(n) {
var buf = BufferWriter.varintBufNum(n);
this.write(buf);
return this;
};
BufferWriter.prototype.writeVarintBN = function(bn) {
var buf = BufferWriter.varintBufBN(bn);
this.write(buf);
return this;
};
BufferWriter.varintBufNum = function(n) {
var buf = undefined;
if (n < 253) {
buf = new Buffer(1);
buf.writeUInt8(n, 0);
} else if (n < 0x10000) {
buf = new Buffer(1 + 2);
buf.writeUInt8(253, 0);
buf.writeUInt16LE(n, 1);
} else if (n < 0x100000000) {
buf = new Buffer(1 + 4);
buf.writeUInt8(254, 0);
buf.writeUInt32LE(n, 1);
} else {
buf = new Buffer(1 + 8);
buf.writeUInt8(255, 0);
buf.writeInt32LE(n & -1, 1);
buf.writeUInt32LE(Math.floor(n / 0x100000000), 5);
}
return buf;
};
BufferWriter.varintBufBN = function(bn) {
var buf = undefined;
var n = bn.toNumber();
if (n < 253) {
buf = new Buffer(1);
buf.writeUInt8(n, 0);
} else if (n < 0x10000) {
buf = new Buffer(1 + 2);
buf.writeUInt8(253, 0);
buf.writeUInt16LE(n, 1);
} else if (n < 0x100000000) {
buf = new Buffer(1 + 4);
buf.writeUInt8(254, 0);
buf.writeUInt32LE(n, 1);
} else {
var bw = new BufferWriter();
bw.writeUInt8(255);
bw.writeUInt64LEBN(bn);
var buf = bw.concat();
}
return buf;
};
module.exports = BufferWriter;

15
lib/constants.js

@ -0,0 +1,15 @@
exports.mainnet = {
pubkeyhash: 0x00,
privkey: 0x80,
scripthash: 0x05,
bip32pubkey: 0x0488b21e,
bip32privkey: 0x0488ade4,
};
exports.testnet = {
pubkeyhash: 0x6f,
privkey: 0xef,
scripthash: 0xc4,
bip32pubkey: 0x043587cf,
bip32privkey: 0x04358394,
};

216
lib/ecdsa.js

@ -0,0 +1,216 @@
var BN = require('./bn');
var Point = require('./point');
var Signature = require('./signature');
var Keypair = require('./keypair');
var Pubkey = require('./pubkey');
var Random = require('./random');
var ECDSA = function ECDSA(obj) {
if (!(this instanceof ECDSA))
return new ECDSA(obj);
if (obj)
this.set(obj);
};
ECDSA.prototype.set = function(obj) {
this.hashbuf = obj.hashbuf || this.hashbuf;
this.keypair = obj.keypair || this.keypair;
this.sig = obj.sig || this.sig;
this.k = obj.k || this.k;
this.verified = obj.verified || this.verified;
return this;
};
ECDSA.prototype.calci = function() {
for (var i = 0; i < 4; i++) {
this.sig.i = i;
try {
var Qprime = this.sig2pubkey();
} catch (e) {
continue;
}
if (Qprime.point.eq(this.keypair.pubkey.point)) {
this.sig.compressed = this.keypair.pubkey.compressed;
return this;
}
}
this.sig.i = undefined;
throw new Error('Unable to find valid recovery factor');
};
ECDSA.prototype.fromString = function(str) {
var obj = JSON.parse(str);
if (obj.hashbuf)
this.hashbuf = new Buffer(obj.hashbuf, 'hex');
if (obj.keypair)
this.keypair = Keypair().fromString(obj.keypair);
if (obj.sig)
this.sig = Signature().fromString(obj.sig);
if (obj.k)
this.k = BN(obj.k, 10);
return this;
};
ECDSA.prototype.randomK = function() {
var N = Point.getN();
var k;
do {
k = BN().fromBuffer(Random.getRandomBuffer(32));
} while (!(k.lt(N) && k.gt(0)));
this.k = k;
return this;
};
// Information about public key recovery:
// https://bitcointalk.org/index.php?topic=6430.0
// http://stackoverflow.com/questions/19665491/how-do-i-get-an-ecdsa-public-key-from-just-a-bitcoin-signature-sec1-4-1-6-k
ECDSA.prototype.sig2pubkey = function() {
var i = this.sig.i;
if (!(i === 0 || i === 1 || i === 2 || i === 3))
throw new Error('i must be equal to 0, 1, 2, or 3');
var e = BN().fromBuffer(this.hashbuf);
var r = this.sig.r;
var s = this.sig.s;
// A set LSB signifies that the y-coordinate is odd
var isYOdd = i & 1;
// The more significant bit specifies whether we should use the
// first or second candidate key.
var isSecondKey = i >> 1;
var n = Point.getN();
var G = Point.getG();
// 1.1 Let x = r + jn
var x = isSecondKey ? r.add(n) : r;
var R = Point.fromX(isYOdd, x);
// 1.4 Check that nR is at infinity
var nR = R.mul(n);
if (!nR.isInfinity())
throw new Error('nR is not a valid curve point');
// Compute -e from e
var eNeg = e.neg().mod(n);
// 1.6.1 Compute Q = r^-1 (sR - eG)
// Q = r^-1 (sR + -eG)
var rInv = r.invm(n);
//var Q = R.multiplyTwo(s, G, eNeg).mul(rInv);
var Q = R.mul(s).add(G.mul(eNeg)).mul(rInv);
var pubkey = new Pubkey({point: Q});
pubkey.compressed = this.sig.compressed;
pubkey.validate();
return pubkey;
};
ECDSA.prototype.sigError = function() {
if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 32)
return 'hashbuf must be a 32 byte buffer';
try {
this.keypair.pubkey.validate();
} catch (e) {
return 'Invalid pubkey: ' + e;
}
var r = this.sig.r;
var s = this.sig.s;
if (!(r.gt(0) && r.lt(Point.getN()))
|| !(s.gt(0) && s.lt(Point.getN())))
return 'r and s not in range';
var e = BN().fromBuffer(this.hashbuf);
var n = Point.getN();
var sinv = s.invm(n);
var u1 = sinv.mul(e).mod(n);
var u2 = sinv.mul(r).mod(n);
var p = Point.getG().mulAdd(u1, this.keypair.pubkey.point, u2);
if (p.isInfinity())
return 'p is infinity';
if (!(p.getX().mod(n).cmp(r) === 0))
return 'Invalid signature';
else
return false;
};
ECDSA.prototype.sign = function() {
var hashbuf = this.hashbuf;
var privkey = this.keypair.privkey;
var k = this.k;
var d = privkey.bn;
if (!k)
throw new Error('You must specify k - perhaps you should run signRandomK instead');
if (!hashbuf || !privkey || !d)
throw new Error('invalid parameters');
if (!Buffer.isBuffer(hashbuf) || hashbuf.length !== 32)
throw new Error('hashbuf must be a 32 byte buffer');
var N = Point.getN();
var G = Point.getG();
var e = BN().fromBuffer(hashbuf);
do {
var Q = G.mul(k);
var r = Q.x.mod(N);
var s = k.invm(N).mul(e.add(d.mul(r))).mod(N);
} while (r.cmp(0) <= 0 || s.cmp(0) <= 0);
this.sig = new Signature({r: r, s: s, compressed: this.keypair.pubkey.compressed});
return this.sig;
};
ECDSA.prototype.signRandomK = function() {
var k = this.randomK();
return this.sign();
};
ECDSA.prototype.toString = function() {
var obj = {};
if (this.hashbuf)
obj.hashbuf = this.hashbuf.toString('hex');
if (this.keypair)
obj.keypair = this.keypair.toString();
if (this.sig)
obj.sig = this.sig.toString();
if (this.k)
obj.k = this.k.toString();
return JSON.stringify(obj);
};
ECDSA.prototype.verify = function() {
if (!this.sigError())
return true;
else
return false;
};
ECDSA.sign = function(hashbuf, keypair) {
return ECDSA().set({
hashbuf: hashbuf,
keypair: keypair
}).signRandomK();
};
ECDSA.verify = function(hashbuf, sig, pubkey) {
return ECDSA().set({
hashbuf: hashbuf,
sig: sig,
keypair: Keypair().set({pubkey: pubkey})
}).verify();
};
module.exports = ECDSA;

47
lib/expmt/aes.js

@ -0,0 +1,47 @@
var aes = require('aes');
var AES = function AES() {
};
AES.encrypt = function(messagebuf, keybuf) {
var key = AES.buf2words(keybuf);
var message = AES.buf2words(messagebuf);
var a = new aes(key);
var enc = a.encrypt(message);
var encbuf = AES.words2buf(enc);
return encbuf;
};
AES.decrypt = function(encbuf, keybuf) {
var enc = AES.buf2words(encbuf);
var key = AES.buf2words(keybuf);
var a = new aes(key);
var message = a.decrypt(enc);
var messagebuf = AES.words2buf(message);
return messagebuf;
};
AES.buf2words = function(buf) {
if (buf.length % 4)
throw new Error('buf length must be a multiple of 4');
var words = [];
for (var i = 0; i < buf.length / 4; i++) {
words.push(buf.readUInt32BE(i * 4));
};
return words;
};
AES.words2buf = function(words) {
var buf = new Buffer(words.length * 4);
for (var i = 0; i < words.length; i++) {
buf.writeUInt32BE(words[i], i * 4);
};
return buf;
};
module.exports = AES;

34
lib/expmt/aescbc.js

@ -0,0 +1,34 @@
var AES = require('./aes');
var CBC = require('./cbc');
var Random = require('../random');
var Hash = require('../hash');
// Symmetric encryption with AES and CBC convenience class
var AESCBC = function AESCBC() {
};
AESCBC.encrypt = function(messagebuf, passwordstr) {
var cipherkeybuf = Hash.sha256(new Buffer(passwordstr));
return AESCBC.encryptCipherkey(messagebuf, cipherkeybuf);
};
AESCBC.decrypt = function(encbuf, passwordstr) {
var cipherkeybuf = Hash.sha256(new Buffer(passwordstr));
return AESCBC.decryptCipherkey(encbuf, cipherkeybuf);
};
AESCBC.encryptCipherkey = function(messagebuf, cipherkeybuf, ivbuf) {
ivbuf = ivbuf || Random.getRandomBuffer(128 / 8);
var ctbuf = CBC.encrypt(messagebuf, ivbuf, AES, cipherkeybuf);
var encbuf = Buffer.concat([ivbuf, ctbuf]);
return encbuf;
};
AESCBC.decryptCipherkey = function(encbuf, cipherkeybuf) {
var ivbuf = encbuf.slice(0, 128 / 8);
var ctbuf = encbuf.slice(128 / 8);
var messagebuf = CBC.decrypt(ctbuf, ivbuf, AES, cipherkeybuf);
return messagebuf;
};
module.exports = AESCBC;

138
lib/expmt/cbc.js

@ -0,0 +1,138 @@
var Random = require('../random');
// Cipher Block Chaining
// http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
var CBC = function CBC(blockcipher, cipherkeybuf, ivbuf) {
if (!(this instanceof CBC))
return new CBC(blockcipher, cipherkeybuf, ivbuf);
this.blockcipher = blockcipher;
this.cipherkeybuf = cipherkeybuf;
this.ivbuf = ivbuf;
};
CBC.buf2blockbufs = function(buf, blocksize) {
var bytesize = blocksize / 8;
var blockbufs = [];
for (var i = 0; i <= buf.length / bytesize; i++) {
var blockbuf = buf.slice(i * bytesize, i * bytesize + bytesize);
if (blockbuf.length < blocksize)
blockbuf = CBC.pkcs7pad(blockbuf, blocksize);
blockbufs.push(blockbuf);
}
return blockbufs;
};
CBC.blockbufs2buf = function(blockbufs, blocksize) {
var bytesize = blocksize / 8;
var last = blockbufs[blockbufs.length - 1];
last = CBC.pkcs7unpad(last);
blockbufs[blockbufs.length - 1] = last;
var buf = Buffer.concat(blockbufs);
return buf;
};
CBC.encrypt = function(messagebuf, ivbuf, blockcipher, cipherkeybuf) {
var blocksize = ivbuf.length * 8;
var blockbufs = CBC.buf2blockbufs(messagebuf, blocksize);
var encbufs = CBC.encryptblocks(blockbufs, ivbuf, blockcipher, cipherkeybuf);
var encbuf = Buffer.concat(encbufs);
return encbuf;
};
CBC.decrypt = function(encbuf, ivbuf, blockcipher, cipherkeybuf) {
var blocksize = ivbuf.length * 8;
var bytesize = ivbuf.length;
var encbufs = [];
for (var i = 0; i < encbuf.length / bytesize; i++) {
encbufs.push(encbuf.slice(i * bytesize, i * bytesize + bytesize));
}
var blockbufs = CBC.decryptblocks(encbufs, ivbuf, blockcipher, cipherkeybuf);
var buf = CBC.blockbufs2buf(blockbufs, blocksize);
return buf;
};
CBC.encryptblock = function(blockbuf, ivbuf, blockcipher, cipherkeybuf) {
var xorbuf = CBC.xorbufs(blockbuf, ivbuf);
var encbuf = blockcipher.encrypt(xorbuf, cipherkeybuf);
return encbuf;
};
CBC.decryptblock = function(encbuf, ivbuf, blockcipher, cipherkeybuf) {
var xorbuf = blockcipher.decrypt(encbuf, cipherkeybuf);
var blockbuf = CBC.xorbufs(xorbuf, ivbuf);
return blockbuf;
};
CBC.encryptblocks = function(blockbufs, ivbuf, blockcipher, cipherkeybuf) {
var encbufs = [];
for (var i = 0; i < blockbufs.length; i++) {
var blockbuf = blockbufs[i];
var encbuf = CBC.encryptblock(blockbuf, ivbuf, blockcipher, cipherkeybuf);
encbufs.push(encbuf);
ivbuf = encbuf;
}
return encbufs;
};
CBC.decryptblocks = function(encbufs, ivbuf, blockcipher, cipherkeybuf) {
var blockbufs = [];
for (var i = 0; i < encbufs.length; i++) {
var encbuf = encbufs[i];
var blockbuf = CBC.decryptblock(encbuf, ivbuf, blockcipher, cipherkeybuf);
blockbufs.push(blockbuf);
ivbuf = encbuf;
}
return blockbufs;
};
CBC.pkcs7pad = function(buf, blocksize) {
var bytesize = blocksize / 8;
var padbytesize = bytesize - buf.length;
var pad = new Buffer(padbytesize);
pad.fill(padbytesize);
var paddedbuf = Buffer.concat([buf, pad]);
return paddedbuf;
};
CBC.pkcs7unpad = function(paddedbuf, blocksize) {
var bytesize = blocksize / 8;
var padbytesize = bytesize - paddedbuf.length;
var padlength = paddedbuf[paddedbuf.length - 1];
var padbuf = paddedbuf.slice(paddedbuf.length - padlength, paddedbuf.length);
var padbuf2 = new Buffer(padlength);
padbuf2.fill(padlength);
if (padbuf.toString('hex') !== padbuf2.toString('hex'))
throw new Error('invalid padding');
return paddedbuf.slice(0, paddedbuf.length - padlength);
};
CBC.xorbufs = function(buf1, buf2) {
if (buf1.length !== buf2.length)
throw new Error('bufs must have the same length');
var buf = new Buffer(buf1.length);
for (var i = 0; i < buf1.length; i++) {
buf[i] = buf1[i] ^ buf2[i];
}
return buf;
};
module.exports = CBC;

55
lib/expmt/ecies.js

@ -0,0 +1,55 @@
var AESCBC = require('./aescbc');
var Keypair = require('../keypair');
var Point = require('../point');
var Hash = require('../hash');
var Pubkey = require('../pubkey');
var Privkey = require('../privkey');
// http://en.wikipedia.org/wiki/Integrated_Encryption_Scheme
var ECIES = function ECIES() {
if (!(this instanceof ECIES))
return new ECIES();
};
ECIES.encrypt = function(messagebuf, topubkey, fromkeypair, ivbuf) {
if (!fromkeypair)
fromkeypair = Keypair().fromRandom();
var r = fromkeypair.privkey.bn;
var R = fromkeypair.pubkey.point;
var Rpubkey = fromkeypair.pubkey;
var Rbuf = Rpubkey.toDER(true);
var KB = topubkey.point;
var P = KB.mul(r);
var S = P.getX();
var Sbuf = S.toBuffer({size: 32});
var kEkM = Hash.sha512(Sbuf);
var kE = kEkM.slice(0, 32);
var kM = kEkM.slice(32, 64);
var c = AESCBC.encryptCipherkey(messagebuf, kE, ivbuf);
var d = Hash.sha256hmac(c, kM);
var encbuf = Buffer.concat([Rbuf, c, d]);
return encbuf;
};
ECIES.decrypt = function(encbuf, toprivkey) {
var kB = toprivkey.bn;
var frompubkey = Pubkey().fromDER(encbuf.slice(0, 33));
var R = frompubkey.point;
var P = R.mul(kB);
if (P.eq(new Point()))
throw new Error('P equals 0');
var S = P.getX();
var Sbuf = S.toBuffer({size: 32});
var kEkM = Hash.sha512(Sbuf);
var kE = kEkM.slice(0, 32);
var kM = kEkM.slice(32, 64);
var c = encbuf.slice(33, encbuf.length - 32);
var d = encbuf.slice(encbuf.length - 32, encbuf.length);
var d2 = Hash.sha256hmac(c, kM);
if (d.toString('hex') !== d2.toString('hex'))
throw new Error('Invalid checksum');
var messagebuf = AESCBC.decryptCipherkey(c, kE);
return messagebuf;
};
module.exports = ECIES;

127
lib/expmt/stealthaddress.js

@ -0,0 +1,127 @@
var Stealthkey = require('./stealthkey');
var Base58check = require('../base58check');
var Pubkey = require('../pubkey');
var KDF = require('../kdf');
var BufferWriter = require('../bufferwriter');
var BufferReader = require('../bufferreader');
var StealthAddress = function StealthAddress(addrstr) {
if (!(this instanceof StealthAddress))
return new StealthAddress(addrstr);
if (typeof addrstr === 'string') {
this.fromString(addrstr)
}
else if (Buffer.isBuffer(addrstr)) {
var buf = addrstr;
this.fromBuffer(buf);
}
else if (addrstr) {
var obj = addrstr;
this.set(obj);
}
};
StealthAddress.mainver = 42;
StealthAddress.testver = 43;
StealthAddress.prototype.set = function(obj) {
this.payloadPubkey = obj.payloadPubkey || this.payloadPubkey;
this.scanPubkey = obj.scanPubkey || this.scanPubkey;
return this;
};
StealthAddress.prototype.fromJSON = function(json) {
this.fromString(json);
return this;
};
StealthAddress.prototype.toJSON = function() {
return this.toString();
};
StealthAddress.prototype.fromStealthkey = function(stealthkey) {
this.set({
payloadPubkey: stealthkey.payloadKeypair.pubkey,
scanPubkey: stealthkey.scanKeypair.pubkey
});
return this;
};
StealthAddress.prototype.fromBuffer = function(buf) {
var parsed = StealthAddress.parseDWBuffer(buf);
if ((parsed.version !== StealthAddress.mainver) && (parsed.version !== StealthAddress.testver))
throw new Error('Invalid version');
if (parsed.options !== 0)
throw new Error('Invalid options');
if (!parsed.scanPubkey)
throw new Error('Invalid scanPubkey');
if (parsed.payloadPubkeys.length !== 1)
throw new Error('Must have exactly one payloadPubkey');
if (parsed.nSigs !== 1)
throw new Error('Must require exactly one signature');
if (parsed.prefix.toString() !== "")
throw new Error('Only blank prefixes supported');
this.scanPubkey = parsed.scanPubkey;
this.payloadPubkey = parsed.payloadPubkeys[0];
return this;
};
StealthAddress.prototype.fromString = function(str) {
return this.fromBuffer(Base58check(str).toBuffer());
};
StealthAddress.prototype.getSharedKeypair = function(senderKeypair) {
var sharedSecretPoint = this.scanPubkey.point.mul(senderKeypair.privkey.bn);
var sharedSecretPubkey = Pubkey(sharedSecretPoint);
var buf = sharedSecretPubkey.toDER(true);
var sharedKeypair = KDF.sha256hmac2keypair(buf);
return sharedKeypair;
};
StealthAddress.prototype.getReceivePubkey = function(senderKeypair) {
var sharedKeypair = this.getSharedKeypair(senderKeypair);
var pubkey = Pubkey(this.payloadPubkey.point.add(sharedKeypair.pubkey.point));
return pubkey;
};
StealthAddress.prototype.toBuffer = function(networkstr) {
if (networkstr === 'testnet')
var version = StealthAddress.testver;
else
var version = StealthAddress.mainver;
var bw = new BufferWriter();
bw.writeUInt8(version);
bw.writeUInt8(0); //options
bw.write(this.scanPubkey.toDER(true));
bw.writeUInt8(1); //number of payload keys - we only support 1 (not multisig)
bw.write(this.payloadPubkey.toDER(true));
bw.writeUInt8(1); //number of signatures - we only support 1 (not multisig)
bw.writeUInt8(0); //prefix length - we do not support prefix yet
var buf = bw.concat();
return buf;
};
StealthAddress.prototype.toString = function(networkstr) {
return Base58check(this.toBuffer(networkstr)).toString();
};
StealthAddress.parseDWBuffer = function(buf) {
var br = new BufferReader(buf);
var parsed = {};
parsed.version = br.readUInt8();
parsed.options = br.readUInt8();
parsed.scanPubkey = Pubkey().fromBuffer(br.read(33));
parsed.nPayloadPubkeys = br.readUInt8();
parsed.payloadPubkeys = [];
for (var i = 0; i < parsed.nPayloadPubkeys; i++)
parsed.payloadPubkeys.push(Pubkey().fromBuffer(br.read(33)));
parsed.nSigs = br.readUInt8();
parsed.nPrefix = br.readUInt8();
parsed.prefix = br.read(parsed.nPrefix / 8);
return parsed;
};
module.exports = StealthAddress;

88
lib/expmt/stealthkey.js

@ -0,0 +1,88 @@
var Keypair = require('../keypair');
var Privkey = require('../privkey');
var Pubkey = require('../pubkey');
var Point = require('../point');
var Hash = require('../hash');
var KDF = require('../kdf');
var Stealthkey = function Stealthkey(payloadKeypair, scanKeypair) {
if (!(this instanceof Stealthkey))
return new Stealthkey(payloadKeypair, scanKeypair);
if (payloadKeypair instanceof Keypair) {
this.set({
payloadKeypair: payloadKeypair,
scanKeypair: scanKeypair
});
}
else if (payloadKeypair) {
var obj = payloadKeypair;
this.set(obj);
}
};
Stealthkey.prototype.set = function(obj) {
this.payloadKeypair = obj.payloadKeypair || this.payloadKeypair;
this.scanKeypair = obj.scanKeypair || this.scanKeypair;
return this;
};
Stealthkey.prototype.fromJSON = function(json) {
this.set({
payloadKeypair: Keypair().fromJSON(json.payloadKeypair),
scanKeypair: Keypair().fromJSON(json.scanKeypair)
});
return this;
};
Stealthkey.prototype.toJSON = function() {
return {
payloadKeypair: this.payloadKeypair.toJSON(),
scanKeypair: this.scanKeypair.toJSON()
};
};
Stealthkey.prototype.fromRandom = function() {
this.payloadKeypair = Keypair().fromRandom();
this.scanKeypair = Keypair().fromRandom();
return this;
};
Stealthkey.prototype.getSharedKeypair = function(senderPubkey) {
var sharedSecretPoint = senderPubkey.point.mul(this.scanKeypair.privkey.bn);
var sharedSecretPubkey = Pubkey({point: sharedSecretPoint});
var buf = sharedSecretPubkey.toDER(true);
var sharedKeypair = KDF.sha256hmac2keypair(buf);
return sharedKeypair;
};
Stealthkey.prototype.getReceivePubkey = function(senderPubkey) {
var sharedKeypair = this.getSharedKeypair(senderPubkey);
var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)});
return pubkey;
};
Stealthkey.prototype.getReceiveKeypair = function(senderPubkey) {
var sharedKeypair = this.getSharedKeypair(senderPubkey);
var privkey = Privkey({bn: this.payloadKeypair.privkey.bn.add(sharedKeypair.privkey.bn).mod(Point.getN())});
var key = Keypair({privkey: privkey});
key.privkey2pubkey();
return key;
};
Stealthkey.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhashbuf) {
var pubkey = this.getReceivePubkey(senderPubkey);
var pubkeybuf = pubkey.toDER(true);
var pubkeyhash = Hash.sha256ripemd160(pubkeybuf);
if (pubkeyhash.toString('hex') === myPossiblePubkeyhashbuf.toString('hex'))
return true;
else
return false;
};
module.exports = Stealthkey;

85
lib/expmt/stealthmessage.js

@ -0,0 +1,85 @@
var Stealthkey = require('./stealthkey');
var StealthAddress = require('./stealthaddress');
var ECIES = require('./ecies');
var Message = require('../message');
var Keypair = require('../keypair');
var Address = require('../address');
var Pubkey = require('../pubkey');
var StealthMessage = function StealthMessage(obj) {
if (!(this instanceof StealthMessage))
return new StealthMessage(obj);
if (obj)
this.set(obj);
};
StealthMessage.prototype.set = function(obj) {
this.messagebuf = obj.messagebuf || this.messagebuf;
this.encbuf = obj.encbuf || this.encbuf;
this.toStealthAddress = obj.toStealthAddress || this.toStealthAddress;
this.toStealthkey = obj.toStealthkey || this.toStealthkey;
this.fromKeypair = obj.fromKeypair || this.fromKeypair;
this.receiveAddress = obj.receiveAddress || this.receiveAddress;
return this;
};
StealthMessage.encrypt = function(messagebuf, toStealthAddress, fromKeypair, ivbuf) {
var sm = StealthMessage().set({
messagebuf: messagebuf,
toStealthAddress: toStealthAddress,
fromKeypair: fromKeypair
});
sm.encrypt(ivbuf);
var buf = Buffer.concat([
sm.receiveAddress.hashbuf,
sm.fromKeypair.pubkey.toDER(true),
sm.encbuf
]);
return buf;
};
StealthMessage.decrypt = function(buf, toStealthkey) {
var sm = StealthMessage().set({
toStealthkey: toStealthkey,
receiveAddress: Address().set({hashbuf: buf.slice(0, 20)}),
fromKeypair: Keypair().set({pubkey: Pubkey().fromDER(buf.slice(20, 20 + 33))}),
encbuf: buf.slice(20 + 33)
});
return sm.decrypt().messagebuf;
};
StealthMessage.isForMe = function(buf, toStealthkey) {
var sm = StealthMessage().set({
toStealthkey: toStealthkey,
receiveAddress: Address().set({hashbuf: buf.slice(0, 20)}),
fromKeypair: Keypair().set({pubkey: Pubkey().fromDER(buf.slice(20, 20 + 33))}),
encbuf: buf.slice(20 + 33)
});
return sm.isForMe();
};
StealthMessage.prototype.encrypt = function(ivbuf) {
if (!this.fromKeypair)
this.fromKeypair = Keypair().fromRandom();
var receivePubkey = this.toStealthAddress.getReceivePubkey(this.fromKeypair);
this.receiveAddress = Address().fromPubkey(receivePubkey);
this.encbuf = ECIES.encrypt(this.messagebuf, receivePubkey, this.fromKeypair, ivbuf);
return this;
};
StealthMessage.prototype.decrypt = function() {
var receiveKeypair = this.toStealthkey.getReceiveKeypair(this.fromKeypair.pubkey);
this.messagebuf = ECIES.decrypt(this.encbuf, receiveKeypair.privkey);
return this;
};
StealthMessage.prototype.isForMe = function() {
var receivePubkey = this.toStealthkey.getReceivePubkey(this.fromKeypair.pubkey);
var receiveAddress = Address().fromPubkey(receivePubkey);
if (receiveAddress.toString('hex') === this.receiveAddress.toString('hex'))
return true;
else
return false;
};
module.exports = StealthMessage;

69
lib/expmt/stealthtx.js

@ -0,0 +1,69 @@
var StealthAddress = require('./stealthaddress');
var StealthKey = require('./stealthkey');
var Transaction = require('../transaction');
var Pubkey = require('../pubkey');
var StealthTx = function StealthTx(tx, sa, sk) {
if (!(this instanceof StealthTx))
return new StealthTx(tx, sa, sk);
if (tx instanceof Transaction) {
this.tx = tx;
this.sa = sa;
this.sk = sk;
} else if (tx) {
var obj = tx;
this.set(obj);
}
};
StealthTx.prototype.set = function(obj) {
this.sk = obj.sk || this.sk;
this.sa = obj.sa || this.sa;
this.tx = obj.tx || this.tx;
return this;
};
StealthTx.prototype.isForMe = function() {
if (!this.notMine())
return true;
else
return false;
};
StealthTx.prototype.notMine = function() {
var err;
if (err = this.notStealth())
return "Not stealth: " + err;
var txopbuf = this.tx.txouts[0].script.chunks[1].buf;
var parsed = StealthTx.parseOpReturnData(txopbuf);
var pubkey = parsed.pubkey;
var pubkeyhashbuf = this.tx.txouts[1].script.chunks[2].buf;
var sk = this.sk;
if (sk.isForMe(pubkey, pubkeyhashbuf)) {
return false;
} else {
return "StealthTx not mine";
}
};
//For now, we only support a very limited variety of stealth tx
StealthTx.prototype.notStealth = function() {
var txouts = this.tx.txouts;
if (!(txouts.length >= 2))
return "Not enough txouts";
if (!txouts[0].script.isOpReturn())
return "First txout is not OP_RETURN";
if (!txouts[1].script.isPubkeyhashOut())
return "Second txout is not pubkeyhash";
return false;
};
StealthTx.parseOpReturnData = function(buf) {
var parsed = {};
parsed.version = buf[0];
parsed.noncebuf = buf.slice(1, 5);
parsed.pubkey = Pubkey().fromBuffer(buf.slice(5, 5 + 33));
return parsed;
};
module.exports = StealthTx;

89
lib/hash.js

@ -0,0 +1,89 @@
var hashjs = require('hash.js');
var sha512 = require('sha512');
var Hash = module.exports;
Hash.sha256 = function(buf) {
if (!Buffer.isBuffer(buf))
throw new Error('sha256 hash must be of a buffer');
var hash = (new hashjs.sha256()).update(buf).digest();
return new Buffer(hash);
};
Hash.sha256.blocksize = 512;
Hash.sha256sha256 = function(buf) {
try {
return Hash.sha256(Hash.sha256(buf));
} catch (e) {
throw new Error('sha256sha256 hash must be of a buffer');
}
};
Hash.ripemd160 = function(buf) {
if (!Buffer.isBuffer(buf))
throw new Error('ripemd160 hash must be of a buffer');
var hash = (new hashjs.ripemd160()).update(buf).digest();
return new Buffer(hash);
};
Hash.sha256ripemd160 = function(buf) {
try {
return Hash.ripemd160(Hash.sha256(buf));
} catch (e) {
throw new Error('sha256ripemd160 hash must be of a buffer');
}
};
Hash.sha512 = function(buf) {
if (!Buffer.isBuffer(buf))
throw new Error('sha512 hash must be of a buffer');
var hash = sha512(buf);
return new Buffer(hash);
};
Hash.sha512.blocksize = 1024;
Hash.hmac = function(hashf, data, key) {
if (!Buffer.isBuffer(data) || !Buffer.isBuffer(key))
throw new Error('data and key must be buffers');
//http://en.wikipedia.org/wiki/Hash-based_message_authentication_code
//http://tools.ietf.org/html/rfc4868#section-2
if (!hashf.blocksize)
throw new Error('Blocksize for hash function unknown');
var blocksize = hashf.blocksize/8;
if (key.length > blocksize)
key = hashf(key);
else if (key < blocksize) {
var fill = new Buffer(blocksize);
fill.fill(0);
key.copy(fill);
key = fill;
}
var o_key = new Buffer(blocksize);
o_key.fill(0x5c);
var i_key = new Buffer(blocksize);
i_key.fill(0x36);
var o_key_pad = new Buffer(blocksize);
var i_key_pad = new Buffer(blocksize);
for (var i = 0; i < blocksize; i++) {
o_key_pad[i] = o_key[i] ^ key[i];
i_key_pad[i] = i_key[i] ^ key[i];
}
return hashf(Buffer.concat([o_key_pad, hashf(Buffer.concat([i_key_pad, data]))]));
};
Hash.sha256hmac = function(data, key) {
return Hash.hmac(Hash.sha256, data, key);
};
Hash.sha512hmac = function(data, key) {
return Hash.hmac(Hash.sha512, data, key);
};

32
lib/kdf.js

@ -0,0 +1,32 @@
var Bn = require('./bn');
var Privkey = require('./privkey');
var Point = require('./point');
var Pubkey = require('./pubkey');
var Keypair = require('./keypair');
var Hash = require('./hash');
function KDF() {
};
KDF.buf2keypair = function(buf) {
return KDF.sha256hmac2keypair(buf);
};
KDF.sha256hmac2keypair = function(buf) {
var privkey = KDF.sha256hmac2privkey(buf);
var keypair = Keypair().fromPrivkey(privkey);
return keypair;
};
KDF.sha256hmac2privkey = function(buf) {
var bn;
var concat = new Buffer([]);
do {
var hash = Hash.sha256hmac(buf, concat);
var bn = Bn.fromBuffer(hash);
concat = Buffer.concat([concat, new Buffer(0)]);
} while(!bn.lt(Point.getN()));
return new Privkey({bn: bn});
};
module.exports = KDF;

73
lib/keypair.js

@ -0,0 +1,73 @@
var Privkey = require('./privkey');
var Pubkey = require('./pubkey');
var BN = require('./bn');
var point = require('./point');
var Keypair = function Keypair(obj) {
if (!(this instanceof Keypair))
return new Keypair(obj);
if (obj)
this.set(obj);
};
Keypair.prototype.set = function(obj) {
this.privkey = obj.privkey || this.privkey || undefined;
this.pubkey = obj.pubkey || this.pubkey || undefined;
return this;
};
Keypair.prototype.fromJSON = function(json) {
if (json.privkey)
this.set({privkey: Privkey().fromJSON(json.privkey)});
if (json.pubkey)
this.set({pubkey: Pubkey().fromJSON(json.pubkey)});
return this;
};
Keypair.prototype.toJSON = function() {
var json = {};
if (this.privkey)
json.privkey = this.privkey.toJSON();
if (this.pubkey)
json.pubkey = this.pubkey.toJSON();
return json;
};
Keypair.prototype.fromPrivkey = function(privkey) {
this.privkey = privkey;
this.privkey2pubkey();
return this;
};
Keypair.prototype.fromRandom = function() {
this.privkey = Privkey().fromRandom();
this.privkey2pubkey();
return this;
};
Keypair.prototype.fromString = function(str) {
var obj = JSON.parse(str);
if (obj.privkey) {
this.privkey = new Privkey();
this.privkey.fromString(obj.privkey);
}
if (obj.pubkey) {
this.pubkey = new Pubkey();
this.pubkey.fromString(obj.pubkey);
}
};
Keypair.prototype.privkey2pubkey = function() {
this.pubkey = Pubkey().fromPrivkey(this.privkey);
};
Keypair.prototype.toString = function() {
var obj = {};
if (this.privkey)
obj.privkey = this.privkey.toString();
if (this.pubkey)
obj.pubkey = this.pubkey.toString();
return JSON.stringify(obj);
};
module.exports = Keypair;

94
lib/message.js

@ -0,0 +1,94 @@
var ECDSA = require('./ecdsa');
var Keypair = require('./keypair');
var Privkey = require('./privkey');
var Pubkey = require('./pubkey');
var BufferWriter = require('./bufferwriter');
var Hash = require('./hash');
var Address = require('./address');
var Signature = require('./signature');
var Message = function Message(obj) {
if (!(this instanceof Message))
return new Message(obj);
if (obj)
this.set(obj);
};
Message.prototype.set = function(obj) {
this.messagebuf = obj.messagebuf || this.messagebuf;
this.keypair = obj.keypair || this.keypair;
this.sig = obj.sig || this.sig;
this.address = obj.address || this.address;
this.verified = typeof obj.verified !== 'undefined' ? obj.verified : this.verified;
return this;
};
Message.magicBytes = new Buffer('Bitcoin Signed Message:\n');
Message.magicHash = function(messagebuf) {
if (!Buffer.isBuffer(messagebuf))
throw new Error('messagebuf must be a buffer');
var bw = new BufferWriter();
bw.writeVarintNum(Message.magicBytes.length);
bw.write(Message.magicBytes);
bw.writeVarintNum(messagebuf.length);
bw.write(messagebuf);
var buf = bw.concat();
var hashbuf = Hash.sha256sha256(buf);
return hashbuf;
};
Message.sign = function(messagebuf, keypair) {
var m = Message({messagebuf: messagebuf, keypair: keypair});
m.sign();
var sigbuf = m.sig.toCompact();
var sigstr = sigbuf.toString('base64');
return sigstr;
};
Message.verify = function(messagebuf, sigstr, address) {
var sigbuf = new Buffer(sigstr, 'base64');
var message = new Message();
message.messagebuf = messagebuf;
message.sig = Signature().fromCompact(sigbuf);
message.address = address;
return message.verify().verified;
};
Message.prototype.sign = function() {
var hashbuf = Message.magicHash(this.messagebuf);
var ecdsa = ECDSA({hashbuf: hashbuf, keypair: this.keypair});
ecdsa.signRandomK();
ecdsa.calci();
this.sig = ecdsa.sig;
return this;
};
Message.prototype.verify = function() {
var hashbuf = Message.magicHash(this.messagebuf);
var ecdsa = new ECDSA();
ecdsa.hashbuf = hashbuf;
ecdsa.sig = this.sig;
ecdsa.keypair = new Keypair();
ecdsa.keypair.pubkey = ecdsa.sig2pubkey();
if (!ecdsa.verify()) {
this.verified = false;
return this;
}
var address = Address().fromPubkey(ecdsa.keypair.pubkey, undefined, this.sig.compressed);
//TODO: what if livenet/testnet mismatch?
if (address.hashbuf.toString('hex') === this.address.hashbuf.toString('hex'))
this.verified = true;
else
this.verified = false;
return this;
};
module.exports = Message;

192
lib/opcode.js

@ -0,0 +1,192 @@
function Opcode(num) {
if (!(this instanceof Opcode))
return new Opcode(num);
if (typeof num === 'number') {
this.num = num;
} else if (typeof num === 'string') {
var str = num;
this.num = Opcode.map[str];
} else if (num) {
var obj = num;
this.set(obj);
}
}
Opcode.prototype.set = function(obj) {
this.num = typeof obj.num !== 'undefined' ? obj.num : this.num;
return this;
};
Opcode.prototype.fromNumber = function(num) {
this.num = num;
return this;
};
Opcode.prototype.toNumber = function() {
return this.num;
};
Opcode.prototype.fromString = function(str) {
var num = Opcode.map[str];
if (typeof num === 'undefined')
throw new Error('Invalid opcodestr');
this.num = num;
return this;
};
Opcode.prototype.toString = function() {
var str = Opcode.reverseMap[this.num];
if (typeof str === 'undefined')
throw new Error('Opcode does not have a string representation');
return str;
};
Opcode.map = {
// push value
OP_FALSE: 0,
OP_0: 0,
OP_PUSHDATA1: 76,
OP_PUSHDATA2: 77,
OP_PUSHDATA4: 78,
OP_1NEGATE: 79,
OP_RESERVED: 80,
OP_TRUE: 81,
OP_1: 81,
OP_2: 82,
OP_3: 83,
OP_4: 84,
OP_5: 85,
OP_6: 86,
OP_7: 87,
OP_8: 88,
OP_9: 89,
OP_10: 90,
OP_11: 91,
OP_12: 92,
OP_13: 93,
OP_14: 94,
OP_15: 95,
OP_16: 96,
// control
OP_NOP: 97,
OP_VER: 98,
OP_IF: 99,
OP_NOTIF: 100,
OP_VERIF: 101,
OP_VERNOTIF: 102,
OP_ELSE: 103,
OP_ENDIF: 104,
OP_VERIFY: 105,
OP_RETURN: 106,
// stack ops
OP_TOALTSTACK: 107,
OP_FROMALTSTACK: 108,
OP_2DROP: 109,
OP_2DUP: 110,
OP_3DUP: 111,
OP_2OVER: 112,
OP_2ROT: 113,
OP_2SWAP: 114,
OP_IFDUP: 115,
OP_DEPTH: 116,
OP_DROP: 117,
OP_DUP: 118,
OP_NIP: 119,
OP_OVER: 120,
OP_PICK: 121,
OP_ROLL: 122,
OP_ROT: 123,
OP_SWAP: 124,
OP_TUCK: 125,
// splice ops
OP_CAT: 126,
OP_SUBSTR: 127,
OP_LEFT: 128,
OP_RIGHT: 129,
OP_SIZE: 130,
// bit logic
OP_INVERT: 131,
OP_AND: 132,
OP_OR: 133,
OP_XOR: 134,
OP_EQUAL: 135,
OP_EQUALVERIFY: 136,
OP_RESERVED1: 137,
OP_RESERVED2: 138,
// numeric
OP_1ADD: 139,
OP_1SUB: 140,
OP_2MUL: 141,
OP_2DIV: 142,
OP_NEGATE: 143,
OP_ABS: 144,
OP_NOT: 145,
OP_0NOTEQUAL: 146,
OP_ADD: 147,
OP_SUB: 148,
OP_MUL: 149,
OP_DIV: 150,
OP_MOD: 151,
OP_LSHIFT: 152,
OP_RSHIFT: 153,
OP_BOOLAND: 154,
OP_BOOLOR: 155,
OP_NUMEQUAL: 156,
OP_NUMEQUALVERIFY: 157,
OP_NUMNOTEQUAL: 158,
OP_LESSTHAN: 159,
OP_GREATERTHAN: 160,
OP_LESSTHANOREQUAL: 161,
OP_GREATERTHANOREQUAL: 162,
OP_MIN: 163,
OP_MAX: 164,
OP_WITHIN: 165,
// crypto
OP_RIPEMD160: 166,
OP_SHA1: 167,
OP_SHA256: 168,
OP_HASH160: 169,
OP_HASH256: 170,
OP_CODESEPARATOR: 171,
OP_CHECKSIG: 172,
OP_CHECKSIGVERIFY: 173,
OP_CHECKMULTISIG: 174,
OP_CHECKMULTISIGVERIFY: 175,
// expansion
OP_NOP1: 176,
OP_NOP2: 177,
OP_NOP3: 178,
OP_NOP4: 179,
OP_NOP5: 180,
OP_NOP6: 181,
OP_NOP7: 182,
OP_NOP8: 183,
OP_NOP9: 184,
OP_NOP10: 185,
// template matching params
OP_PUBKEYHASH: 253,
OP_PUBKEY: 254,
OP_INVALIDOPCODE: 255
};
Opcode.reverseMap = [];
for (var k in Opcode.map) {
if (Opcode.map.hasOwnProperty(k)) {
Opcode.reverseMap[Opcode.map[k]] = k;
}
}
module.exports = Opcode;

50
lib/point.js

@ -0,0 +1,50 @@
var BN = require('./bn');
var elliptic = require('elliptic');
var ec = elliptic.curves.secp256k1;
var ecpoint = ec.curve.point.bind(ec.curve)
var p = ec.curve.point();
var Curve = Object.getPrototypeOf(ec.curve);
var Point = function Point(x, y, isRed) {
return ecpoint(x, y, isRed);
};
Point.prototype = Object.getPrototypeOf(p);
Point.fromX = ec.curve.pointFromX.bind(ec.curve);
Point.getG = function() {
var p = Point(ec.curve.g.getX(), ec.curve.g.getY());
return p;
};
Point.getN = function() {
return BN(ec.curve.n.toArray());
};
Point.prototype._getX = Point.prototype.getX;
Point.prototype.getX = function() {
var n = BN(this._getX().toArray());
return BN(this._getX().toArray());
};
Point.prototype._getY = Point.prototype.getY;
Point.prototype.getY = function() {
return BN(this._getY().toArray());
};
//https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf
Point.prototype.validate = function() {
var p2 = Point.fromX(this.getY().isOdd(), this.getX());
if (!(p2.y.cmp(this.y) === 0))
throw new Error('Invalid y value of public key');
if (!(this.getX().gt(-1) && this.getX().lt(Point.getN()))
||!(this.getY().gt(-1) && this.getY().lt(Point.getN())))
throw new Error('Point does not lie on the curve');
if (!(this.mul(Point.getN()).isInfinity()))
throw new Error('Point times N must be infinity');
return this;
};
module.exports = Point;

104
lib/privkey.js

@ -0,0 +1,104 @@
var BN = require('./bn');
var Point = require('./point');
var constants = require('./constants');
var base58check = require('./base58check');
var Random = require('./random');
var Privkey = function Privkey(bn) {
if (!(this instanceof Privkey))
return new Privkey(bn);
if (bn instanceof BN)
this.bn = bn;
else if (bn) {
var obj = bn;
this.set(obj);
}
};
Privkey.prototype.set = function(obj) {
this.bn = obj.bn || this.bn;
this.networkstr = obj.networkstr || this.networkstr;
this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed;
return this;
};
Privkey.prototype.fromJSON = function(json) {
this.fromString(json);
return this;
};
Privkey.prototype.toJSON = function() {
return this.toString();
};
Privkey.prototype.fromRandom = function() {
do {
var privbuf = Random.getRandomBuffer(32);
var bn = BN().fromBuffer(privbuf);
var condition = bn.lt(Point.getN());
} while (!condition);
this.set({
bn: bn,
networkstr: 'mainnet',
compressed: true
});
return this;
};
Privkey.prototype.validate = function() {
if (!this.bn.lt(Point.getN()))
throw new Error('Number must be less than N');
if (typeof constants[this.networkstr] === undefined)
throw new Error('Must specify the networkstr ("mainnet" or "testnet")');
if (typeof this.compressed !== 'boolean')
throw new Error('Must specify whether the corresponding public key is compressed or not (true or false)');
};
Privkey.prototype.toWIF = function() {
var networkstr = this.networkstr;
var compressed = this.compressed;
if (typeof this.networkstr === 'undefined')
networkstr = 'mainnet';
if (typeof this.compressed === 'undefined')
compressed = true;
var privbuf = this.bn.toBuffer({size: 32});
var buf;
if (compressed)
buf = Buffer.concat([new Buffer([constants[networkstr].privkey]), this.bn.toBuffer({size: 32}), new Buffer([0x01])]);
else
buf = Buffer.concat([new Buffer([constants[networkstr].privkey]), this.bn.toBuffer({size: 32})]);
return base58check.encode(buf);
};
Privkey.prototype.fromWIF = function(str) {
var buf = base58check.decode(str);
if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] == 1)
this.compressed = true;
else if (buf.length === 1 + 32)
this.compressed = false;
else
throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)');
if (buf[0] === constants.mainnet.privkey)
this.networkstr = 'mainnet';
else if (buf[0] === constants.testnet.privkey)
this.networkstr = 'testnet';
else
throw new Error('Invalid networkstr');
this.bn = BN.fromBuffer(buf.slice(1, 32 + 1));
};
Privkey.prototype.toString = function() {
return this.toWIF();
};
Privkey.prototype.fromString = function(str) {
this.fromWIF(str);
};
module.exports = Privkey;

125
lib/pubkey.js

@ -0,0 +1,125 @@
var Point = require('./point');
var bn = require('./bn');
var privkey = require('./privkey');
var Pubkey = function Pubkey(point) {
if (!(this instanceof Pubkey))
return new Pubkey(point);
if (point instanceof Point)
this.point = point;
else if (point) {
var obj = point;
this.set(obj);
}
};
Pubkey.prototype.set = function(obj) {
if (obj.point && !obj.point.getX() && !obj.point.getY())
throw new Error('Invalid point');
this.point = obj.point || this.point;
this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed;
return this;
};
Pubkey.prototype.fromJSON = function(json) {
this.fromBuffer(new Buffer(json, 'hex'));
return this;
};
Pubkey.prototype.toJSON = function() {
return this.toBuffer().toString('hex');
};
Pubkey.prototype.fromPrivkey = function(privkey) {
this.set({
point: Point.getG().mul(privkey.bn),
compressed: privkey.compressed}
);
return this;
};
Pubkey.prototype.fromBuffer = function(buf) {
return this.fromDER(buf);
};
Pubkey.prototype.fromDER = function(buf) {
if (buf[0] == 0x04) {
var xbuf = buf.slice(1, 33);
var ybuf = buf.slice(33, 65);
if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65)
throw new Error('Length of x and y must be 32 bytes');
var x = bn(xbuf);
var y = bn(ybuf);
this.point = Point(x, y);
this.compressed = false;
} else if (buf[0] == 0x03) {
var xbuf = buf.slice(1);
var x = bn(xbuf);
this.fromX(true, x);
this.compressed = true;
} else if (buf[0] == 0x02) {
var xbuf = buf.slice(1);
var x = bn(xbuf);
this.fromX(false, x);
this.compressed = true;
} else {
throw new Error('Invalid DER format pubkey');
}
return this;
};
Pubkey.prototype.fromString = function(str) {
this.fromDER(new Buffer(str, 'hex'));
};
Pubkey.prototype.fromX = function(odd, x) {
if (typeof odd !== 'boolean')
throw new Error('Must specify whether y is odd or not (true or false)');
this.point = Point.fromX(odd, x);
};
Pubkey.prototype.toBuffer = function() {
var compressed = typeof this.compressed === 'undefined' ? true : this.compressed;
return this.toDER(compressed);
};
Pubkey.prototype.toDER = function(compressed) {
compressed = typeof this.compressed === 'undefined' ? compressed : this.compressed;
if (typeof compressed !== 'boolean')
throw new Error('Must specify whether the public key is compressed or not (true or false)');
var x = this.point.getX();
var y = this.point.getY();
var xbuf = x.toBuffer({size: 32});
var ybuf = y.toBuffer({size: 32});
if (!compressed) {
var prefix = new Buffer([0x04]);
return Buffer.concat([prefix, xbuf, ybuf]);
} else {
var odd = ybuf[ybuf.length - 1] % 2;
if (odd)
var prefix = new Buffer([0x03]);
else
var prefix = new Buffer([0x02]);
return Buffer.concat([prefix, xbuf]);
}
};
Pubkey.prototype.toString = function() {
var compressed = typeof this.compressed === 'undefined' ? true : this.compressed;
return this.toDER(compressed).toString('hex');
};
//https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf
Pubkey.prototype.validate = function() {
if (this.point.isInfinity())
throw new Error('point: Point cannot be equal to Infinity');
if (this.point.eq(Point(bn(0), bn(0))))
throw new Error('point: Point cannot be equal to 0, 0');
this.point.validate();
return this;
};
module.exports = Pubkey;

54
lib/random.js

@ -0,0 +1,54 @@
function Random() {
};
/* secure random bytes that sometimes throws an error due to lack of entropy */
Random.getRandomBuffer = function(size) {
if (process.browser)
return Random.getRandomBufferBrowser(size);
else
return Random.getRandomBufferNode(size);
};
Random.getRandomBufferNode = function(size) {
var crypto = require('crypto');
return crypto.randomBytes(size);
}
Random.getRandomBufferBrowser = function(size) {
if (!window.crypto && !window.msCrypto)
throw new Error('window.crypto not available');
if (window.crypto && window.crypto.getRandomValues)
var crypto = window.crypto;
else if (window.msCrypto && window.msCrypto.getRandomValues) //internet explorer
var crypto = window.msCrypto;
else
throw new Error('window.crypto.getRandomValues not available');
var bbuf = new Uint8Array(size);
crypto.getRandomValues(bbuf);
var buf = new Buffer(bbuf);
return buf;
};
/* insecure random bytes, but it never fails */
Random.getPseudoRandomBuffer = function(size) {
var b32 = 0x100000000;
var b = new Buffer(size);
for (var i = 0; i <= size; i++) {
var j = Math.floor(i / 4);
var k = i - j * 4;
if (k == 0) {
r = Math.random() * b32;
b[i] = r & 0xff;
} else {
b[i] = (r = r >>> 8) & 0xff;
}
}
return b;
};
module.exports = Random;

281
lib/script.js

@ -0,0 +1,281 @@
var BufferReader = require('./bufferreader');
var BufferWriter = require('./bufferwriter');
var Opcode = require('./opcode');
var Script = function Script(buf) {
if (!(this instanceof Script))
return new Script(buf);
this.chunks = [];
if (Buffer.isBuffer(buf)) {
this.fromBuffer(buf);
}
else if (typeof buf === 'string') {
var str = buf;
this.fromString(str);
}
else if (typeof buf !== 'undefined') {
var obj = buf;
this.set(obj);
}
};
Script.prototype.set = function(obj) {
this.chunks = obj.chunks || this.chunks;
return this;
};
Script.prototype.fromJSON = function(json) {
return this.fromString(json);
};
Script.prototype.toJSON = function() {
return this.toString();
};
Script.prototype.fromBuffer = function(buf) {
this.chunks = [];
var br = new BufferReader(buf);
while (!br.eof()) {
var opcodenum = br.readUInt8();
var len, buf;
if (opcodenum > 0 && opcodenum < Opcode.map.OP_PUSHDATA1) {
len = opcodenum;
this.chunks.push({
buf: br.read(len),
len: len,
opcodenum: opcodenum
});
} else if (opcodenum === Opcode.map.OP_PUSHDATA1) {
len = br.readUInt8();
var buf = br.read(len);
this.chunks.push({
buf: buf,
len: len,
opcodenum: opcodenum
});
} else if (opcodenum === Opcode.map.OP_PUSHDATA2) {
len = br.readUInt16LE();
buf = br.read(len);
this.chunks.push({
buf: buf,
len: len,
opcodenum: opcodenum
});
} else if (opcodenum === Opcode.map.OP_PUSHDATA4) {
len = br.readUInt32LE();
buf = br.read(len);
this.chunks.push({
buf: buf,
len: len,
opcodenum: opcodenum
});
} else {
this.chunks.push(opcodenum);
}
}
return this;
};
Script.prototype.toBuffer = function() {
var bw = new BufferWriter();
for (var i = 0; i < this.chunks.length; i++) {
var chunk = this.chunks[i];
if (typeof chunk === 'number') {
var opcodenum = chunk;
bw.writeUInt8(opcodenum);
} else {
var opcodenum = chunk.opcodenum;
bw.writeUInt8(chunk.opcodenum);
if (opcodenum < Opcode.map.OP_PUSHDATA1) {
bw.write(chunk.buf);
}
else if (opcodenum === Opcode.map.OP_PUSHDATA1) {
bw.writeUInt8(chunk.len);
bw.write(chunk.buf);
}
else if (opcodenum === Opcode.map.OP_PUSHDATA2) {
bw.writeUInt16LE(chunk.len);
bw.write(chunk.buf);
}
else if (opcodenum === Opcode.map.OP_PUSHDATA4) {
bw.writeUInt32LE(chunk.len);
bw.write(chunk.buf);
}
}
}
return bw.concat();
};
Script.prototype.fromString = function(str) {
this.chunks = [];
var tokens = str.split(' ');
var i = 0;
while (i < tokens.length) {
var token = tokens[i];
var opcode = Opcode(token);
var opcodenum = opcode.toNumber();
if (typeof opcodenum === 'undefined') {
opcodenum = parseInt(token);
if (opcodenum > 0 && opcodenum < Opcode.map.OP_PUSHDATA1) {
this.chunks.push({
buf: new Buffer(tokens[i + 1].slice(2), 'hex'),
len: opcodenum,
opcodenum: opcodenum
});
i = i + 2;
}
else {
throw new Error('Invalid script');
}
} else if (opcodenum === Opcode.map.OP_PUSHDATA1 || opcodenum === Opcode.map.OP_PUSHDATA2 || opcodenum === Opcode.map.OP_PUSHDATA4) {
if (tokens[i + 2].slice(0, 2) != '0x')
throw new Error('Pushdata data must start with 0x');
this.chunks.push({
buf: new Buffer(tokens[i + 2].slice(2), 'hex'),
len: parseInt(tokens[i + 1]),
opcodenum: opcodenum
});
i = i + 3;
} else {
this.chunks.push(opcodenum);
i = i + 1;
}
}
return this;
};
Script.prototype.toString = function() {
var str = "";
for (var i = 0; i < this.chunks.length; i++) {
var chunk = this.chunks[i];
if (typeof chunk === 'number') {
var opcodenum = chunk;
str = str + Opcode(opcodenum).toString() + " ";
} else {
var opcodenum = chunk.opcodenum;
if (opcodenum === Opcode.map.OP_PUSHDATA1 || opcodenum === Opcode.map.OP_PUSHDATA2 || opcodenum === Opcode.map.OP_PUSHDATA4)
str = str + Opcode(opcodenum).toString() + " " ;
str = str + chunk.len + " " ;
str = str + "0x" + chunk.buf.toString('hex') + " ";
}
}
return str.substr(0, str.length - 1);
};
Script.prototype.isOpReturn = function() {
if (this.chunks[0] === Opcode('OP_RETURN').toNumber()
&&
(this.chunks.length === 1
||
(this.chunks.length === 2
&& this.chunks[1].buf
&& this.chunks[1].buf.length <= 40
&& this.chunks[1].length === this.chunks.len))) {
return true;
} else {
return false;
}
};
Script.prototype.isPubkeyhashOut = function() {
if (this.chunks[0] === Opcode('OP_DUP').toNumber()
&& this.chunks[1] === Opcode('OP_HASH160').toNumber()
&& this.chunks[2].buf
&& this.chunks[3] === Opcode('OP_EQUALVERIFY').toNumber()
&& this.chunks[4] === Opcode('OP_CHECKSIG').toNumber()) {
return true;
} else {
return false;
}
};
Script.prototype.isPubkeyhashIn = function() {
if (this.chunks.length === 2
&& this.chunks[0].buf
&& this.chunks[1].buf) {
return true;
} else {
return false;
}
};
Script.prototype.isScripthashOut = function() {
if (this.chunks.length === 3
&& this.chunks[0] === Opcode('OP_HASH160').toNumber()
&& this.chunks[1].buf
&& this.chunks[1].buf.length === 20
&& this.chunks[2] === Opcode('OP_EQUAL').toNumber()) {
return true;
} else {
return false;
}
};
//note that these are frequently indistinguishable from pubkeyhashin
Script.prototype.isScripthashIn = function() {
var allpush = this.chunks.every(function(chunk) {
return Buffer.isBuffer(chunk.buf);
});
if (allpush) {
return true;
} else {
return false;
}
};
Script.prototype.write = function(obj) {
if (typeof obj === 'string')
this.writeOp(obj);
else if (typeof obj === 'number')
this.writeOp(obj);
else if (Buffer.isBuffer(obj))
this.writeBuffer(obj);
else if (typeof obj === 'object')
this.chunks.push(obj);
else
throw new Error('Invalid script chunk');
return this;
};
Script.prototype.writeOp = function(str) {
if (typeof str === 'number')
this.chunks.push(str);
else
this.chunks.push(Opcode(str).toNumber());
return this;
};
Script.prototype.writeBuffer = function(buf) {
var opcodenum;
var len = buf.length;
if (buf.length > 0 && buf.length < Opcode.map.OP_PUSHDATA1) {
opcodenum = buf.length;
} else if (buf.length < Math.pow(2, 8)) {
opcodenum = Opcode.map.OP_PUSHDATA1;
} else if (buf.length < Math.pow(2, 16)) {
opcodenum = Opcode.map.OP_PUSHDATA2;
} else if (buf.length < Math.pow(2, 32)) {
opcodenum = Opcode.map.OP_PUSHDATA4;
} else {
throw new Error("You can't push that much data");
}
this.chunks.push({
buf: buf,
len: len,
opcodenum: opcodenum
});
return this;
};
module.exports = Script;

169
lib/signature.js

@ -0,0 +1,169 @@
var BN = require('./bn');
var Point = require('./point');
var Pubkey = require('./pubkey');
var Signature = function Signature(r, s) {
if (!(this instanceof Signature))
return new Signature(r, s);
if (r instanceof BN) {
this.set({
r: r,
s: s
});
}
else if (r) {
var obj = r;
this.set(obj);
}
};
Signature.prototype.set = function(obj) {
this.r = obj.r || this.r || undefined;
this.s = obj.s || this.s || undefined;
this.i = typeof obj.i !== 'undefined' ? obj.i : this.i; //public key recovery parameter in range [0, 3]
this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; //whether the recovered pubkey is compressed
return this;
};
Signature.prototype.fromCompact = function(buf) {
var compressed = true;
if (i < 0) {
var compressed = false;
i = i + 4;
}
var i = buf.slice(0, 1)[0] - 27 - 4; //TODO: handle uncompressed pubkeys
var b2 = buf.slice(1, 33);
var b3 = buf.slice(33, 65);
if (!(i === 0 || i === 1 || i === 2 || i === 3))
throw new Error('i must be 0, 1, 2, or 3');
if (b2.length !== 32)
throw new Error('r must be 32 bytes');
if (b3.length !== 32)
throw new Error('s must be 32 bytes');
this.compressed = compressed;
this.i = i;
this.r = BN().fromBuffer(b2);
this.s = BN().fromBuffer(b3);
return this;
};
Signature.prototype.fromDER = function(buf) {
var obj = Signature.parseDER(buf);
this.r = obj.r;
this.s = obj.s;
return this;
};
Signature.prototype.fromString = function(str) {
var buf = new Buffer(str, 'hex');
this.fromDER(buf);
return this;
};
Signature.parseDER = function(buf) {
if (!Buffer.isBuffer(buf))
throw new Error('DER formatted signature should be a buffer');
var header = buf[0];
if (header !== 0x30)
throw new Error('Header byte should be 0x30');
var length = buf[1];
if (length !== buf.slice(2).length)
throw new Error('Length byte should length of what follows');
var rheader = buf[2 + 0];
if (rheader !== 0x02)
throw new Error('Integer byte for r should be 0x02');
var rlength = buf[2 + 1];
var rbuf = buf.slice(2 + 2, 2 + 2 + rlength);
var r = BN().fromBuffer(rbuf);
var rneg = buf[2 + 1 + 1] === 0x00 ? true : false;
if (rlength !== rbuf.length)
throw new Error('Length of r incorrect');
var sheader = buf[2 + 2 + rlength + 0];
if (sheader !== 0x02)
throw new Error('Integer byte for s should be 0x02');
var slength = buf[2 + 2 + rlength + 1];
var sbuf = buf.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength);
var s = BN().fromBuffer(sbuf);
var sneg = buf[2 + 2 + rlength + 2 + 2] === 0x00 ? true : false;
if (slength !== sbuf.length)
throw new Error('Length of s incorrect');
var sumlength = 2 + 2 + rlength + 2 + slength;
if (length !== sumlength - 2)
throw new Error('Length of signature incorrect');
var obj = {
header: header,
length: length,
rheader: rheader,
rlength: rlength,
rneg: rneg,
rbuf: rbuf,
r: r,
sheader: sheader,
slength: slength,
sneg: sneg,
sbuf: sbuf,
s: s
};
return obj;
};
Signature.prototype.toCompact = function(i, compressed) {
i = typeof i === 'number' ? i : this.i;
compressed = typeof compressed === 'boolean' ? compressed : this.compressed;
if (!(i === 0 || i === 1 || i === 2 || i === 3))
throw new Error('i must be equal to 0, 1, 2, or 3');
var val = i + 27 + 4;
if (compressed === false)
val = val - 4;
var b1 = new Buffer([val]);
var b2 = this.r.toBuffer({size: 32});
var b3 = this.s.toBuffer({size: 32});
return Buffer.concat([b1, b2, b3]);
};
Signature.prototype.toDER = function() {
var rnbuf = this.r.toBuffer();
var snbuf = this.s.toBuffer();
var rneg = rnbuf[0] & 0x80 ? true : false;
var sneg = snbuf[0] & 0x80 ? true : false;
var rbuf = rneg ? Buffer.concat([new Buffer([0x00]), rnbuf]) : rnbuf;
var sbuf = sneg ? Buffer.concat([new Buffer([0x00]), snbuf]) : snbuf;
var length = 2 + rbuf.length + 2 + sbuf.length;
var rlength = rbuf.length;
var slength = sbuf.length;
var rheader = 0x02;
var sheader = 0x02;
var header = 0x30;
var der = Buffer.concat([new Buffer([header, length, rheader, rlength]), rbuf, new Buffer([sheader, slength]), sbuf]);
return der;
};
Signature.prototype.toString = function() {
var buf = this.toDER();
return buf.toString('hex');
};
module.exports = Signature;

155
lib/transaction.js

@ -0,0 +1,155 @@
var Txin = require('./txin');
var Txout = require('./txout');
var BufferWriter = require('./bufferwriter');
var BufferReader = require('./bufferreader');
var Varint = require('./varint');
var Hash = require('./hash');
var Transaction = function Transaction(version, txinsvi, txins, txoutsvi, txouts, nlocktime) {
if (!(this instanceof Transaction))
return new Transaction(version, txinsvi, txins, txoutsvi, txouts, nlocktime);
if (typeof version === 'number') {
this.initialize();
this.set({
version: version,
txinsvi: txinsvi,
txins: txins,
txoutsvi: txoutsvi,
txouts: txouts,
nlocktime: nlocktime
});
} else if (Buffer.isBuffer(version)) {
//not necessary to initialize, since everything should be overwritten
var txbuf = version;
this.fromBuffer(txbuf);
} else if (version) {
this.initialize();
var obj = version;
this.set(obj);
} else {
this.initialize();
}
};
Transaction.prototype.initialize = function() {
this.version = 1;
this.txinsvi = Varint(0);
this.txins = [];
this.txoutsvi = Varint(0);
this.txouts = [];
this.nlocktime = 0xffffffff;
return this;
};
Transaction.prototype.set = function(obj) {
this.version = typeof obj.version !== 'undefined' ? obj.version : this.version;
this.txinsvi = obj.txinsvi || this.txinsvi;
this.txins = obj.txins || this.txins;
this.txoutsvi = obj.txoutsvi || this.txoutsvi;
this.txouts = obj.txouts || this.txouts;
this.nlocktime = typeof obj.nlocktime !== 'undefined' ? obj.nlocktime : this.nlocktime;
return this;
};
Transaction.prototype.fromJSON = function(json) {
var txins = [];
json.txins.forEach(function(txin) {
txins.push(Txin().fromJSON(txin));
});
var txouts = [];
json.txouts.forEach(function(txout) {
txouts.push(Txout().fromJSON(txout));
});
this.set({
version: json.version,
txinsvi: Varint().fromJSON(json.txinsvi),
txins: txins,
txoutsvi: Varint().fromJSON(json.txoutsvi),
txouts: txouts,
nlocktime: json.nlocktime
});
return this;
};
Transaction.prototype.toJSON = function() {
var txins = [];
this.txins.forEach(function(txin) {
txins.push(txin.toJSON());
});
var txouts = [];
this.txouts.forEach(function(txout) {
txouts.push(txout.toJSON());
});
return {
version: this.version,
txinsvi: this.txinsvi.toJSON(),
txins: txins,
txoutsvi: this.txoutsvi.toJSON(),
txouts: txouts,
nlocktime: this.nlocktime
};
};
Transaction.prototype.fromBuffer = function(buf) {
return this.fromBufferReader(BufferReader(buf));
};
Transaction.prototype.fromBufferReader = function(br) {
this.version = br.readUInt32LE();
this.txinsvi = Varint(br.readVarintBuf());
var txinsnum = this.txinsvi.toNumber();
this.txins = [];
for (var i = 0; i < txinsnum; i++) {
this.txins.push(Txin().fromBufferReader(br));
}
this.txoutsvi = Varint(br.readVarintBuf());
var txoutsnum = this.txoutsvi.toNumber();
this.txouts = [];
for (var i = 0; i < txoutsnum; i++) {
this.txouts.push(Txout().fromBufferReader(br));
}
this.nlocktime = br.readUInt32LE();
return this;
};
Transaction.prototype.toBuffer = function() {
return this.toBufferWriter().concat();
};
Transaction.prototype.toBufferWriter = function(bw) {
if (!bw)
bw = new BufferWriter();
bw.writeUInt32LE(this.version);
bw.write(this.txinsvi.buf);
for (var i = 0; i < this.txins.length; i++) {
this.txins[i].toBufferWriter(bw);
}
bw.write(this.txoutsvi.buf)
for (var i = 0; i < this.txouts.length; i++) {
this.txouts[i].toBufferWriter(bw);
}
bw.writeUInt32LE(this.nlocktime);
return bw;
};
Transaction.prototype.hash = function() {
return Hash.sha256sha256(this.toBuffer());
};
Transaction.prototype.id = function() {
return BufferReader(this.hash()).reverse().read();
};
Transaction.prototype.pushin = function(txin) {
this.txins.push(txin);
this.txinsvi = Varint(this.txinsvi.toNumber() + 1);
return this;
};
Transaction.prototype.pushout = function(txout) {
this.txouts.push(txout);
this.txoutsvi = Varint(this.txoutsvi.toNumber() + 1);
return this;
};
module.exports = Transaction;

81
lib/txin.js

@ -0,0 +1,81 @@
var BufferReader = require('./bufferreader');
var BufferWriter = require('./bufferwriter');
var Varint = require('./varint');
var Script = require('./script');
var Txin = function Txin(txidbuf, txoutnum, scriptvi, script, seqnum) {
if (!(this instanceof Txin))
return new Txin(txidbuf, txoutnum, scriptvi, script, seqnum);
if (Buffer.isBuffer(txidbuf)) {
if (txidbuf.length !== 32)
throw new Error('txidbuf must be 32 bytes');
this.txidbuf = txidbuf;
this.txoutnum = txoutnum;
this.scriptvi = scriptvi;
this.script = script;
this.seqnum = seqnum;
} else if (txidbuf) {
var obj = txidbuf;
this.set(obj);
}
};
Txin.prototype.set = function(obj) {
this.txidbuf = obj.txidbuf || this.txidbuf;
this.txoutnum = typeof obj.txoutnum !== 'undefined' ? obj.txoutnum : this.txoutnum;
this.scriptvi = typeof obj.scriptvi !== 'undefined' ? obj.scriptvi : this.scriptvi;
this.script = obj.script || this.script;
this.seqnum = typeof obj.seqnum !== 'undefined' ? obj.seqnum : this.seqnum;
return this;
};
Txin.prototype.fromJSON = function(json) {
this.set({
txidbuf: new Buffer(json.txidbuf, 'hex'),
txoutnum: json.txoutnum,
scriptvi: Varint().fromJSON(json.scriptvi),
script: Script().fromJSON(json.script),
seqnum: json.seqnum
});
return this;
};
Txin.prototype.toJSON = function() {
return {
txidbuf: this.txidbuf.toString('hex'),
txoutnum: this.txoutnum,
scriptvi: this.scriptvi.toJSON(),
script: this.script.toJSON(),
seqnum: this.seqnum
};
};
Txin.prototype.fromBuffer = function(buf) {
return this.fromBufferReader(BufferReader(buf));
};
Txin.prototype.fromBufferReader = function(br) {
this.txidbuf = br.read(32);
this.txoutnum = br.readUInt32LE();
this.scriptvi = Varint(br.readVarintBuf());
this.script = Script().fromBuffer(br.read(this.scriptvi.toNumber()));
this.seqnum = br.readUInt32LE();
return this;
};
Txin.prototype.toBuffer = function() {
return this.toBufferWriter().concat();
};
Txin.prototype.toBufferWriter = function(bw) {
if (!bw)
bw = new BufferWriter();
bw.write(this.txidbuf);
bw.writeUInt32LE(this.txoutnum);
bw.write(this.scriptvi.buf);
bw.write(this.script.toBuffer());
bw.writeUInt32LE(this.seqnum);
return bw;
};
module.exports = Txin;

71
lib/txout.js

@ -0,0 +1,71 @@
var BN = require('./bn');
var BufferReader = require('./bufferreader');
var BufferWriter = require('./bufferwriter');
var Varint = require('./varint');
var Script = require('./script');
var Txout = function Txout(valuebn, scriptvi, script) {
if (!(this instanceof Txout))
return new Txout(valuebn, scriptvi, script);
if (valuebn instanceof BN) {
this.set({
valuebn: valuebn,
scriptvi: scriptvi,
script: script
});
} else if (valuebn) {
var obj = valuebn;
this.set(obj);
}
};
Txout.prototype.set = function(obj) {
this.valuebn = obj.valuebn || this.valuebn;
this.scriptvi = obj.scriptvi || this.scriptvi;
this.script = obj.script || this.script;
return this;
};
Txout.prototype.fromJSON = function(json) {
this.set({
valuebn: BN().fromJSON(json.valuebn),
scriptvi: Varint().fromJSON(json.scriptvi),
script: Script().fromJSON(json.script)
});
return this;
};
Txout.prototype.toJSON = function() {
return {
valuebn: this.valuebn.toJSON(),
scriptvi: this.scriptvi.toJSON(),
script: this.script.toJSON()
};
};
Txout.prototype.fromBuffer = function(buf) {
return this.fromBufferReader(BufferReader(buf));
};
Txout.prototype.fromBufferReader = function(br) {
this.valuebn = br.readUInt64LEBN();
this.scriptvi = Varint(br.readVarintNum());
this.script = Script().fromBuffer(br.read(this.scriptvi.toNumber()));
return this;
};
Txout.prototype.toBuffer = function() {
var bw = new BufferWriter();
return this.toBufferWriter(bw).concat();
};
Txout.prototype.toBufferWriter = function(bw) {
if (!bw)
bw = new BufferWriter();
bw.writeUInt64LEBN(this.valuebn);
bw.write(this.scriptvi.buf);
bw.write(this.script.toBuffer());
return bw;
};
module.exports = Txout;

70
lib/varint.js

@ -0,0 +1,70 @@
var BufferWriter = require('./bufferwriter');
var BufferReader = require('./bufferreader');
var BN = require('./bn');
var Varint = function Varint(buf) {
if (!(this instanceof Varint))
return new Varint(buf);
if (Buffer.isBuffer(buf)) {
this.buf = buf;
} else if (typeof buf === 'number') {
var num = buf;
this.fromNumber(num);
} else if (buf instanceof BN) {
var bn = buf;
this.fromBN(bn);
} else if (buf) {
var obj = buf;
this.set(obj);
}
};
Varint.prototype.set = function(obj) {
this.buf = obj.buf || this.buf;
return this;
};
Varint.prototype.fromJSON = function(json) {
this.set({
buf: new Buffer(json, 'hex')
});
return this;
};
Varint.prototype.toJSON = function() {
return this.buf.toString('hex');
};
Varint.prototype.fromBuffer = function(buf) {
this.buf = buf;
return this;
};
Varint.prototype.fromBufferReader = function(br) {
this.buf = br.readVarintBuf();
return this;
};
Varint.prototype.fromBN = function(bn) {
this.buf = BufferWriter().writeVarintBN(bn).concat();
return this;
};
Varint.prototype.fromNumber = function(num) {
this.buf = BufferWriter().writeVarintNum(num).concat();
return this;
};
Varint.prototype.toBuffer = function() {
return this.buf;
};
Varint.prototype.toBN = function() {
return BufferReader(this.buf).readVarintBN();
};
Varint.prototype.toNumber = function() {
return BufferReader(this.buf).readVarintNum();
};
module.exports = Varint;

58
npm-shrinkwrap.json

@ -0,0 +1,58 @@
{
"name": "bitcore2",
"version": "0.0.0",
"dependencies": {
"aes": {
"version": "0.1.0",
"from": "aes@=0.1.0",
"resolved": "https://registry.npmjs.org/aes/-/aes-0.1.0.tgz"
},
"bn.js": {
"version": "0.14.0",
"from": "bn.js@0.14.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-0.14.0.tgz"
},
"bs58": {
"version": "1.2.1",
"from": "bs58@=1.2.1",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-1.2.1.tgz"
},
"elliptic": {
"version": "0.15.10",
"from": "elliptic@0.15.10",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-0.15.10.tgz",
"dependencies": {
"bn.js": {
"version": "0.11.7",
"from": "bn.js@0.11.7",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-0.11.7.tgz"
},
"hash.js": {
"version": "0.2.1",
"from": "hash.js@0.2.1",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-0.2.1.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@^2.0.1"
}
}
},
"hash.js": {
"version": "0.3.1",
"from": "hash.js@0.3.1",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-0.3.1.tgz",
"dependencies": {
"inherits": {
"version": "2.0.1",
"from": "inherits@^2.0.1"
}
}
},
"sha512": {
"version": "0.0.1",
"from": "sha512@=0.0.1",
"resolved": "https://registry.npmjs.org/sha512/-/sha512-0.0.1.tgz"
}
}
}

79
package.json

@ -0,0 +1,79 @@
{
"name": "fullnode",
"version": "0.0.0",
"description": "Bitcoin library, CLI and API.",
"author": "Ryan X. Charles <ryanxcharles@gmail.com>",
"main": "index.js",
"scripts": {
"test": "mocha"
},
"contributors": [
{
"name": "Daniel Cousens",
"email": "bitcoin@dcousens.com"
},
{
"name": "Gordon Hall",
"email": "gordon@bitpay.com"
},
{
"name": "Jeff Garzik",
"email": "jgarzik@bitpay.com"
},
{
"name": "Kyle Drake",
"email": "kyle@kyledrake.net"
},
{
"name": "Manuel Araoz",
"email": "manuelaraoz@gmail.com"
},
{
"name": "Matias Alejo Garcia",
"email": "ematiu@gmail.com"
},
{
"name": "Ryan X. Charles",
"email": "ryanxcharles@gmail.com"
},
{
"name": "Stefan Thomas",
"email": "moon@justmoon.net"
},
{
"name": "Stephen Pair",
"email": "stephen@bitpay.com"
},
{
"name": "Wei Lu",
"email": "luwei.here@gmail.com"
}
],
"keywords": [
"bitcoin",
"bip32",
"bip37",
"bip70",
"stealth",
"merge",
"multisig"
],
"repository": {
"type": "git",
"url": "https://github.com/ryanxcharles/fullnode.git"
},
"dependencies": {
"aes": "=0.1.0",
"bn.js": "=0.14.0",
"bs58": "=1.2.1",
"elliptic": "=0.15.10",
"hash.js": "=0.3.1",
"sha512": "=0.0.1"
},
"devDependencies": {
"chai": "~1.9.1",
"mocha": "~1.21.0",
"browserify": "~5.9.1"
},
"license": "MIT"
}

203
test/address.js

@ -0,0 +1,203 @@
var should = require('chai').should();
var constants = require('../lib/constants');
var Pubkey = require('../lib/pubkey');
var Address = require('../lib/address');
var Script = require('../lib/script');
describe('Address', function() {
var pubkeyhash = new Buffer('3c3fa3d4adcaf8f52d5b1843975e122548269937', 'hex');
var buf = Buffer.concat([new Buffer([0]), pubkeyhash]);
var str = '16VZnHwRhwrExfeHFHGjwrgEMq8VcYPs9r';
it('should create a new address object', function() {
var address = new Address();
should.exist(address);
address = Address(buf);
should.exist(address);
address = Address(str);
should.exist(address);
});
describe('@isValid', function() {
it('should validate this valid address string', function() {
Address.isValid(str).should.equal(true);
});
it('should invalidate this valid address string', function() {
Address.isValid(str.substr(1)).should.equal(false);
});
});
describe('#fromBuffer', function() {
it('should make an address from a buffer', function() {
Address().fromBuffer(buf).toString().should.equal(str);
});
});
describe('#fromHashbuf', function() {
it('should make an address from a hashbuf', function() {
Address().fromHashbuf(pubkeyhash).toString().should.equal(str);
var a = Address().fromHashbuf(pubkeyhash, 'testnet', 'scripthash');
a.networkstr.should.equal('testnet');
a.typestr.should.equal('scripthash');
});
it('should throw an error for invalid length hashbuf', function() {
(function() {
Address().fromHashbuf(buf);
}).should.throw('hashbuf must be exactly 20 bytes');
});
});
describe('#fromPubkey', function() {
it('should make this address from a compressed pubkey', function() {
var pubkey = new Pubkey();
pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', 'hex'));
var address = new Address();
address.fromPubkey(pubkey);
address.toString().should.equal('19gH5uhqY6DKrtkU66PsZPUZdzTd11Y7ke');
});
it('should make this address from an uncompressed pubkey', function() {
var pubkey = new Pubkey();
pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', 'hex'));
var address = new Address();
pubkey.compressed = false;
address.fromPubkey(pubkey, 'mainnet');
address.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX');
});
});
describe('#fromScript', function() {
it('should make this address from a script', function() {
var script = Script().fromString("OP_CHECKMULTISIG");
var address = Address().fromScript(script);
address.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm');
});
it('should make this address from other script', function() {
var script = Script().fromString("OP_CHECKSIG OP_HASH160");
var address = Address().fromScript(script);
address.toString().should.equal('347iRqVwks5r493N1rsLN4k9J7Ljg488W7');
});
});
describe('#fromString', function() {
it('should derive from this known address string mainnet', function() {
var address = new Address();
address.fromString(str);
address.toBuffer().slice(1).toString('hex').should.equal(pubkeyhash.toString('hex'));
});
it('should derive from this known address string testnet', function() {
var address = new Address();
address.fromString(str);
address.networkstr = 'testnet';
address.fromString(address.toString());
address.toString().should.equal('mm1X5M2QWyHVjn7txrF7mmtZDpjCXzoa98');
});
it('should derive from this known address string mainnet scripthash', function() {
var address = new Address();
address.fromString(str);
address.networkstr = 'mainnet';
address.typestr = 'scripthash';
address.fromString(address.toString());
address.toString().should.equal('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo');
});
it('should derive from this known address string testnet scripthash', function() {
var address = new Address();
address.fromString(str);
address.networkstr = 'testnet';
address.typestr = 'scripthash';
address.fromString(address.toString());
address.toString().should.equal('2MxjnmaMtsJfyFcyG3WZCzS2RihdNuWqeX4');
});
});
describe('#isValid', function() {
it('should describe this valid address as valid', function() {
var address = new Address();
address.fromString('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo');
address.isValid().should.equal(true);
});
it('should describe this address with unknown network as invalid', function() {
var address = new Address();
address.fromString('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo');
address.networkstr = 'unknown';
address.isValid().should.equal(false);
});
it('should describe this address with unknown type as invalid', function() {
var address = new Address();
address.fromString('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo');
address.typestr = 'unknown';
address.isValid().should.equal(false);
});
});
describe('#toBuffer', function() {
it('should output this known hash', function() {
var address = new Address();
address.fromString(str);
address.toBuffer().slice(1).toString('hex').should.equal(pubkeyhash.toString('hex'));
});
});
describe('#toString', function() {
it('should output the same thing that was input', function() {
var address = new Address();
address.fromString(str);
address.toString().should.equal(str);
});
});
describe('#validate', function() {
it('should not throw an error on this valid address', function() {
var address = new Address();
address.fromString(str);
should.exist(address.validate());
});
it('should throw an error on this invalid network', function() {
var address = new Address();
address.fromString(str);
address.networkstr = 'unknown';
(function() {
address.validate();
}).should.throw('networkstr must be "mainnet" or "testnet"');
});
it('should throw an error on this invalid type', function() {
var address = new Address();
address.fromString(str);
address.typestr = 'unknown';
(function() {
address.validate();
}).should.throw('typestr must be "pubkeyhash" or "scripthash"');
});
});
});

85
test/aes.js

@ -0,0 +1,85 @@
var should = require('chai').should();
var Hash = require('../lib/hash');
var AES = require('../lib/expmt/aes');
describe('AES', function() {
var m128 = Hash.sha256(new Buffer('test1')).slice(0, 128 / 8);
var k128 = Hash.sha256(new Buffer('test2')).slice(0, 128 / 8);
var k192 = Hash.sha256(new Buffer('test2')).slice(0, 192 / 8);
var k256 = Hash.sha256(new Buffer('test2')).slice(0, 256 / 8);
var e128 = new Buffer('3477e13884125038f4dc24e9d2cfbbc7', 'hex');
var e192 = new Buffer('b670954c0e2da1aaa5f9063de04eb961', 'hex');
var e256 = new Buffer('dd2ce24581183a4a7c0b1068f8bc79f0', 'hex');
describe('@encrypt', function() {
it('should encrypt with a 128 bit key', function() {
var encbuf = AES.encrypt(m128, k128);
encbuf.toString('hex').should.equal(e128.toString('hex'));
});
it('should encrypt with a 192 bit key', function() {
var encbuf = AES.encrypt(m128, k192);
encbuf.toString('hex').should.equal(e192.toString('hex'));
});
it('should encrypt with a 256 bit key', function() {
var encbuf = AES.encrypt(m128, k256);
encbuf.toString('hex').should.equal(e256.toString('hex'));
});
});
describe('@decrypt', function() {
it('should encrypt/decrypt with a 128 bit key', function() {
var encbuf = AES.encrypt(m128, k128);
var m = AES.decrypt(encbuf, k128);
m.toString('hex').should.equal(m128.toString('hex'));
});
it('should encrypt/decrypt with a 192 bit key', function() {
var encbuf = AES.encrypt(m128, k192);
var m = AES.decrypt(encbuf, k192);
m.toString('hex').should.equal(m128.toString('hex'));
});
it('should encrypt/decrypt with a 256 bit key', function() {
var encbuf = AES.encrypt(m128, k256);
var m = AES.decrypt(encbuf, k256);
m.toString('hex').should.equal(m128.toString('hex'));
});
});
describe('@buf2words', function() {
it('should convert this 4 length buffer into an array', function() {
var buf = new Buffer([0, 0, 0, 0]);
var words = AES.buf2words(buf);
words.length.should.equal(1);
});
it('should throw an error on this 5 length buffer', function() {
var buf = new Buffer([0, 0, 0, 0, 0]);
(function() {
var words = AES.buf2words(buf);
}).should.throw();
});
});
describe('@words2buf', function() {
it('should convert this array into a buffer', function() {
var a = [100, 0];
var buf = AES.words2buf(a);
buf.length.should.equal(8);
});
});
});

73
test/aescbc.js

@ -0,0 +1,73 @@
var should = require('chai').should();
var AESCBC = require('../lib/expmt/aescbc');
describe('AESCBC', function() {
describe('@encrypt', function() {
it('should return encrypt one block', function() {
var password = "password";
var messagebuf = new Buffer(128 / 8 - 1);
messagebuf.fill(0);
var encbuf = AESCBC.encrypt(messagebuf, password);
encbuf.length.should.equal(128 / 8 + 128 / 8);
});
});
describe('@decrypt', function() {
it('should decrypt that which was encrypted', function() {
var password = "password";
var messagebuf = new Buffer(128 / 8 - 1);
messagebuf.fill(0);
var encbuf = AESCBC.encrypt(messagebuf, password);
var messagebuf2 = AESCBC.decrypt(encbuf, password);
messagebuf2.toString('hex').should.equal(messagebuf.toString('hex'));
});
});
describe('@encryptCipherkey', function() {
it('should return encrypt one block', function() {
var cipherkeybuf = new Buffer(256 / 8);
cipherkeybuf.fill(0x10);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0);
var messagebuf = new Buffer(128 / 8 - 1);
messagebuf.fill(0);
var encbuf = AESCBC.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf);
encbuf.length.should.equal(128 / 8 + 128 / 8);
});
it('should return encrypt two blocks', function() {
var cipherkeybuf = new Buffer(256 / 8);
cipherkeybuf.fill(0x10);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0);
var messagebuf = new Buffer(128 / 8);
messagebuf.fill(0);
var encbuf = AESCBC.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf);
encbuf.length.should.equal(128 / 8 + 128 / 8 + 128 / 8);
});
});
describe('@decryptCipherkey', function() {
it('should decrypt that which was encrypted', function() {
var cipherkeybuf = new Buffer(256 / 8);
cipherkeybuf.fill(0x10);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0);
var messagebuf = new Buffer(128 / 8);
messagebuf.fill(0);
var encbuf = AESCBC.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf);
var messagebuf2 = AESCBC.decryptCipherkey(encbuf, cipherkeybuf);
messagebuf2.toString('hex').should.equal(messagebuf.toString('hex'));
});
});
});

98
test/base58.js

@ -0,0 +1,98 @@
var Base58 = require('../lib/base58');
var should = require('chai').should();
describe('Base58', function() {
var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]);
var enc = "1W7N4RuG";
it('should make an instance with "new"', function() {
var b58 = new Base58();
should.exist(b58);
});
it('should make an instance without "new"', function() {
var b58 = Base58();
should.exist(b58);
});
it('should allow this handy syntax', function() {
Base58(buf).toString().should.equal(enc);
Base58(enc).toBuffer().toString('hex').should.equal(buf.toString('hex'))
});
describe('#set', function() {
it('should set a blank buffer', function() {
Base58().set({buf: new Buffer([])});
});
});
describe('@encode', function() {
it('should encode the buffer accurately', function() {
Base58.encode(buf).should.equal(enc);
});
it('should throw an error when the Input is not a buffer', function() {
(function() {
Base58.encode("string")
}).should.throw('Input should be a buffer');
});
});
describe('@decode', function() {
it('should decode this encoded value correctly', function() {
Base58.decode(enc).toString('hex').should.equal(buf.toString('hex'));
});
it('should throw an error when Input is not a string', function() {
(function() {
Base58.decode(5);
}).should.throw('Input should be a string');
});
});
describe('#fromBuffer', function() {
it('should not fail', function() {
should.exist(Base58().fromBuffer(buf));
});
it('should set buffer', function() {
var b58 = Base58().fromBuffer(buf);
b58.buf.toString('hex').should.equal(buf.toString('hex'));
});
});
describe('#fromString', function() {
it('should convert this known string to a buffer', function() {
Base58().fromString(enc).toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
});
describe('#toBuffer', function() {
it('should return the buffer', function() {
var b58 = Base58({buf: buf});
b58.buf.toString('hex').should.equal(buf.toString('hex'));
});
});
describe('#toString', function() {
it('should return the buffer', function() {
var b58 = Base58({buf: buf});
b58.toString().should.equal(enc);
});
});
});

114
test/base58check.js

@ -0,0 +1,114 @@
var should = require('chai').should();
var Base58Check = require('../lib/base58check');
var base58 = require('../lib/base58');
describe('Base58Check', function() {
var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]);
var enc = "14HV44ipwoaqfg";
it('should make an instance with "new"', function() {
var b58 = new Base58Check();
should.exist(b58);
});
it('should make an instance without "new"', function() {
var b58 = Base58Check();
should.exist(b58);
});
it('should allow this handy syntax', function() {
Base58Check(buf).toString().should.equal(enc);
Base58Check(enc).toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
describe('#set', function() {
it('should set a buf', function() {
should.exist(Base58Check().set({buf: buf}).buf);
});
});
describe('@encode', function() {
it('should encode the buffer accurately', function() {
Base58Check.encode(buf).should.equal(enc);
});
it('should throw an error when the input is not a buffer', function() {
(function() {
Base58Check.encode("string")
}).should.throw('Input must be a buffer');
});
});
describe('@decode', function() {
it('should decode this encoded value correctly', function() {
Base58Check.decode(enc).toString('hex').should.equal(buf.toString('hex'));
});
it('should throw an error when input is not a string', function() {
(function() {
Base58Check.decode(5);
}).should.throw('Input must be a string');
});
it('should throw an error when input is too short', function() {
(function() {
Base58Check.decode(enc.slice(0, 1));
}).should.throw('Input string too short');
});
it('should throw an error when there is a checksum mismatch', function() {
var buf2 = base58.decode(enc);
buf2[0] = buf2[0] + 1;
var enc2 = base58.encode(buf2);
(function() {
Base58Check.decode(enc2);
}).should.throw('Checksum mismatch');
});
});
describe('#fromBuffer', function() {
it('should not fail', function() {
should.exist(Base58Check().fromBuffer(buf));
});
it('should set buffer', function() {
var b58 = Base58Check().fromBuffer(buf);
b58.buf.toString('hex').should.equal(buf.toString('hex'));
});
});
describe('#fromString', function() {
it('should convert this known string to a buffer', function() {
Base58Check().fromString(enc).toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
});
describe('#toBuffer', function() {
it('should return the buffer', function() {
var b58 = Base58Check({buf: buf});
b58.buf.toString('hex').should.equal(buf.toString('hex'));
});
});
describe('#toString', function() {
it('should return the buffer', function() {
var b58 = Base58Check({buf: buf});
b58.toString().should.equal(enc);
});
});
});

359
test/bip32.js

@ -0,0 +1,359 @@
var should = require('chai').should();
var constants = require('../lib/constants');
var BIP32 = require('../lib/bip32');
describe('BIP32', function() {
//test vectors: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
var vector1_master = '000102030405060708090a0b0c0d0e0f';
var vector1_m_public = 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8'
var vector1_m_private = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
var vector1_m0h_public = 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw';
var vector1_m0h_private = 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7';
var vector1_m0h1_public = 'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ';
var vector1_m0h1_private = 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs';
var vector1_m0h12h_public = 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5';
var vector1_m0h12h_private = 'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM';
var vector1_m0h12h2_public = 'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV';
var vector1_m0h12h2_private = 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334';
var vector1_m0h12h21000000000_public = 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy';
var vector1_m0h12h21000000000_private = 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76';
var vector2_master = 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542';
var vector2_m_public = 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB';
var vector2_m_private = 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U';
var vector2_m0_public = 'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH';
var vector2_m0_private = 'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt';
var vector2_m02147483647h_public = 'xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a';
var vector2_m02147483647h_private = 'xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9';
var vector2_m02147483647h1_public = 'xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon';
var vector2_m02147483647h1_private = 'xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef';
var vector2_m02147483647h12147483646h_public = 'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL';
var vector2_m02147483647h12147483646h_private = 'xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc';
var vector2_m02147483647h12147483646h2_public = 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt';
var vector2_m02147483647h12147483646h2_private = 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j';
it('should make a new a bip32', function() {
var bip32;
bip32 = new BIP32();
should.exist(bip32);
bip32 = BIP32();
should.exist(bip32);
new BIP32(vector1_m_private).toString().should.equal(vector1_m_private);
BIP32(vector1_m_private).toString().should.equal(vector1_m_private);
BIP32(BIP32(vector1_m_private)).toString().should.equal(vector1_m_private);
});
it('should initialize test vector 1 from the extended public key', function() {
var bip32 = new BIP32().fromString(vector1_m_public);
should.exist(bip32);
});
it('should initialize test vector 1 from the extended private key', function() {
var bip32 = new BIP32().fromString(vector1_m_private);
should.exist(bip32);
});
it('should get the extended public key from the extended private key for test vector 1', function() {
var bip32 = new BIP32().fromString(vector1_m_private);
bip32.xpubkeyString().should.equal(vector1_m_public);
});
it("should get m/0' ext. private key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'");
should.exist(child);
child.xprivkeyString().should.equal(vector1_m0h_private);
});
it("should get m/0' ext. public key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'");
should.exist(child);
child.xpubkeyString().should.equal(vector1_m0h_public);
});
it("should get m/0'/1 ext. private key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'/1");
should.exist(child);
child.xprivkeyString().should.equal(vector1_m0h1_private);
});
it("should get m/0'/1 ext. public key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'/1");
should.exist(child);
child.xpubkeyString().should.equal(vector1_m0h1_public);
});
it("should get m/0'/1 ext. public key from m/0' public key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'");
var child_pub = new BIP32().fromString(child.xpubkeyString());
var child2 = child_pub.derive("m/1");
should.exist(child2);
child2.xpubkeyString().should.equal(vector1_m0h1_public);
});
it("should get m/0'/1/2h ext. private key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'/1/2'");
should.exist(child);
child.xprivkeyString().should.equal(vector1_m0h12h_private);
});
it("should get m/0'/1/2h ext. public key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'/1/2'");
should.exist(child);
child.xpubkeyString().should.equal(vector1_m0h12h_public);
});
it("should get m/0'/1/2h/2 ext. private key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'/1/2'/2");
should.exist(child);
child.xprivkeyString().should.equal(vector1_m0h12h2_private);
});
it("should get m/0'/1/2'/2 ext. public key from m/0'/1/2' public key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'/1/2'");
var child_pub = new BIP32().fromString(child.xpubkeyString());
var child2 = child_pub.derive("m/2");
should.exist(child2);
child2.xpubkeyString().should.equal(vector1_m0h12h2_public);
});
it("should get m/0'/1/2h/2 ext. public key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'/1/2'/2");
should.exist(child);
child.xpubkeyString().should.equal(vector1_m0h12h2_public);
});
it("should get m/0'/1/2h/2/1000000000 ext. private key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'/1/2'/2/1000000000");
should.exist(child);
child.xprivkeyString().should.equal(vector1_m0h12h21000000000_private);
});
it("should get m/0'/1/2h/2/1000000000 ext. public key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'/1/2'/2/1000000000");
should.exist(child);
child.xpubkeyString().should.equal(vector1_m0h12h21000000000_public);
});
it("should get m/0'/1/2'/2/1000000000 ext. public key from m/0'/1/2'/2 public key from test vector 1", function() {
var bip32 = new BIP32().fromString(vector1_m_private);
var child = bip32.derive("m/0'/1/2'/2");
var child_pub = new BIP32().fromString(child.xpubkeyString());
var child2 = child_pub.derive("m/1000000000");
should.exist(child2);
child2.xpubkeyString().should.equal(vector1_m0h12h21000000000_public);
});
it('should initialize test vector 2 from the extended public key', function() {
var bip32 = new BIP32().fromString(vector2_m_public);
should.exist(bip32);
});
it('should initialize test vector 2 from the extended private key', function() {
var bip32 = new BIP32().fromString(vector2_m_private);
should.exist(bip32);
});
it('should get the extended public key from the extended private key for test vector 2', function() {
var bip32 = new BIP32().fromString(vector2_m_private);
bip32.xpubkeyString().should.equal(vector2_m_public);
});
it("should get m/0 ext. private key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0");
should.exist(child);
child.xprivkeyString().should.equal(vector2_m0_private);
});
it("should get m/0 ext. public key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0");
should.exist(child);
child.xpubkeyString().should.equal(vector2_m0_public);
});
it("should get m/0 ext. public key from m public key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m");
var child_pub = new BIP32().fromString(child.xpubkeyString());
var child2 = child_pub.derive("m/0");
should.exist(child2);
child2.xpubkeyString().should.equal(vector2_m0_public);
});
it("should get m/0/2147483647h ext. private key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0/2147483647'");
should.exist(child);
child.xprivkeyString().should.equal(vector2_m02147483647h_private);
});
it("should get m/0/2147483647h ext. public key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0/2147483647'");
should.exist(child);
child.xpubkeyString().should.equal(vector2_m02147483647h_public);
});
it("should get m/0/2147483647h/1 ext. private key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0/2147483647'/1");
should.exist(child);
child.xprivkeyString().should.equal(vector2_m02147483647h1_private);
});
it("should get m/0/2147483647h/1 ext. public key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0/2147483647'/1");
should.exist(child);
child.xpubkeyString().should.equal(vector2_m02147483647h1_public);
});
it("should get m/0/2147483647h/1 ext. public key from m/0/2147483647h public key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0/2147483647'");
var child_pub = new BIP32().fromString(child.xpubkeyString());
var child2 = child_pub.derive("m/1");
should.exist(child2);
child2.xpubkeyString().should.equal(vector2_m02147483647h1_public);
});
it("should get m/0/2147483647h/1/2147483646h ext. private key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0/2147483647'/1/2147483646'");
should.exist(child);
child.xprivkeyString().should.equal(vector2_m02147483647h12147483646h_private);
});
it("should get m/0/2147483647h/1/2147483646h ext. public key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0/2147483647'/1/2147483646'");
should.exist(child);
child.xpubkeyString().should.equal(vector2_m02147483647h12147483646h_public);
});
it("should get m/0/2147483647h/1/2147483646h/2 ext. private key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0/2147483647'/1/2147483646'/2");
should.exist(child);
child.xprivkeyString().should.equal(vector2_m02147483647h12147483646h2_private);
});
it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0/2147483647'/1/2147483646'/2");
should.exist(child);
child.xpubkeyString().should.equal(vector2_m02147483647h12147483646h2_public);
});
it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from m/0/2147483647h/2147483646h public key from test vector 2", function() {
var bip32 = new BIP32().fromString(vector2_m_private);
var child = bip32.derive("m/0/2147483647'/1/2147483646'");
var child_pub = new BIP32().fromString(child.xpubkeyString());
var child2 = child_pub.derive("m/2");
should.exist(child2);
child2.xpubkeyString().should.equal(vector2_m02147483647h12147483646h2_public);
});
describe('testnet', function() {
it('should initialize a new BIP32 correctly from a random BIP32', function() {
var b1 = new BIP32();
b1.fromRandom('testnet');
var b2 = new BIP32().fromString(b1.xpubkeyString());
b2.xpubkeyString().should.equal(b1.xpubkeyString());
});
it('should generate valid ext pub key for testnet', function() {
var b = new BIP32();
b.fromRandom('testnet');
b.xpubkeyString().substring(0,4).should.equal('tpub');
});
});
describe('#set', function() {
var bip32 = BIP32(vector1_m_private);
var bip322 = BIP32().set({
version: bip32.version,
depth: bip32.depth,
parentfingerprint: bip32.parentfingerprint,
childindex: bip32.childindex,
chaincode: bip32.chaincode,
key: bip32.key,
hasprivkey: bip32.hasprivkey,
pubkeyhash: bip32.pubKeyhash,
xpubkey: bip32.xpubkey,
xprivkey: bip32.xprivkey
});
bip322.toString().should.equal(bip32.toString());
bip322.set({}).toString().should.equal(bip32.toString());
});
describe('#seed', function() {
it('should initialize a new BIP32 correctly from test vector 1 seed', function() {
var hex = vector1_master;
var bip32 = (new BIP32()).fromSeed(new Buffer(hex, 'hex'), 'mainnet');
should.exist(bip32);
bip32.xprivkeyString().should.equal(vector1_m_private);
bip32.xpubkeyString().should.equal(vector1_m_public);
});
it('should initialize a new BIP32 correctly from test vector 2 seed', function() {
var hex = vector2_master;
var bip32 = (new BIP32()).fromSeed(new Buffer(hex, 'hex'), 'mainnet');
should.exist(bip32);
bip32.xprivkeyString().should.equal(vector2_m_private);
bip32.xpubkeyString().should.equal(vector2_m_public);
});
});
describe('#fromString', function() {
it('should make a bip32 from a string', function() {
var str = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
var bip32 = new BIP32().fromString(str);
should.exist(bip32);
bip32.toString().should.equal(str);
});
});
describe('#toString', function() {
var bip32 = new BIP32();
bip32.fromRandom('mainnet');
var tip32 = new BIP32();
tip32.fromRandom('testnet');
it('should return an xprv string', function() {
bip32.toString().slice(0, 4).should.equal('xprv');
});
it('should return an xpub string', function() {
var bip32b = new BIP32().fromString(bip32.xpubkeyString());
bip32b.toString().slice(0, 4).should.equal('xpub');
});
it('should return a tprv string', function() {
tip32.toString().slice(0, 4).should.equal('tprv');
});
it('should return a tpub string', function() {
var tip32b = new BIP32().fromString(tip32.xpubkeyString());
tip32b.toString().slice(0, 4).should.equal('tpub');
});
});
});

151
test/block.js

@ -0,0 +1,151 @@
var Blockheader = require('../lib/blockheader');
var Block = require('../lib/block');
var BufferWriter = require('../lib/bufferwriter');
var BufferReader = require('../lib/bufferreader');
var Varint = require('../lib/varint');
var should = require('chai').should();
var Transaction = require('../lib/transaction');
describe('Block', function() {
var txidhex = '8c9aa966d35bfeaf031409e0001b90ccdafd8d859799eb945a3c515b8260bcf2';
var txhex = '01000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000';
var txbuf = new Buffer(txhex, 'hex');
var tx = Transaction().fromBuffer(txbuf);
var magicnum = 0xd9b4bef9;
var blocksize = 50;
bhhex = '0100000005050505050505050505050505050505050505050505050505050505050505050909090909090909090909090909090909090909090909090909090909090909020000000300000004000000';
bhbuf = new Buffer(bhhex, 'hex');
var bh = Blockheader().fromBuffer(bhbuf);
var txsvi = Varint(1);
var txs = [Transaction().fromBuffer(txbuf)];
var block = Block().set({
magicnum: magicnum,
blocksize: blocksize,
blockheader: bh,
txsvi: txsvi,
txs: txs
});
var blockhex = 'f9beb4d93200000001000000050505050505050505050505050505050505050505050505050505050505050509090909090909090909090909090909090909090909090909090909090909090200000003000000040000000101000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000';
var blockbuf = new Buffer(blockhex, 'hex');
var genesishex = 'f9beb4d91d0100000100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000';
var genesisbuf = new Buffer(genesishex, 'hex');
var genesisidhex = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f';
it('should make a new block', function() {
var block = new Block();
should.exist(block);
block = Block();
should.exist(block);
block = Block(blockbuf);
block.toBuffer().toString('hex').should.equal(blockhex);
});
describe('#set', function() {
it('should set these known values', function() {
var block = Block().set({
magicnum: magicnum,
blocksize: blocksize,
blockheader: bh,
txsvi: txsvi,
txs: txs
});
should.exist(block.magicnum);
should.exist(block.blocksize);
should.exist(block.blockheader);
should.exist(block.txsvi);
should.exist(block.txs);
});
});
describe('#fromJSON', function() {
it('should set these known values', function() {
var block = Block().set({
magicnum: magicnum,
blocksize: blocksize,
blockheader: bh.toJSON(),
txsvi: txsvi.toJSON(),
txs: [txs[0].toJSON()]
});
should.exist(block.magicnum);
should.exist(block.blocksize);
should.exist(block.blockheader);
should.exist(block.txsvi);
should.exist(block.txs);
});
});
describe('#toJSON', function() {
it('should recover these known values', function() {
var json = block.toJSON();
should.exist(json.magicnum);
should.exist(json.blocksize);
should.exist(json.blockheader);
should.exist(json.txsvi);
should.exist(json.txs);
});
});
describe('#fromBuffer', function() {
it('should make a block from this known buffer', function() {
var block = Block().fromBuffer(blockbuf);
block.toBuffer().toString('hex').should.equal(blockhex);
});
});
describe('#fromBufferReader', function() {
it('should make a block from this known buffer', function() {
var block = Block().fromBufferReader(BufferReader(blockbuf));
block.toBuffer().toString('hex').should.equal(blockhex);
});
});
describe('#toBuffer', function() {
it('should recover a block from this known buffer', function() {
var block = Block().fromBuffer(blockbuf);
block.toBuffer().toString('hex').should.equal(blockhex);
});
});
describe('#toBufferWriter', function() {
it('should recover a block from this known buffer', function() {
var block = Block().fromBuffer(blockbuf);
block.toBufferWriter().concat().toString('hex').should.equal(blockhex);
});
});
describe('#hash', function() {
it('should return the correct hash of the genesis block', function() {
var block = Block().fromBuffer(genesisbuf);
var blockhash = new Buffer(Array.apply([], new Buffer(genesisidhex, 'hex')).reverse());
block.hash().toString('hex').should.equal(blockhash.toString('hex'));
});
});
describe('#id', function() {
it('should return the correct id of the genesis block', function() {
var block = Block().fromBuffer(genesisbuf);
block.id().toString('hex').should.equal(genesisidhex);
});
});
});

124
test/blockheader.js

@ -0,0 +1,124 @@
var Blockheader = require('../lib/blockheader');
var BufferWriter = require('../lib/bufferwriter');
var BufferReader = require('../lib/bufferreader');
var should = require('chai').should();
describe('Blockheader', function() {
var bh = new Blockheader();
var version = 1;
var prevblockidbuf = new Buffer(32);
prevblockidbuf.fill(5);
var merklerootbuf = new Buffer(32);
merklerootbuf.fill(9);
var time = 2;
var bits = 3;
var nonce = 4;
bh.set({
version: version,
prevblockidbuf: prevblockidbuf,
merklerootbuf: merklerootbuf,
time: time,
bits: bits,
nonce: nonce
});
bhhex = '0100000005050505050505050505050505050505050505050505050505050505050505050909090909090909090909090909090909090909090909090909090909090909020000000300000004000000';
bhbuf = new Buffer(bhhex, 'hex');
it('should make a new blockheader', function() {
var blockheader = new Blockheader();
should.exist(blockheader);
blockheader = Blockheader();
should.exist(blockheader);
Blockheader(bhbuf).toBuffer().toString('hex').should.equal(bhhex);
});
describe('#set', function() {
it('should set all the variables', function() {
bh.set({
version: version,
prevblockidbuf: prevblockidbuf,
merklerootbuf: merklerootbuf,
time: time,
bits: bits,
nonce: nonce
});
should.exist(bh.version);
should.exist(bh.prevblockidbuf);
should.exist(bh.merklerootbuf);
should.exist(bh.time);
should.exist(bh.bits);
should.exist(bh.nonce);
});
});
describe('#fromJSON', function() {
it('should set all the variables', function() {
var bh = Blockheader().fromJSON({
version: version,
prevblockidbuf: prevblockidbuf.toString('hex'),
merklerootbuf: merklerootbuf.toString('hex'),
time: time,
bits: bits,
nonce: nonce
});
should.exist(bh.version);
should.exist(bh.prevblockidbuf);
should.exist(bh.merklerootbuf);
should.exist(bh.time);
should.exist(bh.bits);
should.exist(bh.nonce);
});
});
describe('#toJSON', function() {
it('should set all the variables', function() {
var json = bh.toJSON();
should.exist(json.version);
should.exist(json.prevblockidbuf);
should.exist(json.merklerootbuf);
should.exist(json.time);
should.exist(json.bits);
should.exist(json.nonce);
});
});
describe('#fromBuffer', function() {
it('should parse this known buffer', function() {
Blockheader().fromBuffer(bhbuf).toBuffer().toString('hex').should.equal(bhhex);
});
});
describe('#fromBufferReader', function() {
it('should parse this known buffer', function() {
Blockheader().fromBufferReader(BufferReader(bhbuf)).toBuffer().toString('hex').should.equal(bhhex);
});
});
describe('#toBuffer', function() {
it('should output this known buffer', function() {
Blockheader().fromBuffer(bhbuf).toBuffer().toString('hex').should.equal(bhhex);
});
});
describe('#toBufferWriter', function() {
it('should output this known buffer', function() {
Blockheader().fromBuffer(bhbuf).toBufferWriter().concat().toString('hex').should.equal(bhhex);
});
});
});

154
test/bn.js

@ -0,0 +1,154 @@
var chai = chai || require('chai');
var should = chai.should();
var assert = chai.assert;
var BN = require('../lib/bn');
describe('BN', function() {
it('should create a bn', function() {
var bn = new BN(50);
should.exist(bn);
bn.toString().should.equal('50');
});
it('should parse this number', function() {
var bn = new BN(999970000);
bn.toString().should.equal('999970000');
});
it('should parse numbers below and at bn.js internal word size', function() {
var bn = new BN(Math.pow(2, 26) - 1);
bn.toString().should.equal((Math.pow(2, 26) - 1).toString());
var bn = new BN(Math.pow(2, 26));
bn.toString().should.equal((Math.pow(2, 26)).toString());
});
describe('#add', function() {
it('should add two small numbers together', function() {
var bn1 = new BN(50);
var bn2 = new BN(75);
var bn3 = bn1.add(bn2);
bn3.toString().should.equal('125');
});
});
describe('#sub', function() {
it('should subtract a small number', function() {
var bn1 = new BN(50);
var bn2 = new BN(25);
var bn3 = bn1.sub(bn2);
bn3.toString().should.equal('25');
});
});
describe('#gt', function() {
it('should say 1 is greater than 0', function() {
var bn1 = new BN(1);
var bn0 = new BN(0);
bn1.gt(bn0).should.equal(true);
});
it('should say a big number is greater than a small big number', function() {
var bn1 = new BN('24023452345398529485723980457');
var bn0 = new BN('34098234283412341234049357');
bn1.gt(bn0).should.equal(true);
});
it('should say a big number is great than a standard number', function() {
var bn1 = new BN('24023452345398529485723980457');
var bn0 = new BN(5);
bn1.gt(bn0).should.equal(true);
});
});
describe('#fromJSON', function() {
it('should make BN from a string', function() {
BN().fromJSON('5').toString().should.equal('5');
});
});
describe('#toJSON', function() {
it('should make string from a BN', function() {
BN(5).toJSON().should.equal('5');
BN().fromJSON('5').toJSON().should.equal('5');
});
});
describe('#fromString', function() {
it('should make BN from a string', function() {
BN().fromString('5').toString().should.equal('5');
});
});
describe('#toString', function() {
it('should make a string', function() {
BN(5).toString().should.equal('5');
});
});
describe('@fromBuffer', function() {
it('should work with big endian', function() {
var bn = BN.fromBuffer(new Buffer('0001', 'hex'), {endian: 'big'});
bn.toString().should.equal('1');
});
it('should work with big endian 256', function() {
var bn = BN.fromBuffer(new Buffer('0100', 'hex'), {endian: 'big'});
bn.toString().should.equal('256');
});
it('should work with little endian if we specify the size', function() {
var bn = BN.fromBuffer(new Buffer('0100', 'hex'), {size: 2, endian: 'little'});
bn.toString().should.equal('1');
});
});
describe('#fromBuffer', function() {
it('should work as a prototype method', function() {
var bn = BN().fromBuffer(new Buffer('0100', 'hex'), {size: 2, endian: 'little'});
bn.toString().should.equal('1');
});
});
describe('#toBuffer', function() {
it('should create a 4 byte buffer', function() {
var bn = new BN(1);
bn.toBuffer({size: 4}).toString('hex').should.equal('00000001');
});
it('should create a 4 byte buffer in little endian', function() {
var bn = new BN(1);
bn.toBuffer({size: 4, endian: 'little'}).toString('hex').should.equal('01000000');
});
it('should create a 2 byte buffer even if you ask for a 1 byte', function() {
var bn = new BN('ff00', 16);
bn.toBuffer({size: 1}).toString('hex').should.equal('ff00');
});
it('should create a 4 byte buffer even if you ask for a 1 byte', function() {
var bn = new BN('ffffff00', 16);
bn.toBuffer({size: 4}).toString('hex').should.equal('ffffff00');
});
});
});

274
test/bufferreader.js

@ -0,0 +1,274 @@
var BufferWriter = require('../lib/bufferwriter');
var BufferReader = require('../lib/bufferreader');
var should = require('chai').should();
var BN = require('../lib/bn');
describe('BufferReader', function() {
it('should make a new BufferReader', function() {
var br = new BufferReader();
should.exist(br);
br = BufferReader();
should.exist(br);
});
it('should create a new bufferreader with a buffer', function() {
var buf = new Buffer(0);
var br = new BufferReader(buf);
should.exist(br);
Buffer.isBuffer(br.buf).should.equal(true);
});
describe('#set', function() {
it('should set pos', function() {
should.exist(BufferReader().set({pos: 1}).pos);
});
});
describe('#eof', function() {
it('should return true for a blank br', function() {
var br = new BufferReader({buf: new Buffer([])});
br.eof().should.equal(true);
});
});
describe('read', function() {
it('should return the same buffer', function() {
var buf = new Buffer([0]);
var br = new BufferReader({buf: buf});
br.read().toString('hex').should.equal(buf.toString('hex'));
});
it('should return a buffer of this length', function() {
var buf = new Buffer(10);
buf.fill(0);
var br = new BufferReader(buf);
var buf2 = br.read(2);
buf2.length.should.equal(2);
br.eof().should.equal(false);
br.pos.should.equal(2);
});
});
describe('#readUInt8', function() {
it('should return 1', function() {
var buf = new Buffer(1);
buf.writeUInt8(1, 0);
var br = new BufferReader({buf: buf});
br.readUInt8().should.equal(1);
});
});
describe('#readUInt16BE', function() {
it('should return 1', function() {
var buf = new Buffer(2);
buf.writeUInt16BE(1, 0);
var br = new BufferReader({buf: buf});
br.readUInt16BE().should.equal(1);
});
});
describe('#readUInt16LE', function() {
it('should return 1', function() {
var buf = new Buffer(2);
buf.writeUInt16LE(1, 0);
var br = new BufferReader({buf: buf});
br.readUInt16LE().should.equal(1);
});
});
describe('#readUInt32BE', function() {
it('should return 1', function() {
var buf = new Buffer(4);
buf.writeUInt32BE(1, 0);
var br = new BufferReader({buf: buf});
br.readUInt32BE().should.equal(1);
});
});
describe('#readUInt32LE', function() {
it('should return 1', function() {
var buf = new Buffer(4);
buf.writeUInt32LE(1, 0);
var br = new BufferReader({buf: buf});
br.readUInt32LE().should.equal(1);
});
});
describe('#readUInt64BEBN', function() {
it('should return 1', function() {
var buf = new Buffer(8);
buf.fill(0);
buf.writeUInt32BE(1, 4);
var br = new BufferReader({buf: buf});
br.readUInt64BEBN().toNumber().should.equal(1);
});
it('should return 2^64', function() {
var buf = new Buffer(8);
buf.fill(0xff);
var br = new BufferReader({buf: buf});
br.readUInt64BEBN().toNumber().should.equal(Math.pow(2, 64));
});
});
describe('#readUInt64LEBN', function() {
it('should return 1', function() {
var buf = new Buffer(8);
buf.fill(0);
buf.writeUInt32LE(1, 0);
var br = new BufferReader({buf: buf});
br.readUInt64LEBN().toNumber().should.equal(1);
});
it('should return 2^30', function() {
var buf = new Buffer(8);
buf.fill(0);
buf.writeUInt32LE(Math.pow(2, 30), 0);
var br = new BufferReader({buf: buf});
br.readUInt64LEBN().toNumber().should.equal(Math.pow(2, 30));
});
it('should return 0', function() {
var buf = new Buffer(8);
buf.fill(0);
var br = new BufferReader({buf: buf});
br.readUInt64LEBN().toNumber().should.equal(0);
});
it('should return 2^64', function() {
var buf = new Buffer(8);
buf.fill(0xff);
var br = new BufferReader({buf: buf});
br.readUInt64LEBN().toNumber().should.equal(Math.pow(2, 64));
});
});
describe('#readVarintBuf', function() {
it('should read a 1 byte varint', function() {
var buf = new Buffer([50]);
var br = new BufferReader({buf: buf});
br.readVarintBuf().length.should.equal(1);
});
it('should read a 3 byte varint', function() {
var buf = new Buffer([253, 253, 0]);
var br = new BufferReader({buf: buf});
br.readVarintBuf().length.should.equal(3);
});
it('should read a 5 byte varint', function() {
var buf = new Buffer([254, 0, 0, 0, 0]);
buf.writeUInt32LE(50000, 1);
var br = new BufferReader({buf: buf});
br.readVarintBuf().length.should.equal(5);
});
it('should read a 9 byte varint', function() {
var buf = BufferWriter().writeVarintBN(BN(Math.pow(2, 54).toString())).concat();
var br = new BufferReader({buf: buf});
br.readVarintBuf().length.should.equal(9);
});
});
describe('#readVarintNum', function() {
it('should read a 1 byte varint', function() {
var buf = new Buffer([50]);
var br = new BufferReader({buf: buf});
br.readVarintNum().should.equal(50);
});
it('should read a 3 byte varint', function() {
var buf = new Buffer([253, 253, 0]);
var br = new BufferReader({buf: buf});
br.readVarintNum().should.equal(253);
});
it('should read a 5 byte varint', function() {
var buf = new Buffer([254, 0, 0, 0, 0]);
buf.writeUInt32LE(50000, 1);
var br = new BufferReader({buf: buf});
br.readVarintNum().should.equal(50000);
});
it('should throw an error on a 9 byte varint over the javascript uint precision limit', function() {
var buf = BufferWriter().writeVarintBN(BN(Math.pow(2, 54).toString())).concat();
var br = new BufferReader({buf: buf});
(function() {
br.readVarintNum();
}).should.throw('number too large to retain precision - use readVarintBN');
});
it('should not throw an error on a 9 byte varint not over the javascript uint precision limit', function() {
var buf = BufferWriter().writeVarintBN(BN(Math.pow(2, 53).toString())).concat();
var br = new BufferReader({buf: buf});
(function() {
br.readVarintNum();
}).should.not.throw('number too large to retain precision - use readVarintBN');
});
});
describe('#readVarintBN', function() {
it('should read a 1 byte varint', function() {
var buf = new Buffer([50]);
var br = new BufferReader({buf: buf});
br.readVarintBN().toNumber().should.equal(50);
});
it('should read a 3 byte varint', function() {
var buf = new Buffer([253, 253, 0]);
var br = new BufferReader({buf: buf});
br.readVarintBN().toNumber().should.equal(253);
});
it('should read a 5 byte varint', function() {
var buf = new Buffer([254, 0, 0, 0, 0]);
buf.writeUInt32LE(50000, 1);
var br = new BufferReader({buf: buf});
br.readVarintBN().toNumber().should.equal(50000);
});
it('should read a 9 byte varint', function() {
var buf = Buffer.concat([new Buffer([255]), new Buffer('ffffffffffffffff', 'hex')]);
var br = new BufferReader({buf: buf});
br.readVarintBN().toNumber().should.equal(Math.pow(2, 64));
});
});
describe('#reverse', function() {
it('should reverse this [0, 1]', function() {
var buf = new Buffer([0, 1]);
var br = new BufferReader({buf: buf});
br.reverse().read().toString('hex').should.equal('0100');
});
});
});

186
test/bufferwriter.js

@ -0,0 +1,186 @@
var BufferWriter = require('../lib/bufferwriter');
var BufferReader = require('../lib/bufferreader');
var BN = require('../lib/bn');
var should = require('chai').should();
describe('BufferWriter', function() {
it('should create a new buffer writer', function() {
var bw = new BufferWriter();
should.exist(bw);
});
describe('#set', function() {
it('set bufs', function() {
var buf1 = new Buffer([0]);
var buf2 = new Buffer([1]);
var bufs = [buf1, buf2];
var bw = new BufferWriter().set({bufs: [buf1, buf2]});
bw.concat().toString('hex').should.equal('0001');
});
});
describe('#toBuffer', function() {
it('should concat these two bufs', function() {
var buf1 = new Buffer([0]);
var buf2 = new Buffer([1]);
var bw = new BufferWriter({bufs: [buf1, buf2]});
bw.toBuffer().toString('hex').should.equal('0001');
});
});
describe('#concat', function() {
it('should concat these two bufs', function() {
var buf1 = new Buffer([0]);
var buf2 = new Buffer([1]);
var bw = new BufferWriter({bufs: [buf1, buf2]});
bw.concat().toString('hex').should.equal('0001');
});
});
describe('#write', function() {
it('should write a buffer', function() {
var buf = new Buffer([0]);
var bw = new BufferWriter();
bw.write(buf);
bw.concat().toString('hex').should.equal('00');
});
});
describe('#writeUInt8', function() {
it('should write 1', function() {
var bw = new BufferWriter();
bw.writeUInt8(1).concat().toString('hex').should.equal('01');
});
});
describe('#writeUInt16BE', function() {
it('should write 1', function() {
var bw = new BufferWriter();
bw.writeUInt16BE(1).concat().toString('hex').should.equal('0001');
});
});
describe('#writeUInt16LE', function() {
it('should write 1', function() {
var bw = new BufferWriter();
bw.writeUInt16LE(1).concat().toString('hex').should.equal('0100');
});
});
describe('#writeUInt32BE', function() {
it('should write 1', function() {
var bw = new BufferWriter();
bw.writeUInt32BE(1).concat().toString('hex').should.equal('00000001');
});
});
describe('#writeUInt32LE', function() {
it('should write 1', function() {
var bw = new BufferWriter();
bw.writeUInt32LE(1).concat().toString('hex').should.equal('01000000');
});
});
describe('#writeUInt64BEBN', function() {
it('should write 1', function() {
var bw = new BufferWriter();
bw.writeUInt64BEBN(BN(1)).concat().toString('hex').should.equal('0000000000000001');
});
});
describe('#writeUInt64LEBN', function() {
it('should write 1', function() {
var bw = new BufferWriter();
bw.writeUInt64LEBN(BN(1)).concat().toString('hex').should.equal('0100000000000000');
});
});
describe('#writeVarint', function() {
it('should write a 1 byte varint', function() {
var bw = new BufferWriter();
bw.writeVarintNum(1);
bw.concat().length.should.equal(1);
});
it('should write a 3 byte varint', function() {
var bw = new BufferWriter();
bw.writeVarintNum(1000);
bw.concat().length.should.equal(3);
});
it('should write a 5 byte varint', function() {
var bw = new BufferWriter();
bw.writeVarintNum(Math.pow(2, 16 + 1));
bw.concat().length.should.equal(5);
});
it('should write a 9 byte varint', function() {
var bw = new BufferWriter();
bw.writeVarintNum(Math.pow(2, 32 + 1));
bw.concat().length.should.equal(9);
});
it('should read back the same value it wrote for a 9 byte varint', function() {
var bw = new BufferWriter();
var n = Math.pow(2, 53);
n.should.equal(n + 1); //javascript number precision limit
bw.writeVarintNum(n);
var br = new BufferReader({buf: bw.concat()});
br.readVarintBN().toNumber().should.equal(n);
});
});
describe('#writeVarintBN', function() {
it('should write a 1 byte varint', function() {
var bw = new BufferWriter();
bw.writeVarintBN(BN(1));
bw.concat().length.should.equal(1);
});
it('should write a 3 byte varint', function() {
var bw = new BufferWriter();
bw.writeVarintBN(BN(1000));
bw.concat().length.should.equal(3);
});
it('should write a 5 byte varint', function() {
var bw = new BufferWriter();
bw.writeVarintBN(BN(Math.pow(2, 16 + 1)));
bw.concat().length.should.equal(5);
});
it('should write a 9 byte varint', function() {
var bw = new BufferWriter();
bw.writeVarintBN(BN(Math.pow(2, 32 + 1)));
bw.concat().length.should.equal(9);
});
});
});

317
test/cbc.js

@ -0,0 +1,317 @@
var AES = require('../lib/expmt/aes');
var should = require('chai').should();
var CBC = require('../lib/expmt/cbc');
describe('CBC', function() {
it('should return a new CBC', function() {
var cbc = new CBC();
should.exist(cbc);
})
it('should return a new CBC when called without "new"', function() {
var cbc = new CBC();
should.exist(cbc);
});
describe('@buf2blockbufs', function() {
it('should convert this buffer into one block', function() {
var buf = new Buffer(16 - 1);
buf.fill(0);
var blockbufs = CBC.buf2blockbufs(buf, 16 * 8);
blockbufs.length.should.equal(1);
blockbufs[0].toString('hex').should.equal('00000000000000000000000000000001');
});
it('should convert this buffer into two blocks', function() {
var buf = new Buffer(16);
buf.fill(0);
var blockbufs = CBC.buf2blockbufs(buf, 16 * 8);
blockbufs.length.should.equal(2);
blockbufs[0].toString('hex').should.equal('00000000000000000000000000000000');
blockbufs[1].toString('hex').should.equal('10101010101010101010101010101010');
});
});
describe('@buf2blockbufs', function() {
it('should convert this buffer into one block and back into the same buffer', function() {
var buf = new Buffer(16 - 1);
buf.fill(0);
var blockbufs = CBC.buf2blockbufs(buf, 16 * 8);
var buf2 = CBC.blockbufs2buf(blockbufs, 16 * 8);
buf2.toString('hex').should.equal(buf.toString('hex'));
});
it('should convert this buffer into two blocks and back into the same buffer', function() {
var buf = new Buffer(16);
buf.fill(0);
var blockbufs = CBC.buf2blockbufs(buf, 16 * 8);
var buf2 = CBC.blockbufs2buf(blockbufs, 16 * 8);
buf2.toString('hex').should.equal(buf.toString('hex'));
});
});
describe('@encrypt', function() {
it('should return this known value', function() {
var messagebuf1 = new Buffer(128 / 8);
messagebuf1.fill(0);
var messagebuf2 = new Buffer(128 / 8);
messagebuf2.fill(0x10);
var messagebuf = Buffer.concat([messagebuf1, messagebuf2]);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0x10);
var cipherkeybuf = new Buffer(128 / 8);
cipherkeybuf.fill(0);
var blockcipher = {};
blockcipher.encrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
blockcipher.decrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf);
var buf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf);
});
it('should return this shorter known value', function() {
var messagebuf1 = new Buffer(128 / 8);
messagebuf1.fill(0);
var messagebuf2 = new Buffer(120 / 8);
messagebuf2.fill(0x10);
var messagebuf = Buffer.concat([messagebuf1, messagebuf2]);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0x10);
var cipherkeybuf = new Buffer(128 / 8);
cipherkeybuf.fill(0);
var blockcipher = {};
blockcipher.encrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
blockcipher.decrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf);
var buf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf);
});
it('should return this shorter known value', function() {
var messagebuf1 = new Buffer(128 / 8);
messagebuf1.fill(0);
var messagebuf2 = new Buffer(136 / 8);
messagebuf2.fill(0x10);
var messagebuf = Buffer.concat([messagebuf1, messagebuf2]);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0x10);
var cipherkeybuf = new Buffer(128 / 8);
cipherkeybuf.fill(0);
var blockcipher = {};
blockcipher.encrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
blockcipher.decrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf);
var buf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf);
});
it('should encrypt something with AES', function() {
var messagebuf1 = new Buffer(128 / 8);
messagebuf1.fill(0);
var messagebuf2 = new Buffer(128 / 8);
messagebuf2.fill(0x10);
var messagebuf = Buffer.concat([messagebuf1, messagebuf2]);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0x10);
var cipherkeybuf = new Buffer(128 / 8);
cipherkeybuf.fill(0);
var blockcipher = AES;
var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf);
var buf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf);
buf2.toString('hex').should.equal(messagebuf.toString('hex'));
});
});
describe('@decrypt', function() {
it('should properly decrypt an encrypted message', function() {
var messagebuf1 = new Buffer(128 / 8);
messagebuf1.fill(0);
var messagebuf2 = new Buffer(128 / 8);
messagebuf2.fill(0x10);
var messagebuf = Buffer.concat([messagebuf1, messagebuf2]);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0x10);
var cipherkeybuf = new Buffer(128 / 8);
cipherkeybuf.fill(0);
var blockcipher = {};
blockcipher.encrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
blockcipher.decrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf);
var messagebuf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf);
messagebuf2.toString('hex').should.equal(messagebuf.toString('hex'));
});
});
describe('@encryptblock', function() {
it('should return this known value', function() {
var messagebuf = new Buffer(128 / 8);
messagebuf.fill(0);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0x10);
var cipherkeybuf = new Buffer(128 / 8);
cipherkeybuf.fill(0);
var blockcipher = {};
blockcipher.encrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipher, cipherkeybuf);
enc.toString('hex').should.equal(ivbuf.toString('hex'));
});
it('should return this other known value', function() {
var messagebuf = new Buffer(128 / 8);
messagebuf.fill(0x10);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0x10);
var cipherkeybuf = new Buffer(128 / 8);
cipherkeybuf.fill(0);
var blockcipher = {};
blockcipher.encrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipher, cipherkeybuf);
enc.toString('hex').should.equal('00000000000000000000000000000000');
});
});
describe('@decryptblock', function() {
it('should decrypt an encrypted block', function() {
var messagebuf = new Buffer(128 / 8);
messagebuf.fill(0);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0x10);
var cipherkeybuf = new Buffer(128 / 8);
cipherkeybuf.fill(0);
var blockcipher = {};
blockcipher.encrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
blockcipher.decrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
var encbuf = CBC.encryptblock(messagebuf, ivbuf, blockcipher, cipherkeybuf);
var buf = CBC.decryptblock(encbuf, ivbuf, blockcipher, cipherkeybuf);
buf.toString('hex').should.equal(messagebuf.toString('hex'));
});
});
describe('@encryptblocks', function() {
it('should return this known value', function() {
var messagebuf1 = new Buffer(128 / 8);
messagebuf1.fill(0);
var messagebuf2 = new Buffer(128 / 8);
messagebuf2.fill(0x10);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0x10);
var cipherkeybuf = new Buffer(128 / 8);
cipherkeybuf.fill(0);
var blockcipher = {}
blockcipher.encrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
var encbufs = CBC.encryptblocks([messagebuf1, messagebuf2], ivbuf, blockcipher, cipherkeybuf);
encbufs[0].toString('hex').should.equal('10101010101010101010101010101010');
encbufs[1].toString('hex').should.equal('00000000000000000000000000000000');
});
});
describe('@decryptblocks', function() {
it('should decrypt encrypted blocks', function() {
var messagebuf1 = new Buffer(128 / 8);
messagebuf1.fill(0);
var messagebuf2 = new Buffer(128 / 8);
messagebuf2.fill(0x10);
var ivbuf = new Buffer(128 / 8);
ivbuf.fill(0x10);
var cipherkeybuf = new Buffer(128 / 8);
cipherkeybuf.fill(0);
var blockcipher = {}
blockcipher.encrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
blockcipher.decrypt = function(messagebuf, cipherkeybuf) {
return messagebuf;
};
var encbufs = CBC.encryptblocks([messagebuf1, messagebuf2], ivbuf, blockcipher, cipherkeybuf);
var bufs = CBC.decryptblocks(encbufs, ivbuf, blockcipher, cipherkeybuf);
bufs[0].toString('hex').should.equal(messagebuf1.toString('hex'));
bufs[1].toString('hex').should.equal(messagebuf2.toString('hex'));
});
});
describe('@pkcs7pad', function() {
it('should pad this 32 bit buffer to 128 bits with the number 128/8 - 32/8', function() {
var buf = new Buffer(32 / 8);
buf.fill(0);
var padbuf = CBC.pkcs7pad(buf, 128);
padbuf.length.should.equal(128 / 8);
padbuf[32 / 8].should.equal(128 / 8 - 32 / 8);
padbuf[32 / 8 + 1].should.equal(128 / 8 - 32 / 8);
// ...
padbuf[32 / 8 + 128 / 8 - 32 / 8 - 1].should.equal(128 / 8 - 32 / 8);
});
});
describe('@pkcs7unpad', function() {
it('should unpad this padded 32 bit buffer', function() {
var buf = new Buffer(32 / 8);
buf.fill(0);
var paddedbuf = CBC.pkcs7pad(buf, 128);
var unpaddedbuf = CBC.pkcs7unpad(paddedbuf, 128);
unpaddedbuf.toString('hex').should.equal(buf.toString('hex'));
});
});
describe('@xorbufs', function() {
it('should xor 1 and 0', function() {
var buf1 = new Buffer([1]);
var buf2 = new Buffer([0]);
var buf = CBC.xorbufs(buf1, buf2);
buf[0].should.equal(1);
});
it('should xor 1 and 1', function() {
var buf1 = new Buffer([1]);
var buf2 = new Buffer([1]);
var buf = CBC.xorbufs(buf1, buf2);
buf[0].should.equal(0);
});
});
});

213
test/ecdsa.js

@ -0,0 +1,213 @@
var ECDSA = require('../lib/ecdsa');
var Hash = require('../lib/hash');
var Keypair = require('../lib/keypair');
var Privkey = require('../lib/privkey');
var Pubkey = require('../lib/pubkey');
var Signature = require('../lib/signature');
var BN = require('../lib/bn');
var point = require('../lib/point');
var should = require('chai').should();
describe("ECDSA", function() {
it('should create a blank ecdsa', function() {
var ecdsa = new ECDSA();
});
var ecdsa = new ECDSA();
ecdsa.hashbuf = Hash.sha256(new Buffer('test data'));
ecdsa.keypair = new Keypair();
ecdsa.keypair.privkey = new Privkey({bn: BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))});
ecdsa.keypair.pubkey = new Pubkey({
point: point(BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')),
BN().fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')))
});
describe('#set', function() {
it('should set hashbuf', function() {
should.exist(ECDSA().set({hashbuf: ecdsa.hashbuf}).hashbuf);
});
});
describe('#calci', function() {
it('should calculate i', function() {
ecdsa.randomK();
ecdsa.sign();
ecdsa.calci();
should.exist(ecdsa.sig.i);
});
it('should calulate this known i', function() {
var hashbuf = Hash.sha256(new Buffer('some data'));
var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10);
var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10);
var ecdsa = new ECDSA();
ecdsa.keypair = new Keypair();
ecdsa.keypair.privkey = Privkey();
ecdsa.keypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test')));
ecdsa.keypair.privkey2pubkey();
ecdsa.hashbuf = hashbuf;
ecdsa.sig = new Signature({r: r, s: s});
ecdsa.calci();
ecdsa.sig.i.should.equal(1);
});
});
describe('#fromString', function() {
it('should to a round trip with to string', function() {
var str = ecdsa.toString();
var ecdsa2 = new ECDSA();
ecdsa2.fromString(str);
should.exist(ecdsa.hashbuf);
should.exist(ecdsa.keypair);
});
});
describe('#randomK', function() {
it('should generate a new random k when called twice in a row', function() {
ecdsa.randomK();
var k1 = ecdsa.k;
ecdsa.randomK();
var k2 = ecdsa.k;
(k1.cmp(k2) === 0).should.equal(false);
});
it('should generate a random k that is (almost always) greater than this relatively small number', function() {
ecdsa.randomK();
var k1 = ecdsa.k;
var k2 = BN(Math.pow(2, 32)).mul(BN(Math.pow(2, 32))).mul(BN(Math.pow(2, 32)));
k2.gt(k1).should.equal(false);
});
});
describe('#sig2pubkey', function() {
it('should calculate the correct public key', function() {
ecdsa.k = BN('114860389168127852803919605627759231199925249596762615988727970217268189974335', 10);
ecdsa.sign();
ecdsa.sig.i = 1;
var pubkey = ecdsa.sig2pubkey();
pubkey.point.eq(ecdsa.keypair.pubkey.point).should.equal(true);
});
});
describe('#sigError', function() {
it('should return an error if the hash is invalid', function() {
var ecdsa = new ECDSA();
ecdsa.sigError().should.equal('hashbuf must be a 32 byte buffer');
});
it('should return an error if the pubkey is invalid', function() {
var ecdsa = new ECDSA();
ecdsa.hashbuf = Hash.sha256(new Buffer('test'));
ecdsa.sigError().indexOf("Invalid pubkey").should.equal(0);
});
it('should return an error if r, s are invalid', function() {
var ecdsa = new ECDSA();
ecdsa.hashbuf = Hash.sha256(new Buffer('test'));
var pk = new Pubkey();
pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
ecdsa.keypair = new Keypair();
ecdsa.keypair.pubkey = pk;
ecdsa.sig = new Signature();
ecdsa.sig.r = BN(0);
ecdsa.sig.s = BN(0);
ecdsa.sigError().should.equal("r and s not in range");
});
it('should return an error if the signature is incorrect', function() {
ecdsa.sig = new Signature();
ecdsa.sig.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827');
ecdsa.sig.r = ecdsa.sig.r.add(BN(1));
ecdsa.sigError().should.equal("Invalid signature");
});
});
describe('#sign', function() {
it('should create a valid signature', function() {
ecdsa.randomK();
ecdsa.sign();
ecdsa.verify().should.equal(true);
});
it('should should throw an error if hashbuf is not 32 bytes', function() {
var ecdsa2 = ECDSA().set({
hashbuf: ecdsa.hashbuf.slice(0, 31),
keypair: ecdsa.keypair
});
ecdsa2.randomK();
(function() {
ecdsa2.sign();
}).should.throw('hashbuf must be a 32 byte buffer');
});
});
describe('#signRandomK', function() {
it('should produce a signature', function() {
ecdsa.signRandomK();
should.exist(ecdsa.sig);
});
});
describe('#toString', function() {
it('should convert this to a string', function() {
var str = ecdsa.toString();
(typeof str === 'string').should.equal(true);
});
});
describe('#verify', function() {
it('should verify a signature that was just signed', function() {
ecdsa.sig = new Signature();
ecdsa.sig.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827');
ecdsa.verify().should.equal(true);
});
it('should verify this known good signature', function() {
ecdsa.signRandomK();
ecdsa.verify().should.equal(true);
});
});
describe('@sign', function() {
it('should produce a signature', function() {
var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.keypair);
(sig instanceof Signature).should.equal(true);
});
});
describe('@verify', function() {
it('should verify a valid signature, and unverify an invalid signature', function() {
var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.keypair);
ECDSA.verify(ecdsa.hashbuf, sig, ecdsa.keypair.pubkey).should.equal(true);
var fakesig = Signature(sig.r.add(1), sig.s);
ECDSA.verify(ecdsa.hashbuf, fakesig, ecdsa.keypair.pubkey).should.equal(false);
});
});
});

52
test/ecies.js

@ -0,0 +1,52 @@
var ECIES = require('../lib/expmt/ecies');
var should = require('chai').should();
var Keypair = require('../lib/keypair');
var Hash = require('../lib/hash');
describe('#ECIES', function() {
it('should make a new ECIES object', function() {
var ecies = new ECIES();
should.exist(ecies);
});
it('should make a new ECIES object when called without "new"', function() {
var ecies = ECIES();
should.exist(ecies);
});
var fromkey = Keypair().fromRandom();
var tokey = Keypair().fromRandom();
var messagebuf = Hash.sha256(new Buffer('my message is the hash of this string'));
describe('@encrypt', function() {
it('should return a buffer', function() {
var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey, fromkey);
Buffer.isBuffer(encbuf).should.equal(true);
});
it('should return a buffer if fromkey is not present', function() {
var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey);
Buffer.isBuffer(encbuf).should.equal(true);
});
});
describe('@decrypt', function() {
it('should decrypt that which was encrypted', function() {
var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey, fromkey);
var messagebuf2 = ECIES.decrypt(encbuf, tokey.privkey);
messagebuf2.toString('hex').should.equal(messagebuf.toString('hex'));
});
it('should decrypt that which was encrypted if fromkeypair was randomly generated', function() {
var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey);
var messagebuf2 = ECIES.decrypt(encbuf, tokey.privkey);
messagebuf2.toString('hex').should.equal(messagebuf.toString('hex'));
});
});
});

32
test/examples.js

@ -0,0 +1,32 @@
if (process.browser)
return; //examples are loaded from files, which doesn't work in the browser
var should = require('chai').should();
var fs = require('fs');
describe('Examples', function() {
var filenames = fs.readdirSync(__dirname + '/../examples/');
filenames.forEach(function(filename) {
if (filename.slice(filename.length - 3) === '.js') {
describe(filename, function() {
it('should not throw any errors', function() {
(function() {
var save = console.log;
console.log = function() {};
require('../examples/' + filename);
console.log = save;
}).should.not.throw();
});
});
}
});
});

115
test/hash.js

@ -0,0 +1,115 @@
var should = require('chai').should();
var Hash = require('../lib/hash');
describe('Hash', function() {
var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]);
var str = "test string";
describe('#sha256', function() {
it('should calculate the hash of this buffer correctly', function() {
var hash = Hash.sha256(buf);
hash.toString('hex').should.equal('6f2c7b22fd1626998287b3636089087961091de80311b9279c4033ec678a83e8');
});
it('should throw an error when the input is not a buffer', function() {
(function() {
Hash.sha256(str);
}).should.throw('sha256 hash must be of a buffer');
});
});
describe('#sha256hmac', function() {
it('should compute this known empty test vector correctly', function() {
var key = new Buffer('');
var data = new Buffer('');
Hash.sha256hmac(data, key).toString('hex').should.equal('b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad');
});
it('should compute this known non-empty test vector correctly', function() {
var key = new Buffer('key');
var data = new Buffer('The quick brown fox jumps over the lazy dog');
Hash.sha256hmac(data, key).toString('hex').should.equal('f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8');
});
});
describe('#sha256sha256', function() {
it('should calculate the hash of this buffer correctly', function() {
var hash = Hash.sha256sha256(buf);
hash.toString('hex').should.equal('be586c8b20dee549bdd66018c7a79e2b67bb88b7c7d428fa4c970976d2bec5ba');
});
it('should throw an error when the input is not a buffer', function() {
(function() {
Hash.sha256sha256(str);
}).should.throw('sha256sha256 hash must be of a buffer');
});
});
describe('#sha256ripemd160', function() {
it('should calculate the hash of this buffer correctly', function() {
var hash = Hash.sha256ripemd160(buf);
hash.toString('hex').should.equal('7322e2bd8535e476c092934e16a6169ca9b707ec');
});
it('should throw an error when the input is not a buffer', function() {
(function() {
Hash.sha256ripemd160(str);
}).should.throw('sha256ripemd160 hash must be of a buffer');
});
});
describe('#ripemd160', function() {
it('should calculate the hash of this buffer correctly', function() {
var hash = Hash.ripemd160(buf);
hash.toString('hex').should.equal('fa0f4565ff776fee0034c713cbf48b5ec06b7f5c');
});
it('should throw an error when the input is not a buffer', function() {
(function() {
Hash.ripemd160(str);
}).should.throw('ripemd160 hash must be of a buffer');
});
});
describe('#sha512', function() {
it('should calculate the hash of this buffer correctly', function() {
var hash = Hash.sha512(buf);
hash.toString('hex').should.equal('c0530aa32048f4904ae162bc14b9eb535eab6c465e960130005feddb71613e7d62aea75f7d3333ba06e805fc8e45681454524e3f8050969fe5a5f7f2392e31d0');
});
it('should throw an error when the input is not a buffer', function() {
(function() {
Hash.sha512(str);
}).should.throw('sha512 hash must be of a buffer');
});
});
describe("#sha512hmac", function() {
it('should calculate this known empty test vector correctly', function() {
var hex = 'b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d47';
Hash.sha512hmac(new Buffer([]), new Buffer([])).toString('hex').should.equal(hex);
});
it('should calculate this known non-empty test vector correctly', function() {
var hex = 'c40bd7c15aa493b309c940e08a73ffbd28b2e4cb729eb94480d727e4df577b13cc403a78e6150d83595f3b17c4cc331f12ca5952691de3735a63c1d4c69a2bac';
var data = new Buffer("test1");
var key = new Buffer("test2");
Hash.sha512hmac(data, key).toString('hex').should.equal(hex);
});
});
});

18
test/index.html

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>Mocha</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="../node_modules/mocha/mocha.js"></script>
<script>mocha.setup('bdd')</script>
<script src="../browser/tests.js"></script>
<script>
mocha.run();
</script>
</body>
</html>

39
test/kdf.js

@ -0,0 +1,39 @@
var should = require('chai').should();
var KDF = require('../lib/kdf');
var Hash = require('../lib/hash');
describe('KDF', function() {
describe('#buf2keypair', function() {
it('should compute these known values', function() {
var buf = Hash.sha256(new Buffer('test'));
var keypair = KDF.buf2keypair(buf);
keypair.privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V');
keypair.pubkey.toString().should.equal('03774f761ae89a0d2fda0d532bad62286ae8fcda9bc38c060036296085592a97c1');
});
});
describe('#sha256hmac2keypair', function() {
it('should compute these known values', function() {
var buf = Hash.sha256(new Buffer('test'));
var keypair = KDF.sha256hmac2keypair(buf);
keypair.privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V');
keypair.pubkey.toString().should.equal('03774f761ae89a0d2fda0d532bad62286ae8fcda9bc38c060036296085592a97c1');
});
});
describe('#sha256hmac2privkey', function() {
it('should compute this known privkey', function() {
var buf = Hash.sha256(new Buffer('test'));
var privkey = KDF.sha256hmac2privkey(buf);
privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V');
});
});
});

165
test/keypair.js

@ -0,0 +1,165 @@
var should = require('chai').should();
var bn = require('../lib/bn');
var point = require('../lib/point');
var Privkey = require('../lib/privkey');
var Pubkey = require('../lib/pubkey');
var Keypair = require('../lib/keypair');
describe('Keypair', function() {
it('should make a blank key', function() {
var key = new Keypair();
should.exist(key);
});
it('should make a key with a priv and pub', function() {
var priv = new Privkey();
var pub = new Pubkey();
var key = new Keypair({privkey: priv, pubkey: pub});
should.exist(key);
should.exist(key.privkey);
should.exist(key.pubkey);
});
describe("#set", function() {
it('should make a new priv and pub', function() {
should.exist(Keypair().set({privkey: Privkey()}).privkey);
});
});
describe('#fromJSON', function() {
it('should make a keypair from this json', function() {
var privkey = Privkey().fromRandom();
var pubkey = Pubkey().fromPrivkey(privkey);
var keypair = Keypair().fromJSON({
privkey: privkey.toJSON(),
pubkey: pubkey.toJSON()
})
keypair.privkey.toString().should.equal(privkey.toString());
keypair.pubkey.toString().should.equal(pubkey.toString());
});
});
describe('#toJSON', function() {
it('should make json from this keypair', function() {
var json = Keypair().fromRandom().toJSON();
should.exist(json.privkey);
should.exist(json.pubkey);
var keypair = Keypair().fromJSON(json);
keypair.toJSON().privkey.toString().should.equal(json.privkey.toString());
keypair.toJSON().pubkey.toString().should.equal(json.pubkey.toString());
});
});
describe("#fromPrivkey", function() {
it('should make a new key from a privkey', function() {
should.exist(Keypair().fromPrivkey(Privkey().fromRandom()).pubkey);
});
});
describe("#fromRandom", function() {
it('should make a new priv and pub, should be compressed, mainnet', function() {
var key = new Keypair();
key.fromRandom();
should.exist(key.privkey);
should.exist(key.pubkey);
key.privkey.bn.gt(bn(0)).should.equal(true);
key.pubkey.point.getX().gt(bn(0)).should.equal(true);
key.pubkey.point.getY().gt(bn(0)).should.equal(true);
key.privkey.compressed.should.equal(true);
key.privkey.networkstr.should.equal('mainnet');
key.pubkey.compressed.should.equal(true);
});
});
describe("#fromString()", function() {
it('should recover a key creating with toString', function() {
var key = new Keypair();
key.fromRandom();
var priv = key.privkey;
var pub = key.pubkey;
var str = key.toString();
key.fromString(str);
should.exist(key.privkey);
should.exist(key.pubkey);
key.privkey.toString().should.equal(priv.toString());
key.pubkey.toString().should.equal(pub.toString());
});
it('should work with only Privkey set', function() {
var key = new Keypair();
key.fromRandom();
key.pubkey = undefined;
var priv = key.privkey;
var str = key.toString();
key.fromString(str);
should.exist(key.privkey);
key.privkey.toString().should.equal(priv.toString());
});
it('should work with only Pubkey set', function() {
var key = new Keypair();
key.fromRandom();
key.privkey = undefined;
var pub = key.pubkey;
var str = key.toString();
key.fromString(str);
should.exist(key.pubkey);
key.pubkey.toString().should.equal(pub.toString());
});
});
describe("#privkey2pubkey", function() {
it('should convert this known Privkey to known Pubkey', function() {
var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff';
var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc';
var key = new Keypair();
key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))});
key.privkey2pubkey();
key.pubkey.toString().should.equal(pubhex);
});
it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() {
var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff';
var key = new Keypair();
key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))});
key.privkey.compressed = true;
key.privkey2pubkey();
key.pubkey.compressed.should.equal(true);
});
it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() {
var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff';
var key = new Keypair();
key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))});
key.privkey.compressed = false;
key.privkey2pubkey();
key.pubkey.compressed.should.equal(false);
});
});
describe("#toString()", function() {
it('should exist', function() {
var key = new Keypair();
key.fromRandom();
should.exist(key.toString());
});
});
});

107
test/message.js

@ -0,0 +1,107 @@
var Address = require('../lib/address');
var Message = require('../lib/message');
var Keypair = require('../lib/keypair');
var should = require('chai').should();
describe('Message', function() {
it('should make a new message', function() {
var message = new Message();
should.exist(message);
});
it('should make a new message when called without "new"', function() {
var message = Message();
should.exist(message);
});
describe('#set', function() {
it('should set the messagebuf', function() {
var messagebuf = new Buffer('message');
should.exist(Message().set({messagebuf: messagebuf}).messagebuf);
});
});
describe('@sign', function() {
var messagebuf = new Buffer('this is my message');
var keypair = Keypair().fromRandom();
it('should return a base64 string', function() {
var sigstr = Message.sign(messagebuf, keypair);
var sigbuf = new Buffer(sigstr, 'base64');
sigbuf.length.should.equal(1 + 32 + 32);
});
it('should sign with a compressed pubkey', function() {
var keypair = Keypair().fromRandom();
keypair.pubkey.compressed = true;
var sigstr = Message.sign(messagebuf, keypair);
var sigbuf = new Buffer(sigstr, 'base64');
sigbuf[0].should.be.above(27 + 4 - 1);
sigbuf[0].should.be.below(27 + 4 + 4 - 1);
});
it('should sign with an uncompressed pubkey', function() {
var keypair = Keypair().fromRandom();
keypair.pubkey.compressed = false;
var sigstr = Message.sign(messagebuf, keypair);
var sigbuf = new Buffer(sigstr, 'base64');
sigbuf[0].should.be.above(27 - 1);
sigbuf[0].should.be.below(27 + 4 - 1);
});
});
describe('@verify', function() {
var messagebuf = new Buffer('this is my message');
var keypair = Keypair().fromRandom();
it('should verify a signed message', function() {
var sigstr = Message.sign(messagebuf, keypair);
var addr = Address().fromPubkey(keypair.pubkey);
Message.verify(messagebuf, sigstr, addr).should.equal(true);
});
it('should verify this known good signature', function() {
var addrstr = '1CKTmxj6DjGrGTfbZzVxnY4Besbv8oxSZb';
var address = Address().fromString(addrstr);
var sigstr = 'IOrTlbNBI0QO990xOw4HAjnvRl/1zR+oBMS6HOjJgfJqXp/1EnFrcJly0UcNelqJNIAH4f0abxOZiSpYmenMH4M=';
Message.verify(messagebuf, sigstr, address);
});
});
describe('#sign', function() {
var messagebuf = new Buffer('this is my message');
var keypair = Keypair().fromRandom();
it('should sign a message', function() {
var message = new Message();
message.messagebuf = messagebuf;
message.keypair = keypair;
message.sign();
var sig = message.sig;
should.exist(sig);
});
});
describe('#verify', function() {
var messagebuf = new Buffer('this is my message');
var keypair = Keypair().fromRandom();
it('should verify a message that was just signed', function() {
var message = new Message();
message.messagebuf = messagebuf;
message.keypair = keypair;
message.address = Address().fromPubkey(keypair.pubkey);
message.sign();
message.verify();
message.verified.should.equal(true);
});
});
});

75
test/opcode.js

@ -0,0 +1,75 @@
var should = require('chai').should();
var Opcode = require('../lib/opcode');
describe('Opcode', function() {
it('should create a new Opcode', function() {
var opcode = new Opcode(5);
});
it('should convert to a string with this handy syntax', function() {
Opcode(0).toString().should.equal('OP_0');
Opcode(96).toString().should.equal('OP_16');
Opcode(97).toString().should.equal('OP_NOP');
});
it('should convert to a number with this handy syntax', function() {
Opcode('OP_0').toNumber().should.equal(0);
Opcode('OP_16').toNumber().should.equal(96);
Opcode('OP_NOP').toNumber().should.equal(97);
});
describe('#fromNumber', function() {
it('should work for 0', function() {
Opcode().fromNumber(0).num.should.equal(0);
});
});
describe('#toNumber', function() {
it('should work for 0', function() {
Opcode().fromNumber(0).toNumber().should.equal(0);
});
});
describe('#fromString', function() {
it('should work for OP_0', function() {
Opcode().fromString('OP_0').num.should.equal(0);
});
});
describe('#toString', function() {
it('should work for OP_0', function() {
Opcode().fromString('OP_0').toString().should.equal('OP_0');
});
});
describe('@map', function() {
it('should have a map containing 116 elements', function() {
var i = 0;
for (var key in Opcode.map) {
i++;
}
i.should.equal(116);
});
});
describe('@reverseMap', function() {
it('should exist and have op 185', function() {
should.exist(Opcode.reverseMap);
Opcode.reverseMap[185].should.equal('OP_NOP10');
});
});
});

117
test/point.js

@ -0,0 +1,117 @@
var should = require('chai').should();
var point = require('../lib/point');
var BN = require('../lib/bn');
describe('Point', function() {
it('should create a point', function() {
var p = point();
should.exist(p);
});
it('should create a point when called with "new"', function() {
var p = new point();
should.exist(p);
});
describe('#getX', function() {
it('should return 0', function() {
var p = point();
p.getX().toString().should.equal('0');
});
it('should be convertable to a buffer', function() {
var p = point();
p.getX().toBuffer({size: 32}).length.should.equal(32);
});
});
describe('#getY', function() {
it('should return 0', function() {
var p = point();
p.getY().toString().should.equal('0');
});
it('should be convertable to a buffer', function() {
var p = point();
p.getY().toBuffer({size: 32}).length.should.equal(32);
});
});
describe('#add', function() {
it('should accurately add g to itself', function() {
var p1 = point.getG();
var p2 = point.getG();
var p3 = p1.add(p2);
p3.getX().toString().should.equal('89565891926547004231252920425935692360644145829622209833684329913297188986597');
p3.getY().toString().should.equal('12158399299693830322967808612713398636155367887041628176798871954788371653930');
});
});
describe('#mul', function() {
it('should accurately multiply g by 2', function() {
var g = point.getG();
var b = g.mul(BN(2));
b.getX().toString().should.equal('89565891926547004231252920425935692360644145829622209833684329913297188986597');
b.getY().toString().should.equal('12158399299693830322967808612713398636155367887041628176798871954788371653930');
});
it('should accurately multiply g by n-1', function() {
var g = point.getG();
var n = point.getN();
var b = g.mul(n.sub(1));
b.getX().toString().should.equal('55066263022277343669578718895168534326250603453777594175500187360389116729240');
b.getY().toString().should.equal('83121579216557378445487899878180864668798711284981320763518679672151497189239');
});
//not sure if this is technically accurate or not...
//normally, you should always multiply g by something less than n
//but it is the same result in OpenSSL
it('should accurately multiply g by n+1', function() {
var g = point.getG();
var n = point.getN();
var b = g.mul(n.add(1));
b.getX().toString().should.equal('55066263022277343669578718895168534326250603453777594175500187360389116729240');
b.getY().toString().should.equal('32670510020758816978083085130507043184471273380659243275938904335757337482424');
});
});
describe('@fromX', function() {
it('should return g', function() {
var g = point.getG();
var p = point.fromX(false, g.getX());
g.eq(p).should.equal(true);
});
});
describe('#validate', function() {
it('should validate this valid point', function() {
var x = BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex'));
var y = BN().fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex'));
var p = point(x, y);
should.exist(p.validate());
});
it('should invalidate this invalid point', function() {
var x = BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex'));
var y = BN().fromBuffer(new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'));
var p = point(x, y);
(function() {
p.validate();
}).should.throw('Invalid y value of public key');
});
});
});

120
test/privkey.js

@ -0,0 +1,120 @@
var Privkey = require('../lib/privkey');
var base58check = require('../lib/base58check');
var BN = require('../lib/bn');
var Point = require('../lib/point');
var should = require('chai').should();
describe('Privkey', function() {
var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a';
var buf = new Buffer(hex, 'hex');
var enctestnet = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG';
var enctu = '92jJzK4tbURm1C7udQXxeCBvXHoHJstDXRxAMouPG1k1XUaXdsu';
var encmainnet = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy';
var encmu = '5JxgQaFM1FMd38cd14e3mbdxsdSa9iM2BV6DHBYsvGzxkTNQ7Un';
it('should create an empty private key', function() {
var privkey = new Privkey();
should.exist(privkey);
});
it('should create a 0 private key with this convenience method', function() {
var bn = BN(0);
var privkey = new Privkey(bn);
privkey.bn.toString().should.equal(bn.toString());
});
it('should create a mainnet private key', function() {
var privkey = new Privkey({bn: BN.fromBuffer(buf), networkstr: 'mainnet', compressed: true});
privkey.toString().should.equal(encmainnet);
});
it('should create an uncompressed testnet private key', function() {
var privkey = new Privkey({bn: BN.fromBuffer(buf), networkstr: 'testnet', compressed: false});
privkey.toString().should.equal(enctu);
});
it('should create an uncompressed mainnet private key', function() {
var privkey = new Privkey({bn: BN.fromBuffer(buf), networkstr: 'mainnet', compressed: false});
privkey.toString().should.equal(encmu);
});
describe('#set', function() {
it('should set bn', function() {
should.exist(Privkey().set({bn: BN.fromBuffer(buf)}).bn);
});
});
describe('#fromJSON', function() {
it('should input this address correctly', function() {
var privkey = new Privkey();
privkey.fromJSON(encmu);
privkey.toWIF().should.equal(encmu);
});
});
describe('#toString', function() {
it('should output this address correctly', function() {
var privkey = new Privkey();
privkey.fromJSON(encmu);
privkey.toJSON().should.equal(encmu);
});
});
describe('#fromRandom', function() {
it('should set bn gt 0 and lt n, and should be compressed', function() {
var privkey = Privkey().fromRandom();
privkey.bn.gt(BN(0)).should.equal(true);
privkey.bn.lt(Point.getN()).should.equal(true);
privkey.compressed.should.equal(true);
});
});
describe('#fromWIF', function() {
it('should parse this compressed testnet address correctly', function() {
var privkey = new Privkey();
privkey.fromWIF(encmainnet);
privkey.toWIF().should.equal(encmainnet);
});
});
describe('#toWIF', function() {
it('should parse this compressed testnet address correctly', function() {
var privkey = new Privkey();
privkey.fromWIF(enctestnet);
privkey.toWIF().should.equal(enctestnet);
});
});
describe('#fromString', function() {
it('should parse this uncompressed testnet address correctly', function() {
var privkey = new Privkey();
privkey.fromString(enctu);
privkey.toWIF().should.equal(enctu);
});
});
describe('#toString', function() {
it('should parse this uncompressed mainnet address correctly', function() {
var privkey = new Privkey();
privkey.fromString(encmu);
privkey.toString().should.equal(encmu);
});
});
});

205
test/pubkey.js

@ -0,0 +1,205 @@
var should = require('chai').should();
var Pubkey = require('../lib/pubkey');
var Point = require('../lib/point');
var Bn = require('../lib/bn');
var Privkey = require('../lib/privkey');
describe('Pubkey', function() {
it('should create a blank public key', function() {
var pk = new Pubkey();
should.exist(pk);
});
it('should create a public key with a point', function() {
var p = Point();
var pk = new Pubkey({point: p});
should.exist(pk.point);
});
it('should create a public key with a point with this convenient method', function() {
var p = Point();
var pk = new Pubkey(p);
should.exist(pk.point);
pk.point.toString().should.equal(p.toString());
});
describe('#set', function() {
it('should make a public key from a point', function() {
should.exist(Pubkey().set({point: Point.getG()}).point);
});
});
describe('#fromJSON', function() {
it('should input this public key', function() {
var pk = new Pubkey();
pk.fromJSON('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
});
});
describe('#toJSON', function() {
it('should output this pubkey', function() {
var pk = new Pubkey();
var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341';
pk.fromJSON(hex).toJSON().should.equal(hex);
});
});
describe('#fromPrivkey', function() {
it('should make a public key from a privkey', function() {
should.exist(Pubkey().fromPrivkey(Privkey().fromRandom()));
});
});
describe('#fromBuffer', function() {
it('should parse this uncompressed public key', function() {
var pk = new Pubkey();
pk.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
});
it('should parse this compressed public key', function() {
var pk = new Pubkey();
pk.fromBuffer(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
});
it('should throw an error on this invalid public key', function() {
var pk = new Pubkey();
(function() {
pk.fromBuffer(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
}).should.throw();
});
});
describe('#fromDER', function() {
it('should parse this uncompressed public key', function() {
var pk = new Pubkey();
pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
});
it('should parse this compressed public key', function() {
var pk = new Pubkey();
pk.fromDER(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
});
it('should throw an error on this invalid public key', function() {
var pk = new Pubkey();
(function() {
pk.fromDER(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
}).should.throw();
});
});
describe('#fromString', function() {
it('should parse this known valid public key', function() {
pk = new Pubkey();
pk.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
});
});
describe('#fromX', function() {
it('should create this known public key', function() {
var x = Bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
var pk = new Pubkey();
pk.fromX(true, x);
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
});
});
describe('#toBuffer', function() {
it('should return this compressed DER format', function() {
var x = Bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
var pk = new Pubkey();
pk.fromX(true, x);
pk.toBuffer().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
});
});
describe('#toDER', function() {
it('should return this compressed DER format', function() {
var x = Bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
var pk = new Pubkey();
pk.fromX(true, x);
pk.toDER(true).toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
});
it('should return this uncompressed DER format', function() {
var x = Bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
var pk = new Pubkey();
pk.fromX(true, x);
pk.toDER(false).toString('hex').should.equal('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
});
});
describe('#toString', function() {
it('should print this known public key', function() {
var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
var pk = new Pubkey();
pk.fromString(hex);
pk.toString().should.equal(hex);
});
});
describe('#validate', function() {
it('should not throw an error if pubkey is valid', function() {
var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
var pk = new Pubkey();
pk.fromString(hex);
should.exist(pk.validate());
});
it('should not throw an error if pubkey is invalid', function() {
var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a0000000000000000000000000000000000000000000000000000000000000000';
var pk = new Pubkey();
pk.fromString(hex);
(function() {
pk.validate();
}).should.throw('Invalid y value of public key');
});
it('should not throw an error if pubkey is infinity', function() {
var pk = new Pubkey();
pk.point = Point.getG().mul(Point.getN());
(function() {
pk.validate();
}).should.throw('Point cannot be equal to Infinity');
});
});
});

61
test/random.js

@ -0,0 +1,61 @@
var should = require('chai').should();
var Random = require('../lib/random');
describe('Random', function() {
describe('@getRandomBuffer', function() {
it('should return a buffer', function() {
var bytes = Random.getRandomBuffer(8);
bytes.length.should.equal(8);
Buffer.isBuffer(bytes).should.equal(true);
});
it('should not equate two 256 bit random buffers', function() {
var bytes1 = Random.getRandomBuffer(32);
var bytes2 = Random.getRandomBuffer(32);
bytes1.toString('hex').should.not.equal(bytes2.toString('hex'));
});
it('should generate 100 8 byte buffers in a row that are not equal', function() {
var hexs = [];
for (var i = 0; i < 100; i++)
hexs[i] = Random.getRandomBuffer(8).toString('hex');
for (var i = 0; i < 100; i++)
for (var j = i + 1; j < 100; j++)
hexs[i].should.not.equal(hexs[j]);
});
});
describe('@getPseudoRandomBuffer', function() {
it('should generate 7 random bytes', function() {
var buf = Random.getPseudoRandomBuffer(7);
buf.length.should.equal(7);
});
it('should generate 8 random bytes', function() {
var buf = Random.getPseudoRandomBuffer(8);
buf.length.should.equal(8);
});
it('should generate 9 random bytes', function() {
var buf = Random.getPseudoRandomBuffer(9);
buf.length.should.equal(9);
});
it('should generate 90 random bytes', function() {
var buf = Random.getPseudoRandomBuffer(90);
buf.length.should.equal(90);
});
it('should generate two 8 byte buffers that are not equal', function() {
var buf1 = Random.getPseudoRandomBuffer(8);
var buf2 = Random.getPseudoRandomBuffer(8);
buf1.toString('hex').should.not.equal(buf2.toString('hex'));
});
});
});

308
test/script.js

@ -0,0 +1,308 @@
var Script = require('../lib/script');
var should = require('chai').should();
var Opcode = require('../lib/opcode');
var BufferReader = require('../lib/bufferreader');
var BufferWriter = require('../lib/bufferwriter');
describe('Script', function() {
it('should make a new script', function() {
var script = new Script();
});
describe('#fromBuffer', function() {
it('should parse this buffer containing an OP code', function() {
var buf = new Buffer(1);
buf[0] = Opcode('OP_0').toNumber();
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].should.equal(buf[0]);
});
it('should parse this buffer containing another OP code', function() {
var buf = new Buffer(1);
buf[0] = Opcode('OP_CHECKMULTISIG').toNumber();
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].should.equal(buf[0]);
});
it('should parse this buffer containing three bytes of data', function() {
var buf = new Buffer([3, 1, 2, 3]);
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].buf.toString('hex').should.equal('010203');
});
it('should parse this buffer containing OP_PUSHDATA1 and three bytes of data', function() {
var buf = new Buffer([0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA1').toNumber();
buf.writeUInt8(3, 1);
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].buf.toString('hex').should.equal('010203');
});
it('should parse this buffer containing OP_PUSHDATA2 and three bytes of data', function() {
var buf = new Buffer([0, 0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA2').toNumber();
buf.writeUInt16LE(3, 1);
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].buf.toString('hex').should.equal('010203');
});
it('should parse this buffer containing OP_PUSHDATA4 and three bytes of data', function() {
var buf = new Buffer([0, 0, 0, 0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA4').toNumber();
buf.writeUInt16LE(3, 1);
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].buf.toString('hex').should.equal('010203');
});
it('should parse this buffer an OP code, data, and another OP code', function() {
var buf = new Buffer([0, 0, 0, 0, 0, 0, 1, 2, 3, 0]);
buf[0] = Opcode('OP_0').toNumber();
buf[1] = Opcode('OP_PUSHDATA4').toNumber();
buf.writeUInt16LE(3, 2);
buf[buf.length - 1] = Opcode('OP_0').toNumber();
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(3);
script.chunks[0].should.equal(buf[0]);
script.chunks[1].buf.toString('hex').should.equal('010203');
script.chunks[2].should.equal(buf[buf.length - 1]);
});
});
describe('#toBuffer', function() {
it('should output this buffer containing an OP code', function() {
var buf = new Buffer(1);
buf[0] = Opcode('OP_0').toNumber();
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].should.equal(buf[0]);
script.toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
it('should output this buffer containing another OP code', function() {
var buf = new Buffer(1);
buf[0] = Opcode('OP_CHECKMULTISIG').toNumber();
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].should.equal(buf[0]);
script.toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
it('should output this buffer containing three bytes of data', function() {
var buf = new Buffer([3, 1, 2, 3]);
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].buf.toString('hex').should.equal('010203');
script.toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
it('should output this buffer containing OP_PUSHDATA1 and three bytes of data', function() {
var buf = new Buffer([0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA1').toNumber();
buf.writeUInt8(3, 1);
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].buf.toString('hex').should.equal('010203');
script.toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
it('should output this buffer containing OP_PUSHDATA2 and three bytes of data', function() {
var buf = new Buffer([0, 0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA2').toNumber();
buf.writeUInt16LE(3, 1);
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].buf.toString('hex').should.equal('010203');
script.toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
it('should output this buffer containing OP_PUSHDATA4 and three bytes of data', function() {
var buf = new Buffer([0, 0, 0, 0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA4').toNumber();
buf.writeUInt16LE(3, 1);
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].buf.toString('hex').should.equal('010203');
script.toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
it('should output this buffer an OP code, data, and another OP code', function() {
var buf = new Buffer([0, 0, 0, 0, 0, 0, 1, 2, 3, 0]);
buf[0] = Opcode('OP_0').toNumber();
buf[1] = Opcode('OP_PUSHDATA4').toNumber();
buf.writeUInt16LE(3, 2);
buf[buf.length - 1] = Opcode('OP_0').toNumber();
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(3);
script.chunks[0].should.equal(buf[0]);
script.chunks[1].buf.toString('hex').should.equal('010203');
script.chunks[2].should.equal(buf[buf.length - 1]);
script.toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
});
describe('#fromString', function() {
it('should parse these known scripts', function() {
Script().fromString('OP_0 OP_PUSHDATA4 3 0x010203 OP_0').toString().should.equal('OP_0 OP_PUSHDATA4 3 0x010203 OP_0');
Script().fromString('OP_0 OP_PUSHDATA2 3 0x010203 OP_0').toString().should.equal('OP_0 OP_PUSHDATA2 3 0x010203 OP_0');
Script().fromString('OP_0 OP_PUSHDATA1 3 0x010203 OP_0').toString().should.equal('OP_0 OP_PUSHDATA1 3 0x010203 OP_0');
Script().fromString('OP_0 3 0x010203 OP_0').toString().should.equal('OP_0 3 0x010203 OP_0');
});
});
describe('#toString', function() {
it('should output this buffer an OP code, data, and another OP code', function() {
var buf = new Buffer([0, 0, 0, 0, 0, 0, 1, 2, 3, 0]);
buf[0] = Opcode('OP_0').toNumber();
buf[1] = Opcode('OP_PUSHDATA4').toNumber();
buf.writeUInt16LE(3, 2);
buf[buf.length - 1] = Opcode('OP_0').toNumber();
var script = Script().fromBuffer(buf);
script.chunks.length.should.equal(3);
script.chunks[0].should.equal(buf[0]);
script.chunks[1].buf.toString('hex').should.equal('010203');
script.chunks[2].should.equal(buf[buf.length - 1]);
script.toString().toString('hex').should.equal('OP_0 OP_PUSHDATA4 3 0x010203 OP_0');
});
});
describe('#fromJSON', function() {
it('should parse this known script', function() {
Script().fromJSON('OP_0 OP_PUSHDATA4 3 0x010203 OP_0').toString().should.equal('OP_0 OP_PUSHDATA4 3 0x010203 OP_0');
});
});
describe('#toJSON', function() {
it('should output this known script', function() {
Script().fromString('OP_0 OP_PUSHDATA4 3 0x010203 OP_0').toJSON().should.equal('OP_0 OP_PUSHDATA4 3 0x010203 OP_0');
});
});
describe('#isOpReturn', function() {
it('should know this is a (blank) OP_RETURN script', function() {
Script('OP_RETURN').isOpReturn().should.equal(true);
});
it('should know this is an OP_RETURN script', function() {
var buf = new Buffer(40);
buf.fill(0);
Script('OP_RETURN 40 0x' + buf.toString('hex')).isOpReturn().should.equal(true);
});
it('should know this is not an OP_RETURN script', function() {
var buf = new Buffer(40);
buf.fill(0);
Script('OP_CHECKMULTISIG 40 0x' + buf.toString('hex')).isOpReturn().should.equal(false);
});
});
describe('#isPubkeyhashIn', function() {
it('should classify this known pubkeyhashin', function() {
Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x04e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6').isPubkeyhashIn().should.equal(true);
});
it('should classify this known non-pubkeyhashin', function() {
Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x04e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6 OP_CHECKSIG').isPubkeyhashIn().should.equal(false);
});
});
describe('#isPubkeyhashOut', function() {
it('should classify this known pubkeyhashout as pubkeyhashout', function() {
Script('OP_DUP OP_HASH160 20 0000000000000000000000000000000000000000 OP_EQUALVERIFY OP_CHECKSIG').isPubkeyhashOut().should.equal(true);
});
it('should classify this known non-pubkeyhashout as not pubkeyhashout', function() {
Script('OP_DUP OP_HASH160 20 0000000000000000000000000000000000000000').isPubkeyhashOut().should.equal(false)
});
});
describe('#isScripthashIn', function() {
it('should classify this known scripthashin', function() {
Script('20 0000000000000000000000000000000000000000').isScripthashIn().should.equal(true);
});
it('should classify this known non-scripthashin', function() {
Script('20 0000000000000000000000000000000000000000 OP_CHECKSIG').isScripthashIn().should.equal(false);
});
});
describe('#isScripthashOut', function() {
it('should classify this known pubkeyhashout as pubkeyhashout', function() {
Script('OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUAL').isScripthashOut().should.equal(true);
});
it('should classify these known non-pubkeyhashout as not pubkeyhashout', function() {
Script('OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUAL OP_EQUAL').isScripthashOut().should.equal(false);
Script('OP_HASH160 21 0x000000000000000000000000000000000000000000 OP_EQUAL').isScripthashOut().should.equal(false);
});
});
describe('#writeOp', function() {
it('should write these ops', function() {
Script().writeOp('OP_CHECKMULTISIG').toString().should.equal('OP_CHECKMULTISIG');
Script().writeOp(Opcode.map.OP_CHECKMULTISIG).toString().should.equal('OP_CHECKMULTISIG');
});
});
describe('#writeBuffer', function() {
it('should write these push data', function() {
var buf = new Buffer(1);
buf.fill(0);
Script().writeBuffer(buf).toString().should.equal('1 0x00');
buf = new Buffer(255);
buf.fill(0);
Script().writeBuffer(buf).toString().should.equal('OP_PUSHDATA1 255 0x' + buf.toString('hex'));
buf = new Buffer(256);
buf.fill(0);
Script().writeBuffer(buf).toString().should.equal('OP_PUSHDATA2 256 0x' + buf.toString('hex'));
buf = new Buffer(Math.pow(2, 16));
buf.fill(0);
Script().writeBuffer(buf).toString().should.equal('OP_PUSHDATA4 ' + Math.pow(2, 16) + ' 0x' + buf.toString('hex'));
});
});
describe('#write', function() {
it('should write both pushdata and non-pushdata chunks', function() {
Script().write('OP_CHECKMULTISIG').toString().should.equal('OP_CHECKMULTISIG');
Script().write(Opcode.map.OP_CHECKMULTISIG).toString().should.equal('OP_CHECKMULTISIG');
var buf = new Buffer(1);
buf.fill(0);
Script().write(buf).toString().should.equal('1 0x00');
});
});
});

149
test/signature.js

@ -0,0 +1,149 @@
var BN = require('../lib/bn');
var should = require('chai').should();
var Signature = require('../lib/signature');
describe('Signature', function() {
it('should make a blank signature', function() {
var sig = new Signature();
should.exist(sig);
});
it('should work with conveniently setting r, s', function() {
var r = BN();
var s = BN();
var sig = new Signature(r, s);
should.exist(sig);
sig.r.toString().should.equal(r.toString());
sig.s.toString().should.equal(s.toString());
});
describe('#set', function() {
it('should set compressed', function() {
should.exist(Signature().set({compressed: true}));
});
});
describe('#fromCompact', function() {
it('should create a signature from a compressed signature', function() {
var blank = new Buffer(32);
blank.fill(0);
var compressed = Buffer.concat([
new Buffer([0 + 27 + 4]),
blank,
blank
]);
var sig = new Signature();
sig.fromCompact(compressed);
sig.r.cmp(0).should.equal(0);
sig.s.cmp(0).should.equal(0);
});
});
describe('#fromDER', function() {
var buf = new Buffer('3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2', 'hex');
it('should parse this DER format signature', function() {
var sig = new Signature();
sig.fromDER(buf);
sig.r.toBuffer({size: 32}).toString('hex').should.equal('75fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e6277');
sig.s.toBuffer({size: 32}).toString('hex').should.equal('729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2');
});
});
describe('#fromString', function() {
var buf = new Buffer('3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2', 'hex');
it('should parse this DER format signature in hex', function() {
var sig = new Signature();
sig.fromString(buf.toString('hex'));
sig.r.toBuffer({size: 32}).toString('hex').should.equal('75fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e6277');
sig.s.toBuffer({size: 32}).toString('hex').should.equal('729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2');
});
});
describe('#parseDER', function() {
it('should parse this signature generated in node', function() {
var sighex = '30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72';
var sig = new Buffer(sighex, 'hex');
var parsed = Signature.parseDER(sig);
parsed.header.should.equal(0x30)
parsed.length.should.equal(69)
parsed.rlength.should.equal(33);
parsed.rneg.should.equal(true);
parsed.rbuf.toString('hex').should.equal('008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa');
parsed.r.toString().should.equal('63173831029936981022572627018246571655303050627048489594159321588908385378810');
parsed.slength.should.equal(32);
parsed.sneg.should.equal(false);
parsed.sbuf.toString('hex').should.equal('0993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72');
parsed.s.toString().should.equal('4331694221846364448463828256391194279133231453999942381442030409253074198130');
});
it('should parse this 69 byte signature', function() {
var sighex = '3043021f59e4705959cc78acbfcf8bd0114e9cc1b389a4287fb33152b73a38c319b50302202f7428a27284c757e409bf41506183e9e49dfb54d5063796dfa0d403a4deccfa';
var sig = new Buffer(sighex, 'hex');
var parsed = Signature.parseDER(sig);
parsed.header.should.equal(0x30)
parsed.length.should.equal(67)
parsed.rlength.should.equal(31);
parsed.rneg.should.equal(false);
parsed.rbuf.toString('hex').should.equal('59e4705959cc78acbfcf8bd0114e9cc1b389a4287fb33152b73a38c319b503');
parsed.r.toString().should.equal('158826015856106182499128681792325160381907915189052224498209222621383996675');
parsed.slength.should.equal(32);
parsed.sneg.should.equal(false);
parsed.sbuf.toString('hex').should.equal('2f7428a27284c757e409bf41506183e9e49dfb54d5063796dfa0d403a4deccfa');
parsed.s.toString().should.equal('21463938592353267769710297084836796652964571266930856168996063301532842380538');
});
it('should parse this 68 byte signature', function() {
var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a51';
var sig = new Buffer(sighex, 'hex');
var parsed = Signature.parseDER(sig);
parsed.header.should.equal(0x30)
parsed.length.should.equal(66)
parsed.rlength.should.equal(30);
parsed.rneg.should.equal(false);
parsed.rbuf.toString('hex').should.equal('17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632');
parsed.r.toString().should.equal('164345250294671732127776123343329699648286106708464198588053542748255794');
parsed.slength.should.equal(32);
parsed.sneg.should.equal(false);
parsed.sbuf.toString('hex').should.equal('61bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a51');
parsed.s.toString().should.equal('44212963026209759051804639008236126356702363229859210154760104982946304432721');
});
});
describe('#toDER', function() {
it('should convert these known r and s values into a known signature', function() {
var r = BN('63173831029936981022572627018246571655303050627048489594159321588908385378810');
var s = BN('4331694221846364448463828256391194279133231453999942381442030409253074198130');
var sig = new Signature({r: r, s: s});
var der = sig.toDER(r, s);
der.toString('hex').should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72');
});
});
describe('#toString', function() {
it('should convert this signature in to hex DER', function() {
var r = BN('63173831029936981022572627018246571655303050627048489594159321588908385378810');
var s = BN('4331694221846364448463828256391194279133231453999942381442030409253074198130');
var sig = new Signature({r: r, s: s});
var hex = sig.toString();
hex.should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72');
});
});
});

149
test/stealthaddress.js

@ -0,0 +1,149 @@
var StealthAddress = require('../lib/expmt/stealthaddress');
var should = require('chai').should();
var Stealthkey = require('../lib/expmt/stealthkey');
var Keypair = require('../lib/keypair');
var Privkey = require('../lib/privkey');
var Pubkey = require('../lib/pubkey');
var BN = require('../lib/bn');
var Hash = require('../lib/hash');
var Base58check = require('../lib/base58check');
describe('StealthAddress', function() {
var stealthkey = Stealthkey();
stealthkey.payloadKeypair = Keypair();
stealthkey.payloadKeypair.privkey = Privkey();
stealthkey.payloadKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1')));
stealthkey.payloadKeypair.privkey2pubkey();
stealthkey.scanKeypair = Keypair();
stealthkey.scanKeypair.privkey = Privkey();
stealthkey.scanKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2')));
stealthkey.scanKeypair.privkey2pubkey();
var senderKeypair = Keypair();
senderKeypair.privkey = Privkey();
senderKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3')));
senderKeypair.privkey2pubkey();
var addressString = 'vJmtuUb8ysKiM1HtHQF23FGfjGAKu5sM94UyyjknqhJHNdj5CZzwtpGzeyaATQ2HvuzomNVtiwsTJSWzzCBgCTtUZbRFpzKVq9MAUr';
var dwhex = '2a0002697763d7e9becb0c180083738c32c05b0e2fee26d6278020c06bbb04d5f66b32010362408459041e0473298af3824dbabe4d2b7f846825ed4d1c2e2c670c07cb275d0100';
var dwbuf = new Buffer(dwhex, 'hex');
it('should make a new stealth address', function() {
var sa = new StealthAddress();
should.exist(sa);
sa = StealthAddress();
should.exist(sa);
sa = StealthAddress(addressString);
should.exist(sa);
sa = StealthAddress(Base58check.decode(addressString));
should.exist(sa);
});
describe('#fromJSON', function() {
it('should give a stealthkey address with the right pubkeys', function() {
var sa = new StealthAddress();
sa.fromJSON(addressString);
sa.payloadPubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString());
sa.scanPubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString());
});
});
describe('#toJSON', function() {
it('should return this known address string', function() {
StealthAddress().fromJSON(addressString).toJSON().should.equal(addressString);
});
});
describe('#fromBuffer', function() {
it('should parse this DW buffer', function() {
StealthAddress().fromBuffer(new Buffer(dwhex, 'hex')).toBuffer().toString('hex').should.equal(dwhex);
});
});
describe('#fromString', function() {
it('should parse this DW buffer', function() {
StealthAddress().fromString(Base58check(new Buffer(dwhex, 'hex')).toString()).toBuffer().toString('hex').should.equal(dwhex);
});
});
describe('#getSharedKeypair', function() {
it('should return a key', function() {
var sa = new StealthAddress();
sa.payloadPubkey = stealthkey.payloadKeypair.pubkey;
sa.scanPubkey = stealthkey.scanKeypair.pubkey;
var key = sa.getSharedKeypair(senderKeypair);
(key instanceof Keypair).should.equal(true);
});
it('should return the same key as Stealthkey.prototype.getSharedKeypair', function() {
var sa = new StealthAddress();
sa.payloadPubkey = stealthkey.payloadKeypair.pubkey;
sa.scanPubkey = stealthkey.scanKeypair.pubkey;
var key = sa.getSharedKeypair(senderKeypair);
var key2 = stealthkey.getSharedKeypair(senderKeypair.pubkey);
key.toString().should.equal(key2.toString());
});
});
describe('#getReceivePubkey', function() {
it('should return a pubkey', function() {
var pubkey = StealthAddress().fromStealthkey(stealthkey).getReceivePubkey(senderKeypair);
(pubkey instanceof Pubkey).should.equal(true);
});
it('should return the same pubkey as getReceivePubkey', function() {
var pubkey = StealthAddress().fromStealthkey(stealthkey).getReceivePubkey(senderKeypair);
var pubkey2 = stealthkey.getReceivePubkey(senderKeypair.pubkey);
pubkey2.toString().should.equal(pubkey.toString());
});
});
describe('#toBuffer', function() {
it('should return this known address buffer', function() {
var buf = Base58check.decode(addressString);
StealthAddress().fromBuffer(dwbuf).toBuffer().toString('hex').should.equal(dwhex);
});
});
describe('#toString', function() {
it('should return this known address buffer', function() {
var buf = Base58check.decode(addressString);
StealthAddress().fromBuffer(buf).toString().should.equal(Base58check(new Buffer(dwhex, 'hex')).toString());
});
});
describe('@parseDWBuffer', function() {
it('should parse this known DW buffer', function() {
var buf = new Buffer(dwhex, 'hex');
var parsed = StealthAddress.parseDWBuffer(buf);
parsed.version.should.equal(42);
parsed.options.should.equal(0);
parsed.scanPubkey.toString().should.equal('02697763d7e9becb0c180083738c32c05b0e2fee26d6278020c06bbb04d5f66b32');
parsed.nPayloadPubkeys.should.equal(1);
parsed.payloadPubkeys[0].toString().should.equal('0362408459041e0473298af3824dbabe4d2b7f846825ed4d1c2e2c670c07cb275d');
parsed.nSigs.should.equal(1);
parsed.prefix.toString().should.equal('');
});
});
});

138
test/stealthkey.js

@ -0,0 +1,138 @@
var should = require('chai').should();
var Stealthkey = require('../lib/expmt/stealthkey');
var Keypair = require('../lib/keypair');
var Privkey = require('../lib/privkey');
var Pubkey = require('../lib/pubkey');
var BN = require('../lib/bn');
var Hash = require('../lib/hash');
describe('Stealthkey', function() {
var stealthkey = Stealthkey();
stealthkey.payloadKeypair = Keypair();
stealthkey.payloadKeypair.privkey = Privkey();
stealthkey.payloadKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1')));
stealthkey.payloadKeypair.privkey2pubkey();
stealthkey.scanKeypair = Keypair();
stealthkey.scanKeypair.privkey = Privkey();
stealthkey.scanKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2')));
stealthkey.scanKeypair.privkey2pubkey();
var senderKeypair = Keypair();
senderKeypair.privkey = Privkey();
senderKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3')));
senderKeypair.privkey2pubkey();
it('should create a new stealthkey', function() {
var stealthkey = new Stealthkey();
should.exist(stealthkey);
});
it('should create a new stealthkey without using "new"', function() {
var stealthkey = Stealthkey();
should.exist(stealthkey);
});
it('should create a new stealthkey with both keypairs in the constructor', function() {
var keypair1 = Keypair();
var keypair2 = Keypair();
var stealthkey = Stealthkey(keypair1, keypair2);
should.exist(stealthkey.payloadKeypair);
should.exist(stealthkey.scanKeypair);
});
describe('#set', function() {
it('should set payload key', function() {
should.exist(Stealthkey().set({payloadKeypair: stealthkey.payloadKeypair}).payloadKeypair);
});
});
describe('#fromJSON', function() {
it('should make a stealthkey from this JSON', function() {
var sk = Stealthkey().fromJSON({
payloadKeypair: stealthkey.payloadKeypair.toJSON(),
scanKeypair: stealthkey.scanKeypair.toJSON()
});
sk.payloadKeypair.toString().should.equal(stealthkey.payloadKeypair.toString());
sk.scanKeypair.toString().should.equal(stealthkey.scanKeypair.toString());
});
});
describe('#toJSON', function() {
it('should convert this stealthkey to json', function() {
var json = stealthkey.toJSON()
var json2 = Stealthkey().fromJSON(json).toJSON();
json.payloadKeypair.privkey.should.equal(json2.payloadKeypair.privkey);
json.scanKeypair.privkey.should.equal(json2.scanKeypair.privkey);
});
});
describe('#fromRandom', function() {
it('should create a new stealthkey from random', function() {
var stealthkey = Stealthkey().fromRandom();
should.exist(stealthkey.payloadKeypair.privkey.bn.gt(0));
should.exist(stealthkey.scanKeypair.privkey.bn.gt(0));
});
});
describe('#getSharedKeypair', function() {
it('should return a key', function() {
var key = stealthkey.getSharedKeypair(senderKeypair.pubkey);
(key instanceof Keypair).should.equal(true);
});
});
describe('#getReceivePubkey', function() {
it('should return a pubkey', function() {
var pubkey = stealthkey.getReceivePubkey(senderKeypair.pubkey);
(pubkey instanceof Pubkey).should.equal(true);
});
});
describe('#getReceiveKeypair', function() {
it('should return a key', function() {
var key = stealthkey.getReceiveKeypair(senderKeypair.pubkey);
(key instanceof Keypair).should.equal(true);
});
it('should return a key with the same pubkey as getReceivePubkey', function() {
var key = stealthkey.getReceiveKeypair(senderKeypair.pubkey);
var pubkey = stealthkey.getReceivePubkey(senderKeypair.pubkey);
key.pubkey.toString().should.equal(pubkey.toString());
});
it('should return private key with length 32 or less', function() {
var key = stealthkey.getReceiveKeypair(senderKeypair.pubkey);
key.privkey.bn.toBuffer().length.should.be.below(33);
});
});
describe('#isForMe', function() {
it('should return true if it (the transaction or message) is for me', function() {
var pubkeyhash = new Buffer('3cb64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex');
stealthkey.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(true);
});
it('should return false if it (the transaction or message) is not for me', function() {
var pubkeyhash = new Buffer('00b64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex');
stealthkey.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(false);
});
});
});

139
test/stealthmessage.js

@ -0,0 +1,139 @@
var Keypair = require('../lib/keypair');
var StealthMessage = require('../lib/expmt/stealthmessage');
var Stealthkey = require('../lib/expmt/stealthkey');
var StealthAddress = require('../lib/expmt/stealthaddress');
var KDF = require('../lib/kdf');
var Hash = require('../lib/hash');
var should = require('chai').should();
var Address = require('../lib/address');
describe('StealthMessage', function() {
var payloadKeypair = KDF.buf2keypair(new Buffer('key1'));
var scanKeypair = KDF.buf2keypair(new Buffer('key2'));
var fromKeypair = KDF.buf2keypair(new Buffer('key3'));
var enchex = 'f557994f16d0d628fa4fdb4ab3d7e0bc5f2754f20381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec0381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec9f86d081884c7d659a2feaa0c55ad01560ba2904d3bc8395b6c4a6f87648edb33db6a22170e5e26f340c7ba943169210234cd6a753ad13919b0ab7d678b47b5e7d63e452382de2c2590fb57ef048f7b3';
var encbuf = new Buffer(enchex, 'hex');
var ivbuf = Hash.sha256(new Buffer('test')).slice(0, 128 / 8);
var sk = Stealthkey().set({payloadKeypair: payloadKeypair, scanKeypair: scanKeypair});
var sa = StealthAddress().fromStealthkey(sk);
var messagebuf = new Buffer('this is my message');
it('should make a new stealthmessage', function() {
var sm = new StealthMessage();
should.exist(sm);
sm = StealthMessage()
should.exist(sm);
});
it('should allow "set" style syntax', function() {
var encbuf = StealthMessage().set({
messagebuf: messagebuf,
toStealthAddress: sa
}).encrypt().encbuf;
should.exist(encbuf);
encbuf.length.should.equal(113);
});
describe('#set', function() {
it('should set the messagebuf', function() {
var sm = StealthMessage().set({messagebuf: messagebuf});
should.exist(sm.messagebuf);
});
});
describe('@encrypt', function() {
it('should encrypt a message', function() {
var encbuf = StealthMessage.encrypt(messagebuf, sa);
encbuf.length.should.equal(166);
});
it('should encrypt a message with this fromKeypair and ivbuf the same each time', function() {
var encbuf = StealthMessage.encrypt(messagebuf, sa, fromKeypair, ivbuf);
encbuf.length.should.equal(166);
encbuf.toString('hex').should.equal(enchex);
});
});
describe('@decrypt', function() {
it('should decrypt this known message correctly', function() {
var messagebuf2 = StealthMessage.decrypt(encbuf, sk);
messagebuf2.toString('hex').should.equal(messagebuf.toString('hex'));
});
});
describe('@isForMe', function() {
it('should know that this message is for me', function() {
StealthMessage.isForMe(encbuf, sk).should.equal(true);
});
it('should know that this message is for me even if my payloadPrivkey is not present', function() {
var sk2 = new Stealthkey();
sk2.scanKeypair = sk.scanKeypair;
sk2.payloadKeypair = Keypair().set({pubkey: sk.payloadKeypair.pubkey});
should.not.exist(sk2.payloadKeypair.privkey);
StealthMessage.isForMe(encbuf, sk2).should.equal(true);
});
});
describe('#encrypt', function() {
it('should encrypt this message', function() {
var sm = StealthMessage().set({
messagebuf: messagebuf,
toStealthAddress: sa,
fromKeypair: fromKeypair
});
sm.encrypt().encbuf.length.should.equal(113);
});
});
describe('#decrypt', function() {
it('should decrypt that which was encrypted', function() {
var sm = StealthMessage().set({
messagebuf: messagebuf,
toStealthAddress: sa
}).encrypt();
var messagebuf2 = StealthMessage().set({
encbuf: sm.encbuf,
fromKeypair: sm.fromKeypair,
toStealthkey: sk
}).decrypt().messagebuf;
messagebuf2.toString('hex').should.equal(messagebuf.toString('hex'));
});
});
describe('#isForMe', function() {
it('should know that this message is for me', function() {
StealthMessage().set({
encbuf: encbuf,
toStealthkey: sk,
fromKeypair: fromKeypair,
receiveAddress: Address().set({hashbuf: encbuf.slice(0, 20)})
}).isForMe().should.equal(true);
});
it('should know that this message is not for me', function() {
StealthMessage().set({
encbuf: encbuf,
toStealthkey: sk,
fromKeypair: fromKeypair,
receiveAddress: Address().set({hashbuf: encbuf.slice(0, 20)})
}).isForMe().should.equal(true);
});
});
});

68
test/stealthtx.js

@ -0,0 +1,68 @@
var should = require('chai').should();
var Txout = require('../lib/txout');
var Stealthkey = require('../lib/expmt/stealthkey');
var StealthTx = require('../lib/expmt/stealthtx');
var Transaction = require('../lib/transaction');
var Varint = require('../lib/varint');
describe('StealthTx', function() {
var txhex = '0100000001c828ccce36eca04f96321ad488528af86c7598e67157c4f8e2f462a9e0e3af5f010000006a47304402204525eef6a56cc57fb184e53efdfdc1086d5265da21480d55c2184536440a64f70220349cdc6c66a8507dde0d172fe64aeb57ae56e014b50315f615086a6b85c5424e012102c0633ddb6bf2a8686e2ba4ce8026c94e1e27ef12e73f8fed6d6d2b97199f9b74ffffffff020000000000000000286a2606deadbeef0365b5a5b0ba059666e907b0b5e07b37fdb162d1399ed829315491fe1f30c87b3f905f0100000000001976a9142042d5e7ef9e82346419fbfe7df5ae52fe4bea3c88ac00000000';
var txbuf = new Buffer(txhex, 'hex');
var txidhex = '66da969fff214c329e27062beaf3baf20ed035801559b31f3e868c2de4cdfc5b';
var tx = Transaction(txbuf);
it('should make a new StealthTx', function() {
var stx = new StealthTx();
should.exist(stx);
stx = StealthTx();
should.exist(stx);
});
describe('#isForMe', function() {
it('should return false for this known tx and random stealthkey', function() {
var sk = Stealthkey().fromRandom();
var stx = StealthTx().set({sk: sk, tx: tx});
stx.isForMe().should.equal(false);
});
});
describe('#notMine', function() {
it('should return true for this known tx and random stealthkey', function() {
var sk = Stealthkey().fromRandom();
var stx = StealthTx().set({sk: sk, tx: tx});
stx.notMine().should.equal("StealthTx not mine");
});
});
describe('#notStealth', function() {
it('should know this is a stealth tx', function() {
var stx = StealthTx().set({tx: tx});
stx.notStealth().should.equal(false);
});
it('should know this is not a stealth tx', function() {
var tx2 = Transaction(tx);
tx2.txouts.pop();
tx2.txoutsvi = Varint(1);
var stx = StealthTx().set({tx: tx2});
stx.notStealth().should.equal("Not enough txouts");
});
});
describe('@parseOpReturnData', function() {
var txout = tx.txouts[0];
var buf = txout.script.chunks[1].buf;
var parsed = StealthTx.parseOpReturnData(buf);
(typeof parsed.version).should.equal('number');
parsed.noncebuf.length.should.be.above(0);
parsed.pubkey.toBuffer().length.should.equal(33);
});
});

195
test/transaction.js

@ -0,0 +1,195 @@
var Varint = require('../lib/varint');
var Transaction = require('../lib/transaction');
var Txin = require('../lib/txin');
var Txout = require('../lib/txout');
var should = require('chai').should();
var BufferReader = require('../lib/bufferreader');
var BufferWriter = require('../lib/bufferwriter');
describe('Transaction', function() {
var txin = Txin().fromBuffer(new Buffer('00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000', 'hex'));
var txout = Txout().fromBuffer(new Buffer('050000000000000001ae', 'hex'));
var tx = Transaction().set({
version: 0,
txinsvi: Varint(1),
txins: [txin],
txoutsvi: Varint(1),
txouts: [txout],
nlocktime: 0
});
var txhex = '000000000100000000000000000000000000000000000000000000000000000000000000000000000001ae0000000001050000000000000001ae00000000';
var txbuf = new Buffer(txhex, 'hex');
var tx2idhex = '8c9aa966d35bfeaf031409e0001b90ccdafd8d859799eb945a3c515b8260bcf2';
var tx2hex = '01000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000';
var tx2buf = new Buffer(tx2hex, 'hex');
it('should make a new transaction', function() {
var tx = new Transaction();
should.exist(tx);
tx = Transaction();
should.exist(tx);
Transaction(txbuf).toBuffer().toString('hex').should.equal(txhex);
//should set known defaults
tx.version.should.equal(1);
tx.txinsvi.toNumber().should.equal(0);
tx.txins.length.should.equal(0);
tx.txoutsvi.toNumber().should.equal(0);
tx.txouts.length.should.equal(0);
tx.nlocktime.should.equal(0xffffffff);
});
describe('#initialize', function() {
it('should set these known defaults', function() {
var tx = new Transaction();
tx.initialize();
tx.version.should.equal(1);
tx.txinsvi.toNumber().should.equal(0);
tx.txins.length.should.equal(0);
tx.txoutsvi.toNumber().should.equal(0);
tx.txouts.length.should.equal(0);
tx.nlocktime.should.equal(0xffffffff);
});
});
describe('#set', function() {
it('should set all the basic parameters', function() {
var tx = Transaction().set({
version: 0,
txinsvi: Varint(1),
txins: [txin],
txoutsvi: Varint(1),
txouts: [txout],
nlocktime: 0
});
should.exist(tx.version);
should.exist(tx.txinsvi);
should.exist(tx.txins);
should.exist(tx.txoutsvi);
should.exist(tx.txouts);
should.exist(tx.nlocktime);
});
});
describe('#fromJSON', function() {
it('should set all the basic parameters', function() {
var tx = Transaction().fromJSON({
version: 0,
txinsvi: Varint(1).toJSON(),
txins: [txin.toJSON()],
txoutsvi: Varint(1).toJSON(),
txouts: [txout.toJSON()],
nlocktime: 0
});
should.exist(tx.version);
should.exist(tx.txinsvi);
should.exist(tx.txins);
should.exist(tx.txoutsvi);
should.exist(tx.txouts);
should.exist(tx.nlocktime);
});
});
describe('#toJSON', function() {
it('should recover all the basic parameters', function() {
var json = tx.toJSON();
should.exist(json.version);
should.exist(json.txinsvi);
should.exist(json.txins);
should.exist(json.txoutsvi);
should.exist(json.txouts);
should.exist(json.nlocktime);
});
});
describe('#fromBuffer', function() {
it('should recover from this known tx', function() {
Transaction().fromBuffer(txbuf).toBuffer().toString('hex').should.equal(txhex);
});
it('should recover from this known tx from the blockchain', function() {
Transaction().fromBuffer(tx2buf).toBuffer().toString('hex').should.equal(tx2hex);
});
});
describe('#fromBufferReader', function() {
it('should recover from this known tx', function() {
Transaction().fromBufferReader(BufferReader(txbuf)).toBuffer().toString('hex').should.equal(txhex);
});
});
describe('#toBuffer', function() {
it('should produce this known tx', function() {
Transaction().fromBuffer(txbuf).toBuffer().toString('hex').should.equal(txhex);
});
});
describe('#toBufferWriter', function() {
it('should produce this known tx', function() {
Transaction().fromBuffer(txbuf).toBufferWriter().concat().toString('hex').should.equal(txhex);
});
});
describe('#hash', function() {
it('should correctly calculate the hash of this known transaction', function() {
var tx = Transaction().fromBuffer(tx2buf);
var txhashbuf = new Buffer(Array.apply([], new Buffer(tx2idhex, 'hex')).reverse());
tx.hash().toString('hex').should.equal(txhashbuf.toString('hex'));
});
});
describe('#id', function() {
it('should correctly calculate the id of this known transaction', function() {
var tx = Transaction().fromBuffer(tx2buf);
tx.id().toString('hex').should.equal(tx2idhex);
});
});
describe('#pushin', function() {
it('should add an input', function() {
var txin = Txin();
var tx = Transaction();
tx.pushin(txin);
tx.txinsvi.toNumber().should.equal(1);
tx.txins.length.should.equal(1);
});
});
describe('#pushout', function() {
it('should add an output', function() {
var txout = Txout();
var tx = Transaction();
tx.pushout(txout);
tx.txoutsvi.toNumber().should.equal(1);
tx.txouts.length.should.equal(1);
});
});
});

124
test/txin.js

@ -0,0 +1,124 @@
var should = require('chai').should();
var Script = require('../lib/script');
var Txin = require('../lib/txin');
var Varint = require('../lib/varint');
var BufferReader = require('../lib/bufferreader');
describe('Txin', function() {
var txidbuf = new Buffer(32);
txidbuf.fill(0);
var txoutnum = 0;
var script = Script().fromString("OP_CHECKMULTISIG");
var scriptvi = Varint(script.toBuffer().length);
var seqnum = 0;
var txin = Txin().set({
txidbuf: txidbuf,
txoutnum: txoutnum,
scriptvi: scriptvi,
script: script,
seqnum: seqnum
});
it('should make a new txin', function() {
var txin = new Txin();
should.exist(txin);
txin = Txin();
should.exist(txin);
var txidbuf = new Buffer(32);
txidbuf.fill(0);
Txin(txidbuf).txidbuf.length.should.equal(32);
(function() {
var txidbuf2 = new Buffer(33);
txidbuf2.fill(0);
Txin(txidbuf2);
}).should.throw('txidbuf must be 32 bytes');
});
describe('#set', function() {
it('should set these vars', function() {
var txin = Txin().set({
txidbuf: txidbuf,
txoutnum: txoutnum,
scriptvi: scriptvi,
script: script,
seqnum: seqnum
});
should.exist(txin.txidbuf);
should.exist(txin.txoutnum);
should.exist(txin.scriptvi);
should.exist(txin.script);
should.exist(txin.seqnum);
});
});
describe('#fromJSON', function() {
it('should set these vars', function() {
var txin2 = Txin().fromJSON(txin.toJSON());
should.exist(txin2.txidbuf);
should.exist(txin2.txoutnum);
should.exist(txin2.scriptvi);
should.exist(txin2.script);
should.exist(txin2.seqnum);
});
});
describe('#toJSON', function() {
it('should set these vars', function() {
var json = txin.toJSON()
should.exist(json.txidbuf);
should.exist(json.txoutnum);
should.exist(json.scriptvi);
should.exist(json.script);
should.exist(json.seqnum);
});
});
describe('#fromBuffer', function() {
it('should convert this known buffer', function() {
var hex = '00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000';
var buf = new Buffer(hex, 'hex');
var txin = Txin().fromBuffer(buf);
txin.scriptvi.toNumber().should.equal(1);
txin.script.toString().should.equal('OP_CHECKMULTISIG');
});
});
describe('#fromBufferReader', function() {
it('should convert this known buffer', function() {
var hex = '00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000';
var buf = new Buffer(hex, 'hex');
var br = BufferReader(buf);
var txin = Txin().fromBufferReader(br);
txin.scriptvi.toNumber().should.equal(1);
txin.script.toString().should.equal('OP_CHECKMULTISIG');
});
});
describe('#toBuffer', function() {
it('should convert this known buffer', function() {
txin.toBuffer().toString('hex').should.equal('00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000');
});
});
describe('#toBufferWriter', function() {
it('should convert this known buffer', function() {
txin.toBufferWriter().concat().toString('hex').should.equal('00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000');
});
});
});

110
test/txout.js

@ -0,0 +1,110 @@
var should = require('chai').should();
var BN = require('../lib/bn');
var Txout = require('../lib/txout');
var Script = require('../lib/script');
var Varint = require('../lib/varint');
var BufferReader = require('../lib/bufferreader');
var BufferWriter = require('../lib/bufferwriter');
describe('Txout', function() {
var valuebn = BN(5);
var script = Script().fromString("OP_CHECKMULTISIG");
var scriptvi = Varint(script.toBuffer().length);
var txout = new Txout().set({
valuebn: valuebn,
scriptvi: scriptvi,
script: script
});
it('should make a new txout', function() {
var txout = new Txout();
should.exist(txout);
txout = Txout();
should.exist(txout);
Txout(valuebn, scriptvi, script).valuebn.toString().should.equal('5');
});
describe('#set', function() {
it('should set this object', function() {
var txout = new Txout().set({
valuebn: valuebn,
scriptvi: scriptvi,
script: script
});
should.exist(txout.valuebn);
should.exist(txout.scriptvi);
should.exist(txout.script);
});
});
describe('#fromJSON', function() {
it('should set from this json', function() {
var txout = Txout().fromJSON({
valuebn: valuebn.toJSON(),
scriptvi: scriptvi.toJSON(),
script: script.toJSON()
});
should.exist(txout.valuebn);
should.exist(txout.scriptvi);
should.exist(txout.script);
});
});
describe('#toJSON', function() {
it('should return this json', function() {
var txout = Txout().fromJSON({
valuebn: valuebn.toJSON(),
scriptvi: scriptvi.toJSON(),
script: script.toJSON()
});
var json = txout.toJSON();
should.exist(json.valuebn);
should.exist(json.scriptvi);
should.exist(json.script);
});
});
describe('#fromBuffer', function() {
it('should make this txin from this known buffer', function() {
var txout = Txout().fromBuffer(new Buffer('050000000000000001ae', 'hex'));
txout.toBuffer().toString('hex').should.equal('050000000000000001ae');
});
});
describe('#fromBufferReader', function() {
it('should make this txin from this known buffer', function() {
var txout = Txout().fromBufferReader(BufferReader(new Buffer('050000000000000001ae', 'hex')));
txout.toBuffer().toString('hex').should.equal('050000000000000001ae');
});
});
describe('#toBuffer', function() {
it('should output this known buffer', function() {
var txout = Txout().fromBufferReader(BufferReader(new Buffer('050000000000000001ae', 'hex')));
txout.toBuffer().toString('hex').should.equal('050000000000000001ae');
});
});
describe('#toBufferWriter', function() {
it('should output this known buffer', function() {
var txout = Txout().fromBufferReader(BufferReader(new Buffer('050000000000000001ae', 'hex')));
txout.toBufferWriter().concat().toString('hex').should.equal('050000000000000001ae');
});
});
});

123
test/varint.js

@ -0,0 +1,123 @@
var BN = require('../lib/bn');
var should = require('chai').should();
var BufferReader = require('../lib/bufferreader');
var BufferWriter = require('../lib/bufferwriter');
var Varint = require('../lib/varint');
describe('Varint', function() {
it('should make a new varint', function() {
var buf = new Buffer('00', 'hex');
var varint = new Varint(buf);
should.exist(varint);
varint.buf.toString('hex').should.equal('00');
varint = Varint(buf);
should.exist(varint);
varint.buf.toString('hex').should.equal('00');
//various ways to use the constructor
Varint(Varint(0).toBuffer()).toNumber().should.equal(0);
Varint(0).toNumber().should.equal(0);
Varint(BN(0)).toNumber().should.equal(0);
});
describe('#set', function() {
it('should set a buffer', function() {
var buf = new Buffer('00', 'hex');
var varint = Varint().set({buf: buf});
varint.buf.toString('hex').should.equal('00');
varint.set({});
varint.buf.toString('hex').should.equal('00');
});
});
describe('#fromJSON', function() {
it('should set a buffer', function() {
var buf = BufferWriter().writeVarintNum(5).concat();
var varint = Varint().fromJSON(buf.toString('hex'));
varint.toNumber().should.equal(5);
});
});
describe('#toJSON', function() {
it('should return a buffer', function() {
var buf = BufferWriter().writeVarintNum(5).concat();
var varint = Varint().fromJSON(buf.toString('hex'));
varint.toJSON().should.equal('05');
});
});
describe('#fromBuffer', function() {
it('should set a buffer', function() {
var buf = BufferWriter().writeVarintNum(5).concat();
var varint = Varint().fromBuffer(buf);
varint.toNumber().should.equal(5);
});
});
describe('#fromBufferReader', function() {
it('should set a buffer reader', function() {
var buf = BufferWriter().writeVarintNum(5).concat();
var br = BufferReader(buf);
var varint = Varint().fromBufferReader(br);
varint.toNumber().should.equal(5);
});
});
describe('#fromBN', function() {
it('should set a number', function() {
var varint = Varint().fromBN(BN(5));
varint.toNumber().should.equal(5);
});
});
describe('#fromNumber', function() {
it('should set a number', function() {
var varint = Varint().fromNumber(5);
varint.toNumber().should.equal(5);
});
});
describe('#toBuffer', function() {
it('should return a buffer', function() {
buf = BufferWriter().writeVarintNum(5).concat();
var varint = Varint(buf);
varint.toBuffer().toString('hex').should.equal(buf.toString('hex'));
});
});
describe('#toBN', function() {
it('should return a buffer', function() {
var varint = Varint(5);
varint.toBN().toString().should.equal(BN(5).toString());
});
});
describe('#toNumber', function() {
it('should return a buffer', function() {
var varint = Varint(5);
varint.toNumber().should.equal(5);
});
});
});
Loading…
Cancel
Save