├── .eslintignore ├── .eslintrc ├── .gitignore ├── .travis.yml ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── index.js ├── jsdoc.json ├── lib ├── constants.js ├── crypto.js ├── ecdsa.js ├── ecpair.js ├── ecsignature.js ├── hdnode.js ├── networks.js ├── time │ └── slots.js ├── transactions │ ├── crypto.js │ ├── delegate.js │ ├── ipfs.js │ ├── multisignature.js │ ├── signature.js │ ├── transaction.js │ └── vote.js ├── types.js └── v2 │ └── transactions │ ├── crypto.js │ └── transfer.js ├── package.json └── test ├── ark.js ├── crypto ├── fixtures.json └── index.js ├── ecdsa ├── fixtures.json └── index.js ├── ecpair ├── fixtures.json └── index.js ├── ecsignature ├── fixtures.json └── index.js ├── hdnode ├── fixtures.json └── index.js ├── integration ├── basic.js └── bip32.js ├── ipfs └── index.js ├── multisignature └── index.js ├── signature └── index.js ├── slot └── index.js ├── transaction └── index.js └── vote └── index.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | app.js -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "mocha": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | ], 10 | "rules": { 11 | "func-names": ["error", "never"], 12 | "max-len": ["error", 140, { "ignoreComments": true }] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Build results 27 | app.js 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directories 33 | node_modules 34 | jspm_packages 35 | 36 | # Generated doc directory 37 | doc 38 | 39 | # Optional npm cache directory 40 | .npm 41 | 42 | # Mac OS X local settings 43 | .DS_Store 44 | .tags 45 | bundle.min.js 46 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | - 8 5 | - 7 6 | - 6 7 | 8 | script: 9 | #- npm run lint 10 | - npm run test 11 | - npm run build:browserify 12 | - npm run clean:browserify 13 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 6 | "codezombiech.gitignore", 7 | "dbaeumer.vscode-eslint", 8 | "eg2.vscode-npm-script" 9 | ] 10 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Mocha Tests", 11 | "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", 12 | "args": [ 13 | "-u", 14 | "tdd", 15 | "--timeout", 16 | "999999", 17 | "--colors", 18 | "${workspaceRoot}/test/ark.js", 19 | "${workspaceRoot}/test/*/*.js" 20 | ], 21 | "internalConsoleOptions": "openOnSessionStart" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "editor.insertSpaces": true, 4 | "files.trimTrailingWhitespace": true 5 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "lint", 9 | "problemMatcher": [ 10 | "$eslint-stylish" 11 | ] 12 | }, 13 | { 14 | "type": "npm", 15 | "script": "build", 16 | "problemMatcher": [], 17 | "group": { 18 | "kind": "build", 19 | "isDefault": true 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # BOUNTY Program 2 | ARK has a bounty program for all accepted PR (Pull Requests) for this repository 3 | 4 | More information can be found at https://blog.ark.io/ark-github-development-bounty-113806ae9ffe 5 | 6 | Before pushing PR, please [jump in our slack #development](https://ark.io/slack) channel in order to discuss your contributions or to connect with other ARKvelopers. 7 | 8 | # Guidelines 9 | - pickup any of the existing issues or if you find an issue make a PR, 10 | - only one PR reward will be awarded per issue it fixes, 11 | - solving an open issue will increase your chances to be picked up as any of the monthly bounty winners. 12 | 13 | # Accepted PR 14 | - increase general code quality, 15 | - add meaningfull tests, 16 | - correct bug, 17 | - add new features, 18 | - improve documentation, 19 | - create something new for ARK. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 bitcoinjs-lib contributors 4 | Copyright (c) 2016 Ark 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **:warning: DEPRECATED IN FAVOR OF https://github.com/ArkEcosystem/core/tree/master/packages/crypto :warning:*** 2 | 3 | ![ARK JavaScript](https://i.imgur.com/ywwE2uF.png) 4 | 5 | 6 | [![Build Status](https://travis-ci.org/ArkEcosystem/ark-js.svg?branch=master)](https://travis-ci.org/ArkEcosystem/ark-js) 7 | 8 | # Ark JS 9 | 10 | Ark JS is a JavaScript library for sending ARK transactions. It's main benefit is that it does not require a locally installed ARK node, and instead utilizes the existing peers on the network. It can be used from the client as a [browserify](http://browserify.org/) compiled module, or on the server as a standard Node.js module. 11 | 12 | ## Installation 13 | 14 | [![npm package](https://nodei.co/npm/arkjs.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/arkjs/) 15 | 16 | ## Building 17 | 18 | Build the browserify module for client use: 19 | 20 | ```sh 21 | npm build:browserify 22 | ``` 23 | 24 | Clean: 25 | 26 | ```sh 27 | npm clean:browserify 28 | ``` 29 | 30 | ## Tests 31 | 32 | ``` 33 | npm test 34 | ``` 35 | 36 | Tests written using mocha + schedule.js. 37 | 38 | *** 39 | 40 | ## Usage 41 | 42 | On the client: 43 | 44 | ```html 45 | 46 | ``` 47 | 48 | On the server: 49 | 50 | ```js 51 | var ark = require("arkjs"); 52 | ``` 53 | 54 | ### Generating a key pair 55 | 56 | To generate a public / private key pair from a given passphrase: 57 | 58 | ```js 59 | var keys = ark.crypto.getKeys("passphrase"); 60 | ``` 61 | 62 | Returning: 63 | 64 | ```js 65 | { 66 | publicKey: "02e012f0a7cac12a74bdc17d844cbc9f637177b470019c32a53cef94c7a56e2ea9", 67 | privateKey: "" 68 | } 69 | ``` 70 | 71 | To get the private key: 72 | 73 | ```js 74 | keys.d.toBuffer().toString("hex"); 75 | ``` 76 | 77 | Returning: 78 | ``` 79 | 1e089e3c5323ad80a90767bdd5907297b4138163f027097fd3bdbeab528d2d68 80 | ``` 81 | 82 | 83 | ### Generating an address 84 | 85 | To generate a unique Ark address from a given public key: 86 | 87 | ```js 88 | var address = ark.crypto.getAddress("5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09"); 89 | ``` 90 | 91 | Returning: 92 | 93 | ``` 94 | AGihocTkwDygiFvmg6aG8jThYTic47GzU9 95 | ``` 96 | 97 | ### Creating a transaction 98 | 99 | To create a signed transaction object, which can then be broadcasted onto the network: 100 | 101 | ```js 102 | var amount = 1000 * Math.pow(10, 8); // 100000000000 103 | var transaction = ark.transaction.createTransaction("AGihocTkwDygiFvmg6aG8jThYTic47GzU9", amount, null, "passphrase", "secondPassphrase"); 104 | ``` 105 | 106 | Returning: 107 | 108 | ```js 109 | { 110 | type: 0, // Transaction type. 0 = Normal transaction. 111 | amount: 100000000000, // The amount to send expressed as an integer value. 112 | asset: {}, // Transaction asset, dependent on tx type. 113 | fee: 100000000, // 0.1 ARK expressed as an integer value. 114 | id: "500224999259823996", // Transaction ID. 115 | recipientId: "AGihocTkwDygiFvmg6aG8jThYTic47GzU9", // Recipient ID. 116 | senderPublicKey: "56e106a1d4a53dbe22cac52fefd8fc4123cfb4ee482f8f25a4fc72eb459b38a5", // Sender's public key. 117 | signSignature: "03fdd33bed30270b97e77ada44764cc8628f6ad3bbd84718571695262a5a18baa37bd76a62dd25bc21beacd61eaf2c63af0cf34edb0d191d225f4974cd3aa509", // Sender's second passphrase signature. 118 | signature: "9419ca3cf11ed2e3fa4c63bc9a4dc18b5001648e74522bc0f22bda46a188e462da4785e5c71a43cfc0486af08d447b9340ba8b93258c4c7f50798060fff2d709", // Transaction signature. 119 | timestamp: 27953413 // Based on UTC time of genesis since epoch. 120 | } 121 | ``` 122 | 123 | ### Network identification with Nethash 124 | 125 | You need to obtain the nethash in order to be sure you are broadcasting to the right network (testnet, mainnet or others). The nethash is simply the payload hash from the genesisBlock. If no nethash or wrong nethash is provided in the headers, the request will be rejected returning the expected nethash. 126 | 127 | ```json 128 | { "success": false, "message": "Request is made on the wrong network", "expected":"e2f8f69ec6ab4b12550a314bd867c46e64e429961bb427514a3a534c602ff467", "received":"wrong-nethash" } 129 | ``` 130 | 131 | The nethash for a given network can be obtained at the following API endpoint: 132 | 133 | ``` 134 | /api/blocks/getNetHash 135 | ``` 136 | 137 | You can also get the nethash from a peer this way: 138 | 139 | On the client using [jQuery](https://jquery.com/): 140 | 141 | ```js 142 | var nethash; 143 | $.ajax({ 144 | url: "https://api.arknode.net/peer/transactions/", 145 | data: JSON.stringify({}), 146 | dataType: "json", 147 | method: "POST", 148 | headers: { 149 | "Content-Type": "application/json", 150 | "os": "linux3.2.0-4-amd64", 151 | "version": "0.3.0", 152 | "port": 1, 153 | "nethash": "wrong-nethash" 154 | }, 155 | success: function(data) { 156 | nethash = data.body.expected; 157 | } 158 | }); 159 | ``` 160 | 161 | From a server using [Request](https://github.com/request/request): 162 | 163 | ```js 164 | var nethash; 165 | request({ 166 | url: "https://api.arknode.net/peer/transactions", 167 | json: { }, 168 | method: "POST", 169 | headers: { 170 | "Content-Type": "application/json", 171 | "os": "linux3.2.0-4-amd64", 172 | "version": "0.3.0", 173 | "port": 1, 174 | "nethash": "wrong-nethash" 175 | } 176 | }, function(error, response, body) { 177 | nethash = body.expected; 178 | }); 179 | ``` 180 | 181 | ### Posting a transaction 182 | 183 | Transaction objects are sent to `/peer/transactions`, using the `POST` method. 184 | 185 | Example: 186 | 187 | ```js 188 | Method: POST 189 | Content-Type: application/json 190 | 191 | { 192 | "transactions" : [{ 193 | ... 194 | }] 195 | } 196 | ``` 197 | 198 | #### Sending transaction on the Client 199 | 200 | Using [jQuery](https://jquery.com/): 201 | 202 | ```js 203 | var success = function(data) { 204 | console.log(data); 205 | }; 206 | 207 | $.ajax({ 208 | url: "https://api.arknode.net/peer/transactions", 209 | data: JSON.stringify({ transactions: [transaction] }), 210 | dataType: "json", 211 | method: "POST", 212 | headers: { 213 | "Content-Type": "application/json", 214 | "os": "linux3.2.0-4-amd64", 215 | "version": "0.3.0", 216 | "port": 1, 217 | "nethash":nethash 218 | }, 219 | success: success 220 | }); 221 | ``` 222 | 223 | #### Sending transaction on the Server 224 | 225 | Using [Request](https://github.com/request/request): 226 | 227 | 228 | ```js 229 | var request = require("request"); 230 | 231 | var callback = function(error, response, body) { 232 | console.log(error || body); 233 | }; 234 | 235 | request({ 236 | url: "https://api.arknode.net/peer/transactions", 237 | json: { transactions: [transaction] }, 238 | method: "POST", 239 | headers: { 240 | "Content-Type": "application/json", 241 | "os": "linux3.2.0-4-amd64", 242 | "version": "0.3.0", 243 | "port": 1, 244 | "nethash": nethash 245 | } 246 | }, callback); 247 | ``` 248 | 249 | #### Peer Response 250 | 251 | Upon successfully accepting a transaction, the receiving node will respond with: 252 | 253 | ```json 254 | { "success": true, "result": "5318121831703437738" } 255 | ``` 256 | 257 | If the transaction is deemed invalid, or an error is encountered, the receiving node will respond with: 258 | 259 | ```json 260 | { "success": false, "message": "Error message" } 261 | ``` 262 | 263 | *** 264 | 265 | ### Other transaction types 266 | 267 | #### Creating a delegate transaction 268 | 269 | ```js 270 | var transaction = ark.delegate.createDelegate("secret", "username", "secondSecret"); 271 | ``` 272 | 273 | #### Creating a second signature transaction 274 | 275 | ```js 276 | var transaction = ark.signature.createSignature("secret", "secondSecret"); 277 | ``` 278 | 279 | #### Creating a vote transaction 280 | 281 | ```js 282 | var transaction = ark.vote.createVote("secret", ["+58199578191950019299181920120128129"], "secondSecret"); 283 | ``` 284 | 285 | *** 286 | 287 | ## Security 288 | 289 | If you discover a security vulnerability within this package, please send an e-mail to security@ark.io. All security vulnerabilities will be promptly addressed. 290 | 291 | ## Authors 292 | - FX Thoorens 293 | - Guillaume Verbal 294 | - Boris Povod 295 | - Oliver Beddows 296 | 297 | ## License 298 | 299 | The MIT License (MIT) 300 | 301 | Copyright (c) 2016-2017 ARK.io
302 | Copyright (c) 2016 Lisk
303 | Copyright (c) 2015 Crypti 304 | 305 | 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: 306 | 307 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 308 | 309 | 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. 310 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module arkjs 3 | * @license MIT 4 | */ 5 | 6 | module.exports = { 7 | /** @see module:crypto */ 8 | crypto: require("./lib/transactions/crypto.js"), 9 | /** @see module:delegate */ 10 | delegate : require("./lib/transactions/delegate.js"), 11 | /** @see module:signature */ 12 | signature : require("./lib/transactions/signature.js"), 13 | /** @see module:multisignature */ 14 | multisignature : require("./lib/transactions/multisignature.js"), 15 | /** @see module:transaction */ 16 | transaction : require("./lib/transactions/transaction.js"), 17 | /** @see module:vote */ 18 | vote : require("./lib/transactions/vote.js"), 19 | /** @see module:ipfs */ 20 | ipfs : require("./lib/transactions/ipfs.js"), 21 | /** @see module:networks */ 22 | networks : require("./lib/networks.js"), 23 | /** @see module:slots */ 24 | slots : require("./lib/time/slots.js"), 25 | 26 | /** @see ECPair */ 27 | ECPair : require("./lib/ecpair.js"), 28 | /** @see HDNode */ 29 | HDNode : require("./lib/hdnode.js"), 30 | /** @see ECSignature */ 31 | ECSignature : require("./lib/ecsignature.js"), 32 | } 33 | 34 | // extra aliases for bitcoinlib-js compatibility 35 | var libCrypto = require('./lib/crypto.js') 36 | for (var method in libCrypto) { 37 | module.exports.crypto[method] = libCrypto[method] 38 | } 39 | 40 | /** 41 | * @typedef ECPoint 42 | * @property {number} x 43 | * @property {number} y 44 | */ 45 | 46 | /** 47 | * @typedef Network 48 | * @property {string} messagePrefix 49 | * @property {object} bip32 50 | * @property {number} bip32.public 51 | * @property {number} bip32.private 52 | * @property {number} pubKeyHash 53 | * @property {number} wif 54 | */ 55 | 56 | /** 57 | * @typedef Transaction 58 | * @property {number} amount 59 | * @property {object} asset 60 | * @property {string} id 61 | * @property {number} fee 62 | * @property {string} recipientId 63 | * @property {*} senderPublicKey 64 | * @property {string} signature 65 | * @property {string} [signSignature] 66 | * @property {number} timestamp 67 | * @property {number} type 68 | * @property {string} [vendorField] 69 | */ 70 | 71 | -------------------------------------------------------------------------------- /jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["plugins/markdown"], 3 | "source": { 4 | "include": ["README.md", "index.js", "lib/"] 5 | }, 6 | "opts": { 7 | "destination": "./doc", 8 | "recurse": true 9 | }, 10 | "tags": { 11 | "dictionaries": ["jsdoc", "closure"] 12 | } 13 | } -------------------------------------------------------------------------------- /lib/constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | fees:{ 3 | send: 10000000, 4 | vote: 100000000, 5 | delegate: 2500000000, 6 | secondsignature: 500000000, 7 | multisignature: 500000000 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/crypto.js: -------------------------------------------------------------------------------- 1 | var createHash = require('create-hash') 2 | 3 | /** 4 | * @memberof module:crypto 5 | * @param {string|Buffer} buffer 6 | * @returns {Buffer} 7 | */ 8 | function ripemd160 (buffer) { 9 | return createHash('rmd160').update(buffer).digest() 10 | } 11 | 12 | /** 13 | * @memberof module:crypto 14 | * @param {string|Buffer} buffer 15 | * @returns {Buffer} 16 | */ 17 | function sha1 (buffer) { 18 | return createHash('sha1').update(buffer).digest() 19 | } 20 | 21 | /** 22 | * @memberof module:crypto 23 | * @param {string|Buffer} buffer 24 | * @returns {Buffer} 25 | */ 26 | function sha256 (buffer) { 27 | return createHash('sha256').update(buffer).digest() 28 | } 29 | 30 | /** 31 | * @memberof module:crypto 32 | * @param {string|Buffer} buffer 33 | * @returns {Buffer} 34 | */ 35 | function hash160 (buffer) { 36 | return ripemd160(sha256(buffer)) 37 | } 38 | 39 | /** 40 | * @memberof module:crypto 41 | * @param {string|Buffer} buffer 42 | * @returns {Buffer} 43 | */ 44 | function hash256 (buffer) { 45 | return sha256(sha256(buffer)) 46 | } 47 | 48 | module.exports = { 49 | hash160: hash160, 50 | hash256: hash256, 51 | ripemd160: ripemd160, 52 | sha1: sha1, 53 | sha256: sha256 54 | } 55 | -------------------------------------------------------------------------------- /lib/ecdsa.js: -------------------------------------------------------------------------------- 1 | /** @module ecdsa */ 2 | 3 | var createHmac = require('create-hmac') 4 | var typeforce = require('typeforce') 5 | var types = require('./types') 6 | 7 | var BigInteger = require('bigi') 8 | var ECSignature = require('./ecsignature') 9 | 10 | var ZERO = new Buffer([0]) 11 | var ONE = new Buffer([1]) 12 | 13 | var ecurve = require('ecurve') 14 | var secp256k1 = ecurve.getCurveByName('secp256k1') 15 | 16 | /** 17 | * [Generation of k.](https://tools.ietf.org/html/rfc6979#section-3.2) 18 | * 19 | * @static 20 | * @param {Buffer} hash 21 | * @param {Buffer} x 22 | * @param {function} checkSig 23 | * @returns {BigInteger} 24 | */ 25 | function deterministicGenerateK (hash, x, checkSig) { 26 | typeforce(types.tuple( 27 | types.Hash256bit, 28 | types.Buffer256bit, 29 | types.Function 30 | ), arguments) 31 | 32 | var k = new Buffer(32) 33 | var v = new Buffer(32) 34 | 35 | // Step A, ignored as hash already provided 36 | // Step B 37 | v.fill(1) 38 | 39 | // Step C 40 | k.fill(0) 41 | 42 | // Step D 43 | k = createHmac('sha256', k) 44 | .update(v) 45 | .update(ZERO) 46 | .update(x) 47 | .update(hash) 48 | .digest() 49 | 50 | // Step E 51 | v = createHmac('sha256', k).update(v).digest() 52 | 53 | // Step F 54 | k = createHmac('sha256', k) 55 | .update(v) 56 | .update(ONE) 57 | .update(x) 58 | .update(hash) 59 | .digest() 60 | 61 | // Step G 62 | v = createHmac('sha256', k).update(v).digest() 63 | 64 | // Step H1/H2a, ignored as tlen === qlen (256 bit) 65 | // Step H2b 66 | v = createHmac('sha256', k).update(v).digest() 67 | 68 | var T = BigInteger.fromBuffer(v) 69 | 70 | // Step H3, repeat until T is within the interval [1, n - 1] and is suitable for ECDSA 71 | while (T.signum() <= 0 || T.compareTo(secp256k1.n) >= 0 || !checkSig(T)) { 72 | k = createHmac('sha256', k) 73 | .update(v) 74 | .update(ZERO) 75 | .digest() 76 | 77 | v = createHmac('sha256', k).update(v).digest() 78 | 79 | // Step H1/H2a, again, ignored as tlen === qlen (256 bit) 80 | // Step H2b again 81 | v = createHmac('sha256', k).update(v).digest() 82 | T = BigInteger.fromBuffer(v) 83 | } 84 | 85 | return T 86 | } 87 | 88 | var N_OVER_TWO = secp256k1.n.shiftRight(1) 89 | 90 | /** 91 | * @static 92 | * @param {Buffer} hash 93 | * @param {BigInteger} d 94 | * @returns {ECSignature} 95 | */ 96 | function sign (hash, d) { 97 | typeforce(types.tuple(types.Hash256bit, types.BigInt), arguments) 98 | 99 | var x = d.toBuffer(32) 100 | var e = BigInteger.fromBuffer(hash) 101 | var n = secp256k1.n 102 | var G = secp256k1.G 103 | 104 | var r, s 105 | deterministicGenerateK(hash, x, function (k) { 106 | var Q = G.multiply(k) 107 | 108 | if (secp256k1.isInfinity(Q)) return false 109 | 110 | r = Q.affineX.mod(n) 111 | if (r.signum() === 0) return false 112 | 113 | s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n) 114 | if (s.signum() === 0) return false 115 | 116 | return true 117 | }) 118 | 119 | // enforce low S values, see bip62: 'low s values in signatures' 120 | if (s.compareTo(N_OVER_TWO) > 0) { 121 | s = n.subtract(s) 122 | } 123 | 124 | return new ECSignature(r, s) 125 | } 126 | 127 | /** 128 | * @static 129 | * @param {Buffer} hash 130 | * @param {ECSignature} signature 131 | * @param {ECPoint} Q 132 | * @returns {boolean} 133 | */ 134 | function verify (hash, signature, Q) { 135 | typeforce(types.tuple( 136 | types.Hash256bit, 137 | types.ECSignature, 138 | types.ECPoint 139 | ), arguments) 140 | 141 | var n = secp256k1.n 142 | var G = secp256k1.G 143 | 144 | var r = signature.r 145 | var s = signature.s 146 | 147 | // 1.4.1 Enforce r and s are both integers in the interval [1, n − 1] 148 | if (r.signum() <= 0 || r.compareTo(n) >= 0) return false 149 | if (s.signum() <= 0 || s.compareTo(n) >= 0) return false 150 | 151 | // 1.4.2 H = Hash(M), already done by the user 152 | // 1.4.3 e = H 153 | var e = BigInteger.fromBuffer(hash) 154 | 155 | // Compute s^-1 156 | var sInv = s.modInverse(n) 157 | 158 | // 1.4.4 Compute u1 = es^−1 mod n 159 | // u2 = rs^−1 mod n 160 | var u1 = e.multiply(sInv).mod(n) 161 | var u2 = r.multiply(sInv).mod(n) 162 | 163 | // 1.4.5 Compute R = (xR, yR) 164 | // R = u1G + u2Q 165 | var R = G.multiplyTwo(u1, Q, u2) 166 | 167 | // 1.4.5 (cont.) Enforce R is not at infinity 168 | if (secp256k1.isInfinity(R)) return false 169 | 170 | // 1.4.6 Convert the field element R.x to an integer 171 | var xR = R.affineX 172 | 173 | // 1.4.7 Set v = xR mod n 174 | var v = xR.mod(n) 175 | 176 | // 1.4.8 If v = r, output "valid", and if v != r, output "invalid" 177 | return v.equals(r) 178 | } 179 | 180 | module.exports = { 181 | deterministicGenerateK: deterministicGenerateK, 182 | sign: sign, 183 | verify: verify, 184 | 185 | // TODO: remove 186 | __curve: secp256k1 187 | } 188 | -------------------------------------------------------------------------------- /lib/ecpair.js: -------------------------------------------------------------------------------- 1 | var base58check = require('bs58check') 2 | var bcrypto = require('./crypto') 3 | var ecdsa = require('./ecdsa') 4 | var ECSignature = require('./ecsignature') 5 | var randomBytes = require('randombytes') 6 | var typeforce = require('typeforce') 7 | var types = require('./types') 8 | var wif = require('wif') 9 | 10 | var NETWORKS = require('./networks') 11 | var BigInteger = require('bigi') 12 | 13 | var ecurve = require('ecurve') 14 | var secp256k1 = ecurve.getCurveByName('secp256k1') 15 | 16 | var secp256k1native = require('secp256k1') 17 | 18 | /** 19 | * Provide either `d` or `Q` but not both. 20 | * 21 | * @constructor 22 | * @param {BigInteger} [d] Private key. 23 | * @param {Point} [Q] Public key. 24 | * @param {object} [options] 25 | * @param {boolean} [options.compressed=true] 26 | * @param {Network} [options.network=networks.ark] 27 | */ 28 | function ECPair (d, Q, options) { 29 | if (options) { 30 | typeforce({ 31 | compressed: types.maybe(types.Boolean), 32 | network: types.maybe(types.Network) 33 | }, options) 34 | } 35 | 36 | options = options || {} 37 | 38 | if (d) { 39 | if (d.signum() <= 0) throw new Error('Private key must be greater than 0') 40 | if (d.compareTo(secp256k1.n) >= 0) throw new Error('Private key must be less than the curve order') 41 | if (Q) throw new TypeError('Unexpected publicKey parameter') 42 | 43 | this.d = d 44 | } else { 45 | typeforce(types.ECPoint, Q) 46 | 47 | this.__Q = Q 48 | } 49 | 50 | /** @type {boolean} */ this.compressed = options.compressed === undefined ? true : options.compressed 51 | /** @type {Network} */ this.network = options.network || NETWORKS.ark 52 | } 53 | 54 | Object.defineProperty(ECPair.prototype, 'Q', { 55 | get: function () { 56 | if (!this.__Q && this.d) { 57 | this.__Q = secp256k1.G.multiply(this.d) 58 | } 59 | 60 | return this.__Q 61 | } 62 | }) 63 | 64 | /** 65 | * @param {Buffer} buffer 66 | * @param {Network} [network=networks.ark] 67 | * @returns {ECPair} 68 | */ 69 | ECPair.fromPublicKeyBuffer = function (buffer, network) { 70 | var Q = ecurve.Point.decodeFrom(secp256k1, buffer) 71 | 72 | return new ECPair(null, Q, { 73 | compressed: Q.compressed, 74 | network: network 75 | }) 76 | } 77 | 78 | /** 79 | * @param {string} string 80 | * @param {Network[]|Network} network 81 | * @returns {ECPair} 82 | */ 83 | ECPair.fromWIF = function (string, network) { 84 | var decoded = wif.decode(string) 85 | var version = decoded.version 86 | 87 | // [network, ...] 88 | if (types.Array(network)) { 89 | network = network.filter(function (network) { 90 | return version === network.wif 91 | }).pop() 92 | 93 | if (!network) throw new Error('Unknown network version') 94 | 95 | // network 96 | } else { 97 | network = network || NETWORKS.ark 98 | 99 | if (version !== network.wif) throw new Error('Invalid network version') 100 | } 101 | 102 | var d = BigInteger.fromBuffer(decoded.privateKey) 103 | 104 | return new ECPair(d, null, { 105 | compressed: decoded.compressed, 106 | network: network 107 | }) 108 | } 109 | 110 | /** 111 | * @param {object} [options] 112 | * @param {function} [options.rng] 113 | * @param {boolean} [options.compressed=true] 114 | * @param {Network} [options.network=networks.ark] 115 | */ 116 | ECPair.makeRandom = function (options) { 117 | options = options || {} 118 | 119 | var rng = options.rng || randomBytes 120 | 121 | var d 122 | do { 123 | var buffer = rng(32) 124 | typeforce(types.Buffer256bit, buffer) 125 | 126 | d = BigInteger.fromBuffer(buffer) 127 | } while (d.signum() <= 0 || d.compareTo(secp256k1.n) >= 0) 128 | 129 | return new ECPair(d, null, options) 130 | } 131 | 132 | /** 133 | * @param {string} seed 134 | * @param {object} [options] 135 | * @param {boolean} [options.compressed=true] 136 | * @param {Network} [options.network=networks.ark] 137 | * @returns {ECPair} 138 | */ 139 | ECPair.fromSeed = function(seed, options) { 140 | var hash = bcrypto.sha256(new Buffer(seed,"utf-8")) 141 | var d = BigInteger.fromBuffer(hash) 142 | if(d.signum() <= 0 || d.compareTo(secp256k1.n) >= 0){ 143 | throw new Error("seed cannot resolve to a compatible private key") 144 | } 145 | else{ 146 | return new ECPair(d, null, options) 147 | } 148 | } 149 | 150 | /** 151 | * @returns {string} 152 | */ 153 | ECPair.prototype.getAddress = function () { 154 | var payload = new Buffer(21) 155 | var hash = bcrypto.ripemd160(this.getPublicKeyBuffer()) 156 | var version = this.getNetwork().pubKeyHash 157 | payload.writeUInt8(version, 0) 158 | hash.copy(payload, 1) 159 | 160 | return base58check.encode(payload) 161 | } 162 | 163 | /** 164 | * @returns {Network} 165 | */ 166 | ECPair.prototype.getNetwork = function () { 167 | return this.network 168 | } 169 | 170 | ECPair.prototype.getPublicKeyBuffer = function () { 171 | return this.Q.getEncoded(this.compressed) 172 | } 173 | 174 | /** 175 | * Requires a private key (`d`) to be set. 176 | * 177 | * @param {Buffer} hash 178 | * @returns {ECSignature} 179 | */ 180 | ECPair.prototype.sign = function (hash) { 181 | if (!this.d) throw new Error('Missing private key') 182 | var native=secp256k1native.sign(hash, this.d.toBuffer(32)) 183 | return ECSignature.parseNativeSecp256k1(native).signature 184 | } 185 | 186 | /** 187 | * Requires a private key (`d`) to be set. 188 | * 189 | * @returns {string} 190 | */ 191 | ECPair.prototype.toWIF = function () { 192 | if (!this.d) throw new Error('Missing private key') 193 | 194 | return wif.encode(this.network.wif, this.d.toBuffer(32), this.compressed) 195 | } 196 | 197 | /** 198 | * @param {Buffer} hash 199 | * @returns {boolean} 200 | */ 201 | ECPair.prototype.verify = function (hash, signature) { 202 | return secp256k1native.verify(hash, signature.toNativeSecp256k1(), this.Q.getEncoded(this.compressed)) 203 | } 204 | 205 | module.exports = ECPair 206 | -------------------------------------------------------------------------------- /lib/ecsignature.js: -------------------------------------------------------------------------------- 1 | var bip66 = require('bip66') 2 | var typeforce = require('typeforce') 3 | var types = require('./types') 4 | 5 | var BigInteger = require('bigi') 6 | 7 | /** 8 | * Creates a new ECSignature. 9 | * 10 | * @constructor 11 | * @param {BigInteger} r 12 | * @param {BigInteger} s 13 | */ 14 | function ECSignature (r, s) { 15 | typeforce(types.tuple(types.BigInt, types.BigInt), arguments) 16 | 17 | /** @type {BigInteger} */ this.r = r 18 | /** @type {BigInteger} */ this.s = s 19 | } 20 | 21 | /** 22 | * @typedef {Object} SignatureParseResult 23 | * @property {boolean} compressed 24 | * @property {number} i 25 | * @property {ECSignature} signature 26 | */ 27 | 28 | /** 29 | * @param {*} native 30 | * @returns {SignatureParseResult} 31 | */ 32 | ECSignature.parseNativeSecp256k1 = function (native) { 33 | if (native.signature.length !== 64) throw new Error('Invalid signature length') 34 | 35 | var compressed = 0 36 | var recoveryParam = native.recovery 37 | 38 | var r = BigInteger.fromBuffer(native.signature.slice(0, 32)) 39 | var s = BigInteger.fromBuffer(native.signature.slice(32)) 40 | 41 | return { 42 | compressed: compressed, 43 | i: recoveryParam, 44 | signature: new ECSignature(r, s) 45 | } 46 | } 47 | 48 | /** 49 | * @returns {Buffer} 50 | */ 51 | ECSignature.prototype.toNativeSecp256k1 = function () { 52 | var buffer = new Buffer(64) 53 | if(this.r.toBuffer().length > 32 || this.s.toBuffer().length > 32){ 54 | return buffer; 55 | } 56 | 57 | this.r.toBuffer(32).copy(buffer, 0) 58 | this.s.toBuffer(32).copy(buffer, 32) 59 | 60 | return buffer 61 | } 62 | 63 | /** 64 | * @param {Buffer} buffer 65 | * @returns {SignatureParseResult} 66 | */ 67 | ECSignature.parseCompact = function (buffer) { 68 | if (buffer.length !== 65) throw new Error('Invalid signature length') 69 | 70 | var flagByte = buffer.readUInt8(0) - 27 71 | if (flagByte !== (flagByte & 7)) throw new Error('Invalid signature parameter') 72 | 73 | var compressed = !!(flagByte & 4) 74 | var recoveryParam = flagByte & 3 75 | 76 | var r = BigInteger.fromBuffer(buffer.slice(1, 33)) 77 | var s = BigInteger.fromBuffer(buffer.slice(33)) 78 | 79 | return { 80 | compressed: compressed, 81 | i: recoveryParam, 82 | signature: new ECSignature(r, s) 83 | } 84 | } 85 | 86 | /** 87 | * @param {Buffer} 88 | * @returns {ECSignature} 89 | */ 90 | ECSignature.fromDER = function (buffer) { 91 | var decode = bip66.decode(buffer) 92 | var r = BigInteger.fromDERInteger(decode.r) 93 | var s = BigInteger.fromDERInteger(decode.s) 94 | 95 | return new ECSignature(r, s) 96 | } 97 | 98 | /** 99 | * BIP62: 1-byte `hashType` flag (only `0x01`, `0x02`, `0x03`, `0x81`, `0x82`, and `0x83` are allowed). 100 | * 101 | * @param {Buffer} buffer 102 | */ 103 | ECSignature.parseScriptSignature = function (buffer) { 104 | var hashType = buffer.readUInt8(buffer.length - 1) 105 | var hashTypeMod = hashType & ~0x80 106 | 107 | if (hashTypeMod <= 0x00 || hashTypeMod >= 0x04) throw new Error('Invalid hashType ' + hashType) 108 | 109 | return { 110 | signature: ECSignature.fromDER(buffer.slice(0, -1)), 111 | hashType: hashType 112 | } 113 | } 114 | 115 | /** 116 | * @param {number} i 117 | * @param {boolean} [compressed=false] 118 | * @returns {Buffer} 119 | */ 120 | ECSignature.prototype.toCompact = function (i, compressed) { 121 | if (compressed) { 122 | i += 4 123 | } 124 | 125 | i += 27 126 | 127 | var buffer = new Buffer(65) 128 | buffer.writeUInt8(i, 0) 129 | 130 | this.r.toBuffer(32).copy(buffer, 1) 131 | this.s.toBuffer(32).copy(buffer, 33) 132 | 133 | return buffer 134 | } 135 | 136 | /** 137 | * @return {Buffer} 138 | */ 139 | ECSignature.prototype.toDER = function () { 140 | var r = new Buffer(this.r.toDERInteger()) 141 | var s = new Buffer(this.s.toDERInteger()) 142 | 143 | return bip66.encode(r, s) 144 | } 145 | 146 | /** 147 | * @param {number} hashType 148 | * @returns {Buffer} 149 | */ 150 | ECSignature.prototype.toScriptSignature = function (hashType) { 151 | var hashTypeMod = hashType & ~0x80 152 | if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) 153 | 154 | var hashTypeBuffer = new Buffer(1) 155 | hashTypeBuffer.writeUInt8(hashType, 0) 156 | 157 | return Buffer.concat([this.toDER(), hashTypeBuffer]) 158 | } 159 | 160 | module.exports = ECSignature 161 | -------------------------------------------------------------------------------- /lib/hdnode.js: -------------------------------------------------------------------------------- 1 | var base58check = require('bs58check') 2 | var bcrypto = require('./crypto') 3 | var createHmac = require('create-hmac') 4 | var typeforce = require('typeforce') 5 | var types = require('./types') 6 | var NETWORKS = require('./networks') 7 | 8 | var BigInteger = require('bigi') 9 | var ECPair = require('./ecpair') 10 | 11 | var ecurve = require('ecurve') 12 | var curve = ecurve.getCurveByName('secp256k1') 13 | 14 | /** 15 | * @constructor 16 | * @param {ECPair} keyPair 17 | * @param {Buffer} chainCode 18 | */ 19 | function HDNode (keyPair, chainCode) { 20 | typeforce(types.tuple('ECPair', types.Buffer256bit), arguments) 21 | 22 | if (!keyPair.compressed) throw new TypeError('BIP32 only allows compressed keyPairs') 23 | 24 | /** @type {ECPair} */ this.keyPair = keyPair 25 | /** @type {Buffer} */ this.chainCode = chainCode 26 | /** @type {number} */ this.depth = 0 27 | /** @type {number} */ this.index = 0 28 | /** @type {number} */ this.parentFingerprint = 0x00000000 29 | } 30 | 31 | HDNode.HIGHEST_BIT = 0x80000000 32 | HDNode.LENGTH = 78 33 | HDNode.MASTER_SECRET = new Buffer('Bitcoin seed') 34 | 35 | /** 36 | * @param {string|Buffer} seed 37 | * @param {Network} [network] 38 | * @returns {HDNode} 39 | */ 40 | HDNode.fromSeedBuffer = function (seed, network) { 41 | typeforce(types.tuple(types.Buffer, types.maybe(types.Network)), arguments) 42 | 43 | if (seed.length < 16) throw new TypeError('Seed should be at least 128 bits') 44 | if (seed.length > 64) throw new TypeError('Seed should be at most 512 bits') 45 | 46 | var I = createHmac('sha512', HDNode.MASTER_SECRET).update(seed).digest() 47 | var IL = I.slice(0, 32) 48 | var IR = I.slice(32) 49 | 50 | // In case IL is 0 or >= n, the master key is invalid 51 | // This is handled by the ECPair constructor 52 | var pIL = BigInteger.fromBuffer(IL) 53 | var keyPair = new ECPair(pIL, null, { 54 | network: network 55 | }) 56 | 57 | return new HDNode(keyPair, IR) 58 | } 59 | 60 | /** 61 | * @param {string} hex 62 | * @returns {HDNode} 63 | */ 64 | HDNode.fromSeedHex = function (hex, network) { 65 | return HDNode.fromSeedBuffer(new Buffer(hex, 'hex'), network) 66 | } 67 | 68 | /** 69 | * @param {Buffer|string} string 70 | * @returns {HDNode} 71 | */ 72 | HDNode.fromBase58 = function (string, networks) { 73 | var buffer = base58check.decode(string) 74 | if (buffer.length !== 78) throw new Error('Invalid buffer length') 75 | 76 | // 4 bytes: version bytes 77 | var version = buffer.readUInt32BE(0) 78 | var network 79 | 80 | // list of networks? 81 | if (Array.isArray(networks)) { 82 | network = networks.filter(function (network) { 83 | return version === network.bip32.private || 84 | version === network.bip32.public 85 | }).pop() 86 | 87 | if (!network) throw new Error('Unknown network version') 88 | 89 | // otherwise, assume a network object (or default to ark) 90 | } else { 91 | network = networks || NETWORKS.ark 92 | } 93 | 94 | if (version !== network.bip32.private && 95 | version !== network.bip32.public) throw new Error('Invalid network version') 96 | 97 | // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ... 98 | var depth = buffer[4] 99 | 100 | // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) 101 | var parentFingerprint = buffer.readUInt32BE(5) 102 | if (depth === 0) { 103 | if (parentFingerprint !== 0x00000000) throw new Error('Invalid parent fingerprint') 104 | } 105 | 106 | // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. 107 | // This is encoded in MSB order. (0x00000000 if master key) 108 | var index = buffer.readUInt32BE(9) 109 | if (depth === 0 && index !== 0) throw new Error('Invalid index') 110 | 111 | // 32 bytes: the chain code 112 | var chainCode = buffer.slice(13, 45) 113 | var keyPair 114 | 115 | // 33 bytes: private key data (0x00 + k) 116 | if (version === network.bip32.private) { 117 | if (buffer.readUInt8(45) !== 0x00) throw new Error('Invalid private key') 118 | 119 | var d = BigInteger.fromBuffer(buffer.slice(46, 78)) 120 | keyPair = new ECPair(d, null, { network: network }) 121 | 122 | // 33 bytes: public key data (0x02 + X or 0x03 + X) 123 | } else { 124 | var Q = ecurve.Point.decodeFrom(curve, buffer.slice(45, 78)) 125 | // Q.compressed is assumed, if somehow this assumption is broken, `new HDNode` will throw 126 | 127 | // Verify that the X coordinate in the public point corresponds to a point on the curve. 128 | // If not, the extended public key is invalid. 129 | curve.validate(Q) 130 | 131 | keyPair = new ECPair(null, Q, { network: network }) 132 | } 133 | 134 | var hd = new HDNode(keyPair, chainCode) 135 | hd.depth = depth 136 | hd.index = index 137 | hd.parentFingerprint = parentFingerprint 138 | 139 | return hd 140 | } 141 | 142 | /** 143 | * @returns {string} 144 | */ 145 | HDNode.prototype.getAddress = function () { 146 | return this.keyPair.getAddress() 147 | } 148 | 149 | /** 150 | * @returns {Buffer} 151 | */ 152 | HDNode.prototype.getIdentifier = function () { 153 | return bcrypto.hash160(this.keyPair.getPublicKeyBuffer()) 154 | } 155 | 156 | /** 157 | * @returns {Buffer} 158 | */ 159 | HDNode.prototype.getFingerprint = function () { 160 | return this.getIdentifier().slice(0, 4) 161 | } 162 | 163 | /** 164 | * @returns {Network} 165 | */ 166 | HDNode.prototype.getNetwork = function () { 167 | return this.keyPair.getNetwork() 168 | } 169 | 170 | /** 171 | * @returns {Buffer} 172 | */ 173 | HDNode.prototype.getPublicKeyBuffer = function () { 174 | return this.keyPair.getPublicKeyBuffer() 175 | } 176 | 177 | /** 178 | * @returns {HDNode} 179 | */ 180 | HDNode.prototype.neutered = function () { 181 | var neuteredKeyPair = new ECPair(null, this.keyPair.Q, { 182 | network: this.keyPair.network 183 | }) 184 | 185 | var neutered = new HDNode(neuteredKeyPair, this.chainCode) 186 | neutered.depth = this.depth 187 | neutered.index = this.index 188 | neutered.parentFingerprint = this.parentFingerprint 189 | 190 | return neutered 191 | } 192 | 193 | /** 194 | * @param {Buffer} hash 195 | * @returns {ECSignature} 196 | */ 197 | HDNode.prototype.sign = function (hash) { 198 | return this.keyPair.sign(hash) 199 | } 200 | 201 | /** 202 | * @param {Buffer} hash 203 | * @returns {boolean} 204 | */ 205 | HDNode.prototype.verify = function (hash, signature) { 206 | return this.keyPair.verify(hash, signature) 207 | } 208 | 209 | /** 210 | * @returns {string} 211 | */ 212 | HDNode.prototype.toBase58 = function () { 213 | // Version 214 | var network = this.keyPair.network 215 | var version = (!this.isNeutered()) ? network.bip32.private : network.bip32.public 216 | var buffer = new Buffer(78) 217 | 218 | // 4 bytes: version bytes 219 | buffer.writeUInt32BE(version, 0) 220 | 221 | // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, .... 222 | buffer.writeUInt8(this.depth, 4) 223 | 224 | // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) 225 | buffer.writeUInt32BE(this.parentFingerprint, 5) 226 | 227 | // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. 228 | // This is encoded in big endian. (0x00000000 if master key) 229 | buffer.writeUInt32BE(this.index, 9) 230 | 231 | // 32 bytes: the chain code 232 | this.chainCode.copy(buffer, 13) 233 | 234 | // 33 bytes: the public key or private key data 235 | if (!this.isNeutered()) { 236 | // 0x00 + k for private keys 237 | buffer.writeUInt8(0, 45) 238 | this.keyPair.d.toBuffer(32).copy(buffer, 46) 239 | 240 | // 33 bytes: the public key 241 | } else { 242 | // X9.62 encoding for public keys 243 | this.keyPair.getPublicKeyBuffer().copy(buffer, 45) 244 | } 245 | 246 | return base58check.encode(buffer) 247 | } 248 | 249 | /** 250 | * https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions 251 | * 252 | * @param {number} index UInt32 253 | * @returns {HDNode} 254 | */ 255 | HDNode.prototype.derive = function (index) { 256 | typeforce(types.UInt32, index) 257 | 258 | var isHardened = index >= HDNode.HIGHEST_BIT 259 | var data = new Buffer(37) 260 | 261 | // Hardened child 262 | if (isHardened) { 263 | if (this.isNeutered()) throw new TypeError('Could not derive hardened child key') 264 | 265 | // data = 0x00 || ser256(kpar) || ser32(index) 266 | data[0] = 0x00 267 | this.keyPair.d.toBuffer(32).copy(data, 1) 268 | data.writeUInt32BE(index, 33) 269 | 270 | // Normal child 271 | } else { 272 | // data = serP(point(kpar)) || ser32(index) 273 | // = serP(Kpar) || ser32(index) 274 | this.keyPair.getPublicKeyBuffer().copy(data, 0) 275 | data.writeUInt32BE(index, 33) 276 | } 277 | 278 | var I = createHmac('sha512', this.chainCode).update(data).digest() 279 | var IL = I.slice(0, 32) 280 | var IR = I.slice(32) 281 | 282 | var pIL = BigInteger.fromBuffer(IL) 283 | 284 | // In case parse256(IL) >= n, proceed with the next value for i 285 | if (pIL.compareTo(curve.n) >= 0) { 286 | return this.derive(index + 1) 287 | } 288 | 289 | // Private parent key -> private child key 290 | var derivedKeyPair 291 | if (!this.isNeutered()) { 292 | // ki = parse256(IL) + kpar (mod n) 293 | var ki = pIL.add(this.keyPair.d).mod(curve.n) 294 | 295 | // In case ki == 0, proceed with the next value for i 296 | if (ki.signum() === 0) { 297 | return this.derive(index + 1) 298 | } 299 | 300 | derivedKeyPair = new ECPair(ki, null, { 301 | network: this.keyPair.network 302 | }) 303 | 304 | // Public parent key -> public child key 305 | } else { 306 | // Ki = point(parse256(IL)) + Kpar 307 | // = G*IL + Kpar 308 | var Ki = curve.G.multiply(pIL).add(this.keyPair.Q) 309 | 310 | // In case Ki is the point at infinity, proceed with the next value for i 311 | if (curve.isInfinity(Ki)) { 312 | return this.derive(index + 1) 313 | } 314 | 315 | derivedKeyPair = new ECPair(null, Ki, { 316 | network: this.keyPair.network 317 | }) 318 | } 319 | 320 | var hd = new HDNode(derivedKeyPair, IR) 321 | hd.depth = this.depth + 1 322 | hd.index = index 323 | hd.parentFingerprint = this.getFingerprint().readUInt32BE(0) 324 | 325 | return hd 326 | } 327 | 328 | /** 329 | * @param {number} index 330 | * @returns {HDNode} 331 | */ 332 | HDNode.prototype.deriveHardened = function (index) { 333 | typeforce(types.UInt31, index) 334 | 335 | // Only derives hardened private keys by default 336 | return this.derive(index + HDNode.HIGHEST_BIT) 337 | } 338 | 339 | /** 340 | * Private `===` not neutered. 341 | * Public `===` neutered. 342 | * 343 | * @returns {boolean} 344 | */ 345 | HDNode.prototype.isNeutered = function () { 346 | return !(this.keyPair.d) 347 | } 348 | 349 | /** 350 | * @param {string} path 351 | * @returns {HDNode} 352 | */ 353 | HDNode.prototype.derivePath = function (path) { 354 | typeforce(types.BIP32Path, path) 355 | 356 | var splitPath = path.split('/') 357 | if (splitPath[0] === 'm') { 358 | if (this.parentFingerprint) { 359 | throw new Error('Not a master node') 360 | } 361 | 362 | splitPath = splitPath.slice(1) 363 | } 364 | 365 | return splitPath.reduce(function (prevHd, indexStr) { 366 | var index 367 | if (indexStr.slice(-1) === "'") { 368 | index = parseInt(indexStr.slice(0, -1), 10) 369 | return prevHd.deriveHardened(index) 370 | } else { 371 | index = parseInt(indexStr, 10) 372 | return prevHd.derive(index) 373 | } 374 | }, this) 375 | } 376 | 377 | 378 | module.exports = HDNode 379 | -------------------------------------------------------------------------------- /lib/networks.js: -------------------------------------------------------------------------------- 1 | /** @module networks */ 2 | 3 | module.exports = { 4 | /** @type {Network} */ 5 | ark: { 6 | messagePrefix: '\x18Ark Signed Message:\n', 7 | bip32: { 8 | public: 0x2bf4968, // base58 will have a prefix 'apub' 9 | private: 0x2bf4530 // base58Priv will have a prefix 'apriv' 10 | }, 11 | name: 'mainnet', 12 | nethash: '6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988', 13 | token: 'ARK', 14 | symbol: 'Ѧ', 15 | pubKeyHash: 0x17, // Addresses will begin with 'A' 16 | explorer: 'https://explorer.ark.io', 17 | wif: 0xaa, // Network prefix for wif generation 18 | activePeer: { 19 | ip: 'node1.arknet.cloud', 20 | port: 4001 21 | }, 22 | peers: [ 23 | { ip: '5.39.9.240', port: 4001 }, 24 | { ip: '5.39.9.241', port: 4001 }, 25 | { ip: '5.39.9.242', port: 4001 }, 26 | { ip: '5.39.9.243', port: 4001 }, 27 | { ip: '5.39.9.244', port: 4001 }, 28 | { ip: '5.39.9.250', port: 4001 }, 29 | { ip: '5.39.9.251', port: 4001 }, 30 | { ip: '5.39.9.252', port: 4001 }, 31 | { ip: '5.39.9.253', port: 4001 }, 32 | { ip: '5.39.9.254', port: 4001 }, 33 | { ip: '5.39.9.255', port: 4001 }, 34 | { ip: '5.39.53.48', port: 4001 }, 35 | { ip: '5.39.53.49', port: 4001 }, 36 | { ip: '5.39.53.50', port: 4001 }, 37 | { ip: '5.39.53.51', port: 4001 }, 38 | { ip: '5.39.53.52', port: 4001 }, 39 | { ip: '5.39.53.53', port: 4001 }, 40 | { ip: '5.39.53.54', port: 4001 }, 41 | { ip: '5.39.53.55', port: 4001 }, 42 | { ip: '37.59.129.160', port: 4001 }, 43 | { ip: '37.59.129.161', port: 4001 }, 44 | { ip: '37.59.129.162', port: 4001 }, 45 | { ip: '37.59.129.163', port: 4001 }, 46 | { ip: '37.59.129.164', port: 4001 }, 47 | { ip: '37.59.129.165', port: 4001 }, 48 | { ip: '37.59.129.166', port: 4001 }, 49 | { ip: '37.59.129.167', port: 4001 }, 50 | { ip: '37.59.129.168', port: 4001 }, 51 | { ip: '37.59.129.169', port: 4001 }, 52 | { ip: '37.59.129.170', port: 4001 }, 53 | { ip: '37.59.129.171', port: 4001 }, 54 | { ip: '37.59.129.172', port: 4001 }, 55 | { ip: '37.59.129.173', port: 4001 }, 56 | { ip: '37.59.129.174', port: 4001 }, 57 | { ip: '37.59.129.175', port: 4001 }, 58 | { ip: '193.70.72.80', port: 4001 }, 59 | { ip: '193.70.72.81', port: 4001 }, 60 | { ip: '193.70.72.82', port: 4001 }, 61 | { ip: '193.70.72.83', port: 4001 }, 62 | { ip: '193.70.72.84', port: 4001 }, 63 | { ip: '193.70.72.85', port: 4001 }, 64 | { ip: '193.70.72.86', port: 4001 }, 65 | { ip: '193.70.72.87', port: 4001 }, 66 | { ip: '193.70.72.88', port: 4001 }, 67 | { ip: '193.70.72.89', port: 4001 }, 68 | { ip: '193.70.72.90', port: 4001 } 69 | ], 70 | }, 71 | /** @type {Network} */ 72 | testnet: { 73 | messagePrefix: '\x18Ark Testnet Signed Message:\n', 74 | bip32: { 75 | public: 0x043587cf, 76 | private: 0x04358394 77 | }, 78 | name: 'devnet', 79 | nethash: '578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23', 80 | token: 'DARK', 81 | symbol: 'DѦ', 82 | pubKeyHash: 0x52, // Addresses will begin with 'a' 83 | explorer: 'https://dexplorer.ark.io', 84 | wif: 0xba, // Network prefix for wif generation 85 | activePeer: { 86 | ip: '104.238.165.129', 87 | port: 4002 88 | }, 89 | peers: [ 90 | { ip: '45.63.74.5', port: 4002 }, 91 | { ip: '167.114.29.50', port: 4002 }, 92 | { ip: '167.114.29.45', port: 4002 }, 93 | { ip: '167.114.43.35', port: 4002 }, 94 | { ip: '104.129.43.144', port: 4002 }, 95 | { ip: '167.114.29.58', port: 4002 }, 96 | { ip: '173.253.129.20', port: 4002 }, 97 | { ip: '62.113.246.106', port: 4002 }, 98 | { ip: '45.76.218.117', port: 4002 }, 99 | { ip: '158.69.193.185', port: 4002 }, 100 | { ip: '144.217.194.188', port: 4002 }, 101 | { ip: '167.114.29.57', port: 4002 }, 102 | { ip: '167.114.29.40', port: 4002 }, 103 | { ip: '167.114.29.63', port: 4002 }, 104 | { ip: '185.177.21.65', port: 4002 }, 105 | { ip: '167.114.29.62', port: 4002 }, 106 | { ip: '167.114.29.54', port: 4002 }, 107 | { ip: '45.76.139.131', port: 4002 }, 108 | { ip: '138.68.29.19', port: 4002 }, 109 | { ip: '216.174.63.138', port: 4002 }, 110 | { ip: '213.32.9.98', port: 4002 }, 111 | { ip: '137.74.168.191', port: 4002 }, 112 | { ip: '104.236.122.76', port: 4002 }, 113 | { ip: '167.114.29.47', port: 4002 }, 114 | { ip: '167.114.29.37', port: 4002 }, 115 | { ip: '45.63.42.140', port: 4002 }, 116 | { ip: '45.33.86.168', port: 4002 }, 117 | { ip: '108.61.209.209', port: 4002 }, 118 | { ip: '167.114.29.33', port: 4002 }, 119 | { ip: '107.170.196.22', port: 4002 }, 120 | { ip: '178.62.102.209', port: 4002 }, 121 | { ip: '145.239.88.3', port: 4002 }, 122 | { ip: '167.114.43.37', port: 4002 }, 123 | { ip: '145.239.88.2', port: 4002 }, 124 | { ip: '217.182.70.37', port: 4002 }, 125 | { ip: '185.5.54.0', port: 4002 }, 126 | { ip: '45.76.174.177', port: 4002 }, 127 | { ip: '216.174.63.137', port: 4002 }, 128 | { ip: '167.114.29.59', port: 4002 }, 129 | { ip: '104.238.165.129', port: 4002 }, 130 | { ip: '217.182.79.88', port: 4002 }, 131 | { ip: '167.114.29.61', port: 4002 }, 132 | { ip: '51.255.198.137', port: 4002 }, 133 | { ip: '167.114.29.38', port: 4002 }, 134 | { ip: '167.114.43.33', port: 4002 }, 135 | { ip: '45.77.117.174', port: 4002 }, 136 | { ip: '213.32.9.97', port: 4002 }, 137 | { ip: '193.70.1.222', port: 4002 }, 138 | { ip: '45.76.39.61', port: 4002 }, 139 | { ip: '204.10.184.45', port: 4002 }, 140 | { ip: '45.76.93.4', port: 4002 }, 141 | { ip: '167.114.29.46', port: 4002 }, 142 | { ip: '91.121.1.174', port: 4002 }, 143 | { ip: '165.227.59.129', port: 4002 }, 144 | { ip: '167.114.29.44', port: 4002 }, 145 | { ip: '139.99.156.231', port: 4002 }, 146 | { ip: '164.8.251.90', port: 4002 }, 147 | { ip: '167.114.29.56', port: 4002 }, 148 | { ip: '108.61.169.37', port: 4002 }, 149 | { ip: '104.238.189.78', port: 4002 }, 150 | { ip: '173.254.228.67', port: 4002 }, 151 | { ip: '204.10.184.46', port: 4002 }, 152 | { ip: '66.70.207.89', port: 4002 }, 153 | { ip: '172.110.20.39', port: 4002 }, 154 | { ip: '137.74.47.141', port: 4002 }, 155 | { ip: '167.114.29.41', port: 4002 }, 156 | { ip: '167.114.29.52', port: 4002 }, 157 | { ip: '167.114.43.41', port: 4002 }, 158 | { ip: '51.254.103.18', port: 4002 }, 159 | { ip: '167.114.43.47', port: 4002 }, 160 | { ip: '45.76.136.154', port: 4002 }, 161 | { ip: '167.114.29.39', port: 4002 }, 162 | { ip: '167.114.29.34', port: 4002 }, 163 | { ip: '138.197.165.165', port: 4002 }, 164 | { ip: '167.114.43.34', port: 4002 }, 165 | { ip: '145.239.88.101', port: 4002 }, 166 | { ip: '45.76.245.155', port: 4002 }, 167 | { ip: '167.114.43.43', port: 4002 }, 168 | { ip: '91.134.115.30', port: 4002 }, 169 | { ip: '46.231.204.207', port: 4002 }, 170 | { ip: '167.114.43.46', port: 4002 }, 171 | { ip: '138.197.29.215', port: 4002 }, 172 | { ip: '104.238.177.174', port: 4002 }, 173 | { ip: '109.154.47.137', port: 4002 }, 174 | { ip: '104.207.138.122', port: 4002 }, 175 | { ip: '138.68.21.196', port: 4002 }, 176 | { ip: '167.114.29.36', port: 4002 }, 177 | { ip: '165.227.150.45', port: 4002 }, 178 | { ip: '37.59.70.165', port: 4002 }, 179 | { ip: '94.177.218.138', port: 4002 }, 180 | { ip: '167.114.29.55', port: 4002 }, 181 | { ip: '51.254.201.227', port: 4002 }, 182 | { ip: '167.114.43.48', port: 4002 }, 183 | { ip: '104.154.82.122', port: 4002 }, 184 | { ip: '217.182.78.226', port: 4002 }, 185 | { ip: '207.154.220.137', port: 4002 }, 186 | { ip: '195.181.219.91', port: 4002 }, 187 | { ip: '192.155.91.248', port: 4002 }, 188 | { ip: '167.114.29.53', port: 4002 }, 189 | { ip: '43.224.34.53', port: 4002 }, 190 | { ip: '167.114.43.40', port: 4002 }, 191 | { ip: '138.197.206.43', port: 4002 } 192 | ] 193 | }, 194 | /** @type {Network} */ 195 | bitcoin: { 196 | messagePrefix: '\x18Bitcoin Signed Message:\n', 197 | bip32: { 198 | public: 0x0488b21e, 199 | private: 0x0488ade4 200 | }, 201 | pubKeyHash: 0x00, 202 | wif: 0x80 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /lib/time/slots.js: -------------------------------------------------------------------------------- 1 | /** @module slots */ 2 | 3 | /** 4 | * @param {Date} [time] 5 | * @returns {number} 6 | */ 7 | function getEpochTime(time) { 8 | if (time === undefined) { 9 | time = (new Date()).getTime(); 10 | } 11 | var d = beginEpochTime(); 12 | var t = d.getTime(); 13 | return Math.floor((time - t) / 1000); 14 | } 15 | 16 | /** 17 | * @returns {Date} 18 | */ 19 | function beginEpochTime() { 20 | var d = new Date(Date.UTC(2017,2,21,13,0,0,0)) 21 | 22 | return d; 23 | } 24 | 25 | var interval = 8, 26 | delegates = 51; 27 | 28 | /** 29 | * @param {Date} [time] 30 | * @returns {number} 31 | */ 32 | function getTime(time) { 33 | return getEpochTime(time); 34 | } 35 | 36 | /** 37 | * @param {number} [epochTime] 38 | * @returns {number} 39 | */ 40 | function getRealTime(epochTime) { 41 | if (epochTime === undefined) { 42 | epochTime = getTime() 43 | } 44 | var d = beginEpochTime(); 45 | var t = Math.floor(d.getTime() / 1000) * 1000; 46 | return t + epochTime * 1000; 47 | } 48 | 49 | /** 50 | * @param {number} [epochTime] 51 | * @returns {number} an integer 52 | */ 53 | function getSlotNumber(epochTime) { 54 | if (epochTime === undefined) { 55 | epochTime = getTime() 56 | } 57 | 58 | return Math.floor(epochTime / interval); 59 | } 60 | 61 | /** 62 | * @param {number} slot 63 | * @returns {number} 64 | */ 65 | function getSlotTime(slot) { 66 | return slot * interval; 67 | } 68 | 69 | /** 70 | * @returns {number} 71 | */ 72 | function getNextSlot() { 73 | var slot = getSlotNumber(); 74 | 75 | return slot + 1; 76 | } 77 | 78 | /** 79 | * @param {number} nextSlot 80 | * @returns {number} 81 | */ 82 | function getLastSlot(nextSlot) { 83 | return nextSlot + delegates; 84 | } 85 | 86 | module.exports = { 87 | interval: interval, 88 | delegates: delegates, 89 | getTime: getTime, 90 | getRealTime: getRealTime, 91 | getSlotNumber: getSlotNumber, 92 | getSlotTime: getSlotTime, 93 | getNextSlot: getNextSlot, 94 | getLastSlot: getLastSlot 95 | } 96 | -------------------------------------------------------------------------------- /lib/transactions/crypto.js: -------------------------------------------------------------------------------- 1 | /** @module crypto */ 2 | 3 | var crypto = require("crypto"); 4 | var crypto_utils = require("../crypto.js"); 5 | var ECPair = require("../ecpair.js"); 6 | var ECSignature = require("../ecsignature.js"); 7 | /** @type {Object.} */ 8 | var networks = require("../networks.js") 9 | 10 | var bs58check = require('bs58check') 11 | 12 | if (typeof Buffer === "undefined") { 13 | Buffer = require("buffer/").Buffer; 14 | } 15 | 16 | var ByteBuffer = require("bytebuffer"); 17 | var bignum = require("browserify-bignum"); 18 | 19 | 20 | var fixedPoint = Math.pow(10, 8); 21 | // default is ark mainnet 22 | var networkVersion = 0x17; 23 | 24 | /** 25 | * @static 26 | * @param {*} obj 27 | * @returns {boolean} 28 | */ 29 | function isECPair(obj) { 30 | return obj instanceof ECPair; 31 | } 32 | 33 | /** 34 | * @static 35 | * @param {ECSignature} signature 36 | * @returns {Uint8Array} 37 | */ 38 | function getSignatureBytes(signature) { 39 | var bb = new ByteBuffer(33, true); 40 | var publicKeyBuffer = new Buffer(signature.publicKey, "hex"); 41 | 42 | for (var i = 0; i < publicKeyBuffer.length; i++) { 43 | bb.writeByte(publicKeyBuffer[i]); 44 | } 45 | 46 | bb.flip(); 47 | return new Uint8Array(bb.toArrayBuffer()); 48 | } 49 | 50 | /** 51 | * @static 52 | * @param {Transaction} transaction 53 | * @param {boolean} [skipSignature=false] 54 | * @param {boolean} [skipSecondSignature=false] 55 | * @returns {Buffer} 56 | */ 57 | function getBytes(transaction, skipSignature, skipSecondSignature) { 58 | var assetSize = 0, 59 | assetBytes = null; 60 | 61 | switch (transaction.type) { 62 | case 1: // Signature 63 | assetBytes = getSignatureBytes(transaction.asset.signature); 64 | assetSize = assetBytes.length; 65 | break; 66 | 67 | case 2: // Delegate 68 | assetBytes = new Buffer(transaction.asset.delegate.username, "utf8"); 69 | assetSize = assetBytes.length; 70 | break; 71 | 72 | case 3: // Vote 73 | if (transaction.asset.votes !== null) { 74 | assetBytes = new Buffer(transaction.asset.votes.join(""), "utf8"); 75 | assetSize = assetBytes.length; 76 | } 77 | break; 78 | 79 | case 4: // Multi-Signature 80 | var keysgroupBuffer = new Buffer(transaction.asset.multisignature.keysgroup.join(""), "utf8"); 81 | var bb = new ByteBuffer(1 + 1 + keysgroupBuffer.length, true); 82 | 83 | bb.writeByte(transaction.asset.multisignature.min); 84 | bb.writeByte(transaction.asset.multisignature.lifetime); 85 | 86 | for (var i = 0; i < keysgroupBuffer.length; i++) { 87 | bb.writeByte(keysgroupBuffer[i]); 88 | } 89 | 90 | bb.flip(); 91 | 92 | assetBytes = bb.toBuffer(); 93 | assetSize = assetBytes.length; 94 | break; 95 | 96 | } 97 | 98 | var bb = new ByteBuffer(1 + 4 + 32 + 8 + 8 + 21 + 64 + 64 + 64 + assetSize, true); 99 | bb.writeByte(transaction.type); 100 | bb.writeInt(transaction.timestamp); 101 | 102 | var senderPublicKeyBuffer = new Buffer(transaction.senderPublicKey, "hex"); 103 | for (var i = 0; i < senderPublicKeyBuffer.length; i++) { 104 | bb.writeByte(senderPublicKeyBuffer[i]); 105 | } 106 | 107 | if (transaction.recipientId) { 108 | var recipient = bs58check.decode(transaction.recipientId); 109 | for (var i = 0; i < recipient.length; i++) { 110 | bb.writeByte(recipient[i]); 111 | } 112 | } else { 113 | for (var i = 0; i < 21; i++) { 114 | bb.writeByte(0); 115 | } 116 | } 117 | 118 | if(transaction.vendorFieldHex){ 119 | var vf = new Buffer(transaction.vendorFieldHex,"hex"); 120 | var fillstart=vf.length; 121 | for (i = 0; i < fillstart; i++) { 122 | bb.writeByte(vf[i]); 123 | } 124 | for (i = fillstart; i < 64; i++) { 125 | bb.writeByte(0); 126 | } 127 | } 128 | else if (transaction.vendorField) { 129 | var vf = new Buffer(transaction.vendorField); 130 | var fillstart=vf.length; 131 | for (i = 0; i < fillstart; i++) { 132 | bb.writeByte(vf[i]); 133 | } 134 | for (i = fillstart; i < 64; i++) { 135 | bb.writeByte(0); 136 | } 137 | } else { 138 | for (i = 0; i < 64; i++) { 139 | bb.writeByte(0); 140 | } 141 | } 142 | 143 | bb.writeLong(transaction.amount); 144 | 145 | bb.writeLong(transaction.fee); 146 | 147 | if (assetSize > 0) { 148 | for (var i = 0; i < assetSize; i++) { 149 | bb.writeByte(assetBytes[i]); 150 | } 151 | } 152 | 153 | if (!skipSignature && transaction.signature) { 154 | var signatureBuffer = new Buffer(transaction.signature, "hex"); 155 | for (var i = 0; i < signatureBuffer.length; i++) { 156 | bb.writeByte(signatureBuffer[i]); 157 | } 158 | } 159 | 160 | if (!skipSecondSignature && transaction.signSignature) { 161 | var signSignatureBuffer = new Buffer(transaction.signSignature, "hex"); 162 | for (var i = 0; i < signSignatureBuffer.length; i++) { 163 | bb.writeByte(signSignatureBuffer[i]); 164 | } 165 | } 166 | 167 | bb.flip(); 168 | var arrayBuffer = new Uint8Array(bb.toArrayBuffer()); 169 | var buffer = []; 170 | 171 | for (var i = 0; i < arrayBuffer.length; i++) { 172 | buffer[i] = arrayBuffer[i]; 173 | } 174 | 175 | return new Buffer(buffer); 176 | } 177 | 178 | /** 179 | * @static 180 | * @param {string} hexString 181 | * @returns {Transaction} 182 | */ 183 | function fromBytes(hexString){ 184 | var tx={}; 185 | var buf = new Buffer(hexString, "hex"); 186 | tx.type = buf.readInt8(0) & 0xff; 187 | tx.timestamp = buf.readUInt32LE(1); 188 | tx.senderPublicKey = hexString.substring(10,10+33*2); 189 | tx.amount = buf.readUInt32LE(38+21+64); 190 | tx.fee = buf.readUInt32LE(38+21+64+8); 191 | tx.vendorFieldHex = hexString.substring(76+42,76+42+128); 192 | tx.recipientId = bs58check.encode(buf.slice(38,38+21)); 193 | if(tx.type == 0){ // transfer 194 | parseSignatures(hexString, tx, 76+42+128+32); 195 | } 196 | else if(tx.type == 1){ // second signature registration 197 | delete tx.recipientId; 198 | tx.asset = { 199 | signature : { 200 | publicKey : hexString.substring(76+42+128+32,76+42+128+32+66) 201 | } 202 | } 203 | parseSignatures(hexString, tx, 76+42+128+32+66); 204 | } 205 | else if(tx.type == 2){ // delegate registration 206 | delete tx.recipientId; 207 | // Impossible to assess size of delegate asset, trying to grab signature and derive delegate asset 208 | var offset = findAndParseSignatures(hexString, tx); 209 | 210 | tx.asset = { 211 | delegate: { 212 | username: new Buffer(hexString.substring(76+42+128+32,hexString.length-offset),"hex").toString("utf8") 213 | } 214 | }; 215 | } 216 | else if(tx.type == 3){ // vote 217 | // Impossible to assess size of vote asset, trying to grab signature and derive vote asset 218 | var offset = findAndParseSignatures(hexString, tx); 219 | tx.asset = { 220 | votes: new Buffer(hexString.substring(76+42+128+32,hexString.length-offset),"hex").toString("utf8").split(",") 221 | }; 222 | } 223 | else if(tx.type == 4){ // multisignature creation 224 | delete tx.recipientId; 225 | var offset = findAndParseSignatures(hexString, tx); 226 | var buffer = new Buffer(hexString.substring(76+42+128+32,hexString.length-offset),"hex") 227 | tx.asset = { 228 | multisignature: {} 229 | } 230 | tx.asset.multisignature.min = buffer.readInt8(0) & 0xff; 231 | tx.asset.multisignature.lifetime = buffer.readInt8(1) & 0xff; 232 | tx.asset.multisignature.keysgroup = []; 233 | var index = 0; 234 | while(index + 2 < buffer.length){ 235 | var key = buffer.slice(index+2,index+67+2).toString("utf8"); 236 | tx.asset.multisignature.keysgroup.push(key); 237 | index = index + 67; 238 | } 239 | } 240 | else if(tx.type == 5){ // ipfs 241 | delete tx.recipientId; 242 | parseSignatures(hexString, tx, 76+42+128+32); 243 | } 244 | 245 | return tx; 246 | } 247 | 248 | /** 249 | * @static 250 | * @param {string} hexString 251 | * @param {Transaction} tx 252 | * @returns {number} 253 | */ 254 | function findAndParseSignatures(hexString, tx){ 255 | var signature1 = new Buffer(hexString.substring(hexString.length-146), "hex"); 256 | var signature2 = null; 257 | var found = false; 258 | var offset = 0; 259 | while(!found && signature1.length > 8){ 260 | if(signature1[0] != 0x30){ 261 | signature1 = signature1.slice(1); 262 | } 263 | else try { 264 | ECSignature.fromDER(signature1,"hex"); 265 | found = true; 266 | } catch(error){ 267 | signature1 = signature1.slice(1); 268 | } 269 | } 270 | if(!found){ 271 | offset = 0; 272 | signature1 = null; 273 | } 274 | else { 275 | found = false; 276 | offset = signature1.length*2; 277 | var signature2 = new Buffer(hexString.substring(hexString.length-offset-146, hexString.length-offset), "hex"); 278 | while(!found && signature2.length > 8){ 279 | if(signature2[0] != 0x30){ 280 | signature2 = signature2.slice(1); 281 | } 282 | else try { 283 | ECSignature.fromDER(signature2,"hex"); 284 | found = true; 285 | } catch(error){ 286 | signature2 = signature2.slice(1); 287 | } 288 | } 289 | if(!found){ 290 | signature2 = null; 291 | tx.signature = signature1.toString("hex"); 292 | offset = tx.signature.length; 293 | } 294 | else if(signature2){ 295 | tx.signSignature = signature1.toString("hex"); 296 | tx.signature = signature2.toString("hex"); 297 | offset = tx.signature.length+tx.signSignature.length; 298 | } 299 | } 300 | return offset; 301 | } 302 | 303 | /** 304 | * @static 305 | * @param {string} hexString 306 | * @param {Transaction} tx 307 | * @param {number} startOffset 308 | */ 309 | function parseSignatures(hexString, tx, startOffset){ 310 | tx.signature = hexString.substring(startOffset); 311 | if(tx.signature.length == 0) delete tx.signature; 312 | else { 313 | var length = parseInt("0x" + tx.signature.substring(2,4), 16) + 2; 314 | tx.signature = hexString.substring(startOffset, startOffset + length*2); 315 | tx.signSignature = hexString.substring(startOffset + length*2); 316 | if(tx.signSignature.length == 0) delete tx.signSignature; 317 | } 318 | } 319 | 320 | /** 321 | * @static 322 | * @param {Transaction} transaction 323 | * @returns {string} 324 | */ 325 | function getId(transaction) { 326 | return crypto.createHash("sha256").update(getBytes(transaction)).digest().toString("hex"); 327 | } 328 | 329 | /** 330 | * @static 331 | * @param {Transaction} transaction 332 | * @param {boolean} [skipSignature=false] 333 | * @param {boolean} [skipSecondSignature=false] 334 | * @returns {Buffer} 335 | */ 336 | function getHash(transaction, skipSignature, skipSecondSignature) { 337 | return crypto.createHash("sha256").update(getBytes(transaction, skipSignature, skipSecondSignature)).digest(); 338 | } 339 | 340 | /** 341 | * @static 342 | * @param {Transaction} transaction 343 | * @returns {number} 344 | */ 345 | function getFee(transaction) { 346 | switch (transaction.type) { 347 | case 0: // Normal 348 | return 0.1 * fixedPoint; 349 | break; 350 | 351 | case 1: // Signature 352 | return 100 * fixedPoint; 353 | break; 354 | 355 | case 2: // Delegate 356 | return 10000 * fixedPoint; 357 | break; 358 | 359 | case 3: // Vote 360 | return 1 * fixedPoint; 361 | break; 362 | } 363 | } 364 | 365 | /** 366 | * @static 367 | * @param {Transaction} transaction 368 | * @param {ECPair} keys 369 | * @returns {ECSignature} 370 | */ 371 | function sign(transaction, keys) { 372 | var hash = getHash(transaction, true, true); 373 | 374 | var signature = keys.sign(hash).toDER().toString("hex"); 375 | 376 | if (!transaction.signature) { 377 | transaction.signature = signature; 378 | } 379 | return signature; 380 | 381 | } 382 | 383 | /** 384 | * @static 385 | * @param {Transaction} transaction 386 | * @param {ECPair} keys 387 | */ 388 | function secondSign(transaction, keys) { 389 | var hash = getHash(transaction, false, true); 390 | 391 | var signature = keys.sign(hash).toDER().toString("hex"); 392 | 393 | if (!transaction.signSignature) { 394 | transaction.signSignature = signature; 395 | } 396 | return signature; 397 | } 398 | 399 | /** 400 | * @static 401 | * @param {Transaction} transaction 402 | * @param {Network} [network=networks.ark] 403 | */ 404 | function verify(transaction, network) { 405 | network = network || networks.ark; 406 | 407 | var hash = getHash(transaction, true, true); 408 | 409 | var signatureBuffer = new Buffer(transaction.signature, "hex"); 410 | var senderPublicKeyBuffer = new Buffer(transaction.senderPublicKey, "hex"); 411 | var ecpair = ECPair.fromPublicKeyBuffer(senderPublicKeyBuffer, network); 412 | var ecsignature = ECSignature.fromDER(signatureBuffer); 413 | var res = ecpair.verify(hash, ecsignature); 414 | 415 | return res; 416 | } 417 | 418 | /** 419 | * @static 420 | * @param {Transaction} transaction 421 | * @param {string} publicKey 422 | * @param {Network} [network] 423 | */ 424 | function verifySecondSignature(transaction, publicKey, network) { 425 | network = network || networks.ark; 426 | 427 | var hash = getHash(transaction, false, true); 428 | 429 | var signSignatureBuffer = new Buffer(transaction.signSignature, "hex"); 430 | var publicKeyBuffer = new Buffer(publicKey, "hex"); 431 | var ecpair = ECPair.fromPublicKeyBuffer(publicKeyBuffer, network); 432 | var ecsignature = ECSignature.fromDER(signSignatureBuffer); 433 | var res = ecpair.verify(hash, ecsignature); 434 | 435 | return res; 436 | } 437 | 438 | /** 439 | * @static 440 | * @param {string} secret 441 | * @param {Network} [network] 442 | * @returns {ECPair} 443 | */ 444 | function getKeys(secret, network) { 445 | var ecpair = ECPair.fromSeed(secret, network || networks.ark); 446 | 447 | ecpair.publicKey = ecpair.getPublicKeyBuffer().toString("hex"); 448 | ecpair.privateKey = ''; 449 | 450 | return ecpair; 451 | } 452 | 453 | /** 454 | * @static 455 | * @param {string} publicKey 456 | * @param {number} [version] 457 | * @returns {string} 458 | */ 459 | function getAddress(publicKey, version){ 460 | var pubKeyRegex = /^[0-9A-Fa-f]{66}$/; 461 | if(!version){ 462 | version = networkVersion; 463 | } 464 | if (!pubKeyRegex.test(publicKey)) 465 | throw "publicKey is invalid"; 466 | var buffer = crypto_utils.ripemd160(new Buffer(publicKey, "hex")); 467 | var payload = new Buffer(21); 468 | payload.writeUInt8(version, 0); 469 | buffer.copy(payload, 1); 470 | 471 | return bs58check.encode(payload); 472 | } 473 | 474 | /** 475 | * @static 476 | * @param {number} version 477 | */ 478 | function setNetworkVersion(version){ 479 | networkVersion = version; 480 | } 481 | 482 | /** 483 | * @static 484 | * @returns {number} 485 | */ 486 | function getNetworkVersion(){ 487 | return networkVersion; 488 | } 489 | 490 | /** 491 | * @static 492 | * @param {string} address 493 | * @param {number} [version] 494 | * @returns {boolean} 495 | */ 496 | function validateAddress(address, version){ 497 | if(!version){ 498 | version = networkVersion; 499 | } 500 | try { 501 | var decode = bs58check.decode(address); 502 | return decode[0] == version; 503 | } catch(e){ 504 | return false; 505 | } 506 | } 507 | 508 | module.exports = { 509 | getBytes: getBytes, 510 | fromBytes: fromBytes, 511 | getHash: getHash, 512 | getId: getId, 513 | getFee: getFee, 514 | sign: sign, 515 | secondSign: secondSign, 516 | getKeys: getKeys, 517 | getAddress: getAddress, 518 | validateAddress: validateAddress, 519 | verify: verify, 520 | verifySecondSignature: verifySecondSignature, 521 | fixedPoint: fixedPoint, 522 | setNetworkVersion: setNetworkVersion, 523 | getNetworkVersion: getNetworkVersion, 524 | isECPair: isECPair 525 | } 526 | -------------------------------------------------------------------------------- /lib/transactions/delegate.js: -------------------------------------------------------------------------------- 1 | /** @module delegate */ 2 | 3 | var crypto = require("./crypto.js"), 4 | constants = require("../constants.js"), 5 | slots = require("../time/slots.js"); 6 | 7 | /** 8 | * @static 9 | * @param {string} secret 10 | * @param {string} username 11 | * @param {ECPair|string} [secondSecret] 12 | * @param {number} [feeOverride] 13 | */ 14 | function createDelegate(secret, username, secondSecret, feeOverride) { 15 | if (typeof secret === 'undefined' || !username) return false; 16 | 17 | var keys = secret; 18 | 19 | if (!crypto.isECPair(secret)) { 20 | keys = crypto.getKeys(secret); 21 | } 22 | 23 | if (!keys.publicKey) { 24 | throw new Error("Invalid public key"); 25 | } 26 | 27 | if (feeOverride && !Number.isInteger(feeOverride)) { 28 | throw new Error('Not a valid fee') 29 | } 30 | 31 | var transaction = { 32 | type: 2, 33 | amount: 0, 34 | fee: feeOverride || constants.fees.delegate, 35 | recipientId: null, 36 | senderPublicKey: keys.publicKey, 37 | timestamp: slots.getTime(), 38 | asset: { 39 | delegate: { 40 | username: username, 41 | publicKey: keys.publicKey 42 | } 43 | } 44 | }; 45 | 46 | crypto.sign(transaction, keys); 47 | 48 | if (secondSecret) { 49 | var secondKeys = secondSecret; 50 | if (!crypto.isECPair(secondSecret)) { 51 | secondKeys = crypto.getKeys(secondSecret); 52 | } 53 | crypto.secondSign(transaction, secondKeys); 54 | } 55 | 56 | transaction.id = crypto.getId(transaction); 57 | return transaction; 58 | } 59 | 60 | module.exports = { 61 | createDelegate : createDelegate 62 | } 63 | -------------------------------------------------------------------------------- /lib/transactions/ipfs.js: -------------------------------------------------------------------------------- 1 | /** @module ipfs */ 2 | 3 | var crypto = require("./crypto.js"), 4 | constants = require("../constants.js"), 5 | slots = require("../time/slots.js"); 6 | 7 | /** 8 | * @static 9 | * @param {string} ipfshash 10 | * @param {ECPair|string} secret 11 | * @param {ECPair|string} [secondSecret] 12 | * @param {number} [feeOverride] 13 | */ 14 | function createHashRegistration(ipfshash, secret, secondSecret, feeOverride) { 15 | if (!ipfshash || typeof secret === 'undefined') return false; 16 | 17 | if (feeOverride && !Number.isInteger(feeOverride)) { 18 | throw new Error('Not a valid fee') 19 | } 20 | 21 | var transaction = { 22 | type: 5, 23 | amount:0, 24 | fee: feeOverride || constants.fees.send, 25 | timestamp: slots.getTime(), 26 | asset: {} 27 | }; 28 | 29 | transaction.vendorFieldHex = new Buffer(ipfshash,"utf8").toString("hex"); 30 | //filling with 0x00 31 | while(transaction.vendorFieldHex.length < 128){ 32 | transaction.vendorFieldHex = "00"+transaction.vendorFieldHex; 33 | } 34 | 35 | var keys = secret; 36 | 37 | if (!crypto.isECPair(secret)) { 38 | keys = crypto.getKeys(secret); 39 | } 40 | 41 | if (!keys.publicKey) { 42 | throw new Error("Invalid public key"); 43 | } 44 | 45 | transaction.senderPublicKey = keys.publicKey; 46 | 47 | crypto.sign(transaction, keys); 48 | 49 | if (secondSecret) { 50 | var secondKeys = secondSecret; 51 | if (!crypto.isECPair(secondSecret)) { 52 | secondKeys = crypto.getKeys(secondSecret); 53 | } 54 | crypto.secondSign(transaction, secondKeys); 55 | } 56 | 57 | transaction.id = crypto.getId(transaction); 58 | return transaction; 59 | } 60 | 61 | module.exports = { 62 | createHashRegistration: createHashRegistration 63 | } 64 | -------------------------------------------------------------------------------- /lib/transactions/multisignature.js: -------------------------------------------------------------------------------- 1 | /** @module multisignature */ 2 | 3 | var crypto = require("./crypto.js"), 4 | constants = require("../constants.js"), 5 | slots = require("../time/slots.js"); 6 | 7 | /** 8 | * @static 9 | * @param {Transaction} trs 10 | * @param {string} secret 11 | */ 12 | function signTransaction(trs, secret) { 13 | if (!trs || !secret) return false; 14 | 15 | var keys = secret; 16 | 17 | if (!crypto.isECPair(secret)) { 18 | keys = crypto.getKeys(secret); 19 | } 20 | 21 | var signature = crypto.sign(trs, keys); 22 | 23 | return signature; 24 | } 25 | 26 | /** 27 | * @static 28 | * @param {ECPair|string} secret 29 | * @param {ECPair|string} secondSecret 30 | * @param {*} keysgroup 31 | * @param {*} lifetime 32 | * @param {*} min 33 | * @param {number} [feeOverride] 34 | */ 35 | function createMultisignature(secret, secondSecret, keysgroup, lifetime, min, feeOverride) { 36 | if (typeof secret === 'undefined' || !keysgroup || !lifetime || !min) return false; 37 | 38 | var keys = secret; 39 | 40 | if (!crypto.isECPair(secret)) { 41 | keys = crypto.getKeys(secret); 42 | } 43 | 44 | if (feeOverride && !Number.isInteger(feeOverride)) { 45 | throw new Error('Not a valid fee') 46 | } 47 | 48 | var transaction = { 49 | type: 4, 50 | amount: 0, 51 | fee: (keysgroup.length + 1) * (feeOverride || constants.fees.multisignature), 52 | recipientId: null, 53 | senderPublicKey: keys.publicKey, 54 | timestamp: slots.getTime(), 55 | asset: { 56 | multisignature: { 57 | min: min, 58 | lifetime: lifetime, 59 | keysgroup: keysgroup 60 | } 61 | } 62 | }; 63 | 64 | crypto.sign(transaction, keys); 65 | 66 | if (secondSecret) { 67 | var secondKeys = secondSecret; 68 | if (!crypto.isECPair(secondSecret)) { 69 | secondKeys = crypto.getKeys(secondSecret); 70 | } 71 | crypto.secondSign(transaction, secondKeys); 72 | } 73 | 74 | transaction.id = crypto.getId(transaction); 75 | return transaction; 76 | } 77 | 78 | module.exports = { 79 | createMultisignature : createMultisignature, 80 | signTransaction: signTransaction 81 | } 82 | -------------------------------------------------------------------------------- /lib/transactions/signature.js: -------------------------------------------------------------------------------- 1 | /** @module signature */ 2 | 3 | var crypto = require("./crypto.js"), 4 | constants = require("../constants.js"), 5 | slots = require("../time/slots.js"); 6 | 7 | /** 8 | * @param {string} secondSecret 9 | * @returns {{publicKey: ECPair}} 10 | */ 11 | function newSignature(secondSecret) { 12 | var keys = crypto.getKeys(secondSecret); 13 | 14 | var signature = { 15 | publicKey: keys.publicKey 16 | }; 17 | 18 | return signature; 19 | } 20 | 21 | /** 22 | * @static 23 | * @param {ECPair|string} secret 24 | * @param {string} secondSecret 25 | * @param {number} [feeOverride] 26 | * @returns {Transaction} 27 | */ 28 | function createSignature(secret, secondSecret, feeOverride) { 29 | if (typeof secret === 'undefined' || !secondSecret) return false; 30 | 31 | var keys = secret; 32 | 33 | if (!crypto.isECPair(secret)) { 34 | keys = crypto.getKeys(secret); 35 | } 36 | 37 | if (!keys.publicKey) { 38 | throw new Error("Invalid public key"); 39 | } 40 | 41 | if (feeOverride && !Number.isInteger(feeOverride)) { 42 | throw new Error('Not a valid fee') 43 | } 44 | 45 | var signature = newSignature(secondSecret); 46 | var transaction = { 47 | type: 1, 48 | amount: 0, 49 | fee: feeOverride || constants.fees.secondsignature, 50 | recipientId: null, 51 | senderPublicKey: keys.publicKey, 52 | timestamp: slots.getTime(), 53 | asset: { 54 | signature: signature 55 | } 56 | }; 57 | 58 | crypto.sign(transaction, keys); 59 | transaction.id = crypto.getId(transaction); 60 | 61 | return transaction; 62 | } 63 | 64 | module.exports = { 65 | createSignature: createSignature 66 | } 67 | -------------------------------------------------------------------------------- /lib/transactions/transaction.js: -------------------------------------------------------------------------------- 1 | /** @module transaction */ 2 | 3 | var crypto = require("./crypto.js"), 4 | constants = require("../constants.js"), 5 | slots = require("../time/slots.js"); 6 | 7 | /** 8 | * @static 9 | * @param {string} recipientId 10 | * @param {number} amount 11 | * @param {string|null} vendorField 12 | * @param {ECPair|string} secret 13 | * @param {ECPair|string} [secondSecret] 14 | * @param {number} [version] 15 | * @param {number} [feeOverride] 16 | * @returns {Transaction} 17 | */ 18 | function createTransaction(recipientId, amount, vendorField, secret, secondSecret, version, feeOverride) { 19 | if (!recipientId || !amount || typeof secret === 'undefined') return false; 20 | 21 | if(!crypto.validateAddress(recipientId, version)){ 22 | throw new Error("Wrong recipientId"); 23 | } 24 | 25 | var keys = secret; 26 | 27 | if (!crypto.isECPair(secret)) { 28 | keys = crypto.getKeys(secret); 29 | } 30 | 31 | if (!keys.publicKey) { 32 | throw new Error("Invalid public key"); 33 | } 34 | 35 | if (feeOverride && !Number.isInteger(feeOverride)) { 36 | throw new Error('Not a valid fee') 37 | } 38 | 39 | var transaction = { 40 | type: 0, 41 | amount: amount, 42 | fee: feeOverride || constants.fees.send, 43 | recipientId: recipientId, 44 | timestamp: slots.getTime(), 45 | asset: {} 46 | }; 47 | 48 | if(vendorField){ 49 | transaction.vendorField=vendorField; 50 | if(transaction.vendorField.length > 64){ 51 | return null; 52 | } 53 | } 54 | 55 | transaction.senderPublicKey = keys.publicKey; 56 | 57 | crypto.sign(transaction, keys); 58 | 59 | if (secondSecret) { 60 | var secondKeys = secondSecret; 61 | if (!crypto.isECPair(secondSecret)) { 62 | secondKeys = crypto.getKeys(secondSecret); 63 | } 64 | crypto.secondSign(transaction, secondKeys); 65 | } 66 | 67 | transaction.id = crypto.getId(transaction); 68 | return transaction; 69 | } 70 | 71 | module.exports = { 72 | createTransaction: createTransaction 73 | } 74 | -------------------------------------------------------------------------------- /lib/transactions/vote.js: -------------------------------------------------------------------------------- 1 | /** @module vote */ 2 | 3 | var crypto = require("./crypto.js"), 4 | constants = require("../constants.js"), 5 | slots = require("../time/slots.js"); 6 | 7 | /** 8 | * @static 9 | * @param {ECPair|string} secret 10 | * @param {Array} delegates 11 | * @param {ECPair|string} [secondSecret] 12 | * @param {number} [feeOverride] 13 | * @returns {Transaction} 14 | */ 15 | function createVote(secret, delegates, secondSecret, feeOverride) { 16 | if (typeof secret === 'undefined' || !Array.isArray(delegates)) return; 17 | 18 | var keys = secret; 19 | 20 | if (!crypto.isECPair(secret)) { 21 | keys = crypto.getKeys(secret); 22 | } 23 | 24 | if (!keys.publicKey) { 25 | throw new Error("Invalid public key"); 26 | } 27 | 28 | if (feeOverride && !Number.isInteger(feeOverride)) { 29 | throw new Error('Not a valid fee') 30 | } 31 | 32 | var transaction = { 33 | type: 3, 34 | amount: 0, 35 | fee: feeOverride || constants.fees.vote, 36 | recipientId: crypto.getAddress(keys.publicKey), 37 | senderPublicKey: keys.publicKey, 38 | timestamp: slots.getTime(), 39 | asset: { 40 | votes: delegates 41 | } 42 | }; 43 | 44 | crypto.sign(transaction, keys); 45 | 46 | if (secondSecret) { 47 | var secondKeys = secondSecret; 48 | if (!crypto.isECPair(secondSecret)) { 49 | secondKeys = crypto.getKeys(secondSecret); 50 | } 51 | crypto.secondSign(transaction, secondKeys); 52 | } 53 | 54 | transaction.id = crypto.getId(transaction); 55 | 56 | return transaction; 57 | } 58 | 59 | module.exports = { 60 | createVote: createVote 61 | } 62 | -------------------------------------------------------------------------------- /lib/types.js: -------------------------------------------------------------------------------- 1 | var typeforce = require('typeforce') 2 | 3 | var UINT31_MAX = Math.pow(2, 31) - 1 4 | function UInt31 (value) { 5 | return typeforce.UInt32(value) && value <= UINT31_MAX 6 | } 7 | 8 | function BIP32Path (value) { 9 | return typeforce.String(value) && value.match(/^(m\/)?(\d+'?\/)*\d+'?$/) 10 | } 11 | BIP32Path.toJSON = function () { return 'BIP32 derivation path' } 12 | 13 | var SATOSHI_MAX = 21 * 1e14 14 | function Satoshi (value) { 15 | return typeforce.UInt53(value) && value <= SATOSHI_MAX 16 | } 17 | 18 | // external dependent types 19 | var BigInt = typeforce.quacksLike('BigInteger') 20 | var ECPoint = typeforce.quacksLike('Point') 21 | 22 | // exposed, external API 23 | var ECSignature = typeforce.compile({ r: BigInt, s: BigInt }) 24 | var Network = typeforce.compile({ 25 | messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), 26 | bip32: { 27 | public: typeforce.UInt32, 28 | private: typeforce.UInt32 29 | }, 30 | pubKeyHash: typeforce.UInt8, 31 | wif: typeforce.UInt8 32 | }) 33 | 34 | // extend typeforce types with ours 35 | var types = { 36 | BigInt: BigInt, 37 | BIP32Path: BIP32Path, 38 | Buffer256bit: typeforce.BufferN(32), 39 | ECPoint: ECPoint, 40 | ECSignature: ECSignature, 41 | Hash160bit: typeforce.BufferN(20), 42 | Hash256bit: typeforce.BufferN(32), 43 | Network: Network, 44 | Satoshi: Satoshi, 45 | UInt31: UInt31 46 | } 47 | 48 | for (var typeName in typeforce) { 49 | types[typeName] = typeforce[typeName] 50 | } 51 | 52 | module.exports = types 53 | -------------------------------------------------------------------------------- /lib/v2/transactions/crypto.js: -------------------------------------------------------------------------------- 1 | var crypto = require("crypto"); 2 | var crypto_utils = require("../../crypto.js"); 3 | var ECPair = require("../../ecpair.js"); 4 | var ECSignature = require("../../ecsignature.js"); 5 | var networks = require("../../networks.js") 6 | 7 | var bs58check = require('bs58check') 8 | 9 | if (typeof Buffer === "undefined") { 10 | Buffer = require("buffer/").Buffer; 11 | } 12 | 13 | var ByteBuffer = require("bytebuffer"); 14 | 15 | 16 | var fixedPoint = Math.pow(10, 8); 17 | // default is ark mainnet 18 | var networkVersion = 0x17; 19 | 20 | 21 | function getBytes(transaction) { 22 | var bb = new ByteBuffer(512, true); 23 | bb.writeByte(0xff); // fill, to disambiguate from v1 24 | bb.writeByte(transaction.version); // version 2 25 | bb.writeByte(transaction.network); // ark = 0x17, devnet = 0x30 26 | bb.writeByte(transaction.type); 27 | bb.writeInt(transaction.timestamp); 28 | bb.append(transaction.senderPublicKey, "hex"); 29 | bb.writeLong(transaction.fee); 30 | if(transaction.vendorFieldHex){ 31 | bb.writeByte(transaction.vendorFieldHex.length/2); 32 | bb.append(transaction.vendorFieldHex, "hex"); 33 | } 34 | else { 35 | bb.writeByte(0x00); 36 | } 37 | 38 | switch (transaction.type) { 39 | case 0: // Transfer 40 | bb.writeLong(transaction.amount); 41 | bb.writeInt(transaction.expiration); 42 | bb.append(bs58check.decode(transaction.recipientId)); 43 | break; 44 | 45 | case 1: // Signature 46 | bb.append(transaction.asset.signature.publicKey,"hex"); 47 | break; 48 | 49 | case 2: // Delegate 50 | var delegateBytes = new Buffer(transaction.asset.delegate.username, "utf8"); 51 | bb.writeByte(delegateBytes.length/2); 52 | bb.append(delegateBytes,"hex"); 53 | break; 54 | 55 | case 3: // Vote 56 | var voteBytes = transaction.asset.votes.map(function(vote){ 57 | return (vote[0] == "+" ? "01" : "00") + vote.slice(1); 58 | }).join(""); 59 | bb.writeByte(transaction.asset.votes.length); 60 | bb.append(voteBytes,"hex"); 61 | break; 62 | 63 | case 4: // Multi-Signature 64 | var keysgroupBuffer = new Buffer(transaction.asset.multisignature.keysgroup.join(""), "hex"); 65 | bb.writeByte(transaction.asset.multisignature.min); 66 | bb.writeByte(transaction.asset.multisignature.keysgroup.length); 67 | bb.writeByte(transaction.asset.multisignature.lifetime); 68 | bb.append(keysgroupBuffer,"hex"); 69 | break; 70 | 71 | case 5: // IPFS 72 | bb.writeByte(transaction.asset.ipfs.dag.length/2); 73 | bb.append(transaction.asset.ipfs.dag,"hex"); 74 | break; 75 | 76 | case 6: // timelock transfer 77 | bb.writeLong(transaction.amount); 78 | bb.writeByte(transaction.timelocktype); 79 | bb.writeInt(transaction.timelock); 80 | bb.append(bs58check.decode(transaction.recipientId)); 81 | break; 82 | 83 | case 7: // multipayment 84 | bb.writeInt(transaction.asset.payments.length); 85 | transaction.asset.payments.forEach(function(p) { 86 | bb.writeLong(p.amount); 87 | bb.append(bs58check.decode(p.recipientId)); 88 | }); 89 | break; 90 | 91 | case 8: // delegate resignation - empty payload 92 | break; 93 | } 94 | bb.flip(); 95 | return bb.toBuffer(); 96 | } 97 | 98 | function fromBytes(hexString){ 99 | var tx={}; 100 | var buf = new Buffer(hexString, "hex"); 101 | tx.version = buf.readInt8(1) & 0xff; 102 | tx.network = buf.readInt8(2) & 0xff; 103 | tx.type = buf.readInt8(3) & 0xff; 104 | tx.timestamp = buf.readUInt32LE(4); 105 | tx.senderPublicKey = hexString.substring(16,16+33*2); 106 | tx.fee = buf.readUInt32LE(41); 107 | var vflength = buf.readInt8(41+8) & 0xff; 108 | if(vflength > 0){ 109 | tx.vendorFieldHex=hexString.substring((41+8+1)*2,(41+8+1)*2+vflength*2); 110 | } 111 | 112 | var assetOffset = (41+8+1)*2+vflength*2; 113 | 114 | if(tx.type == 0){ // transfer 115 | tx.amount = buf.readUInt32LE(assetOffset/2); 116 | tx.expiration = buf.readUInt32LE(assetOffset/2+8); 117 | tx.recipientId = bs58check.encode(buf.slice(assetOffset/2+12,assetOffset/2+12+21)); 118 | parseSignatures(hexString, tx, assetOffset+(21+12)*2); 119 | } 120 | else if(tx.type == 1){ // second signature registration 121 | tx.asset = { 122 | signature : { 123 | publicKey : hexString.substring(assetOffset,assetOffset+66) 124 | } 125 | } 126 | parseSignatures(hexString, tx, assetOffset+66); 127 | } 128 | else if(tx.type == 2){ // delegate registration 129 | var usernamelength = buf.readInt8(assetOffset/2) & 0xff; 130 | 131 | tx.asset = { 132 | delegate: { 133 | username: buf.slice(assetOffset/2+1, assetOffset/2+1 + usernamelength).toString("utf8") 134 | } 135 | }; 136 | parseSignatures(hexString, tx, assetOffset + (usernamelength+1)*2); 137 | } 138 | else if(tx.type == 3){ // vote 139 | var votelength = buf.readInt8(assetOffset/2) & 0xff; 140 | tx.asset = {votes:[]}; 141 | var vote; 142 | for(var i = 0; i=4" 8 | }, 9 | "scripts": { 10 | "build:browserify": "browserify index.js -o app.js", 11 | "clean:browserify": "shx rm app.js", 12 | "build:docs": "jsdoc -c jsdoc.json", 13 | "clean:docs": "shx rm -r ./docs", 14 | "lint": "eslint .", 15 | "test": "mocha test/ark.js test/*/*" 16 | }, 17 | "directories": { 18 | "doc": "./doc", 19 | "lib": "./lib", 20 | "test": "./test" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/ArkEcosystem/ark-js.git" 25 | }, 26 | "homepage": "https://github.com/ArkEcosystem/ark-js", 27 | "keywords": [ 28 | "api", 29 | "ark", 30 | "blockchain", 31 | "client", 32 | "cryptocurrency", 33 | "javascript", 34 | "server", 35 | "transaction" 36 | ], 37 | "bugs": "https://github.com/ArkEcosystem/ark-js/issues", 38 | "contributors": [ 39 | "FX Thoorens ", 40 | "Guillaume Verbal ", 41 | "Boris Povod ", 42 | "Oliver Beddows " 43 | ], 44 | "license": "MIT", 45 | "dependencies": { 46 | "bigi": "^1.4.2", 47 | "bip66": "^1.1.5", 48 | "browserify-bignum": "^1.3.0-2", 49 | "bs58check": "^2.0.2", 50 | "buffer": "^5.0.8", 51 | "bytebuffer": "^5.0.1", 52 | "create-hash": "^1.1.3", 53 | "create-hmac": "^1.1.6", 54 | "crypto-browserify": "^3.12.0", 55 | "ecdsa": "^0.7.0", 56 | "ecurve": "^1.0.5", 57 | "js-nacl": "^1.2.2", 58 | "randombytes": "^2.0.5", 59 | "secp256k1": "^3.3.0", 60 | "typeforce": "^1.11.7", 61 | "wif": "^2.0.6" 62 | }, 63 | "devDependencies": { 64 | "browserify": "^14.4.0", 65 | "eslint": "^4.10.0", 66 | "jsdoc": "^3.5.5", 67 | "marked": "^0.3.12", 68 | "mocha": "^4.0.1", 69 | "proxyquire": "^1.8.0", 70 | "should": "^13.1.3", 71 | "shx": "^0.2.2", 72 | "sinon": "^4.1.1", 73 | "sinon-test": "^2.1.2" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/ark.js: -------------------------------------------------------------------------------- 1 | var Buffer = require("buffer/").Buffer; 2 | var should = require("should"); 3 | var ark = require("../index.js"); 4 | 5 | describe("Ark JS", function () { 6 | 7 | it("should be ok", function () { 8 | (ark).should.be.ok; 9 | }); 10 | 11 | it("should be object", function () { 12 | (ark).should.be.type("object"); 13 | }); 14 | 15 | it("should have properties", function () { 16 | var properties = ["transaction", "signature", "vote", "delegate", "crypto"]; 17 | 18 | properties.forEach(function (property) { 19 | (ark).should.have.property(property); 20 | }); 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /test/crypto/fixtures.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "hex": "0000000000000001", 4 | "hash160": "cdb00698f02afd929ffabea308340fa99ac2afa8", 5 | "hash256": "3ae5c198d17634e79059c2cd735491553d22c4e09d1d9fea3ecf214565df2284", 6 | "ripemd160": "8d1a05d1bc08870968eb8a81ad4393fd3aac6633", 7 | "sha1": "cb473678976f425d6ec1339838f11011007ad27d", 8 | "sha256": "cd2662154e6d76b2b2b92e70c0cac3ccf534f9b74eb5b89819ec509083d00a50" 9 | }, 10 | { 11 | "hex": "0101010101010101", 12 | "hash160": "abaf1119f83e384210fe8e222eac76e2f0da39dc", 13 | "hash256": "728338d99f356175c4945ef5cccfa61b7b56143cbbf426ddd0e0fc7cfe8c3c23", 14 | "ripemd160": "5825701b4b9767fd35063b286dca3582853e0630", 15 | "sha1": "c0357a32ed1f6a03be92dd094476f7f1a2e214ec", 16 | "sha256": "04abc8821a06e5a30937967d11ad10221cb5ac3b5273e434f1284ee87129a061" 17 | }, 18 | { 19 | "hex": "ffffffffffffffff", 20 | "hash160": "f86221f5a1fca059a865c0b7d374dfa9d5f3aeb4", 21 | "hash256": "752adad0a7b9ceca853768aebb6965eca126a62965f698a0c1bc43d83db632ad", 22 | "ripemd160": "cb760221600ed34337ca3ab70016b5f58c838120", 23 | "sha1": "be673e8a56eaa9d8c1d35064866701c11ef8e089", 24 | "sha256": "12a3ae445661ce5dee78d0650d33362dec29c4f82af05e7e57fb595bbbacf0ca" 25 | }, 26 | { 27 | "hex": "4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e65632061742066617563696275732073617069656e2c2076656c20666163696c6973697320617263752e20536564207574206d61737361206e6962682e205574206d6f6c6c69732070756c76696e6172206d617373612e20557420756c6c616d636f7270657220646f6c6f7220656e696d2c20696e206d6f6c657374696520656e696d20636f6e64696d656e74756d2061632e20416c697175616d206572617420766f6c75747061742e204e756c6c6120736f64616c657320617420647569206e656320", 28 | "hash160": "9763e6b367c363bd6b88a7b361c98e6beee243a5", 29 | "hash256": "033588797115feb3545052670cac2a46584ab3cb460de63756ee0275e66b5799", 30 | "ripemd160": "cad8593dcdef12ee334c97bab9787f07b3f3a1a5", 31 | "sha1": "10d96fb43aca84e342206887bbeed3065d4e4344", 32 | "sha256": "a7fb8276035057ed6479c5f2305a96da100ac43f0ac10f277e5ab8c5457429da" 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /test/crypto/index.js: -------------------------------------------------------------------------------- 1 | var Buffer = require("buffer/").Buffer; 2 | var should = require("should"); 3 | var ark = require("../../index.js"); 4 | var ECPair = require('../../lib/ecpair'); 5 | 6 | var ecdsa = require('../../lib/ecdsa') 7 | var ecurve = require('ecurve') 8 | var curve = ecdsa.__curve 9 | 10 | describe("crypto.js", function () { 11 | 12 | var crypto = ark.crypto; 13 | 14 | it("should be ok", function () { 15 | (crypto).should.be.ok; 16 | }); 17 | 18 | it("should be object", function () { 19 | (crypto).should.be.type("object"); 20 | }); 21 | 22 | it("should has properties", function () { 23 | var properties = ["getBytes", "getHash", "getId", "getFee", "sign", "secondSign", "getKeys", "getAddress", "verify", "verifySecondSignature", "fixedPoint"]; 24 | properties.forEach(function (property) { 25 | (crypto).should.have.property(property); 26 | }); 27 | }); 28 | 29 | describe("#getBytes", function () { 30 | var getBytes = crypto.getBytes; 31 | var bytes = null; 32 | 33 | it("should be ok", function () { 34 | (getBytes).should.be.ok; 35 | }); 36 | 37 | it("should be a function", function () { 38 | (getBytes).should.be.type("function"); 39 | }); 40 | 41 | it("should return Buffer of simply transaction and buffer must be 202 length", function () { 42 | var transaction = { 43 | type: 0, 44 | amount: 1000, 45 | fee: 2000, 46 | recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 47 | timestamp: 141738, 48 | asset: {}, 49 | senderPublicKey: "5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09", 50 | signature: "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", 51 | id: "13987348420913138422" 52 | }; 53 | 54 | bytes = getBytes(transaction); 55 | (bytes).should.be.ok; 56 | (bytes).should.be.type("object"); 57 | (bytes.length).should.be.equal(202); 58 | }); 59 | 60 | it("should return Buffer of transaction with second signature and buffer must be 266 length", function () { 61 | var transaction = { 62 | type: 0, 63 | amount: 1000, 64 | fee: 2000, 65 | recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 66 | timestamp: 141738, 67 | asset: {}, 68 | senderPublicKey: "5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09", 69 | signature: "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", 70 | signSignature: "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", 71 | id: "13987348420913138422" 72 | }; 73 | 74 | bytes = getBytes(transaction); 75 | (bytes).should.be.ok; 76 | (bytes).should.be.type("object"); 77 | (bytes.length).should.be.equal(266); 78 | }); 79 | }); 80 | 81 | describe("#getHash", function () { 82 | var getHash = crypto.getHash; 83 | 84 | it("should be ok", function () { 85 | (getHash).should.be.ok; 86 | }); 87 | 88 | it("should be a function", function () { 89 | (getHash).should.be.type("function"); 90 | }) 91 | 92 | it("should return Buffer and Buffer most be 32 bytes length", function () { 93 | var transaction = { 94 | type: 0, 95 | amount: 1000, 96 | fee: 2000, 97 | recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 98 | timestamp: 141738, 99 | asset: {}, 100 | senderPublicKey: "5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09", 101 | signature: "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", 102 | }; 103 | 104 | var result = getHash(transaction); 105 | (result).should.be.ok; 106 | (result).should.be.type("object"); 107 | (result.length).should.be.equal(32); 108 | }); 109 | }); 110 | 111 | describe("#getId", function () { 112 | var getId = crypto.getId; 113 | 114 | it("should be ok", function () { 115 | (getId).should.be.ok; 116 | }); 117 | 118 | it("should be a function", function () { 119 | (getId).should.be.type("function"); 120 | }); 121 | 122 | it("should return string id and be equal to 619fd7971db6f317fdee3675c862291c976d072a0a1782410e3a6f5309022491", function () { 123 | var transaction = { 124 | type: 0, 125 | amount: 1000, 126 | fee: 2000, 127 | recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 128 | timestamp: 141738, 129 | asset: {}, 130 | senderPublicKey: "5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09", 131 | signature: "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a" 132 | }; 133 | 134 | var id = getId(transaction); 135 | (id).should.be.type("string").and.equal("952e33b66c35a3805015657c008e73a0dee1efefd9af8c41adb59fe79745ccea"); 136 | }); 137 | }); 138 | 139 | describe("#getFee", function () { 140 | var getFee = crypto.getFee; 141 | 142 | it("should be ok", function () { 143 | (getFee).should.be.ok; 144 | }) 145 | 146 | it("should be a function", function () { 147 | (getFee).should.be.type("function"); 148 | }); 149 | 150 | it("should return number", function () { 151 | var fee = getFee({amount: 100000, type: 0}); 152 | (fee).should.be.type("number"); 153 | (fee).should.be.not.NaN; 154 | }); 155 | 156 | it("should return 10000000", function () { 157 | var fee = getFee({amount: 100000, type: 0}); 158 | (fee).should.be.type("number").and.equal(10000000); 159 | }); 160 | 161 | it("should return 10000000000", function () { 162 | var fee = getFee({type: 1}); 163 | (fee).should.be.type("number").and.equal(10000000000); 164 | }); 165 | 166 | it("should be equal 1000000000000", function () { 167 | var fee = getFee({type: 2}); 168 | (fee).should.be.type("number").and.equal(1000000000000); 169 | }); 170 | 171 | it("should be equal 100000000", function () { 172 | var fee = getFee({type: 3}); 173 | (fee).should.be.type("number").and.equal(100000000); 174 | }); 175 | }); 176 | 177 | describe("fixedPoint", function () { 178 | var fixedPoint = crypto.fixedPoint; 179 | 180 | it("should be ok", function () { 181 | (fixedPoint).should.be.ok; 182 | }) 183 | 184 | it("should be number", function () { 185 | (fixedPoint).should.be.type("number").and.not.NaN; 186 | }); 187 | 188 | it("should be equal 100000000", function () { 189 | (fixedPoint).should.be.equal(100000000); 190 | }); 191 | }); 192 | 193 | describe("#sign", function () { 194 | var sign = crypto.sign; 195 | 196 | it("should be ok", function () { 197 | (sign).should.be.ok; 198 | }); 199 | 200 | it("should be a function", function () { 201 | (sign).should.be.type("function"); 202 | }); 203 | }); 204 | 205 | describe("#secondSign", function () { 206 | var secondSign = crypto.secondSign; 207 | 208 | it("should be ok", function () { 209 | (secondSign).should.be.ok; 210 | }); 211 | 212 | it("should be a function", function () { 213 | (secondSign).should.be.type("function"); 214 | }); 215 | }); 216 | 217 | describe("#getKeys", function () { 218 | var getKeys = crypto.getKeys; 219 | 220 | it("should be ok", function () { 221 | (getKeys).should.be.ok; 222 | }); 223 | 224 | it("should be a function", function () { 225 | (getKeys).should.be.type("function"); 226 | }); 227 | 228 | it("should return two keys in hex", function () { 229 | var keys = getKeys("secret"); 230 | 231 | (keys).should.be.ok; 232 | (keys).should.be.type("object"); 233 | (keys).should.have.property("publicKey"); 234 | (keys).should.have.property("privateKey"); 235 | (keys.publicKey).should.be.type("string").and.match(function () { 236 | try { 237 | new Buffer(keys.publicKey, "hex"); 238 | } catch (e) { 239 | return false; 240 | } 241 | 242 | return true; 243 | }); 244 | (keys.privateKey).should.be.type("string").and.match(function () { 245 | try { 246 | new Buffer(keys.privateKey, "hex"); 247 | } catch (e) { 248 | return false; 249 | } 250 | 251 | return true; 252 | }); 253 | }); 254 | }); 255 | 256 | describe("#getAddress", function () { 257 | var getAddress = crypto.getAddress; 258 | 259 | it("should be ok", function () { 260 | (getAddress).should.be.ok; 261 | }) 262 | 263 | it("should be a function", function () { 264 | (getAddress).should.be.type("function"); 265 | }); 266 | 267 | it("should generate address by publicKey", function () { 268 | var keys = crypto.getKeys("secret"); 269 | var address = getAddress(keys.publicKey); 270 | 271 | (address).should.be.ok; 272 | (address).should.be.type("string"); 273 | (address).should.be.equal("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff"); 274 | }); 275 | 276 | it("should generate address by publicKey - second test", function () { 277 | var keys = crypto.getKeys("secret second test to be sure it works correctly"); 278 | var address = getAddress(keys.publicKey); 279 | 280 | (address).should.be.ok; 281 | (address).should.be.type("string"); 282 | (address).should.be.equal("AQSqYnjmwj1GBL5twD4K9EBXDaTHZognox"); 283 | }); 284 | 285 | it("should generate the same address as ECPair.getAddress()", function () { 286 | var keys = crypto.getKeys("secret second test to be sure it works correctly"); 287 | var address = getAddress(keys.publicKey); 288 | 289 | var Q = ecurve.Point.decodeFrom(curve, new Buffer(keys.publicKey, 'hex')) 290 | var keyPair = new ECPair(null, Q); 291 | 292 | (address).should.be.equal(keyPair.getAddress()); 293 | }); 294 | }); 295 | 296 | describe("#verify", function () { 297 | var verify = crypto.verify; 298 | 299 | it("should be ok", function () { 300 | (verify).should.be.ok; 301 | }) 302 | 303 | it("should be function", function () { 304 | (verify).should.be.type("function"); 305 | }); 306 | }); 307 | 308 | describe("#verifySecondSignature", function () { 309 | var verifySecondSignature = crypto.verifySecondSignature; 310 | 311 | it("should be ok", function () { 312 | (verifySecondSignature).should.be.ok; 313 | }); 314 | 315 | it("should be function", function () { 316 | (verifySecondSignature).should.be.type("function"); 317 | }); 318 | }); 319 | }); 320 | 321 | describe("different networks", function () { 322 | 323 | it("validate address on tesnet should be ok", function () { 324 | ark.crypto.setNetworkVersion(0x52); 325 | ark.crypto.getNetworkVersion().should.equal(0x52); 326 | var validate = ark.crypto.validateAddress("a6fpb1BJZq4otWiVsBcuLG1ZGs5WsqqQtH"); 327 | (validate).should.equal(true); 328 | ark.crypto.setNetworkVersion(0x17); 329 | ark.crypto.getNetworkVersion().should.equal(0x17); 330 | }); 331 | }); 332 | 333 | describe("delegate.js", function () { 334 | var delegate = ark.delegate; 335 | 336 | it("should be ok", function () { 337 | (delegate).should.be.ok; 338 | }); 339 | 340 | it("should be function", function () { 341 | (delegate).should.be.type("object"); 342 | }); 343 | 344 | it("should have property createDelegate", function () { 345 | (delegate).should.have.property("createDelegate"); 346 | }); 347 | 348 | describe("#createDelegate", function () { 349 | var createDelegate = delegate.createDelegate; 350 | var trs = null; 351 | 352 | it("should be ok", function () { 353 | (createDelegate).should.be.ok; 354 | }); 355 | 356 | it("should be function", function () { 357 | (createDelegate).should.be.type("function"); 358 | }); 359 | 360 | it("should create delegate", function () { 361 | trs = createDelegate("secret", "delegate", "secret 2"); 362 | }); 363 | 364 | it("should create transaction with fee override", function () { 365 | const feeOverride = 1000000 366 | trs = createDelegate('secret', 'delegate', 'second secret', feeOverride); 367 | (trs).should.be.ok; 368 | (trs.fee).should.equal(feeOverride) 369 | }); 370 | 371 | it("should fail to create transaction with invalid fee override", function (done) { 372 | const feeOverride = '1000000' 373 | try { 374 | trs = createDelegate('secret', 'delegate', 'second secret', feeOverride); 375 | should.fail() 376 | } catch (error) { 377 | done() 378 | } 379 | }); 380 | 381 | it("should be deserialised correctly", function () { 382 | var deserialisedTx = ark.crypto.fromBytes(ark.crypto.getBytes(trs).toString("hex")); 383 | delete deserialisedTx.vendorFieldHex; 384 | var keys = Object.keys(deserialisedTx) 385 | for(key in keys){ 386 | if(keys[key] == "asset"){ 387 | deserialisedTx.asset.delegate.username.should.equal(trs.asset.delegate.username); 388 | } 389 | else { 390 | deserialisedTx[keys[key]].should.equal(trs[keys[key]]); 391 | } 392 | } 393 | }); 394 | 395 | describe("returned delegate", function () { 396 | var keys = ark.crypto.getKeys("secret"); 397 | var secondKeys = ark.crypto.getKeys("secret 2"); 398 | 399 | it("should be ok", function () { 400 | (trs).should.be.ok; 401 | }); 402 | 403 | it("should be object", function () { 404 | (trs).should.be.type("object"); 405 | }); 406 | 407 | it("should have recipientId equal null", function () { 408 | (trs).should.have.property("recipientId").and.type("object").and.be.empty; 409 | }) 410 | 411 | it("shoud have amount equal 0", function () { 412 | (trs).should.have.property("amount").and.type("number").and.equal(0); 413 | }) 414 | 415 | it("should have type equal 2", function () { 416 | (trs).should.have.property("type").and.type("number").and.equal(2); 417 | }); 418 | 419 | // it("should have id equal 11636400490162225218", function () { 420 | // (trs).should.have.property("id").and.type("string").and.equal('11636400490162225218'); 421 | // }); 422 | 423 | it("should have timestamp number", function () { 424 | (trs).should.have.property("timestamp").and.type("number"); 425 | }); 426 | 427 | it("should have senderPublicKey in hex", function () { 428 | (trs).should.have.property("senderPublicKey").and.type("string").and.match(function () { 429 | try { 430 | new Buffer(trs.senderPublicKey, "hex"); 431 | } catch (e) { 432 | return false; 433 | } 434 | 435 | return true; 436 | }).and.equal(keys.publicKey); 437 | }); 438 | 439 | it("should have signature in hex", function () { 440 | (trs).should.have.property("signature").and.type("string").and.match(function () { 441 | try { 442 | new Buffer(trs.signature, "hex"); 443 | } catch (e) { 444 | return false; 445 | } 446 | 447 | return true; 448 | }); 449 | }); 450 | 451 | it("should have second signature in hex", function () { 452 | (trs).should.have.property("signSignature").and.type("string").and.match(function () { 453 | try { 454 | new Buffer(trs.signSignature, "hex"); 455 | } catch (e) { 456 | return false; 457 | } 458 | 459 | return true; 460 | }); 461 | }); 462 | 463 | it("should have delegate asset", function () { 464 | (trs).should.have.property("asset").and.type("object"); 465 | (trs.asset).should.have.have.property("delegate"); 466 | }) 467 | 468 | it("should be signed correctly", function () { 469 | var result = ark.crypto.verify(trs); 470 | (result).should.be.ok; 471 | }); 472 | 473 | it("should be second signed correctly", function () { 474 | var result = ark.crypto.verifySecondSignature(trs, secondKeys.publicKey); 475 | (result).should.be.ok; 476 | }); 477 | 478 | it("should not be signed correctly now", function () { 479 | trs.amount = 100; 480 | var result = ark.crypto.verify(trs); 481 | (result).should.be.not.ok; 482 | }); 483 | 484 | it("should not be second signed correctly now", function () { 485 | trs.amount = 100; 486 | var result = ark.crypto.verifySecondSignature(trs, secondKeys.publicKey); 487 | (result).should.be.not.ok; 488 | }); 489 | 490 | describe("delegate asset", function () { 491 | it("should be ok", function () { 492 | (trs.asset.delegate).should.be.ok; 493 | }); 494 | 495 | it("should be object", function () { 496 | (trs.asset.delegate).should.be.type("object"); 497 | }); 498 | 499 | it("should be have property username", function () { 500 | (trs.asset.delegate).should.have.property("username").and.be.type("string").and.equal("delegate"); 501 | }); 502 | }); 503 | }); 504 | }); 505 | 506 | }); 507 | -------------------------------------------------------------------------------- /test/ecdsa/fixtures.json: -------------------------------------------------------------------------------- 1 | { 2 | "valid": { 3 | "ecdsa": [ 4 | { 5 | "d": "01", 6 | "k": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5", 7 | "message": "Everything should be made as simple as possible, but not simpler.", 8 | "i": 0, 9 | "signature": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262" 10 | }, 11 | { 12 | "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", 13 | "k": "9dc74cbfd383980fb4ae5d2680acddac9dac956dca65a28c80ac9c847c2374e4", 14 | "message": "Equations are more important to me, because politics is for the present, but an equation is something for eternity.", 15 | "i": 0, 16 | "signature": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5" 17 | }, 18 | { 19 | "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", 20 | "k": "fd27071f01648ebbdd3e1cfbae48facc9fa97edc43bbbc9a7fdc28eae13296f5", 21 | "message": "Not only is the Universe stranger than we think, it is stranger than we can think.", 22 | "i": 0, 23 | "signature": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283" 24 | }, 25 | { 26 | "d": "0000000000000000000000000000000000000000000000000000000000000001", 27 | "k": "f0cd2ba5fc7c183de589f6416220a36775a146740798756d8d949f7166dcc87f", 28 | "message": "How wonderful that we have met with a paradox. Now we have some hope of making progress.", 29 | "i": 1, 30 | "signature": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3" 31 | }, 32 | { 33 | "d": "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64", 34 | "k": "6bb4a594ad57c1aa22dbe991a9d8501daf4688bf50a4892ef21bd7c711afda97", 35 | "message": "Computer science is no more about computers than astronomy is about telescopes.", 36 | "i": 0, 37 | "signature": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6" 38 | }, 39 | { 40 | "d": "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637", 41 | "k": "097b5c8ee22c3ea78a4d3635e0ff6fe85a1eb92ce317ded90b9e71aab2b861cb", 42 | "message": "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough", 43 | "i": 1, 44 | "signature": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37" 45 | }, 46 | { 47 | "d": "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3", 48 | "k": "19355c36c8cbcdfb2382e23b194b79f8c97bf650040fc7728dfbf6b39a97c25b", 49 | "message": "The question of whether computers can think is like the question of whether submarines can swim.", 50 | "i": 1, 51 | "signature": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef" 52 | } 53 | ], 54 | "rfc6979": [ 55 | { 56 | "message": "test data", 57 | "d": "fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e", 58 | "k0": "fcce1de7a9bcd6b2d3defade6afa1913fb9229e3b7ddf4749b55c4848b2a196e", 59 | "k1": "727fbcb59eb48b1d7d46f95a04991fc512eb9dbf9105628e3aec87428df28fd8", 60 | "k15": "398f0e2c9f79728f7b3d84d447ac3a86d8b2083c8f234a0ffa9c4043d68bd258" 61 | }, 62 | { 63 | "message": "Everything should be made as simple as possible, but not simpler.", 64 | "d": "0000000000000000000000000000000000000000000000000000000000000001", 65 | "k0": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5", 66 | "k1": "df55b6d1b5c48184622b0ead41a0e02bfa5ac3ebdb4c34701454e80aabf36f56", 67 | "k15": "def007a9a3c2f7c769c75da9d47f2af84075af95cadd1407393dc1e26086ef87" 68 | }, 69 | { 70 | "message": "Satoshi Nakamoto", 71 | "d": "0000000000000000000000000000000000000000000000000000000000000002", 72 | "k0": "d3edc1b8224e953f6ee05c8bbf7ae228f461030e47caf97cde91430b4607405e", 73 | "k1": "f86d8e43c09a6a83953f0ab6d0af59fb7446b4660119902e9967067596b58374", 74 | "k15": "241d1f57d6cfd2f73b1ada7907b199951f95ef5ad362b13aed84009656e0254a" 75 | }, 76 | { 77 | "message": "Diffie Hellman", 78 | "d": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", 79 | "k0": "c378a41cb17dce12340788dd3503635f54f894c306d52f6e9bc4b8f18d27afcc", 80 | "k1": "90756c96fef41152ac9abe08819c4e95f16da2af472880192c69a2b7bac29114", 81 | "k15": "7b3f53300ab0ccd0f698f4d67db87c44cf3e9e513d9df61137256652b2e94e7c" 82 | }, 83 | { 84 | "message": "Japan", 85 | "d": "8080808080808080808080808080808080808080808080808080808080808080", 86 | "k0": "f471e61b51d2d8db78f3dae19d973616f57cdc54caaa81c269394b8c34edcf59", 87 | "k1": "6819d85b9730acc876fdf59e162bf309e9f63dd35550edf20869d23c2f3e6d17", 88 | "k15": "d8e8bae3ee330a198d1f5e00ad7c5f9ed7c24c357c0a004322abca5d9cd17847" 89 | }, 90 | { 91 | "message": "Bitcoin", 92 | "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", 93 | "k0": "36c848ffb2cbecc5422c33a994955b807665317c1ce2a0f59c689321aaa631cc", 94 | "k1": "4ed8de1ec952a4f5b3bd79d1ff96446bcd45cabb00fc6ca127183e14671bcb85", 95 | "k15": "56b6f47babc1662c011d3b1f93aa51a6e9b5f6512e9f2e16821a238d450a31f8" 96 | }, 97 | { 98 | "message": "i2FLPP8WEus5WPjpoHwheXOMSobUJVaZM1JPMQZq", 99 | "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", 100 | "k0": "6e9b434fcc6bbb081a0463c094356b47d62d7efae7da9c518ed7bac23f4e2ed6", 101 | "k1": "ae5323ae338d6117ce8520a43b92eacd2ea1312ae514d53d8e34010154c593bb", 102 | "k15": "3eaa1b61d1b8ab2f1ca71219c399f2b8b3defa624719f1e96fe3957628c2c4ea" 103 | }, 104 | { 105 | "message": "lEE55EJNP7aLrMtjkeJKKux4Yg0E8E1SAJnWTCEh", 106 | "d": "3881e5286abc580bb6139fe8e83d7c8271c6fe5e5c2d640c1f0ed0e1ee37edc9", 107 | "k0": "5b606665a16da29cc1c5411d744ab554640479dd8abd3c04ff23bd6b302e7034", 108 | "k1": "f8b25263152c042807c992eacd2ac2cc5790d1e9957c394f77ea368e3d9923bd", 109 | "k15": "ea624578f7e7964ac1d84adb5b5087dd14f0ee78b49072aa19051cc15dab6f33" 110 | }, 111 | { 112 | "message": "2SaVPvhxkAPrayIVKcsoQO5DKA8Uv5X/esZFlf+y", 113 | "d": "7259dff07922de7f9c4c5720d68c9745e230b32508c497dd24cb95ef18856631", 114 | "k0": "3ab6c19ab5d3aea6aa0c6da37516b1d6e28e3985019b3adb388714e8f536686b", 115 | "k1": "19af21b05004b0ce9cdca82458a371a9d2cf0dc35a813108c557b551c08eb52e", 116 | "k15": "117a32665fca1b7137a91c4739ac5719fec0cf2e146f40f8e7c21b45a07ebc6a" 117 | }, 118 | { 119 | "message": "00A0OwO2THi7j5Z/jp0FmN6nn7N/DQd6eBnCS+/b", 120 | "d": "0d6ea45d62b334777d6995052965c795a4f8506044b4fd7dc59c15656a28f7aa", 121 | "k0": "79487de0c8799158294d94c0eb92ee4b567e4dc7ca18addc86e49d31ce1d2db6", 122 | "k1": "9561d2401164a48a8f600882753b3105ebdd35e2358f4f808c4f549c91490009", 123 | "k15": "b0d273634129ff4dbdf0df317d4062a1dbc58818f88878ffdb4ec511c77976c0" 124 | } 125 | ] 126 | }, 127 | "invalid": { 128 | "verify": [ 129 | { 130 | "description": "The wrong signature", 131 | "d": "01", 132 | "message": "foo", 133 | "signature": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5" 134 | }, 135 | { 136 | "description": "Invalid r value (< 0)", 137 | "d": "01", 138 | "message": "foo", 139 | "signatureRaw": { 140 | "r": "-01", 141 | "s": "02" 142 | } 143 | }, 144 | { 145 | "description": "Invalid r value (== 0)", 146 | "d": "01", 147 | "message": "foo", 148 | "signatureRaw": { 149 | "r": "00", 150 | "s": "02" 151 | } 152 | }, 153 | { 154 | "description": "Invalid r value (>= n)", 155 | "d": "01", 156 | "message": "foo", 157 | "signatureRaw": { 158 | "r": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 159 | "s": "02" 160 | } 161 | }, 162 | { 163 | "description": "Invalid s value (< 0)", 164 | "d": "01", 165 | "message": "foo", 166 | "signatureRaw": { 167 | "r": "02", 168 | "s": "-01" 169 | } 170 | }, 171 | { 172 | "description": "Invalid s value (== 0)", 173 | "d": "01", 174 | "message": "foo", 175 | "signatureRaw": { 176 | "r": "02", 177 | "s": "00" 178 | } 179 | }, 180 | { 181 | "description": "Invalid s value (>= n)", 182 | "d": "01", 183 | "message": "foo", 184 | "signatureRaw": { 185 | "r": "02", 186 | "s": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" 187 | } 188 | }, 189 | { 190 | "description": "Invalid r, s values (r = s = -n)", 191 | "d": "01", 192 | "message": "foo", 193 | "signatureRaw": { 194 | "r": "-fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 195 | "s": "-fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" 196 | } 197 | } 198 | ] 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /test/ecdsa/index.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var assert = require('assert') 4 | var bcrypto = require('../../lib/crypto') 5 | var ecdsa = require('../../lib/ecdsa') 6 | var sinon = require('sinon') 7 | var sinonTest = require('sinon-test')(sinon) 8 | 9 | var BigInteger = require('bigi') 10 | var ECSignature = require('../../lib/ecsignature') 11 | 12 | var curve = ecdsa.__curve 13 | 14 | var fixtures = require('./fixtures.json') 15 | 16 | describe('ecdsa', function () { 17 | describe('deterministicGenerateK', function () { 18 | function checkSig () { 19 | return true 20 | } 21 | 22 | fixtures.valid.ecdsa.forEach(function (f) { 23 | it('for "' + f.message + '"', function () { 24 | var x = BigInteger.fromHex(f.d).toBuffer(32) 25 | var h1 = bcrypto.sha256(f.message) 26 | 27 | var k = ecdsa.deterministicGenerateK(h1, x, checkSig) 28 | assert.strictEqual(k.toHex(), f.k) 29 | }) 30 | }) 31 | 32 | it('loops until an appropriate k value is found', sinonTest(function () { 33 | this.mock(BigInteger).expects('fromBuffer') 34 | .exactly(3) 35 | .onCall(0).returns(new BigInteger('0')) // < 1 36 | .onCall(1).returns(curve.n) // > n-1 37 | .onCall(2).returns(new BigInteger('42')) // valid 38 | 39 | var x = new BigInteger('1').toBuffer(32) 40 | var h1 = new Buffer(32) 41 | var k = ecdsa.deterministicGenerateK(h1, x, checkSig) 42 | 43 | assert.strictEqual(k.toString(), '42') 44 | })) 45 | 46 | it('loops until a suitable signature is found', sinonTest(function () { 47 | this.mock(BigInteger).expects('fromBuffer') 48 | .exactly(4) 49 | .onCall(0).returns(new BigInteger('0')) // < 1 50 | .onCall(1).returns(curve.n) // > n-1 51 | .onCall(2).returns(new BigInteger('42')) // valid, but 'bad' signature 52 | .onCall(3).returns(new BigInteger('53')) // valid, good signature 53 | 54 | var checkSig = this.mock() 55 | checkSig.exactly(2) 56 | checkSig.onCall(0).returns(false) // bad signature 57 | checkSig.onCall(1).returns(true) // good signature 58 | 59 | var x = new BigInteger('1').toBuffer(32) 60 | var h1 = new Buffer(32) 61 | var k = ecdsa.deterministicGenerateK(h1, x, checkSig) 62 | 63 | assert.strictEqual(k.toString(), '53') 64 | })) 65 | 66 | fixtures.valid.rfc6979.forEach(function (f) { 67 | it('produces the expected k values for ' + f.message + " if k wasn't suitable", function () { 68 | var x = BigInteger.fromHex(f.d).toBuffer(32) 69 | var h1 = bcrypto.sha256(f.message) 70 | 71 | var results = [] 72 | ecdsa.deterministicGenerateK(h1, x, function (k) { 73 | results.push(k) 74 | 75 | return results.length === 16 76 | }) 77 | 78 | assert.strictEqual(results[0].toHex(), f.k0) 79 | assert.strictEqual(results[1].toHex(), f.k1) 80 | assert.strictEqual(results[15].toHex(), f.k15) 81 | }) 82 | }) 83 | }) 84 | 85 | describe('sign', function () { 86 | fixtures.valid.ecdsa.forEach(function (f) { 87 | it('produces a deterministic signature for "' + f.message + '"', function () { 88 | var d = BigInteger.fromHex(f.d) 89 | var hash = bcrypto.sha256(f.message) 90 | var signature = ecdsa.sign(hash, d).toDER() 91 | 92 | assert.strictEqual(signature.toString('hex'), f.signature) 93 | }) 94 | }) 95 | 96 | it('should sign with low S value', function () { 97 | var hash = bcrypto.sha256('Vires in numeris') 98 | var sig = ecdsa.sign(hash, BigInteger.ONE) 99 | 100 | // See BIP62 for more information 101 | var N_OVER_TWO = curve.n.shiftRight(1) 102 | assert(sig.s.compareTo(N_OVER_TWO) <= 0) 103 | }) 104 | }) 105 | 106 | describe('verify', function () { 107 | fixtures.valid.ecdsa.forEach(function (f) { 108 | it('verifies a valid signature for "' + f.message + '"', function () { 109 | var d = BigInteger.fromHex(f.d) 110 | var H = bcrypto.sha256(f.message) 111 | var signature = ECSignature.fromDER(new Buffer(f.signature, 'hex')) 112 | var Q = curve.G.multiply(d) 113 | 114 | assert(ecdsa.verify(H, signature, Q)) 115 | }) 116 | }) 117 | 118 | fixtures.invalid.verify.forEach(function (f) { 119 | it('fails to verify with ' + f.description, function () { 120 | var H = bcrypto.sha256(f.message) 121 | var d = BigInteger.fromHex(f.d) 122 | 123 | var signature 124 | if (f.signature) { 125 | signature = ECSignature.fromDER(new Buffer(f.signature, 'hex')) 126 | } else if (f.signatureRaw) { 127 | signature = new ECSignature(new BigInteger(f.signatureRaw.r, 16), new BigInteger(f.signatureRaw.s, 16)) 128 | } 129 | 130 | var Q = curve.G.multiply(d) 131 | 132 | assert.strictEqual(ecdsa.verify(H, signature, Q), false) 133 | }) 134 | }) 135 | }) 136 | }) 137 | -------------------------------------------------------------------------------- /test/ecpair/fixtures.json: -------------------------------------------------------------------------------- 1 | { 2 | "valid": [ 3 | { 4 | "d": "1", 5 | "Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 6 | "compressed": true, 7 | "network": "ark", 8 | "address": "AcMiVQNHjggC1PyfVSvCcdWZKMisMKj8eo", 9 | "WIF": "S9aCCSFvm8kNeyFb1t6pLb5oJs9tv96ag6uA8Du6UM7zsmsNHQiz" 10 | }, 11 | { 12 | "d": "1", 13 | "Q": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 14 | "compressed": false, 15 | "network": "ark", 16 | "address": "AXTte1XMqsiihEqTkdJwzC9q3S4MqFHWKi", 17 | "WIF": "6hTYzRJRsKyVvTnu7YWs9WvegPh5WiFrmf3JUTwPzQ8vtvPwoBG" 18 | }, 19 | { 20 | "d": "19898843618908353587043383062236220484949425084007183071220218307100305431102", 21 | "Q": "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", 22 | "compressed": true, 23 | "network": "ark", 24 | "address": "AL9uJWA5nd6RWn8VSUzGN7spWeZGHeudg9", 25 | "WIF": "SB3iDxYmKgjkhfDZSKgLaBrp3Ynzd3yd3ZZF2ujVBK7vLpv6hWKK" 26 | }, 27 | { 28 | "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", 29 | "Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34", 30 | "compressed": true, 31 | "network": "ark", 32 | "address": "AHXTCefovyiq4TSAnwmszRNSXQXPbgWMiV", 33 | "WIF": "SDCe8styqokHi4pSe5jVRiYVV63Mef2TGsE1D4HhtGAL1DytHLtd" 34 | }, 35 | { 36 | "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", 37 | "Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6", 38 | "compressed": false, 39 | "network": "ark", 40 | "address": "Ad1JqGpoWxW1EU7azNMKh3qzBbq1HGxK2M", 41 | "WIF": "6iHEQ5jbB9n9meZxCaFAAE39ii1zEyCCquca8GFCyvjSc1UFLp2" 42 | }, 43 | 44 | 45 | 46 | { 47 | "d": "1", 48 | "Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 49 | "compressed": true, 50 | "network": "ark", 51 | "address": "AcMiVQNHjggC1PyfVSvCcdWZKMisMKj8eo", 52 | "WIF": "S9aCCSFvm8kNeyFb1t6pLb5oJs9tv96ag6uA8Du6UM7zsmsNHQiz" 53 | }, 54 | { 55 | "d": "1", 56 | "Q": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 57 | "compressed": false, 58 | "network": "ark", 59 | "address": "AXTte1XMqsiihEqTkdJwzC9q3S4MqFHWKi", 60 | "WIF": "6hTYzRJRsKyVvTnu7YWs9WvegPh5WiFrmf3JUTwPzQ8vtvPwoBG" 61 | }, 62 | { 63 | "d": "19898843618908353587043383062236220484949425084007183071220218307100305431102", 64 | "Q": "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", 65 | "compressed": true, 66 | "network": "ark", 67 | "address": "AL9uJWA5nd6RWn8VSUzGN7spWeZGHeudg9", 68 | "WIF": "SB3iDxYmKgjkhfDZSKgLaBrp3Ynzd3yd3ZZF2ujVBK7vLpv6hWKK" 69 | }, 70 | { 71 | "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", 72 | "Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34", 73 | "compressed": true, 74 | "network": "ark", 75 | "address": "AHXTCefovyiq4TSAnwmszRNSXQXPbgWMiV", 76 | "WIF": "SDCe8styqokHi4pSe5jVRiYVV63Mef2TGsE1D4HhtGAL1DytHLtd" 77 | }, 78 | { 79 | "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", 80 | "Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6", 81 | "compressed": false, 82 | "network": "ark", 83 | "address": "Ad1JqGpoWxW1EU7azNMKh3qzBbq1HGxK2M", 84 | "WIF": "6iHEQ5jbB9n9meZxCaFAAE39ii1zEyCCquca8GFCyvjSc1UFLp2" 85 | }, 86 | { 87 | "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", 88 | "Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34", 89 | "compressed": true, 90 | "network": "testnet", 91 | "address": "a2U3J4Fopd4XK2fHEhRgbpRrfAj3XjRYWm", 92 | "WIF": "UaUjHTfNeGQVJM2jVJ3nAW4HU3Fo1MN5swC6AKqDjvDiWXAunqqY" 93 | }, 94 | { 95 | "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", 96 | "Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6", 97 | "compressed": false, 98 | "network": "testnet", 99 | "address": "aMwtvgQoQbqhV3LhS818JSuQKN2fCAdqGx", 100 | "WIF": "7FP9Dnv6oxbmvmsEhrB9RMdYNXsUeEgDS8kJ2fFYtQqeVxBZpAC" 101 | }, 102 | { 103 | "d": "115792089237316195423570985008687907852837564279074904382605163141518161494336", 104 | "Q": "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 105 | "compressed": true, 106 | "network": "ark", 107 | "address": "AeiYjhgFV9Gx3Hn2mRB3BZbKYxehR4GimK", 108 | "WIF": "SJ9pmbcpwHfWdVGMZxVi8mAwZ45Gsdq4BeR8y6poSyje5EthYKP9" 109 | } 110 | ], 111 | "invalid": { 112 | "constructor": [ 113 | { 114 | "exception": "Private key must be greater than 0", 115 | "d": "-1" 116 | }, 117 | { 118 | "exception": "Private key must be greater than 0", 119 | "d": "0" 120 | }, 121 | { 122 | "exception": "Private key must be less than the curve order", 123 | "d": "115792089237316195423570985008687907852837564279074904382605163141518161494337" 124 | }, 125 | { 126 | "exception": "Private key must be less than the curve order", 127 | "d": "115792089237316195423570985008687907853269984665640564039457584007913129639935" 128 | }, 129 | { 130 | "exception": "Expected property \"compressed\" of type \\?Boolean, got Number 2", 131 | "d": "1", 132 | "options": { 133 | "compressed": 2 134 | } 135 | }, 136 | { 137 | "exception": "Unexpected publicKey parameter", 138 | "d": "1", 139 | "Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" 140 | }, 141 | { 142 | "exception": "Expected property \"network.messagePrefix\" of type Buffer|String, got undefined", 143 | "d": "1", 144 | "options": { 145 | "network": {} 146 | } 147 | } 148 | 149 | ], 150 | "fromWIF": [ 151 | { 152 | "exception": "Invalid network version", 153 | "network": "ark", 154 | "WIF": "7FP9Dnv6oxbmvmsEhrB9RMdYNXsUeEgDS8kJ2fFYtQqeVxBZpAC" 155 | }, 156 | { 157 | "exception": "Unknown network version", 158 | "WIF": "brQnSed3Fia1w9VcbbS6ZGDgJ6ENkgwuQY2LS7pEC5bKHD1fMF" 159 | }, 160 | { 161 | "exception": "Invalid compression flag", 162 | "WIF": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sfZr2ym" 163 | }, 164 | { 165 | "exception": "Invalid WIF length", 166 | "WIF": "3tq8Vmhh9SN5XhjTGSWgx8iKk59XbKG6UH4oqpejRuJhfYD" 167 | }, 168 | { 169 | "exception": "Invalid WIF length", 170 | "WIF": "38uMpGARR2BJy5p4dNFKYg9UsWNoBtkpbdrXDjmfvz8krCtw3T1W92ZDSR" 171 | } 172 | ] 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /test/ecpair/index.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, beforeEach */ 2 | /* eslint-disable no-new */ 3 | 4 | var assert = require('assert') 5 | var ecdsa = require('../../lib/ecdsa') 6 | var ecurve = require('ecurve') 7 | var proxyquire = require('proxyquire') 8 | var sinon = require('sinon') 9 | var sinonTest = require('sinon-test')(sinon) 10 | 11 | var BigInteger = require('bigi') 12 | var ECPair = require('../../lib/ecpair') 13 | 14 | var fixtures = require('./fixtures.json') 15 | var curve = ecdsa.__curve 16 | 17 | var NETWORKS = require('../../lib/networks') 18 | var NETWORKS_LIST = [] // Object.values(NETWORKS) 19 | for (var networkName in NETWORKS) { 20 | NETWORKS_LIST.push(NETWORKS[networkName]) 21 | } 22 | 23 | describe('ECPair', function () { 24 | describe('constructor', function () { 25 | it('defaults to compressed', function () { 26 | var keyPair = new ECPair(BigInteger.ONE) 27 | 28 | assert.strictEqual(keyPair.compressed, true) 29 | }) 30 | 31 | it('supports the uncompressed option', function () { 32 | var keyPair = new ECPair(BigInteger.ONE, null, { 33 | compressed: false 34 | }) 35 | 36 | assert.strictEqual(keyPair.compressed, false) 37 | }) 38 | 39 | it('supports the network option', function () { 40 | var keyPair = new ECPair(BigInteger.ONE, null, { 41 | compressed: false, 42 | network: NETWORKS.testnet 43 | }) 44 | 45 | assert.strictEqual(keyPair.network, NETWORKS.testnet) 46 | }) 47 | 48 | fixtures.valid.forEach(function (f) { 49 | it('calculates the public point for ' + f.WIF, function () { 50 | var d = new BigInteger(f.d) 51 | var keyPair = new ECPair(d, null, { 52 | compressed: f.compressed 53 | }) 54 | 55 | assert.strictEqual(keyPair.getPublicKeyBuffer().toString('hex'), f.Q) 56 | }) 57 | }) 58 | 59 | fixtures.invalid.constructor.forEach(function (f) { 60 | it('throws ' + f.exception, function () { 61 | var d = f.d && new BigInteger(f.d) 62 | var Q = f.Q && ecurve.Point.decodeFrom(curve, new Buffer(f.Q, 'hex')) 63 | 64 | assert.throws(function () { 65 | new ECPair(d, Q, f.options) 66 | }, new RegExp(f.exception)) 67 | }) 68 | }) 69 | }) 70 | 71 | describe('getPublicKeyBuffer', function () { 72 | var keyPair 73 | 74 | beforeEach(function () { 75 | keyPair = new ECPair(BigInteger.ONE) 76 | }) 77 | 78 | it('wraps Q.getEncoded', sinonTest(function () { 79 | this.mock(keyPair.Q).expects('getEncoded') 80 | .once().withArgs(keyPair.compressed) 81 | 82 | keyPair.getPublicKeyBuffer() 83 | })) 84 | }) 85 | 86 | describe('fromWIF', function () { 87 | fixtures.valid.forEach(function (f) { 88 | it('imports ' + f.WIF + ' (' + f.network + ')', function () { 89 | var network = NETWORKS[f.network] 90 | var keyPair = ECPair.fromWIF(f.WIF, network) 91 | 92 | assert.strictEqual(keyPair.d.toString(), f.d) 93 | assert.strictEqual(keyPair.getPublicKeyBuffer().toString("hex"), f.Q) 94 | assert.strictEqual(keyPair.compressed, f.compressed) 95 | assert.strictEqual(keyPair.network, network) 96 | }) 97 | }) 98 | 99 | fixtures.valid.forEach(function (f) { 100 | it('imports ' + f.WIF + ' (via list of networks)', function () { 101 | var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) 102 | 103 | assert.strictEqual(keyPair.d.toString(), f.d) 104 | assert.strictEqual(keyPair.getPublicKeyBuffer().toString("hex"), f.Q) 105 | assert.strictEqual(keyPair.compressed, f.compressed) 106 | assert.strictEqual(keyPair.network, NETWORKS[f.network]) 107 | }) 108 | }) 109 | 110 | fixtures.invalid.fromWIF.forEach(function (f) { 111 | it('throws on ' + f.WIF, function () { 112 | assert.throws(function () { 113 | var networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST 114 | 115 | ECPair.fromWIF(f.WIF, networks) 116 | }, new RegExp(f.exception)) 117 | }) 118 | }) 119 | }) 120 | 121 | describe('toWIF', function () { 122 | fixtures.valid.forEach(function (f) { 123 | it('exports ' + f.WIF, function () { 124 | var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) 125 | var result = keyPair.toWIF() 126 | 127 | assert.strictEqual(result, f.WIF) 128 | }) 129 | }) 130 | }) 131 | 132 | describe('makeRandom', function () { 133 | var d = new Buffer('0404040404040404040404040404040404040404040404040404040404040404', 'hex') 134 | var exWIF = 'S9hzwiZ5ziKjUiFpuZX4Lri3rUocDxZSTy7YzKKHvx8TSjUrYQ27' 135 | 136 | describe('uses randombytes RNG', function () { 137 | it('generates a ECPair', function () { 138 | var stub = { randombytes: function () { return d } } 139 | var ProxiedECPair = proxyquire('../../lib/ecpair', stub) 140 | 141 | var keyPair = ProxiedECPair.makeRandom() 142 | assert.strictEqual(keyPair.toWIF(), exWIF) 143 | }) 144 | }) 145 | 146 | it('allows a custom RNG to be used', function () { 147 | var keyPair = ECPair.makeRandom({ 148 | rng: function (size) { return d.slice(0, size) } 149 | }) 150 | 151 | assert.strictEqual(keyPair.toWIF(), exWIF) 152 | }) 153 | 154 | it('retains the same defaults as ECPair constructor', function () { 155 | var keyPair = ECPair.makeRandom() 156 | 157 | assert.strictEqual(keyPair.compressed, true) 158 | assert.strictEqual(keyPair.network, NETWORKS.ark) 159 | }) 160 | 161 | it('supports the options parameter', function () { 162 | var keyPair = ECPair.makeRandom({ 163 | compressed: false, 164 | network: NETWORKS.testnet 165 | }) 166 | 167 | assert.strictEqual(keyPair.compressed, false) 168 | assert.strictEqual(keyPair.network, NETWORKS.testnet) 169 | }) 170 | 171 | it('loops until d is within interval [1, n - 1] : 1', sinonTest(function () { 172 | var rng = this.mock() 173 | rng.exactly(2) 174 | rng.onCall(0).returns(BigInteger.ZERO.toBuffer(32)) // invalid length 175 | rng.onCall(1).returns(BigInteger.ONE.toBuffer(32)) // === 1 176 | 177 | ECPair.makeRandom({ rng: rng }) 178 | })) 179 | 180 | it('loops until d is within interval [1, n - 1] : n - 1', sinonTest(function () { 181 | var rng = this.mock() 182 | rng.exactly(3) 183 | rng.onCall(0).returns(BigInteger.ZERO.toBuffer(32)) // < 1 184 | rng.onCall(1).returns(curve.n.toBuffer(32)) // > n-1 185 | rng.onCall(2).returns(curve.n.subtract(BigInteger.ONE).toBuffer(32)) // === n-1 186 | 187 | ECPair.makeRandom({ rng: rng }) 188 | })) 189 | }) 190 | 191 | describe('getAddress', function () { 192 | fixtures.valid.forEach(function (f) { 193 | it('returns ' + f.address + ' for ' + f.WIF, function () { 194 | var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) 195 | 196 | assert.strictEqual(keyPair.getAddress(), f.address) 197 | }) 198 | }) 199 | }) 200 | 201 | describe('getNetwork', function () { 202 | fixtures.valid.forEach(function (f) { 203 | it('returns ' + f.network + ' for ' + f.WIF, function () { 204 | var network = NETWORKS[f.network] 205 | var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) 206 | 207 | assert.strictEqual(keyPair.getNetwork(), network) 208 | }) 209 | }) 210 | }) 211 | 212 | describe('ecdsa wrappers', function () { 213 | var keyPair, hash 214 | 215 | beforeEach(function () { 216 | keyPair = ECPair.makeRandom() 217 | hash = new Buffer(32) 218 | }) 219 | 220 | describe('signing', function () { 221 | // it('wraps ecdsa.sign', sinonTest(function () { 222 | // this.mock(ecdsa).expects('sign') 223 | // .once().withArgs(hash, keyPair.d) 224 | // 225 | // keyPair.sign(hash) 226 | // })) 227 | 228 | it('throws if no private key is found', function () { 229 | keyPair.d = null 230 | 231 | assert.throws(function () { 232 | keyPair.sign(hash) 233 | }, /Missing private key/) 234 | }) 235 | }) 236 | 237 | describe('verify', function () { 238 | var signature 239 | 240 | beforeEach(function () { 241 | signature = keyPair.sign(hash) 242 | }) 243 | 244 | // it('wraps ecdsa.verify', sinonTest(function () { 245 | // this.mock(ecdsa).expects('verify') 246 | // .once().withArgs(hash, signature, keyPair.Q) 247 | // 248 | // keyPair.verify(hash, signature) 249 | // })) 250 | }) 251 | }) 252 | }) 253 | -------------------------------------------------------------------------------- /test/ecsignature/fixtures.json: -------------------------------------------------------------------------------- 1 | { 2 | "valid": [ 3 | { 4 | "compact": { 5 | "hex": "1f33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262", 6 | "compressed": true, 7 | "i": 0 8 | }, 9 | "scriptSignature": { 10 | "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226201", 11 | "hashType": 1 12 | }, 13 | "DER": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262", 14 | "signature": { 15 | "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", 16 | "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" 17 | } 18 | }, 19 | { 20 | "compact": { 21 | "hex": "1b54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5", 22 | "compressed": false, 23 | "i": 0 24 | }, 25 | "DER": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5", 26 | "scriptSignature": { 27 | "hex": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a502", 28 | "hashType": 2 29 | }, 30 | "signature": { 31 | "r": "38341707918488238920692284707283974715538935465589664377561695343399725051885", 32 | "s": "3180566392414476763164587487324397066658063772201694230600609996154610926757" 33 | } 34 | }, 35 | { 36 | "compact": { 37 | "hex": "1fff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283", 38 | "compressed": true, 39 | "i": 0 40 | }, 41 | "scriptSignature": { 42 | "hex": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b28303", 43 | "hashType": 3 44 | }, 45 | "DER": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283", 46 | "signature": { 47 | "r": "115464191557905790016094131873849783294273568009648050793030031933291767741904", 48 | "s": "50562520307781850052192542766631199590053690478900449960232079510155113443971" 49 | } 50 | }, 51 | { 52 | "compact": { 53 | "hex": "1cc0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3", 54 | "compressed": false, 55 | "i": 1 56 | }, 57 | "scriptSignature": { 58 | "hex": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d381", 59 | "hashType": 129 60 | }, 61 | "DER": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3", 62 | "signature": { 63 | "r": "87230998027579607140680851455601772643840468630989315269459846730712163783123", 64 | "s": "53231320085894623106179381504478252331065330583563809963303318469380290929875" 65 | } 66 | }, 67 | { 68 | "compact": { 69 | "hex": "1f7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6", 70 | "compressed": true, 71 | "i": 0 72 | }, 73 | "scriptSignature": { 74 | "hex": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df682", 75 | "hashType": 130 76 | }, 77 | "DER": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6", 78 | "signature": { 79 | "r": "51348483531757779992459563033975330355971795607481991320287437101831125115997", 80 | "s": "6277080015686056199074771961940657638578000617958603212944619747099038735862" 81 | } 82 | }, 83 | { 84 | "compact": { 85 | "hex": "1cfbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37", 86 | "compressed": false, 87 | "i": 1 88 | }, 89 | "scriptSignature": { 90 | "hex": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd3783", 91 | "hashType": 131 92 | }, 93 | "DER": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37", 94 | "signature": { 95 | "r": "113979859486826658566290715281614250298918272782414232881639314569529560769671", 96 | "s": "6517071009538626957379450615706485096874328019806177698938278220732027419959" 97 | } 98 | }, 99 | { 100 | "compact": { 101 | "hex": "20cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef", 102 | "compressed": true, 103 | "i": 1 104 | }, 105 | "scriptSignature": { 106 | "hex": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef81", 107 | "hashType": 129 108 | }, 109 | "DER": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef", 110 | "signature": { 111 | "r": "93122007060065279508564838030979550535085999589142852106617159184757394422777", 112 | "s": "3078539468410661027472930027406594684630312677495124015420811882501887769839" 113 | } 114 | } 115 | ], 116 | "invalid": { 117 | "compact": [ 118 | { 119 | "exception": "Invalid signature parameter", 120 | "hex": "23987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62" 121 | }, 122 | { 123 | "exception": "Invalid signature length", 124 | "hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62000000" 125 | }, 126 | { 127 | "exception": "Invalid signature length", 128 | "hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e9379" 129 | } 130 | ], 131 | "DER": [ 132 | { 133 | "exception": "DER sequence length is too short", 134 | "hex": "ffffffffffffff" 135 | }, 136 | { 137 | "exception": "DER sequence length is too long", 138 | "hex": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 139 | }, 140 | { 141 | "exception": "Expected DER sequence", 142 | "hex": "00ffff0400ffffff020400ffffff" 143 | }, 144 | { 145 | "exception": "DER sequence length is invalid", 146 | "hex": "30ff020400ffffff020400ffffff" 147 | }, 148 | { 149 | "exception": "DER sequence length is invalid", 150 | "hex": "300c030400ffffff030400ffffff0000" 151 | }, 152 | { 153 | "exception": "Expected DER integer", 154 | "hex": "300cff0400ffffff020400ffffff" 155 | }, 156 | { 157 | "exception": "Expected DER integer \\(2\\)", 158 | "hex": "300c020200ffffff020400ffffff" 159 | }, 160 | { 161 | "exception": "R length is zero", 162 | "hex": "30080200020400ffffff" 163 | }, 164 | { 165 | "exception": "S length is zero", 166 | "hex": "3008020400ffffff0200" 167 | }, 168 | { 169 | "exception": "R length is too long", 170 | "hex": "300c02dd00ffffff020400ffffff" 171 | }, 172 | { 173 | "exception": "S length is invalid", 174 | "hex": "300c020400ffffff02dd00ffffff" 175 | }, 176 | { 177 | "exception": "R value is negative", 178 | "hex": "300c020480000000020400ffffff" 179 | }, 180 | { 181 | "exception": "S value is negative", 182 | "hex": "300c020400ffffff020480000000" 183 | }, 184 | { 185 | "exception": "R value excessively padded", 186 | "hex": "300c02040000ffff020400ffffff" 187 | }, 188 | { 189 | "exception": "S value excessively padded", 190 | "hex": "300c020400ffffff02040000ffff" 191 | } 192 | ], 193 | "scriptSignature": [ 194 | { 195 | "exception": "Invalid hashType 7", 196 | "hashType": 7, 197 | "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226207", 198 | "signature": { 199 | "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", 200 | "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" 201 | } 202 | }, 203 | { 204 | "exception": "Invalid hashType 140", 205 | "hashType": 140, 206 | "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa543422628c", 207 | "signature": { 208 | "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", 209 | "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" 210 | } 211 | } 212 | ] 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /test/ecsignature/index.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var assert = require('assert') 4 | 5 | var BigInteger = require('bigi') 6 | var ECSignature = require('../../lib/ecsignature') 7 | 8 | var fixtures = require('./fixtures.json') 9 | 10 | describe('ECSignature', function () { 11 | describe('toCompact', function () { 12 | fixtures.valid.forEach(function (f) { 13 | it('exports ' + f.compact.hex + ' correctly', function () { 14 | var signature = new ECSignature(new BigInteger(f.signature.r), new BigInteger(f.signature.s)) 15 | 16 | var buffer = signature.toCompact(f.compact.i, f.compact.compressed) 17 | assert.strictEqual(buffer.toString('hex'), f.compact.hex) 18 | }) 19 | }) 20 | }) 21 | 22 | describe('parseCompact', function () { 23 | fixtures.valid.forEach(function (f) { 24 | it('imports ' + f.compact.hex + ' correctly', function () { 25 | var buffer = new Buffer(f.compact.hex, 'hex') 26 | var parsed = ECSignature.parseCompact(buffer) 27 | 28 | assert.strictEqual(parsed.compressed, f.compact.compressed) 29 | assert.strictEqual(parsed.i, f.compact.i) 30 | assert.strictEqual(parsed.signature.r.toString(), f.signature.r) 31 | assert.strictEqual(parsed.signature.s.toString(), f.signature.s) 32 | }) 33 | }) 34 | 35 | fixtures.invalid.compact.forEach(function (f) { 36 | it('throws on ' + f.hex, function () { 37 | var buffer = new Buffer(f.hex, 'hex') 38 | 39 | assert.throws(function () { 40 | ECSignature.parseCompact(buffer) 41 | }, new RegExp(f.exception)) 42 | }) 43 | }) 44 | }) 45 | 46 | describe('toDER', function () { 47 | fixtures.valid.forEach(function (f) { 48 | it('exports ' + f.DER + ' correctly', function () { 49 | var signature = new ECSignature(new BigInteger(f.signature.r), new BigInteger(f.signature.s)) 50 | 51 | var DER = signature.toDER() 52 | assert.strictEqual(DER.toString('hex'), f.DER) 53 | }) 54 | }) 55 | }) 56 | 57 | describe('fromDER', function () { 58 | fixtures.valid.forEach(function (f) { 59 | it('imports ' + f.DER + ' correctly', function () { 60 | var buffer = new Buffer(f.DER, 'hex') 61 | var signature = ECSignature.fromDER(buffer) 62 | 63 | assert.strictEqual(signature.r.toString(), f.signature.r) 64 | assert.strictEqual(signature.s.toString(), f.signature.s) 65 | }) 66 | }) 67 | 68 | fixtures.invalid.DER.forEach(function (f) { 69 | it('throws "' + f.exception + '" for ' + f.hex, function () { 70 | var buffer = new Buffer(f.hex, 'hex') 71 | 72 | assert.throws(function () { 73 | ECSignature.fromDER(buffer) 74 | }, new RegExp(f.exception)) 75 | }) 76 | }) 77 | }) 78 | 79 | describe('toScriptSignature', function () { 80 | fixtures.valid.forEach(function (f) { 81 | it('exports ' + f.scriptSignature.hex + ' correctly', function () { 82 | var signature = new ECSignature(new BigInteger(f.signature.r), new BigInteger(f.signature.s)) 83 | 84 | var scriptSignature = signature.toScriptSignature(f.scriptSignature.hashType) 85 | assert.strictEqual(scriptSignature.toString('hex'), f.scriptSignature.hex) 86 | }) 87 | }) 88 | 89 | fixtures.invalid.scriptSignature.forEach(function (f) { 90 | it('throws ' + f.exception, function () { 91 | var signature = new ECSignature(new BigInteger(f.signature.r), new BigInteger(f.signature.s)) 92 | 93 | assert.throws(function () { 94 | signature.toScriptSignature(f.hashType) 95 | }, new RegExp(f.exception)) 96 | }) 97 | }) 98 | }) 99 | 100 | describe('parseScriptSignature', function () { 101 | fixtures.valid.forEach(function (f) { 102 | it('imports ' + f.scriptSignature.hex + ' correctly', function () { 103 | var buffer = new Buffer(f.scriptSignature.hex, 'hex') 104 | var parsed = ECSignature.parseScriptSignature(buffer) 105 | 106 | assert.strictEqual(parsed.signature.r.toString(), f.signature.r) 107 | assert.strictEqual(parsed.signature.s.toString(), f.signature.s) 108 | assert.strictEqual(parsed.hashType, f.scriptSignature.hashType) 109 | }) 110 | }) 111 | 112 | fixtures.invalid.scriptSignature.forEach(function (f) { 113 | it('throws on ' + f.hex, function () { 114 | var buffer = new Buffer(f.hex, 'hex') 115 | 116 | assert.throws(function () { 117 | ECSignature.parseScriptSignature(buffer) 118 | }, new RegExp(f.exception)) 119 | }) 120 | }) 121 | }) 122 | }) 123 | -------------------------------------------------------------------------------- /test/hdnode/index.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, beforeEach */ 2 | /* eslint-disable no-new */ 3 | 4 | var assert = require('assert') 5 | var ecdsa = require('../../lib/ecdsa') 6 | var sinon = require('sinon') 7 | var sinonTest = require('sinon-test')(sinon) 8 | 9 | var BigInteger = require('bigi') 10 | var ECPair = require('../../lib/ecpair') 11 | var HDNode = require('../../lib/hdnode') 12 | 13 | var fixtures = require('./fixtures.json') 14 | var curve = ecdsa.__curve 15 | 16 | var NETWORKS = require('../../lib/networks') 17 | var NETWORKS_LIST = [] // Object.values(NETWORKS) 18 | for (var networkName in NETWORKS) { 19 | NETWORKS_LIST.push(NETWORKS[networkName]) 20 | } 21 | 22 | var validAll = [] 23 | fixtures.valid.forEach(function (f) { 24 | function addNetwork (n) { 25 | n.network = f.network 26 | return n 27 | } 28 | 29 | validAll = validAll.concat(addNetwork(f.master), f.children.map(addNetwork)) 30 | }) 31 | 32 | describe('HDNode', function () { 33 | describe('Constructor', function () { 34 | var keyPair, chainCode 35 | 36 | beforeEach(function () { 37 | var d = BigInteger.ONE 38 | 39 | keyPair = new ECPair(d, null) 40 | chainCode = new Buffer(32) 41 | chainCode.fill(1) 42 | }) 43 | 44 | it('stores the keyPair/chainCode directly', function () { 45 | var hd = new HDNode(keyPair, chainCode) 46 | 47 | assert.strictEqual(hd.keyPair, keyPair) 48 | assert.strictEqual(hd.chainCode, chainCode) 49 | }) 50 | 51 | it('has a default depth/index of 0', function () { 52 | var hd = new HDNode(keyPair, chainCode) 53 | 54 | assert.strictEqual(hd.depth, 0) 55 | assert.strictEqual(hd.index, 0) 56 | }) 57 | 58 | it('throws on uncompressed keyPair', function () { 59 | keyPair.compressed = false 60 | 61 | assert.throws(function () { 62 | new HDNode(keyPair, chainCode) 63 | }, /BIP32 only allows compressed keyPairs/) 64 | }) 65 | 66 | it('throws when an invalid length chain code is given', function () { 67 | assert.throws(function () { 68 | new HDNode(keyPair, new Buffer(20)) 69 | }, /Expected property "1" of type Buffer\(Length: 32\), got Buffer\(Length: 20\)/) 70 | }) 71 | }) 72 | 73 | describe('fromSeed*', function () { 74 | fixtures.valid.forEach(function (f) { 75 | it('calculates privKey and chainCode for ' + f.master.fingerprint, function () { 76 | var network = NETWORKS[f.network] 77 | var hd = HDNode.fromSeedHex(f.master.seed, network) 78 | 79 | assert.strictEqual(hd.keyPair.toWIF(), f.master.wif) 80 | assert.strictEqual(hd.chainCode.toString('hex'), f.master.chainCode) 81 | }) 82 | }) 83 | 84 | it('throws if IL is not within interval [1, n - 1] | IL === 0', sinonTest(function () { 85 | this.mock(BigInteger).expects('fromBuffer') 86 | .once().returns(BigInteger.ZERO) 87 | 88 | assert.throws(function () { 89 | HDNode.fromSeedHex('ffffffffffffffffffffffffffffffff') 90 | }, /Private key must be greater than 0/) 91 | })) 92 | 93 | it('throws if IL is not within interval [1, n - 1] | IL === n', sinonTest(function () { 94 | this.mock(BigInteger).expects('fromBuffer') 95 | .once().returns(curve.n) 96 | 97 | assert.throws(function () { 98 | HDNode.fromSeedHex('ffffffffffffffffffffffffffffffff') 99 | }, /Private key must be less than the curve order/) 100 | })) 101 | 102 | it('throws on low entropy seed', function () { 103 | assert.throws(function () { 104 | HDNode.fromSeedHex('ffffffffff') 105 | }, /Seed should be at least 128 bits/) 106 | }) 107 | 108 | it('throws on too high entropy seed', function () { 109 | assert.throws(function () { 110 | HDNode.fromSeedHex('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') 111 | }, /Seed should be at most 512 bits/) 112 | }) 113 | }) 114 | 115 | describe('ECPair wrappers', function () { 116 | var keyPair, hd, hash 117 | 118 | beforeEach(function () { 119 | keyPair = ECPair.makeRandom() 120 | hash = new Buffer(32) 121 | 122 | var chainCode = new Buffer(32) 123 | hd = new HDNode(keyPair, chainCode) 124 | }) 125 | 126 | describe('getAddress', function () { 127 | it('wraps keyPair.getAddress', sinonTest(function () { 128 | this.mock(keyPair).expects('getAddress') 129 | .once().withArgs().returns('foobar') 130 | 131 | assert.strictEqual(hd.getAddress(), 'foobar') 132 | })) 133 | }) 134 | 135 | describe('getNetwork', function () { 136 | it('wraps keyPair.getNetwork', sinonTest(function () { 137 | this.mock(keyPair).expects('getNetwork') 138 | .once().withArgs().returns('network') 139 | 140 | assert.strictEqual(hd.getNetwork(), 'network') 141 | })) 142 | }) 143 | 144 | describe('getPublicKeyBuffer', function () { 145 | it('wraps keyPair.getPublicKeyBuffer', sinonTest(function () { 146 | this.mock(keyPair).expects('getPublicKeyBuffer') 147 | .once().withArgs().returns('pubKeyBuffer') 148 | 149 | assert.strictEqual(hd.getPublicKeyBuffer(), 'pubKeyBuffer') 150 | })) 151 | }) 152 | 153 | describe('sign', function () { 154 | it('wraps keyPair.sign', sinonTest(function () { 155 | this.mock(keyPair).expects('sign') 156 | .once().withArgs(hash).returns('signed') 157 | 158 | assert.strictEqual(hd.sign(hash), 'signed') 159 | })) 160 | }) 161 | 162 | describe('verify', function () { 163 | var signature 164 | 165 | beforeEach(function () { 166 | signature = hd.sign(hash) 167 | }) 168 | 169 | it('wraps keyPair.verify', sinonTest(function () { 170 | this.mock(keyPair).expects('verify') 171 | .once().withArgs(hash, signature).returns('verified') 172 | 173 | assert.strictEqual(hd.verify(hash, signature), 'verified') 174 | })) 175 | }) 176 | }) 177 | 178 | describe('fromBase58 / toBase58', function () { 179 | validAll.forEach(function (f) { 180 | it('exports ' + f.base58 + ' (public) correctly', function () { 181 | var hd = HDNode.fromBase58(f.base58, NETWORKS_LIST) 182 | 183 | assert.strictEqual(hd.toBase58(), f.base58) 184 | assert.throws(function () { hd.keyPair.toWIF() }, /Missing private key/) 185 | }) 186 | }) 187 | 188 | validAll.forEach(function (f) { 189 | it('exports ' + f.base58Priv + ' (private) correctly', function () { 190 | var hd = HDNode.fromBase58(f.base58Priv, NETWORKS_LIST) 191 | 192 | assert.strictEqual(hd.toBase58(), f.base58Priv) 193 | assert.strictEqual(hd.keyPair.toWIF(), f.wif) 194 | }) 195 | }) 196 | 197 | fixtures.invalid.fromBase58.forEach(function (f) { 198 | it('throws on ' + f.string, function () { 199 | assert.throws(function () { 200 | var networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST 201 | 202 | HDNode.fromBase58(f.string, networks) 203 | }, new RegExp(f.exception)) 204 | }) 205 | }) 206 | }) 207 | 208 | describe('getIdentifier', function () { 209 | validAll.forEach(function (f) { 210 | it('returns the identifier for ' + f.fingerprint, function () { 211 | var hd = HDNode.fromBase58(f.base58, NETWORKS_LIST) 212 | 213 | assert.strictEqual(hd.getIdentifier().toString('hex'), f.identifier) 214 | }) 215 | }) 216 | }) 217 | 218 | describe('getFingerprint', function () { 219 | validAll.forEach(function (f) { 220 | it('returns the fingerprint for ' + f.fingerprint, function () { 221 | var hd = HDNode.fromBase58(f.base58, NETWORKS_LIST) 222 | 223 | assert.strictEqual(hd.getFingerprint().toString('hex'), f.fingerprint) 224 | }) 225 | }) 226 | }) 227 | 228 | describe('neutered / isNeutered', function () { 229 | validAll.forEach(function (f) { 230 | it('drops the private key for ' + f.fingerprint, function () { 231 | var hd = HDNode.fromBase58(f.base58Priv, NETWORKS_LIST) 232 | var hdn = hd.neutered() 233 | 234 | assert.notEqual(hdn.keyPair, hd.keyPair) 235 | assert.throws(function () { hdn.keyPair.toWIF() }, /Missing private key/) 236 | assert.strictEqual(hdn.toBase58(), f.base58) 237 | assert.strictEqual(hdn.chainCode, hd.chainCode) 238 | assert.strictEqual(hdn.depth, f.depth >>> 0) 239 | assert.strictEqual(hdn.index, f.index >>> 0) 240 | assert.strictEqual(hdn.isNeutered(), true) 241 | 242 | // does not modify the original 243 | assert.strictEqual(hd.toBase58(), f.base58Priv) 244 | assert.strictEqual(hd.isNeutered(), false) 245 | }) 246 | }) 247 | }) 248 | 249 | describe('derive', function () { 250 | function verifyVector (hd, v) { 251 | if (hd.isNeutered()) { 252 | assert.strictEqual(hd.toBase58(), v.base58) 253 | } else { 254 | assert.strictEqual(hd.neutered().toBase58(), v.base58) 255 | assert.strictEqual(hd.toBase58(), v.base58Priv) 256 | } 257 | 258 | assert.strictEqual(hd.getFingerprint().toString('hex'), v.fingerprint) 259 | assert.strictEqual(hd.getIdentifier().toString('hex'), v.identifier) 260 | assert.strictEqual(hd.getAddress(), v.address) 261 | assert.strictEqual(hd.keyPair.toWIF(), v.wif) 262 | assert.strictEqual(hd.keyPair.getPublicKeyBuffer().toString('hex'), v.pubKey) 263 | assert.strictEqual(hd.chainCode.toString('hex'), v.chainCode) 264 | assert.strictEqual(hd.depth, v.depth >>> 0) 265 | assert.strictEqual(hd.index, v.index >>> 0) 266 | } 267 | 268 | fixtures.valid.forEach(function (f) { 269 | var network = NETWORKS[f.network] 270 | var hd = HDNode.fromSeedHex(f.master.seed, network) 271 | var master = hd 272 | 273 | // testing deriving path from master 274 | f.children.forEach(function (c) { 275 | it(c.path + ' from ' + f.master.fingerprint + ' by path', function () { 276 | var child = master.derivePath(c.path) 277 | var childNoM = master.derivePath(c.path.slice(2)) // no m/ on path 278 | 279 | verifyVector(child, c) 280 | verifyVector(childNoM, c) 281 | }) 282 | }) 283 | 284 | // testing deriving path from children 285 | f.children.forEach(function (c, i) { 286 | var cn = master.derivePath(c.path) 287 | 288 | f.children.slice(i + 1).forEach(function (cc) { 289 | it(cc.path + ' from ' + c.fingerprint + ' by path', function () { 290 | var ipath = cc.path.slice(2).split('/').slice(i + 1).join('/') 291 | var child = cn.derivePath(ipath) 292 | verifyVector(child, cc) 293 | 294 | assert.throws(function () { 295 | cn.derivePath('m/' + ipath) 296 | }, /Not a master node/) 297 | }) 298 | }) 299 | }) 300 | 301 | // FIXME: test data is only testing Private -> private for now 302 | f.children.forEach(function (c, i) { 303 | if (c.m === undefined) return 304 | 305 | it(c.path + ' from ' + f.master.fingerprint, function () { 306 | if (c.hardened) { 307 | hd = hd.deriveHardened(c.m) 308 | } else { 309 | hd = hd.derive(c.m) 310 | } 311 | 312 | verifyVector(hd, c) 313 | }) 314 | }) 315 | }) 316 | 317 | it('works for Private -> public (neutered)', function () { 318 | var f = fixtures.valid[1] 319 | var c = f.children[0] 320 | 321 | var master = HDNode.fromBase58(f.master.base58Priv, NETWORKS_LIST) 322 | var child = master.derive(c.m).neutered() 323 | 324 | assert.strictEqual(child.toBase58(), c.base58) 325 | }) 326 | 327 | it('works for Private -> public (neutered, hardened)', function () { 328 | var f = fixtures.valid[0] 329 | var c = f.children[0] 330 | 331 | var master = HDNode.fromBase58(f.master.base58Priv, NETWORKS_LIST) 332 | var child = master.deriveHardened(c.m).neutered() 333 | 334 | assert.strictEqual(c.base58, child.toBase58()) 335 | }) 336 | 337 | it('works for Public -> public', function () { 338 | var f = fixtures.valid[1] 339 | var c = f.children[0] 340 | 341 | var master = HDNode.fromBase58(f.master.base58, NETWORKS_LIST) 342 | var child = master.derive(c.m) 343 | 344 | assert.strictEqual(c.base58, child.toBase58()) 345 | }) 346 | 347 | it('throws on Public -> public (hardened)', function () { 348 | var f = fixtures.valid[0] 349 | var c = f.children[0] 350 | 351 | var master = HDNode.fromBase58(f.master.base58, NETWORKS_LIST) 352 | 353 | assert.throws(function () { 354 | master.deriveHardened(c.m) 355 | }, /Could not derive hardened child key/) 356 | }) 357 | 358 | it('throws on wrong types', function () { 359 | var f = fixtures.valid[0] 360 | var master = HDNode.fromBase58(f.master.base58, NETWORKS_LIST) 361 | 362 | fixtures.invalid.derive.forEach(function (fx) { 363 | assert.throws(function () { 364 | master.derive(fx) 365 | }, /Expected UInt32/) 366 | }) 367 | 368 | fixtures.invalid.deriveHardened.forEach(function (fx) { 369 | assert.throws(function () { 370 | master.deriveHardened(fx) 371 | }, /Expected UInt31/) 372 | }) 373 | 374 | fixtures.invalid.derivePath.forEach(function (fx) { 375 | assert.throws(function () { 376 | master.derivePath(fx) 377 | }, /Expected BIP32 derivation path/) 378 | }) 379 | }) 380 | 381 | it('works when private key has leading zeros', function () { 382 | var key = 'xprv9s21ZrQH143K3ckY9DgU79uMTJkQRLdbCCVDh81SnxTgPzLLGax6uHeBULTtaEtcAvKjXfT7ZWtHzKjTpujMkUd9dDb8msDeAfnJxrgAYhr' 383 | var hdkey = HDNode.fromBase58(key, NETWORKS.bitcoin) 384 | assert.strictEqual(hdkey.keyPair.d.toBuffer(32).toString('hex'), '00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd') 385 | var child = hdkey.derivePath('m/44\'/0\'/0\'/0/0\'') 386 | assert.strictEqual(child.keyPair.d.toBuffer().toString('hex'), '3348069561d2a0fb925e74bf198762acc47dce7db27372257d2d959a9e6f8aeb') 387 | }) 388 | }) 389 | }) 390 | -------------------------------------------------------------------------------- /test/integration/basic.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var assert = require('assert') 4 | var bigi = require('bigi') 5 | var ark = require('../../') 6 | 7 | describe('ark-js (basic)', function () { 8 | it('can generate a random ark address', function () { 9 | // for testing only 10 | function rng () { return new Buffer('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } 11 | 12 | // generate random keyPair 13 | var keyPair = ark.ECPair.makeRandom({ rng: rng }) 14 | var address = keyPair.getAddress() 15 | 16 | assert.strictEqual(address, 'ANoMWEJ9jSdE2FgohBLLXeLzci59BDFsP4') 17 | }) 18 | 19 | it('can generate an address from a SHA256 hash', function () { 20 | var hash = ark.crypto.sha256('correct horse battery staple') 21 | var d = bigi.fromBuffer(hash) 22 | 23 | var keyPair = new ark.ECPair(d) 24 | var address = keyPair.getAddress() 25 | 26 | assert.strictEqual(address, 'AG5AtmiNbgv51eLwAWnRGvkMudVd7anYP2') 27 | }) 28 | 29 | it('can generate a random keypair for alternative networks', function () { 30 | // for testing only 31 | function rng () { return new Buffer('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } 32 | 33 | var bitcoin = ark.networks.bitcoin 34 | 35 | var keyPair = ark.ECPair.makeRandom({ network: bitcoin, rng: rng }) 36 | var wif = keyPair.toWIF() 37 | var address = keyPair.getAddress() 38 | 39 | assert.strictEqual(address, '182UrjSXQHy5DHUp8Xg1Nm5u979SojJY2P') 40 | assert.strictEqual(wif, 'L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1') 41 | }) 42 | 43 | it('can import an address via WIF', function () { 44 | var keyPair = ark.ECPair.fromWIF('S9aCCSFvm8kNeyFb1t6pLb5oJs9tv96ag6uA8Du6UM7zsmsNHQiz') 45 | var address = keyPair.getAddress() 46 | 47 | assert.strictEqual(address, 'AcMiVQNHjggC1PyfVSvCcdWZKMisMKj8eo') 48 | }) 49 | 50 | }) 51 | -------------------------------------------------------------------------------- /test/integration/bip32.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var assert = require('assert') 4 | var bigi = require('bigi') 5 | var ark = require('../../') 6 | var crypto = require('crypto') 7 | 8 | var ecurve = require('ecurve') 9 | var secp256k1 = ecurve.getCurveByName('secp256k1') 10 | 11 | describe('ark-js (BIP32)', function () { 12 | it('can create a BIP32 wallet external address', function () { 13 | var path = "m/0'/0/0" 14 | var root = ark.HDNode.fromSeedHex('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd') 15 | 16 | var child1 = root.derivePath(path) 17 | 18 | // option 2, manually 19 | var child2 = root.deriveHardened(0) 20 | .derive(0) 21 | .derive(0) 22 | 23 | assert.equal(child1.getAddress(), 'AZXdSTRFGHPokX6yfXTfHcTzzHKncioj31') 24 | assert.equal(child2.getAddress(), 'AZXdSTRFGHPokX6yfXTfHcTzzHKncioj31') 25 | }) 26 | 27 | it('can create a BIP44, ark, account 0, external address', function () { 28 | var path = "m/44'/0'/0'/0/0" 29 | var root = ark.HDNode.fromSeedHex('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd') 30 | 31 | var child1 = root.derivePath(path) 32 | 33 | // option 2, manually 34 | var child2 = root.deriveHardened(44) 35 | .deriveHardened(0) 36 | .deriveHardened(0) 37 | .derive(0) 38 | .derive(0) 39 | 40 | assert.equal(child1.getAddress(), 'AVbXc2KyxtXeAP9zQpp7ixsnaxEEQ6wZbq') 41 | assert.equal(child2.getAddress(), 'AVbXc2KyxtXeAP9zQpp7ixsnaxEEQ6wZbq') 42 | }) 43 | 44 | it('can recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key', function () { 45 | function recoverParent (master, child) { 46 | assert(!master.keyPair.d, 'You already have the parent private key') 47 | assert(child.keyPair.d, 'Missing child private key') 48 | 49 | var curve = secp256k1 50 | var QP = master.keyPair.Q 51 | var serQP = master.keyPair.getPublicKeyBuffer() 52 | 53 | var d1 = child.keyPair.d 54 | var d2 55 | var data = new Buffer(37) 56 | serQP.copy(data, 0) 57 | 58 | // search index space until we find it 59 | for (var i = 0; i < ark.HDNode.HIGHEST_BIT; ++i) { 60 | data.writeUInt32BE(i, 33) 61 | 62 | // calculate I 63 | var I = crypto.createHmac('sha512', master.chainCode).update(data).digest() 64 | var IL = I.slice(0, 32) 65 | var pIL = bigi.fromBuffer(IL) 66 | 67 | // See hdnode.js:273 to understand 68 | d2 = d1.subtract(pIL).mod(curve.n) 69 | 70 | var Qp = new ark.ECPair(d2).Q 71 | if (Qp.equals(QP)) break 72 | } 73 | 74 | var node = new ark.HDNode(new ark.ECPair(d2), master.chainCode, master.network) 75 | node.depth = master.depth 76 | node.index = master.index 77 | node.masterFingerprint = master.masterFingerprint 78 | return node 79 | } 80 | 81 | var seed = crypto.randomBytes(32) 82 | var master = ark.HDNode.fromSeedBuffer(seed) 83 | var child = master.derive(6) // m/6 84 | 85 | // now for the recovery 86 | var neuteredMaster = master.neutered() 87 | var recovered = recoverParent(neuteredMaster, child) 88 | assert.strictEqual(recovered.toBase58(), master.toBase58()) 89 | }) 90 | }) 91 | -------------------------------------------------------------------------------- /test/ipfs/index.js: -------------------------------------------------------------------------------- 1 | var Buffer = require("buffer/").Buffer; 2 | var should = require("should"); 3 | var ark = require("../../index.js"); 4 | 5 | describe("ipfs.js", function () { 6 | 7 | var ipfs = ark.ipfs; 8 | 9 | it("should be ok", function () { 10 | (ipfs).should.be.ok; 11 | }); 12 | 13 | it("should be object", function () { 14 | (ipfs).should.be.type("object"); 15 | }); 16 | 17 | it("should have createHashRegistration property", function () { 18 | (ipfs).should.have.property("createHashRegistration"); 19 | }); 20 | 21 | it("should create transaction with fee override", function () { 22 | const feeOverride = 1000000 23 | trs = ipfs.createHashRegistration("QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg", "secret", undefined, feeOverride); 24 | (trs).should.be.ok; 25 | (trs.fee).should.equal(feeOverride) 26 | }); 27 | 28 | it("should fail to create transaction with invalid fee override", function (done) { 29 | const feeOverride = '1000000' 30 | try { 31 | trs = ipfs.createHashRegistration("QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg", "secret", undefined, feeOverride); 32 | should.fail() 33 | } catch (error) { 34 | done() 35 | } 36 | }); 37 | 38 | it("should create transaction with hashid", function () { 39 | trs = ipfs.createHashRegistration("QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg", "secret"); 40 | (trs).should.be.ok; 41 | }); 42 | 43 | it("should be deserialised correctly", function () { 44 | var deserialisedTx = ark.crypto.fromBytes(ark.crypto.getBytes(trs).toString("hex")); 45 | var keys = Object.keys(deserialisedTx); 46 | for(key in keys){ 47 | deserialisedTx[keys[key]].should.equal(trs[keys[key]]); 48 | } 49 | }); 50 | 51 | describe("returned transaction", function () { 52 | it("should be object", function () { 53 | (trs).should.be.type("object"); 54 | }); 55 | 56 | it("should have id as string", function () { 57 | (trs.id).should.be.type("string"); 58 | }); 59 | 60 | it("should have vendorField as string", function () { 61 | (trs.vendorFieldHex).should.be.type("string"); 62 | }); 63 | 64 | it("should have vendorFieldHex equal to '00000000000000000000516d5732575169376a36633755674a546172416374703774444e696b453442327158744643664c506473676154512f6361742e6a7067'", function () { 65 | (trs.vendorFieldHex).should.be.type("string").and.equal('00000000000000000000516d5732575169376a36633755674a546172416374703774444e696b453442327158744643664c506473676154512f6361742e6a7067'); 66 | }); 67 | 68 | it("should have type as number and equal 5", function () { 69 | (trs.type).should.be.type("number").and.equal(5); 70 | }); 71 | 72 | it("should have timestamp as number", function () { 73 | (trs.timestamp).should.be.type("number").and.not.NaN; 74 | }); 75 | 76 | it("should have senderPublicKey as hex string", function () { 77 | (trs.senderPublicKey).should.be.type("string").and.match(function () { 78 | try { 79 | new Buffer(trs.senderPublicKey, "hex") 80 | } catch (e) { 81 | return false; 82 | } 83 | 84 | return true; 85 | }) 86 | }); 87 | 88 | it("should have amount as number and equal to 0", function () { 89 | (trs.amount).should.be.type("number").and.equal(0); 90 | }); 91 | 92 | it("should have empty asset object", function () { 93 | (trs.asset).should.be.type("object").and.empty; 94 | }); 95 | 96 | it("should does not have second signature", function () { 97 | (trs).should.not.have.property("signSignature"); 98 | }); 99 | 100 | it("should have signature as hex string", function () { 101 | (trs.signature).should.be.type("string").and.match(function () { 102 | try { 103 | new Buffer(trs.signature, "hex") 104 | } catch (e) { 105 | return false; 106 | } 107 | 108 | return true; 109 | }) 110 | }); 111 | 112 | it("should be signed correctly", function () { 113 | var result = ark.crypto.verify(trs); 114 | (result).should.be.ok; 115 | }); 116 | 117 | it("should not be signed correctly now (changed amount)", function () { 118 | trs.amount = 10000; 119 | var result = ark.crypto.verify(trs); 120 | (result).should.be.not.ok; 121 | }); 122 | 123 | it("should not be signed correctly now (changed vendorField)", function () { 124 | trs.vendorField = "bouloup"; 125 | var result = ark.crypto.verify(trs); 126 | (result).should.be.not.ok; 127 | }); 128 | }); 129 | 130 | }); 131 | -------------------------------------------------------------------------------- /test/multisignature/index.js: -------------------------------------------------------------------------------- 1 | var Buffer = require("buffer/").Buffer; 2 | var should = require("should"); 3 | var ark = require("../../index.js"); 4 | var constants = require("../../lib/constants.js"); 5 | 6 | describe("multisignature.js", function () { 7 | 8 | var multisignature = ark.multisignature; 9 | 10 | it("should be ok", function () { 11 | (multisignature).should.be.ok; 12 | }); 13 | 14 | it("should be object", function () { 15 | (multisignature).should.be.type("object"); 16 | }); 17 | 18 | it("should have properties", function () { 19 | (multisignature).should.have.property("createMultisignature"); 20 | }); 21 | 22 | describe("#createMultisignature", function () { 23 | var createMultisignature = multisignature.createMultisignature; 24 | var sgn = null; 25 | var keysgroup = ["+03a02b9d5fdd1307c2ee4652ba54d492d1fd11a7d1bb3f3a44c4a05e79f19de933", "+13a02b9d5fdd1307c2ee4652ba54d492d1fd11a7d1bb3f3a44c4a05e79f19de933", "+23a02b9d5fdd1307c2ee4652ba54d492d1fd11a7d1bb3f3a44c4a05e79f19de933"]; 26 | 27 | it("should be function", function () { 28 | (createMultisignature).should.be.type("function"); 29 | }); 30 | 31 | it("should create multisignature transaction", function () { 32 | sgn = createMultisignature("secret", "second secret", keysgroup, 255, 2); 33 | (sgn).should.be.ok; 34 | (sgn).should.be.type("object"); 35 | }); 36 | 37 | it("should create multisignature transaction from keys", function () { 38 | var secretKey = ark.ECPair.fromSeed("secret"); 39 | secretKey.publicKey = secretKey.getPublicKeyBuffer().toString("hex"); 40 | 41 | var secondSecretKey = ark.ECPair.fromSeed("second secret"); 42 | secondSecretKey.publicKey = secondSecretKey.getPublicKeyBuffer().toString("hex"); 43 | 44 | sgn = createMultisignature(secretKey, secondSecretKey, keysgroup, 255, 2); 45 | (sgn).should.be.ok; 46 | (sgn).should.be.type("object"); 47 | }); 48 | 49 | it ("should have the correct multisignature fee", function () { 50 | sgn = createMultisignature('secret', 'second secret', keysgroup, 255, 2); 51 | sgn['fee'].should.equal((keysgroup.length + 1) * constants.fees.multisignature); 52 | }); 53 | 54 | it("should create transaction with fee override", function () { 55 | const feeOverride = 1000000 56 | trs = createMultisignature('secret', 'second secret', keysgroup, 255, 2, feeOverride); 57 | (trs).should.be.ok; 58 | (trs.fee).should.equal((keysgroup.length + 1) * feeOverride) 59 | }); 60 | 61 | it("should fail to create transaction with invalid fee override", function (done) { 62 | const feeOverride = '1000000' 63 | try { 64 | trs = createMultisignature('secret', 'second secret', keysgroup, 255, 2, feeOverride); 65 | should.fail() 66 | } catch (error) { 67 | done() 68 | } 69 | }); 70 | 71 | it("should be deserialised correctly", function () { 72 | sgn = createMultisignature('secret key', 'second secret key', keysgroup, 255, 2); 73 | var deserialisedTx = ark.crypto.fromBytes(ark.crypto.getBytes(sgn).toString("hex")); 74 | delete deserialisedTx.vendorFieldHex; 75 | var keys = Object.keys(deserialisedTx) 76 | for(key in keys){ 77 | if(keys[key] == "asset"){ 78 | deserialisedTx.asset.multisignature.min.should.equal(sgn.asset.multisignature.min); 79 | deserialisedTx.asset.multisignature.lifetime.should.equal(sgn.asset.multisignature.lifetime); 80 | deserialisedTx.asset.multisignature.keysgroup.length.should.equal(sgn.asset.multisignature.keysgroup.length); 81 | console.log(JSON.stringify(deserialisedTx.asset.multisignature.keysgroup)); 82 | deserialisedTx.asset.multisignature.keysgroup[0].should.equal(sgn.asset.multisignature.keysgroup[0]); 83 | deserialisedTx.asset.multisignature.keysgroup[1].should.equal(sgn.asset.multisignature.keysgroup[1]); 84 | deserialisedTx.asset.multisignature.keysgroup[2].should.equal(sgn.asset.multisignature.keysgroup[2]); 85 | } 86 | else { 87 | deserialisedTx[keys[key]].should.equal(sgn[keys[key]]); 88 | } 89 | } 90 | }); 91 | 92 | describe("returned multisignature transaction", function () { 93 | it("should have empty recipientId", function () { 94 | (sgn).should.have.property("recipientId").equal(null); 95 | }); 96 | 97 | it("should have amount equal 0", function () { 98 | (sgn.amount).should.be.type("number").equal(0); 99 | }); 100 | 101 | it("should have asset", function () { 102 | (sgn.asset).should.be.type("object"); 103 | (sgn.asset).should.be.not.empty; 104 | }); 105 | 106 | it("should have multisignature inside asset", function () { 107 | (sgn.asset).should.have.property("multisignature"); 108 | }); 109 | 110 | describe("multisignature asset", function () { 111 | it("should be ok", function () { 112 | (sgn.asset.multisignature).should.be.ok; 113 | }) 114 | 115 | it("should be object", function () { 116 | (sgn.asset.multisignature).should.be.type("object"); 117 | }); 118 | 119 | it("should have min property", function () { 120 | (sgn.asset.multisignature).should.have.property("min"); 121 | }); 122 | 123 | it("should have lifetime property", function () { 124 | (sgn.asset.multisignature).should.have.property("lifetime"); 125 | }); 126 | 127 | it("should have keysgroup property", function () { 128 | (sgn.asset.multisignature).should.have.property("keysgroup"); 129 | }); 130 | 131 | it("should have 3 keys keysgroup", function () { 132 | (sgn.asset.multisignature.keysgroup.length).should.be.equal(3); 133 | }); 134 | }); 135 | }); 136 | }); 137 | 138 | }); 139 | -------------------------------------------------------------------------------- /test/signature/index.js: -------------------------------------------------------------------------------- 1 | var Buffer = require("buffer/").Buffer; 2 | var should = require("should"); 3 | var ark = require("../../index.js"); 4 | 5 | describe("signature.js", function () { 6 | 7 | var signature = ark.signature; 8 | 9 | it("should be ok", function () { 10 | (signature).should.be.ok; 11 | }); 12 | 13 | it("should be object", function () { 14 | (signature).should.be.type("object"); 15 | }); 16 | 17 | it("should have properties", function () { 18 | (signature).should.have.property("createSignature"); 19 | }); 20 | 21 | describe("#createSignature", function () { 22 | var createSignature = signature.createSignature; 23 | var sgn = null; 24 | 25 | it("should be function", function () { 26 | (createSignature).should.be.type("function"); 27 | }); 28 | 29 | it("should create signature transaction", function () { 30 | sgn = createSignature("secret", "second secret"); 31 | (sgn).should.be.ok; 32 | (sgn).should.be.type("object"); 33 | }); 34 | 35 | it("should create signature transaction", function () { 36 | var secretKey = ark.ECPair.fromSeed("secret"); 37 | secretKey.publicKey = secretKey.getPublicKeyBuffer().toString("hex"); 38 | 39 | sgn = createSignature(secretKey, "second secret"); 40 | (sgn).should.be.ok; 41 | (sgn).should.be.type("object"); 42 | }); 43 | 44 | it("should create transaction with fee override", function () { 45 | const feeOverride = 1000000 46 | trs = createSignature('secret', 'second secret', feeOverride); 47 | (trs).should.be.ok; 48 | (trs.fee).should.equal(feeOverride) 49 | }); 50 | 51 | it("should fail to create transaction with invalid fee override", function (done) { 52 | const feeOverride = '1000000' 53 | try { 54 | trs = createSignature('secret', 'second secret', feeOverride); 55 | should.fail() 56 | } catch (error) { 57 | done() 58 | } 59 | }); 60 | 61 | it("should be deserialised correctly", function () { 62 | var deserialisedTx = ark.crypto.fromBytes(ark.crypto.getBytes(sgn).toString("hex")); 63 | delete deserialisedTx.vendorFieldHex; 64 | var keys = Object.keys(deserialisedTx) 65 | for(key in keys){ 66 | if(keys[key] == "asset"){ 67 | deserialisedTx.asset.signature.publicKey.should.equal(sgn.asset.signature.publicKey); 68 | } 69 | else { 70 | deserialisedTx[keys[key]].should.equal(sgn[keys[key]]); 71 | } 72 | } 73 | }); 74 | 75 | describe("returned signature transaction", function () { 76 | it("should have empty recipientId", function () { 77 | (sgn).should.have.property("recipientId").equal(null); 78 | }); 79 | 80 | it("should have amount equal 0", function () { 81 | (sgn.amount).should.be.type("number").equal(0); 82 | }); 83 | 84 | it("should have asset", function () { 85 | (sgn.asset).should.be.type("object"); 86 | (sgn.asset).should.be.not.empty; 87 | }); 88 | 89 | it("should have signature inside asset", function () { 90 | (sgn.asset).should.have.property("signature"); 91 | }); 92 | 93 | describe("signature asset", function () { 94 | it("should be ok", function () { 95 | (sgn.asset.signature).should.be.ok; 96 | }) 97 | 98 | it("should be object", function () { 99 | (sgn.asset.signature).should.be.type("object"); 100 | }); 101 | 102 | it("should have publicKey property", function () { 103 | (sgn.asset.signature).should.have.property("publicKey"); 104 | }); 105 | 106 | it("should have publicKey in hex", function () { 107 | (sgn.asset.signature.publicKey).should.be.type("string").and.match(function () { 108 | try { 109 | new Buffer(sgn.asset.signature.publicKey); 110 | } catch (e) { 111 | return false; 112 | } 113 | 114 | return true; 115 | }); 116 | }); 117 | 118 | it("should have publicKey in 33 bytes", function () { 119 | var publicKey = new Buffer(sgn.asset.signature.publicKey, "hex"); 120 | (publicKey.length).should.be.equal(33); 121 | }); 122 | }); 123 | }); 124 | }); 125 | 126 | }); 127 | -------------------------------------------------------------------------------- /test/slot/index.js: -------------------------------------------------------------------------------- 1 | var Buffer = require("buffer/").Buffer; 2 | var should = require("should"); 3 | var ark = require("../../index.js"); 4 | 5 | describe("slots.js", function () { 6 | 7 | var slots = require("../../lib/time/slots.js"); 8 | 9 | it("should be ok", function () { 10 | (slots).should.be.ok; 11 | }); 12 | 13 | it("should be object", function () { 14 | (slots).should.be.type("object"); 15 | }); 16 | 17 | it("should have properties", function () { 18 | var properties = ["interval", "delegates", "getTime", "getRealTime", "getSlotNumber", "getSlotTime", "getNextSlot", "getLastSlot"]; 19 | properties.forEach(function (property) { 20 | (slots).should.have.property(property); 21 | }); 22 | }); 23 | 24 | describe(".interval", function () { 25 | var interval = slots.interval; 26 | 27 | it("should be ok", function () { 28 | (interval).should.be.ok; 29 | }); 30 | 31 | it("should be number and not NaN", function () { 32 | (interval).should.be.type("number").and.not.NaN; 33 | }); 34 | }); 35 | 36 | describe(".delegates", function () { 37 | var delegates = slots.delegates; 38 | 39 | it("should be ok", function () { 40 | (delegates).should.be.ok; 41 | }); 42 | 43 | it("should be number and not NaN", function () { 44 | (delegates).should.be.type("number").and.not.NaN; 45 | }); 46 | }); 47 | 48 | describe("#getTime", function () { 49 | var getTime = slots.getTime; 50 | 51 | it("should be ok", function () { 52 | (getTime).should.be.ok; 53 | }); 54 | 55 | it("should be a function", function () { 56 | (getTime).should.be.type("function"); 57 | }); 58 | 59 | it("should return epoch time as number, equal to 10", function () { 60 | var d = 1490101210000; 61 | var time = getTime(d); 62 | (time).should.be.type("number").and.equal(10); 63 | }); 64 | }); 65 | 66 | describe("#getRealTime", function () { 67 | var getRealTime = slots.getRealTime; 68 | 69 | it("should be ok", function () { 70 | (getRealTime).should.be.ok; 71 | }); 72 | 73 | it("should be a function", function () { 74 | (getRealTime).should.be.type("function"); 75 | }); 76 | 77 | it("should return return real time, convert 10 to 1490101210000", function () { 78 | var d = 10; 79 | var real = getRealTime(d); 80 | (real).should.be.ok; 81 | (real).should.be.type("number").and.equal(1490101210000); 82 | }); 83 | }); 84 | 85 | describe("#getSlotNumber", function () { 86 | var getSlotNumber = slots.getSlotNumber; 87 | 88 | it("should be ok", function () { 89 | (getSlotNumber).should.be.ok; 90 | }); 91 | 92 | it("should be a function", function () { 93 | (getSlotNumber).should.be.type("function"); 94 | }); 95 | 96 | it("should return slot number, equal to 1", function () { 97 | var slot = getSlotNumber(10); 98 | (slot).should.be.type("number").and.equal(1); 99 | }); 100 | }); 101 | 102 | describe("#getSlotTime", function () { 103 | var getSlotTime = slots.getSlotTime; 104 | 105 | it("should be ok", function () { 106 | (getSlotTime).should.be.ok; 107 | }); 108 | 109 | it("should be function", function () { 110 | (getSlotTime).should.be.type("function"); 111 | }); 112 | 113 | it("should return slot time number, equal to ", function () { 114 | var slotTime = getSlotTime(19614); 115 | (slotTime).should.be.ok; 116 | (slotTime).should.be.type("number").and.equal(156912); 117 | }); 118 | }); 119 | 120 | describe("#getNextSlot", function () { 121 | var getNextSlot = slots.getNextSlot; 122 | 123 | it("should be ok", function () { 124 | (getNextSlot).should.be.ok; 125 | }); 126 | 127 | it("should be function", function () { 128 | (getNextSlot).should.be.type("function"); 129 | }); 130 | 131 | it("should return next slot number", function () { 132 | var nextSlot = getNextSlot(); 133 | (nextSlot).should.be.ok; 134 | (nextSlot).should.be.type("number").and.not.NaN; 135 | }); 136 | }); 137 | 138 | describe("#getLastSlot", function () { 139 | var getLastSlot = slots.getLastSlot; 140 | 141 | it("should be ok", function () { 142 | (getLastSlot).should.be.ok; 143 | }); 144 | 145 | it("should be function", function () { 146 | (getLastSlot).should.be.type("function"); 147 | }); 148 | 149 | it("should return last slot number", function () { 150 | var lastSlot = getLastSlot(slots.getNextSlot()); 151 | (lastSlot).should.be.ok; 152 | (lastSlot).should.be.type("number").and.not.NaN; 153 | }); 154 | }); 155 | 156 | }); 157 | -------------------------------------------------------------------------------- /test/transaction/index.js: -------------------------------------------------------------------------------- 1 | var Buffer = require("buffer/").Buffer; 2 | var should = require("should"); 3 | var ark = require("../../index.js"); 4 | 5 | describe("transaction.js", function () { 6 | 7 | var transaction = ark.transaction; 8 | 9 | it("should be object", function () { 10 | (transaction).should.be.type("object"); 11 | }); 12 | 13 | it("should have properties", function () { 14 | (transaction).should.have.property("createTransaction"); 15 | }) 16 | 17 | describe("#createTransaction", function () { 18 | var createTransaction = transaction.createTransaction; 19 | var trs = null; 20 | 21 | it("should be a function", function () { 22 | (createTransaction).should.be.type("function"); 23 | }); 24 | 25 | it("should create transaction without second signature", function () { 26 | trs = createTransaction("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 1000, null, "secret"); 27 | (trs).should.be.ok; 28 | }); 29 | 30 | it("should create transaction without second signature from keys", function () { 31 | var secretKey = ark.ECPair.fromSeed("secret"); 32 | secretKey.publicKey = secretKey.getPublicKeyBuffer().toString("hex"); 33 | 34 | trs = createTransaction("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 1000, null, secretKey); 35 | (trs).should.be.ok; 36 | }); 37 | 38 | it("should create transaction with vendorField", function () { 39 | trs = createTransaction("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 1000, "this is a test vendorfield", "secret"); 40 | (trs).should.be.ok; 41 | }); 42 | 43 | it("should create transaction with fee override", function () { 44 | const feeOverride = 1000000 45 | trs = createTransaction("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 1000, null, "secret", undefined, undefined, feeOverride); 46 | (trs).should.be.ok; 47 | (trs.fee).should.equal(feeOverride) 48 | }); 49 | 50 | it("should fail to create transaction with invalid fee override", function (done) { 51 | const feeOverride = '1000000' 52 | try { 53 | trs = createTransaction("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 1000, null, "secret", undefined, undefined, feeOverride); 54 | should.fail() 55 | } catch (error) { 56 | done() 57 | } 58 | }); 59 | 60 | it("should fail if transaction with vendorField length > 64", function () { 61 | var vf="z"; 62 | for(i=0;i<6;i++){ 63 | vf=vf+vf; 64 | } 65 | vf=vf+"z"; 66 | trs = createTransaction("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 1000, vf, "secret"); 67 | return (trs===null).should.equal(true); 68 | 69 | }); 70 | 71 | it("should be ok if transaction with vendorField length = 64", function () { 72 | var vf="z"; 73 | for(i=0;i<6;i++){ 74 | vf=vf+vf; 75 | } 76 | trs = createTransaction("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 1000, vf, "secret"); 77 | (trs).should.be.ok; 78 | }); 79 | 80 | describe("returned transaction", function () { 81 | it("should be object", function () { 82 | (trs).should.be.type("object"); 83 | }); 84 | 85 | it("should have id as string", function () { 86 | (trs.id).should.be.type("string"); 87 | }); 88 | 89 | it("should have type as number and eqaul 0", function () { 90 | (trs.type).should.be.type("number").and.equal(0); 91 | }); 92 | 93 | it("should have timestamp as number", function () { 94 | (trs.timestamp).should.be.type("number").and.not.NaN; 95 | }); 96 | 97 | it("should have senderPublicKey as hex string", function () { 98 | (trs.senderPublicKey).should.be.type("string").and.match(function () { 99 | try { 100 | new Buffer(trs.senderPublicKey, "hex") 101 | } catch (e) { 102 | return false; 103 | } 104 | 105 | return true; 106 | }) 107 | }); 108 | 109 | it("should have recipientId as string and to be equal AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", function () { 110 | (trs.recipientId).should.be.type("string").and.equal("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff"); 111 | }); 112 | 113 | it("should have amount as number and eqaul to 1000", function () { 114 | (trs.amount).should.be.type("number").and.equal(1000); 115 | }); 116 | 117 | it("should have empty asset object", function () { 118 | (trs.asset).should.be.type("object").and.empty; 119 | }); 120 | 121 | it("should does not have second signature", function () { 122 | (trs).should.not.have.property("signSignature"); 123 | }); 124 | 125 | it("should have signature as hex string", function () { 126 | (trs.signature).should.be.type("string").and.match(function () { 127 | try { 128 | new Buffer(trs.signature, "hex") 129 | } catch (e) { 130 | return false; 131 | } 132 | 133 | return true; 134 | }) 135 | }); 136 | 137 | it("should be signed correctly", function () { 138 | var result = ark.crypto.verify(trs); 139 | result.should.equal(true); 140 | }); 141 | 142 | it("should be deserialised correctly", function () { 143 | var deserialisedTx = ark.crypto.fromBytes(ark.crypto.getBytes(trs).toString("hex")); 144 | deserialisedTx.vendorField = new Buffer(deserialisedTx.vendorFieldHex, "hex").toString("utf8") 145 | delete deserialisedTx.vendorFieldHex; 146 | var keys = Object.keys(deserialisedTx) 147 | for(key in keys){ 148 | if(keys[key] != "vendorFieldHex"){ 149 | deserialisedTx[keys[key]].should.equal(trs[keys[key]]); 150 | } 151 | } 152 | 153 | }); 154 | 155 | it("should not be signed correctly now", function () { 156 | trs.amount = 10000; 157 | var result = ark.crypto.verify(trs); 158 | result.should.equal(false); 159 | }); 160 | }); 161 | }); 162 | 163 | describe("createTransaction and try to tamper signature", function(){ 164 | 165 | it("should not validate overflown signatures", function(){ 166 | var BigInteger = require('bigi') 167 | var bip66 = require('bip66') 168 | 169 | // custom bip66 encode for hacking away signature 170 | function BIP66_encode (r, s) { 171 | var lenR = r.length; 172 | var lenS = s.length; 173 | var signature = new Buffer(6 + lenR + lenS); 174 | 175 | // 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] 176 | signature[0] = 0x30; 177 | signature[1] = signature.length - 2; 178 | signature[2] = 0x02; 179 | signature[3] = r.length; 180 | r.copy(signature, 4); 181 | signature[4 + lenR] = 0x02; 182 | signature[5 + lenR] = s.length; 183 | s.copy(signature, 6 + lenR); 184 | 185 | return signature; 186 | } 187 | 188 | // The transaction to replay 189 | var old_transaction = ark.transaction.createTransaction('AacRfTLtxAkR3Mind1XdPCddj1uDkHtwzD', 1, null, 'randomstring'); 190 | 191 | // Decode signature 192 | var decode = bip66.decode(Buffer(old_transaction.signature, "hex")); 193 | 194 | var r = BigInteger.fromDERInteger(decode.r); 195 | var s = BigInteger.fromDERInteger(decode.s); 196 | 197 | // Transform the signature 198 | /* 199 | result = r|00 200 | result = result - r 201 | r = r + result 202 | */ 203 | 204 | result = BigInteger.fromBuffer(Buffer(r.toBuffer(r.toDERInteger().length).toString('hex') + '06', 'hex')); 205 | result = result.subtract(r); 206 | r = r.add(result); 207 | 208 | new_signature = BIP66_encode(r.toBuffer(r.toDERInteger().length), s.toBuffer(s.toDERInteger().length)).toString('hex'); 209 | // 210 | // console.log("OLD TRANSACTION : "); 211 | // console.log("TXID " + ark.crypto.getId(old_transaction)); 212 | // console.log("VERIFY " + ark.crypto.verify(old_transaction)); 213 | // console.log("SIG " + old_transaction.signature + "\n"); 214 | 215 | ark.crypto.verify(old_transaction).should.equal(true); 216 | 217 | old_transaction.signature = new_signature; 218 | // 219 | // console.log("NEW TRANSACTION : "); 220 | // console.log("TXID " + ark.crypto.getId(old_transaction)); 221 | // console.log("VERIFY " + ark.crypto.verify(old_transaction)); 222 | // console.log("SIG " + old_transaction.signature); 223 | 224 | ark.crypto.verify(old_transaction).should.equal(false); 225 | 226 | }); 227 | 228 | }); 229 | 230 | describe("#createTransaction with second secret", function () { 231 | var createTransaction = transaction.createTransaction; 232 | var trs = null; 233 | var secondSecret = "second secret"; 234 | var keys = ark.crypto.getKeys(secondSecret); 235 | 236 | it("should be a function", function () { 237 | (createTransaction).should.be.type("function"); 238 | }); 239 | 240 | it("should not accept bitcoin address", function(){ 241 | try { 242 | trs = createTransaction("14owCmVDn8SaAFZcLbZfCVu5jvc4Lq7Tm1", 1000, null, "secret", secondSecret); 243 | } catch(error){ 244 | return (error).should.have.property("message").and.equal("Wrong recipientId") 245 | } 246 | true.should.equal(false); 247 | }); 248 | 249 | it("should create transaction without second signature", function () { 250 | trs = createTransaction("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", 1000, null, "secret", secondSecret); 251 | (trs).should.be.ok; 252 | }); 253 | 254 | describe("returned transaction", function () { 255 | it("should be object", function () { 256 | (trs).should.be.type("object"); 257 | }); 258 | 259 | it("should have id as string", function () { 260 | (trs.id).should.be.type("string"); 261 | }); 262 | 263 | it("should have type as number and eqaul 0", function () { 264 | (trs.type).should.be.type("number").and.equal(0); 265 | }); 266 | 267 | it("should have timestamp as number", function () { 268 | (trs.timestamp).should.be.type("number").and.not.NaN; 269 | }); 270 | 271 | it("should have senderPublicKey as hex string", function () { 272 | (trs.senderPublicKey).should.be.type("string").and.match(function () { 273 | try { 274 | new Buffer(trs.senderPublicKey, "hex") 275 | } catch (e) { 276 | return false; 277 | } 278 | 279 | return true; 280 | }) 281 | }); 282 | 283 | it("should have recipientId as string and to be equal AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", function () { 284 | (trs.recipientId).should.be.type("string").and.equal("AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff"); 285 | }); 286 | 287 | it("should have amount as number and eqaul to 1000", function () { 288 | (trs.amount).should.be.type("number").and.equal(1000); 289 | }); 290 | 291 | it("should have empty asset object", function () { 292 | (trs.asset).should.be.type("object").and.empty; 293 | }); 294 | 295 | it("should have second signature", function () { 296 | (trs).should.have.property("signSignature"); 297 | }); 298 | 299 | it("should have signature as hex string", function () { 300 | (trs.signature).should.be.type("string").and.match(function () { 301 | try { 302 | new Buffer(trs.signature, "hex") 303 | } catch (e) { 304 | return false; 305 | } 306 | 307 | return true; 308 | }) 309 | }); 310 | 311 | it("should have signSignature as hex string", function () { 312 | (trs.signSignature).should.be.type("string").and.match(function () { 313 | try { 314 | new Buffer(trs.signSignature, "hex"); 315 | } catch (e) { 316 | return false; 317 | } 318 | 319 | return true; 320 | }); 321 | }); 322 | 323 | it("should be deserialised correctly", function () { 324 | var deserialisedTx = ark.crypto.fromBytes(ark.crypto.getBytes(trs).toString("hex")); 325 | delete deserialisedTx.vendorFieldHex; 326 | var keys = Object.keys(deserialisedTx) 327 | for(key in keys){ 328 | deserialisedTx[keys[key]].should.equal(trs[keys[key]]); 329 | } 330 | 331 | }); 332 | 333 | it("should be signed correctly", function () { 334 | var result = ark.crypto.verify(trs); 335 | (result).should.equal(true); 336 | }); 337 | 338 | it("should be second signed correctly", function () { 339 | var result = ark.crypto.verifySecondSignature(trs, keys.publicKey); 340 | (result).should.equal(true); 341 | }); 342 | 343 | it("should not be signed correctly now", function () { 344 | trs.amount = 10000; 345 | var result = ark.crypto.verify(trs); 346 | (result).should.equal(false); 347 | }); 348 | 349 | it("should not be second signed correctly now", function () { 350 | trs.amount = 10000; 351 | var result = ark.crypto.verifySecondSignature(trs, keys.publicKey); 352 | (result).should.equal(false); 353 | }); 354 | }); 355 | }); 356 | 357 | }); 358 | -------------------------------------------------------------------------------- /test/vote/index.js: -------------------------------------------------------------------------------- 1 | var Buffer = require("buffer/").Buffer; 2 | var should = require("should"); 3 | var ark = require("../../index.js"); 4 | 5 | var NETWORKS = require('../../lib/networks'); 6 | 7 | describe("vote.js", function () { 8 | 9 | var vote = ark.vote; 10 | 11 | it("should be ok", function () { 12 | (vote).should.be.ok; 13 | }); 14 | 15 | it("should be object", function () { 16 | (vote).should.be.type("object"); 17 | }); 18 | 19 | it("should have createVote property", function () { 20 | (vote).should.have.property("createVote"); 21 | }); 22 | 23 | describe("#createVote", function () { 24 | var createVote = vote.createVote, 25 | vt = null, 26 | publicKey = ark.crypto.getKeys("secret").publicKey, 27 | publicKeys = ["+" + publicKey]; 28 | 29 | it("should be ok", function () { 30 | (createVote).should.be.ok; 31 | }); 32 | 33 | it("should be function", function () { 34 | (createVote).should.be.type("function"); 35 | }); 36 | 37 | it("should create vote", function () { 38 | vt = createVote("secret", publicKeys, "second secret"); 39 | }); 40 | 41 | it("should create vote from ecpair", function () { 42 | var secretKey = ark.ECPair.fromSeed("secret"); 43 | secretKey.publicKey = secretKey.getPublicKeyBuffer().toString("hex"); 44 | 45 | var secondSecretKey = ark.ECPair.fromSeed("second secret"); 46 | secondSecretKey.publicKey = secondSecretKey.getPublicKeyBuffer().toString("hex"); 47 | 48 | vt = createVote(secretKey, publicKeys, secondSecretKey); 49 | }); 50 | 51 | it("should create vote from wif", function () { 52 | var secretKey = ark.ECPair.fromWIF("SB3iDxYmKgjkhfDZSKgLaBrp3Ynzd3yd3ZZF2ujVBK7vLpv6hWKK", NETWORKS.ark); 53 | secretKey.publicKey = secretKey.getPublicKeyBuffer().toString("hex"); 54 | 55 | var tx = createVote(secretKey, publicKeys); 56 | (tx).should.be.ok; 57 | (tx).should.be.type("object"); 58 | (tx).should.have.property("recipientId").and.be.type("string").and.be.equal("AL9uJWA5nd6RWn8VSUzGN7spWeZGHeudg9"); 59 | }); 60 | 61 | it("should create transaction with fee override", function () { 62 | const feeOverride = 1000000 63 | trs = createVote('secret', publicKeys, 'second secret', feeOverride); 64 | (trs).should.be.ok; 65 | (trs.fee).should.equal(feeOverride) 66 | }); 67 | 68 | it("should fail to create transaction with invalid fee override", function (done) { 69 | const feeOverride = '1000000' 70 | try { 71 | trs = createVote('secret', publicKeys, 'second secret', feeOverride); 72 | should.fail() 73 | } catch (error) { 74 | done() 75 | } 76 | }); 77 | 78 | it("should be deserialised correctly", function () { 79 | var deserialisedTx = ark.crypto.fromBytes(ark.crypto.getBytes(vt).toString("hex")); 80 | delete deserialisedTx.vendorFieldHex; 81 | var keys = Object.keys(deserialisedTx) 82 | for(key in keys){ 83 | if(keys[key] == "asset"){ 84 | deserialisedTx.asset.votes[0].should.equal(vt.asset.votes[0]); 85 | } 86 | else{ 87 | deserialisedTx[keys[key]].should.equal(vt[keys[key]]); 88 | } 89 | } 90 | 91 | }); 92 | 93 | describe("returned vote", function () { 94 | it("should be ok", function () { 95 | (vt).should.be.ok; 96 | }); 97 | 98 | it("should be object", function () { 99 | (vt).should.be.type("object"); 100 | }); 101 | 102 | it("should have recipientId string equal to sender", function () { 103 | (vt).should.have.property("recipientId").and.be.type("string").and.equal(ark.crypto.getAddress(publicKey)) 104 | }); 105 | 106 | it("should have amount number equal to 0", function () { 107 | (vt).should.have.property("amount").and.be.type("number").and.equal(0); 108 | }); 109 | 110 | it("should have type number equal to 3", function () { 111 | (vt).should.have.property("type").and.be.type("number").and.equal(3); 112 | }); 113 | 114 | it("should have timestamp number", function () { 115 | (vt).should.have.property("timestamp").and.be.type("number"); 116 | }); 117 | 118 | it("should have senderPublicKey hex string equal to sender public key", function () { 119 | (vt).should.have.property("senderPublicKey").and.be.type("string").and.match(function () { 120 | try { 121 | new Buffer(vt.senderPublicKey, "hex"); 122 | } catch (e) { 123 | return false; 124 | } 125 | 126 | return true; 127 | }).and.equal(publicKey); 128 | }); 129 | 130 | it("should have signature hex string", function () { 131 | (vt).should.have.property("signature").and.be.type("string").and.match(function () { 132 | try { 133 | new Buffer(vt.signature, "hex"); 134 | } catch (e) { 135 | return false; 136 | } 137 | 138 | return true; 139 | }); 140 | }); 141 | 142 | it("should have second signature hex string", function () { 143 | (vt).should.have.property("signSignature").and.be.type("string").and.match(function () { 144 | try { 145 | new Buffer(vt.signSignature, "hex"); 146 | } catch (e) { 147 | return false; 148 | } 149 | 150 | return true; 151 | }); 152 | }); 153 | 154 | it("should be signed correctly", function () { 155 | var result = ark.crypto.verify(vt); 156 | (result).should.be.ok; 157 | }); 158 | 159 | it("should be second signed correctly", function () { 160 | var result = ark.crypto.verifySecondSignature(vt, ark.crypto.getKeys("second secret").publicKey); 161 | (result).should.be.ok; 162 | }); 163 | 164 | it("should not be signed correctly now", function () { 165 | vt.amount = 100; 166 | var result = ark.crypto.verify(vt); 167 | (result).should.be.not.ok; 168 | }); 169 | 170 | it("should not be second signed correctly now", function () { 171 | vt.amount = 100; 172 | var result = ark.crypto.verifySecondSignature(vt, ark.crypto.getKeys("second secret").publicKey); 173 | (result).should.be.not.ok; 174 | }); 175 | 176 | it("should have asset", function () { 177 | (vt).should.have.property("asset").and.not.empty; 178 | }); 179 | 180 | describe("vote asset", function () { 181 | it("should be ok", function () { 182 | (vt.asset).should.have.property("votes").and.be.ok; 183 | }); 184 | 185 | it("should be object", function () { 186 | (vt.asset.votes).should.be.type("object"); 187 | }); 188 | 189 | it("should be not empty", function () { 190 | (vt.asset.votes).should.be.not.empty; 191 | }); 192 | 193 | it("should contains one element", function () { 194 | (vt.asset.votes.length).should.be.equal(1); 195 | }); 196 | 197 | it("should have public keys in hex", function () { 198 | vt.asset.votes.forEach(function (v) { 199 | (v).should.be.type("string").startWith("+").and.match(function () { 200 | try { 201 | new Buffer(v.substring(1, v.length), "hex"); 202 | } catch (e) { 203 | return false; 204 | } 205 | 206 | return true; 207 | }); 208 | }); 209 | }); 210 | 211 | it("should be equal to sender public key", function () { 212 | var v = vt.asset.votes[0]; 213 | (v.substring(1, v.length)).should.be.equal(publicKey); 214 | }); 215 | }) 216 | }); 217 | }); 218 | 219 | }); 220 | --------------------------------------------------------------------------------