Compare commits

...

52 Commits

Author SHA1 Message Date
Geert Weening
0f212e4dd1 [TASK] bump version to 0.10.1-rc1 2015-01-06 14:59:05 -08:00
Geert Weening
c263654c88 [DOC] update release notes 2015-01-06 14:58:30 -08:00
Geert Weening
874e3f24a6 Merge branch 'release' into develop 2015-01-06 14:52:03 -08:00
Geert Weening
b14343f3cf [TASK] shrinkwrap dependencies 2015-01-06 14:48:13 -08:00
Geert Weening
732b50dea7 [TASK] bump version to 0.10.0 2015-01-06 14:48:05 -08:00
Geert Weening
be3bbe9b61 [DOC] update release notes 2015-01-06 14:46:44 -08:00
Geert Weening
51211bbba0 Revert "[TASK] Add Transaction.getBalanceChanges, computed from metadata"
See the balance change package in [ripple-lib-extensions](https://github.com/ripple/ripple-lib-extensions)

This reverts commit 72387873b4.

Conflicts:
	test/metadata-test.js
2015-01-06 14:42:24 -08:00
Geert Weening
5d1ff1c912 Merge pull request #245 from ripple/binary-account-tx
Wrap account_tx binary parsing in async.setImmediate
2015-01-06 11:08:22 -08:00
wltsmrz
ea1be4fc50 Wrap account_tx binary parsing in async.setImmediate 2015-01-06 06:51:18 -08:00
Geert Weening
7cc05f0d92 Merge pull request #244 from mehulkar/patch-1
Update link to deprecated API docs
2015-01-05 14:48:12 -08:00
Mehul Kar
54606f3c21 Update link to deprecated API docs 2015-01-05 14:43:05 -08:00
wltsmrz
a5d1705930 Merge pull request #236 from ripple/sum-account-offers
Fix cumulative account order tracking for native amounts
2014-12-26 21:06:42 -08:00
wltsmrz
bfc0fb6c88 Merge pull request #240 from lumberj/task/add_limit_to_bookoffers
[TASK] Allow `limit` option in remote.requestBookOffers
2014-12-24 14:44:15 -08:00
Alan Cohen
d1d4452217 [TASK] Allow limit option in remote.requestBookOffers 2014-12-24 14:24:32 -08:00
wltsmrz
2166a434a3 Merge pull request #241 from ripple/add-jshintrc
Add jshintrc
2014-12-23 23:08:52 -08:00
wltsmrz
1053fa18e1 Add jshintrc 2014-12-23 20:20:31 -08:00
wltsmrz
fa147d467e Merge pull request #239 from ripple/metadata-account-fix
Fix metadata account check
2014-12-23 20:19:09 -08:00
wltsmrz
3f61598d6c Fix metadata account check 2014-12-23 03:22:46 -08:00
wltsmrz
9bf3724ce6 Merge pull request #237 from lumberj/task/fix_jsdoc
[TASK] Fixup the annotation for remote.requestBookOffers
2014-12-22 15:37:28 -08:00
Alan Cohen
c2f27a4deb [TASK] Fixup the annotation for remote.requestBookOffers 2014-12-22 15:31:56 -08:00
wltsmrz
b6b99dde02 Fix cumulative account order tracking for native amounts 2014-12-20 20:43:01 -08:00
Geert Weening
1fd0f4a8fe Merge pull request #234 from ripple/sum-account-offers
Track order funded status based on cumulative account offers
2014-12-19 17:27:38 -08:00
wltsmrz
67d39737a4 Track order funded status based on cumulative account orders 2014-12-19 16:43:37 -08:00
Geert Weening
aef4fe29a3 Merge pull request #233 from lumberj/feature_test/request_book_offers
[FEATURE] Add tests and support for ledger for requestBookOffers (RLJS-115)
2014-12-19 16:39:24 -08:00
Alan Cohen
34c0677c45 [FEATURE] Add tests and support for ledger for requestBookOffers 2014-12-19 16:35:29 -08:00
Geert Weening
3cb4a64b47 [TEST] fix Meta require 2014-12-19 16:29:29 -08:00
Geert Weening
0db0375a5e Merge pull request #227 from clark800/feature/balance_changes
[TASK] Add balance_change array to transaction response, computed from metadata
2014-12-19 15:14:22 -08:00
wltsmrz
47e6bdc644 Merge pull request #231 from clark800/feature/test_compiled_js
[TASK] Refactor tests to support testing compiled javascript
2014-12-19 13:28:03 -08:00
Chris Clark
66c2e27711 [TASK] Refactor tests to support testing compiled javascript 2014-12-19 13:17:02 -08:00
Chris Clark
72387873b4 [TASK] Add Transaction.getBalanceChanges, computed from metadata 2014-12-18 16:12:06 -08:00
Geert Weening
59017bc0bd Merge pull request #229 from ripple/account-txn-id
Add AccountTxnID flag and setter
2014-12-17 12:42:40 -08:00
wltsmrz
2dde114d3d Add AccountTxnID flag and setter 2014-12-16 13:25:32 -08:00
wltsmrz
9e89904f03 Fix undefined log function in Transaction.finalize() 2014-12-12 21:26:54 -08:00
Geert Weening
56d0aca254 Merge pull request #224 from kureus/patch-1
Doc example code syntax error
2014-12-12 11:13:54 -08:00
Dan Quirk
239710cebf Doc example code syntax error 2014-12-11 13:16:14 +00:00
Geert Weening
1eaad617cb [TASK] bump version to 0.10.0-rc2 2014-12-11 00:20:50 -08:00
wltsmrz
3c21994adc [FIX] Fix empty queue check on reconnect 2014-12-10 23:58:00 -08:00
wltsmrz
d15d14e197 [FIX] Fix undefined Transaction.prototype.summary() 2014-12-10 14:17:28 -08:00
Geert Weening
e32694dc79 [TASK] bump version to 0.10.0-rc1 2014-12-10 14:02:48 -08:00
wltsmrz
6ec8124287 [DOC] Update release notes 2014-12-10 14:00:10 -08:00
Geert Weening
2222adfc10 [DOC] update release notes 2014-12-10 13:29:47 -08:00
Geert Weening
fcc2377657 Merge pull request #223 from geertweening/feature/memo_format_type
[FEATURE] improve memo support
2014-12-10 13:08:04 -08:00
Geert Weening
1704ac4ae1 [FEATURE] improve memo support
- add MemoFormat property for memo
- MemoFormat and MemoType must be valid ASCII
- Memo content is converted on the serialization level
- add parsed_* version of Memo content if the parser understand the format
- support `text` and `json` MemoFormat

[FIX] double serialization overriding Memo contents

The copy made in from_json wasn't a deep copy
2014-12-10 13:07:44 -08:00
Geert Weening
666e4348e0 Merge pull request #222 from boxbag/transaction-set-fee
[FEATURE] allow per transaction fees to be set
2014-12-10 13:07:20 -08:00
Bo Chen
9b22f279bc [FEATURE] allow per transaction fees to be set 2014-12-10 11:52:16 -08:00
Geert Weening
0835de983b Merge pull request #221 from ripple/transaction-updates
Transaction updates
2014-12-10 11:37:36 -08:00
wltsmrz
1a892d58fc Rework tef and tel-class transaction error handling
Do not finalize tef or tel-class errors until LastLedgerSequence is
exceeded. Transactions that fail in this way will now be aborted
with a tej-class error. Errors like tefALREADY and tefMAX_LEDGER
should now be opaque to the user and have no consequence in
determining a final state.
2014-12-10 11:30:45 -08:00
wltsmrz
73a3cce4a4 Do not bump LastLedgerSequence on resubmit 2014-12-10 11:30:45 -08:00
wltsmrz
d5ef4774fa Make LastLedgerSequence offset configurable, default to 3 2014-12-10 11:30:45 -08:00
wltsmrz
c5bd4239a4 Cleanup
- Deprecate 'save' event
- Add TransactionQueue.getMinLedger(), use this as ledger_index_min
  in account_tx request on reconnect
- tx.sign() no longer accepts a callback
- Add various setters and jsdoc to transaction.js
- Normalize setters, e.g. sourceTag() and destinationTag()
- Minor optimization in call to tx.hash() in TransactionManager prior
  to submit; allow `serialized` argument to tx.hash() such that the
  transaction is not serialized twice
2014-12-10 11:30:45 -08:00
Geert Weening
634e811888 Merge pull request #220 from geertweening/update_sjcl
[TASK] update sjcl
2014-12-08 15:21:13 -08:00
Geert Weening
3204998fcb [TASK] update sjcl
- native support for k256
- improved entropy by taking advantage of platform crypto

- remove unnecessary sjcl overrides from sjcl-secp256k1.js
- updated ripple-lib curve instantiations to use k256
- add curve override so c256 points to k256

16dde36fa2
2014-12-08 15:02:24 -08:00
87 changed files with 4025 additions and 1347 deletions

1
.gitignore vendored
View File

@@ -25,6 +25,7 @@ Release/*.*
# Ignore locally installed node_modules
node_modules
!test/node_modules
# Ignore tmp directory.
tmp

View File

@@ -151,26 +151,30 @@ gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug', 'bowe
gulp.task('lint', function() {
gulp.src('src/js/ripple/*.js')
.pipe(jshint())
.pipe(jshint('./jshintrc'))
.pipe(map(function(file, callback) {
if (!file.jshint.success) {
console.log('\nIn', file.path);
file.jshint.results.forEach(function(err) {
if (err && err.error) {
var col1 = err.error.line + ':' + err.error.character;
var col2 = '[' + err.error.reason + ']';
var col3 = '(' + err.error.code + ')';
while (col1.length < 8) {
col1 += ' ';
}
console.log(' ' + [ col1, col2, col3 ].join(' '));
}
});
if (file.jshint.success) {
return callback(null, file);
}
console.log('\n>' + file.path.substring(file.base.length) + '\n');
file.jshint.results.forEach(function(err) {
if (err && err.error) {
var col1 = err.error.line + ':' + err.error.character;
var col2 = err.error.reason;
var col3 = '(' + err.error.code + ')';
while (col1.length < 8) {
col1 = ' ' + col1;
}
col1 += ' |';
console.log([ col1, col2, col3 ].join(' '));
}
});
callback(null, file);
}));
});

View File

@@ -1,3 +1,41 @@
##0.10.1
+ [Track the funded status of an order based on cumulative account orders](https://github.com/ripple/ripple-lib/commit/67d39737a4d5e0fcd9d9b47b9083ee00e5a9e652) and [67d3973](https://github.com/ripple/ripple-lib/commit/b6b99dde022e1e14c4797e454b1d7fca50e49482)
+ [Add support for `ledger` option in requestBookOffers](https://github.com/ripple/ripple-lib/commit/34c0677c453c409ef0a5b351959abdc176d3bacb)
+ [Add support for `limit` option in requestBookOffers](https://github.com/ripple/ripple-lib/commit/d1d4452217c878d0b377d24830b4cd8b3162f6e0)
+ [Fix metadata account check](https://github.com/ripple/ripple-lib/commit/3f61598d6c87e3cc877af60e2d515f9eff73dfe1)
##0.10.0
+ [Transaction changes](https://github.com/ripple/ripple-lib/pull/221)
+ **Important** `tef*` and `tel*` and errors will no longer be presented as
final. Rather than considering these errors final, ripple-lib will wait until
the `LastLedgerSequence` specified in the transaction is exceeded. This makes
failures more definitive, and ensures that no transaction will resubmit
indefinitely.
+ A new, final tej-class error is introduced to account for transactions that
are locally determined to have expired: `tejMaxLedger`.
+ [Allow per transaction fees to be set, `transaction.setFixedFee()`](https://github.com/ripple/ripple-lib/commit/9b22f279bcbe60ee6bcf4b7fa60a48e9c197a828)
+ [Improve memo support](https://github.com/ripple/ripple-lib/commit/1704ac4ae144c0ce54afad86f644c75a632080b1)
- Add `MemoFormat` property for memo
- Enforce `MemoFormat` and `MemoType` to be valid ASCII
- Support `text` and `json` MemoFormat
+ [Update jscl library](https://github.com/ripple/ripple-lib/commit/3204998fcb6f31d6c90532a737a4adb8a1e420f6)
- Improved entropy by taking advantage of platform crypto
- Use jscl's k256 curve instead of altering the c256 curve with k256 configuration
- **deprecated:** the c256 curve is linked to the k256 curve to provide backwards compatibility, this link will be removed in the future
+ [Fix empty queue check on reconnect](https://github.com/ripple/ripple-lib/commit/3c21994adcf72d1fbd87d453ceb917f9ad6df4ec)
##0.9.4
+ [Normalize offers from book_offers and transaction stream](https://github.com/ripple/ripple-lib/commit/86ed24b94cf7c8929c87db3a63e9bbea7f767e9c)

View File

@@ -9,7 +9,7 @@ A JavaScript API for interacting with Ripple in Node.js and the browser
###Features
+ Connect to a rippled server in JavaScript (Node.js or browser)
+ Issue [rippled API](https://ripple.com/wiki/JSON_Messages) requests
+ Issue [rippled API](https://ripple.com/build/rippled-apis/) requests
+ Listen to events on the Ripple network (transaction, ledger, etc.)
+ Sign and submit transactions to the Ripple network

View File

@@ -39,7 +39,7 @@ This file provides step-by-step walkthroughs for some of the most common usages
]
}
var remote = new Remote({options});
var remote = new Remote(options);
remote.connect(function(err, res) {
/* remote connected, use some remote functions here */

22
jshintrc Normal file
View File

@@ -0,0 +1,22 @@
{
"browser": true,
"jquery": true,
"node": true,
"camelcase": false,
"eqeqeq": true,
"eqnull": true,
"latedef": true,
"newcap": true,
"quotmark": "single",
"undef": true,
"unused": true,
"curly": true,
"freeze": true,
"nonbsp": true,
"boss": true,
"laxbreak": true,
"maxparams": 4,
"maxdepth": 3,
"esnext": true,
"-W004": true
}

33
npm-shrinkwrap.json generated
View File

@@ -1,18 +1,21 @@
{
"name": "ripple-lib",
"version": "0.9.4",
"version": "0.10.1-rc1",
"dependencies": {
"async": {
"version": "0.8.0",
"from": "async@>=0.8.0 <0.9.0"
"from": "async@>=0.8.0 <0.9.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.8.0.tgz"
},
"extend": {
"version": "1.2.1",
"from": "extend@>=1.2.1 <1.3.0"
"from": "extend@>=1.2.1 <1.3.0",
"resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz"
},
"lru-cache": {
"version": "2.5.0",
"from": "lru-cache@>=2.5.0 <2.6.0"
"from": "lru-cache@>=2.5.0 <2.6.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz"
},
"ripple-wallet-generator": {
"version": "1.0.1",
@@ -22,6 +25,7 @@
"superagent": {
"version": "0.18.2",
"from": "superagent@>=0.18.0 <0.19.0",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-0.18.2.tgz",
"dependencies": {
"qs": {
"version": "0.6.6",
@@ -56,6 +60,7 @@
"debug": {
"version": "1.0.4",
"from": "debug@>=1.0.1 <1.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz",
"dependencies": {
"ms": {
"version": "0.6.2",
@@ -75,8 +80,9 @@
"resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.3.tgz",
"dependencies": {
"combined-stream": {
"version": "0.0.5",
"version": "0.0.7",
"from": "combined-stream@>=0.0.4 <0.1.0",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
"dependencies": {
"delayed-stream": {
"version": "0.0.5",
@@ -87,17 +93,20 @@
},
"async": {
"version": "0.9.0",
"from": "async@>=0.9.0 <0.10.0"
"from": "async@>=0.9.0 <0.10.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz"
}
}
},
"readable-stream": {
"version": "1.0.27-1",
"from": "readable-stream@1.0.27-1",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@>=1.0.0 <1.1.0"
"from": "core-util-is@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
@@ -106,11 +115,13 @@
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@>=0.10.0 <0.11.0"
"from": "string_decoder@>=0.10.0 <0.11.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@>=2.0.1 <2.1.0"
"from": "inherits@>=2.0.1 <2.1.0",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
}
@@ -119,10 +130,12 @@
"ws": {
"version": "0.4.32",
"from": "ws@>=0.4.31 <0.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-0.4.32.tgz",
"dependencies": {
"commander": {
"version": "2.1.0",
"from": "commander@>=2.1.0 <2.2.0"
"from": "commander@>=2.1.0 <2.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz"
},
"nan": {
"version": "1.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.9.4",
"version": "0.10.1-rc1",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
"src/js/*",

View File

@@ -2,6 +2,9 @@
* Data type map.
*
* Mapping of type ids to data types. The type id is specified by the high
*
* For reference, see rippled's definition:
* https://github.com/ripple/rippled/blob/develop/src/ripple/data/protocol/SField.cpp
*/
var TYPES_MAP = exports.types = [
void(0),
@@ -375,7 +378,7 @@ exports.ledger = {
['Balance', REQUIRED],
['LowLimit', REQUIRED],
['HighLimit', REQUIRED]])
}
};
exports.metadata = [
[ 'TransactionIndex' , REQUIRED ],

View File

@@ -5,6 +5,7 @@ exports.Account = require('./account').Account;
exports.Transaction = require('./transaction').Transaction;
exports.Currency = require('./currency').Currency;
exports.Base = require('./base').Base;
exports.UInt128 = require('./uint128').UInt128;
exports.UInt160 = require('./uint160').UInt160;
exports.UInt256 = require('./uint256').UInt256;
exports.Seed = require('./seed').Seed;
@@ -19,6 +20,10 @@ exports.binformat = require('./binformat');
exports.utils = require('./utils');
exports.Server = require('./server').Server;
exports.Wallet = require('./wallet');
exports.Ledger = require('./ledger').Ledger;
exports.TransactionQueue = require('./transactionqueue').TransactionQueue;
exports.VaultClient = require('./vaultclient').VaultClient;
exports.Blob = require('./blob').Blob;
// Important: We do not guarantee any specific version of SJCL or for any
// specific features to be included. The version and configuration may change at
@@ -28,6 +33,8 @@ exports.Wallet = require('./wallet');
// the official client, it makes sense to expose the SJCL instance so we don't
// have to include it twice.
exports.sjcl = require('./utils').sjcl;
exports.jsbn = require('./utils').jsbn;
exports.types = require('./serializedtypes');
exports.config = require('./config');

View File

@@ -5,7 +5,7 @@ var UInt256 = require('./uint256').UInt256;
var Base = require('./base').Base;
function KeyPair() {
this._curve = sjcl.ecc.curves.c256;
this._curve = sjcl.ecc.curves.k256;
this._secret = null;
this._pubkey = null;
};
@@ -15,7 +15,7 @@ KeyPair.from_bn_secret = function(j) {
};
KeyPair.prototype.parse_bn_secret = function(j) {
this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.c256, j);
this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.k256, j);
return this;
};

View File

@@ -1,5 +1,5 @@
var extend = require('extend');
var utils = require('./utils');
var utils = require('./utils');
var UInt160 = require('./uint160').UInt160;
var Amount = require('./amount').Amount;
@@ -26,28 +26,37 @@ function Meta(data) {
data.AffectedNodes.forEach(this.addNode, this);
};
Meta.nodeTypes = [
Meta.NODE_TYPES = [
'CreatedNode',
'ModifiedNode',
'DeletedNode'
];
Meta.amountFieldsAffectingIssuer = [
Meta.AMOUNT_FIELDS_AFFECTING_ISSUER = [
'LowLimit',
'HighLimit',
'TakerPays',
'TakerGets'
];
Meta.ACCOUNT_FIELDS = [
'Account',
'Owner',
'Destination',
'Issuer',
'Target'
];
/**
* @param {Object} node
* @api private
*/
Meta.prototype.getNodeType = function(node) {
var result = null;
for (var i=0; i<Meta.nodeTypes.length; i++) {
var type = Meta.nodeTypes[i];
for (var i=0; i<Meta.NODE_TYPES.length; i++) {
var type = Meta.NODE_TYPES[i];
if (node.hasOwnProperty(type)) {
result = type;
break;
@@ -57,6 +66,15 @@ Meta.prototype.getNodeType = function(node) {
return result;
};
/**
* @param {String} field
* @api private
*/
Meta.prototype.isAccountField = function(field) {
return Meta.ACCOUNT_FIELDS.indexOf(field) !== -1;
};
/**
* Add node to metadata
*
@@ -72,7 +90,6 @@ Meta.prototype.addNode = function(node) {
if ((result.nodeType = this.getNodeType(node))) {
node = node[result.nodeType];
result.diffType = result.nodeType;
result.entryType = node.LedgerEntryType;
result.ledgerIndex = node.LedgerIndex;
@@ -114,56 +131,6 @@ Meta.prototype.getNodes = function(options) {
}
};
/**
* Execute a function on each affected node.
*
* The callback is passed two parameters. The first is a node object which looks
* like this:
*
* {
* // Type of diff, e.g. CreatedNode, ModifiedNode
* nodeType: 'CreatedNode'
*
* // Type of node affected, e.g. RippleState, AccountRoot
* entryType: 'RippleState',
*
* // Index of the ledger this change occurred in
* ledgerIndex: '01AB01AB...',
*
* // Contains all fields with later versions taking precedence
* //
* // This is a shorthand for doing things like checking which account
* // this affected without having to check the nodeType.
* fields: {...},
*
* // Old fields (before the change)
* fieldsPrev: {...},
*
* // New fields (that have been added)
* fieldsNew: {...},
*
* // Changed fields
* fieldsFinal: {...}
* }
*
* The second parameter to the callback is the index of the node in the metadata
* (first entry is index 0).
*/
[
'forEach',
'map',
'filter',
'every',
'some',
'reduce'
].forEach(function(fn) {
Meta.prototype[fn] = function() {
return Array.prototype[fn].apply(this.nodes, arguments);
};
});
Meta.prototype.each = Meta.prototype.forEach;
Meta.prototype.getAffectedAccounts = function(from) {
if (this._affectedAccounts) {
@@ -176,12 +143,16 @@ Meta.prototype.getAffectedAccounts = function(from) {
// TransactionMetaSet::getAffectedAccounts
for (var i=0; i<this.nodes.length; i++) {
var node = this.nodes[i];
var fields = (node.nodeType === 'CreatedNode') ? node.fieldsNew : node.fieldsFinal;
var fields = (node.nodeType === 'CreatedNode')
? node.fieldsNew
: node.fieldsFinal;
for (var fieldName in fields) {
var field = fields[fieldName];
if (typeof field === 'string' && UInt160.is_valid(field)) {
if (this.isAccountField(fieldName) && UInt160.is_valid(field)) {
accounts.push(field);
} else if (~Meta.amountFieldsAffectingIssuer.indexOf(fieldName)) {
} else if (~Meta.AMOUNT_FIELDS_AFFECTING_ISSUER.indexOf(fieldName)) {
var amount = Amount.from_json(field);
var issuer = amount.issuer();
if (issuer.is_valid() && !issuer.is_zero()) {

View File

@@ -43,8 +43,9 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
this._shouldSubscribe = true;
this._listeners = 0;
this._offers = [ ];
this._ownerFunds = { };
this._offerCounts = { };
this._ownerFunds = { };
this._ownerOffers = { };
// We consider ourselves synchronized if we have a current
// copy of the offers, we are online and subscribed to updates.
@@ -180,6 +181,7 @@ OrderBook.prototype.unsubscribe = function() {
OrderBook.prototype.resetCache = function() {
this._ownerFunds = { };
this._ownerOffers = { };
this._offerCounts = { };
this._synchronized = false;
};
@@ -232,6 +234,8 @@ OrderBook.prototype.removeCachedFunds = function(account) {
/**
* Get offer count for offer owner
*
* @param {String} account address
*/
OrderBook.prototype.getOfferCount = function(account) {
@@ -241,6 +245,8 @@ OrderBook.prototype.getOfferCount = function(account) {
/**
* Increment offer count for offer owner
*
* @param {String} account address
*/
OrderBook.prototype.incrementOfferCount = function(account) {
@@ -252,6 +258,8 @@ OrderBook.prototype.incrementOfferCount = function(account) {
/**
* Decrement offer count for offer owner
*
* @param {String} account address
*/
OrderBook.prototype.decrementOfferCount = function(account) {
@@ -261,6 +269,58 @@ OrderBook.prototype.decrementOfferCount = function(account) {
return result;
};
/**
* Add offer amount to sum amount being offered by an account
*
* @param {String} account address
* @param {Object|String} offer amount
* @return sum
*/
OrderBook.prototype.addOwnerOffer = function(account, amount) {
assert(UInt160.is_valid(account), 'Account is invalid');
var previousAmount = this.getOwnerOfferSum(account);
var newAmount = previousAmount.add(Amount.from_json(amount));
this._ownerOffers[account] = newAmount;
return newAmount;
};
/**
* Subtract offer amount from sum amount being offered by an account
*
* @param {String} account address
* @param {Object|String} offer amount
* @return sum
*/
OrderBook.prototype.subtractOwnerOffer = function(account, amount) {
assert(UInt160.is_valid(account), 'Account is invalid');
var previousAmount = this.getOwnerOfferSum(account);
var newAmount = previousAmount.subtract(Amount.from_json(amount));
this._ownerOffers[account] = newAmount;
return newAmount;
};
/**
* Get sum amount for offers by an account
*
* @param {String} account address
* @return sum
*/
OrderBook.prototype.getOwnerOfferSum = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
var amount = this._ownerOffers[account];
if (!amount) {
if (this._currencyGets.is_native()) {
amount = Amount.from_json('0');
} else {
amount = Amount.from_json('0' + OrderBook.IOU_SUFFIX);
}
}
return amount;
};
/**
* Compute funded amount for a balance/transferRate
*
@@ -358,11 +418,20 @@ OrderBook.prototype.setFundedAmount = function(offer, fundedAmount) {
return offer;
}
// Sum of offer amounts by an account
var offerSum = this.getOwnerOfferSum(offer.Account);
if (offerSum.is_zero()) {
// If there are no cached offer amounts for the account, default to
// TakerGets of this offer
offerSum = Amount.from_json(offer.TakerGets);
}
offer.is_fully_funded = Amount.from_json(
this._currencyGets.is_native()
? fundedAmount
: fundedAmount + OrderBook.IOU_SUFFIX
).compareTo(Amount.from_json(offer.TakerGets)) >= 0;
).compareTo(offerSum) >= 0;
if (offer.is_fully_funded) {
offer.taker_gets_funded = Amount.from_json(offer.TakerGets).to_text();
@@ -376,12 +445,8 @@ OrderBook.prototype.setFundedAmount = function(offer, fundedAmount) {
? offer.TakerPays.value
: offer.TakerPays;
var takerGetsValue = (typeof offer.TakerGets === 'object')
? offer.TakerGets.value
: offer.TakerGets;
var takerPays = Amount.from_json(takerPaysValue + OrderBook.IOU_SUFFIX);
var takerGets = Amount.from_json(takerGetsValue + OrderBook.IOU_SUFFIX);
var takerGets = Amount.from_json(offerSum);
var fundedPays = Amount.from_json(fundedAmount + OrderBook.IOU_SUFFIX);
var rate = takerPays.divide(takerGets);
@@ -662,9 +727,16 @@ OrderBook.offerRewrite = function(offer) {
OrderBook.prototype.setOffers = function(offers) {
assert(Array.isArray(offers));
var l = offers.length;
var newOffers = [ ];
for (var i=0, l=offers.length; i<l; i++) {
for (var i=0; i<l; i++) {
var offer = offers[i];
// Add offer amount to sum for account
this.addOwnerOffer(offer.Account, offer.TakerGets);
}
for (var i=0; i<l; i++) {
var offer = OrderBook.offerRewrite(offers[i]);
var fundedAmount;
@@ -675,8 +747,8 @@ OrderBook.prototype.setOffers = function(offers) {
this.addCachedFunds(offer.Account, fundedAmount);
}
this.setFundedAmount(offer, fundedAmount);
this.incrementOfferCount(offer.Account);
this.setFundedAmount(offer, fundedAmount);
newOffers.push(offer);
}
@@ -888,6 +960,9 @@ OrderBook.prototype.insertOffer = function(node, fundedAmount) {
log.info('inserting offer', this._key, node.fields);
}
// Add offer amount to sum for account
this.addOwnerOffer(node.fields.Account, node.fields.TakerGets);
var nodeFields = OrderBook.offerRewrite(node.fields);
nodeFields.LedgerEntryType = node.entryType;
nodeFields.index = node.ledgerIndex;
@@ -904,7 +979,7 @@ OrderBook.prototype.insertOffer = function(node, fundedAmount) {
// XXX Should use Amount#from_quality
var price = Amount.from_json(
nodeFields.TakerPays
).ratio_human(node.fields.TakerGets, DATE_REF);
).ratio_human(nodeFields.TakerGets, DATE_REF);
for (var i=0, l=this._offers.length; i<l; i++) {
var offer = this._offers[i];
@@ -944,14 +1019,21 @@ OrderBook.prototype.modifyOffer = function(node, isDeletedNode) {
var offer = this._offers[i];
if (offer.index === node.ledgerIndex) {
if (isDeletedNode) {
// Multiple offers same account?
// Remove offer amount from sum for account
this.subtractOwnerOffer(offer.Account, offer.TakerGets);
this._offers.splice(i, 1);
this.emit('offer_removed', offer);
} else {
// TODO: This assumes no fields are deleted, which is
// probably a safe assumption, but should be checked.
var previousOffer = extend({}, offer);
var previousOffer = extend({ }, offer);
extend(offer, node.fieldsFinal);
// Remove offer amount from sum for account
this.subtractOwnerOffer(offer.Account, previousOffer.TakerGets);
// Add offer amount from sum for account
this.addOwnerOffer(offer.Account, offer.TakerGets);
this.emit('offer_changed', previousOffer, offer);
}
break;

View File

@@ -17,6 +17,7 @@ var EventEmitter = require('events').EventEmitter;
var util = require('util');
var assert = require('assert');
var LRU = require('lru-cache');
var async = require('async');
var Server = require('./server').Server;
var Request = require('./request').Request;
var Server = require('./server').Server;
@@ -111,6 +112,7 @@ function Remote(opts, trace) {
this._should_connect = true;
this._submission_timeout = 1000 * (typeof opts.submission_timeout === 'number' ? opts.submission_timeout : 20);
this._last_ledger_offset = (typeof opts.last_ledger_offset === 'number') ? opts.last_ledger_offset : 3;
this._received_tx = LRU({ max: 100 });
this._cur_path_find = null;
@@ -1257,7 +1259,7 @@ Remote.accountRequest = function(type, options, callback) {
request.message.peer = UInt160.json_rewrite(peer);
}
if (!isNaN(Number(limit))) {
if (!isNaN(limit)) {
limit = Number(limit);
// max for 32-bit unsigned int is 4294967295
@@ -1423,10 +1425,19 @@ Remote.prototype.requestAccountTx = function(options, callback) {
}, options);
request.once('success', function(res) {
if (options.parseBinary) {
res.transactions = res.transactions.map(Remote.parseBinaryTransaction);
if (!options.parseBinary) {
request.emit('transactions', res);
return;
}
request.emit('transactions', res);
async.mapSeries(res.transactions, function(transaction, next) {
async.setImmediate(function() {
next(null, Remote.parseBinaryTransaction(transaction));
});
}, function(err, transactions) {
res.transactions = transactions;
request.emit('transactions', res);
});
});
request.callback(callback, 'transactions');
@@ -1475,14 +1486,19 @@ Remote.prototype.requestTxHistory = function(start, callback) {
/**
* Request book_offers
*
* @param {Object} gets
* @param {Object} pays
* @param {String} taker
* @param [Function] calback
* @param {Object} options
* @param {Object} options.gets - taker_gets with issuer and currency
* @param {Object} options.pays - taker_pays with issuer and currency
* @param {String} [options.taker]
* @param {String} [options.ledger]
* @param {String|Number} [options.limit]
* @param [Function] callback
* @return {Request}
*/
Remote.prototype.requestBookOffers = function(gets, pays, taker, callback) {
var ledger;
var limit;
var lastArg = arguments[arguments.length - 1];
if (gets.hasOwnProperty('gets') || gets.hasOwnProperty('taker_gets')) {
@@ -1494,6 +1510,8 @@ Remote.prototype.requestBookOffers = function(gets, pays, taker, callback) {
taker = options.taker;
pays = options.pays || options.taker_pays;
gets = options.gets || options.taker_gets;
ledger = options.ledger;
limit = options.limit;
}
if (typeof lastArg === 'function') {
@@ -1519,9 +1537,26 @@ Remote.prototype.requestBookOffers = function(gets, pays, taker, callback) {
}
request.message.taker = taker ? taker : UInt160.ACCOUNT_ONE;
request.ledgerSelect(ledger);
if (!isNaN(limit)) {
limit = Number(limit);
// max for 32-bit unsigned int is 4294967295
// we'll clamp to 1e9
if (limit > 1e9) {
limit = 1e9;
}
// min for 32-bit unsigned int is 0
// we'll clamp to 0
if (limit < 0) {
limit = 0;
}
request.message.limit = limit;
}
request.callback(callback);
return request;
};

View File

@@ -16,7 +16,7 @@ var KeyPair = require('./keypair').KeyPair;
var Seed = extend(function () {
// Internal form: NaN or BigInteger
this._curve = sjcl.ecc.curves.c256;
this._curve = sjcl.ecc.curves.k256;
this._value = NaN;
}, UInt);

View File

@@ -42,7 +42,7 @@ function SerializedObject(buf) {
SerializedObject.from_json = function(obj) {
// Create a copy of the object so we don't modify it
var obj = extend({}, obj);
var obj = extend(true, {}, obj);
var so = new SerializedObject();
var typedef;

View File

@@ -24,6 +24,7 @@ var Currency = amount.Currency;
// Shortcuts
var hex = sjcl.codec.hex;
var bytes = sjcl.codec.bytes;
var utf8 = sjcl.codec.utf8String;
var BigInteger = utils.jsbn.BigInteger;
@@ -52,7 +53,7 @@ function isBigInteger(val) {
return val instanceof BigInteger;
};
function serialize_hex(so, hexData, noLength) {
function serializeHex(so, hexData, noLength) {
var byteData = bytes.fromBits(hex.toBits(hexData));
if (!noLength) {
SerializedType.serialize_varint(so, byteData.length);
@@ -63,10 +64,18 @@ function serialize_hex(so, hexData, noLength) {
/**
* parses bytes as hex
*/
function convert_bytes_to_hex (byte_array) {
function convertByteArrayToHex (byte_array) {
return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(byte_array)).toUpperCase();
};
function convertStringToHex(string) {
return hex.fromBits(utf8.toBits(string)).toUpperCase();
}
function convertHexToString(hexString) {
return utf8.fromBits(hex.toBits(hexString));
}
SerializedType.serialize_varint = function (so, val) {
if (val < 0) {
throw new Error('Variable integers are unsigned.');
@@ -115,7 +124,7 @@ SerializedType.prototype.parse_varint = function (so) {
*
* The result is appended to the serialized object ('so').
*/
function append_byte_array(so, val, bytes) {
function convertIntegerToByteArray(val, bytes) {
if (!isNumber(val)) {
throw new Error('Value is not a number', bytes);
}
@@ -130,7 +139,7 @@ function append_byte_array(so, val, bytes) {
newBytes.unshift(val >>> (i * 8) & 0xff);
}
so.append(newBytes);
return newBytes;
};
// Convert a certain number of bytes from the serialized object ('so') into an integer.
@@ -152,7 +161,7 @@ function readAndSum(so, bytes) {
var STInt8 = exports.Int8 = new SerializedType({
serialize: function (so, val) {
append_byte_array(so, val, 1);
so.append(convertIntegerToByteArray(val, 1));
},
parse: function (so) {
return readAndSum(so, 1);
@@ -163,7 +172,7 @@ STInt8.id = 16;
var STInt16 = exports.Int16 = new SerializedType({
serialize: function (so, val) {
append_byte_array(so, val, 2);
so.append(convertIntegerToByteArray(val, 2));
},
parse: function (so) {
return readAndSum(so, 2);
@@ -174,7 +183,7 @@ STInt16.id = 1;
var STInt32 = exports.Int32 = new SerializedType({
serialize: function (so, val) {
append_byte_array(so, val, 4);
so.append(convertIntegerToByteArray(val, 4));
},
parse: function (so) {
return readAndSum(so, 4);
@@ -217,7 +226,7 @@ var STInt64 = exports.Int64 = new SerializedType({
hex = '0' + hex;
}
serialize_hex(so, hex, true); //noLength = true
serializeHex(so, hex, true); //noLength = true
},
parse: function (so) {
var bytes = so.read(8);
@@ -237,7 +246,7 @@ var STHash128 = exports.Hash128 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash128');
}
serialize_hex(so, hash.to_hex(), true); //noLength = true
serializeHex(so, hash.to_hex(), true); //noLength = true
},
parse: function (so) {
return UInt128.from_bytes(so.read(16));
@@ -252,7 +261,7 @@ var STHash256 = exports.Hash256 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash256');
}
serialize_hex(so, hash.to_hex(), true); //noLength = true
serializeHex(so, hash.to_hex(), true); //noLength = true
},
parse: function (so) {
return UInt256.from_bytes(so.read(32));
@@ -267,7 +276,7 @@ var STHash160 = exports.Hash160 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash160');
}
serialize_hex(so, hash.to_hex(), true); //noLength = true
serializeHex(so, hash.to_hex(), true); //noLength = true
},
parse: function (so) {
return UInt160.from_bytes(so.read(20));
@@ -294,7 +303,7 @@ var STCurrency = new SerializedType({
// UInt160 value and consider it valid. But it doesn't, so for the
// deserialization to be usable, we need to allow invalid results for now.
//if (!currency.is_valid()) {
// throw new Error('Invalid currency: '+convert_bytes_to_hex(bytes));
// throw new Error('Invalid currency: '+convertByteArrayToHex(bytes));
//}
return currency;
}
@@ -409,15 +418,16 @@ STAmount.id = 6;
var STVL = exports.VariableLength = exports.VL = new SerializedType({
serialize: function (so, val) {
if (typeof val === 'string') {
serialize_hex(so, val);
serializeHex(so, val);
} else {
throw new Error('Unknown datatype.');
}
},
parse: function (so) {
var len = this.parse_varint(so);
return convert_bytes_to_hex(so.read(len));
return convertByteArrayToHex(so.read(len));
}
});
@@ -429,7 +439,7 @@ var STAccount = exports.Account = new SerializedType({
if (!account.is_valid()) {
throw new Error('Invalid account!');
}
serialize_hex(so, account.to_hex());
serializeHex(so, account.to_hex());
},
parse: function (so) {
var len = this.parse_varint(so);
@@ -441,7 +451,6 @@ var STAccount = exports.Account = new SerializedType({
var result = UInt160.from_bytes(so.read(len));
result.set_version(Base.VER_ACCOUNT_ID);
//console.log('PARSED 160:', result.to_json());
if (false && !result.is_valid()) {
throw new Error('Invalid Account');
}
@@ -593,6 +602,105 @@ var STVector256 = exports.Vector256 = new SerializedType({
STVector256.id = 19;
// Internal
var STMemo = exports.STMemo = new SerializedType({
serialize: function(so, val, no_marker) {
var keys = [];
Object.keys(val).forEach(function (key) {
// Ignore lowercase field names - they're non-serializable fields by
// convention.
if (key[0] === key[0].toLowerCase()) {
return;
}
if (typeof binformat.fieldsInverseMap[key] === 'undefined') {
throw new Error('JSON contains unknown field: "' + key + '"');
}
keys.push(key);
});
// Sort fields
keys = sort_fields(keys);
// store that we're dealing with json
var isJson = val.MemoFormat === 'json';
for (var i=0; i<keys.length; i++) {
var key = keys[i];
var value = val[key];
switch (key) {
// MemoType and MemoFormat are always ASCII strings
case 'MemoType':
case 'MemoFormat':
value = convertStringToHex(value);
break;
// MemoData can be a JSON object, otherwise it's a string
case 'MemoData':
if (typeof value !== 'string') {
if (isJson) {
try {
value = convertStringToHex(JSON.stringify(value));
} catch (e) {
throw new Error('MemoFormat json with invalid JSON in MemoData field');
}
} else {
throw new Error('MemoData can only be a JSON object with a valid json MemoFormat');
}
} else if (isString(value)) {
value = convertStringToHex(value);
}
break;
}
serialize(so, key, value);
}
if (!no_marker) {
//Object ending marker
STInt8.serialize(so, 0xe1);
}
},
parse: function(so) {
var output = {};
while (so.peek(1)[0] !== 0xe1) {
var keyval = parse(so);
output[keyval[0]] = keyval[1];
}
if (output['MemoType'] !== void(0)) {
output['parsed_memo_type'] = convertHexToString(output['MemoType']);
}
if (output['MemoFormat'] !== void(0)) {
output['parsed_memo_format'] = convertHexToString(output['MemoFormat']);
}
if (output['MemoData'] !== void(0)) {
// see if we can parse JSON
if (output['parsed_memo_format'] === 'json') {
try {
output['parsed_memo_data'] = JSON.parse(convertHexToString(output['MemoData']));
} catch(e) {
// fail, which is fine, we just won't add the memo_data field
}
} else if(output['parsed_memo_format'] === 'text') {
output['parsed_memo_data'] = convertHexToString(output['MemoData']);
}
}
so.read(1);
return output;
}
});
exports.serialize = exports.serialize_whatever = serialize;
function serialize(so, field_name, value) {
@@ -622,9 +730,15 @@ function serialize(so, field_name, value) {
STInt8.serialize(so, field_bits);
}
// Get the serializer class (ST...) for a field based on the type bits.
var serialized_object_type = exports[binformat.types[type_bits]];
//do something with val[keys] and val[keys[i]];
// Get the serializer class (ST...)
var serialized_object_type;
if (field_name === 'Memo' && typeof value === 'object') {
// for Memo we override the default behavior with our STMemo serializer
serialized_object_type = exports.STMemo;
} else {
// for a field based on the type bits.
serialized_object_type = exports[binformat.types[type_bits]];
}
try {
serialized_object_type.serialize(so, value);
@@ -645,18 +759,21 @@ function parse(so) {
type_bits = so.read(1)[0];
}
// Get the parser class (ST...) for a field based on the type bits.
var type = exports[binformat.types[type_bits]];
assert(type, 'Unknown type - header byte is 0x' + tag_byte.toString(16));
var field_bits = tag_byte & 0x0f;
var field_name = (field_bits === 0)
? field_name = binformat.fields[type_bits][so.read(1)[0]]
: field_name = binformat.fields[type_bits][field_bits];
? field_name = binformat.fields[type_bits][so.read(1)[0]]
: field_name = binformat.fields[type_bits][field_bits];
assert(field_name, 'Unknown field - header byte is 0x' + tag_byte.toString(16));
// Get the parser class (ST...) for a field based on the type bits.
var type = (field_name === 'Memo')
? exports.STMemo
: exports[binformat.types[type_bits]];
assert(type, 'Unknown type - header byte is 0x' + tag_byte.toString(16));
return [ field_name, type.parse(so) ]; //key, value
};
@@ -678,18 +795,20 @@ function sort_fields(keys) {
var STObject = exports.Object = new SerializedType({
serialize: function (so, val, no_marker) {
var keys = Object.keys(val);
var keys = [];
// Ignore lowercase field names - they're non-serializable fields by
// convention.
keys = keys.filter(function (key) {
return key[0] !== key[0].toLowerCase();
});
Object.keys(val).forEach(function (key) {
// Ignore lowercase field names - they're non-serializable fields by
// convention.
if (key[0] === key[0].toLowerCase()) {
return;
}
keys.forEach(function (key) {
if (typeof binformat.fieldsInverseMap[key] === 'undefined') {
throw new Error('JSON contains unknown field: "' + key + '"');
}
keys.push(key);
});
// Sort fields

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,20 @@
var LRU = require('lru-cache');
var Transaction = require('./transaction').Transaction;
/**
* Manager for pending transactions
*/
var LRU = require('lru-cache');
var Transaction = require('./transaction').Transaction;
function TransactionQueue() {
this._queue = [ ];
this._idCache = LRU();
this._sequenceCache = LRU();
this._idCache = LRU({ max: 200 });
this._sequenceCache = LRU({ max: 200 });
};
/**
* Store received (validated) sequence
*
* @param {Number} sequence
*/
TransactionQueue.prototype.addReceivedSequence = function(sequence) {
@@ -23,6 +24,9 @@ TransactionQueue.prototype.addReceivedSequence = function(sequence) {
/**
* Check that sequence number has been consumed by a validated
* transaction
*
* @param {Number} sequence
* @return {Boolean}
*/
TransactionQueue.prototype.hasSequence = function(sequence) {
@@ -31,6 +35,9 @@ TransactionQueue.prototype.hasSequence = function(sequence) {
/**
* Store received (validated) ID transaction
*
* @param {String} transaction id
* @param {Transaction} transaction
*/
TransactionQueue.prototype.addReceivedId = function(id, transaction) {
@@ -39,6 +46,9 @@ TransactionQueue.prototype.addReceivedId = function(id, transaction) {
/**
* Get received (validated) transaction by ID
*
* @param {String} transaction id
* @return {Object}
*/
TransactionQueue.prototype.getReceived = function(id) {
@@ -48,6 +58,9 @@ TransactionQueue.prototype.getReceived = function(id) {
/**
* Get a submitted transaction by ID. Transactions
* may have multiple associated IDs.
*
* @param {String} transaction id
* @return {Transaction}
*/
TransactionQueue.prototype.getSubmission = function(id) {
@@ -63,8 +76,32 @@ TransactionQueue.prototype.getSubmission = function(id) {
return result;
};
/**
* Get earliest ledger in the pending queue
*
* @return {Number} ledger
*/
TransactionQueue.prototype.getMinLedger = function() {
var result = Infinity;
for (var i=0, tx; (tx=this._queue[i]); i++) {
if (tx.initialSubmitIndex < result) {
result = tx.initialSubmitIndex;
}
}
if (!isFinite(result)) {
result = -1;
}
return result;
};
/**
* Remove a transaction from the queue
*
* @param {String|Transaction} transaction or id
*/
TransactionQueue.prototype.remove = function(tx) {
@@ -87,14 +124,30 @@ TransactionQueue.prototype.remove = function(tx) {
}
};
/**
* Add a transaction to pending queue
*
* @param {Transaction} transaction
*/
TransactionQueue.prototype.push = function(tx) {
this._queue.push(tx);
};
/**
* Iterate over pending transactions
*
* @param {Function} iterator
*/
TransactionQueue.prototype.forEach = function(fn) {
this._queue.forEach(fn);
};
/**
* @return {Number} length of pending queue
*/
TransactionQueue.prototype.length =
TransactionQueue.prototype.getLength = function() {
return this._queue.length;

View File

@@ -13,7 +13,6 @@ var BigInteger = utils.jsbn.BigInteger;
var UInt = function() {
// Internal form: NaN or BigInteger
this._value = NaN;
this._update();
};
@@ -96,6 +95,10 @@ UInt.prototype.clone = function() {
UInt.prototype.copyTo = function(d) {
d._value = this._value;
if (this._version_byte !== void(0)) {
d._version_byte = this._version_byte;
}
if (typeof d._update === 'function') {
d._update();
}
@@ -104,7 +107,9 @@ UInt.prototype.copyTo = function(d) {
};
UInt.prototype.equals = function(d) {
return this._value instanceof BigInteger && d._value instanceof BigInteger && this._value.equals(d._value);
return this._value instanceof BigInteger
&& d._value instanceof BigInteger
&& this._value.equals(d._value);
};
UInt.prototype.is_valid = function() {

View File

@@ -65,7 +65,7 @@ sjcl.ecc.ecdsa.secretKey.prototype.signWithRecoverablePublicKey = function(hash,
*
* @param {bitArray} hash
* @param {bitArray} signature
* @param {sjcl.ecc.curve} [sjcl.ecc.curves['c256']] curve
* @param {sjcl.ecc.curve} [sjcl.ecc.curves['k256']] curve
* @returns {sjcl.ecc.ecdsa.publicKey} Public key
*/
sjcl.ecc.ecdsa.publicKey.recoverFromSignature = function(hash, signature, curve) {
@@ -75,7 +75,7 @@ sjcl.ecc.ecdsa.publicKey.recoverFromSignature = function(hash, signature, curve)
}
if (!curve) {
curve = sjcl.ecc.curves['c256'];
curve = sjcl.ecc.curves['k256'];
}
// Convert hash to bits and determine encoding for output

View File

@@ -1,17 +1,16 @@
// ----- for secp256k1 ------
// Overwrite NIST-P256 with secp256k1
sjcl.ecc.curves.c256 = new sjcl.ecc.curve(
sjcl.bn.pseudoMersennePrime(256, [[0,-1],[4,-1],[6,-1],[7,-1],[8,-1],[9,-1],[32,-1]]),
"0x14551231950b75fc4402da1722fc9baee",
0,
7,
"0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
);
sjcl.ecc.point.prototype.toBytesCompressed = function () {
var header = this.y.mod(2).toString() == "0x0" ? 0x02 : 0x03;
return [header].concat(sjcl.codec.bytes.fromBits(this.x.toBits()))
};
// Replace point addition and doubling algorithms
// NIST-P256 is a=-3, we need algorithms for a=0
//
// This is a custom point addition formula that
// only works for a=-3 Jacobian curve. It's much
// faster than the generic implementation
sjcl.ecc.pointJac.prototype.add = function(T) {
var S = this;
if (S.curve !== T.curve) {
@@ -43,7 +42,7 @@ sjcl.ecc.pointJac.prototype.add = function(T) {
var j = h.mul(i);
var r = s2.sub(S.y).doubleM();
var v = S.x.mul(i);
var x = r.square().subM(j).subM(v.copy().doubleM());
var y = r.mul(v.sub(x)).subM(S.y.mul(j).doubleM());
var z = S.z.add(h).square().subM(z1z1).subM(hh);
@@ -51,6 +50,9 @@ sjcl.ecc.pointJac.prototype.add = function(T) {
return new sjcl.ecc.pointJac(this.curve,x,y,z);
};
// This is a custom doubling algorithm that
// only works for a=-3 Jacobian curve. It's much
// faster than the generic implementation
sjcl.ecc.pointJac.prototype.doubl = function () {
if (this.isIdentity) { return this; }
@@ -66,7 +68,9 @@ sjcl.ecc.pointJac.prototype.doubl = function () {
return new sjcl.ecc.pointJac(this.curve, x, y, z);
};
sjcl.ecc.point.prototype.toBytesCompressed = function () {
var header = this.y.mod(2).toString() == "0x0" ? 0x02 : 0x03;
return [header].concat(sjcl.codec.bytes.fromBits(this.x.toBits()))
};
// DEPRECATED:
// previously the c256 curve was overridden with the secp256k1 curve
// since then, sjcl has been updated to support k256
// this override exist to keep supporting the old c256 with k256 behavior
// this will be removed in future release
sjcl.ecc.curves.c256 = sjcl.ecc.curves.k256;

2
src/js/sjcl/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
core.js
core_closure.js

9
src/js/sjcl/.travis.yml Normal file
View File

@@ -0,0 +1,9 @@
before_script: ./configure --with-all
language: node_js
node_js:
- "0.11"
- "0.10"
- "0.8"
# 0.6 appears to be unreliable on Travis at the moment.
# - "0.6"

View File

@@ -54,10 +54,11 @@ lint: core.js core/*.js test/*.js browserTest/*.js lint/coding_guidelines.pl
lint/coding_guidelines.pl core/*.js test/*.js browserTest/*.js
TEST_COMMON= browserTest/rhinoUtil.js test/test.js
TEST_COMMON= browserTest/nodeUtil.js test/test.js
TEST_SCRIPTS= $(TEST_COMMON) \
test/aes_vectors.js test/aes_test.js \
test/bitArray_vectors.js test/bitArray_test.js \
test/ocb2_vectors.js test/ocb2_test.js \
test/ccm_vectors.js test/ccm_test.js \
test/cbc_vectors.js test/cbc_test.js \
@@ -70,18 +71,14 @@ TEST_SCRIPTS= $(TEST_COMMON) \
test/hmac_vectors.js test/hmac_test.js \
test/pbkdf2_test.js \
test/bn_vectors.js test/bn_test.js \
test/ecdsa_test.js test/ecdsa_vectors.js test/ecdh_test.js
test/ecdsa_test.js test/ecdsa_vectors.js test/ecdh_test.js \
test/srp_vectors.js test/srp_test.js \
test/json_test.js
TEST_SCRIPTS_OPT= $(TEST_COMMON) \
test/srp_vectors.js test/srp_test.js
# Run all tests in node.js.
# Rhino fails at -O 0. Probably because the big files full of test vectors blow the
# bytecode limit. So, run most tests with -O -1. But modular exponentiation is
# currently very slow (on Rhino), so run the SRP test with optimizations on.
test: sjcl.js $(TEST_SCRIPTS) test/run_tests_rhino.js
@rhino -O -1 -w test/run_tests_rhino.js $< $(TEST_SCRIPTS)
@rhino -O 9 -w test/run_tests_rhino.js $< $(TEST_SCRIPTS_OPT)
test: sjcl.js $(TEST_SCRIPTS) test/run_tests_node.js
node test/run_tests_node.js $< $(TEST_SCRIPTS)
tidy:
find . -name '*~' -delete

21
src/js/sjcl/README.md Normal file
View File

@@ -0,0 +1,21 @@
sjcl
====
[![Build Status](https://travis-ci.org/bitwiseshiftleft/sjcl.png)](https://travis-ci.org/bitwiseshiftleft/sjcl)
Stanford Javascript Crypto Library
Security Advisories
===
* 12.02.2014: the current development version has a paranoia bug in the ecc module. The bug was introduced in commit [ac0b3fe0](https://github.com/bitwiseshiftleft/sjcl/commit/ac0b3fe0) and might affect ecc key generation on platforms without a platform random number generator.
*
Security Contact
====
Security Mail: sjcl@ovt.me
OpenPGP-Key Fingerprint: 0D54 3E52 87B4 EC06 3FA9 0115 72ED A6C7 7AAF 48ED
Keyserver: pool.sks-keyservers.net
Documentation
====
The documentation is available [here](http://bitwiseshiftleft.github.io/sjcl/doc/)

12
src/js/sjcl/bower.json Normal file
View File

@@ -0,0 +1,12 @@
{
"name": "sjcl",
"version": "1.0.0",
"main": ["./sjcl.js"],
"ignore": [
"**/*",
"!README.md",
"!README/*",
"!bower.json",
"!sjcl.js"
]
}

View File

@@ -1,6 +1,6 @@
browserUtil = {};
browserUtil.isRhino = (typeof(window) === 'undefined');
browserUtil.isNodeJS = (typeof(window) === 'undefined');
/**
* Pause (for the graphics to update and the script timer to clear), then run the

View File

@@ -0,0 +1,88 @@
<html>
<head>
<title>Entropy Generator Progress</title>
<!-- ProgressBar source: http://stackoverflow.com/questions/7190898/progress-bar-with-html-and-css -->
<style>
#progressbar {
background-color: black;
border-radius: 13px; /* (height of inner div) / 2 + padding */
padding: 3px;
}
#progressbar > div {
background-color: orange;
width: 0%; /* Adjust with JavaScript */
height: 20px;
border-radius: 10px;
}
</style>
<script type="text/javascript" src="../sjcl.js">
</script>
<script type="text/javascript">
var busy = 0;
var collecting = 0;
function showprogress () {
var barwidth = document.getElementById ("progresswidth");
var paranoia = parseInt (document.getElementById ("paranoialevel").value);
var progress = 100 * sjcl.random.getProgress (paranoia);
barwidth.style.width = progress+"%";
if (!sjcl.random.isReady (paranoia)) {
setTimeout ("showprogress()", 10, "JavaScript");
} else {
busy = 0;
document.getElementById ("startbutton").style.disabled = 1;
}
}
function startup () {
if (collecting == 0) {
sjcl.random.startCollectors ();
collecting = 1;
}
if (busy == 0) {
busy = 1;
document.getElementById ("startbutton").style.disabled = 1;
showprogress ();
}
}
function consume (numbits) {
var collector = document.getElementById ("collector");
collector.value = "retrieving random data";
var paranoia = document.getElementById ("paranoialevel").value;
var numwords = Math.ceil (numbits / 32);
var bits = sjcl.random.randomWords (numwords, paranoia);
collector.value = '';
for (var i=0; i<numwords; i++) {
var hi = (bits [i] >> 16) & 0x0000ffff;
var lo = bits [i] & 0x0000ffff;
collector.value = collector.value + hi.toString (16) + lo.toString (16);
}
startup ();
}
</script>
</head>
<body>
<h1>Entropy Generator Progress</h1>
<p>Target: 192 bits, available at paranoia level 5.</p>
<p>Corresponding paranoia level from [0,1..10]: <input type="text" value="5" id="paranoialevel"/> <input type=button onclick="startup ()" id="startbutton" value=" Start &gt;&gt; "> (the idea being that you can see the progress bar advance gently from empty/black to full/yellow after you press this)</p>
<p><input type=button onclick="consume (192)" value=" Consume 192 bits &gt;&gt; "><input type=text id=collector size=50 value="" onkeypress="consume (192)"> (also consumes 192 bits with every keypress in the text field; use key repeat to consume swiftly)</p>
<div id="progressbar">
<div id="progresswidth"></div>
</div>
<p>Please move your mouse, play around and generally introduce entropy into your environment.</p>
</body>
</html>

View File

@@ -0,0 +1,44 @@
browserUtil = {
isNodeJS: true,
pauseAndThen: function (cb) { cb(); },
cpsIterate: function (f, start, end, pause, callback) {
function go() {
var called = false;
if (start >= end) {
callback && callback();
} else {
f(start, function () {
if (!called) { called = true; start++; go(); }
});
}
}
go (start);
},
cpsMap: function (map, list, pause, callback) {
browserUtil.cpsIterate(function (i, cb) { map(list[i], i, list.length, cb); },
0, list.length, pause, callback);
},
loadScripts: function(scriptNames, callback) {
for (i=0; i<scriptNames.length; i++) {
load(scriptNames[i]);
callback && callback();
}
},
write: function(type, message) {
console.log(message);
return { update: function (type2, message2) {
if (type2 === 'pass') { console.log(" + " + message2); }
else if (type2 === 'unimplemented') { console.log(" ? " + message2); }
else { console.log(" - " + message2); }
}};
},
writeNewline: function () { console.log(""); },
status: function(message) {}
};

View File

@@ -16,7 +16,7 @@ sub digitize {
}
while (<>) {
s/([^a-zA-Z0-9_])(\d+)/$1 . digitize $2/eg;
s/([^a-zA-Z0-9_"])(\d+)/$1 . digitize $2/eg;
print;
}

11
src/js/sjcl/configure vendored
View File

@@ -4,12 +4,13 @@ use strict;
my ($arg, $i, $j, $targ);
my @targets = qw/sjcl aes bitArray codecString codecHex codecBase64 codecBytes sha256 sha512 sha1 ccm cbc ocb2 gcm hmac pbkdf2 random convenience bn ecc srp/;
my @targets = qw/sjcl aes bitArray codecString codecHex codecBase32 codecBase64 codecBytes sha256 sha512 sha1 ccm cbc ocb2 gcm hmac pbkdf2 random convenience bn ecc srp/;
my %deps = ('aes'=>'sjcl',
'bitArray'=>'sjcl',
'codecString'=>'bitArray',
'codecHex'=>'bitArray',
'codecBase64'=>'bitArray',
'codecBase32'=>'bitArray',
'codecBytes'=>'bitArray',
'sha256'=>'codecString',
'sha512'=>'codecString',
@@ -32,10 +33,10 @@ my %enabled = ();
$enabled{$_} = 0 foreach (@targets);
# by default, all but codecBytes, srp, bn
$enabled{$_} = 1 foreach (qw/aes bitArray codecString codecHex codecBase64 sha256 ccm ocb2 gcm hmac pbkdf2 random convenience/);
$enabled{$_} = 1 foreach (qw/aes bitArray codecString codecHex codecBase32 codecBase64 sha256 ccm ocb2 gcm hmac pbkdf2 random convenience/);
# argument parsing
while ($arg = shift @ARGV) {
while (my $arg = shift @ARGV) {
if ($arg =~ /^--?with-all$/) {
foreach (@targets) {
if ($enabled{$_} == 0) {
@@ -97,7 +98,7 @@ my $config = '';
my $pconfig;
# dependency analysis: forbidden
foreach $i (@targets) {
foreach my $i (@targets) {
if ($enabled{$i} > 0) {
foreach $j (split /,/, $deps{$i}) {
if ($enabled{$j} == -1) {
@@ -114,7 +115,7 @@ foreach $i (@targets) {
}
# reverse
foreach $i (reverse @targets) {
foreach my $i (reverse @targets) {
if ($enabled{$i} > 0) {
foreach $j (split /,/, $deps{$i}) {
if ($enabled{$j} < $enabled{$i}) {

View File

@@ -74,7 +74,7 @@ sjcl.bitArray = {
return a1.concat(a2);
}
var out, i, last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last);
var last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last);
if (shift === 32) {
return a1.concat(a2);
} else {
@@ -183,5 +183,19 @@ sjcl.bitArray = {
*/
_xor4: function(x,y) {
return [x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]];
},
/** byteswap a word array inplace.
* (does not handle partial words)
* @param {sjcl.bitArray} a word array
* @return {sjcl.bitArray} byteswapped array
*/
byteswapM: function(a) {
var i, v, m = 0xff00;
for (i = 0; i < a.length; ++i) {
v = a[i];
a[i] = (v >>> 24) | ((v >>> 8) & m) | ((v & m) << 8) | (v << 24);
}
return a;
}
};

View File

@@ -19,7 +19,7 @@ sjcl.bn.prototype = {
* Initializes this with it, either as a bn, a number, or a hex string.
*/
initWith: function(it) {
var i=0, k, n, l;
var i=0, k;
switch(typeof it) {
case "object":
this.limbs = it.limbs.slice(0);
@@ -328,7 +328,7 @@ sjcl.bn.prototype = {
carry = (l-m)*ipv;
}
if (carry === -1) {
limbs[i-1] -= this.placeVal;
limbs[i-1] -= pv;
}
return this;
},
@@ -370,7 +370,9 @@ sjcl.bn.prototype = {
}
};
/** @this { sjcl.bn } */
/** @memberOf sjcl.bn
* @this { sjcl.bn }
*/
sjcl.bn.fromBits = function(bits) {
var Class = this, out = new Class(), words=[], w=sjcl.bitArray, t = this.prototype,
l = Math.min(this.bitLength || 0x100000000, w.bitLength(bits)), e = l % t.radix || t.radix;
@@ -394,7 +396,9 @@ sjcl.bn.prototype.radixMask = (1 << sjcl.bn.prototype.radix) - 1;
* i.e. a prime of the form 2^e + sum(a * 2^b),where the sum is negative and sparse.
*/
sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
/** @constructor */
/** @constructor
* @private
*/
function p(it) {
this.initWith(it);
/*if (this.limbs[this.modOffset]) {
@@ -427,10 +431,11 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
ppr.modulus.cnormalize();
/** Approximate reduction mod p. May leave a number which is negative or slightly larger than p.
* @this {sjcl.bn}
* @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr.reduce = function() {
var i, k, l, mo = this.modOffset, limbs = this.limbs, aff, off = this.offset, ol = this.offset.length, fac = this.factor, ll;
var i, k, l, mo = this.modOffset, limbs = this.limbs, off = this.offset, ol = this.offset.length, fac = this.factor, ll;
i = this.minOffset;
while (limbs.length > mo) {
@@ -452,7 +457,9 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
return this;
};
/** @this {sjcl.bn} */
/** @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr._strongReduce = (ppr.fullMask === -1) ? ppr.reduce : function() {
var limbs = this.limbs, i = limbs.length - 1, k, l;
this.reduce();
@@ -467,7 +474,8 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
};
/** mostly constant-time, very expensive full reduction.
* @this {sjcl.bn}
* @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr.fullReduce = function() {
var greater, i;
@@ -501,7 +509,9 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
};
/** @this {sjcl.bn} */
/** @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr.inverse = function() {
return (this.power(this.modulus.sub(2)));
};
@@ -512,18 +522,24 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
};
// a small Mersenne prime
var sbp = sjcl.bn.pseudoMersennePrime;
sjcl.bn.prime = {
p127: sjcl.bn.pseudoMersennePrime(127, [[0,-1]]),
p127: sbp(127, [[0,-1]]),
// Bernstein's prime for Curve25519
p25519: sjcl.bn.pseudoMersennePrime(255, [[0,-19]]),
p25519: sbp(255, [[0,-19]]),
// Koblitz primes
p192k: sbp(192, [[32,-1],[12,-1],[8,-1],[7,-1],[6,-1],[3,-1],[0,-1]]),
p224k: sbp(224, [[32,-1],[12,-1],[11,-1],[9,-1],[7,-1],[4,-1],[1,-1],[0,-1]]),
p256k: sbp(256, [[32,-1],[9,-1],[8,-1],[7,-1],[6,-1],[4,-1],[0,-1]]),
// NIST primes
p192: sjcl.bn.pseudoMersennePrime(192, [[0,-1],[64,-1]]),
p224: sjcl.bn.pseudoMersennePrime(224, [[0,1],[96,-1]]),
p256: sjcl.bn.pseudoMersennePrime(256, [[0,-1],[96,1],[192,1],[224,-1]]),
p384: sjcl.bn.pseudoMersennePrime(384, [[0,-1],[32,1],[96,-1],[128,-1]]),
p521: sjcl.bn.pseudoMersennePrime(521, [[0,-1]])
p192: sbp(192, [[0,-1],[64,-1]]),
p224: sbp(224, [[0,1],[96,-1]]),
p256: sbp(256, [[0,-1],[96,1],[192,1],[224,-1]]),
p384: sbp(384, [[0,-1],[32,1],[96,-1],[128,-1]]),
p521: sbp(521, [[0,-1]])
};
sjcl.bn.random = function(modulus, paranoia) {

View File

@@ -5,18 +5,18 @@
* @author Dan Boneh
*/
/** @namespace
* Dangerous: CBC mode with PKCS#5 padding.
*
* @author Emily Stark
* @author Mike Hamburg
* @author Dan Boneh
*/
if (sjcl.beware === undefined) {
sjcl.beware = {};
}
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."
] = function() {
/** @namespace
* Dangerous: CBC mode with PKCS#5 padding.
*
* @author Emily Stark
* @author Mike Hamburg
* @author Dan Boneh
*/
sjcl.mode.cbc = {
/** The name of the mode.
* @constant
@@ -100,7 +100,7 @@ sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity.
/* check and remove the pad */
bi = output[i-1] & 255;
if (bi == 0 || bi > 16) {
if (bi === 0 || bi > 16) {
throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");
}
bo = bi * 0x1010101;

View File

@@ -25,7 +25,7 @@ sjcl.mode.ccm = {
* @return {bitArray} The encrypted data, an array of bytes.
*/
encrypt: function(prf, plaintext, iv, adata, tlen) {
var L, i, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
var L, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
tlen = tlen || 64;
adata = adata || [];
@@ -59,7 +59,7 @@ sjcl.mode.ccm = {
decrypt: function(prf, ciphertext, iv, adata, tlen) {
tlen = tlen || 64;
adata = adata || [];
var L, i,
var L,
w=sjcl.bitArray,
ivl = w.bitLength(iv) / 8,
ol = w.bitLength(ciphertext),
@@ -101,7 +101,7 @@ sjcl.mode.ccm = {
*/
_computeTag: function(prf, plaintext, iv, adata, tlen, L) {
// compute B[0]
var q, mac, field = 0, offset = 24, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
var mac, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
tlen /= 8;
@@ -161,7 +161,7 @@ sjcl.mode.ccm = {
* @private
*/
_ctrMode: function(prf, data, iv, tag, tlen, L) {
var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, b, l = data.length, bl=w.bitLength(data);
var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, l = data.length, bl=w.bitLength(data);
// start the ctr
ctr = w.concat([w.partial(8,L-1)],iv).concat([0,0,0]).slice(0,4);

View File

@@ -0,0 +1,64 @@
/** @fileOverview Bit array codec implementations.
*
* @author Nils Kenneweg
*/
/** @namespace Base32 encoding/decoding */
sjcl.codec.base32 = {
/** The base32 alphabet.
* @private
*/
_chars: "0123456789abcdefghjkmnpqrstvwxyz",
/* bits in an array */
BITS: 32,
/* base to encode at (2^x) */
BASE: 5,
/* bits - base */
REMAINING: 27,
/** Convert from a bitArray to a base32 string. */
fromBits: function (arr, _noEquals) {
var BITS = sjcl.codec.base32.BITS, BASE = sjcl.codec.base32.BASE, REMAINING = sjcl.codec.base32.REMAINING;
var out = "", i, bits=0, c = sjcl.codec.base32._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
for (i=0; out.length * BASE <= bl; ) {
out += c.charAt((ta ^ arr[i]>>>bits) >>> REMAINING);
if (bits < BASE) {
ta = arr[i] << (BASE-bits);
bits += REMAINING;
i++;
} else {
ta <<= BASE;
bits -= BASE;
}
}
return out;
},
/** Convert from a base32 string to a bitArray */
toBits: function(str) {
var BITS = sjcl.codec.base32.BITS, BASE = sjcl.codec.base32.BASE, REMAINING = sjcl.codec.base32.REMAINING;
var out = [], i, bits=0, c = sjcl.codec.base32._chars, ta=0, x;
for (i=0; i<str.length; i++) {
x = c.indexOf(str.charAt(i));
if (x < 0) {
throw new sjcl.exception.invalid("this isn't base32!");
}
if (bits > REMAINING) {
bits -= REMAINING;
out.push(ta ^ x>>>bits);
ta = x << (BITS-bits);
} else {
bits += BASE;
ta ^= x << (BITS-bits);
}
}
if (bits&56) {
out.push(sjcl.bitArray.partial(bits&56, ta, 1));
}
return out;
}
};

View File

@@ -15,7 +15,9 @@ sjcl.codec.base64 = {
/** Convert from a bitArray to a base64 string. */
fromBits: function (arr, _noEquals, _url) {
var out = "", i, bits=0, c = sjcl.codec.base64._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
if (_url) c = c.substr(0,62) + '-_';
if (_url) {
c = c.substr(0,62) + '-_';
}
for (i=0; out.length * 6 < bl; ) {
out += c.charAt((ta ^ arr[i]>>>bits) >>> 26);
if (bits < 6) {
@@ -35,7 +37,9 @@ sjcl.codec.base64 = {
toBits: function(str, _url) {
str = str.replace(/\s|=/g,'');
var out = [], i, bits=0, c = sjcl.codec.base64._chars, ta=0, x;
if (_url) c = c.substr(0,62) + '-_';
if (_url) {
c = c.substr(0,62) + '-_';
}
for (i=0; i<str.length; i++) {
x = c.indexOf(str.charAt(i));
if (x < 0) {

View File

@@ -9,7 +9,7 @@
sjcl.codec.hex = {
/** Convert from a bitArray to a hex string. */
fromBits: function (arr) {
var out = "", i, x;
var out = "", i;
for (i=0; i<arr.length; i++) {
out += ((arr[i]|0)+0xF00000000000).toString(16).substr(4);
}

View File

@@ -15,13 +15,13 @@
* @param {String} plaintext The data to encrypt.
* @param {Object} [params] The parameters including tag, iv and salt.
* @param {Object} [rp] A returned version with filled-in parameters.
* @return {String} The ciphertext.
* @return {Object} The cipher raw data.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
*/
encrypt: function (password, plaintext, params, rp) {
_encrypt: function (password, plaintext, params, rp) {
params = params || {};
rp = rp || {};
var j = sjcl.json, p = j._add({ iv: sjcl.random.randomWords(4,0) },
j.defaults), tmp, prp, adata;
j._add(p, params);
@@ -32,7 +32,7 @@
if (typeof p.iv === "string") {
p.iv = sjcl.codec.base64.toBits(p.iv);
}
if (!sjcl.mode[p.mode] ||
!sjcl.cipher[p.cipher] ||
(typeof password === "string" && p.iter <= 100) ||
@@ -41,7 +41,7 @@
(p.iv.length < 2 || p.iv.length > 4)) {
throw new sjcl.exception.invalid("json encrypt: invalid parameters");
}
if (typeof password === "string") {
tmp = sjcl.misc.cachedPbkdf2(password, p);
password = tmp.key.slice(0,p.ks/32);
@@ -58,39 +58,52 @@
adata = sjcl.codec.utf8String.toBits(adata);
}
prp = new sjcl.cipher[p.cipher](password);
/* return the json data */
j._add(rp, p);
rp.key = password;
/* do the encryption */
p.ct = sjcl.mode[p.mode].encrypt(prp, plaintext, p.iv, adata, p.ts);
//return j.encode(j._subtract(p, j.defaults));
return p;
},
/** Simple encryption function.
* @param {String|bitArray} password The password or key.
* @param {String} plaintext The data to encrypt.
* @param {Object} [params] The parameters including tag, iv and salt.
* @param {Object} [rp] A returned version with filled-in parameters.
* @return {String} The ciphertext serialized data.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
*/
encrypt: function (password, plaintext, params, rp) {
var j = sjcl.json, p = j._encrypt.apply(j, arguments);
return j.encode(p);
},
/** Simple decryption function.
* @param {String|bitArray} password The password or key.
* @param {String} ciphertext The ciphertext to decrypt.
* @param {Object} ciphertext The cipher raw data to decrypt.
* @param {Object} [params] Additional non-default parameters.
* @param {Object} [rp] A returned object with filled parameters.
* @return {String} The plaintext.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
* @throws {sjcl.exception.corrupt} if the ciphertext is corrupt.
*/
decrypt: function (password, ciphertext, params, rp) {
_decrypt: function (password, ciphertext, params, rp) {
params = params || {};
rp = rp || {};
var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),j.decode(ciphertext)), params, true), ct, tmp, prp, adata=p.adata;
var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),ciphertext), params, true), ct, tmp, prp, adata=p.adata;
if (typeof p.salt === "string") {
p.salt = sjcl.codec.base64.toBits(p.salt);
}
if (typeof p.iv === "string") {
p.iv = sjcl.codec.base64.toBits(p.iv);
}
if (!sjcl.mode[p.mode] ||
!sjcl.cipher[p.cipher] ||
(typeof password === "string" && p.iter <= 100) ||
@@ -100,7 +113,7 @@
(p.iv.length < 2 || p.iv.length > 4)) {
throw new sjcl.exception.invalid("json decrypt: invalid parameters");
}
if (typeof password === "string") {
tmp = sjcl.misc.cachedPbkdf2(password, p);
password = tmp.key.slice(0,p.ks/32);
@@ -112,15 +125,33 @@
adata = sjcl.codec.utf8String.toBits(adata);
}
prp = new sjcl.cipher[p.cipher](password);
/* do the decryption */
ct = sjcl.mode[p.mode].decrypt(prp, p.ct, p.iv, adata, p.ts);
/* return the json data */
j._add(rp, p);
rp.key = password;
return sjcl.codec.utf8String.fromBits(ct);
if (params.raw === 1) {
return ct;
} else {
return sjcl.codec.utf8String.fromBits(ct);
}
},
/** Simple decryption function.
* @param {String|bitArray} password The password or key.
* @param {String} ciphertext The ciphertext to decrypt.
* @param {Object} [params] Additional non-default parameters.
* @param {Object} [rp] A returned object with filled parameters.
* @return {String} The plaintext.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
* @throws {sjcl.exception.corrupt} if the ciphertext is corrupt.
*/
decrypt: function (password, ciphertext, params, rp) {
var j = sjcl.json;
return j._decrypt(password, j.decode(ciphertext), params, rp);
},
/** Encode a flat structure into a JSON string.
@@ -138,23 +169,23 @@
}
out += comma + '"' + i + '":';
comma = ',';
switch (typeof obj[i]) {
case 'number':
case 'boolean':
out += obj[i];
break;
case 'string':
out += '"' + escape(obj[i]) + '"';
break;
case 'object':
out += '"' + sjcl.codec.base64.fromBits(obj[i],0) + '"';
break;
default:
throw new sjcl.exception.bug("json encode: unsupported type");
case 'number':
case 'boolean':
out += obj[i];
break;
case 'string':
out += '"' + escape(obj[i]) + '"';
break;
case 'object':
out += '"' + sjcl.codec.base64.fromBits(obj[i],0) + '"';
break;
default:
throw new sjcl.exception.bug("json encode: unsupported type");
}
}
}
@@ -174,13 +205,15 @@
}
var a = str.replace(/^\{|\}$/g, '').split(/,/), out={}, i, m;
for (i=0; i<a.length; i++) {
if (!(m=a[i].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))) {
if (!(m=a[i].match(/^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i))) {
throw new sjcl.exception.invalid("json decode: this isn't json!");
}
if (m[3]) {
out[m[2]] = parseInt(m[3],10);
} else {
} else if (m[4]) {
out[m[2]] = m[2].match(/^(ct|salt|iv)$/) ? sjcl.codec.base64.toBits(m[4]) : unescape(m[4]);
} else if (m[5]) {
out[m[2]] = m[5] === 'true';
}
}
return out;
@@ -213,13 +246,13 @@
*/
_subtract: function (plus, minus) {
var out = {}, i;
for (i in plus) {
if (plus.hasOwnProperty(i) && plus[i] !== minus[i]) {
out[i] = plus[i];
}
}
return out;
},
@@ -262,7 +295,7 @@ sjcl.misc._pbkdf2Cache = {};
/** Cached PBKDF2 key derivation.
* @param {String} password The password.
* @param {Object} [params] The derivation params (iteration count and optional salt).
* @param {Object} [obj] The derivation params (iteration count and optional salt).
* @return {Object} The derived data in key, the salt in salt.
*/
sjcl.misc.cachedPbkdf2 = function (password, obj) {

View File

@@ -1,3 +1,6 @@
/**
* base class for all ecc operations.
*/
sjcl.ecc = {};
/**
@@ -152,7 +155,9 @@ sjcl.ecc.pointJac.prototype = {
a = y2.mul(this.x.mul(4)),
b = y2.square().mul(8),
z2 = this.z.square(),
c = this.x.sub(z2).mul(3).mul(this.x.add(z2)),
c = this.curve.a.toString() == (new sjcl.bn(-3)).toString() ?
this.x.sub(z2).mul(3).mul(this.x.add(z2)) :
this.x.square().mul(3).add(z2.square().mul(this.curve.a)),
x = c.square().subM(a).subM(a),
y = a.sub(x).mul(c).subM(b),
z = this.y.add(this.y).mul(this.z);
@@ -162,7 +167,7 @@ sjcl.ecc.pointJac.prototype = {
/**
* Returns a copy of this point converted to affine coordinates.
* @return {sjcl.ecc.point} The converted point.
*/
*/
toAffine: function() {
if (this.isIdentity || this.z.equals(0)) {
return new sjcl.ecc.point(this.curve);
@@ -250,7 +255,7 @@ sjcl.ecc.pointJac.prototype = {
*/
sjcl.ecc.curve = function(Field, r, a, b, x, y) {
this.field = Field;
this.r = Field.prototype.modulus.sub(r);
this.r = new sjcl.bn(r);
this.a = new Field(a);
this.b = new Field(b);
this.G = new sjcl.ecc.point(this, new Field(x), new Field(y));
@@ -269,7 +274,7 @@ sjcl.ecc.curve.prototype.fromBits = function (bits) {
sjcl.ecc.curves = {
c192: new sjcl.ecc.curve(
sjcl.bn.prime.p192,
"0x662107c8eb94364e4b2dd7ce",
"0xffffffffffffffffffffffff99def836146bc9b1b4d22831",
-3,
"0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1",
"0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012",
@@ -277,7 +282,7 @@ sjcl.ecc.curves = {
c224: new sjcl.ecc.curve(
sjcl.bn.prime.p224,
"0xe95c1f470fc1ec22d6baa3a3d5c4",
"0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d",
-3,
"0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4",
"0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
@@ -285,7 +290,7 @@ sjcl.ecc.curves = {
c256: new sjcl.ecc.curve(
sjcl.bn.prime.p256,
"0x4319055358e8617b0c46353d039cdaae",
"0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
-3,
"0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
"0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
@@ -293,71 +298,135 @@ sjcl.ecc.curves = {
c384: new sjcl.ecc.curve(
sjcl.bn.prime.p384,
"0x389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68c",
"0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973",
-3,
"0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef",
"0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7",
"0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f")
"0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f"),
k192: new sjcl.ecc.curve(
sjcl.bn.prime.p192k,
"0xfffffffffffffffffffffffe26f2fc170f69466a74defd8d",
0,
3,
"0xdb4ff10ec057e9ae26b07d0280b7f4341da5d1b1eae06c7d",
"0x9b2f2f6d9c5628a7844163d015be86344082aa88d95e2f9d"),
k224: new sjcl.ecc.curve(
sjcl.bn.prime.p224k,
"0x010000000000000000000000000001dce8d2ec6184caf0a971769fb1f7",
0,
5,
"0xa1455b334df099df30fc28a169a467e9e47075a90f7e650eb6b7a45c",
"0x7e089fed7fba344282cafbd6f7e319f7c0b0bd59e2ca4bdb556d61a5"),
k256: new sjcl.ecc.curve(
sjcl.bn.prime.p256k,
"0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
0,
7,
"0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8")
};
/* Diffie-Hellman-like public-key system */
sjcl.ecc._dh = function(cn) {
sjcl.ecc[cn] = {
/** @constructor */
publicKey: function(curve, point) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
if (point instanceof Array) {
this._point = curve.fromBits(point);
} else {
this._point = point;
}
this.get = function() {
var pointbits = this._point.toBits();
var len = sjcl.bitArray.bitLength(pointbits);
var x = sjcl.bitArray.bitSlice(pointbits, 0, len/2);
var y = sjcl.bitArray.bitSlice(pointbits, len/2);
return { x: x, y: y };
}
},
/** @constructor */
secretKey: function(curve, exponent) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
this._exponent = exponent;
this.get = function() {
return this._exponent.toBits();
}
},
/** @constructor */
generateKeys: function(curve, paranoia, sec) {
if (curve === undefined) {
curve = 256;
}
if (typeof curve === "number") {
curve = sjcl.ecc.curves['c'+curve];
if (curve === undefined) {
throw new sjcl.exception.invalid("no such curve");
}
}
if (sec === undefined) {
var sec = sjcl.bn.random(curve.r, paranoia);
}
var pub = curve.G.mult(sec);
return { pub: new sjcl.ecc[cn].publicKey(curve, pub),
sec: new sjcl.ecc[cn].secretKey(curve, sec) };
/** our basicKey classes
*/
sjcl.ecc.basicKey = {
/** ecc publicKey.
* @constructor
* @param {curve} curve the elliptic curve
* @param {point} point the point on the curve
*/
publicKey: function(curve, point) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
if (point instanceof Array) {
this._point = curve.fromBits(point);
} else {
this._point = point;
}
};
/** get this keys point data
* @return x and y as bitArrays
*/
this.get = function() {
var pointbits = this._point.toBits();
var len = sjcl.bitArray.bitLength(pointbits);
var x = sjcl.bitArray.bitSlice(pointbits, 0, len/2);
var y = sjcl.bitArray.bitSlice(pointbits, len/2);
return { x: x, y: y };
};
},
/** ecc secretKey
* @constructor
* @param {curve} curve the elliptic curve
* @param exponent
*/
secretKey: function(curve, exponent) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
this._exponent = exponent;
/** get this keys exponent data
* @return {bitArray} exponent
*/
this.get = function () {
return this._exponent.toBits();
};
}
};
sjcl.ecc._dh("elGamal");
/** @private */
sjcl.ecc.basicKey.generateKeys = function(cn) {
return function generateKeys(curve, paranoia, sec) {
curve = curve || 256;
if (typeof curve === "number") {
curve = sjcl.ecc.curves['c'+curve];
if (curve === undefined) {
throw new sjcl.exception.invalid("no such curve");
}
}
sec = sec || sjcl.bn.random(curve.r, paranoia);
var pub = curve.G.mult(sec);
return { pub: new sjcl.ecc[cn].publicKey(curve, pub),
sec: new sjcl.ecc[cn].secretKey(curve, sec) };
};
};
/** elGamal keys */
sjcl.ecc.elGamal = {
/** generate keys
* @function
* @param curve
* @param {int} paranoia Paranoia for generation (default 6)
* @param {secretKey} sec secret Key to use. used to get the publicKey for ones secretKey
*/
generateKeys: sjcl.ecc.basicKey.generateKeys("elGamal"),
/** elGamal publicKey.
* @constructor
* @augments sjcl.ecc.basicKey.publicKey
*/
publicKey: function (curve, point) {
sjcl.ecc.basicKey.publicKey.apply(this, arguments);
},
/** elGamal secretKey
* @constructor
* @augments sjcl.ecc.basicKey.secretKey
*/
secretKey: function (curve, exponent) {
sjcl.ecc.basicKey.secretKey.apply(this, arguments);
}
};
sjcl.ecc.elGamal.publicKey.prototype = {
/** Kem function of elGamal Public Key
* @param paranoia paranoia to use for randomization.
* @return {object} key and tag. unkem(tag) with the corresponding secret key results in the key returned.
*/
kem: function(paranoia) {
var sec = sjcl.bn.random(this._curve.r, paranoia),
tag = this._curve.G.mult(sec).toBits(),
@@ -367,34 +436,58 @@ sjcl.ecc.elGamal.publicKey.prototype = {
};
sjcl.ecc.elGamal.secretKey.prototype = {
/** UnKem function of elGamal Secret Key
* @param {bitArray} tag The Tag to decrypt.
* @return {bitArray} decrypted key.
*/
unkem: function(tag) {
return sjcl.hash.sha256.hash(this._curve.fromBits(tag).mult(this._exponent).toBits());
},
/** Diffie-Hellmann function
* @param {elGamal.publicKey} pk The Public Key to do Diffie-Hellmann with
* @return {bitArray} diffie-hellmann result for this key combination.
*/
dh: function(pk) {
return sjcl.hash.sha256.hash(pk._point.mult(this._exponent).toBits());
}
},
/** Diffie-Hellmann function, compatible with Java generateSecret
* @param {elGamal.publicKey} pk The Public Key to do Diffie-Hellmann with
* @return {bitArray} undigested X value, diffie-hellmann result for this key combination,
* compatible with Java generateSecret().
*/
dhJavaEc: function(pk) {
return pk._point.mult(this._exponent).x.toBits();
}
};
sjcl.ecc._dh("ecdsa");
sjcl.ecc.ecdsa.secretKey.prototype = {
sign: function(hash, paranoia, fakeLegacyVersion, fixedKForTesting) {
if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) {
hash = sjcl.bitArray.clamp(hash, this._curveBitLength);
}
var R = this._curve.r,
l = R.bitLength(),
k = fixedKForTesting || sjcl.bn.random(R.sub(1), paranoia).add(1),
r = this._curve.G.mult(k).x.mod(R),
ss = sjcl.bn.fromBits(hash).add(r.mul(this._exponent)),
s = fakeLegacyVersion ? ss.inverseMod(R).mul(k).mod(R)
: ss.mul(k.inverseMod(R)).mod(R);
return sjcl.bitArray.concat(r.toBits(l), s.toBits(l));
}
/** ecdsa keys */
sjcl.ecc.ecdsa = {
/** generate keys
* @function
* @param curve
* @param {int} paranoia Paranoia for generation (default 6)
* @param {secretKey} sec secret Key to use. used to get the publicKey for ones secretKey
*/
generateKeys: sjcl.ecc.basicKey.generateKeys("ecdsa")
};
/** ecdsa publicKey.
* @constructor
* @augments sjcl.ecc.basicKey.publicKey
*/
sjcl.ecc.ecdsa.publicKey = function (curve, point) {
sjcl.ecc.basicKey.publicKey.apply(this, arguments);
};
/** specific functions for ecdsa publicKey. */
sjcl.ecc.ecdsa.publicKey.prototype = {
/** Diffie-Hellmann function
* @param {bitArray} hash hash to verify.
* @param {bitArray} rs signature bitArray.
* @param {boolean} fakeLegacyVersion use old legacy version
*/
verify: function(hash, rs, fakeLegacyVersion) {
if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) {
hash = sjcl.bitArray.clamp(hash, this._curveBitLength);
@@ -418,3 +511,33 @@ sjcl.ecc.ecdsa.publicKey.prototype = {
return true;
}
};
/** ecdsa secretKey
* @constructor
* @augments sjcl.ecc.basicKey.publicKey
*/
sjcl.ecc.ecdsa.secretKey = function (curve, exponent) {
sjcl.ecc.basicKey.secretKey.apply(this, arguments);
};
/** specific functions for ecdsa secretKey. */
sjcl.ecc.ecdsa.secretKey.prototype = {
/** Diffie-Hellmann function
* @param {bitArray} hash hash to sign.
* @param {int} paranoia paranoia for random number generation
* @param {boolean} fakeLegacyVersion use old legacy version
*/
sign: function(hash, paranoia, fakeLegacyVersion, fixedKForTesting) {
if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) {
hash = sjcl.bitArray.clamp(hash, this._curveBitLength);
}
var R = this._curve.r,
l = R.bitLength(),
k = fixedKForTesting || sjcl.bn.random(R.sub(1), paranoia).add(1),
r = this._curve.G.mult(k).x.mod(R),
ss = sjcl.bn.fromBits(hash).add(r.mul(this._exponent)),
s = fakeLegacyVersion ? ss.inverseMod(R).mul(k).mod(R)
: ss.mul(k.inverseMod(R)).mod(R);
return sjcl.bitArray.concat(r.toBits(l), s.toBits(l));
}
};

View File

@@ -120,7 +120,7 @@ sjcl.mode.gcm = {
* @param {Number} tlen The length of the tag, in bits.
*/
_ctrMode: function(encrypt, prf, data, adata, iv, tlen) {
var H, J0, S0, enc, i, ctr, tag, last, l, bl, abl, ivbl, w=sjcl.bitArray, xor=w._xor4;
var H, J0, S0, enc, i, ctr, tag, last, l, bl, abl, ivbl, w=sjcl.bitArray;
// Calculate data lengths
l = data.length;

View File

@@ -27,13 +27,35 @@ sjcl.misc.hmac = function (key, Hash) {
this._baseHash[0].update(exKey[0]);
this._baseHash[1].update(exKey[1]);
this._resultHash = new Hash(this._baseHash[0]);
};
/** HMAC with the specified hash function. Also called encrypt since it's a prf.
* @param {bitArray|String} data The data to mac.
*/
sjcl.misc.hmac.prototype.encrypt = sjcl.misc.hmac.prototype.mac = function (data) {
var w = new (this._hash)(this._baseHash[0]).update(data).finalize();
return new (this._hash)(this._baseHash[1]).update(w).finalize();
if (!this._updated) {
this.update(data);
return this.digest(data);
} else {
throw new sjcl.exception.invalid("encrypt on already updated hmac called!");
}
};
sjcl.misc.hmac.prototype.reset = function () {
this._resultHash = new this._hash(this._baseHash[0]);
this._updated = false;
};
sjcl.misc.hmac.prototype.update = function (data) {
this._updated = true;
this._resultHash.update(data);
};
sjcl.misc.hmac.prototype.digest = function () {
var w = this._resultHash.finalize(), result = new (this._hash)(this._baseHash[1]).update(w).finalize();
this.reset();
return result;
};

View File

@@ -12,7 +12,7 @@
* This is the method specified by RSA's PKCS #5 standard.
*
* @param {bitArray|String} password The password.
* @param {bitArray} salt The salt. Should have lots of entropy.
* @param {bitArray|String} salt The salt. Should have lots of entropy.
* @param {Number} [count=1000] The number of iterations. Higher numbers make the function slower but more secure.
* @param {Number} [length] The length of the derived key. Defaults to the
output size of the hash function.
@@ -30,6 +30,10 @@ sjcl.misc.pbkdf2 = function (password, salt, count, length, Prff) {
password = sjcl.codec.utf8String.toBits(password);
}
if (typeof salt === "string") {
salt = sjcl.codec.utf8String.toBits(salt);
}
Prff = Prff || sjcl.misc.hmac;
var prf = new Prff(password),

View File

@@ -3,12 +3,13 @@
* @author Emily Stark
* @author Mike Hamburg
* @author Dan Boneh
* @author Michael Brooks
*/
/** @constructor
* @class Random number generator
*
* @description
* <b>Use sjcl.random as a singleton for this class!</b>
* <p>
* This random number generator is a derivative of Ferguson and Schneier's
* generator Fortuna. It collects entropy from various events into several
@@ -74,10 +75,11 @@ sjcl.prng = function(defaultParanoia) {
this._PARANOIA_LEVELS = [0,48,64,96,128,192,256,384,512,768,1024];
this._MILLISECONDS_PER_RESEED = 30000;
this._BITS_PER_RESEED = 80;
}
};
sjcl.prng.prototype = {
/** Generate several random words, and return them in an array
/** Generate several random words, and return them in an array.
* A word consists of 32 bits (4 bytes)
* @param {Number} nwords The number of words to generate.
*/
randomWords: function (nwords, paranoia) {
@@ -102,7 +104,11 @@ sjcl.prng.prototype = {
return out.slice(0,nwords);
},
setDefaultParanoia: function (paranoia) {
setDefaultParanoia: function (paranoia, allowZeroParanoia) {
if (paranoia === 0 && allowZeroParanoia !== "Setting paranoia=0 will ruin your security; use it only for testing") {
throw "Setting paranoia=0 will ruin your security; use it only for testing";
}
this._defaultParanoia = paranoia;
},
@@ -119,7 +125,7 @@ sjcl.prng.prototype = {
i, tmp,
t = (new Date()).valueOf(),
robin = this._robins[source],
oldReady = this.isReady(), err = 0;
oldReady = this.isReady(), err = 0, objName;
id = this._collectorIds[source];
if (id === undefined) { id = this._collectorIds[source] = this._collectorIdNext ++; }
@@ -137,7 +143,7 @@ sjcl.prng.prototype = {
break;
case "object":
var objName = Object.prototype.toString.call(data);
objName = Object.prototype.toString.call(data);
if (objName === "[object Uint32Array]") {
tmp = [];
for (i = 0; i < data.length; i++) {
@@ -149,7 +155,7 @@ sjcl.prng.prototype = {
err = 1;
}
for (i=0; i<data.length && !err; i++) {
if (typeof(data[i]) != "number") {
if (typeof(data[i]) !== "number") {
err = 1;
}
}
@@ -234,14 +240,25 @@ sjcl.prng.prototype = {
startCollectors: function () {
if (this._collectorsStarted) { return; }
this._eventListener = {
loadTimeCollector: this._bind(this._loadTimeCollector),
mouseCollector: this._bind(this._mouseCollector),
keyboardCollector: this._bind(this._keyboardCollector),
accelerometerCollector: this._bind(this._accelerometerCollector),
touchCollector: this._bind(this._touchCollector)
};
if (window.addEventListener) {
window.addEventListener("load", this._loadTimeCollector, false);
window.addEventListener("mousemove", this._mouseCollector, false);
window.addEventListener("load", this._eventListener.loadTimeCollector, false);
window.addEventListener("mousemove", this._eventListener.mouseCollector, false);
window.addEventListener("keypress", this._eventListener.keyboardCollector, false);
window.addEventListener("devicemotion", this._eventListener.accelerometerCollector, false);
window.addEventListener("touchmove", this._eventListener.touchCollector, false);
} else if (document.attachEvent) {
document.attachEvent("onload", this._loadTimeCollector);
document.attachEvent("onmousemove", this._mouseCollector);
}
else {
document.attachEvent("onload", this._eventListener.loadTimeCollector);
document.attachEvent("onmousemove", this._eventListener.mouseCollector);
document.attachEvent("keypress", this._eventListener.keyboardCollector);
} else {
throw new sjcl.exception.bug("can't attach event");
}
@@ -253,12 +270,17 @@ sjcl.prng.prototype = {
if (!this._collectorsStarted) { return; }
if (window.removeEventListener) {
window.removeEventListener("load", this._loadTimeCollector, false);
window.removeEventListener("mousemove", this._mouseCollector, false);
} else if (window.detachEvent) {
window.detachEvent("onload", this._loadTimeCollector);
window.detachEvent("onmousemove", this._mouseCollector);
window.removeEventListener("load", this._eventListener.loadTimeCollector, false);
window.removeEventListener("mousemove", this._eventListener.mouseCollector, false);
window.removeEventListener("keypress", this._eventListener.keyboardCollector, false);
window.removeEventListener("devicemotion", this._eventListener.accelerometerCollector, false);
window.removeEventListener("touchmove", this._eventListener.touchCollector, false);
} else if (document.detachEvent) {
document.detachEvent("onload", this._eventListener.loadTimeCollector);
document.detachEvent("onmousemove", this._eventListener.mouseCollector);
document.detachEvent("keypress", this._eventListener.keyboardCollector);
}
this._collectorsStarted = false;
},
@@ -275,23 +297,30 @@ sjcl.prng.prototype = {
/** remove an event listener for progress or seeded-ness */
removeEventListener: function (name, cb) {
var i, j, cbs=this._callbacks[name], jsTemp=[];
/* I'm not sure if this is necessary; in C++, iterating over a
* collection and modifying it at the same time is a no-no.
*/
for (j in cbs) {
if (cbs.hasOwnProperty(j) && cbs[j] === cb) {
if (cbs.hasOwnProperty(j) && cbs[j] === cb) {
jsTemp.push(j);
}
}
for (i=0; i<jsTemp.length; i++) {
j = jsTemp[i];
delete cbs[j];
}
},
_bind: function (func) {
var that = this;
return function () {
func.apply(that, arguments);
};
},
/** Generate 4 random words, no reseed, no gate.
* @private
*/
@@ -363,44 +392,131 @@ sjcl.prng.prototype = {
this._reseed(reseedData);
},
_keyboardCollector: function () {
this._addCurrentTimeToEntropy(1);
},
_mouseCollector: function (ev) {
var x = ev.x || ev.clientX || ev.offsetX || 0, y = ev.y || ev.clientY || ev.offsetY || 0;
sjcl.random.addEntropy([x,y], 2, "mouse");
var x, y;
try {
x = ev.x || ev.clientX || ev.offsetX || 0;
y = ev.y || ev.clientY || ev.offsetY || 0;
} catch (err) {
// Event originated from a secure element. No mouse position available.
x = 0;
y = 0;
}
if (x != 0 && y!= 0) {
sjcl.random.addEntropy([x,y], 2, "mouse");
}
this._addCurrentTimeToEntropy(0);
},
_touchCollector: function(ev) {
var touch = ev.touches[0] || ev.changedTouches[0];
var x = touch.pageX || touch.clientX,
y = touch.pageY || touch.clientY;
sjcl.random.addEntropy([x,y],1,"touch");
this._addCurrentTimeToEntropy(0);
},
_loadTimeCollector: function (ev) {
sjcl.random.addEntropy((new Date()).valueOf(), 2, "loadtime");
_loadTimeCollector: function () {
this._addCurrentTimeToEntropy(2);
},
_addCurrentTimeToEntropy: function (estimatedEntropy) {
if (typeof window !== 'undefined' && window.performance && typeof window.performance.now === "function") {
//how much entropy do we want to add here?
sjcl.random.addEntropy(window.performance.now(), estimatedEntropy, "loadtime");
} else {
sjcl.random.addEntropy((new Date()).valueOf(), estimatedEntropy, "loadtime");
}
},
_accelerometerCollector: function (ev) {
var ac = ev.accelerationIncludingGravity.x||ev.accelerationIncludingGravity.y||ev.accelerationIncludingGravity.z;
if(window.orientation){
var or = window.orientation;
if (typeof or === "number") {
sjcl.random.addEntropy(or, 1, "accelerometer");
}
}
if (ac) {
sjcl.random.addEntropy(ac, 2, "accelerometer");
}
this._addCurrentTimeToEntropy(0);
},
_fireEvent: function (name, arg) {
var j, cbs=sjcl.random._callbacks[name], cbsTemp=[];
/* TODO: there is a race condition between removing collectors and firing them */
/* TODO: there is a race condition between removing collectors and firing them */
/* I'm not sure if this is necessary; in C++, iterating over a
* collection and modifying it at the same time is a no-no.
*/
for (j in cbs) {
if (cbs.hasOwnProperty(j)) {
if (cbs.hasOwnProperty(j)) {
cbsTemp.push(cbs[j]);
}
}
}
for (j=0; j<cbsTemp.length; j++) {
cbsTemp[j](arg);
cbsTemp[j](arg);
}
}
};
/** an instance for the prng.
* @see sjcl.prng
*/
sjcl.random = new sjcl.prng(6);
(function(){
try {
// get cryptographically strong entropy in Webkit
var ab = new Uint32Array(32);
crypto.getRandomValues(ab);
sjcl.random.addEntropy(ab, 1024, "crypto.getRandomValues");
} catch (e) {
// no getRandomValues :-(
// function for getting nodejs crypto module. catches and ignores errors.
function getCryptoModule() {
try {
return require('crypto');
}
catch (e) {
return null;
}
}
})();
try {
var buf, crypt, ab;
// get cryptographically strong entropy depending on runtime environment
if (typeof module !== 'undefined' && module.exports && (crypt = getCryptoModule()) && crypt.randomBytes) {
buf = crypt.randomBytes(1024/8);
buf = new Uint32Array(new Uint8Array(buf).buffer);
sjcl.random.addEntropy(buf, 1024, "crypto.randomBytes");
} else if (typeof window !== 'undefined' && typeof Uint32Array !== 'undefined') {
ab = new Uint32Array(32);
if (window.crypto && window.crypto.getRandomValues) {
window.crypto.getRandomValues(ab);
} else if (window.msCrypto && window.msCrypto.getRandomValues) {
window.msCrypto.getRandomValues(ab);
} else {
return;
}
// get cryptographically strong entropy in Webkit
sjcl.random.addEntropy(ab, 1024, "crypto.getRandomValues");
} else {
// no getRandomValues :-(
}
} catch (e) {
if (typeof window !== 'undefined' && window.console) {
console.log("There was an error collecting entropy from the browser:");
console.log(e);
//we do not want the library to fail due to randomness not being maintained.
}
}
}());

View File

@@ -138,8 +138,7 @@ sjcl.hash.sha1.prototype = {
_block:function (words) {
var t, tmp, a, b, c, d, e,
w = words.slice(0),
h = this._h,
k = this._key;
h = this._h;
a = h[0]; b = h[1]; c = h[2]; d = h[3]; e = h[4];

View File

@@ -299,7 +299,7 @@ sjcl.hash.sha512.prototype = {
t1h += chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0);
t1l += krl;
t1h += krh + ((t1l >>> 0) < (krl >>> 0) ? 1 : 0);
t1l += wrl;
t1l = t1l + wrl|0; // FF32..FF34 perf issue https://bugzilla.mozilla.org/show_bug.cgi?id=1054972
t1h += wrh + ((t1l >>> 0) < (wrl >>> 0) ? 1 : 0);
// t2 = sigma0 + maj

View File

@@ -10,7 +10,7 @@
"use strict";
/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */
/*global document, window, escape, unescape */
/*global document, window, escape, unescape, module, require, Uint32Array */
/** @namespace The Stanford Javascript Crypto Library, top-level namespace. */
var sjcl = {
@@ -68,6 +68,11 @@ var sjcl = {
}
};
if(typeof module != 'undefined' && module.exports){
if(typeof module !== 'undefined' && module.exports){
module.exports = sjcl;
}
if (typeof define === "function") {
define([], function () {
return sjcl;
});
}

View File

@@ -36,7 +36,7 @@
<h2>Key Derivation</h2>
<div class="section">
<div>
<label for="salt"">Salt:</label>
<label for="salt">Salt:</label>
<a class="random floatright" href="javascript:randomize('salt',2,0)">random</a>
</div>
<input type="text" id="salt" class="wide hex" autocomplete="off" size="17" maxlength="35"/>

16
src/js/sjcl/package.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "sjcl",
"version": "1.0.1",
"description": "Stanford Javascript Crypto Library",
"main": "sjcl.js",
"author": "bitwiseshiftleft",
"keywords": ["encryption", "high-level", "crypto"],
"repository" : {
"type": "git",
"url": "https://github.com/bitwiseshiftleft/sjcl.git"
},
"scripts": {
"test": "make test"
},
"engines": { "node": "*" }
}

View File

@@ -1,48 +1,54 @@
"use strict";function q(a){throw a;}var t=void 0,u=!1;var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
"undefined"!=typeof module&&module.exports&&(module.exports=sjcl);
sjcl.cipher.aes=function(a){this.j[0][0][0]||this.D();var b,c,d,e,f=this.j[0][4],g=this.j[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&
"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);"function"===typeof define&&define([],function(){return sjcl});
sjcl.cipher.aes=function(a){this.k[0][0][0]||this.D();var b,c,d,e,f=this.k[0][4],g=this.k[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&
255]]};
sjcl.cipher.aes.prototype={encrypt:function(a){return y(this,a,0)},decrypt:function(a){return y(this,a,1)},j:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.j[0],b=this.j[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e=
sjcl.cipher.aes.prototype={encrypt:function(a){return y(this,a,0)},decrypt:function(a){return y(this,a,1)},k:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.k[0],b=this.k[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e=
0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}};
function y(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.a[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,s=[0,0,0,0];h=a.j[c];a=h[0];var r=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m<n;m++)h=a[e>>>24]^r[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4>
function y(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,s=[0,0,0,0];h=a.k[c];a=h[0];var r=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m<n;m++)h=a[e>>>24]^r[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4>
m;m++)s[c?3&-m:m]=x[e>>>24]<<24^x[f>>16&255]<<16^x[g>>8&255]<<8^x[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return s}
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.O(a.slice(b/32),32-(b&31)).slice(1);return c===t?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.O(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===t?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.P(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
b?0:32*(b-1)+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(32*a.length<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;0<c&&b&&(a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return u;var c=0,d;for(d=0;d<a.length;d++)c|=a[d]^b[d];return 0===
c},O:function(a,b,c,d){var e;e=0;for(d===t&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
c},P:function(a,b,c,d){var e;e=0;for(d===t&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},l:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]},byteswapM:function(a){var b,c;for(b=0;b<a.length;++b)c=a[b],a[b]=c>>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}};
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++)0===(d&3)&&(e=a[d/4]),b+=String.fromCharCode(e>>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++)d=d<<8|a.charCodeAt(c),3===(c&3)&&(b.push(d),d=0);c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,4*d)}};
sjcl.codec.base64={I:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.I,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.I,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;d<a.length;d++)h=f.indexOf(a.charAt(d)),
0>h&&q(new sjcl.exception.invalid("this isn't base64!")),26<e?(e-=26,c.push(g^h>>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.a[0]||this.D();a?(this.q=a.q.slice(0),this.m=a.m.slice(0),this.g=a.g):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.q=this.M.slice(0);this.m=[];this.g=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.m=sjcl.bitArray.concat(this.m,a);b=this.g;a=this.g=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)z(this,c.splice(0,16));return this},finalize:function(){var a,b=this.m,c=this.q,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.g/
4294967296));for(b.push(this.g|0);b.length;)z(this,b.splice(0,16));this.reset();return c},M:[],a:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.M[b]=a(Math.pow(c,0.5)));this.a[b]=a(Math.pow(c,1/3));b++}}};
function z(a,b){var c,d,e,f=b.slice(0),g=a.q,h=a.a,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],s=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(s^r))+h[c],v=r,r=s,s=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+s|0;g[6]=
sjcl.codec.base64={J:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.J,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.J,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;d<a.length;d++)h=f.indexOf(a.charAt(d)),
0>h&&q(new sjcl.exception.invalid("this isn't base64!")),26<e?(e-=26,c.push(g^h>>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.D();a?(this.r=a.r.slice(0),this.o=a.o.slice(0),this.h=a.h):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.r=this.N.slice(0);this.o=[];this.h=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.o=sjcl.bitArray.concat(this.o,a);b=this.h;a=this.h=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)z(this,c.splice(0,16));return this},finalize:function(){var a,b=this.o,c=this.r,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.h/
4294967296));for(b.push(this.h|0);b.length;)z(this,b.splice(0,16));this.reset();return c},N:[],b:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.N[b]=a(Math.pow(c,0.5)));this.b[b]=a(Math.pow(c,1/3));b++}}};
function z(a,b){var c,d,e,f=b.slice(0),g=a.r,h=a.b,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],s=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(s^r))+h[c],v=r,r=s,s=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+s|0;g[6]=
g[6]+r|0;g[7]=g[7]+v|0}
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.K(a,b,c,d,e,f);g=sjcl.mode.ccm.n(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b,
h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.n(a,l,c,k,e,b);a=sjcl.mode.ccm.K(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return l.data},K:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.k;e/=8;(e%2||4>e||16<e)&&q(new sjcl.exception.invalid("ccm: invalid tag length"));(0xffffffff<d.length||0xffffffff<b.length)&&q(new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"));
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(l(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(l(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,8*e)},n:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.k;var l=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.L(a,b,c,d,e,f);g=sjcl.mode.ccm.p(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b,
h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.p(a,l,c,k,e,b);a=sjcl.mode.ccm.L(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return l.data},L:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.l;e/=8;(e%2||4>e||16<e)&&q(new sjcl.exception.invalid("ccm: invalid tag length"));(0xffffffff<d.length||0xffffffff<b.length)&&q(new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"));
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(l(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(l(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,8*e)},p:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.l;var l=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!l)return{tag:d,data:[]};for(g=0;g<l;g+=4)c[3]++,e=a.encrypt(c),b[g]^=e[0],b[g+1]^=e[1],b[g+2]^=e[2],b[g+3]^=e[3];return{tag:d,data:h.clamp(b,k)}}};
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));var g,h=sjcl.mode.ocb2.G,l=sjcl.bitArray,k=l.k,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4)m=b.slice(g,g+4),n=k(n,m),p=p.concat(k(c,a.encrypt(k(c,m)))),c=h(c);m=b.slice(g);b=l.bitLength(m);g=a.encrypt(k(c,[0,0,0,b]));m=l.clamp(k(m.concat([0,0,0]),g),b);n=k(n,k(m.concat([0,0,0]),g));n=a.encrypt(k(n,k(c,h(c))));d.length&&
(n=k(n,f?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(l.concat(m,l.clamp(n,e)))},decrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));e=e||64;var g=sjcl.mode.ocb2.G,h=sjcl.bitArray,l=h.k,k=[0,0,0,0],n=g(a.encrypt(c)),m,p,s=sjcl.bitArray.bitLength(b)-e,r=[];d=d||[];for(c=0;c+4<s/32;c+=4)m=l(n,a.decrypt(l(n,b.slice(c,c+4)))),k=l(k,m),r=r.concat(m),n=g(n);p=s-32*c;m=a.encrypt(l(n,[0,0,0,p]));m=l(m,h.clamp(b.slice(c),p).concat([0,0,0]));
k=l(k,m);k=a.encrypt(l(k,l(n,g(n))));d.length&&(k=l(k,f?d:sjcl.mode.ocb2.pmac(a,d)));h.equal(h.clamp(k,e),h.bitSlice(b,s))||q(new sjcl.exception.corrupt("ocb: tag doesn't match"));return r.concat(h.clamp(m,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.G,e=sjcl.bitArray,f=e.k,g=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),g=f(g,a.encrypt(f(h,b.slice(c,c+4))));c=b.slice(c);128>e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h,
d(h))),g))},G:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};
sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.n(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.n(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},U:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.k;e=[0,0,0,0];f=b.slice(0);
for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0<d;d--)f[d]=f[d]>>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},f:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.U(b,a);return b},n:function(a,b,c,d,e,f){var g,h,l,k,n,m,p,s,r=sjcl.bitArray;m=c.length;p=r.bitLength(c);s=r.bitLength(d);h=r.bitLength(e);g=b.encrypt([0,
0,0,0]);96===h?(e=e.slice(0),e=r.concat(e,[1])):(e=sjcl.mode.gcm.f(g,[0,0,0,0],e),e=sjcl.mode.gcm.f(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.f(g,[0,0,0,0],d);n=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.f(g,h,c));for(k=0;k<m;k+=4)n[3]++,l=b.encrypt(n),c[k]^=l[0],c[k+1]^=l[1],c[k+2]^=l[2],c[k+3]^=l[3];c=r.clamp(c,p);a&&(d=sjcl.mode.gcm.f(g,h,c));a=[Math.floor(s/0x100000000),s&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.f(g,d,a);l=b.encrypt(e);d[0]^=l[0];
d[1]^=l[1];d[2]^=l[2];d[3]^=l[3];return{tag:r.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.L=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.o=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.o[0].update(c[0]);this.o[1].update(c[1])};sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){a=(new this.L(this.o[0])).update(a).finalize();return(new this.L(this.o[1])).update(a).finalize()};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;(0>d||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}d&&(k=n.clamp(k,d));return k};
sjcl.prng=function(a){this.b=[new sjcl.hash.sha256];this.h=[0];this.F=0;this.t={};this.C=0;this.J={};this.N=this.c=this.i=this.T=0;this.a=[0,0,0,0,0,0,0,0];this.e=[0,0,0,0];this.A=t;this.B=a;this.p=u;this.z={progress:{},seeded:{}};this.l=this.S=0;this.u=1;this.w=2;this.Q=0x10000;this.H=[0,48,64,96,128,192,0x100,384,512,768,1024];this.R=3E4;this.P=80};
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;d===this.l&&q(new sjcl.exception.notReady("generator isn't seeded"));if(d&this.w){d=!(d&this.u);e=[];var f=0,g;this.N=e[0]=(new Date).valueOf()+this.R;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.b.length&&!(e=e.concat(this.b[g].finalize()),f+=this.h[g],this.h[g]=0,!d&&this.F&1<<g);g++);this.F>=1<<this.b.length&&(this.b.push(new sjcl.hash.sha256),this.h.push(0));this.c-=f;f>this.i&&(this.i=f);this.F++;
this.a=sjcl.hash.sha256.hash(this.a.concat(e));this.A=new sjcl.cipher.aes(this.a);for(d=0;4>d&&!(this.e[d]=this.e[d]+1|0,this.e[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.Q&&A(this),e=B(this),c.push(e[0],e[1],e[2],e[3]);A(this);return c.slice(0,a)},setDefaultParanoia:function(a){this.B=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.t[c],h=this.isReady(),l=0;d=this.J[c];d===t&&(d=this.J[c]=this.T++);g===t&&(g=this.t[c]=0);this.t[c]=(this.t[c]+1)%this.b.length;switch(typeof a){case "number":b===
t&&(b=1);this.b[g].update([d,this.C++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{"[object Array]"!==c&&(l=1);for(c=0;c<a.length&&!l;c++)"number"!=typeof a[c]&&(l=1)}if(!l){if(b===t)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,e>>>=1;this.b[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case "string":b===t&&(b=a.length);this.b[g].update([d,this.C++,3,b,f,a.length]);this.b[g].update(a);
break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.h[g]+=b;this.c+=b;h===this.l&&(this.isReady()!==this.l&&C("seeded",Math.max(this.i,this.c)),C("progress",this.getProgress()))},isReady:function(a){a=this.H[a!==t?a:this.B];return this.i&&this.i>=a?this.h[0]>this.P&&(new Date).valueOf()>this.N?this.w|this.u:this.u:this.c>=a?this.w|this.l:this.l},getProgress:function(a){a=this.H[a?a:this.B];return this.i>=a?1:this.c>a?1:this.c/
a},startCollectors:function(){this.p||(window.addEventListener?(window.addEventListener("load",this.r,u),window.addEventListener("mousemove",this.s,u)):document.attachEvent?(document.attachEvent("onload",this.r),document.attachEvent("onmousemove",this.s)):q(new sjcl.exception.bug("can't attach event")),this.p=!0)},stopCollectors:function(){this.p&&(window.removeEventListener?(window.removeEventListener("load",this.r,u),window.removeEventListener("mousemove",this.s,u)):window.detachEvent&&(window.detachEvent("onload",
this.r),window.detachEvent("onmousemove",this.s)),this.p=u)},addEventListener:function(a,b){this.z[a][this.S++]=b},removeEventListener:function(a,b){var c,d,e=this.z[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;c<f.length;c++)d=f[c],delete e[d]},s:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX||0,a.y||a.clientY||a.offsetY||0],2,"mouse")},r:function(){sjcl.random.addEntropy((new Date).valueOf(),2,"loadtime")}};
function C(a,b){var c,d=sjcl.random.z[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}function A(a){a.a=B(a).concat(B(a));a.A=new sjcl.cipher.aes(a.a)}function B(a){for(var b=0;4>b&&!(a.e[b]=a.e[b]+1|0,a.e[b]);b++);return a.A.encrypt(a.e)}sjcl.random=new sjcl.prng(6);try{var D=new Uint32Array(32);crypto.getRandomValues(D);sjcl.random.addEntropy(D,1024,"crypto['getRandomValues']")}catch(E){}
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.d({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.d(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||
4<f.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.d(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return e.encode(f)},
decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.d(e.d(e.d({},e.defaults),e.decode(b)),c,!0);var f;c=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)&&q(new sjcl.exception.invalid("json decrypt: invalid parameters"));
"string"===typeof a?(f=sjcl.misc.cachedPbkdf2(a,b),a=f.key.slice(0,b.ks/32),b.salt=f.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));f=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,c,b.ts);e.d(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b))switch(b.match(/^[a-z0-9]+$/i)||
q(new sjcl.exception.invalid("json encode: invalid property name")),c+=d+'"'+b+'":',d=",",typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:q(new sjcl.exception.bug("json encode: unsupported type"))}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");a.match(/^\{.*\}$/)||q(new sjcl.exception.invalid("json decode: this isn't json!"));a=a.replace(/^\{|\}$/g,"").split(/,/);var b=
{},c,d;for(c=0;c<a.length;c++)(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))||q(new sjcl.exception.invalid("json decode: this isn't json!")),b[d[2]]=d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]);return b},d:function(a,b,c){a===t&&(a={});if(b===t)return a;for(var d in b)b.hasOwnProperty(d)&&(c&&(a[d]!==t&&a[d]!==b[d])&&q(new sjcl.exception.invalid("required parameter overridden")),a[d]=b[d]);return a},X:function(a,
b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},W:function(a,b){var c={},d;for(d=0;d<b.length;d++)a[b[d]]!==t&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.V={};
sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.V,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===t?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));var g,h=sjcl.mode.ocb2.H,l=sjcl.bitArray,k=l.l,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4)m=b.slice(g,g+4),n=k(n,m),p=p.concat(k(c,a.encrypt(k(c,m)))),c=h(c);m=b.slice(g);b=l.bitLength(m);g=a.encrypt(k(c,[0,0,0,b]));m=l.clamp(k(m.concat([0,0,0]),g),b);n=k(n,k(m.concat([0,0,0]),g));n=a.encrypt(k(n,k(c,h(c))));d.length&&
(n=k(n,f?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(l.concat(m,l.clamp(n,e)))},decrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));e=e||64;var g=sjcl.mode.ocb2.H,h=sjcl.bitArray,l=h.l,k=[0,0,0,0],n=g(a.encrypt(c)),m,p,s=sjcl.bitArray.bitLength(b)-e,r=[];d=d||[];for(c=0;c+4<s/32;c+=4)m=l(n,a.decrypt(l(n,b.slice(c,c+4)))),k=l(k,m),r=r.concat(m),n=g(n);p=s-32*c;m=a.encrypt(l(n,[0,0,0,p]));m=l(m,h.clamp(b.slice(c),p).concat([0,0,0]));
k=l(k,m);k=a.encrypt(l(k,l(n,g(n))));d.length&&(k=l(k,f?d:sjcl.mode.ocb2.pmac(a,d)));h.equal(h.clamp(k,e),h.bitSlice(b,s))||q(new sjcl.exception.corrupt("ocb: tag doesn't match"));return r.concat(h.clamp(m,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.H,e=sjcl.bitArray,f=e.l,g=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),g=f(g,a.encrypt(f(h,b.slice(c,c+4))));c=b.slice(c);128>e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h,
d(h))),g))},H:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};
sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.p(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.p(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},Z:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.l;e=[0,0,0,0];f=b.slice(0);
for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0<d;d--)f[d]=f[d]>>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},g:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.Z(b,a);return b},p:function(a,b,c,d,e,f){var g,h,l,k,n,m,p,s,r=sjcl.bitArray;m=c.length;p=r.bitLength(c);s=r.bitLength(d);h=r.bitLength(e);g=b.encrypt([0,
0,0,0]);96===h?(e=e.slice(0),e=r.concat(e,[1])):(e=sjcl.mode.gcm.g(g,[0,0,0,0],e),e=sjcl.mode.gcm.g(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.g(g,[0,0,0,0],d);n=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.g(g,h,c));for(k=0;k<m;k+=4)n[3]++,l=b.encrypt(n),c[k]^=l[0],c[k+1]^=l[1],c[k+2]^=l[2],c[k+3]^=l[3];c=r.clamp(c,p);a&&(d=sjcl.mode.gcm.g(g,h,c));a=[Math.floor(s/0x100000000),s&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.g(g,d,a);l=b.encrypt(e);d[0]^=l[0];
d[1]^=l[1];d[2]^=l[2];d[3]^=l[3];return{tag:r.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.M=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.n=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.n[0].update(c[0]);this.n[1].update(c[1]);this.G=new b(this.n[0])};
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){this.Q&&q(new sjcl.exception.invalid("encrypt on already updated hmac called!"));this.update(a);return this.digest(a)};sjcl.misc.hmac.prototype.reset=function(){this.G=new this.M(this.n[0]);this.Q=u};sjcl.misc.hmac.prototype.update=function(a){this.Q=!0;this.G.update(a)};sjcl.misc.hmac.prototype.digest=function(){var a=this.G.finalize(),a=(new this.M(this.n[1])).update(a).finalize();this.reset();return a};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;(0>d||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}d&&(k=n.clamp(k,d));return k};
sjcl.prng=function(a){this.c=[new sjcl.hash.sha256];this.i=[0];this.F=0;this.s={};this.C=0;this.K={};this.O=this.d=this.j=this.W=0;this.b=[0,0,0,0,0,0,0,0];this.f=[0,0,0,0];this.A=t;this.B=a;this.q=u;this.w={progress:{},seeded:{}};this.m=this.V=0;this.t=1;this.u=2;this.S=0x10000;this.I=[0,48,64,96,128,192,0x100,384,512,768,1024];this.T=3E4;this.R=80};
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;d===this.m&&q(new sjcl.exception.notReady("generator isn't seeded"));if(d&this.u){d=!(d&this.t);e=[];var f=0,g;this.O=e[0]=(new Date).valueOf()+this.T;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.c.length&&!(e=e.concat(this.c[g].finalize()),f+=this.i[g],this.i[g]=0,!d&&this.F&1<<g);g++);this.F>=1<<this.c.length&&(this.c.push(new sjcl.hash.sha256),this.i.push(0));this.d-=f;f>this.j&&(this.j=f);this.F++;
this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.A=new sjcl.cipher.aes(this.b);for(d=0;4>d&&!(this.f[d]=this.f[d]+1|0,this.f[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.S&&A(this),e=B(this),c.push(e[0],e[1],e[2],e[3]);A(this);return c.slice(0,a)},setDefaultParanoia:function(a,b){0===a&&"Setting paranoia=0 will ruin your security; use it only for testing"!==b&&q("Setting paranoia=0 will ruin your security; use it only for testing");this.B=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),
g=this.s[c],h=this.isReady(),l=0;d=this.K[c];d===t&&(d=this.K[c]=this.W++);g===t&&(g=this.s[c]=0);this.s[c]=(this.s[c]+1)%this.c.length;switch(typeof a){case "number":b===t&&(b=1);this.c[g].update([d,this.C++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{"[object Array]"!==c&&(l=1);for(c=0;c<a.length&&!l;c++)"number"!==typeof a[c]&&(l=1)}if(!l){if(b===t)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,
e>>>=1;this.c[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case "string":b===t&&(b=a.length);this.c[g].update([d,this.C++,3,b,f,a.length]);this.c[g].update(a);break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.i[g]+=b;this.d+=b;h===this.m&&(this.isReady()!==this.m&&C("seeded",Math.max(this.j,this.d)),C("progress",this.getProgress()))},isReady:function(a){a=this.I[a!==t?a:this.B];return this.j&&this.j>=a?this.i[0]>this.R&&
(new Date).valueOf()>this.O?this.u|this.t:this.t:this.d>=a?this.u|this.m:this.m},getProgress:function(a){a=this.I[a?a:this.B];return this.j>=a?1:this.d>a?1:this.d/a},startCollectors:function(){this.q||(this.a={loadTimeCollector:D(this,this.aa),mouseCollector:D(this,this.ba),keyboardCollector:D(this,this.$),accelerometerCollector:D(this,this.U)},window.addEventListener?(window.addEventListener("load",this.a.loadTimeCollector,u),window.addEventListener("mousemove",this.a.mouseCollector,u),window.addEventListener("keypress",
this.a.keyboardCollector,u),window.addEventListener("devicemotion",this.a.accelerometerCollector,u)):document.attachEvent?(document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector)):q(new sjcl.exception.bug("can't attach event")),this.q=!0)},stopCollectors:function(){this.q&&(window.removeEventListener?(window.removeEventListener("load",this.a.loadTimeCollector,u),window.removeEventListener("mousemove",
this.a.mouseCollector,u),window.removeEventListener("keypress",this.a.keyboardCollector,u),window.removeEventListener("devicemotion",this.a.accelerometerCollector,u)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove",this.a.mouseCollector),document.detachEvent("keypress",this.a.keyboardCollector)),this.q=u)},addEventListener:function(a,b){this.w[a][this.V++]=b},removeEventListener:function(a,b){var c,d,e=this.w[a],f=[];for(d in e)e.hasOwnProperty(d)&&
e[d]===b&&f.push(d);for(c=0;c<f.length;c++)d=f[c],delete e[d]},$:function(){E(1)},ba:function(a){var b,c;try{b=a.x||a.clientX||a.offsetX||0,c=a.y||a.clientY||a.offsetY||0}catch(d){c=b=0}0!=b&&0!=c&&sjcl.random.addEntropy([b,c],2,"mouse");E(0)},aa:function(){E(2)},U:function(a){a=a.accelerationIncludingGravity.x||a.accelerationIncludingGravity.y||a.accelerationIncludingGravity.z;if(window.orientation){var b=window.orientation;"number"===typeof b&&sjcl.random.addEntropy(b,1,"accelerometer")}a&&sjcl.random.addEntropy(a,
2,"accelerometer");E(0)}};function C(a,b){var c,d=sjcl.random.w[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}function E(a){"undefined"!==typeof window&&window.performance&&"function"===typeof window.performance.now?sjcl.random.addEntropy(window.performance.now(),a,"loadtime"):sjcl.random.addEntropy((new Date).valueOf(),a,"loadtime")}function A(a){a.b=B(a).concat(B(a));a.A=new sjcl.cipher.aes(a.b)}
function B(a){for(var b=0;4>b&&!(a.f[b]=a.f[b]+1|0,a.f[b]);b++);return a.A.encrypt(a.f)}function D(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6);
a:try{var F,G,H,I;if(I="undefined"!==typeof module){var J;if(J=module.exports){var K;try{K=require("crypto")}catch(L){K=null}J=(G=K)&&G.randomBytes}I=J}if(I)F=G.randomBytes(128),F=new Uint32Array((new Uint8Array(F)).buffer),sjcl.random.addEntropy(F,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){H=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(H);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(H);
else break a;sjcl.random.addEntropy(H,1024,"crypto['getRandomValues']")}}catch(M){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(M))}
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},Y:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.e({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.e(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||4<
f.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.e(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return f},encrypt:function(a,
b,c,d){var e=sjcl.json,f=e.Y.apply(e,arguments);return e.encode(f)},X:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.e(e.e(e.e({},e.defaults),b),c,!0);var f,g;f=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)&&q(new sjcl.exception.invalid("json decrypt: invalid parameters"));
"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,b),a=g.key.slice(0,b.ks/32),b.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof f&&(f=sjcl.codec.utf8String.toBits(f));g=new sjcl.cipher[b.cipher](a);f=sjcl.mode[b.mode].decrypt(g,b.ct,b.iv,f,b.ts);e.e(d,b);d.key=a;return 1===c.raw?f:sjcl.codec.utf8String.fromBits(f)},decrypt:function(a,b,c,d){var e=sjcl.json;return e.X(a,e.decode(b),c,d)},encode:function(a){var b,
c="{",d="";for(b in a)if(a.hasOwnProperty(b))switch(b.match(/^[a-z0-9]+$/i)||q(new sjcl.exception.invalid("json encode: invalid property name")),c+=d+'"'+b+'":',d=",",typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:q(new sjcl.exception.bug("json encode: unsupported type"))}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");a.match(/^\{.*\}$/)||q(new sjcl.exception.invalid("json decode: this isn't json!"));
a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++)(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))||q(new sjcl.exception.invalid("json decode: this isn't json!")),b[d[2]]=d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]);return b},e:function(a,b,c){a===t&&(a={});if(b===t)return a;for(var d in b)b.hasOwnProperty(d)&&(c&&(a[d]!==t&&a[d]!==b[d])&&q(new sjcl.exception.invalid("required parameter overridden")),
a[d]=b[d]);return a},ea:function(a,b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},da:function(a,b){var c={},d;for(d=0;d<b.length;d++)a[b[d]]!==t&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.ca={};
sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.ca,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===t?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};

View File

@@ -0,0 +1,115 @@
(function() {
function word2hex(w) {
return "0x" + ((w|0)+0xF00000000000).toString(16).substr(4);
}
var b0 = sjcl.bitArray.partial(1, 0);
var b1 = sjcl.bitArray.partial(1, 1);
function concatbits(s) {
var j, b, a = [];
for (j = 0; j < s.length; ++j) {
b = (s[j] == '1' ? b1 : b0);
a = sjcl.bitArray.concat(a, [b]);
}
return a;
}
new sjcl.test.TestCase("bitArray single bits", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
this.require((b0|0) === (0x00000000|0), "bitstring '0': " + word2hex(b0));
this.require((b1|0) === (0x80000000|0), "bitstring '1': " + word2hex(b1));
cb && cb();
});
new sjcl.test.TestCase("bitArray concat small bitstrings", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
var i, kat = sjcl.test.vector.bitArray.bits, tv, a, b, bitlen, t;
for (i=0; i<kat.length; i++) {
tv = kat[i];
a = concatbits(tv[0]);
bitlen = sjcl.bitArray.bitLength(a);
t = "bitstring '" + tv[0] + "': ";
this.require(1 === a.length, t + "array length is 1: " + a.length);
this.require(bitlen === tv[0].length, t + "length " + bitlen + " matches input length " + tv[0].length);
b = sjcl.bitArray.partial(tv[0].length, tv[1]);
this.require(a[0] === b, t + "array matches shifted number: " + word2hex(a[0]) + " == " + word2hex(b));
b = 0 | (a[0] >>> (32 - tv[0].length)); // unsigned shift, convert to signed word
this.require(b === (tv[1]|0), t + "array entry shifted is number: " + word2hex(b) + " == " + word2hex(tv[1]));
}
cb && cb();
});
new sjcl.test.TestCase("bitArray concat, slicing, shifting and clamping", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
var i, j, kat = sjcl.test.vector.bitArray.slices, tv, a, a1, b, bitlen, t;
for (i=0; i<kat.length; i++) {
tv = kat[i];
a = [];
b = [];
bitlen = 0;
for (j=0; j<tv[0].length; j++) {
b[j] = concatbits(tv[0][j]);
a = sjcl.bitArray.concat(a, b[j]);
bitlen += tv[0][j].length;
}
// shift last array entry and set partial length on it
a1 = tv[1]; a1 = a1.slice(0, a1.length);
bitlen &= 31;
if (0 !== bitlen) a1[a1.length-1] = sjcl.bitArray.partial(bitlen, a1[a1.length-1]);
this.require(sjcl.bitArray.equal(a, a1), "concat: [" + a + "] == [" + a1 + "]");
t = 0;
for (j=0; j<tv[0].length; j++) {
bitlen = sjcl.bitArray.bitLength(b[j]);
this.require(bitlen === tv[0][j].length, "bitstring length");
a1 = sjcl.bitArray.bitSlice(a, t, t + bitlen);
this.require(sjcl.bitArray.equal(b[j], a1), "slice after concat: [" + b[j] + "] == [" + a1 + "]");
t += bitlen;
}
}
cb && cb();
});
new sjcl.test.TestCase("bitArray byteswap", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
var i, kat = sjcl.test.vector.bitArray.byteswap, tv, a;
for (i=0; i<kat.length; i++) {
tv = kat[i];
a = tv[1];
this.require(sjcl.bitArray.equal(tv[0], sjcl.bitArray.byteswapM(a.slice(0, a.length))));
}
cb && cb();
});
})();

View File

@@ -0,0 +1,163 @@
sjcl.test.vector.bitArray = {};
// random test cases generated with ruby String#to_i(radix) and Fixnum#to_s(radix)
// bitstrings (<= 32 bits) encoding a number
sjcl.test.vector.bitArray.bits = [
[ "00" , 0|0x0 ],
[ "01" , 0|0x1 ],
[ "10" , 0|0x2 ],
[ "11" , 0|0x3 ],
[ "000" , 0|0x0 ],
[ "011" , 0|0x3 ],
[ "101" , 0|0x5 ],
[ "110" , 0|0x6 ],
[ "0100" , 0|0x4 ],
[ "0101" , 0|0x5 ],
[ "1000" , 0|0x8 ],
[ "1100" , 0|0xc ],
[ "00101" , 0|0x5 ],
[ "01010" , 0|0xa ],
[ "10011" , 0|0x13 ],
[ "11010" , 0|0x1a ],
[ "001100" , 0|0xc ],
[ "001110" , 0|0xe ],
[ "010100" , 0|0x14 ],
[ "100111" , 0|0x27 ],
[ "0001011" , 0|0xb ],
[ "0001101" , 0|0xd ],
[ "1000100" , 0|0x44 ],
[ "1101011" , 0|0x6b ],
[ "00100001" , 0|0x21 ],
[ "00100111" , 0|0x27 ],
[ "00101000" , 0|0x28 ],
[ "10101111" , 0|0xaf ],
[ "000100000" , 0|0x20 ],
[ "100100011" , 0|0x123 ],
[ "100111001" , 0|0x139 ],
[ "111010011" , 0|0x1d3 ],
[ "0001001011" , 0|0x4b ],
[ "0001100110" , 0|0x66 ],
[ "0010110111" , 0|0xb7 ],
[ "1011101111" , 0|0x2ef ],
[ "01000010110" , 0|0x216 ],
[ "01001101000" , 0|0x268 ],
[ "10111101001" , 0|0x5e9 ],
[ "11111100000" , 0|0x7e0 ],
[ "000101010001" , 0|0x151 ],
[ "010101111111" , 0|0x57f ],
[ "101010001110" , 0|0xa8e ],
[ "110101100010" , 0|0xd62 ],
[ "0010001111010" , 0|0x47a ],
[ "1000000001100" , 0|0x100c ],
[ "1100011000000" , 0|0x18c0 ],
[ "1110011000011" , 0|0x1cc3 ],
[ "00111101111110" , 0|0xf7e ],
[ "01101011001001" , 0|0x1ac9 ],
[ "10111000111101" , 0|0x2e3d ],
[ "11101010011110" , 0|0x3a9e ],
[ "010111101010110" , 0|0x2f56 ],
[ "100011010110000" , 0|0x46b0 ],
[ "110001001100100" , 0|0x6264 ],
[ "111011000100110" , 0|0x7626 ],
[ "0100101111001100" , 0|0x4bcc ],
[ "1000000001101100" , 0|0x806c ],
[ "1001000100110110" , 0|0x9136 ],
[ "1101010000100011" , 0|0xd423 ],
[ "01001001001101110" , 0|0x926e ],
[ "01111001111000010" , 0|0xf3c2 ],
[ "10011011011000011" , 0|0x136c3 ],
[ "10101011001110000" , 0|0x15670 ],
[ "010000000101000110" , 0|0x10146 ],
[ "011000100101110001" , 0|0x18971 ],
[ "101100100110110111" , 0|0x2c9b7 ],
[ "101110100100101111" , 0|0x2e92f ],
[ "0010101100101000000" , 0|0x15940 ],
[ "1011010010000101010" , 0|0x5a42a ],
[ "1011100111011011000" , 0|0x5ced8 ],
[ "1111011110011111110" , 0|0x7bcfe ],
[ "00101000011011111111" , 0|0x286ff ],
[ "01111001100011000100" , 0|0x798c4 ],
[ "11111001001110101011" , 0|0xf93ab ],
[ "11111001111001101001" , 0|0xf9e69 ],
[ "000110100000110010101" , 0|0x34195 ],
[ "011110000101101101111" , 0|0xf0b6f ],
[ "101111010011001100110" , 0|0x17a666 ],
[ "111101001011110010001" , 0|0x1e9791 ],
[ "1001111100011011100001" , 0|0x27c6e1 ],
[ "1011110101000101010110" , 0|0x2f5156 ],
[ "1100000100011110001011" , 0|0x30478b ],
[ "1100001010010110111111" , 0|0x30a5bf ],
[ "01001111001101000111101" , 0|0x279a3d ],
[ "10110011101111110000001" , 0|0x59df81 ],
[ "11000001101100110100011" , 0|0x60d9a3 ],
[ "11011000010110110010110" , 0|0x6c2d96 ],
[ "010011110100100110010100" , 0|0x4f4994 ],
[ "011101110001100111111110" , 0|0x7719fe ],
[ "011110001010011011100011" , 0|0x78a6e3 ],
[ "111100010001011101111110" , 0|0xf1177e ],
[ "0010110000010110001000010" , 0|0x582c42 ],
[ "0100100011001001101110000" , 0|0x919370 ],
[ "1000111001010110111010110" , 0|0x11cadd6 ],
[ "1111001100101000010010101" , 0|0x1e65095 ],
[ "00110010011100010101111111" , 0|0xc9c57f ],
[ "00111101011011010100111110" , 0|0xf5b53e ],
[ "01100000111011111010011100" , 0|0x183be9c ],
[ "11010001100110101111010111" , 0|0x3466bd7 ],
[ "010000001111100110000110011" , 0|0x207cc33 ],
[ "011010010000110000101011111" , 0|0x348615f ],
[ "011010110001110110001110010" , 0|0x358ec72 ],
[ "110110100001001001001110000" , 0|0x6d09270 ],
[ "0101000000000001000011100101" , 0|0x50010e5 ],
[ "0110011001001100011111111100" , 0|0x664c7fc ],
[ "1001011010000001100110111101" , 0|0x96819bd ],
[ "1011011101000000111000000010" , 0|0xb740e02 ],
[ "00100111111011011111010101101" , 0|0x4fdbead ],
[ "00110000011001110110101110010" , 0|0x60ced72 ],
[ "10111110111000010010010111101" , 0|0x17dc24bd ],
[ "11010001000001110010101000010" , 0|0x1a20e542 ],
[ "001001010001010111111101010111" , 0|0x9457f57 ],
[ "100110011101100000110111111010" , 0|0x26760dfa ],
[ "100111000100011001010011111011" , 0|0x271194fb ],
[ "111011110110101110110111010101" , 0|0x3bdaedd5 ],
[ "0111100111010100101010000111100" , 0|0x3cea543c ],
[ "1000010011010100111001110000100" , 0|0x426a7384 ],
[ "1001110000000100001011010001010" , 0|0x4e02168a ],
[ "1101000000000110110010011010100" , 0|0x680364d4 ],
[ "00000011100100001011101011100111", 0|0x390bae7 ],
[ "10011110110111111000010010010011", 0|0x9edf8493 ],
[ "11000101100000110001011010111100", 0|0xc58316bc ],
[ "11111010101110011001010001011000", 0|0xfab99458 ]
];
// concat some bitstrings into an array (the last array entry here is not "high" shifted yet)
sjcl.test.vector.bitArray.slices = [
// lengths: 17, 16, 15, 14, 13
[ ["00100010111010110", "0010000111100001", "101111111100010", "10111011110000", "1100100100001" ], [0|0x22eb10f0, 0|0xdfe2bbc3, 0|0x121] ],
[ ["00001010100110010", "0000100011110001", "111110101100100", "10110111000101", "0000100111011" ], [0|0x0a990478, 0|0xfd64b714, 0|0x13b] ],
[ ["10110101101100001", "0000110110010100", "110100011010100", "01011010010111", "0101101011011" ], [0|0xb5b086ca, 0|0x68d45a5d, 0|0x35b] ],
[ ["01010010001010110", "1110010000100111", "001010110101110", "10110111101000", "0100000010100" ], [0|0x522b7213, 0|0x95aeb7a1, 0|0x014] ],
[ ["10011001001100110", "0100100011100010", "000110011001001", "00101101101000", "0001111101100" ], [0|0x99332471, 0|0x0cc92da0, 0|0x3ec] ],
[ ["01111100000110000", "1000101001000010", "110101001111111", "01001010110110", "0111100000001" ], [0|0x7c184521, 0|0x6a7f4ad9, 0|0x701] ],
[ ["00110011010100110", "0100101101110000", "110101110100100", "00111010010110", "0110011011100" ], [0|0x335325b8, 0|0x6ba43a59, 0|0x4dc] ],
[ ["11111110011100100", "1111101001010011", "001000100110010", "01101101100001", "0001011101011" ], [0|0xfe727d29, 0|0x91326d84, 0|0x2eb] ],
[ ["00100011001001000", "1001010100000100", "010111001100111", "11011111000000", "0100111101110" ], [0|0x23244a82, 0|0x2e67df01, 0|0x1ee] ],
[ ["10111111100000010", "0111110011010001", "001000101001110", "11101010011001", "0101111101110" ], [0|0xbf813e68, 0|0x914eea65, 0|0x3ee] ],
// lengths: 17, 15, 32, 10, 20
[ ["00001111000110001", "110100011111001", "10010110001110010011011000011111", "1001101101", "01110010001111001110" ], [0|0x0f18e8f9, 0|0x9639361f, 0|0x26d723ce] ],
[ ["11011111010101100", "101010100101100", "11101101011001011011010111101011", "0010110010", "11110011110110000010" ], [0|0xdf56552c, 0|0xed65b5eb, 0|0x0b2f3d82] ],
[ ["10100100001000101", "100111001100011", "00111100101000110010000011101001", "1001110001", "11000000001111111110" ], [0|0xa422ce63, 0|0x3ca320e9, 0|0x271c03fe] ],
[ ["01110001111111010", "000101001101110", "00001100011010100100001011111001", "1011111010", "00011001110111111001" ], [0|0x71fd0a6e, 0|0x0c6a42f9, 0|0x2fa19df9] ],
[ ["01001100010011001", "000111011000100", "10100000110101110110011110001100", "0011000001", "11001101010101011100" ], [0|0x4c4c8ec4, 0|0xa0d7678c, 0|0x0c1cd55c] ],
[ ["01010010100110110", "110111110001000", "10011100001100010100000110000000", "1100111101", "00010001110100111101" ], [0|0x529b6f88, 0|0x9c314180, 0|0x33d11d3d] ],
[ ["11010100011101101", "110101011011010", "01000110000011001111101110100001", "0001001110", "01001100010010001010" ], [0|0xd476eada, 0|0x460cfba1, 0|0x04e4c48a] ],
[ ["11000001010001101", "001111011110111", "10000010100111010111011000100001", "0011000011", "11011110110000101000" ], [0|0xc1469ef7, 0|0x829d7621, 0|0x0c3dec28] ],
[ ["11000101100010100", "000110111111110", "11111011110010001100001100010000", "0000101010", "10000011010011100011" ], [0|0xc58a0dfe, 0|0xfbc8c310, 0|0x02a834e3] ],
[ ["11111111110100101", "110101000011001", "10101010010100111010100010110100", "0010011000", "10100110001000000010" ], [0|0xffd2ea19, 0|0xaa53a8b4, 0|0x098a6202] ]
];
sjcl.test.vector.bitArray.byteswap = [
[ [ 0xdab1a44e, 0x73fdc3de, 0xed6e6f00, 0x5d221b85, 0xadb987a4, 0xc20eda76, 0xa0d40d0c, 0xd1da9657, 0xd85eebf0, 0xd3f9c06d, 0xca1e39b0, 0xccd7eaeb, 0xb1dd6bd0, 0x7315a9e6, 0x77cfcac4, 0xf7914c19, 0x1bc15354, 0x935afabc, 0x3ee9d375, 0xd18a095f]
, [ 0x4ea4b1da, 0xdec3fd73, 0x006f6eed, 0x851b225d, 0xa487b9ad, 0x76da0ec2, 0x0c0dd4a0, 0x5796dad1, 0xf0eb5ed8, 0x6dc0f9d3, 0xb0391eca, 0xebead7cc, 0xd06bddb1, 0xe6a91573, 0xc4cacf77, 0x194c91f7, 0x5453c11b, 0xbcfa5a93, 0x75d3e93e, 0x5f098ad1]
]
];

View File

@@ -7,7 +7,7 @@ new sjcl.test.TestCase("ECC convenience test", function (cb) {
try {
var keys = sjcl.ecc.elGamal.generateKeys(192,0);
var ciphertext = sjcl.encrypt(keys.pub, "hello world");
var plaintext = sjcl.decrypt(keys.sec, ciphertext);

View File

@@ -5,11 +5,23 @@ new sjcl.test.TestCase("HMAC official test vectors", function (cb) {
return;
}
var i, kat = sjcl.test.vector.hmac, tv, h=sjcl.codec.hex, out;
var i, kat = sjcl.test.vector.hmac, tv, h=sjcl.codec.hex, out, data, mac;
for (i=0; i<kat.length; i++) {
tv = kat[i];
out = h.fromBits((new sjcl.misc.hmac(h.toBits(tv.key))).mac(h.toBits(tv.data)));
data = h.toBits(tv.data);
mac = new sjcl.misc.hmac(h.toBits(tv.key));
out = h.fromBits(mac.mac(data));
this.require (out.substr(0,tv.mac.length) == tv.mac, "hmac #"+i);
out = h.fromBits(mac.mac(data));
this.require (out.substr(0,tv.mac.length) == tv.mac, "hmac reset #"+i);
mac.update(sjcl.bitArray.bitSlice(data, 0, sjcl.bitArray.bitLength(data)/2));
mac.update(sjcl.bitArray.bitSlice(data, sjcl.bitArray.bitLength(data)/2));
out = h.fromBits(mac.digest());
this.require (out.substr(0,tv.mac.length) == tv.mac, "hmac reset #"+i);
}
cb && cb();
});

View File

@@ -1,4 +1,5 @@
/* Official HMAC test vectors. */
//Nilos: http://tools.ietf.org/html/draft-nystrom-smime-hmac-sha-02 for
sjcl.test.vector.hmac = [
{ key: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
data: "4869205468657265",

View File

@@ -0,0 +1,126 @@
/*
* Asserts that an object can be encoded to an expected string.
*/
new sjcl.test.TestCase("JSON Encode Test", function (cb) {
if(!sjcl.json) {
this.unimplemented();
cb && cb();
return;
}
//Build up a standard object for encoding, this includes a nice wide variety of properties.
var obj = new Object();
obj.int = 4;
obj.nint = -5;
obj.str = 'string';
obj.iv = [ -95577995, -949876189, 1443400017, 697058741 ];
obj.truth = true;
obj.lie = false;
try {
var str = sjcl.json.encode(obj);
this.require(!(!str)); //Check for non-'falsey'
}
catch (e) {
//The standard object should encode just fine, so this is out of place. Fail.
this.fail(e);
}
cb && cb();
});
/*
* Asserts that a JSON string can be decoded to an expected object.
*/
new sjcl.test.TestCase("JSON Decode Test", function (cb) {
if(!sjcl.json) {
this.unimplemented();
cb && cb();
return;
}
var str = ''; var i;
str = '{"int":4,"nint":-5,"str":"string","iv":"/////wAAAAAAAAABAAAAAg==","truth":true,"lie":false}';
try {
var obj = sjcl.json.decode(str);
this.require(obj.int === 4);
this.require(obj.nint === -5);
this.require(obj.str === 'string');
this.require(obj.truth === true);
this.require(obj.lie === false);
for(i in obj.iv) {
this.require(obj.iv[i] == (i-1)); //Array in iv is [-1,0,1,2]
}
} catch (e) { this.fail(e); }
str = '{ "int" : 4, "nint" : -5,"str":"string", "iv": "/////wAAAAAAAAABAAAAAg==","truth": true,"lie": false }';
try {
var obj = sjcl.json.decode(str);
this.require(obj.int === 4);
this.require(obj.nint === -5);
this.require(obj.str === 'string');
this.require(obj.truth === true);
this.require(obj.lie === false);
for(i in obj.iv) {
this.require(obj.iv[i] == (i-1)); //Array in iv is [-1,0,1,2]
}
} catch (e) { this.fail(e); }
//Tests passed, return.
cb && cb();
});
/*
* Asserts that an Object can be Encoded to a string that can be decoded to an equivalent object
* as well as the converse.
*/
new sjcl.test.TestCase("JSON Commutative Test", function (cb) {
if(!sjcl.json) {
this.unimplemented();
cb && cb();
return;
}
var obj1 = new Object();
obj1.int = 4;
obj1.nint = -5;
obj1.str = 'string';
obj1.iv = [ -95577995, -949876189, 1443400017, 697058741 ];
obj1.truth = true;
obj1.lie = false;
var str1 = '';
var str2 = '';
var obj2;
try {
str1 = sjcl.json.encode(obj1);
obj2 = sjcl.json.decode(str1);
str2 = sjcl.json.encode(obj2);
}
catch (e) {
this.fail(e);
}
try {
this.require(str1 === str2);
this.require(obj1.int == obj2.int);
this.require(obj1.str == obj2.str);
this.require(obj1.lie == obj2.lie);
this.require(obj1.nint == obj2.nint);
this.require(obj1.truth == obj2.truth);
var i;
for(i in obj1.iv)
{
this.require(obj1.iv[i] == obj2.iv[i]);
}
}
catch (e) {
this.fail(e);
}
//Tests passed.
cb && cb();
});

View File

@@ -0,0 +1,16 @@
var fs = require('fs');
var vm = require('vm');
var load = function(path) {
vm.runInThisContext(fs.readFileSync(path));
};
// Assume we're run using `make test`.
// That means argv[0] is `node` and argv[1] is this file.
process.argv.slice(2).map(load);
sjcl.test.run(undefined, function(){
if(!browserUtil.allPassed) {
process.exit(1);
}
});

View File

@@ -1,5 +1,5 @@
var assert = require('assert');
var Account = require('../src/js/ripple/account').Account;
var Account = require('ripple-lib').Account;
describe('Account', function(){

View File

@@ -1,9 +1,12 @@
var assert = require('assert');
var utils = require('./testutils');
var BigInteger = require('../src/js/jsbn/jsbn').BigInteger;
var Amount = utils.load_module('amount').Amount;
var UInt160 = utils.load_module('uint160').UInt160;
var config = utils.get_config();
var assert = require('assert');
var BigInteger = require('ripple-lib').jsbn.BigInteger;
var Amount = require('ripple-lib').Amount;
var UInt160 = require('ripple-lib').UInt160;
var load_config = require('ripple-lib').config.load;
var config = require('./config-example');
load_config(config);
describe('Amount', function() {
describe('Negatives', function() {

View File

@@ -1,7 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var Seed = utils.load_module('seed').Seed;
var config = require('./testutils').get_config();
var Seed = require('ripple-lib').Seed;
describe('Base58', function() {
describe('Seed', function() {

View File

@@ -1,7 +1,6 @@
var assert = require('assert');
var utils = require('./testutils');
var currency = utils.load_module('currency').Currency;
var timeUtil = utils.load_module('utils').time;
var currency = require('ripple-lib').Currency;
var timeUtil = require('ripple-lib').utils.time;
describe('Currency', function() {
describe('json_rewrite', function() {

View File

@@ -0,0 +1,303 @@
{
"AffectedNodes": [
{
"DeletedNode": {
"FinalFields": {
"Account": "rNGySgyyEdRJ6MKmzsZwyhhVeFKdENRGQ6",
"BookDirectory": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FC79E1600",
"BookNode": "0000000000000000",
"Flags": 131072,
"OwnerNode": "0000000000000031",
"PreviousTxnID": "83CA9AB231B0B4DA8ACF6305A6D7B00AB83404A1FDB8F8BCF7108EB87E0A8196",
"PreviousTxnLgrSeq": 10555009,
"Sequence": 12370,
"TakerGets": "44978350398",
"TakerPays": {
"currency": "USD",
"issuer": "rQ3qJZwtzi4Zo3nWbmX9gwCke6S8jmHRCn",
"value": "1056.990784602728"
}
},
"LedgerEntryType": "Offer",
"LedgerIndex": "0496450E3F46368FB011B8B524605A906C0854441D30420457A81EA89BE649BE"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "0"
},
"Flags": 1114112,
"HighLimit": {
"currency": "USD",
"issuer": "rabVnHuo1eq747GbnLDAJfE9GpsGwcL9Hy",
"value": "0"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rGa3Tb6vaMVU5RQMjxk4nsMGSArbu8epGG",
"value": "5"
},
"LowNode": "0000000000000000"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "2DECFAC23B77D5AEA6116C15F5C6D4669EBAEE9E7EE050A40FE2B1E47B6A9419",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "3"
}
},
"PreviousTxnID": "6F35AD78AA196389D15F4BAF054122070506633C1506EF16A48877E2593CCE2D",
"PreviousTxnLgrSeq": 10555014
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "1228.52678703286"
},
"Flags": 1114112,
"HighLimit": {
"currency": "USD",
"issuer": "rabVnHuo1eq747GbnLDAJfE9GpsGwcL9Hy",
"value": "0"
},
"HighNode": "0000000000000076",
"LowLimit": {
"currency": "USD",
"issuer": "rPofp4ruTavGuFnb9AYN2vRPBFxHB7RRsH",
"value": "50000"
},
"LowNode": "0000000000000000"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "4AB2FFDEE65E025AAB54305A161C80D32082574BB0502311F29227089C696388",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "1225.52678703286"
}
},
"PreviousTxnID": "65C5A92212AA82A89C3824F6F071FE49C95C45DE9113EB51763A217DBACB5B4C",
"PreviousTxnLgrSeq": 10554856
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"Owner": "rP5VvumKn5qqn4RYTMGipmPE9965ARVQNT",
"RootIndex": "65E53030C59CD541BB6A8B631F13FA9BBF6F6E28B63D41AA363F23313B34B094"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "65E53030C59CD541BB6A8B631F13FA9BBF6F6E28B63D41AA363F23313B34B094"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rNGySgyyEdRJ6MKmzsZwyhhVeFKdENRGQ6",
"Balance": "335297994919",
"Flags": 0,
"OwnerCount": 12,
"Sequence": 12379
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "6ACA1AAB7CD8877EA63E0C282A37FC212AD24640743F019AC2DD8674E003B741",
"PreviousFields": {
"OwnerCount": 13
},
"PreviousTxnID": "83CA9AB231B0B4DA8ACF6305A6D7B00AB83404A1FDB8F8BCF7108EB87E0A8196",
"PreviousTxnLgrSeq": 10555009
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-15.68270575272517"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "rGa3Tb6vaMVU5RQMjxk4nsMGSArbu8epGG",
"value": "5000"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rQ3qJZwtzi4Zo3nWbmX9gwCke6S8jmHRCn",
"value": "0"
},
"LowNode": "000000000000004A"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "826CF5BFD28F3934B518D0BDF3231259CBD3FD0946E3C3CA0C97D2C75D2D1A09",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-12.68270575272517"
}
},
"PreviousTxnID": "6F35AD78AA196389D15F4BAF054122070506633C1506EF16A48877E2593CCE2D",
"PreviousTxnLgrSeq": 10555014
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"IndexPrevious": "000000000000002C",
"Owner": "rNGySgyyEdRJ6MKmzsZwyhhVeFKdENRGQ6",
"RootIndex": "A93C6B313E260AC7C7734DF44F4461075E2C937C936C2B81DA2C9F69D4A0B0F2"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "8D8FB3359BBA810ED5C5894088F2415E322811181ADCC5BB087E829207DFBBEB"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rP5VvumKn5qqn4RYTMGipmPE9965ARVQNT",
"Balance": "25116082528",
"Flags": 0,
"OwnerCount": 5,
"Sequence": 2197
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "8F55B7E947241AD38FD6D47374BF8E7CA7DF177C8B79712B4CAC5E91FD5023FF",
"PreviousFields": {
"OwnerCount": 6
},
"PreviousTxnID": "0327747C391C678CA2AC46F422E1E8D307A41E9C0ED5DB2677B51CEBE41BD243",
"PreviousTxnLgrSeq": 10554892
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rauAjp7gUp8xqHnPFDSo72Nc6aMx2k9yDk",
"Balance": "233929959",
"Flags": 0,
"OwnerCount": 14,
"Sequence": 31927
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "A27BB98F7C9D32F404B364622645F80480F87C8A91BB13CA9F6E569144C2A5A8",
"PreviousFields": {
"Balance": "233941959",
"Sequence": 31926
},
"PreviousTxnID": "65C5A92212AA82A89C3824F6F071FE49C95C45DE9113EB51763A217DBACB5B4C",
"PreviousTxnLgrSeq": 10554856
}
},
{
"DeletedNode": {
"FinalFields": {
"ExchangeRate": "4D08594FC79E1600",
"Flags": 0,
"RootIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FC79E1600",
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
"TakerPaysCurrency": "0000000000000000000000005553440000000000",
"TakerPaysIssuer": "0520B3C85F482532A9578DBB3950B85CA03594D1"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FC79E1600"
}
},
{
"DeletedNode": {
"FinalFields": {
"ExchangeRate": "4D08594FE7353EDE",
"Flags": 0,
"RootIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FE7353EDE",
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
"TakerPaysCurrency": "0000000000000000000000005553440000000000",
"TakerPaysIssuer": "0520B3C85F482532A9578DBB3950B85CA03594D1"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FE7353EDE"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-2818.268620725051"
},
"Flags": 2228224,
"HighLimit": {
"currency": "USD",
"issuer": "rauAjp7gUp8xqHnPFDSo72Nc6aMx2k9yDk",
"value": "50000"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rQ3qJZwtzi4Zo3nWbmX9gwCke6S8jmHRCn",
"value": "0"
},
"LowNode": "00000000000001B0"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "FAD28E839AF29C6CCB8DA6DC71510A5BF8A9C34062C128071C7D22E2469B8288",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-2821.274620725051"
}
},
"PreviousTxnID": "65C5A92212AA82A89C3824F6F071FE49C95C45DE9113EB51763A217DBACB5B4C",
"PreviousTxnLgrSeq": 10554856
}
},
{
"DeletedNode": {
"FinalFields": {
"Account": "rP5VvumKn5qqn4RYTMGipmPE9965ARVQNT",
"BookDirectory": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FE7353EDE",
"BookNode": "0000000000000000",
"Flags": 0,
"OwnerNode": "0000000000000000",
"PreviousTxnID": "0327747C391C678CA2AC46F422E1E8D307A41E9C0ED5DB2677B51CEBE41BD243",
"PreviousTxnLgrSeq": 10554892,
"Sequence": 2196,
"TakerGets": "4255320000",
"TakerPays": {
"currency": "USD",
"issuer": "rQ3qJZwtzi4Zo3nWbmX9gwCke6S8jmHRCn",
"value": "100"
}
},
"LedgerEntryType": "Offer",
"LedgerIndex": "FB2E442ED1A5BCA1E237BA133807AE17AED1A7E4B9F404906308ADB01A57609D"
}
}
],
"DeliveredAmount": {
"currency": "USD",
"issuer": "rabVnHuo1eq747GbnLDAJfE9GpsGwcL9Hy",
"value": "3"
},
"TransactionIndex": 5,
"TransactionResult": "tesSUCCESS"
}

View File

@@ -1,7 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var Seed = utils.load_module('seed').Seed;
var config = require('./testutils').get_config();
var Seed = require('ripple-lib').Seed;
describe('KeyPair', function() {
it('can generate an address', function () {

View File

@@ -1,9 +1,7 @@
var assert = require('assert');
var fs = require('fs');
var utils = require('./testutils');
var Ledger = utils.load_module('ledger').Ledger;
var config = require('./testutils').get_config();
var Ledger = require('ripple-lib').Ledger;
/**
* @param ledger_index {Number}

View File

@@ -1,8 +1,8 @@
var assert = require('assert');
var sjcl = require('../build/sjcl');
var Message = require('../src/js/ripple/message').Message;
var Seed = require('../src/js/ripple/seed').Seed;
var Remote = require('../src/js/ripple/remote').Remote;
var sjcl = require('ripple-lib').sjcl;
var Message = require('ripple-lib').Message;
var Seed = require('ripple-lib').Seed;
var Remote = require('ripple-lib').Remote;
describe('Message', function(){

1
test/node_modules/ripple-lib generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../../src/js/ripple/

View File

@@ -1,9 +1,8 @@
var assert = require('assert');
var utils = require('./testutils');
var Remote = utils.load_module('remote').Remote;
var Currency = utils.load_module('currency').Currency;
var Amount = utils.load_module('amount').Amount;
var Meta = utils.load_module('meta').Meta;
var Remote = require('ripple-lib').Remote;
var Currency = require('ripple-lib').Currency;
var Amount = require('ripple-lib').Amount;
var Meta = require('ripple-lib').Meta;
describe('OrderBook', function() {
it('toJSON', function() {
@@ -338,6 +337,7 @@ describe('OrderBook', function() {
});
var offer = {
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
TakerGets: {
value: '100',
currency: 'BTC',
@@ -349,6 +349,7 @@ describe('OrderBook', function() {
book.setFundedAmount(offer, '100.1234');
var expected = {
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: true,
@@ -368,6 +369,7 @@ describe('OrderBook', function() {
});
var offer = {
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
TakerGets: {
value: '100',
currency: 'BTC',
@@ -379,6 +381,7 @@ describe('OrderBook', function() {
book.setFundedAmount(offer, '99');
var expected = {
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: false,
@@ -398,6 +401,7 @@ describe('OrderBook', function() {
});
var offer = {
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
TakerGets: '100',
TakerPays: {
value: '123.456',
@@ -409,6 +413,7 @@ describe('OrderBook', function() {
book.setFundedAmount(offer, '100.1');
var expected = {
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: true,
@@ -428,6 +433,7 @@ describe('OrderBook', function() {
});
var offer = {
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
TakerGets: '100',
TakerPays: {
value: '123.456',
@@ -439,6 +445,7 @@ describe('OrderBook', function() {
book.setFundedAmount(offer, '99');
var expected = {
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: false,
@@ -458,6 +465,7 @@ describe('OrderBook', function() {
});
var offer = {
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
TakerGets: {
value: '100',
currency: 'BTC',
@@ -469,6 +477,7 @@ describe('OrderBook', function() {
book.setFundedAmount(offer, '0');
assert.deepEqual(offer, {
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: false,
@@ -1451,6 +1460,56 @@ describe('OrderBook', function() {
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
quality: '498.6116758238228'
},
{
Account: 'rwBG69mujDoD5yQfL3Sf7Yuh7rUNYdxe9Y',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29356,
TakerGets: {
currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '0.5'
},
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
quality: '498.6116758238228'
},
{
Account: 'rwBG69mujDoD5yQfL3Sf7Yuh7rUNYdxe9Y',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131078,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29354,
TakerGets: {
currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '0.5'
},
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
quality: '498.6116758238228'
}
]
};
@@ -1534,10 +1593,258 @@ describe('OrderBook', function() {
is_fully_funded: true,
taker_gets_funded: '0.2',
taker_pays_funded: '99.72233516476456'
}
},
{ Account: 'rwBG69mujDoD5yQfL3Sf7Yuh7rUNYdxe9Y',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
Sequence: 29356,
TakerGets: { currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '0.5'
},
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
is_fully_funded: false,
taker_gets_funded: '0.9484660776278363',
taker_pays_funded: '94.58325208561269' },
{ Account: 'rwBG69mujDoD5yQfL3Sf7Yuh7rUNYdxe9Y',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131078,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
Sequence: 29354,
TakerGets: {
currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '0.5'
},
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
is_fully_funded: false,
taker_gets_funded: '0.9484660776278363',
taker_pays_funded: '94.58325208561269'
}
]
book.on('model', function(model) {
assert.deepEqual(model, expected);
assert.strictEqual(book._synchronized, true);
done();
});
});
it('Request offers -- native currency', function(done) {
var remote = new Remote();
var offers = {
offers: [
{
Account: 'rGCHV41NxoK7wHQJhmao2RqjWZvBrTUhW1',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711A3A4254F5000',
BookNode: '0000000000000000',
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000000',
Sequence: 195,
TakerGets: '1000',
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '56.06639660617357'
},
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
owner_funds: '600'
},
{
Account: 'raudnGKfTK23YKfnS7ixejHrqGERTYNFXk',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
Sequence: 29354,
TakerGets: '2000',
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '4000',
},
{
Account: 'rwBG69mujDoD5yQfL3Sf7Yuh7rUNYdxe9Y',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
Sequence: 29356,
TakerGets: '2000',
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '3900',
},
{
Account: 'rwBG69mujDoD5yQfL3Sf7Yuh7rUNYdxe9Y',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131078,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29354,
TakerGets: '2000',
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
quality: '498.6116758238228'
}
]
};
var expected = [
{
Account: 'rGCHV41NxoK7wHQJhmao2RqjWZvBrTUhW1',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711A3A4254F5000',
BookNode: '0000000000000000',
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000000',
Sequence: 195,
TakerGets: '1000',
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '56.06639660617357'
},
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
owner_funds: '600',
is_fully_funded: false,
taker_gets_funded: '600',
taker_pays_funded: '33.63983796370414'
},
{
Account: 'raudnGKfTK23YKfnS7ixejHrqGERTYNFXk',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
Sequence: 29354,
TakerGets: '2000',
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '4000',
is_fully_funded: true,
taker_gets_funded: '2000',
taker_pays_funded: '99.72233516476456'
},
{
Account: 'rwBG69mujDoD5yQfL3Sf7Yuh7rUNYdxe9Y',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
Sequence: 29356,
TakerGets: '2000',
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '3900',
is_fully_funded: false,
taker_gets_funded: '3900',
taker_pays_funded: '97.22927678564545'
},
{
Account: 'rwBG69mujDoD5yQfL3Sf7Yuh7rUNYdxe9Y',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131078,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
Sequence: 29354,
TakerGets: '2000',
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
is_fully_funded: false,
taker_gets_funded: '3900',
taker_pays_funded: '97.22927678564545'
}
]
remote.request = function(request) {
switch (request.message.command) {
case 'book_offers':
assert.deepEqual(request.message, {
command: 'book_offers',
id: void(0),
taker_gets: {
currency: '0000000000000000000000000000000000000000',
},
taker_pays: {
currency: '0000000000000000000000005553440000000000',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
},
taker: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
});
setImmediate(function() {
request.emit('success', offers);
});
break;
}
};
var book = remote.createOrderBook({
currency_gets: 'XRP',
currency_pays: 'USD',
issuer_pays: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
});
book.on('model', function(model) {
assert.deepEqual(model, expected);
assert.strictEqual(book._synchronized, true);

View File

@@ -1,9 +1,9 @@
var assert = require('assert');
var utils = require('./testutils');
var Remote = utils.load_module('remote').Remote;
var Server = utils.load_module('server').Server;
var Request = utils.load_module('request').Request;
var Remote = require('ripple-lib').Remote;
var Server = require('ripple-lib').Server;
var Request = require('ripple-lib').Request;
var UInt160 = require('ripple-lib').UInt160;
var Currency = require('ripple-lib').Currency;
var options, remote, callback, database, tx;
@@ -335,7 +335,7 @@ describe('Remote', function () {
});
it('requestAccountLines, account and callback', function() {
var callback = function() {};
function callback() {}
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
@@ -354,7 +354,7 @@ describe('Remote', function () {
});
it('requestAccountLines, ledger, peer', function() {
var callback = function() {};
function callback() {}
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
@@ -379,7 +379,7 @@ describe('Remote', function () {
});
it('requestAccountLines, ledger, peer, limit and marker', function() {
var callback = function() {};
function callback() {}
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
@@ -408,7 +408,7 @@ describe('Remote', function () {
});
it('requestAccountOffers, ledger, peer, limit and marker', function() {
var callback = function() {};
function callback() {}
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
@@ -436,6 +436,81 @@ describe('Remote', function () {
assert(request.requested);
});
it('requestBookOffers, ledger', function() {
function callback() {}
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
var request = remote.requestBookOffers(
{
gets: {
currency: 'USD',
issuer: ADDRESS
},
pays: {
currency: 'XRP'
},
ledger: LEDGER_HASH
},
callback
);
assert.deepEqual(request.message, {
command: 'book_offers',
id: undefined,
taker_gets: {
currency: Currency.from_human('USD').to_hex(),
issuer: ADDRESS
},
taker_pays: {
currency: '0000000000000000000000000000000000000000'
},
taker: UInt160.ACCOUNT_ONE,
ledger_hash: LEDGER_HASH
});
assert(request.requested);
});
it('requestBookOffers, ledger and limit', function() {
function callback() {}
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
var request = remote.requestBookOffers(
{
gets: {
currency: 'USD',
issuer: ADDRESS
},
pays: {
currency: 'XRP'
},
ledger: LEDGER_HASH,
limit: 10
},
callback
);
assert.deepEqual(request.message, {
command: 'book_offers',
id: undefined,
taker_gets: {
currency: Currency.from_human('USD').to_hex(),
issuer: ADDRESS
},
taker_pays: {
currency: Currency.from_human('XRP').to_hex()
},
taker: UInt160.ACCOUNT_ONE,
ledger_hash: LEDGER_HASH,
limit: 10
});
assert(request.requested);
});
it('create remote and get pending transactions', function() {
before(function() {
tx = [{

View File

@@ -1,9 +1,8 @@
var assert = require('assert');
var utils = require('./testutils');
var Request = utils.load_module('request').Request;
var Remote = utils.load_module('remote').Remote;
var Server = utils.load_module('server').Server;
var Currency = utils.load_module('currency').Currency;
var Request = require('ripple-lib').Request;
var Remote = require('ripple-lib').Remote;
var Server = require('ripple-lib').Server;
var Currency = require('ripple-lib').Currency;
function makeServer(url) {
var server = new Server(new process.EventEmitter(), url);

View File

@@ -1,7 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var Seed = utils.load_module('seed').Seed;
var config = require('./testutils').get_config();
var Seed = require('ripple-lib').Seed;
describe('Seed', function() {
it('can generate many addresses', function () {

View File

@@ -1,8 +1,22 @@
var utils = require('./testutils');
var assert = require('assert');
var SerializedObject = utils.load_module('serializedobject').SerializedObject;
var SerializedObject = require('ripple-lib').SerializedObject;
var sjcl = require('ripple-lib').sjcl;
// Shortcuts
var hex = sjcl.codec.hex;
var bytes = sjcl.codec.bytes;
var utf8 = sjcl.codec.utf8String;
describe('Serialized object', function() {
function convertStringToHex(string) {
return hex.fromBits(utf8.toBits(string)).toUpperCase();
}
function convertHexToString(hexString) {
return utf8.fromBits(hex.toBits(hexString));
}
describe('#from_json(v).to_json() == v', function(){
it('outputs same as passed to from_json', function() {
var input_json = {
@@ -35,6 +49,7 @@ describe('Serialized object', function() {
assert.deepEqual(input_json, output_json);
});
});
describe('#from_json', function() {
it('understands TransactionType as a Number', function() {
var input_json = {
@@ -52,6 +67,7 @@ describe('Serialized object', function() {
assert.equal(0, input_json.TransactionType);
assert.equal("Payment", output_json.TransactionType);
});
it('understands LedgerEntryType as a Number', function() {
var input_json = {
// no, non required fields
@@ -65,6 +81,7 @@ describe('Serialized object', function() {
assert.equal(100, input_json.LedgerEntryType);
assert.equal("DirectoryNode", output_json.LedgerEntryType);
});
describe('Format validation', function() {
// Peercover actually had a problem submitting transactions without a `Fee`
// and rippled was only informing of "transaction is invalid"
@@ -80,15 +97,231 @@ describe('Serialized object', function() {
};
assert.throws (
function() {
var output_json = SerializedObject.from_json(input_json);
SerializedObject.from_json(input_json);
},
/Payment is missing fields: \["Fee"\]/
);
});
});
})
describe('Memos', function() {
var input_json;
beforeEach(function() {
input_json = {
"Flags": 2147483648,
"TransactionType": "Payment",
"Account": "rhXzSyt1q9J8uiFXpK3qSugAAPJKXLtnrF",
"Amount": "1",
"Destination": "radqi6ppXFxVhJdjzaATRBxdrPcVTf1Ung",
"Sequence": 281,
"SigningPubKey": "03D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C51",
"Fee": "12000",
"LastLedgerSequence": 10074214,
"TxnSignature": "304402201180636F2CE215CE97A29CD302618FAE60D63EBFC8903DE17A356E857A449C430220290F4A54F9DE4AC79034C8BEA5F1F8757F7505F1A6FF04D2E19B6D62E867256B"
};
});
it('should serialize and parse - full memo, all strings text/plain ', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "text",
"MemoData": "some data"
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'text';
input_json.Memos[0].Memo.parsed_memo_data = 'some data';
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('text');
input_json.Memos[0].Memo.MemoData = convertStringToHex('some data');
assert.deepEqual(so, input_json);
});
it('should serialize and parse - full memo, all strings, invalid MemoFormat', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "application/json",
"MemoData": "some data"
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'application/json';
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('application/json');
input_json.Memos[0].Memo.MemoData = convertStringToHex('some data');
assert.deepEqual(so, input_json);
assert.strictEqual(input_json.Memos[0].Memo.parsed_memo_data, void(0));
});
it('should throw an error - full memo, json data, invalid MemoFormat', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "text",
"MemoData": {
"string" : "some_string",
"boolean" : true
}
}
}
];
assert.throws(function() {
SerializedObject.from_json(input_json);
}, /^Error: MemoData can only be a JSON object with a valid json MemoFormat \(Memo\) \(Memos\)/);
});
it('should serialize and parse - full memo, json data, valid MemoFormat, ignored field', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "json",
"ignored" : "ignored",
"MemoData": {
"string" : "some_string",
"boolean" : true
}
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
delete input_json.Memos[0].Memo.ignored;
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'json';
input_json.Memos[0].Memo.parsed_memo_data = {
"string" : "some_string",
"boolean" : true
};
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.stringify(
{
"string" : "some_string",
"boolean" : true
}
));
assert.deepEqual(so, input_json);
});
it('should serialize and parse - full memo, json data, valid MemoFormat', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "json",
"MemoData": {
"string" : "some_string",
"boolean" : true
}
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'json';
input_json.Memos[0].Memo.parsed_memo_data = {
"string" : "some_string",
"boolean" : true
};
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.stringify(
{
"string" : "some_string",
"boolean" : true
}
));
assert.deepEqual(so, input_json);
});
it('should serialize and parse - full memo, json data, valid MemoFormat, integer', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "json",
"MemoData": 3
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'json';
input_json.Memos[0].Memo.parsed_memo_data = 3;
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.parse(3));
assert.deepEqual(so, input_json);
});
it('should throw an error - invalid Memo field', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoParty": "json",
"MemoData": 3
}
}
];
assert.throws(function() {
SerializedObject.from_json(input_json);
}, /^Error: JSON contains unknown field: "MemoParty" \(Memo\) \(Memos\)/);
});
it('should serialize json with memo - match hex output', function() {
var input_json = {
Flags: 2147483648,
TransactionType: 'Payment',
Account: 'rhXzSyt1q9J8uiFXpK3qSugAAPJKXLtnrF',
Amount: '1',
Destination: 'radqi6ppXFxVhJdjzaATRBxdrPcVTf1Ung',
Memos: [
{
Memo: {
MemoType: 'image'
}
}
],
Sequence: 294,
SigningPubKey: '03D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C51',
Fee: '12000',
LastLedgerSequence: 10404607,
TxnSignature: '304402206B53EDFA6EFCF6FE5BA76C81BABB60A3B55E9DE8A1462DEDC5F387879575E498022015AE7B59AA49E735D7F2E252802C4406CD00689BCE5057C477FE979D38D2DAC9'
};
var serializedHex = '12000022800000002400000126201B009EC2FF614000000000000001684000000000002EE0732103D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C517446304402206B53EDFA6EFCF6FE5BA76C81BABB60A3B55E9DE8A1462DEDC5F387879575E498022015AE7B59AA49E735D7F2E252802C4406CD00689BCE5057C477FE979D38D2DAC9811426C4CFB3BD05A9AA23936F2E81634C66A9820C9483143DD06317D19C6110CAFF150AE528F58843BE2CA1F9EA7C05696D616765E1F1';
assert.strictEqual(SerializedObject.from_json(input_json).to_hex(), serializedHex);
});
});
});
});
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,11 +1,8 @@
var utils = require('./testutils');
var assert = require('assert');
var SerializedObject = utils.load_module('serializedobject').SerializedObject;
var types = utils.load_module('serializedtypes');
var amountConstants = require('../src/js/ripple/amount').consts;
var BigInteger = require('../src/js/jsbn/jsbn').BigInteger;
var config = require('./testutils').get_config();
var SerializedObject = require('ripple-lib').SerializedObject;
var types = require('ripple-lib').types;
var Amount = require('ripple-lib').Amount;
var BigInteger = require('ripple-lib').jsbn.BigInteger;
describe('Serialized types', function() {
describe('Int8', function() {
@@ -553,7 +550,7 @@ describe('Serialized types', function() {
});
it('Serialize max_value/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
var so = new SerializedObject();
types.Amount.serialize(so, amountConstants.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
types.Amount.serialize(so, Amount.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(so.to_hex(), 'EC6386F26FC0FFFF0000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
});
it('Parse 1 XRP', function () {
@@ -590,7 +587,7 @@ describe('Serialized types', function() {
});
it('Parse max_value/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
var so = new SerializedObject('EC6386F26FC0FFFF0000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
assert.strictEqual(types.Amount.parse(so).to_text_full(), amountConstants.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(types.Amount.parse(so).to_text_full(), Amount.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
});

View File

@@ -1,10 +1,9 @@
var assert = require('assert');
var ws = require('ws');
var utils = require('./testutils');
var Remote = utils.load_module('remote').Remote;
var Server = utils.load_module('server').Server;
var Request = utils.load_module('request').Request;
var Transaction = utils.load_module('transaction').Transaction;
var Remote = require('ripple-lib').Remote;
var Server = require('ripple-lib').Server;
var Request = require('ripple-lib').Request;
var Transaction = require('ripple-lib').Transaction;
describe('Server', function() {
it('Server constructor - invalid options', function() {

View File

@@ -1,6 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var Seed = utils.load_module('seed').Seed;
var Seed = require('ripple-lib').Seed;
function _isNaN(n) {
return typeof n === 'number' && isNaN(n);

View File

@@ -1,7 +1,6 @@
var assert = require('assert');
var utils = require('./testutils');
var sjcl = require('../build/sjcl');
var Seed = require('../src/js/ripple/seed').Seed;
var sjcl = require('ripple-lib').sjcl;
var Seed = require('ripple-lib').Seed;
describe('SJCL ECDSA Canonicalization', function() {
describe('canonicalizeSignature', function() {

View File

@@ -1,6 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var sjcl = require('../build/sjcl');
var sjcl = require('ripple-lib').sjcl;
describe('ECDSA signing with recoverable public key', function(){
@@ -30,7 +29,7 @@ describe('ECDSA signing with recoverable public key', function(){
// signature: 'AAAAG8L/yOA3nNqK4aOiQWJmOaWvkvr3NoTk6wCdX97U3qowdgFd98UK3evWV16qO3RHgFMEnUW/Vt4+kcidqW6hMo0='
}];
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
for (var m = 0; m < messages.length; m++) {
@@ -64,7 +63,7 @@ describe('ECDSA signing with recoverable public key', function(){
// // TODO: figure out why bitcoinjs-lib and this produce different signature values
// var curve = sjcl.ecc.curves['c256'];
// var curve = sjcl.ecc.curves['k256'];
// var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
// var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
@@ -85,7 +84,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should produce an error if the hash is not given as a bitArray', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -100,7 +99,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should return a bitArray', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -114,7 +113,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should return a bitArray where the first word contains the recovery factor', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -145,7 +144,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should produce an error if the signature given does not have the recovery factor prefix', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -162,7 +161,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should produce an error if it is not given both the hash and the signature', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -183,7 +182,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should produce an error if it cannot generate a valid public key from the the signature', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -203,7 +202,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should return a publicKey object', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -219,7 +218,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('tampering with the signature should produce a different public key, if it produces a valid one at all', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -238,6 +237,22 @@ describe('ECDSA signing with recoverable public key', function(){
});
it('should return a publicKey object, while using the overridden c256 curve', function(){
var curve = sjcl.ecc.curves['c256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
var signature = secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
var key = sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
assert(key instanceof sjcl.ecc.ecdsa.publicKey);
});
});
});

View File

@@ -1,6 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var sjcl = require('../build/sjcl');
var sjcl = require('ripple-lib').sjcl;
describe('SJCL Extramath', function() {
describe('setBitM', function() {

View File

@@ -1,6 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var sjcl = require('../build/sjcl');
var sjcl = require('ripple-lib').sjcl;
describe('SJCL Jacobi', function() {
it('(15/13) = -1', function () {

View File

@@ -1,6 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var sjcl = require('../build/sjcl');
var sjcl = require('ripple-lib').sjcl;
function testExp(vec) {
var actual = new sjcl.bn(vec.g).powermodMontgomery(new sjcl.bn(vec.e),

View File

@@ -1,31 +0,0 @@
var ripple = require('../src/js/ripple');
exports.get_config = get_config;
function get_config() {
var config = { };
try {
config = require('./config');
} catch(exception) {
config = require('./config-example');
}
return load_config(config);
};
exports.load_config = load_config;
function load_config(config) {
return load_module('config').load(config);
};
exports.load_module = load_module;
function load_module(name) {
if (process.env.RIPPLE_LIB_COV) {
return require('../src-cov/js/ripple/' + name)
} else if (!ripple.hasOwnProperty(name)) {
return require('../src/js/ripple/' + name);
} else {
return require('../src/js/ripple')[name];
}
};

View File

@@ -1,7 +1,6 @@
var assert = require('assert');
var utils = require('./testutils');
var Transaction = utils.load_module('transaction').Transaction;
var TransactionQueue = utils.load_module('transactionqueue').TransactionQueue;
var Transaction = require('ripple-lib').Transaction;
var TransactionQueue = require('ripple-lib').TransactionQueue;
describe('Transaction queue', function() {
it('Push transaction', function() {

View File

@@ -1,9 +1,9 @@
var utils = require('./testutils');
var assert = require('assert');
var Amount = utils.load_module('amount').Amount;
var Transaction = utils.load_module('transaction').Transaction;
var Remote = utils.load_module('remote').Remote;
var Server = utils.load_module('server').Server;
var assert = require('assert');
var Amount = require('ripple-lib').Amount;
var Transaction = require('ripple-lib').Transaction;
var TransactionQueue = require('ripple-lib').TransactionQueue;
var Remote = require('ripple-lib').Remote;
var Server = require('ripple-lib').Server;
var transactionResult = {
engine_result: 'tesSUCCESS',
@@ -38,7 +38,7 @@ describe('Transaction', function() {
it('Success listener', function(done) {
var transaction = new Transaction();
transaction.once('cleanup', function(message) {
transaction.once('final', function(message) {
assert.deepEqual(message, transactionResult);
assert(transaction.finalized);
assert.strictEqual(transaction.state, 'validated');
@@ -51,7 +51,7 @@ describe('Transaction', function() {
it('Error listener', function(done) {
var transaction = new Transaction();
transaction.once('cleanup', function(message) {
transaction.once('final', function(message) {
assert.deepEqual(message, transactionResult);
assert(transaction.finalized);
assert.strictEqual(transaction.state, 'failed');
@@ -130,25 +130,29 @@ describe('Transaction', function() {
it('Set state', function(done) {
var transaction = new Transaction();
transaction.state = 'pending';
assert.strictEqual(transaction.state, 'unsubmitted');
var receivedEvents = 0;
var events = 2;
var events = [
'submitted',
'pending',
'validated'
];
transaction.once('state', function(state) {
assert.strictEqual(state, 'validated');
if (++receivedEvents === events) {
done();
}
transaction.on('state', function(state) {
receivedEvents++;
assert(events.indexOf(state) > -1, 'Invalid state: ' + state);
});
transaction.once('save', function() {
if (++receivedEvents === events) {
done();
}
});
transaction.setState(events[0]);
transaction.setState(events[1]);
transaction.setState(events[1]);
transaction.setState(events[2]);
transaction.setState('validated');
assert.strictEqual(receivedEvents, 3);
assert.strictEqual(transaction.state, events[2]);
done();
});
it('Finalize submission', function() {
@@ -720,47 +724,20 @@ describe('Transaction', function() {
done();
});
it('Sign transaction - with callback', function(done) {
var transaction = new Transaction();
transaction._secret = 'sh2pTicynUEG46jjR4EoexHcQEoij';
transaction.tx_json.SigningPubKey = '021FED5FD081CE5C4356431267D04C6E2167E4112C897D5E10335D4E22B4DA49ED';
transaction.tx_json.Account = 'rMWwx3Ma16HnqSd4H6saPisihX9aKpXxHJ';
transaction.tx_json.Flags = 0;
transaction.tx_json.Fee = 10;
transaction.tx_json.Sequence = 1;
transaction.tx_json.TransactionType = 'AccountSet';
transaction.sign(function() {
var signature = transaction.tx_json.TxnSignature;
assert.strictEqual(transaction.previousSigningHash, 'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
assert(/^[A-Z0-9]+$/.test(signature));
done();
});
});
it('Add transaction ID', function(done) {
var transaction = new Transaction();
var saved = 0;
transaction.on('save', function() {
++saved;
});
transaction.once('save', function() {
setImmediate(function() {
assert.strictEqual(saved, 2);
assert.deepEqual(
transaction.submittedIDs,
[ 'F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE',
'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE' ]
);
done();
});
});
transaction.addId('D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
transaction.addId('F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
transaction.addId('F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
assert.deepEqual(
transaction.submittedIDs,
[ 'F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE',
'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE' ]
);
done();
});
it('Find transaction IDs in cache', function(done) {
@@ -791,9 +768,10 @@ describe('Transaction', function() {
it('Set DestinationTag', function() {
var transaction = new Transaction();
transaction.destinationTag();
transaction.destinationTag('tag');
assert.strictEqual(transaction.tx_json.DestinationTag, 'tag');
assert.strictEqual(transaction.tx_json.DestinationTag, void(0));
transaction.destinationTag(1);
assert.strictEqual(transaction.tx_json.DestinationTag, 1);
});
it('Set InvoiceID', function() {
@@ -849,6 +827,23 @@ describe('Transaction', function() {
assert.strictEqual(transaction._setMaxFee, true);
});
it('Set Fixed Fee', function() {
var transaction = new Transaction();
transaction.setFixedFee('a');
assert(!transaction._setFixedFee);
transaction.setFixedFee(-1000);
assert(!transaction._setFixedFee);
transaction.setFixedFee(NaN);
assert(!transaction._setFixedFee);
transaction.setFixedFee(1000);
assert.strictEqual(transaction._setFixedFee, true);
assert.strictEqual(transaction.tx_json.Fee, '1000');
});
it('Rewrite transaction path', function() {
var transaction = new Transaction();
@@ -872,7 +867,7 @@ describe('Transaction', function() {
}
];
assert.deepEqual(Transaction._pathRewrite(path), [
assert.deepEqual(Transaction._rewritePath(path), [
{
account: 'rP51ycDJw5ZhgvdKiRjBYZKYjsyoCcHmnY',
issuer: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
@@ -892,7 +887,9 @@ describe('Transaction', function() {
});
it('Rewrite transaction path - invalid path', function() {
assert.strictEqual(Transaction._pathRewrite(1), void(0));
assert.throws(function() {
assert.strictEqual(Transaction._rewritePath(1), void(0));
});
});
it('Add transaction path', function() {
@@ -1001,77 +998,136 @@ describe('Transaction', function() {
it('Set SourceTag', function() {
var transaction = new Transaction();
transaction.sourceTag('tag');
assert.strictEqual(transaction.tx_json.SourceTag, 'tag');
assert.strictEqual(transaction.tx_json.SourceTag, void(0));
transaction.sourceTag(1);
assert.strictEqual(transaction.tx_json.SourceTag, 1);
});
it('Set TransferRate', function() {
var transaction = new Transaction();
assert.throws(function() {
transaction.transferRate(1);
});
assert.throws(function() {
transaction.transferRate('1');
});
transaction.transferRate(1);
assert.strictEqual(transaction.tx_json.TransferRate, void(0));
transaction.transferRate(1.5 * 1e9);
assert.strictEqual(transaction.tx_json.TransferRate, 1.5 * 1e9);
});
it('Set Flags', function(done) {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags();
assert.strictEqual(transaction.tx_json.Flags, 0);
transaction.setFlags(1);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags(Transaction.flags.Payment.PartialPayment);
assert.strictEqual(transaction.tx_json.Flags, 131072);
assert.strictEqual(transaction.tx_json.Flags, 1);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags('NoRippleDirect');
assert.strictEqual(transaction.tx_json.Flags, 65536);
transaction.setFlags('PartialPayment');
assert.strictEqual(transaction.tx_json.Flags, 131073);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags('PartialPayment', 'NoRippleDirect');
assert.strictEqual(transaction.tx_json.Flags, 196608);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags([ 'LimitQuality', 'PartialPayment' ]);
assert.strictEqual(transaction.tx_json.Flags, 393216);
assert.strictEqual(transaction.tx_json.Flags, 524289);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.once('error', function(err) {
assert.strictEqual(err.result, 'tejInvalidFlag');
done();
});
transaction.setFlags('test');
transaction.setFlags('asdf');
});
it('Add Memo', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.addMemo('testkey', 'testvalue');
transaction.addMemo('testkey2', 'testvalue2');
transaction.addMemo('testkey3');
transaction.addMemo(void(0), 'testvalue4');
var memoType = 'message';
var memoFormat = 'application/json';
var memoData = {
string: 'value',
bool: true,
integer: 1
};
transaction.addMemo(memoType, memoFormat, memoData);
var expected = [
{
Memo:
{
MemoType: memoType,
MemoFormat: memoFormat,
MemoData: memoData
}
}
];
assert.deepEqual(transaction.tx_json.Memos, expected);
});
it('Add Memo - by object', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
var memo = {
memoType: 'type',
memoData: 'data'
};
transaction.addMemo(memo);
var expected = [
{
Memo: {
MemoType: memo.memoType,
MemoData: memo.memoData
}
}
];
assert.deepEqual(transaction.tx_json.Memos, expected);
});
it('Add Memos', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.addMemo('testkey', void(0), 'testvalue');
transaction.addMemo('testkey2', void(0), 'testvalue2');
transaction.addMemo('testkey3', 'text/html');
transaction.addMemo(void(0), void(0), 'testvalue4');
transaction.addMemo('testkey4', 'text/html', '<html>');
var expected = [
{ Memo: {
MemoType: new Buffer('testkey').toString('hex'),
MemoData: new Buffer('testvalue').toString('hex')
MemoType: 'testkey',
MemoData: 'testvalue'
}},
{ Memo: {
MemoType: new Buffer('testkey2').toString('hex'),
MemoData: new Buffer('testvalue2').toString('hex')
MemoType: 'testkey2',
MemoData: 'testvalue2'
}},
{ Memo: {
MemoType: new Buffer('testkey3').toString('hex')
MemoType: 'testkey3',
MemoFormat: 'text/html'
}},
{ Memo: {
MemoData: new Buffer('testvalue4').toString('hex')
} }
MemoData: 'testvalue4'
}},
{ Memo: {
MemoType: 'testkey4',
MemoFormat: 'text/html',
MemoData: '<html>'
}}
];
assert.deepEqual(transaction.tx_json.Memos, expected);
@@ -1086,23 +1142,87 @@ describe('Transaction', function() {
}, /^Error: MemoType must be a string$/);
});
it('Add Memo - invalid MemoData', function() {
it('Add Memo - invalid ASCII MemoType', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
assert.throws(function() {
transaction.addMemo('key', 1);
}, /^Error: MemoData must be a string$/);
transaction.addMemo('한국어');
}, /^Error: MemoType must be valid ASCII$/);
});
it('Construct AccountSet transaction', function() {
var transaction = new Transaction().accountSet('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm');
it('Add Memo - invalid MemoFormat', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
assert.throws(function() {
transaction.addMemo(void(0), 1);
}, /^Error: MemoFormat must be a string$/);
});
it('Add Memo - invalid ASCII MemoFormat', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
assert.throws(function() {
transaction.addMemo(void(0), 'России');
}, /^Error: MemoFormat must be valid ASCII$/);
});
it('Add Memo - MemoData string', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.addMemo({memoData:'some_string'});
assert.deepEqual(transaction.tx_json.Memos, [
{
Memo: {
MemoData: 'some_string'
}
}
]);
});
it('Add Memo - MemoData complex object', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
var memo = {
memoData: {
string: 'string',
int: 1,
array: [
{
string: 'string'
}
],
object: {
string: 'string'
}
}
};
transaction.addMemo(memo);
assert.deepEqual(transaction.tx_json.Memos, [
{
Memo: {
MemoData: memo.memoData
}
}
]);
});
it('Set AccountTxnID', function() {
var transaction = new Transaction();
assert(transaction instanceof Transaction);
transaction.accountTxnID('75C5A92212AA82A89C3824F6F071FE49C95C45DE9113EB51763A217DBACB5B4F');
assert.deepEqual(transaction.tx_json, {
Flags: 0,
TransactionType: 'AccountSet',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm'
AccountTxnID: '75C5A92212AA82A89C3824F6F071FE49C95C45DE9113EB51763A217DBACB5B4F'
});
});
@@ -1119,6 +1239,18 @@ describe('Transaction', function() {
});
});
it('Construct AccountSet transaction - with AccountTxnID SetFlag', function() {
var transaction = new Transaction().accountSet('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'asfAccountTxnID');
assert(transaction instanceof Transaction);
assert.deepEqual(transaction.tx_json, {
Flags: 0,
SetFlag: 5,
TransactionType: 'AccountSet',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm'
});
});
it('Construct AccountSet transaction - params object', function() {
var transaction = new Transaction().accountSet({
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
@@ -1269,7 +1401,7 @@ describe('Transaction', function() {
var bid = '1/USD/rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm';
var ask = '1/EUR/rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm';
assert.throws(function() {
var transaction = new Transaction().offerCreate('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', bid, ask);
new Transaction().offerCreate('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', bid, ask);
});
});
@@ -1302,13 +1434,13 @@ describe('Transaction', function() {
it('Construct SetRegularKey transaction - invalid account', function() {
assert.throws(function() {
var transaction = new Transaction().setRegularKey('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
new Transaction().setRegularKey('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
});
});
it('Construct SetRegularKey transaction - invalid regularKey', function() {
assert.throws(function() {
var transaction = new Transaction().setRegularKey('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'xr36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
new Transaction().setRegularKey('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'xr36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
});
});
@@ -1548,16 +1680,10 @@ describe('Transaction', function() {
it('Submit transaction - invalid account', function(done) {
var remote = new Remote();
var transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
transaction.tx_json.Account += 'z';
transaction.once('error', function(err) {
assert.strictEqual(err.result, 'tejInvalidAccount');
done();
assert.throws(function() {
var transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWeZ');
});
transaction.submit();
done();
});
it('Abort submission on presubmit', function(done) {
@@ -1589,6 +1715,39 @@ describe('Transaction', function() {
});
});
});
it('Get min ledger', function() {
var queue = new TransactionQueue();
// Randomized submit indexes
[
28093,
456944,
347213,
165662,
729760,
808990,
927393,
925550,
872298,
543305
]
.forEach(function(index){
var tx = new Transaction();
tx.initialSubmitIndex = index;
queue.push(tx);
});
// Pending queue sorted by submit index
var sorted = queue._queue.slice().sort(function(a, b) {
return a.initialSubmitIndex - b.initialSubmitIndex;
});
sorted.forEach(function(tx){
assert.strictEqual(queue.getMinLedger(), tx.initialSubmitIndex);
queue.remove(tx);
});
});
});
// vim:sw=2:sts=2:ts=8:et
//vim:sw=2:sts=2:ts=8:et

View File

@@ -1,7 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var UInt128 = utils.load_module('uint128').UInt128;
var config = require('./testutils').get_config();
var UInt128 = require('ripple-lib').UInt128;
describe('UInt', function() {
describe('128', function() {

View File

@@ -1,6 +1,5 @@
var fs = require('fs');
var assert = require('assert');
var utils = require('./testutils').load_module('utils');
var utils = require('ripple-lib').utils;
describe('Utils', function() {
describe('hexToString and stringToHex', function() {

View File

@@ -1,11 +1,11 @@
var assert = require('assert');
var RippleTxt = require('../src/js/ripple/rippletxt').RippleTxt;
var AuthInfo = require('../src/js/ripple/authinfo').AuthInfo;
var VaultClient = require('../src/js/ripple/vaultclient').VaultClient;
var Blob = require('../src/js/ripple/blob').Blob;
var UInt256 = require('../src/js/ripple/uint256').UInt256;
var sjcl = require('../build/sjcl');
var nock = require('nock');
var RippleTxt = require('ripple-lib').RippleTxt;
var AuthInfo = require('ripple-lib').AuthInfo;
var VaultClient = require('ripple-lib').VaultClient;
var Blob = require('ripple-lib').Blob;
var UInt256 = require('ripple-lib').UInt256;
var sjcl = require('ripple-lib').sjcl;
var online = process.argv.indexOf('--online-blobvault') !== -1 ? true : false;
var exampleData = {