Compare commits

...

26 Commits

Author SHA1 Message Date
Simon e97ae1dd2e Add fix for v4 joinsplits using Groth16 proof.
Closes https://github.com/zcash/zcash/issues/3636 where a tx with
multiple joinsplits would not be parsed correctly.
2018-10-28 14:48:10 -07:00
Simon c3bbbc3f08 Fix incorrect reporting of block difficulty.
The nBits value for mainnet genesis was being used instead of testnet.
2018-10-18 14:26:31 -07:00
Simon 4c6a7df8be Update package.json 2018-09-18 09:43:08 -07:00
Simon 02e607b770 Add Sapling transaction support. 2018-09-17 22:42:37 -07:00
Simon 76a1efebbf Update url to zcash-hackworks. 2018-05-09 14:26:05 -07:00
Simon 75c168836d Update contributors. 2018-05-07 23:31:33 -07:00
Simon 542b3f5893 Add support for Overwinter transactions. 2018-05-07 23:28:04 -07:00
Ian Munoz 97f7b5d3e3 added fOverwintered flag, nVersionGroupId and nExpiryHeight to transaction 2018-05-01 08:34:37 -06:00
str4d 208069bee8 Merge pull request #1 from runn1ng/patch-1
Correcting addresses for multibyte versions
2016-12-07 11:38:56 +13:00
Karel Bílek aeaa33f237 Correcting addresses for multibyte versions
Fixes https://github.com/str4d/insight-ui-zcash/issues/1
2016-11-02 19:03:23 +01:00
Jack Grigg b84f7ac683
Update network magics and genesis difficulty for v1.0.0 2016-10-28 08:55:21 -07:00
Jack Grigg 9e0ec8cc89
Update network details for v1.0.0-rc4 2016-10-27 18:59:27 -07:00
Jack Grigg 387541af3d
Update network magics and genesis block difficulty for v1.0.0-rc2 2016-10-23 00:02:31 -05:00
Jack Grigg ebb8b5683a
Update network details and address prefixes for release 1.0.0-beta2 2016-10-09 10:04:56 -05:00
Jack Grigg 1a93179609 Update network details for release 1.0.0-beta1 2016-09-10 15:43:03 +12:00
Jack Grigg 8f3a99b1be Remove versionGuard error while we are not versioning 2016-08-30 17:51:09 +12:00
Jack Grigg d7d6682eab Update network magics and genesis block difficulty to z9 2016-08-29 21:28:14 +12:00
Jack Grigg 73223235d5 Add Zcash payment address and spending key prefixes 2016-08-29 21:28:06 +12:00
Jack Grigg 53366f34fe Change RPC ports 2016-08-28 18:25:25 +12:00
Jack Grigg 80f5b45034 Implement parsing of transactions with JoinSplits (version 2) 2016-08-28 13:57:45 +12:00
Jack Grigg 8740d9e964 Implement Zcash modifications to block header 2016-08-27 23:47:16 +12:00
Jack Grigg bc8e5f4121 Zcash-ify 2016-08-27 23:22:04 +12:00
Gabe Gattis 764aa6d4e9
Merge branch 'gpg'
PR #85
2016-08-17 17:22:50 -04:00
Braydon Fuller 24df178a08
Add pgp key for @matiu 2016-08-17 17:14:48 -04:00
Braydon Fuller cdb538b94e
Update gpg keys 2016-08-17 17:01:11 -04:00
Braydon Fuller f6b0b14ac6 Bump package version to 0.13.19 2016-08-10 13:29:13 -04:00
16 changed files with 807 additions and 61 deletions

View File

@ -66,8 +66,11 @@ This will generate files named `bitcore-lib.js` and `bitcore-lib.min.js`.
You can also use our pre-generated files, provided for each release along with a PGP signature by one of the project's maintainers. To get them, checkout a release commit (for example, https://github.com/bitpay/bitcore-lib/commit/e33b6e3ba6a1e5830a079e02d949fce69ea33546 for v0.12.6).
To verify signatures, use the following PGP keys:
- @braydonf: https://pgp.mit.edu/pks/lookup?op=get&search=0x9BBF07CAC07A276D
- @pnagurny: https://pgp.mit.edu/pks/lookup?op=get&search=0x0909B33F0AA53013
- @braydonf: https://pgp.mit.edu/pks/lookup?op=get&search=0x9BBF07CAC07A276D `D909 EFE6 70B5 F6CC 89A3 607A 9BBF 07CA C07A 276D`
- @gabegattis: https://pgp.mit.edu/pks/lookup?op=get&search=0x441430987182732C `F3EA 8E28 29B4 EC93 88CB B0AA 4414 3098 7182 732C`
- @kleetus: https://pgp.mit.edu/pks/lookup?op=get&search=0x33195D27EF6BDB7F `F8B0 891C C459 C197 65C2 5043 3319 5D27 EF6B DB7F`
- @matiu: https://pgp.mit.edu/pks/lookup?op=get&search=0x9EDE6DE4DE531FAC `25CE ED88 A1B1 0CD1 12CD 4121 9EDE 6DE4 DE53 1FAC`
## Development & Tests

View File

@ -1,17 +1,18 @@
{
"name": "bitcore-lib",
"main": "./bitcore-lib.min.js",
"version": "0.13.18",
"name": "bitcore-lib-zcash",
"main": "./bitcore-lib-zcash.min.js",
"version": "0.13.19",
"homepage": "http://bitcore.io",
"authors": [
"BitPay, Inc."
"BitPay, Inc.",
"Jack Grigg"
],
"description": "A pure, powerful core for your bitcoin project.",
"description": "A pure, powerful core for your zcash project.",
"moduleType": [
"globals"
],
"keywords": [
"bitcoin",
"zcash",
"bitcore",
"btc",
"satoshi"

View File

@ -1,5 +1,5 @@
var bitcoreTasks = require('bitcore-build');
var bitcoreTasks = require('bitcore-build-zcash');
bitcoreTasks('lib');

View File

@ -6,10 +6,11 @@ var bitcore = module.exports;
bitcore.version = 'v' + require('./package.json').version;
bitcore.versionGuard = function(version) {
if (version !== undefined) {
var message = 'More than one instance of bitcore-lib found. ' +
'Please make sure to require bitcore-lib and check that submodules do' +
' not also include their own bitcore-lib dependency.';
throw new Error(message);
var message = 'More than one instance of bitcore-lib-zcash found. ' +
'Please make sure to require bitcore-lib-zcash and check that submodules do' +
' not also include their own bitcore-lib-zcash dependency.';
// TODO: put this back if we start versioning again
//throw new Error(message);
}
};
bitcore.versionGuard(global._bitcore);

View File

@ -162,8 +162,9 @@ Address._transformObject = function(data) {
Address._classifyFromVersion = function(buffer) {
var version = {};
var pubkeyhashNetwork = Networks.get(buffer[0], 'pubkeyhash');
var scripthashNetwork = Networks.get(buffer[0], 'scripthash');
var prefix = buffer[0]*256 + buffer[1];
var pubkeyhashNetwork = Networks.get(prefix, 'pubkeyhash');
var scripthashNetwork = Networks.get(prefix, 'scripthash');
if (pubkeyhashNetwork) {
version.network = pubkeyhashNetwork;
@ -191,8 +192,8 @@ Address._transformBuffer = function(buffer, network, type) {
if (!(buffer instanceof Buffer) && !(buffer instanceof Uint8Array)) {
throw new TypeError('Address supplied is not a buffer.');
}
if (buffer.length !== 1 + 20) {
throw new TypeError('Address buffers must be exactly 21 bytes.');
if (buffer.length !== 2 + 20) {
throw new TypeError('Address buffers must be exactly 22 bytes.');
}
network = Networks.get(network);
@ -206,7 +207,7 @@ Address._transformBuffer = function(buffer, network, type) {
throw new TypeError('Address has mismatched type.');
}
info.hashBuffer = buffer.slice(1);
info.hashBuffer = buffer.slice(2);
info.network = bufferVersion.network;
info.type = bufferVersion.type;
return info;
@ -459,7 +460,8 @@ Address.prototype.isPayToScriptHash = function() {
* @returns {Buffer} Bitcoin address buffer
*/
Address.prototype.toBuffer = function() {
var version = new Buffer([this.network[this.type]]);
var version = new Buffer(2);
version.writeUInt16BE(this.network[this.type], 0);
var buf = Buffer.concat([version, this.hashBuffer]);
return buf;
};

View File

@ -9,7 +9,8 @@ var Hash = require('../crypto/hash');
var JSUtil = require('../util/js');
var $ = require('../util/preconditions');
var GENESIS_BITS = 0x1d00ffff;
// Mainnet 0x1f07ffff, Testnet 0x2007ffff
var GENESIS_BITS = 0x2007ffff;
/**
* Instantiate a BlockHeader from a Buffer, JSON object, or Object with
@ -27,10 +28,12 @@ var BlockHeader = function BlockHeader(arg) {
this.version = info.version;
this.prevHash = info.prevHash;
this.merkleRoot = info.merkleRoot;
this.reserved = info.reserved;
this.time = info.time;
this.timestamp = info.time;
this.bits = info.bits;
this.nonce = info.nonce;
this.solution = info.solution;
if (info.hash) {
$.checkState(
@ -69,21 +72,35 @@ BlockHeader._fromObject = function _fromObject(data) {
$.checkArgument(data, 'data is required');
var prevHash = data.prevHash;
var merkleRoot = data.merkleRoot;
var reserved = data.reserved;
var nonce = data.nonce;
var solution = data.solution;
if (_.isString(data.prevHash)) {
prevHash = BufferUtil.reverse(new Buffer(data.prevHash, 'hex'));
}
if (_.isString(data.merkleRoot)) {
merkleRoot = BufferUtil.reverse(new Buffer(data.merkleRoot, 'hex'));
}
if (_.isString(data.reserved)) {
reserved = BufferUtil.reverse(new Buffer(data.reserved, 'hex'));
}
if (_.isString(data.nonce)) {
nonce = BufferUtil.reverse(new Buffer(data.nonce, 'hex'));
}
if (_.isString(data.solution)) {
solution = new Buffer(data.solution, 'hex');
}
var info = {
hash: data.hash,
version: data.version,
prevHash: prevHash,
merkleRoot: merkleRoot,
reserved: reserved,
time: data.time,
timestamp: data.time,
bits: data.bits,
nonce: data.nonce
nonce: nonce,
solution: solution
};
return info;
};
@ -139,9 +156,12 @@ BlockHeader._fromBufferReader = function _fromBufferReader(br) {
info.version = br.readUInt32LE();
info.prevHash = br.read(32);
info.merkleRoot = br.read(32);
info.reserved = br.read(32);
info.time = br.readUInt32LE();
info.bits = br.readUInt32LE();
info.nonce = br.readUInt32LE();
info.nonce = br.read(32);
var lenSolution = br.readVarintNum();
info.solution = br.read(lenSolution);
return info;
};
@ -163,9 +183,11 @@ BlockHeader.prototype.toObject = BlockHeader.prototype.toJSON = function toObjec
version: this.version,
prevHash: BufferUtil.reverse(this.prevHash).toString('hex'),
merkleRoot: BufferUtil.reverse(this.merkleRoot).toString('hex'),
reserved: BufferUtil.reverse(this.reserved).toString('hex'),
time: this.time,
bits: this.bits,
nonce: this.nonce
nonce: BufferUtil.reverse(this.nonce).toString('hex'),
solution: this.solution.toString('hex')
};
};
@ -194,9 +216,12 @@ BlockHeader.prototype.toBufferWriter = function toBufferWriter(bw) {
bw.writeUInt32LE(this.version);
bw.write(this.prevHash);
bw.write(this.merkleRoot);
bw.write(this.reserved);
bw.writeUInt32LE(this.time);
bw.writeUInt32LE(this.bits);
bw.writeUInt32LE(this.nonce);
bw.write(this.nonce);
bw.writeVarintNum(this.solution.length);
bw.write(this.solution);
return bw;
};

View File

@ -59,6 +59,8 @@ function get(arg, keys) {
* @param {Number} data.scripthash - The scripthash prefix
* @param {Number} data.xpubkey - The extended public key magic
* @param {Number} data.xprivkey - The extended private key magic
* @param {Number} data.zaddr - The Zcash payment address prefix
* @param {Number} data.zkey - The Zcash spending key prefix
* @param {Number} data.networkMagic - The network magic number
* @param {Number} data.port - The network port
* @param {Array} data.dnsSeeds - An array of dns seeds
@ -75,7 +77,9 @@ function addNetwork(data) {
privatekey: data.privatekey,
scripthash: data.scripthash,
xpubkey: data.xpubkey,
xprivkey: data.xprivkey
xprivkey: data.xprivkey,
zaddr: data.zaddr,
zkey: data.zkey
});
if (data.networkMagic) {
@ -129,20 +133,19 @@ function removeNetwork(network) {
addNetwork({
name: 'livenet',
alias: 'mainnet',
pubkeyhash: 0x00,
pubkeyhash: 0x1cb8,
privatekey: 0x80,
scripthash: 0x05,
scripthash: 0x1cbd,
xpubkey: 0x0488b21e,
xprivkey: 0x0488ade4,
networkMagic: 0xf9beb4d9,
port: 8333,
zaddr: 0x169a,
zkey: 0xab36,
networkMagic: 0x24e92764,
port: 8233,
dnsSeeds: [
'seed.bitcoin.sipa.be',
'dnsseed.bluematt.me',
'dnsseed.bitcoin.dashjr.org',
'seed.bitcoinstats.com',
'seed.bitnodes.io',
'bitseed.xf2.org'
'dnsseed.z.cash',
'dnsseed.str4d.xyz',
'dnsseed.znodes.org'
]
});
@ -155,11 +158,13 @@ var livenet = get('livenet');
addNetwork({
name: 'testnet',
alias: 'regtest',
pubkeyhash: 0x6f,
pubkeyhash: 0x1d25,
privatekey: 0xef,
scripthash: 0xc4,
scripthash: 0x1cba,
xpubkey: 0x043587cf,
xprivkey: 0x04358394
xprivkey: 0x04358394,
zaddr: 0x16b6,
zkey: 0xac08,
});
/**
@ -171,13 +176,10 @@ var testnet = get('testnet');
// Add configurable values for testnet/regtest
var TESTNET = {
PORT: 18333,
NETWORK_MAGIC: BufferUtil.integerAsBuffer(0x0b110907),
PORT: 18233,
NETWORK_MAGIC: BufferUtil.integerAsBuffer(0xfa1af9bf),
DNS_SEEDS: [
'testnet-seed.bitcoin.petertodd.org',
'testnet-seed.bluematt.me',
'testnet-seed.alexykot.me',
'testnet-seed.bitcoin.schildbach.de'
'dnsseed.testnet.z.cash',
]
};
@ -189,7 +191,7 @@ for (var key in TESTNET) {
var REGTEST = {
PORT: 18444,
NETWORK_MAGIC: BufferUtil.integerAsBuffer(0xfabfb5da),
NETWORK_MAGIC: BufferUtil.integerAsBuffer(0xaae83f5f),
DNS_SEEDS: []
};

View File

@ -0,0 +1,218 @@
'use strict';
var _ = require('lodash');
var $ = require('../util/preconditions');
var BN = require('../crypto/bn');
var buffer = require('buffer');
var BufferWriter = require('../encoding/bufferwriter');
var BufferUtil = require('../util/buffer');
var JSUtil = require('../util/js');
// TODO: Update ZCProof for Groth
//var ZCProof = require('../zcash/proof');
var ZC_NUM_JS_INPUTS = 2;
var ZC_NUM_JS_OUTPUTS = 2;
// leading + v + rho + r + memo + auth
var ZC_NOTECIPHERTEXT_SIZE = 1 + 8 + 32 + 32 + 512 + 16;
function JSDescription(params) {
if (!(this instanceof JSDescription)) {
return new JSDescription(params);
}
this.nullifiers = [];
this.commitments = [];
this.ciphertexts = [];
this.macs = [];
if (params) {
return this._fromObject(params);
}
}
Object.defineProperty(JSDescription.prototype, 'vpub_old', {
configurable: false,
enumerable: true,
get: function() {
return this._vpub_old;
},
set: function(num) {
if (num instanceof BN) {
this._vpub_oldBN = num;
this._vpub_old = num.toNumber();
} else if (_.isString(num)) {
this._vpub_old = parseInt(num);
this._vpub_oldBN = BN.fromNumber(this._vpub_old);
} else {
$.checkArgument(
JSUtil.isNaturalNumber(num),
'vpub_old is not a natural number'
);
this._vpub_oldBN = BN.fromNumber(num);
this._vpub_old = num;
}
$.checkState(
JSUtil.isNaturalNumber(this._vpub_old),
'vpub_old is not a natural number'
);
}
});
Object.defineProperty(JSDescription.prototype, 'vpub_new', {
configurable: false,
enumerable: true,
get: function() {
return this._vpub_new;
},
set: function(num) {
if (num instanceof BN) {
this._vpub_newBN = num;
this._vpub_new = num.toNumber();
} else if (_.isString(num)) {
this._vpub_new = parseInt(num);
this._vpub_newBN = BN.fromNumber(this._vpub_new);
} else {
$.checkArgument(
JSUtil.isNaturalNumber(num),
'vpub_new is not a natural number'
);
this._vpub_newBN = BN.fromNumber(num);
this._vpub_new = num;
}
$.checkState(
JSUtil.isNaturalNumber(this._vpub_new),
'vpub_new is not a natural number'
);
}
});
JSDescription.fromObject = function(obj) {
$.checkArgument(_.isObject(obj));
var jsdesc = new JSDescription();
return jsdesc._fromObject(obj);
};
JSDescription.prototype._fromObject = function(params) {
var nullifiers = [];
_.each(params.nullifiers, function(nullifier) {
nullifiers.push(BufferUtil.reverse(new buffer.Buffer(nullifier, 'hex')));
});
var commitments = [];
_.each(params.commitments, function(commitment) {
commitments.push(BufferUtil.reverse(new buffer.Buffer(commitment, 'hex')));
});
var ciphertexts = [];
_.each(params.ciphertexts, function(ciphertext) {
ciphertexts.push(new buffer.Buffer(ciphertext, 'hex'));
});
var macs = [];
_.each(params.macs, function(mac) {
macs.push(BufferUtil.reverse(new buffer.Buffer(mac, 'hex')));
});
this.vpub_old = params.vpub_old;
this.vpub_new = params.vpub_new;
this.anchor = BufferUtil.reverse(new buffer.Buffer(params.anchor, 'hex'));
this.nullifiers = nullifiers;
this.commitments = commitments;
this.ephemeralKey = BufferUtil.reverse(new buffer.Buffer(params.ephemeralKey, 'hex'));
this.ciphertexts = ciphertexts;
this.randomSeed = BufferUtil.reverse(new buffer.Buffer(params.randomSeed, 'hex'));
this.macs = macs;
this.proof = params.proof; // TODO: Update ZCProof for Groth: ZCProof.fromObject(params.proof);
return this;
};
JSDescription.prototype.toObject = JSDescription.prototype.toJSON = function toObject() {
var nullifiers = [];
_.each(this.nullifiers, function(nullifier) {
nullifiers.push(BufferUtil.reverse(nullifier).toString('hex'));
});
var commitments = [];
_.each(this.commitments, function(commitment) {
commitments.push(BufferUtil.reverse(commitment).toString('hex'));
});
var ciphertexts = [];
_.each(this.ciphertexts, function(ciphertext) {
ciphertexts.push(ciphertext.toString('hex'));
});
var macs = [];
_.each(this.macs, function(mac) {
macs.push(BufferUtil.reverse(mac).toString('hex'));
});
var obj = {
vpub_old: this.vpub_old,
vpub_new: this.vpub_new,
anchor: BufferUtil.reverse(this.anchor).toString('hex'),
nullifiers: nullifiers,
commitments: commitments,
ephemeralKey: BufferUtil.reverse(this.ephemeralKey).toString('hex'),
ciphertexts: ciphertexts,
randomSeed: BufferUtil.reverse(this.randomSeed).toString('hex'),
macs: macs,
proof: this.proof, // TODO: Update ZCProof for Groth: this.proof.toObject(),
};
return obj;
};
JSDescription.fromBufferReader = function(br, useGrothFlagParam) {
var i;
var jsdesc = new JSDescription();
jsdesc.vpub_old = br.readUInt64LEBN();
jsdesc.vpub_new = br.readUInt64LEBN();
jsdesc.anchor = br.read(32);
for (i = 0; i < ZC_NUM_JS_INPUTS; i++) {
jsdesc.nullifiers.push(br.read(32));
}
for (i = 0; i < ZC_NUM_JS_OUTPUTS; i++) {
jsdesc.commitments.push(br.read(32));
}
jsdesc.ephemeralKey = br.read(32);
jsdesc.randomSeed = br.read(32);
for (i = 0; i < ZC_NUM_JS_INPUTS; i++) {
jsdesc.macs.push(br.read(32));
}
// Default parameter requires ECMASCript 6 which might not be available, so use workaround.
var useGrothFlag = useGrothFlagParam || false;
if (!useGrothFlag) {
jsdesc.proof = br.read(296); // TODO: Update ZCProof for Groth: ZCProof.fromBufferReader(br);
} else {
jsdesc.proof = br.read(48 + 96 + 48);
}
for (i = 0; i < ZC_NUM_JS_OUTPUTS; i++) {
jsdesc.ciphertexts.push(br.read(ZC_NOTECIPHERTEXT_SIZE));
}
return jsdesc;
};
JSDescription.prototype.toBufferWriter = function(writer) {
var i;
if (!writer) {
writer = new BufferWriter();
}
writer.writeUInt64LEBN(this._vpub_oldBN);
writer.writeUInt64LEBN(this._vpub_newBN);
writer.write(this.anchor);
for (i = 0; i < ZC_NUM_JS_INPUTS; i++) {
writer.write(this.nullifiers[i]);
}
for (i = 0; i < ZC_NUM_JS_OUTPUTS; i++) {
writer.write(this.commitments[i]);
}
writer.write(this.ephemeralKey);
writer.write(this.randomSeed);
for (i = 0; i < ZC_NUM_JS_INPUTS; i++) {
writer.write(this.macs[i]);
}
// TODO: Update ZCProof for Groth: this.proof.toBufferWriter(writer);
writer.write(this.proof);
for (i = 0; i < ZC_NUM_JS_OUTPUTS; i++) {
writer.write(this.ciphertexts[i]);
}
return writer;
};
module.exports = JSDescription;

View File

@ -0,0 +1,78 @@
'use strict';
var _ = require('lodash');
var $ = require('../util/preconditions');
var BN = require('../crypto/bn');
var buffer = require('buffer');
var BufferWriter = require('../encoding/bufferwriter');
var BufferUtil = require('../util/buffer');
var JSUtil = require('../util/js');
// Sapling note magic values, copied from src/zcash/Zcash.h
var NOTEENCRYPTION_AUTH_BYTES = 16;
var ZC_NOTEPLAINTEXT_LEADING = 1;
var ZC_V_SIZE = 8;
var ZC_RHO_SIZE = 32;
var ZC_R_SIZE = 32;
var ZC_MEMO_SIZE = 512;
var ZC_DIVERSIFIER_SIZE = 11;
var ZC_JUBJUB_POINT_SIZE = 32;
var ZC_JUBJUB_SCALAR_SIZE = 32;
var ZC_NOTEPLAINTEXT_SIZE = ZC_NOTEPLAINTEXT_LEADING + ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE;
var ZC_SAPLING_ENCPLAINTEXT_SIZE = ZC_NOTEPLAINTEXT_LEADING + ZC_DIVERSIFIER_SIZE + ZC_V_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE;
var ZC_SAPLING_OUTPLAINTEXT_SIZE = ZC_JUBJUB_POINT_SIZE + ZC_JUBJUB_SCALAR_SIZE;
var ZC_SAPLING_ENCCIPHERTEXT_SIZE = ZC_SAPLING_ENCPLAINTEXT_SIZE + NOTEENCRYPTION_AUTH_BYTES;
var ZC_SAPLING_OUTCIPHERTEXT_SIZE = ZC_SAPLING_OUTPLAINTEXT_SIZE + NOTEENCRYPTION_AUTH_BYTES;
function OutputDescription(params) {
if (!(this instanceof OutputDescription)) {
return new OutputDescription(params);
}
if (params) {
return this._fromObject(params);
}
}
OutputDescription.fromObject = function(obj) {
$.checkArgument(_.isObject(obj));
var outputdesc = new OutputDescription();
return outputdesc._fromObject(obj);
};
OutputDescription.prototype._fromObject = function(params) {
// TODO: Populate from parameters, but for now it's ok to do nothing.
return this;
};
OutputDescription.prototype.toObject = OutputDescription.prototype.toJSON = function toObject() {
// TODO: Populate JSON object, but for now it's ok to return a placeholder.
var obj = {};
return obj;
};
OutputDescription.fromBufferReader = function(br) {
var obj = new OutputDescription();
obj.cv = br.read(32);
obj.cmu = br.read(32);
obj.ephemeralKey = br.read(32);
obj.encCipherText = br.read(ZC_SAPLING_ENCCIPHERTEXT_SIZE);
obj.outCipherText = br.read(ZC_SAPLING_OUTCIPHERTEXT_SIZE);
obj.proof = br.read(48 + 96 + 48);
return obj;
};
OutputDescription.prototype.toBufferWriter = function(writer) {
var i;
if (!writer) {
writer = new BufferWriter();
}
writer.write(this.cv);
writer.write(this.cmu);
writer.write(this.ephemeralKey);
writer.write(this.encCipherText);
writer.write(this.outCipherText);
writer.write(this.proof);
return writer;
};
module.exports = OutputDescription;

View File

@ -0,0 +1,62 @@
'use strict';
var _ = require('lodash');
var $ = require('../util/preconditions');
var BN = require('../crypto/bn');
var buffer = require('buffer');
var BufferWriter = require('../encoding/bufferwriter');
var BufferUtil = require('../util/buffer');
var JSUtil = require('../util/js');
function SpendDescription(params) {
if (!(this instanceof SpendDescription)) {
return new SpendDescription(params);
}
if (params) {
return this._fromObject(params);
}
}
SpendDescription.fromObject = function(obj) {
$.checkArgument(_.isObject(obj));
var spenddesc = new SpendDescription();
return spenddesc._fromObject(obj);
};
SpendDescription.prototype._fromObject = function(params) {
// TODO: Populate from parameters, but for now it's ok to do nothing.
return this;
};
SpendDescription.prototype.toObject = SpendDescription.prototype.toJSON = function toObject() {
// TODO: Populate JSON object, but for now it's ok to return a placeholder.
var obj = {};
return obj;
};
SpendDescription.fromBufferReader = function(br) {
var obj = new SpendDescription();
obj.cv = br.read(32);
obj.anchor = br.read(32);
obj.nullifier = br.read(32);
obj.rk = br.read(32);
obj.proof = br.read(48 + 96 + 48);
obj.spendAuthSig = br.read(64);
return obj;
};
SpendDescription.prototype.toBufferWriter = function(writer) {
var i;
if (!writer) {
writer = new BufferWriter();
}
writer.write(this.cv);
writer.write(this.anchor);
writer.write(this.nullifier);
writer.write(this.rk);
writer.write(this.proof);
writer.write(this.spendAuthSig);
return writer;
};
module.exports = SpendDescription;

View File

@ -26,6 +26,10 @@ var Script = require('../script');
var PrivateKey = require('../privatekey');
var BN = require('../crypto/bn');
var JSDescription = require('./jsdescription');
var SpendDescription = require('./spenddescription');
var OutputDescription = require('./outputdescription');
/**
* Represents a transaction, a set of inputs and outputs to change ownership of tokens
*
@ -38,6 +42,11 @@ function Transaction(serialized) {
}
this.inputs = [];
this.outputs = [];
this.joinSplits = [];
this.spendDescs = [];
this.outputDescs = [];
this._inputAmount = undefined;
this._outputAmount = undefined;
@ -279,7 +288,19 @@ Transaction.prototype.toBuffer = function() {
};
Transaction.prototype.toBufferWriter = function(writer) {
writer.writeUInt32LE(this.version);
if (!this.fOverwintered) {
writer.writeUInt32LE(this.version);
} else {
// We don't use bitwise operators which expect 32 bit operands and return a 32 bit signed integer.
// For example, var header = 0x80000000 | this.version; returns -7fffffff (-2147483645).
var header = 0x80000000 + this.version;
writer.writeUInt32LE(header);
}
if (this.fOverwintered) {
writer.writeUInt32LE(this.nVersionGroupId);
}
writer.writeVarintNum(this.inputs.length);
_.each(this.inputs, function(input) {
input.toBufferWriter(writer);
@ -289,6 +310,38 @@ Transaction.prototype.toBufferWriter = function(writer) {
output.toBufferWriter(writer);
});
writer.writeUInt32LE(this.nLockTime);
if (this.fOverwintered) {
writer.writeUInt32LE(this.nExpiryHeight);
}
if (this.version >= 4) {
writer.writeUInt64LEBN(this.valueBalance);
writer.writeVarintNum(this.spendDescs.length);
_.each(this.spendDescs, function(desc) {
desc.toBufferWriter(writer);
});
writer.writeVarintNum(this.outputDescs.length);
_.each(this.outputDescs, function(desc) {
desc.toBufferWriter(writer);
});
}
if (this.version >= 2) {
writer.writeVarintNum(this.joinSplits.length);
_.each(this.joinSplits, function(jsdesc) {
jsdesc.toBufferWriter(writer);
});
if (this.joinSplits.length > 0) {
writer.write(this.joinSplitPubKey);
writer.write(this.joinSplitSig);
}
}
if (this.version >= 4 && !(this.spendDescs.length==0 && this.outputDescs.length==0)) {
writer.write(this.bindingSig);
}
return writer;
};
@ -299,9 +352,20 @@ Transaction.prototype.fromBuffer = function(buffer) {
Transaction.prototype.fromBufferReader = function(reader) {
$.checkArgument(!reader.finished(), 'No transaction data received');
var i, sizeTxIns, sizeTxOuts;
var i, sizeTxIns, sizeTxOuts, sizeJSDescs, sizeSpendDescs, sizeOutputDescs;
var header = reader.readUInt32LE();
this.fOverwintered = ((header >>> 31) == 1);
if (this.fOverwintered == true) {
this.version = header & 0x7fffffff;
} else {
this.version = header;
}
if (this.version >= 3 ){
this.nVersionGroupId = reader.readUInt32LE();
}
this.version = reader.readUInt32LE();
sizeTxIns = reader.readVarintNum();
for (i = 0; i < sizeTxIns; i++) {
var input = Input.fromBufferReader(reader);
@ -311,7 +375,43 @@ Transaction.prototype.fromBufferReader = function(reader) {
for (i = 0; i < sizeTxOuts; i++) {
this.outputs.push(Output.fromBufferReader(reader));
}
this.nLockTime = reader.readUInt32LE();
if (this.version >= 3) {
this.nExpiryHeight = reader.readUInt32LE();
}
if (this.version >= 4) {
this.valueBalance = reader.readUInt64LEBN();
sizeSpendDescs = reader.readVarintNum();
for (i = 0; i < sizeSpendDescs; i++) {
var spend = SpendDescription.fromBufferReader(reader);
this.spendDescs.push(spend);
}
sizeOutputDescs = reader.readVarintNum();
for (i = 0; i < sizeOutputDescs; i++) {
var output = OutputDescription.fromBufferReader(reader);
this.outputDescs.push(output);
}
}
var useGrothFlag = (this.version >= 4);
if (this.version >= 2) {
sizeJSDescs = reader.readVarintNum();
for (i = 0; i < sizeJSDescs; i++) {
this.joinSplits.push(JSDescription.fromBufferReader(reader, useGrothFlag));
}
if (sizeJSDescs > 0) {
this.joinSplitPubKey = reader.read(32);
this.joinSplitSig = reader.read(64);
}
}
if (this.version >=4 && !(sizeSpendDescs==0 && sizeOutputDescs==0)) {
this.bindingSig = reader.read(64);
}
return this;
};
@ -326,11 +426,50 @@ Transaction.prototype.toObject = Transaction.prototype.toJSON = function toObjec
});
var obj = {
hash: this.hash,
fOverwintered: this.fOverwintered,
version: this.version,
inputs: inputs,
outputs: outputs,
nLockTime: this.nLockTime
};
if (this.fOverwintered) {
obj.nVersionGroupId = this.nVersionGroupId;
obj.nExpiryHeight = this.nExpiryHeight;
}
if (this.version >= 4) {
obj.valueBalance - this.valueBalance;
var spendDescs = [];
this.spendDescs.forEach(function(desc) {
spendDescs.push(desc.toObject());
});
obj.spendDescs = spendDescs;
var outputDescs = [];
this.outputDescs.forEach(function(desc) {
outputDescs.push(desc.toObject());
});
obj.outputDescs = outputDescs;
}
if (this.version >= 2) {
var joinSplits = [];
this.joinSplits.forEach(function(joinSplit) {
joinSplits.push(joinSplit.toObject());
});
obj.joinSplits = joinSplits;
if (this.joinSplits.length > 0) {
obj.joinSplitPubKey = BufferUtil.reverse(this.joinSplitPubKey).toString('hex');
obj.joinSplitSig = this.joinSplitSig.toString('hex');
}
}
if (this.version >=4 && !(this.spendDescs.length==0 && this.outputDescs.length==0)) {
obj.bindingSig = this.bindingSig.toString('hex');
}
if (this._changeScript) {
obj.changeScript = this._changeScript.toString();
}
@ -387,6 +526,38 @@ Transaction.prototype.fromObject = function fromObject(arg) {
}
this.nLockTime = transaction.nLockTime;
this.version = transaction.version;
this.fOverwintered = transaction.fOverwintered;
if (this.fOverwintered) {
this.nExpiryHeight = transaction.nExpiryHeight;
this.nVersionGroupId = transaction.nVersionGroupId;
}
if (this.version >= 4) {
this.valueBalance = transaction.valueBalance;
_.each(transaction.spendDescs, function(desc) {
self.spendDescs.push(new SpendDescription(desc));
});
_.each(transaction.outputDescs, function(desc) {
self.outputDescs.push(new OutputDescription(desc));
});
}
if (this.version >= 2) {
_.each(transaction.joinSplits, function(joinSplit) {
self.joinSplits.push(new JSDescription(joinSplit));
});
if (self.joinSplits.length > 0) {
self.joinSplitPubKey = BufferUtil.reverse(new Buffer(transaction.joinSplitPubKey, 'hex'));
self.joinSplitSig = new Buffer(transaction.joinSplitSig, 'hex');
}
}
if (this.version >=4 && !(transaction.spendDescs.length==0 && transaction.outputDescs.length==0)) {
this.bindingSig = transaction.bindingSig;
}
this._checkConsistency(arg);
return this;
};

View File

@ -110,8 +110,8 @@ URI.isValid = function(arg, knownParams) {
URI.parse = function(uri) {
var info = URL.parse(uri, true);
if (info.protocol !== 'bitcoin:') {
throw new TypeError('Invalid bitcoin URI');
if (info.protocol !== 'zcash:') {
throw new TypeError('Invalid zcash URI');
}
// workaround to host insensitiveness
@ -135,7 +135,7 @@ URI.prototype._fromObject = function(obj) {
/* jshint maxcomplexity: 10 */
if (!Address.isValid(obj.address)) {
throw new TypeError('Invalid bitcoin address');
throw new TypeError('Invalid zcash address');
}
this.address = new Address(obj.address);
@ -205,7 +205,7 @@ URI.prototype.toString = function() {
_.extend(query, this.extras);
return URL.format({
protocol: 'bitcoin:',
protocol: 'zcash:',
host: this.address,
query: query
});

171
lib/zcash/proof.js Normal file
View File

@ -0,0 +1,171 @@
'use strict';
var $ = require('../util/preconditions');
var buffer = require('buffer');
var BufferWriter = require('../encoding/bufferwriter');
var G1_PREFIX_MASK = 0x02;
var G2_PREFIX_MASK = 0x0a;
function CompressedG1(params) {
if (!(this instanceof CompressedG1)) {
return new CompressedG1(params);
}
if (params) {
return this._fromObject(params);
}
}
CompressedG1.fromObject = function(obj) {
$.checkArgument(_.isObject(obj));
var pt = new CompressedG1();
return pt._fromObject(obj);
};
CompressedG1.prototype._fromObject = function(params) {
this.y_lsb = params.y_lsb;
this.x = new buffer.Buffer(params.x, 'hex');
return this;
};
CompressedG1.prototype.toObject = CompressedG1.prototype.toJSON = function toObject() {
var obj = {
y_lsb: this.y_lsb,
x: this.x.toString('hex'),
};
return obj;
};
CompressedG1.fromBufferReader = function(br) {
var pt = new CompressedG1();
var y_lsb = br.readUInt8();
pt.y_lsb = y_lsb & 1;
pt.x = br.read(32);
return pt;
};
CompressedG1.prototype.toBufferWriter = function(writer) {
if (!writer) {
writer = new BufferWriter();
}
writer.writeUInt8(G1_PREFIX_MASK | this.y_lsb);
writer.write(this.x);
return writer;
};
function CompressedG2(params) {
if (!(this instanceof CompressedG2)) {
return new CompressedG2(params);
}
if (params) {
return this._fromObject(params);
}
}
CompressedG2.fromObject = function(obj) {
$.checkArgument(_.isObject(obj));
var pt = new CompressedG2();
return pt._fromObject(obj);
};
CompressedG2.prototype._fromObject = function(params) {
this.y_gt = params.y_gt;
this.x = new buffer.Buffer(params.x, 'hex');
return this;
};
CompressedG2.prototype.toObject = CompressedG2.prototype.toJSON = function toObject() {
var obj = {
y_gt: this.y_gt,
x: this.x.toString('hex'),
};
return obj;
};
CompressedG2.fromBufferReader = function(br) {
var pt = new CompressedG2();
var y_gt = br.readUInt8();
pt.y_gt = y_gt & 1;
pt.x = br.read(64);
return pt;
};
CompressedG2.prototype.toBufferWriter = function(writer) {
if (!writer) {
writer = new BufferWriter();
}
writer.writeUInt8(G2_PREFIX_MASK | this.y_gt);
writer.write(this.x);
return writer;
};
function ZCProof(params) {
if (!(this instanceof ZCProof)) {
return new ZCProof(params);
}
if (params) {
return this._fromObject(params);
}
}
ZCProof.fromObject = function(obj) {
$.checkArgument(_.isObject(obj));
var proof = new ZCProof();
return proof._fromObject(obj);
};
ZCProof.prototype._fromObject = function(params) {
this.g_A = CompressedG1.fromObject(params.g_A);
this.g_A_prime = CompressedG1.fromObject(params.g_A_prime);
this.g_B = CompressedG2.fromObject(params.g_B);
this.g_B_prime = CompressedG1.fromObject(params.g_B_prime);
this.g_C = CompressedG1.fromObject(params.g_C);
this.g_C_prime = CompressedG1.fromObject(params.g_C_prime);
this.g_K = CompressedG1.fromObject(params.g_K);
this.g_H = CompressedG1.fromObject(params.g_H);
return this;
};
ZCProof.prototype.toObject = ZCProof.prototype.toJSON = function toObject() {
var obj = {
g_A: this.g_A.toObject(),
g_A_prime: this.g_A_prime.toObject(),
g_B: this.g_B.toObject(),
g_B_prime: this.g_B_prime.toObject(),
g_C: this.g_C.toObject(),
g_C_prime: this.g_C_prime.toObject(),
g_K: this.g_K.toObject(),
g_H: this.g_H.toObject(),
};
return obj;
};
ZCProof.fromBufferReader = function(br) {
var proof = new ZCProof();
proof.g_A = CompressedG1.fromBufferReader(br);
proof.g_A_prime = CompressedG1.fromBufferReader(br);
proof.g_B = CompressedG2.fromBufferReader(br);
proof.g_B_prime = CompressedG1.fromBufferReader(br);
proof.g_C = CompressedG1.fromBufferReader(br);
proof.g_C_prime = CompressedG1.fromBufferReader(br);
proof.g_K = CompressedG1.fromBufferReader(br);
proof.g_H = CompressedG1.fromBufferReader(br);
return proof;
};
ZCProof.prototype.toBufferWriter = function(writer) {
if (!writer) {
writer = new BufferWriter();
}
this.g_A.toBufferWriter(writer);
this.g_A_prime.toBufferWriter(writer);
this.g_B.toBufferWriter(writer);
this.g_B_prime.toBufferWriter(writer);
this.g_C.toBufferWriter(writer);
this.g_C_prime.toBufferWriter(writer);
this.g_K.toBufferWriter(writer);
this.g_H.toBufferWriter(writer);
return writer;
};
module.exports = ZCProof;

2
npm-shrinkwrap.json generated
View File

@ -1,6 +1,6 @@
{
"name": "bitcore",
"version": "0.13.18",
"version": "0.13.19",
"dependencies": {
"bn.js": {
"version": "2.0.4",

View File

@ -1,7 +1,7 @@
{
"name": "bitcore-lib",
"version": "0.13.18",
"description": "A pure and powerful JavaScript Bitcoin library.",
"name": "bitcore-lib-zcash",
"version": "0.13.19",
"description": "A pure and powerful JavaScript Zcash library.",
"author": "BitPay <dev@bitpay.com>",
"main": "index.js",
"scripts": {
@ -54,10 +54,22 @@
{
"name": "Wei Lu",
"email": "luwei.here@gmail.com"
},
{
"name": "Jack Grigg",
"email": "jack@z.cash"
},
{
"name": "Simon Liu",
"email": "simon@z.cash"
},
{
"name": "Ian Munoz",
"email": "ian.org@gmail.com"
}
],
"keywords": [
"bitcoin",
"zcash",
"transaction",
"address",
"p2p",
@ -74,7 +86,7 @@
],
"repository": {
"type": "git",
"url": "https://github.com/bitpay/bitcore-lib.git"
"url": "https://github.com/zcash-hackworks/bitcore-lib-zcash.git"
},
"browser": {
"request": "browser-request"
@ -88,7 +100,7 @@
"lodash": "=3.10.1"
},
"devDependencies": {
"bitcore-build": "bitpay/bitcore-build",
"bitcore-build-zcash": "zcash-hackworks/bitcore-build-zcash",
"brfs": "^1.2.0",
"chai": "^1.10.0",
"gulp": "^3.8.10",

View File

@ -4,11 +4,11 @@
<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/bitcore-build/node_modules/mocha/mocha.css" />
<link rel="stylesheet" href="../node_modules/bitcore-build-zcash/node_modules/mocha/mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="../node_modules/bitcore-build/node_modules/mocha/mocha.js"></script>
<script src="../node_modules/bitcore-build-zcash/node_modules/mocha/mocha.js"></script>
<script>mocha.setup('bdd')</script>
<script src="../tests.js"></script>
<script>