Browse Source

Remove bitcore-message dependency.

master
Emilio Almansi 7 years ago
parent
commit
29f09ac5f6
  1. 32
      docs/examples.md
  2. 1
      gulpfile.js
  3. 1
      lib/bitcoincash.js
  4. 166
      lib/message.js
  5. 74
      package-lock.json
  6. 1
      package.json
  7. 1
      src/bitcoincash.js
  8. 168
      src/message.js
  9. 164
      test/message.js

32
docs/examples.md

@ -2,6 +2,8 @@
## Generate a random address
```javascript
const bch = require('bitcoincashjs');
const privateKey = new bch.PrivateKey();
const address = privateKey.toAddress();
@ -10,6 +12,8 @@ console.log(address.toString()) // 15WZwpw3BofscM2u43ji85BXucai5YGToL
## Generate a address from a SHA256 hash
```javascript
const bch = require('bitcoincashjs');
const value = new Buffer('Bitcoin Cash - Peer-to-Peer Electronic Cash');
const hash = bch.crypto.Hash.sha256(value);
const bn = bch.crypto.BN.fromBuffer(hash);
@ -20,6 +24,8 @@ console.log(address.toString()) // 126tFHmNHNAXDYT1QeEBEwBbEojib1VZyg
## Translate an address to any Bitcoin Cash address format
```javascript
const bch = require('bitcoincashjs');
const Address = bch.Address;
const BitpayFormat = Address.BitpayFormat;
const CashAddrFormat = Address.CashAddrFormat;
@ -33,21 +39,25 @@ console.log(address.toString(CashAddrFormat)) // bitcoincash:qr0q67nsn66cf3klfuf
## Read an address from any Bitcoin Cash address format
```javascript
const bch = require('bitcoincashjs');
const Address = bch.Address;
const fromString = Address.fromString;
const BitpayFormat = Address.BitpayFormat;
const CashAddrFormat = Address.CashAddrFormat;
const legacy = fromString('1MF7A5H2nHYYJMieouip2SkZiFZMBKqSZe',
'mainnet', 'pubkeyhash');
'livenet', 'pubkeyhash');
const bitpay = fromString('Cchzj7d6fLX5CVd5Vf3jbxNbLNmm4BTYuG',
'mainnet', 'pubkeyhash', BitpayFormat);
'livenet', 'pubkeyhash', BitpayFormat);
const cashaddr = fromString('bitcoincash:qr0q67nsn66cf3klfufttr0vuswh3w5nt5jqpp20t9',
'mainnet', 'pubkeyhash', CashAddrFormat);
'livenet', 'pubkeyhash', CashAddrFormat);
```
## Import an address via WIF
```javascript
const bch = require('bitcoincashjs');
const wif = 'Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct';
const address = new bch.PrivateKey(wif).toAddress();
@ -56,6 +66,8 @@ console.log(address.toString()) // 19AAjaTUbRjQCMuVczepkoPswiZRhjtg31
## Create a Transaction
```javascript
const bch = require('bitcoincashjs');
const privateKey = new bch.PrivateKey('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy');
const utxo = {
'txId' : '115e8f72f39fad874cfab0deed11a80f24f967a84079fb56ddf53ea02e308986',
@ -74,7 +86,9 @@ console.log(transaction.toString()) // 01000000018689302ea03ef...
## Verify a Bitcoin message
```javascript
const Message = require('bitcore-message');
const bch = require('bitcoincashjs');
const Message = bch.Message;
const message = new Message('Bitcoin Cash - Peer-to-Peer Electronic Cash.');
const address = '13Js7D3q4KvfSqgKN8LpNq57gcahrVc5JZ';
@ -85,7 +99,9 @@ console.log(message.verify(address, signature)) // true
## Sign a Bitcoin message
```javascript
const Message = require('bitcore-message');
const bch = require('bitcoincashjs');
const Message = bch.Message;
const message = new Message('Bitcoin Cash - Peer-to-Peer Electronic Cash.');
const privateKey =
@ -97,6 +113,8 @@ console.log(signature.toString()) // IJuZCwN/4HtIRulOb/zRLU1oCP...
## Create an OP RETURN transaction
```javascript
const bch = require('bitcoincashjs');
const privateKey = new bch.PrivateKey('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy');
const utxo = {
'txId' : '115e8f72f39fad874cfab0deed11a80f24f967a84079fb56ddf53ea02e308986',
@ -115,6 +133,8 @@ console.log(transaction.toString()) // 01000000018689302ea03ef...
## Create a 2-of-3 multisig P2SH address
```javascript
const bch = require('bitcoincashjs');
const publicKeys = [
'026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01',
'02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9',
@ -128,6 +148,8 @@ console.log(address.toString()) // 36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7
## Spend from a 2-of-2 multisig P2SH address
```javascript
const bch = require('bitcoincashjs');
const privateKeys = [
new bch.PrivateKey('91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx'),
new bch.PrivateKey('91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT')

1
gulpfile.js

@ -67,6 +67,7 @@ gulp.task(
gulp.task(
'test:node',
['build:node'],
shell.task([
`npx nyc --reporter=html --reporter=text npx mocha ${getTaskArgs()}`,
])

1
lib/bitcoincash.js

@ -43,6 +43,7 @@ bch.errors = require('./errors');
bch.Address = require('./address');
bch.Block = require('./block');
bch.MerkleBlock = require('./block/merkleblock');
bch.Message = require('./message');
bch.BlockHeader = require('./block/blockheader');
bch.HDPrivateKey = require('./hdprivatekey.js');
bch.HDPublicKey = require('./hdpublickey.js');

166
lib/message.js

@ -0,0 +1,166 @@
'use strict';
var _ = require('lodash');
var $ = require('./util/preconditions');
var Address = require('./address');
var PublicKey = require('./publickey');
var PrivateKey = require('./privatekey');
var BufferWriter = require('./encoding/bufferwriter');
var ECDSA = require('./crypto/ecdsa');
var Signature = require('./crypto/signature');
var sha256sha256 = require('./crypto/hash').sha256sha256;
var JSUtil = require('./util/js');
/**
* constructs a new message to sign and verify.
*
* @param {String} message
* @returns {Message}
*/
var Message = function Message(message) {
if (!(this instanceof Message)) {
return new Message(message);
}
$.checkArgument(_.isString(message), 'First argument should be a string');
this.message = message;
return this;
};
Message.MAGIC_BYTES = new Buffer('Bitcoin Signed Message:\n');
Message.prototype.magicHash = function magicHash() {
var prefix1 = BufferWriter.varintBufNum(Message.MAGIC_BYTES.length);
var messageBuffer = new Buffer(this.message);
var prefix2 = BufferWriter.varintBufNum(messageBuffer.length);
var buf = Buffer.concat([prefix1, Message.MAGIC_BYTES, prefix2, messageBuffer]);
var hash = sha256sha256(buf);
return hash;
};
Message.prototype._sign = function _sign(privateKey) {
$.checkArgument(privateKey instanceof PrivateKey, 'First argument should be an instance of PrivateKey');
var hash = this.magicHash();
var ecdsa = new ECDSA();
ecdsa.hashbuf = hash;
ecdsa.privkey = privateKey;
ecdsa.pubkey = privateKey.toPublicKey();
ecdsa.signRandomK();
ecdsa.calci();
return ecdsa.sig;
};
/**
* Will sign a message with a given bitcoin private key.
*
* @param {PrivateKey} privateKey - An instance of PrivateKey
* @returns {String} A base64 encoded compact signature
*/
Message.prototype.sign = function sign(privateKey) {
var signature = this._sign(privateKey);
return signature.toCompact().toString('base64');
};
Message.prototype._verify = function _verify(publicKey, signature) {
$.checkArgument(publicKey instanceof PublicKey, 'First argument should be an instance of PublicKey');
$.checkArgument(signature instanceof Signature, 'Second argument should be an instance of Signature');
var hash = this.magicHash();
var verified = ECDSA.verify(hash, signature, publicKey);
if (!verified) {
this.error = 'The signature was invalid';
}
return verified;
};
/**
* Will return a boolean of the signature is valid for a given bitcoin address.
* If it isn't the specific reason is accessible via the "error" member.
*
* @param {Address|String} bitcoinAddress - A bitcoin address
* @param {String} signatureString - A base64 encoded compact signature
* @returns {Boolean}
*/
Message.prototype.verify = function verify(bitcoinAddress, signatureString) {
$.checkArgument(bitcoinAddress);
$.checkArgument(signatureString && _.isString(signatureString));
if (_.isString(bitcoinAddress)) {
bitcoinAddress = Address.fromString(bitcoinAddress);
}
var signature = Signature.fromCompact(new Buffer(signatureString, 'base64'));
// recover the public key
var ecdsa = new ECDSA();
ecdsa.hashbuf = this.magicHash();
ecdsa.sig = signature;
var publicKey = ecdsa.toPublicKey();
var signatureAddress = Address.fromPublicKey(publicKey, bitcoinAddress.network);
// check that the recovered address and specified address match
if (bitcoinAddress.toString() !== signatureAddress.toString()) {
this.error = 'The signature did not match the message digest';
return false;
}
return this._verify(publicKey, signature);
};
/**
* Instantiate a message from a message string
*
* @param {String} str - A string of the message
* @returns {Message} A new instance of a Message
*/
Message.fromString = function (str) {
return new Message(str);
};
/**
* Instantiate a message from JSON
*
* @param {String} json - An JSON string or Object with keys: message
* @returns {Message} A new instance of a Message
*/
Message.fromJSON = function fromJSON(json) {
if (JSUtil.isValidJSON(json)) {
json = JSON.parse(json);
}
return new Message(json.message);
};
/**
* @returns {Object} A plain object with the message information
*/
Message.prototype.toObject = function toObject() {
return {
message: this.message
};
};
/**
* @returns {String} A JSON representation of the message information
*/
Message.prototype.toJSON = function toJSON() {
return JSON.stringify(this.toObject());
};
/**
* Will return a the string representation of the message
*
* @returns {String} Message
*/
Message.prototype.toString = function () {
return this.message;
};
/**
* Will return a string formatted for the console
*
* @returns {String} Message
*/
Message.prototype.inspect = function () {
return '<Message: ' + this.toString() + '>';
};
module.exports = Message;

74
package-lock.json

@ -1398,80 +1398,6 @@
"integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=",
"dev": true
},
"bitcore-lib": {
"version": "0.13.19",
"resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-0.13.19.tgz",
"integrity": "sha1-SK8em9oQBnwasWJjRyta3SAA89w=",
"requires": {
"bn.js": "2.0.4",
"bs58": "2.0.0",
"buffer-compare": "1.0.0",
"elliptic": "3.0.3",
"inherits": "2.0.1",
"lodash": "3.10.1"
},
"dependencies": {
"bn.js": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-2.0.4.tgz",
"integrity": "sha1-Igp81nf38b+pNif/QZN3b+eBlIA="
},
"bs58": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-2.0.0.tgz",
"integrity": "sha1-crcTvtIjoKxRi72g484/SBfznrU="
},
"buffer-compare": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-compare/-/buffer-compare-1.0.0.tgz",
"integrity": "sha1-rKp6lm6Y7un64Usxw5pfFY+zxKI="
},
"elliptic": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-3.0.3.tgz",
"integrity": "sha1-hlybQgv75VAGuflp+XoNLESWZZU=",
"requires": {
"bn.js": "2.0.4",
"brorand": "1.0.5",
"hash.js": "1.0.3",
"inherits": "2.0.1"
},
"dependencies": {
"brorand": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz",
"integrity": "sha1-B7VMowKGq9Fxig4qgwgD79yb+gQ="
},
"hash.js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
"integrity": "sha1-EzL/ABVsCg/92CNgE9B7d6BFFXM=",
"requires": {
"inherits": "2.0.1"
}
}
}
},
"inherits": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
"integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
},
"lodash": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
"integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
}
}
},
"bitcore-message": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/bitcore-message/-/bitcore-message-1.0.4.tgz",
"integrity": "sha1-QMJHnRtPvcUboKbvF6RqfA5ImFM=",
"requires": {
"bitcore-lib": "0.13.19"
}
},
"bl": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz",

1
package.json

@ -45,7 +45,6 @@
},
"dependencies": {
"big-integer": "^1.6.26",
"bitcore-message": "^1.0.4",
"bn.js": "=2.0.4",
"bs58": "=2.0.0",
"buffer-compare": "=1.0.0",

1
src/bitcoincash.js

@ -45,6 +45,7 @@ bch.errors = require('./errors');
bch.Address = require('./address');
bch.Block = require('./block');
bch.MerkleBlock = require('./block/merkleblock');
bch.Message = require('./message');
bch.BlockHeader = require('./block/blockheader');
bch.HDPrivateKey = require('./hdprivatekey.js');
bch.HDPublicKey = require('./hdpublickey.js');

168
src/message.js

@ -0,0 +1,168 @@
'use strict';
var _ = require('lodash');
var $ = require('./util/preconditions');
var Address = require('./address');
var PublicKey = require('./publickey');
var PrivateKey = require('./privatekey');
var BufferWriter = require('./encoding/bufferwriter');
var ECDSA = require('./crypto/ecdsa');
var Signature = require('./crypto/signature');
var sha256sha256 = require('./crypto/hash').sha256sha256;
var JSUtil = require('./util/js');
/**
* constructs a new message to sign and verify.
*
* @param {String} message
* @returns {Message}
*/
var Message = function Message(message) {
if (!(this instanceof Message)) {
return new Message(message);
}
$.checkArgument(_.isString(message), 'First argument should be a string');
this.message = message;
return this;
};
Message.MAGIC_BYTES = new Buffer('Bitcoin Signed Message:\n');
Message.prototype.magicHash = function magicHash() {
var prefix1 = BufferWriter.varintBufNum(Message.MAGIC_BYTES.length);
var messageBuffer = new Buffer(this.message);
var prefix2 = BufferWriter.varintBufNum(messageBuffer.length);
var buf = Buffer.concat([prefix1, Message.MAGIC_BYTES, prefix2, messageBuffer]);
var hash = sha256sha256(buf);
return hash;
};
Message.prototype._sign = function _sign(privateKey) {
console.log(privateKey);
$.checkArgument(privateKey instanceof PrivateKey,
'First argument should be an instance of PrivateKey');
var hash = this.magicHash();
var ecdsa = new ECDSA();
ecdsa.hashbuf = hash;
ecdsa.privkey = privateKey;
ecdsa.pubkey = privateKey.toPublicKey();
ecdsa.signRandomK();
ecdsa.calci();
return ecdsa.sig;
};
/**
* Will sign a message with a given bitcoin private key.
*
* @param {PrivateKey} privateKey - An instance of PrivateKey
* @returns {String} A base64 encoded compact signature
*/
Message.prototype.sign = function sign(privateKey) {
var signature = this._sign(privateKey);
return signature.toCompact().toString('base64');
};
Message.prototype._verify = function _verify(publicKey, signature) {
$.checkArgument(publicKey instanceof PublicKey, 'First argument should be an instance of PublicKey');
$.checkArgument(signature instanceof Signature, 'Second argument should be an instance of Signature');
var hash = this.magicHash();
var verified = ECDSA.verify(hash, signature, publicKey);
if (!verified) {
this.error = 'The signature was invalid';
}
return verified;
};
/**
* Will return a boolean of the signature is valid for a given bitcoin address.
* If it isn't the specific reason is accessible via the "error" member.
*
* @param {Address|String} bitcoinAddress - A bitcoin address
* @param {String} signatureString - A base64 encoded compact signature
* @returns {Boolean}
*/
Message.prototype.verify = function verify(bitcoinAddress, signatureString) {
$.checkArgument(bitcoinAddress);
$.checkArgument(signatureString && _.isString(signatureString));
if (_.isString(bitcoinAddress)) {
bitcoinAddress = Address.fromString(bitcoinAddress);
}
var signature = Signature.fromCompact(new Buffer(signatureString, 'base64'));
// recover the public key
var ecdsa = new ECDSA();
ecdsa.hashbuf = this.magicHash();
ecdsa.sig = signature;
var publicKey = ecdsa.toPublicKey();
var signatureAddress = Address.fromPublicKey(publicKey, bitcoinAddress.network);
// check that the recovered address and specified address match
if (bitcoinAddress.toString() !== signatureAddress.toString()) {
this.error = 'The signature did not match the message digest';
return false;
}
return this._verify(publicKey, signature);
};
/**
* Instantiate a message from a message string
*
* @param {String} str - A string of the message
* @returns {Message} A new instance of a Message
*/
Message.fromString = function(str) {
return new Message(str);
};
/**
* Instantiate a message from JSON
*
* @param {String} json - An JSON string or Object with keys: message
* @returns {Message} A new instance of a Message
*/
Message.fromJSON = function fromJSON(json) {
if (JSUtil.isValidJSON(json)) {
json = JSON.parse(json);
}
return new Message(json.message);
};
/**
* @returns {Object} A plain object with the message information
*/
Message.prototype.toObject = function toObject() {
return {
message: this.message
};
};
/**
* @returns {String} A JSON representation of the message information
*/
Message.prototype.toJSON = function toJSON() {
return JSON.stringify(this.toObject());
};
/**
* Will return a the string representation of the message
*
* @returns {String} Message
*/
Message.prototype.toString = function() {
return this.message;
};
/**
* Will return a string formatted for the console
*
* @returns {String} Message
*/
Message.prototype.inspect = function() {
return '<Message: ' + this.toString() + '>';
};
module.exports = Message;

164
test/message.js

@ -0,0 +1,164 @@
'use strict';
var chai = require('chai');
var expect = chai.expect;
var should = chai.should();
var bch = require('..');
var Address = bch.Address;
var Signature = bch.crypto.Signature;
var Message = bch.Message;
describe('Message', function() {
var address = 'n1ZCYg9YXtB5XCZazLxSmPDa8iwJRZHhGx';
var badAddress = 'mmRcrB5fTwgxaFJmVLNtaG8SV454y1E3kC';
var privateKey = bch.PrivateKey.fromWIF('cPBn5A4ikZvBTQ8D7NnvHZYCAxzDZ5Z2TSGW2LkyPiLxqYaJPBW4');
var text = 'hello, world';
var signatureString = 'H/DIn8uA1scAuKLlCx+/9LnAcJtwQQ0PmcPrJUq90aboLv3fH5fFvY+vmbfOSFEtGarznYli6ShPr9RXwY9UrIY=';
var badSignatureString = 'H69qZ4mbZCcvXk7CWjptD5ypnYVLvQ3eMXLM8+1gX21SLH/GaFnAjQrDn37+TDw79i9zHhbiMMwhtvTwnPigZ6k=';
var signature = Signature.fromCompact(new Buffer(signatureString, 'base64'));
var badSignature = Signature.fromCompact(new Buffer(badSignatureString, 'base64'));
var publicKey = privateKey.toPublicKey();
it('will error with incorrect message type', function() {
expect(function() {
return new Message(new Date());
}).to.throw('First argument should be a string');
});
it('will instantiate without "new"', function() {
var message = Message(text);
should.exist(message);
});
var signature2;
var signature3;
it('can sign a message', function() {
var message2 = new Message(text);
signature2 = message2._sign(privateKey);
signature3 = Message(text).sign(privateKey);
should.exist(signature2);
should.exist(signature3);
});
it('sign will error with incorrect private key argument', function() {
expect(function() {
var message3 = new Message(text);
return message3.sign('not a private key');
}).to.throw('First argument should be an instance of PrivateKey');
});
it('can verify a message with signature', function() {
var message4 = new Message(text);
var verified = message4._verify(publicKey, signature2);
verified.should.equal(true);
});
it('can verify a message with existing signature', function() {
var message5 = new Message(text);
var verified = message5._verify(publicKey, signature);
verified.should.equal(true);
});
it('verify will error with incorrect public key argument', function() {
expect(function() {
var message6 = new Message(text);
return message6._verify('not a public key', signature);
}).to.throw('First argument should be an instance of PublicKey');
});
it('verify will error with incorrect signature argument', function() {
expect(function() {
var message7 = new Message(text);
return message7._verify(publicKey, 'not a signature');
}).to.throw('Second argument should be an instance of Signature');
});
it('verify will correctly identify a bad signature', function() {
var message8 = new Message(text);
var verified = message8._verify(publicKey, badSignature);
should.exist(message8.error);
verified.should.equal(false);
});
it('can verify a message with address and generated signature string', function() {
var message9 = new Message(text);
var verified = message9.verify(address, signature3);
should.not.exist(message9.error);
verified.should.equal(true);
});
it('will not verify with address mismatch', function() {
var message10 = new Message(text);
var verified = message10.verify(badAddress, signatureString);
should.exist(message10.error);
verified.should.equal(false);
});
it('will verify with an uncompressed pubkey', function() {
var privateKey = new bch.PrivateKey('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss');
var message = new Message('This is an example of a signed message.');
var signature = message.sign(privateKey);
var verified = message.verify(privateKey.toAddress(), signature);
verified.should.equal(true);
});
it('can chain methods', function() {
var verified = Message(text).verify(address, signatureString);
verified.should.equal(true);
});
describe('#json', function() {
it('roundtrip to-from-to', function() {
var json = new Message(text).toJSON();
var message = Message.fromJSON(json);
message.toString().should.equal(text);
});
it('checks that the string parameter is valid JSON', function() {
expect(function() {
return Message.fromJSON('¹');
}).to.throw();
});
});
describe('#toString', function() {
it('message string', function() {
var message = new Message(text);
message.toString().should.equal(text);
});
it('roundtrip to-from-to', function() {
var str = new Message(text).toString();
var message = Message.fromString(str);
message.toString().should.equal(text);
});
});
describe('#inspect', function() {
it('should output formatted output correctly', function() {
var message = new Message(text);
var output = '<Message: '+text+'>';
message.inspect().should.equal(output);
});
});
it('accepts Address for verification', function() {
var verified = Message(text)
.verify(new Address(address), signatureString);
verified.should.equal(true);
});
});
Loading…
Cancel
Save