From 29f09ac5f63bd1734d9465741bf6bf9c67c357c5 Mon Sep 17 00:00:00 2001 From: Emilio Almansi Date: Thu, 4 Jan 2018 18:52:21 -0300 Subject: [PATCH] Remove bitcore-message dependency. --- docs/examples.md | 32 +++++++-- gulpfile.js | 1 + lib/bitcoincash.js | 1 + lib/message.js | 166 ++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 74 -------------------- package.json | 1 - src/bitcoincash.js | 1 + src/message.js | 168 +++++++++++++++++++++++++++++++++++++++++++++ test/message.js | 164 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 528 insertions(+), 80 deletions(-) create mode 100644 lib/message.js create mode 100644 src/message.js create mode 100644 test/message.js diff --git a/docs/examples.md b/docs/examples.md index 9c5b14c..d1fee2b 100644 --- a/docs/examples.md +++ b/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') diff --git a/gulpfile.js b/gulpfile.js index deeab4e..461b8fb 100644 --- a/gulpfile.js +++ b/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()}`, ]) diff --git a/lib/bitcoincash.js b/lib/bitcoincash.js index b2bffba..63b1f10 100644 --- a/lib/bitcoincash.js +++ b/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'); diff --git a/lib/message.js b/lib/message.js new file mode 100644 index 0000000..7cd0751 --- /dev/null +++ b/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 ''; +}; + +module.exports = Message; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c2b2ac8..4ce526d 100644 --- a/package-lock.json +++ b/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", diff --git a/package.json b/package.json index 4d3c591..b3f2e2b 100644 --- a/package.json +++ b/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", diff --git a/src/bitcoincash.js b/src/bitcoincash.js index f5b5acc..03aed64 100644 --- a/src/bitcoincash.js +++ b/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'); diff --git a/src/message.js b/src/message.js new file mode 100644 index 0000000..94e41a5 --- /dev/null +++ b/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 ''; +}; + +module.exports = Message; diff --git a/test/message.js b/test/message.js new file mode 100644 index 0000000..9ca4d4c --- /dev/null +++ b/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.inspect().should.equal(output); + }); + + }); + + + it('accepts Address for verification', function() { + var verified = Message(text) + .verify(new Address(address), signatureString); + verified.should.equal(true); + }); + +});