From f08ebde70cf06db0765541f6aa8107264b98efe4 Mon Sep 17 00:00:00 2001 From: valzav Date: Mon, 8 Jun 2015 13:37:07 -0400 Subject: [PATCH] Initial commit --- .eslintrc | 34 + .gitignore | 8 + LICENSE.txt | 15 + README.md | 38 + cli/app.js | 33 + cli/package.json | 35 + cli/webpack.config.js | 34 + dl/lib/bytebuffer_3.5.4.js | 4246 +++++++++++++++++ dl/package.json | 40 + dl/src/actions/AccountActions.js | 141 + dl/src/actions/AssetActions.js | 31 + dl/src/actions/BlockchainActions.js | 87 + dl/src/actions/IntlActions.js | 15 + dl/src/actions/KeyActions.js | 12 + dl/src/actions/MarketsActions.js | 68 + dl/src/actions/SessionActions.js | 40 + dl/src/actions/WitnessActions.js | 43 + dl/src/actions/mockApi.js | 223 + dl/src/alt-instance.js | 4 + dl/src/api/accountApi.js | 47 + dl/src/chain/chain_types.coffee | 72 + dl/src/chain/config.coffee | 3 + dl/src/chain/lookup.coffee | 207 + dl/src/chain/market_operations.coffee | 31 + dl/src/chain/object_id.coffee | 55 + dl/src/chain/serializer.coffee | 87 + dl/src/chain/serializer_convert.coffee | 30 + .../chain/serializer_operation_types.coffee | 732 +++ dl/src/chain/serializer_types.coffee | 382 ++ dl/src/chain/serializer_validation.coffee | 127 + dl/src/chain/transaction_helper.coffee | 103 + dl/src/chain/transaction_operations.coffee | 187 + dl/src/common/Counterpart.js | 3 + dl/src/common/Promise.js | 1 + dl/src/common/bytebuffer.js | 10 + dl/src/common/error_with_cause.coffee | 17 + dl/src/common/fast_parser.coffee | 44 + dl/src/common/hash.js | 45 + dl/src/common/utils.js | 20 + dl/src/common/validation.coffee | 26 + dl/src/ecc/address.coffee | 48 + dl/src/ecc/aes.coffee | 120 + dl/src/ecc/ecdsa.js | 202 + dl/src/ecc/ecsignature.js | 126 + dl/src/ecc/key_private.coffee | 90 + dl/src/ecc/key_public.coffee | 110 + dl/src/ecc/signature.coffee | 115 + dl/src/ecc/types.js | 40 + dl/src/rpc_api/ApiInstances.js | 50 + dl/src/rpc_api/ApplicationApi.js | 149 + dl/src/rpc_api/DatabaseApi.js | 54 + dl/src/rpc_api/GrapheneApi.js | 25 + dl/src/rpc_api/MarketApi.js | 46 + dl/src/rpc_api/NetworkApi.js | 25 + dl/src/rpc_api/WalletApi.js | 79 + dl/src/rpc_api/WebSocketRpc.js | 110 + dl/src/stores/AccountStore.js | 126 + dl/src/stores/AssetStore.js | 32 + dl/src/stores/BaseStore.js | 13 + dl/src/stores/BlockchainStore.js | 60 + dl/src/stores/IntlStore.js | 59 + dl/src/stores/KeyStore.js | 21 + dl/src/stores/MarketsStore.js | 56 + dl/src/stores/SessionStore.js | 52 + dl/src/stores/WitnessStore.js | 50 + dl/src/stores/__tests__/account-store-test.js | 8 + dl/src/stores/tcomb_structs.js | 124 + dl/test/README.txt | 65 + dl/test/crypto.coffee | 49 + dl/test/market_tests.coffee | 47 + dl/test/test_helper.coffee | 26 + dl/test/tr_tests.coffee | 179 + dl/test/types_test.coffee | 48 + doc/README.md | 1 + ios/.flowconfig | 3 + ios/Graphene.xcodeproj/project.pbxproj | 614 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/Graphene.xccheckout | 41 + .../UserInterfaceState.xcuserstate | Bin 0 -> 20894 bytes .../xcshareddata/xcschemes/Graphene.xcscheme | 88 + .../xcschemes/xcschememanagement.plist | 22 + ios/iOS/AppDelegate.h | 16 + ios/iOS/AppDelegate.m | 51 + ios/iOS/Base.lproj/LaunchScreen.xib | 42 + .../AppIcon.appiconset/Contents.json | 38 + ios/iOS/Info.plist | 42 + ios/iOS/main.jsbundle | 5 + ios/iOS/main.m | 18 + ios/index.ios.js | 171 + ios/package.json | 17 + web/.gitignore | 4 + .../components/Account/Identicon-test.jsx | 41 + web/__tests__/utils/stub_router_context.js | 41 + web/app/App.jsx | 127 + web/app/assets/fonts/Roboto-Bold.eot | Bin 0 -> 34266 bytes web/app/assets/fonts/Roboto-Bold.svg | 2067 ++++++++ web/app/assets/fonts/Roboto-Bold.ttf | Bin 0 -> 82584 bytes web/app/assets/fonts/Roboto-Bold.woff | Bin 0 -> 40816 bytes web/app/assets/fonts/Roboto-Light.eot | Bin 0 -> 34527 bytes web/app/assets/fonts/Roboto-Light.svg | 2230 +++++++++ web/app/assets/fonts/Roboto-Light.ttf | Bin 0 -> 84588 bytes web/app/assets/fonts/Roboto-Light.woff | Bin 0 -> 40892 bytes web/app/assets/fonts/Roboto-Regular.eot | Bin 0 -> 34393 bytes web/app/assets/fonts/Roboto-Regular.svg | 2179 +++++++++ web/app/assets/fonts/Roboto-Regular.ttf | Bin 0 -> 83600 bytes web/app/assets/fonts/Roboto-Regular.woff | Bin 0 -> 40788 bytes .../assets/fonts/RobotoCondensed-Regular.eot | Bin 0 -> 34964 bytes .../assets/fonts/RobotoCondensed-Regular.svg | 2233 +++++++++ .../assets/fonts/RobotoCondensed-Regular.ttf | Bin 0 -> 84068 bytes .../assets/fonts/RobotoCondensed-Regular.woff | Bin 0 -> 41272 bytes web/app/assets/index.html | 14 + web/app/assets/loader.js | 10 + web/app/assets/locales/locale-en.js | 116 + web/app/assets/locales/locale-fr.js | 118 + web/app/assets/stylesheets/app.scss | 23 + web/app/assets/stylesheets/fonts.scss | 32 + web/app/assets/stylesheets/footer.scss | 13 + web/app/assets/stylesheets/forms.scss | 17 + .../stylesheets/foundation_overrides.scss | 66 + .../stylesheets/foundation_settings.scss | 591 +++ web/app/assets/stylesheets/header.scss | 82 + web/app/assets/stylesheets/page_layout.scss | 30 + web/app/assets/stylesheets/transfer.scss | 7 + web/app/assets/stylesheets/wallet.scss | 50 + web/app/components/Account/Account.jsx | 206 + .../components/Account/AccountContainer.jsx | 48 + web/app/components/Account/AccountImage.jsx | 27 + web/app/components/Account/AccountInfo.jsx | 24 + web/app/components/Account/Connections.jsx | 37 + web/app/components/Account/Identicon.jsx | 43 + web/app/components/Account/MemberStats.jsx | 60 + web/app/components/Account/TransactionRow.jsx | 237 + web/app/components/AuthenticatedComponent.jsx | 27 + web/app/components/BaseComponent.jsx | 77 + web/app/components/Blockchain/Asset.jsx | 62 + .../components/Blockchain/AssetContainer.jsx | 34 + web/app/components/Blockchain/Block.jsx | 167 + .../components/Blockchain/BlockContainer.jsx | 46 + web/app/components/Blockchain/Transaction.jsx | 300 ++ web/app/components/CreateAccount.jsx | 60 + web/app/components/Dashboard/AccountCard.jsx | 104 + web/app/components/Dashboard/Dashboard.jsx | 67 + .../Dashboard/DashboardContainer.jsx | 32 + web/app/components/Discover/Discover.jsx | 199 + .../components/Discover/DiscoverContainer.jsx | 43 + web/app/components/Exchange/Exchange.jsx | 193 + .../components/Exchange/ExchangeContainer.jsx | 38 + web/app/components/Exchange/MarketCard.jsx | 35 + web/app/components/Exchange/Markets.jsx | 68 + .../components/Exchange/MarketsContainer.jsx | 30 + web/app/components/Exchange/exchange.scss | 7 + web/app/components/Footer/Footer.jsx | 44 + web/app/components/Header/Header.jsx | 145 + web/app/components/Header/MobileMenu.jsx | 41 + web/app/components/Icon/Icon.jsx | 25 + web/app/components/Icon/chevron-down.svg | 6 + web/app/components/Icon/cog.svg | 7 + web/app/components/Icon/icon.scss | 4 + web/app/components/Icon/menu.svg | 8 + web/app/components/Icon/plus-circle.svg | 7 + web/app/components/Icon/trash.svg | 8 + web/app/components/Icon/user.svg | 7 + .../LoadingIndicator/LoadingIndicator.jsx | 31 + .../LoadingIndicator/loading-indicator.scss | 97 + web/app/components/Login.jsx | 91 + web/app/components/Logout.jsx | 36 + web/app/components/Notifier/Notifier.jsx | 58 + .../components/Notifier/NotifierContainer.jsx | 35 + web/app/components/Settings/Settings.jsx | 63 + .../Transfer/ConfirmationScreen.jsx | 54 + web/app/components/Transfer/DoneScreen.jsx | 34 + web/app/components/Transfer/Transfer.jsx | 304 ++ web/app/components/Transfer/TransferPage.jsx | 41 + web/app/components/Transfer/transfer.scss | 29 + web/app/components/Utility/FormattedAsset.jsx | 51 + web/app/components/Utility/intlData.js | 14 + web/app/components/Wallet/Accounts.jsx | 71 + web/app/components/Wallet/Assets.jsx | 45 + web/app/components/Wallet/History.jsx | 21 + web/app/components/Wallet/Receive.jsx | 21 + web/app/components/Wallet/Wallet.jsx | 59 + web/app/counterpart-instance.js | 2 + web/bower.json | 18 + web/karma.conf.js | 5 + web/package.json | 94 + web/webpack.config.js | 58 + 186 files changed, 24952 insertions(+) create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100755 cli/app.js create mode 100644 cli/package.json create mode 100644 cli/webpack.config.js create mode 100644 dl/lib/bytebuffer_3.5.4.js create mode 100644 dl/package.json create mode 100644 dl/src/actions/AccountActions.js create mode 100644 dl/src/actions/AssetActions.js create mode 100644 dl/src/actions/BlockchainActions.js create mode 100644 dl/src/actions/IntlActions.js create mode 100644 dl/src/actions/KeyActions.js create mode 100644 dl/src/actions/MarketsActions.js create mode 100644 dl/src/actions/SessionActions.js create mode 100644 dl/src/actions/WitnessActions.js create mode 100644 dl/src/actions/mockApi.js create mode 100644 dl/src/alt-instance.js create mode 100644 dl/src/api/accountApi.js create mode 100644 dl/src/chain/chain_types.coffee create mode 100644 dl/src/chain/config.coffee create mode 100644 dl/src/chain/lookup.coffee create mode 100644 dl/src/chain/market_operations.coffee create mode 100644 dl/src/chain/object_id.coffee create mode 100644 dl/src/chain/serializer.coffee create mode 100644 dl/src/chain/serializer_convert.coffee create mode 100644 dl/src/chain/serializer_operation_types.coffee create mode 100644 dl/src/chain/serializer_types.coffee create mode 100644 dl/src/chain/serializer_validation.coffee create mode 100644 dl/src/chain/transaction_helper.coffee create mode 100644 dl/src/chain/transaction_operations.coffee create mode 100644 dl/src/common/Counterpart.js create mode 100644 dl/src/common/Promise.js create mode 100644 dl/src/common/bytebuffer.js create mode 100644 dl/src/common/error_with_cause.coffee create mode 100644 dl/src/common/fast_parser.coffee create mode 100644 dl/src/common/hash.js create mode 100644 dl/src/common/utils.js create mode 100644 dl/src/common/validation.coffee create mode 100644 dl/src/ecc/address.coffee create mode 100644 dl/src/ecc/aes.coffee create mode 100644 dl/src/ecc/ecdsa.js create mode 100644 dl/src/ecc/ecsignature.js create mode 100644 dl/src/ecc/key_private.coffee create mode 100644 dl/src/ecc/key_public.coffee create mode 100644 dl/src/ecc/signature.coffee create mode 100644 dl/src/ecc/types.js create mode 100644 dl/src/rpc_api/ApiInstances.js create mode 100644 dl/src/rpc_api/ApplicationApi.js create mode 100644 dl/src/rpc_api/DatabaseApi.js create mode 100644 dl/src/rpc_api/GrapheneApi.js create mode 100644 dl/src/rpc_api/MarketApi.js create mode 100644 dl/src/rpc_api/NetworkApi.js create mode 100644 dl/src/rpc_api/WalletApi.js create mode 100644 dl/src/rpc_api/WebSocketRpc.js create mode 100644 dl/src/stores/AccountStore.js create mode 100644 dl/src/stores/AssetStore.js create mode 100644 dl/src/stores/BaseStore.js create mode 100644 dl/src/stores/BlockchainStore.js create mode 100644 dl/src/stores/IntlStore.js create mode 100644 dl/src/stores/KeyStore.js create mode 100644 dl/src/stores/MarketsStore.js create mode 100644 dl/src/stores/SessionStore.js create mode 100644 dl/src/stores/WitnessStore.js create mode 100644 dl/src/stores/__tests__/account-store-test.js create mode 100644 dl/src/stores/tcomb_structs.js create mode 100644 dl/test/README.txt create mode 100644 dl/test/crypto.coffee create mode 100644 dl/test/market_tests.coffee create mode 100644 dl/test/test_helper.coffee create mode 100644 dl/test/tr_tests.coffee create mode 100644 dl/test/types_test.coffee create mode 100644 doc/README.md create mode 100644 ios/.flowconfig create mode 100644 ios/Graphene.xcodeproj/project.pbxproj create mode 100644 ios/Graphene.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Graphene.xcodeproj/project.xcworkspace/xcshareddata/Graphene.xccheckout create mode 100644 ios/Graphene.xcodeproj/project.xcworkspace/xcuserdata/vz.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 ios/Graphene.xcodeproj/xcshareddata/xcschemes/Graphene.xcscheme create mode 100644 ios/Graphene.xcodeproj/xcuserdata/vz.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 ios/iOS/AppDelegate.h create mode 100644 ios/iOS/AppDelegate.m create mode 100644 ios/iOS/Base.lproj/LaunchScreen.xib create mode 100644 ios/iOS/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios/iOS/Info.plist create mode 100644 ios/iOS/main.jsbundle create mode 100644 ios/iOS/main.m create mode 100644 ios/index.ios.js create mode 100644 ios/package.json create mode 100644 web/.gitignore create mode 100644 web/__tests__/components/Account/Identicon-test.jsx create mode 100644 web/__tests__/utils/stub_router_context.js create mode 100644 web/app/App.jsx create mode 100644 web/app/assets/fonts/Roboto-Bold.eot create mode 100644 web/app/assets/fonts/Roboto-Bold.svg create mode 100644 web/app/assets/fonts/Roboto-Bold.ttf create mode 100644 web/app/assets/fonts/Roboto-Bold.woff create mode 100644 web/app/assets/fonts/Roboto-Light.eot create mode 100644 web/app/assets/fonts/Roboto-Light.svg create mode 100644 web/app/assets/fonts/Roboto-Light.ttf create mode 100644 web/app/assets/fonts/Roboto-Light.woff create mode 100644 web/app/assets/fonts/Roboto-Regular.eot create mode 100644 web/app/assets/fonts/Roboto-Regular.svg create mode 100644 web/app/assets/fonts/Roboto-Regular.ttf create mode 100644 web/app/assets/fonts/Roboto-Regular.woff create mode 100644 web/app/assets/fonts/RobotoCondensed-Regular.eot create mode 100644 web/app/assets/fonts/RobotoCondensed-Regular.svg create mode 100644 web/app/assets/fonts/RobotoCondensed-Regular.ttf create mode 100644 web/app/assets/fonts/RobotoCondensed-Regular.woff create mode 100644 web/app/assets/index.html create mode 100644 web/app/assets/loader.js create mode 100644 web/app/assets/locales/locale-en.js create mode 100644 web/app/assets/locales/locale-fr.js create mode 100644 web/app/assets/stylesheets/app.scss create mode 100644 web/app/assets/stylesheets/fonts.scss create mode 100644 web/app/assets/stylesheets/footer.scss create mode 100644 web/app/assets/stylesheets/forms.scss create mode 100644 web/app/assets/stylesheets/foundation_overrides.scss create mode 100644 web/app/assets/stylesheets/foundation_settings.scss create mode 100644 web/app/assets/stylesheets/header.scss create mode 100644 web/app/assets/stylesheets/page_layout.scss create mode 100644 web/app/assets/stylesheets/transfer.scss create mode 100644 web/app/assets/stylesheets/wallet.scss create mode 100644 web/app/components/Account/Account.jsx create mode 100644 web/app/components/Account/AccountContainer.jsx create mode 100644 web/app/components/Account/AccountImage.jsx create mode 100644 web/app/components/Account/AccountInfo.jsx create mode 100644 web/app/components/Account/Connections.jsx create mode 100644 web/app/components/Account/Identicon.jsx create mode 100644 web/app/components/Account/MemberStats.jsx create mode 100644 web/app/components/Account/TransactionRow.jsx create mode 100644 web/app/components/AuthenticatedComponent.jsx create mode 100644 web/app/components/BaseComponent.jsx create mode 100644 web/app/components/Blockchain/Asset.jsx create mode 100644 web/app/components/Blockchain/AssetContainer.jsx create mode 100644 web/app/components/Blockchain/Block.jsx create mode 100644 web/app/components/Blockchain/BlockContainer.jsx create mode 100644 web/app/components/Blockchain/Transaction.jsx create mode 100644 web/app/components/CreateAccount.jsx create mode 100644 web/app/components/Dashboard/AccountCard.jsx create mode 100644 web/app/components/Dashboard/Dashboard.jsx create mode 100644 web/app/components/Dashboard/DashboardContainer.jsx create mode 100644 web/app/components/Discover/Discover.jsx create mode 100644 web/app/components/Discover/DiscoverContainer.jsx create mode 100644 web/app/components/Exchange/Exchange.jsx create mode 100644 web/app/components/Exchange/ExchangeContainer.jsx create mode 100644 web/app/components/Exchange/MarketCard.jsx create mode 100644 web/app/components/Exchange/Markets.jsx create mode 100644 web/app/components/Exchange/MarketsContainer.jsx create mode 100644 web/app/components/Exchange/exchange.scss create mode 100644 web/app/components/Footer/Footer.jsx create mode 100644 web/app/components/Header/Header.jsx create mode 100644 web/app/components/Header/MobileMenu.jsx create mode 100644 web/app/components/Icon/Icon.jsx create mode 100755 web/app/components/Icon/chevron-down.svg create mode 100755 web/app/components/Icon/cog.svg create mode 100644 web/app/components/Icon/icon.scss create mode 100755 web/app/components/Icon/menu.svg create mode 100755 web/app/components/Icon/plus-circle.svg create mode 100644 web/app/components/Icon/trash.svg create mode 100755 web/app/components/Icon/user.svg create mode 100644 web/app/components/LoadingIndicator/LoadingIndicator.jsx create mode 100644 web/app/components/LoadingIndicator/loading-indicator.scss create mode 100644 web/app/components/Login.jsx create mode 100644 web/app/components/Logout.jsx create mode 100644 web/app/components/Notifier/Notifier.jsx create mode 100644 web/app/components/Notifier/NotifierContainer.jsx create mode 100644 web/app/components/Settings/Settings.jsx create mode 100644 web/app/components/Transfer/ConfirmationScreen.jsx create mode 100644 web/app/components/Transfer/DoneScreen.jsx create mode 100644 web/app/components/Transfer/Transfer.jsx create mode 100644 web/app/components/Transfer/TransferPage.jsx create mode 100644 web/app/components/Transfer/transfer.scss create mode 100644 web/app/components/Utility/FormattedAsset.jsx create mode 100644 web/app/components/Utility/intlData.js create mode 100644 web/app/components/Wallet/Accounts.jsx create mode 100644 web/app/components/Wallet/Assets.jsx create mode 100644 web/app/components/Wallet/History.jsx create mode 100644 web/app/components/Wallet/Receive.jsx create mode 100644 web/app/components/Wallet/Wallet.jsx create mode 100644 web/app/counterpart-instance.js create mode 100644 web/bower.json create mode 100644 web/karma.conf.js create mode 100644 web/package.json create mode 100644 web/webpack.config.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000000..d18ff78e39 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,34 @@ +// see http://eslint.org/docs/rules/ +{ + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "env": { + "browser": true, + "node": true, + "es6": true + }, + "globals": { + "__DEV__": true, + "__SERVER__": true, + "jest": true, + "it": true, + "describe": true, + "expect": true, + "beforeEach": true + }, + "ecmaFeatures": { + "jsx": true + }, + "rules": { + // Strict mode + "strict": [2, "never"], + // Code style + "no-underscore-dangle": [0], + "indent": [4, 4], + "camelcase": [0], + "new-cap": [0], + "no-trailing-spaces": [0] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..ffe42c2216 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.idea +*/node_modules +*/npm-debug.log +cli/bundle.js* +*.map +*.log +cli/examples.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000..2a8be16e74 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,15 @@ +Copyright (c) 2015, Cryptonomex, Inc. +All rights reserved. + +This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and +the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, +are permitted until September 8, 2015, provided that the following conditions are met: + +1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..0933bff822 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +Graphene GUI +============ + +## Install + +``` +cd cli; npm install +cd ../dl; npm install +cd ../ios; npm install +cd ../web; npm install +``` + +## Run it + +Go to cli or web dir and run: + +``` +npm start +``` + +## Testing +Jest currently doesn't work with node (see https://github.com/facebook/jest/issues/243), so in order to run the tests you need to install iojs. Under Ubuntu instructions can be found here: + +[Nodesource Ubuntu io.js installation](https://nodesource.com/blog/nodejs-v012-iojs-and-the-nodesource-linux-repositories "Nodesource iojs") + +In order for jest to correctly follow paths it is necessary to add a local path to your NODE_PATH variable. Under Ubuntu, you can do so by running the following from the web directory: + +``` +export NODE_PATH=$NODE_PATH:. +``` + +Tests are then run using + +``` +npm test +``` + + diff --git a/cli/app.js b/cli/app.js new file mode 100755 index 0000000000..13b42f4170 --- /dev/null +++ b/cli/app.js @@ -0,0 +1,33 @@ +var repl = require("repl"); +var repl_history = require("repl.history"); +var promisify = require("repl-promised").promisify; + +var Apis = require('../dl/src/rpc_api/ApiInstances'); +var ApplicationApi = require('../dl/src/rpc_api/ApplicationApi'); +var WalletApi = require('../dl/src/rpc_api/WalletApi'); + +Apis.instance().init_promise.then(() => { + var repl_instance = repl.start({ + prompt: "Graphene > ", + input: process.stdin, + output: process.stdout, + ignoreUndefined: true + }); + promisify(repl_instance); + repl_instance.on("exit", function () { + Apis.instance().close(); + }); + var database_api = Apis.instance().db_api(); + var network_api = Apis.instance().network_api(); + repl_instance.context.$g = {} + repl_instance.context.$g.db = database_api; + repl_instance.context.$g.net = network_api; + repl_instance.context.$g.app = new ApplicationApi(); + repl_instance.context.$g.wallet = new WalletApi(); + var hist_file = process.env.HOME + "/.graphene_history"; + repl_history(repl_instance, hist_file); +}).catch(error => { + console.log("[App.js] ----- ERROR ----->", error, error.stack); + this.setState({loading: false}); +}); + diff --git a/cli/package.json b/cli/package.json new file mode 100644 index 0000000000..7feb712bec --- /dev/null +++ b/cli/package.json @@ -0,0 +1,35 @@ +{ + "name": "cli", + "description": "cli powered by graphene", + "homepage": "https://github.com/cryptonomex/graphene_ui", + "author": "Cryptonomex, Inc.", + "license" : "LicenseRef-LICENSE", + "version": "0.0.1", + "description": "graphene cli", + "engines": { + "node": "0.12.2", + "npm": "2.8.3" + }, + "repository": { + "type": "git", + "url": "git://github.com/....git" + }, + "scripts": { + "postinstall": "", + "test": "", + "start": "webpack; node ./bundle.js" + }, + "dependencies": { + "babel-core": "~5.1.11", + "babel-loader": "~5.0.0", + "coffee-loader": "^0.7.2", + "json-loader": "^0.5.1", + "repl": "^0.1.3", + "repl-promised": "^0.1.0", + "repl.history": "^0.1.3", + "webpack": "~1.8.7" + }, + "devDependencies": { + "source-map-support": "^0.2.10" + } +} diff --git a/cli/webpack.config.js b/cli/webpack.config.js new file mode 100644 index 0000000000..885f948288 --- /dev/null +++ b/cli/webpack.config.js @@ -0,0 +1,34 @@ +var path = require("path"); +var webpack = require("webpack"); + +var config = { + entry: path.resolve(__dirname, "app.js"), + target: "node", + output: { + path: path.resolve(__dirname), + filename: "bundle.js" + }, + module: { + loaders: [ + { test: /\.js$/, exclude: /node_modules/, loader: "babel"}, + { test: /\.json/, loader: "json" }, + { test: /\.coffee$/, loader: "coffee" } + ] + }, + resolve: { + root: [path.resolve(__dirname, "./"), path.resolve(__dirname, "../dl/src")], + extensions: ["", ".js", ".jsx", ".json", ".coffee"], + modulesDirectories: ["node_modules"], + fallback: [path.resolve(__dirname, "./node_modules")] + }, + resolveLoader: { + fallback: [path.resolve(__dirname, "./node_modules")] + }, + plugins: [ + new webpack.IgnorePlugin(/\.(css|less)$/), + new webpack.BannerPlugin("require('source-map-support').install();", { raw: true, entryOnly: false }) + ], + devtool: "sourcemap" +}; + +module.exports = config; diff --git a/dl/lib/bytebuffer_3.5.4.js b/dl/lib/bytebuffer_3.5.4.js new file mode 100644 index 0000000000..cbf1d553f7 --- /dev/null +++ b/dl/lib/bytebuffer_3.5.4.js @@ -0,0 +1,4246 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.bytebuffer = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/** + * @license ByteBuffer.js (c) 2013-2014 Daniel Wirtz + * This version of ByteBuffer.js uses an ArrayBuffer (AB) as its backing buffer and is compatible with modern browsers. + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/ByteBuffer.js for details + */ // +(function(global) { + "use strict"; + + /** + * @param {function(new: Long, number, number, boolean=)=} Long + * @returns {function(new: ByteBuffer, number=, boolean=, boolean=)}} + * @inner + */ + function loadByteBuffer(Long) { + + /** + * Constructs a new ByteBuffer. + * @class The swiss army knife for binary data in JavaScript. + * @exports ByteBuffer + * @constructor + * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}. + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @expose + */ + var ByteBuffer = function(capacity, littleEndian, noAssert) { + if (typeof capacity === 'undefined') capacity = ByteBuffer.DEFAULT_CAPACITY; + if (typeof littleEndian === 'undefined') littleEndian = ByteBuffer.DEFAULT_ENDIAN; + if (typeof noAssert === 'undefined') noAssert = ByteBuffer.DEFAULT_NOASSERT; + if (!noAssert) { + capacity = capacity | 0; + if (capacity < 0) + throw RangeError("Illegal capacity"); + littleEndian = !!littleEndian; + noAssert = !!noAssert; + } + + /** + * Backing buffer. + * @type {!ArrayBuffer} + * @expose + */ + this.buffer = capacity === 0 ? EMPTY_BUFFER : new ArrayBuffer(capacity); + + /** + * Data view to manipulate the backing buffer. Becomes `null` if the backing buffer has a capacity of `0`. + * @type {?DataView} + * @expose + */ + this.view = capacity === 0 ? null : new DataView(this.buffer); + + /** + * Absolute read/write offset. + * @type {number} + * @expose + * @see ByteBuffer#flip + * @see ByteBuffer#clear + */ + this.offset = 0; + + /** + * Marked offset. + * @type {number} + * @expose + * @see ByteBuffer#mark + * @see ByteBuffer#reset + */ + this.markedOffset = -1; + + /** + * Absolute limit of the contained data. Set to the backing buffer's capacity upon allocation. + * @type {number} + * @expose + * @see ByteBuffer#flip + * @see ByteBuffer#clear + */ + this.limit = capacity; + + /** + * Whether to use little endian byte order, defaults to `false` for big endian. + * @type {boolean} + * @expose + */ + this.littleEndian = typeof littleEndian !== 'undefined' ? !!littleEndian : false; + + /** + * Whether to skip assertions of offsets and values, defaults to `false`. + * @type {boolean} + * @expose + */ + this.noAssert = !!noAssert; + }; + + /** + * ByteBuffer version. + * @type {string} + * @const + * @expose + */ + ByteBuffer.VERSION = "3.5.4"; + + /** + * Little endian constant that can be used instead of its boolean value. Evaluates to `true`. + * @type {boolean} + * @const + * @expose + */ + ByteBuffer.LITTLE_ENDIAN = true; + + /** + * Big endian constant that can be used instead of its boolean value. Evaluates to `false`. + * @type {boolean} + * @const + * @expose + */ + ByteBuffer.BIG_ENDIAN = false; + + /** + * Default initial capacity of `16`. + * @type {number} + * @expose + */ + ByteBuffer.DEFAULT_CAPACITY = 16; + + /** + * Default endianess of `false` for big endian. + * @type {boolean} + * @expose + */ + ByteBuffer.DEFAULT_ENDIAN = ByteBuffer.BIG_ENDIAN; + + /** + * Default no assertions flag of `false`. + * @type {boolean} + * @expose + */ + ByteBuffer.DEFAULT_NOASSERT = false; + + /** + * A `Long` class for representing a 64-bit two's-complement integer value. May be `null` if Long.js has not been loaded + * and int64 support is not available. + * @type {?Long} + * @const + * @see https://github.com/dcodeIO/Long.js + * @expose + */ + ByteBuffer.Long = Long || null; + + /** + * @alias ByteBuffer.prototype + * @inner + */ + var ByteBufferPrototype = ByteBuffer.prototype; + + // helpers + + /** + * @type {!ArrayBuffer} + * @inner + */ + var EMPTY_BUFFER = new ArrayBuffer(0); + + /** + * String.fromCharCode reference for compile-time renaming. + * @type {function(...number):string} + * @inner + */ + var stringFromCharCode = String.fromCharCode; + + /** + * Creates a source function for a string. + * @param {string} s String to read from + * @returns {function():number|null} Source function returning the next char code respectively `null` if there are + * no more characters left. + * @throws {TypeError} If the argument is invalid + * @inner + */ + function stringSource(s) { + var i=0; return function() { + return i < s.length ? s.charCodeAt(i++) : null; + }; + } + + /** + * Creates a destination function for a string. + * @returns {function(number=):undefined|string} Destination function successively called with the next char code. + * Returns the final string when called without arguments. + * @inner + */ + function stringDestination() { + var cs = [], ps = []; return function() { + if (arguments.length === 0) + return ps.join('')+stringFromCharCode.apply(String, cs); + if (cs.length + arguments.length > 1024) + ps.push(stringFromCharCode.apply(String, cs)), + cs.length = 0; + Array.prototype.push.apply(cs, arguments); + }; + } + + /** + * Allocates a new ByteBuffer backed by a buffer of the specified capacity. + * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}. + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} + * @expose + */ + ByteBuffer.allocate = function(capacity, littleEndian, noAssert) { + return new ByteBuffer(capacity, littleEndian, noAssert); + }; + + /** + * Concatenates multiple ByteBuffers into one. + * @param {!Array.} buffers Buffers to concatenate + * @param {(string|boolean)=} encoding String encoding if `buffers` contains a string ("base64", "hex", "binary", + * defaults to "utf8") + * @param {boolean=} littleEndian Whether to use little or big endian byte order for the resulting ByteBuffer. Defaults + * to {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values for the resulting ByteBuffer. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} Concatenated ByteBuffer + * @expose + */ + ByteBuffer.concat = function(buffers, encoding, littleEndian, noAssert) { + if (typeof encoding === 'boolean' || typeof encoding !== 'string') { + noAssert = littleEndian; + littleEndian = encoding; + encoding = undefined; + } + var capacity = 0; + for (var i=0, k=buffers.length, length; i 0) capacity += length; + } + if (capacity === 0) + return new ByteBuffer(0, littleEndian, noAssert); + var bb = new ByteBuffer(capacity, littleEndian, noAssert), + bi; + var view = new Uint8Array(bb.buffer); + i=0; while (i} buffer Anything that can be wrapped + * @param {(string|boolean)=} encoding String encoding if `buffer` is a string ("base64", "hex", "binary", defaults to + * "utf8") + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} A ByteBuffer wrapping `buffer` + * @expose + */ + ByteBuffer.wrap = function(buffer, encoding, littleEndian, noAssert) { + if (typeof encoding !== 'string') { + noAssert = littleEndian; + littleEndian = encoding; + encoding = undefined; + } + if (typeof buffer === 'string') { + if (typeof encoding === 'undefined') + encoding = "utf8"; + switch (encoding) { + case "base64": + return ByteBuffer.fromBase64(buffer, littleEndian); + case "hex": + return ByteBuffer.fromHex(buffer, littleEndian); + case "binary": + return ByteBuffer.fromBinary(buffer, littleEndian); + case "utf8": + return ByteBuffer.fromUTF8(buffer, littleEndian); + case "debug": + return ByteBuffer.fromDebug(buffer, littleEndian); + default: + throw Error("Unsupported encoding: "+encoding); + } + } + if (buffer === null || typeof buffer !== 'object') + throw TypeError("Illegal buffer"); + var bb; + if (ByteBuffer.isByteBuffer(buffer)) { + bb = ByteBufferPrototype.clone.call(buffer); + bb.markedOffset = -1; + return bb; + } + if (buffer instanceof Uint8Array) { // Extract ArrayBuffer from Uint8Array + bb = new ByteBuffer(0, littleEndian, noAssert); + if (buffer.length > 0) { // Avoid references to more than one EMPTY_BUFFER + bb.buffer = buffer.buffer; + bb.offset = buffer.byteOffset; + bb.limit = buffer.byteOffset + buffer.length; + bb.view = buffer.length > 0 ? new DataView(buffer.buffer) : null; + } + } else if (buffer instanceof ArrayBuffer) { // Reuse ArrayBuffer + bb = new ByteBuffer(0, littleEndian, noAssert); + if (buffer.byteLength > 0) { + bb.buffer = buffer; + bb.offset = 0; + bb.limit = buffer.byteLength; + bb.view = buffer.byteLength > 0 ? new DataView(buffer) : null; + } + } else if (Object.prototype.toString.call(buffer) === "[object Array]") { // Create from octets + bb = new ByteBuffer(buffer.length, littleEndian, noAssert); + bb.limit = buffer.length; + for (i=0; i>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 1; + var capacity0 = this.buffer.byteLength; + if (offset > capacity0) + this.resize((capacity0 *= 2) > offset ? capacity0 : offset); + offset -= 1; + this.view.setInt8(offset, value); + if (relative) this.offset += 1; + return this; + }; + + /** + * Writes an 8bit signed integer. This is an alias of {@link ByteBuffer#writeInt8}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeByte = ByteBufferPrototype.writeInt8; + + /** + * Reads an 8bit signed integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readInt8 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + var value = this.view.getInt8(offset); + if (relative) this.offset += 1; + return value; + }; + + /** + * Reads an 8bit signed integer. This is an alias of {@link ByteBuffer#readInt8}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readByte = ByteBufferPrototype.readInt8; + + /** + * Writes an 8bit unsigned integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeUint8 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value >>>= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 1; + var capacity1 = this.buffer.byteLength; + if (offset > capacity1) + this.resize((capacity1 *= 2) > offset ? capacity1 : offset); + offset -= 1; + this.view.setUint8(offset, value); + if (relative) this.offset += 1; + return this; + }; + + /** + * Reads an 8bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readUint8 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + var value = this.view.getUint8(offset); + if (relative) this.offset += 1; + return value; + }; + + // types/ints/int16 + + /** + * Writes a 16bit signed integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @throws {TypeError} If `offset` or `value` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.writeInt16 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value |= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 2; + var capacity2 = this.buffer.byteLength; + if (offset > capacity2) + this.resize((capacity2 *= 2) > offset ? capacity2 : offset); + offset -= 2; + this.view.setInt16(offset, value, this.littleEndian); + if (relative) this.offset += 2; + return this; + }; + + /** + * Writes a 16bit signed integer. This is an alias of {@link ByteBuffer#writeInt16}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @throws {TypeError} If `offset` or `value` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.writeShort = ByteBufferPrototype.writeInt16; + + /** + * Reads a 16bit signed integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @returns {number} Value read + * @throws {TypeError} If `offset` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.readInt16 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 2 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.byteLength); + } + var value = this.view.getInt16(offset, this.littleEndian); + if (relative) this.offset += 2; + return value; + }; + + /** + * Reads a 16bit signed integer. This is an alias of {@link ByteBuffer#readInt16}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @returns {number} Value read + * @throws {TypeError} If `offset` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.readShort = ByteBufferPrototype.readInt16; + + /** + * Writes a 16bit unsigned integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @throws {TypeError} If `offset` or `value` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.writeUint16 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value >>>= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 2; + var capacity3 = this.buffer.byteLength; + if (offset > capacity3) + this.resize((capacity3 *= 2) > offset ? capacity3 : offset); + offset -= 2; + this.view.setUint16(offset, value, this.littleEndian); + if (relative) this.offset += 2; + return this; + }; + + /** + * Reads a 16bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @returns {number} Value read + * @throws {TypeError} If `offset` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.readUint16 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 2 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.byteLength); + } + var value = this.view.getUint16(offset, this.littleEndian); + if (relative) this.offset += 2; + return value; + }; + + // types/ints/int32 + + /** + * Writes a 32bit signed integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @expose + */ + ByteBufferPrototype.writeInt32 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value |= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 4; + var capacity4 = this.buffer.byteLength; + if (offset > capacity4) + this.resize((capacity4 *= 2) > offset ? capacity4 : offset); + offset -= 4; + this.view.setInt32(offset, value, this.littleEndian); + if (relative) this.offset += 4; + return this; + }; + + /** + * Writes a 32bit signed integer. This is an alias of {@link ByteBuffer#writeInt32}. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @expose + */ + ByteBufferPrototype.writeInt = ByteBufferPrototype.writeInt32; + + /** + * Reads a 32bit signed integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readInt32 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 4 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength); + } + var value = this.view.getInt32(offset, this.littleEndian); + if (relative) this.offset += 4; + return value; + }; + + /** + * Reads a 32bit signed integer. This is an alias of {@link ByteBuffer#readInt32}. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readInt = ByteBufferPrototype.readInt32; + + /** + * Writes a 32bit unsigned integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @expose + */ + ByteBufferPrototype.writeUint32 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value >>>= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 4; + var capacity5 = this.buffer.byteLength; + if (offset > capacity5) + this.resize((capacity5 *= 2) > offset ? capacity5 : offset); + offset -= 4; + this.view.setUint32(offset, value, this.littleEndian); + if (relative) this.offset += 4; + return this; + }; + + /** + * Reads a 32bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readUint32 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 4 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength); + } + var value = this.view.getUint32(offset, this.littleEndian); + if (relative) this.offset += 4; + return value; + }; + + // types/ints/int64 + + if (Long) { + + /** + * Writes a 64bit signed integer. + * @param {number|!Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeInt64 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value === 'number') + value = Long.fromNumber(value); + else if (!(value && value instanceof Long)) + throw TypeError("Illegal value: "+value+" (not an integer or Long)"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + if (typeof value === 'number') + value = Long.fromNumber(value); + offset += 8; + var capacity6 = this.buffer.byteLength; + if (offset > capacity6) + this.resize((capacity6 *= 2) > offset ? capacity6 : offset); + offset -= 8; + if (this.littleEndian) { + this.view.setInt32(offset , value.low , true); + this.view.setInt32(offset+4, value.high, true); + } else { + this.view.setInt32(offset , value.high, false); + this.view.setInt32(offset+4, value.low , false); + } + if (relative) this.offset += 8; + return this; + }; + + /** + * Writes a 64bit signed integer. This is an alias of {@link ByteBuffer#writeInt64}. + * @param {number|!Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeLong = ByteBufferPrototype.writeInt64; + + /** + * Reads a 64bit signed integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!Long} + * @expose + */ + ByteBufferPrototype.readInt64 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 8 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength); + } + var value = this.littleEndian + ? new Long(this.view.getInt32(offset , true ), this.view.getInt32(offset+4, true ), false) + : new Long(this.view.getInt32(offset+4, false), this.view.getInt32(offset , false), false); + if (relative) this.offset += 8; + return value; + }; + + /** + * Reads a 64bit signed integer. This is an alias of {@link ByteBuffer#readInt64}. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!Long} + * @expose + */ + ByteBufferPrototype.readLong = ByteBufferPrototype.readInt64; + + /** + * Writes a 64bit unsigned integer. + * @param {number|!Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeUint64 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value === 'number') + value = Long.fromNumber(value); + else if (!(value && value instanceof Long)) + throw TypeError("Illegal value: "+value+" (not an integer or Long)"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + if (typeof value === 'number') + value = Long.fromNumber(value); + offset += 8; + var capacity7 = this.buffer.byteLength; + if (offset > capacity7) + this.resize((capacity7 *= 2) > offset ? capacity7 : offset); + offset -= 8; + if (this.littleEndian) { + this.view.setInt32(offset , value.low , true); + this.view.setInt32(offset+4, value.high, true); + } else { + this.view.setInt32(offset , value.high, false); + this.view.setInt32(offset+4, value.low , false); + } + if (relative) this.offset += 8; + return this; + }; + + /** + * Reads a 64bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!Long} + * @expose + */ + ByteBufferPrototype.readUint64 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 8 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength); + } + var value = this.littleEndian + ? new Long(this.view.getInt32(offset , true ), this.view.getInt32(offset+4, true ), true) + : new Long(this.view.getInt32(offset+4, false), this.view.getInt32(offset , false), true); + if (relative) this.offset += 8; + return value; + }; + + } // Long + + + // types/floats/float32 + + /** + * Writes a 32bit float. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeFloat32 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number') + throw TypeError("Illegal value: "+value+" (not a number)"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 4; + var capacity8 = this.buffer.byteLength; + if (offset > capacity8) + this.resize((capacity8 *= 2) > offset ? capacity8 : offset); + offset -= 4; + this.view.setFloat32(offset, value, this.littleEndian); + if (relative) this.offset += 4; + return this; + }; + + /** + * Writes a 32bit float. This is an alias of {@link ByteBuffer#writeFloat32}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeFloat = ByteBufferPrototype.writeFloat32; + + /** + * Reads a 32bit float. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} + * @expose + */ + ByteBufferPrototype.readFloat32 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 4 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength); + } + var value = this.view.getFloat32(offset, this.littleEndian); + if (relative) this.offset += 4; + return value; + }; + + /** + * Reads a 32bit float. This is an alias of {@link ByteBuffer#readFloat32}. + * @function + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} + * @expose + */ + ByteBufferPrototype.readFloat = ByteBufferPrototype.readFloat32; + + // types/floats/float64 + + /** + * Writes a 64bit float. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeFloat64 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number') + throw TypeError("Illegal value: "+value+" (not a number)"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 8; + var capacity9 = this.buffer.byteLength; + if (offset > capacity9) + this.resize((capacity9 *= 2) > offset ? capacity9 : offset); + offset -= 8; + this.view.setFloat64(offset, value, this.littleEndian); + if (relative) this.offset += 8; + return this; + }; + + /** + * Writes a 64bit float. This is an alias of {@link ByteBuffer#writeFloat64}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeDouble = ByteBufferPrototype.writeFloat64; + + /** + * Reads a 64bit float. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {number} + * @expose + */ + ByteBufferPrototype.readFloat64 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 8 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength); + } + var value = this.view.getFloat64(offset, this.littleEndian); + if (relative) this.offset += 8; + return value; + }; + + /** + * Reads a 64bit float. This is an alias of {@link ByteBuffer#readFloat64}. + * @function + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {number} + * @expose + */ + ByteBufferPrototype.readDouble = ByteBufferPrototype.readFloat64; + + + // types/varints/varint32 + + /** + * Maximum number of bytes required to store a 32bit base 128 variable-length integer. + * @type {number} + * @const + * @expose + */ + ByteBuffer.MAX_VARINT32_BYTES = 5; + + /** + * Calculates the actual number of bytes required to store a 32bit base 128 variable-length integer. + * @param {number} value Value to encode + * @returns {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT32_BYTES} + * @expose + */ + ByteBuffer.calculateVarint32 = function(value) { + // ref: src/google/protobuf/io/coded_stream.cc + value = value >>> 0; + if (value < 1 << 7 ) return 1; + else if (value < 1 << 14) return 2; + else if (value < 1 << 21) return 3; + else if (value < 1 << 28) return 4; + else return 5; + }; + + /** + * Zigzag encodes a signed 32bit integer so that it can be effectively used with varint encoding. + * @param {number} n Signed 32bit integer + * @returns {number} Unsigned zigzag encoded 32bit integer + * @expose + */ + ByteBuffer.zigZagEncode32 = function(n) { + return (((n |= 0) << 1) ^ (n >> 31)) >>> 0; // ref: src/google/protobuf/wire_format_lite.h + }; + + /** + * Decodes a zigzag encoded signed 32bit integer. + * @param {number} n Unsigned zigzag encoded 32bit integer + * @returns {number} Signed 32bit integer + * @expose + */ + ByteBuffer.zigZagDecode32 = function(n) { + return ((n >>> 1) ^ -(n & 1)) | 0; // // ref: src/google/protobuf/wire_format_lite.h + }; + + /** + * Writes a 32bit base 128 variable-length integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} this if `offset` is omitted, else the actual number of bytes written + * @expose + */ + ByteBufferPrototype.writeVarint32 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value |= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var size = ByteBuffer.calculateVarint32(value), + b; + offset += size; + var capacity10 = this.buffer.byteLength; + if (offset > capacity10) + this.resize((capacity10 *= 2) > offset ? capacity10 : offset); + offset -= size; + // ref: http://code.google.com/searchframe#WTeibokF6gE/trunk/src/google/protobuf/io/coded_stream.cc + this.view.setUint8(offset, b = value | 0x80); + value >>>= 0; + if (value >= 1 << 7) { + b = (value >> 7) | 0x80; + this.view.setUint8(offset+1, b); + if (value >= 1 << 14) { + b = (value >> 14) | 0x80; + this.view.setUint8(offset+2, b); + if (value >= 1 << 21) { + b = (value >> 21) | 0x80; + this.view.setUint8(offset+3, b); + if (value >= 1 << 28) { + this.view.setUint8(offset+4, (value >> 28) & 0x0F); + size = 5; + } else { + this.view.setUint8(offset+3, b & 0x7F); + size = 4; + } + } else { + this.view.setUint8(offset+2, b & 0x7F); + size = 3; + } + } else { + this.view.setUint8(offset+1, b & 0x7F); + size = 2; + } + } else { + this.view.setUint8(offset, b & 0x7F); + size = 1; + } + if (relative) { + this.offset += size; + return this; + } + return size; + }; + + /** + * Writes a zig-zag encoded 32bit base 128 variable-length integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} this if `offset` is omitted, else the actual number of bytes written + * @expose + */ + ByteBufferPrototype.writeVarint32ZigZag = function(value, offset) { + return this.writeVarint32(ByteBuffer.zigZagEncode32(value), offset); + }; + + /** + * Reads a 32bit base 128 variable-length integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {number|!{value: number, length: number}} The value read if offset is omitted, else the value read + * and the actual number of bytes read. + * @throws {Error} If it's not a valid varint. Has a property `truncated = true` if there is not enough data available + * to fully decode the varint. + * @expose + */ + ByteBufferPrototype.readVarint32 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + // ref: src/google/protobuf/io/coded_stream.cc + var size = 0, + value = 0 >>> 0, + temp, + ioffset; + do { + ioffset = offset+size; + if (!this.noAssert && ioffset > this.limit) { + var err = Error("Truncated"); + err['truncated'] = true; + throw err; + } + temp = this.view.getUint8(ioffset); + if (size < 5) + value |= ((temp&0x7F)<<(7*size)) >>> 0; + ++size; + } while ((temp & 0x80) === 0x80); + value = value | 0; // Make sure to discard the higher order bits + if (relative) { + this.offset += size; + return value; + } + return { + "value": value, + "length": size + }; + }; + + /** + * Reads a zig-zag encoded 32bit base 128 variable-length integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {number|!{value: number, length: number}} The value read if offset is omitted, else the value read + * and the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBufferPrototype.readVarint32ZigZag = function(offset) { + var val = this.readVarint32(offset); + if (typeof val === 'object') + val["value"] = ByteBuffer.zigZagDecode32(val["value"]); + else + val = ByteBuffer.zigZagDecode32(val); + return val; + }; + + // types/varints/varint64 + + if (Long) { + + /** + * Maximum number of bytes required to store a 64bit base 128 variable-length integer. + * @type {number} + * @const + * @expose + */ + ByteBuffer.MAX_VARINT64_BYTES = 10; + + /** + * Calculates the actual number of bytes required to store a 64bit base 128 variable-length integer. + * @param {number|!Long} value Value to encode + * @returns {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT64_BYTES} + * @expose + */ + ByteBuffer.calculateVarint64 = function(value) { + if (typeof value === 'number') + value = Long.fromNumber(value); + // ref: src/google/protobuf/io/coded_stream.cc + var part0 = value.toInt() >>> 0, + part1 = value.shiftRightUnsigned(28).toInt() >>> 0, + part2 = value.shiftRightUnsigned(56).toInt() >>> 0; + if (part2 == 0) { + if (part1 == 0) { + if (part0 < 1 << 14) + return part0 < 1 << 7 ? 1 : 2; + else + return part0 < 1 << 21 ? 3 : 4; + } else { + if (part1 < 1 << 14) + return part1 < 1 << 7 ? 5 : 6; + else + return part1 < 1 << 21 ? 7 : 8; + } + } else + return part2 < 1 << 7 ? 9 : 10; + }; + + /** + * Zigzag encodes a signed 64bit integer so that it can be effectively used with varint encoding. + * @param {number|!Long} value Signed long + * @returns {!Long} Unsigned zigzag encoded long + * @expose + */ + ByteBuffer.zigZagEncode64 = function(value) { + if (typeof value === 'number') + value = Long.fromNumber(value, false); + else if (value.unsigned !== false) value = value.toSigned(); + // ref: src/google/protobuf/wire_format_lite.h + return value.shiftLeft(1).xor(value.shiftRight(63)).toUnsigned(); + }; + + /** + * Decodes a zigzag encoded signed 64bit integer. + * @param {!Long|number} value Unsigned zigzag encoded long or JavaScript number + * @returns {!Long} Signed long + * @expose + */ + ByteBuffer.zigZagDecode64 = function(value) { + if (typeof value === 'number') + value = Long.fromNumber(value, false); + else if (value.unsigned !== false) value = value.toSigned(); + // ref: src/google/protobuf/wire_format_lite.h + return value.shiftRightUnsigned(1).xor(value.and(Long.ONE).toSigned().negate()).toSigned(); + }; + + /** + * Writes a 64bit base 128 variable-length integer. + * @param {number|Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} `this` if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBufferPrototype.writeVarint64 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value === 'number') + value = Long.fromNumber(value); + else if (!(value && value instanceof Long)) + throw TypeError("Illegal value: "+value+" (not an integer or Long)"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + if (typeof value === 'number') + value = Long.fromNumber(value, false); + else if (value.unsigned !== false) value = value.toSigned(); + var size = ByteBuffer.calculateVarint64(value), + part0 = value.toInt() >>> 0, + part1 = value.shiftRightUnsigned(28).toInt() >>> 0, + part2 = value.shiftRightUnsigned(56).toInt() >>> 0; + offset += size; + var capacity11 = this.buffer.byteLength; + if (offset > capacity11) + this.resize((capacity11 *= 2) > offset ? capacity11 : offset); + offset -= size; + switch (size) { + case 10: this.view.setUint8(offset+9, (part2 >>> 7) & 0x01); + case 9 : this.view.setUint8(offset+8, size !== 9 ? (part2 ) | 0x80 : (part2 ) & 0x7F); + case 8 : this.view.setUint8(offset+7, size !== 8 ? (part1 >>> 21) | 0x80 : (part1 >>> 21) & 0x7F); + case 7 : this.view.setUint8(offset+6, size !== 7 ? (part1 >>> 14) | 0x80 : (part1 >>> 14) & 0x7F); + case 6 : this.view.setUint8(offset+5, size !== 6 ? (part1 >>> 7) | 0x80 : (part1 >>> 7) & 0x7F); + case 5 : this.view.setUint8(offset+4, size !== 5 ? (part1 ) | 0x80 : (part1 ) & 0x7F); + case 4 : this.view.setUint8(offset+3, size !== 4 ? (part0 >>> 21) | 0x80 : (part0 >>> 21) & 0x7F); + case 3 : this.view.setUint8(offset+2, size !== 3 ? (part0 >>> 14) | 0x80 : (part0 >>> 14) & 0x7F); + case 2 : this.view.setUint8(offset+1, size !== 2 ? (part0 >>> 7) | 0x80 : (part0 >>> 7) & 0x7F); + case 1 : this.view.setUint8(offset , size !== 1 ? (part0 ) | 0x80 : (part0 ) & 0x7F); + } + if (relative) { + this.offset += size; + return this; + } else { + return size; + } + }; + + /** + * Writes a zig-zag encoded 64bit base 128 variable-length integer. + * @param {number|Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} `this` if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBufferPrototype.writeVarint64ZigZag = function(value, offset) { + return this.writeVarint64(ByteBuffer.zigZagEncode64(value), offset); + }; + + /** + * Reads a 64bit base 128 variable-length integer. Requires Long.js. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {!Long|!{value: Long, length: number}} The value read if offset is omitted, else the value read and + * the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBufferPrototype.readVarint64 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + // ref: src/google/protobuf/io/coded_stream.cc + var start = offset, + part0 = 0, + part1 = 0, + part2 = 0, + b = 0; + b = this.view.getUint8(offset++); part0 = (b & 0x7F) ; if (b & 0x80) { + b = this.view.getUint8(offset++); part0 |= (b & 0x7F) << 7; if (b & 0x80) { + b = this.view.getUint8(offset++); part0 |= (b & 0x7F) << 14; if (b & 0x80) { + b = this.view.getUint8(offset++); part0 |= (b & 0x7F) << 21; if (b & 0x80) { + b = this.view.getUint8(offset++); part1 = (b & 0x7F) ; if (b & 0x80) { + b = this.view.getUint8(offset++); part1 |= (b & 0x7F) << 7; if (b & 0x80) { + b = this.view.getUint8(offset++); part1 |= (b & 0x7F) << 14; if (b & 0x80) { + b = this.view.getUint8(offset++); part1 |= (b & 0x7F) << 21; if (b & 0x80) { + b = this.view.getUint8(offset++); part2 = (b & 0x7F) ; if (b & 0x80) { + b = this.view.getUint8(offset++); part2 |= (b & 0x7F) << 7; if (b & 0x80) { + throw Error("Buffer overrun"); }}}}}}}}}} + var value = Long.fromBits(part0 | (part1 << 28), (part1 >>> 4) | (part2) << 24, false); + if (relative) { + this.offset = offset; + return value; + } else { + return { + 'value': value, + 'length': offset-start + }; + } + }; + + /** + * Reads a zig-zag encoded 64bit base 128 variable-length integer. Requires Long.js. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {!Long|!{value: Long, length: number}} The value read if offset is omitted, else the value read and + * the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBufferPrototype.readVarint64ZigZag = function(offset) { + var val = this.readVarint64(offset); + if (val && val['value'] instanceof Long) + val["value"] = ByteBuffer.zigZagDecode64(val["value"]); + else + val = ByteBuffer.zigZagDecode64(val); + return val; + }; + + } // Long + + + // types/strings/cstring + + /** + * Writes a NULL-terminated UTF8 encoded string. For this to work the specified string must not contain any NULL + * characters itself. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * contained in `str` + 1 if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written + * @expose + */ + ByteBufferPrototype.writeCString = function(str, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + var i, + k = str.length; + if (!this.noAssert) { + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + for (i=0; i>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var start = offset; + // UTF8 strings do not contain zero bytes in between except for the zero character, so: + k = utfx.calculateUTF16asUTF8(stringSource(str))[1]; + offset += k+1; + var capacity12 = this.buffer.byteLength; + if (offset > capacity12) + this.resize((capacity12 *= 2) > offset ? capacity12 : offset); + offset -= k+1; + utfx.encodeUTF16toUTF8(stringSource(str), function(b) { + this.view.setUint8(offset++, b); + }.bind(this)); + this.view.setUint8(offset++, 0); + if (relative) { + this.offset = offset - start; + return this; + } + return k; + }; + + /** + * Reads a NULL-terminated UTF8 encoded string. For this to work the string read must not contain any NULL characters + * itself. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @expose + */ + ByteBufferPrototype.readCString = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + var start = offset, + temp; + // UTF8 strings do not contain zero bytes in between except for the zero character itself, so: + var sd, b = -1; + utfx.decodeUTF8toUTF16(function() { + if (b === 0) return null; + if (offset >= this.limit) + throw RangeError("Illegal range: Truncated data, "+offset+" < "+this.limit); + return (b = this.view.getUint8(offset++)) === 0 ? null : b; + }.bind(this), sd = stringDestination(), true); + if (relative) { + this.offset = offset; + return sd(); + } else { + return { + "string": sd(), + "length": offset - start + }; + } + }; + + // types/strings/istring + + /** + * Writes a length as uint32 prefixed UTF8 encoded string. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} `this` if `offset` is omitted, else the actual number of bytes written + * @expose + * @see ByteBuffer#writeVarint32 + */ + ByteBufferPrototype.writeIString = function(str, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var start = offset, + k; + k = utfx.calculateUTF16asUTF8(stringSource(str), this.noAssert)[1]; + offset += 4+k; + var capacity13 = this.buffer.byteLength; + if (offset > capacity13) + this.resize((capacity13 *= 2) > offset ? capacity13 : offset); + offset -= 4+k; + this.view.setUint32(offset, k, this.littleEndian); + offset += 4; + utfx.encodeUTF16toUTF8(stringSource(str), function(b) { + this.view.setUint8(offset++, b); + }.bind(this)); + if (offset !== start + 4 + k) + throw RangeError("Illegal range: Truncated data, "+offset+" == "+(offset+4+k)); + if (relative) { + this.offset = offset; + return this; + } + return offset - start; + }; + + /** + * Reads a length as uint32 prefixed UTF8 encoded string. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @expose + * @see ByteBuffer#readVarint32 + */ + ByteBufferPrototype.readIString = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 4 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength); + } + var temp = 0, + start = offset, + str; + temp = this.view.getUint32(offset, this.littleEndian); + offset += 4; + var k = offset + temp, + sd; + utfx.decodeUTF8toUTF16(function() { + return offset < k ? this.view.getUint8(offset++) : null; + }.bind(this), sd = stringDestination(), this.noAssert); + str = sd(); + if (relative) { + this.offset = offset; + return str; + } else { + return { + 'string': str, + 'length': offset - start + }; + } + }; + + // types/strings/utf8string + + /** + * Metrics representing number of UTF8 characters. Evaluates to `c`. + * @type {string} + * @const + * @expose + */ + ByteBuffer.METRICS_CHARS = 'c'; + + /** + * Metrics representing number of bytes. Evaluates to `b`. + * @type {string} + * @const + * @expose + */ + ByteBuffer.METRICS_BYTES = 'b'; + + /** + * Writes an UTF8 encoded string. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBufferPrototype.writeUTF8String = function(str, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var k; + var start = offset; + k = utfx.calculateUTF16asUTF8(stringSource(str))[1]; + offset += k; + var capacity14 = this.buffer.byteLength; + if (offset > capacity14) + this.resize((capacity14 *= 2) > offset ? capacity14 : offset); + offset -= k; + utfx.encodeUTF16toUTF8(stringSource(str), function(b) { + this.view.setUint8(offset++, b); + }.bind(this)); + if (relative) { + this.offset = offset; + return this; + } + return offset - start; + }; + + /** + * Writes an UTF8 encoded string. This is an alias of {@link ByteBuffer#writeUTF8String}. + * @function + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBufferPrototype.writeString = ByteBufferPrototype.writeUTF8String; + + /** + * Calculates the number of UTF8 characters of a string. JavaScript itself uses UTF-16, so that a string's + * `length` property does not reflect its actual UTF8 size if it contains code points larger than 0xFFFF. + * @function + * @param {string} str String to calculate + * @returns {number} Number of UTF8 characters + * @expose + */ + ByteBuffer.calculateUTF8Chars = function(str) { + return utfx.calculateUTF16asUTF8(stringSource(str))[0]; + }; + + /** + * Calculates the number of UTF8 bytes of a string. + * @function + * @param {string} str String to calculate + * @returns {number} Number of UTF8 bytes + * @expose + */ + ByteBuffer.calculateUTF8Bytes = function(str) { + return utfx.calculateUTF16asUTF8(stringSource(str))[1]; + }; + + /** + * Reads an UTF8 encoded string. + * @param {number} length Number of characters or bytes to read. + * @param {string=} metrics Metrics specifying what `length` is meant to count. Defaults to + * {@link ByteBuffer.METRICS_CHARS}. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @expose + */ + ByteBufferPrototype.readUTF8String = function(length, metrics, offset) { + if (typeof metrics === 'number') { + offset = metrics; + metrics = undefined; + } + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (typeof metrics === 'undefined') metrics = ByteBuffer.METRICS_CHARS; + if (!this.noAssert) { + if (typeof length !== 'number' || length % 1 !== 0) + throw TypeError("Illegal length: "+length+" (not an integer)"); + length |= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var i = 0, + start = offset, + sd; + if (metrics === ByteBuffer.METRICS_CHARS) { // The same for node and the browser + sd = stringDestination(); + utfx.decodeUTF8(function() { + return i < length && offset < this.limit ? this.view.getUint8(offset++) : null; + }.bind(this), function(cp) { + ++i; utfx.UTF8toUTF16(cp, sd); + }.bind(this)); + if (i !== length) + throw RangeError("Illegal range: Truncated data, "+i+" == "+length); + if (relative) { + this.offset = offset; + return sd(); + } else { + return { + "string": sd(), + "length": offset - start + }; + } + } else if (metrics === ByteBuffer.METRICS_BYTES) { + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + length > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+length+") <= "+this.buffer.byteLength); + } + var k = offset + length; + utfx.decodeUTF8toUTF16(function() { + return offset < k ? this.view.getUint8(offset++) : null; + }.bind(this), sd = stringDestination(), this.noAssert); + if (offset !== k) + throw RangeError("Illegal range: Truncated data, "+offset+" == "+k); + if (relative) { + this.offset = offset; + return sd(); + } else { + return { + 'string': sd(), + 'length': offset - start + }; + } + } else + throw TypeError("Unsupported metrics: "+metrics); + }; + + /** + * Reads an UTF8 encoded string. This is an alias of {@link ByteBuffer#readUTF8String}. + * @function + * @param {number} length Number of characters or bytes to read + * @param {number=} metrics Metrics specifying what `n` is meant to count. Defaults to + * {@link ByteBuffer.METRICS_CHARS}. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @expose + */ + ByteBufferPrototype.readString = ByteBufferPrototype.readUTF8String; + + // types/strings/vstring + + /** + * Writes a length as varint32 prefixed UTF8 encoded string. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} `this` if `offset` is omitted, else the actual number of bytes written + * @expose + * @see ByteBuffer#writeVarint32 + */ + ByteBufferPrototype.writeVString = function(str, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var start = offset, + k, l; + k = utfx.calculateUTF16asUTF8(stringSource(str), this.noAssert)[1]; + l = ByteBuffer.calculateVarint32(k); + offset += l+k; + var capacity15 = this.buffer.byteLength; + if (offset > capacity15) + this.resize((capacity15 *= 2) > offset ? capacity15 : offset); + offset -= l+k; + offset += this.writeVarint32(k, offset); + utfx.encodeUTF16toUTF8(stringSource(str), function(b) { + this.view.setUint8(offset++, b); + }.bind(this)); + if (offset !== start+k+l) + throw RangeError("Illegal range: Truncated data, "+offset+" == "+(offset+k+l)); + if (relative) { + this.offset = offset; + return this; + } + return offset - start; + }; + + /** + * Reads a length as varint32 prefixed UTF8 encoded string. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @expose + * @see ByteBuffer#readVarint32 + */ + ByteBufferPrototype.readVString = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + var temp = this.readVarint32(offset), + start = offset, + str; + offset += temp['length']; + temp = temp['value']; + var k = offset + temp, + sd = stringDestination(); + utfx.decodeUTF8toUTF16(function() { + return offset < k ? this.view.getUint8(offset++) : null; + }.bind(this), sd, this.noAssert); + str = sd(); + if (relative) { + this.offset = offset; + return str; + } else { + return { + 'string': str, + 'length': offset - start + }; + } + }; + + + /** + * Appends some data to this ByteBuffer. This will overwrite any contents behind the specified offset up to the appended + * data's length. + * @param {!ByteBuffer|!ArrayBuffer|!Uint8Array|string} source Data to append. If `source` is a ByteBuffer, its offsets + * will be modified according to the performed read operation. + * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8") + * @param {number=} offset Offset to append at. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {!ByteBuffer} this + * @expose + * @example A relative `<01 02>03.append(<04 05>)` will result in `<01 02 04 05>, 04 05|` + * @example An absolute `<01 02>03.append(04 05>, 1)` will result in `<01 04>05, 04 05|` + */ + ByteBufferPrototype.append = function(source, encoding, offset) { + if (typeof encoding === 'number' || typeof encoding !== 'string') { + offset = encoding; + encoding = undefined; + } + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + if (!(source instanceof ByteBuffer)) + source = ByteBuffer.wrap(source, encoding); + var length = source.limit - source.offset; + if (length <= 0) return this; // Nothing to append + offset += length; + var capacity16 = this.buffer.byteLength; + if (offset > capacity16) + this.resize((capacity16 *= 2) > offset ? capacity16 : offset); + offset -= length; + new Uint8Array(this.buffer, offset).set(new Uint8Array(source.buffer).subarray(source.offset, source.limit)); + source.offset += length; + if (relative) this.offset += length; + return this; + }; + + /** + * Appends this ByteBuffer's contents to another ByteBuffer. This will overwrite any contents behind the specified + * offset up to the length of this ByteBuffer's data. + * @param {!ByteBuffer} target Target ByteBuffer + * @param {number=} offset Offset to append to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {!ByteBuffer} this + * @expose + * @see ByteBuffer#append + */ + ByteBufferPrototype.appendTo = function(target, offset) { + target.append(this, offset); + return this; + }; + + /** + * Enables or disables assertions of argument types and offsets. Assertions are enabled by default but you can opt to + * disable them if your code already makes sure that everything is valid. + * @param {boolean} assert `true` to enable assertions, otherwise `false` + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.assert = function(assert) { + this.noAssert = !assert; + return this; + }; + + /** + * Gets the capacity of this ByteBuffer's backing buffer. + * @returns {number} Capacity of the backing buffer + * @expose + */ + ByteBufferPrototype.capacity = function() { + return this.buffer.byteLength; + }; + + /** + * Clears this ByteBuffer's offsets by setting {@link ByteBuffer#offset} to `0` and {@link ByteBuffer#limit} to the + * backing buffer's capacity. Discards {@link ByteBuffer#markedOffset}. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.clear = function() { + this.offset = 0; + this.limit = this.buffer.byteLength; + this.markedOffset = -1; + return this; + }; + + /** + * Creates a cloned instance of this ByteBuffer, preset with this ByteBuffer's values for {@link ByteBuffer#offset}, + * {@link ByteBuffer#markedOffset} and {@link ByteBuffer#limit}. + * @param {boolean=} copy Whether to copy the backing buffer or to return another view on the same, defaults to `false` + * @returns {!ByteBuffer} Cloned instance + * @expose + */ + ByteBufferPrototype.clone = function(copy) { + var bb = new ByteBuffer(0, this.littleEndian, this.noAssert); + if (copy) { + var buffer = new ArrayBuffer(this.buffer.byteLength); + new Uint8Array(buffer).set(this.buffer); + bb.buffer = buffer; + bb.view = new DataView(buffer); + } else { + bb.buffer = this.buffer; + bb.view = this.view; + } + bb.offset = this.offset; + bb.markedOffset = this.markedOffset; + bb.limit = this.limit; + return bb; + }; + + /** + * Compacts this ByteBuffer to be backed by a {@link ByteBuffer#buffer} of its contents' length. Contents are the bytes + * between {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will set `offset = 0` and `limit = capacity` and + * adapt {@link ByteBuffer#markedOffset} to the same relative position if set. + * @param {number=} begin Offset to start at, defaults to {@link ByteBuffer#offset} + * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit} + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.compact = function(begin, end) { + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + if (begin === 0 && end === this.buffer.byteLength) + return this; // Already compacted + var len = end - begin; + if (len === 0) { + this.buffer = EMPTY_BUFFER; + this.view = null; + if (this.markedOffset >= 0) this.markedOffset -= begin; + this.offset = 0; + this.limit = 0; + return this; + } + var buffer = new ArrayBuffer(len); + new Uint8Array(buffer).set(new Uint8Array(this.buffer).subarray(begin, end)); + this.buffer = buffer; + this.view = new DataView(buffer); + if (this.markedOffset >= 0) this.markedOffset -= begin; + this.offset = 0; + this.limit = len; + return this; + }; + + /** + * Creates a copy of this ByteBuffer's contents. Contents are the bytes between {@link ByteBuffer#offset} and + * {@link ByteBuffer#limit}. + * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}. + * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}. + * @returns {!ByteBuffer} Copy + * @expose + */ + ByteBufferPrototype.copy = function(begin, end) { + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + if (begin === end) + return new ByteBuffer(0, this.littleEndian, this.noAssert); + var capacity = end - begin, + bb = new ByteBuffer(capacity, this.littleEndian, this.noAssert); + bb.offset = 0; + bb.limit = capacity; + if (bb.markedOffset >= 0) bb.markedOffset -= begin; + this.copyTo(bb, 0, begin, end); + return bb; + }; + + /** + * Copies this ByteBuffer's contents to another ByteBuffer. Contents are the bytes between {@link ByteBuffer#offset} and + * {@link ByteBuffer#limit}. + * @param {!ByteBuffer} target Target ByteBuffer + * @param {number=} targetOffset Offset to copy to. Will use and increase the target's {@link ByteBuffer#offset} + * by the number of bytes copied if omitted. + * @param {number=} sourceOffset Offset to start copying from. Will use and increase {@link ByteBuffer#offset} by the + * number of bytes copied if omitted. + * @param {number=} sourceLimit Offset to end copying from, defaults to {@link ByteBuffer#limit} + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.copyTo = function(target, targetOffset, sourceOffset, sourceLimit) { + var relative, + targetRelative; + if (!this.noAssert) { + if (!ByteBuffer.isByteBuffer(target)) + throw TypeError("Illegal target: Not a ByteBuffer"); + } + targetOffset = (targetRelative = typeof targetOffset === 'undefined') ? target.offset : targetOffset | 0; + sourceOffset = (relative = typeof sourceOffset === 'undefined') ? this.offset : sourceOffset | 0; + sourceLimit = typeof sourceLimit === 'undefined' ? this.limit : sourceLimit | 0; + + if (targetOffset < 0 || targetOffset > target.buffer.byteLength) + throw RangeError("Illegal target range: 0 <= "+targetOffset+" <= "+target.buffer.byteLength); + if (sourceOffset < 0 || sourceLimit > this.buffer.byteLength) + throw RangeError("Illegal source range: 0 <= "+sourceOffset+" <= "+this.buffer.byteLength); + + var len = sourceLimit - sourceOffset; + if (len === 0) + return target; // Nothing to copy + + target.ensureCapacity(targetOffset + len); + + new Uint8Array(target.buffer).set(new Uint8Array(this.buffer).subarray(sourceOffset, sourceLimit), targetOffset); + + if (relative) this.offset += len; + if (targetRelative) target.offset += len; + + return this; + }; + + /** + * Makes sure that this ByteBuffer is backed by a {@link ByteBuffer#buffer} of at least the specified capacity. If the + * current capacity is exceeded, it will be doubled. If double the current capacity is less than the required capacity, + * the required capacity will be used instead. + * @param {number} capacity Required capacity + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.ensureCapacity = function(capacity) { + var current = this.buffer.byteLength; + if (current < capacity) + return this.resize((current *= 2) > capacity ? current : capacity); + return this; + }; + + /** + * Overwrites this ByteBuffer's contents with the specified value. Contents are the bytes between + * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. + * @param {number|string} value Byte value to fill with. If given as a string, the first character is used. + * @param {number=} begin Begin offset. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. defaults to {@link ByteBuffer#offset}. + * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}. + * @returns {!ByteBuffer} this + * @expose + * @example `someByteBuffer.clear().fill(0)` fills the entire backing buffer with zeroes + */ + ByteBufferPrototype.fill = function(value, begin, end) { + var relative = typeof begin === 'undefined'; + if (relative) begin = this.offset; + if (typeof value === 'string' && value.length > 0) + value = value.charCodeAt(0); + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value |= 0; + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + if (begin >= end) + return this; // Nothing to fill + while (begin < end) this.view.setUint8(begin++, value); + if (relative) this.offset = begin; + return this; + }; + + /** + * Makes this ByteBuffer ready for a new sequence of write or relative read operations. Sets `limit = offset` and + * `offset = 0`. Make sure always to flip a ByteBuffer when all relative read or write operations are complete. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.flip = function() { + this.limit = this.offset; + this.offset = 0; + return this; + }; + /** + * Marks an offset on this ByteBuffer to be used later. + * @param {number=} offset Offset to mark. Defaults to {@link ByteBuffer#offset}. + * @returns {!ByteBuffer} this + * @throws {TypeError} If `offset` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @see ByteBuffer#reset + * @expose + */ + ByteBufferPrototype.mark = function(offset) { + offset = typeof offset === 'undefined' ? this.offset : offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + this.markedOffset = offset; + return this; + }; + /** + * Sets the byte order. + * @param {boolean} littleEndian `true` for little endian byte order, `false` for big endian + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.order = function(littleEndian) { + if (!this.noAssert) { + if (typeof littleEndian !== 'boolean') + throw TypeError("Illegal littleEndian: Not a boolean"); + } + this.littleEndian = !!littleEndian; + return this; + }; + + /** + * Switches (to) little endian byte order. + * @param {boolean=} littleEndian Defaults to `true`, otherwise uses big endian + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.LE = function(littleEndian) { + this.littleEndian = typeof littleEndian !== 'undefined' ? !!littleEndian : true; + return this; + }; + + /** + * Switches (to) big endian byte order. + * @param {boolean=} bigEndian Defaults to `true`, otherwise uses little endian + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.BE = function(bigEndian) { + this.littleEndian = typeof bigEndian !== 'undefined' ? !bigEndian : false; + return this; + }; + /** + * Prepends some data to this ByteBuffer. This will overwrite any contents before the specified offset up to the + * prepended data's length. If there is not enough space available before the specified `offset`, the backing buffer + * will be resized and its contents moved accordingly. + * @param {!ByteBuffer|string|!ArrayBuffer} source Data to prepend. If `source` is a ByteBuffer, its offset will be + * modified according to the performed read operation. + * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8") + * @param {number=} offset Offset to prepend at. Will use and decrease {@link ByteBuffer#offset} by the number of bytes + * prepended if omitted. + * @returns {!ByteBuffer} this + * @expose + * @example A relative `00<01 02 03>.prepend(<04 05>)` results in `<04 05 01 02 03>, 04 05|` + * @example An absolute `00<01 02 03>.prepend(<04 05>, 2)` results in `04<05 02 03>, 04 05|` + */ + ByteBufferPrototype.prepend = function(source, encoding, offset) { + if (typeof encoding === 'number' || typeof encoding !== 'string') { + offset = encoding; + encoding = undefined; + } + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + if (!(source instanceof ByteBuffer)) + source = ByteBuffer.wrap(source, encoding); + var len = source.limit - source.offset; + if (len <= 0) return this; // Nothing to prepend + var diff = len - offset; + var arrayView; + if (diff > 0) { // Not enough space before offset, so resize + move + var buffer = new ArrayBuffer(this.buffer.byteLength + diff); + arrayView = new Uint8Array(buffer); + arrayView.set(new Uint8Array(this.buffer).subarray(offset, this.buffer.byteLength), len); + this.buffer = buffer; + this.view = new DataView(buffer); + this.offset += diff; + if (this.markedOffset >= 0) this.markedOffset += diff; + this.limit += diff; + offset += diff; + } else { + arrayView = new Uint8Array(this.buffer); + } + arrayView.set(new Uint8Array(source.buffer).subarray(source.offset, source.limit), offset - len); + source.offset = source.limit; + if (relative) + this.offset -= len; + return this; + }; + + /** + * Prepends this ByteBuffer to another ByteBuffer. This will overwrite any contents before the specified offset up to the + * prepended data's length. If there is not enough space available before the specified `offset`, the backing buffer + * will be resized and its contents moved accordingly. + * @param {!ByteBuffer} target Target ByteBuffer + * @param {number=} offset Offset to prepend at. Will use and decrease {@link ByteBuffer#offset} by the number of bytes + * prepended if omitted. + * @returns {!ByteBuffer} this + * @expose + * @see ByteBuffer#prepend + */ + ByteBufferPrototype.prependTo = function(target, offset) { + target.prepend(this, offset); + return this; + }; + /** + * Prints debug information about this ByteBuffer's contents. + * @param {function(string)=} out Output function to call, defaults to console.log + * @expose + */ + ByteBufferPrototype.printDebug = function(out) { + if (typeof out !== 'function') out = console.log.bind(console); + out( + this.toString()+"\n"+ + "-------------------------------------------------------------------\n"+ + this.toDebug(/* columns */ true) + ); + }; + + /** + * Gets the number of remaining readable bytes. Contents are the bytes between {@link ByteBuffer#offset} and + * {@link ByteBuffer#limit}, so this returns `limit - offset`. + * @returns {number} Remaining readable bytes. May be negative if `offset > limit`. + * @expose + */ + ByteBufferPrototype.remaining = function() { + return this.limit - this.offset; + }; + /** + * Resets this ByteBuffer's {@link ByteBuffer#offset}. If an offset has been marked through {@link ByteBuffer#mark} + * before, `offset` will be set to {@link ByteBuffer#markedOffset}, which will then be discarded. If no offset has been + * marked, sets `offset = 0`. + * @returns {!ByteBuffer} this + * @see ByteBuffer#mark + * @expose + */ + ByteBufferPrototype.reset = function() { + if (this.markedOffset >= 0) { + this.offset = this.markedOffset; + this.markedOffset = -1; + } else { + this.offset = 0; + } + return this; + }; + /** + * Resizes this ByteBuffer to be backed by a buffer of at least the given capacity. Will do nothing if already that + * large or larger. + * @param {number} capacity Capacity required + * @returns {!ByteBuffer} this + * @throws {TypeError} If `capacity` is not a number + * @throws {RangeError} If `capacity < 0` + * @expose + */ + ByteBufferPrototype.resize = function(capacity) { + if (!this.noAssert) { + if (typeof capacity !== 'number' || capacity % 1 !== 0) + throw TypeError("Illegal capacity: "+capacity+" (not an integer)"); + capacity |= 0; + if (capacity < 0) + throw RangeError("Illegal capacity: 0 <= "+capacity); + } + if (this.buffer.byteLength < capacity) { + var buffer = new ArrayBuffer(capacity); + new Uint8Array(buffer).set(new Uint8Array(this.buffer)); + this.buffer = buffer; + this.view = new DataView(buffer); + } + return this; + }; + /** + * Reverses this ByteBuffer's contents. + * @param {number=} begin Offset to start at, defaults to {@link ByteBuffer#offset} + * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit} + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.reverse = function(begin, end) { + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + if (begin === end) + return this; // Nothing to reverse + Array.prototype.reverse.call(new Uint8Array(this.buffer).subarray(begin, end)); + this.view = new DataView(this.buffer); // FIXME: Why exactly is this necessary? + return this; + }; + /** + * Skips the next `length` bytes. This will just advance + * @param {number} length Number of bytes to skip. May also be negative to move the offset back. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.skip = function(length) { + if (!this.noAssert) { + if (typeof length !== 'number' || length % 1 !== 0) + throw TypeError("Illegal length: "+length+" (not an integer)"); + length |= 0; + } + var offset = this.offset + length; + if (!this.noAssert) { + if (offset < 0 || offset > this.buffer.byteLength) + throw RangeError("Illegal length: 0 <= "+this.offset+" + "+length+" <= "+this.buffer.byteLength); + } + this.offset = offset; + return this; + }; + + /** + * Slices this ByteBuffer by creating a cloned instance with `offset = begin` and `limit = end`. + * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}. + * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}. + * @returns {!ByteBuffer} Clone of this ByteBuffer with slicing applied, backed by the same {@link ByteBuffer#buffer} + * @expose + */ + ByteBufferPrototype.slice = function(begin, end) { + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + var bb = this.clone(); + bb.offset = begin; + bb.limit = end; + return bb; + }; + /** + * Returns a copy of the backing buffer that contains this ByteBuffer's contents. Contents are the bytes between + * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will transparently {@link ByteBuffer#flip} this + * ByteBuffer if `offset > limit` but the actual offsets remain untouched. + * @param {boolean=} forceCopy If `true` returns a copy, otherwise returns a view referencing the same memory if + * possible. Defaults to `false` + * @returns {!ArrayBuffer} Contents as an ArrayBuffer + * @expose + */ + ByteBufferPrototype.toBuffer = function(forceCopy) { + var offset = this.offset, + limit = this.limit; + if (offset > limit) { + var t = offset; + offset = limit; + limit = t; + } + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: Not an integer"); + offset >>>= 0; + if (typeof limit !== 'number' || limit % 1 !== 0) + throw TypeError("Illegal limit: Not an integer"); + limit >>>= 0; + if (offset < 0 || offset > limit || limit > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+offset+" <= "+limit+" <= "+this.buffer.byteLength); + } + // NOTE: It's not possible to have another ArrayBuffer reference the same memory as the backing buffer. This is + // possible with Uint8Array#subarray only, but we have to return an ArrayBuffer by contract. So: + if (!forceCopy && offset === 0 && limit === this.buffer.byteLength) { + return this.buffer; + } + if (offset === limit) { + return EMPTY_BUFFER; + } + var buffer = new ArrayBuffer(limit - offset); + new Uint8Array(buffer).set(new Uint8Array(this.buffer).subarray(offset, limit), 0); + return buffer; + }; + + /** + * Returns a raw buffer compacted to contain this ByteBuffer's contents. Contents are the bytes between + * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will transparently {@link ByteBuffer#flip} this + * ByteBuffer if `offset > limit` but the actual offsets remain untouched. This is an alias of + * {@link ByteBuffer#toBuffer}. + * @function + * @param {boolean=} forceCopy If `true` returns a copy, otherwise returns a view referencing the same memory. + * Defaults to `false` + * @returns {!ArrayBuffer} Contents as an ArrayBuffer + * @expose + */ + ByteBufferPrototype.toArrayBuffer = ByteBufferPrototype.toBuffer; + + + /** + * Converts the ByteBuffer's contents to a string. + * @param {string=} encoding Output encoding. Returns an informative string representation if omitted but also allows + * direct conversion to "utf8", "hex", "base64" and "binary" encoding. "debug" returns a hex representation with + * highlighted offsets. + * @param {number=} begin Offset to begin at, defaults to {@link ByteBuffer#offset} + * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit} + * @returns {string} String representation + * @throws {Error} If `encoding` is invalid + * @expose + */ + ByteBufferPrototype.toString = function(encoding, begin, end) { + if (typeof encoding === 'undefined') + return "ByteBufferAB(offset="+this.offset+",markedOffset="+this.markedOffset+",limit="+this.limit+",capacity="+this.capacity()+")"; + if (typeof encoding === 'number') + encoding = "utf8", + begin = encoding, + end = begin; + switch (encoding) { + case "utf8": + return this.toUTF8(begin, end); + case "base64": + return this.toBase64(begin, end); + case "hex": + return this.toHex(begin, end); + case "binary": + return this.toBinary(begin, end); + case "debug": + return this.toDebug(); + case "columns": + return this.toColumns(); + default: + throw Error("Unsupported encoding: "+encoding); + } + }; + + // lxiv-embeddable + + /** + * lxiv-embeddable (c) 2014 Daniel Wirtz + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/lxiv for details + */ + var lxiv = function() { + "use strict"; + + /** + * lxiv namespace. + * @type {!Object.} + * @exports lxiv + */ + var lxiv = {}; + + /** + * Character codes for output. + * @type {!Array.} + * @inner + */ + var aout = [ + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47 + ]; + + /** + * Character codes for input. + * @type {!Array.} + * @inner + */ + var ain = []; + for (var i=0, k=aout.length; i>2)&0x3f]); + t = (b&0x3)<<4; + if ((b = src()) !== null) { + t |= (b>>4)&0xf; + dst(aout[(t|((b>>4)&0xf))&0x3f]); + t = (b&0xf)<<2; + if ((b = src()) !== null) + dst(aout[(t|((b>>6)&0x3))&0x3f]), + dst(aout[b&0x3f]); + else + dst(aout[t&0x3f]), + dst(61); + } else + dst(aout[t&0x3f]), + dst(61), + dst(61); + } + }; + + /** + * Decodes base64 char codes to bytes. + * @param {!function():number|null} src Characters source as a function returning the next char code respectively + * `null` if there are no more characters left. + * @param {!function(number)} dst Bytes destination as a function successively called with the next byte. + * @throws {Error} If a character code is invalid + */ + lxiv.decode = function(src, dst) { + var c, t1, t2; + function fail(c) { + throw Error("Illegal character code: "+c); + } + while ((c = src()) !== null) { + t1 = ain[c]; + if (typeof t1 === 'undefined') fail(c); + if ((c = src()) !== null) { + t2 = ain[c]; + if (typeof t2 === 'undefined') fail(c); + dst((t1<<2)>>>0|(t2&0x30)>>4); + if ((c = src()) !== null) { + t1 = ain[c]; + if (typeof t1 === 'undefined') + if (c === 61) break; else fail(c); + dst(((t2&0xf)<<4)>>>0|(t1&0x3c)>>2); + if ((c = src()) !== null) { + t2 = ain[c]; + if (typeof t2 === 'undefined') + if (c === 61) break; else fail(c); + dst(((t1&0x3)<<6)>>>0|t2); + } + } + } + } + }; + + /** + * Tests if a string is valid base64. + * @param {string} str String to test + * @returns {boolean} `true` if valid, otherwise `false` + */ + lxiv.test = function(str) { + return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(str); + }; + + return lxiv; + }(); + + // encodings/base64 + + /** + * Encodes this ByteBuffer's contents to a base64 encoded string. + * @param {number=} begin Offset to begin at, defaults to {@link ByteBuffer#offset}. + * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit}. + * @returns {string} Base64 encoded string + * @expose + */ + ByteBufferPrototype.toBase64 = function(begin, end) { + if (typeof begin === 'undefined') + begin = this.offset; + if (typeof end === 'undefined') + end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + var sd; lxiv.encode(function() { + return begin < end ? this.view.getUint8(begin++) : null; + }.bind(this), sd = stringDestination()); + return sd(); + }; + + /** + * Decodes a base64 encoded string to a ByteBuffer. + * @param {string} str String to decode + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} ByteBuffer + * @expose + */ + ByteBuffer.fromBase64 = function(str, littleEndian, noAssert) { + if (!noAssert) { + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + if (str.length % 4 !== 0) + throw TypeError("Illegal str: Length not a multiple of 4"); + } + var bb = new ByteBuffer(str.length/4*3, littleEndian, noAssert), + i = 0; + lxiv.decode(stringSource(str), function(b) { + bb.view.setUint8(i++, b); + }); + bb.limit = i; + return bb; + }; + + /** + * Encodes a binary string to base64 like `window.btoa` does. + * @param {string} str Binary string + * @returns {string} Base64 encoded string + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.btoa + * @expose + */ + ByteBuffer.btoa = function(str) { + return ByteBuffer.fromBinary(str).toBase64(); + }; + + /** + * Decodes a base64 encoded string to binary like `window.atob` does. + * @param {string} b64 Base64 encoded string + * @returns {string} Binary string + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.atob + * @expose + */ + ByteBuffer.atob = function(b64) { + return ByteBuffer.fromBase64(b64).toBinary(); + }; + + // encodings/binary + + /** + * Encodes this ByteBuffer to a binary encoded string, that is using only characters 0x00-0xFF as bytes. + * @param {number=} begin Offset to begin at. Defaults to {@link ByteBuffer#offset}. + * @param {number=} end Offset to end at. Defaults to {@link ByteBuffer#limit}. + * @returns {string} Binary encoded string + * @throws {RangeError} If `offset > limit` + * @expose + */ + ByteBufferPrototype.toBinary = function(begin, end) { + begin = typeof begin === 'undefined' ? this.offset : begin; + end = typeof end === 'undefined' ? this.limit : end; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + if (begin === end) + return ""; + var cc = [], pt = []; + while (begin < end) { + cc.push(this.view.getUint8(begin++)); + if (cc.length >= 1024) + pt.push(String.fromCharCode.apply(String, cc)), + cc = []; + } + return pt.join('') + String.fromCharCode.apply(String, cc); + }; + + /** + * Decodes a binary encoded string, that is using only characters 0x00-0xFF as bytes, to a ByteBuffer. + * @param {string} str String to decode + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} ByteBuffer + * @expose + */ + ByteBuffer.fromBinary = function(str, littleEndian, noAssert) { + if (!noAssert) { + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + } + var i = 0, k = str.length, charCode, + bb = new ByteBuffer(k, littleEndian, noAssert); + while (i 255) + throw RangeError("Illegal charCode at "+i+": 0 <= "+charCode+" <= 255"); + bb.view.setUint8(i++, charCode); + } + bb.limit = k; + return bb; + }; + + // encodings/debug + + /** + * Encodes this ByteBuffer to a hex encoded string with marked offsets. Offset symbols are: + * * `<` : offset, + * * `'` : markedOffset, + * * `>` : limit, + * * `|` : offset and limit, + * * `[` : offset and markedOffset, + * * `]` : markedOffset and limit, + * * `!` : offset, markedOffset and limit + * @param {boolean=} columns If `true` returns two columns hex + ascii, defaults to `false` + * @returns {string|!Array.} Debug string or array of lines if `asArray = true` + * @expose + * @example `>00'01 02<03` contains four bytes with `limit=0, markedOffset=1, offset=3` + * @example `00[01 02 03>` contains four bytes with `offset=markedOffset=1, limit=4` + * @example `00|01 02 03` contains four bytes with `offset=limit=1, markedOffset=-1` + * @example `|` contains zero bytes with `offset=limit=0, markedOffset=-1` + */ + ByteBufferPrototype.toDebug = function(columns) { + var i = -1, + k = this.buffer.byteLength, + b, + hex = "", + asc = "", + out = ""; + while (i 32 && b < 127 ? String.fromCharCode(b) : '.'; + } + } + ++i; + if (columns) { + if (i > 0 && i % 16 === 0 && i !== k) { + while (hex.length < 3*16+3) hex += " "; + out += hex+asc+"\n"; + hex = asc = ""; + } + } + if (i === this.offset && i === this.limit) + hex += i === this.markedOffset ? "!" : "|"; + else if (i === this.offset) + hex += i === this.markedOffset ? "[" : "<"; + else if (i === this.limit) + hex += i === this.markedOffset ? "]" : ">"; + else + hex += i === this.markedOffset ? "'" : (columns || (i !== 0 && i !== k) ? " " : ""); + } + if (columns && hex !== " ") { + while (hex.length < 3*16+3) hex += " "; + out += hex+asc+"\n"; + } + return columns ? out : hex; + }; + + /** + * Decodes a hex encoded string with marked offsets to a ByteBuffer. + * @param {string} str Debug string to decode (not be generated with `columns = true`) + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} ByteBuffer + * @expose + * @see ByteBuffer#toDebug + */ + ByteBuffer.fromDebug = function(str, littleEndian, noAssert) { + var k = str.length, + bb = new ByteBuffer(((k+1)/3)|0, littleEndian, noAssert); + var i = 0, j = 0, ch, b, + rs = false, // Require symbol next + ho = false, hm = false, hl = false, // Already has offset, markedOffset, limit? + fail = false; + while (i': + if (!noAssert) { + if (hl) { + fail = true; break; + } + hl = true; + } + bb.limit = j; + rs = false; + break; + case "'": + if (!noAssert) { + if (hm) { + fail = true; break; + } + hm = true; + } + bb.markedOffset = j; + rs = false; + break; + case ' ': + rs = false; + break; + default: + if (!noAssert) { + if (rs) { + fail = true; break; + } + } + b = parseInt(ch+str.charAt(i++), 16); + if (!noAssert) { + if (isNaN(b) || b < 0 || b > 255) + throw TypeError("Illegal str: Not a debug encoded string"); + } + bb.view.setUint8(j++, b); + rs = true; + } + if (fail) + throw TypeError("Illegal str: Invalid symbol at "+i); + } + if (!noAssert) { + if (!ho || !hl) + throw TypeError("Illegal str: Missing offset or limit"); + if (j>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + var out = new Array(end - begin), + b; + while (begin < end) { + b = this.view.getUint8(begin++); + if (b < 0x10) + out.push("0", b.toString(16)); + else out.push(b.toString(16)); + } + return out.join(''); + }; + + /** + * Decodes a hex encoded string to a ByteBuffer. + * @param {string} str String to decode + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} ByteBuffer + * @expose + */ + ByteBuffer.fromHex = function(str, littleEndian, noAssert) { + if (!noAssert) { + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + if (str.length % 2 !== 0) + throw TypeError("Illegal str: Length not a multiple of 2"); + } + var k = str.length, + bb = new ByteBuffer((k / 2) | 0, littleEndian), + b; + for (var i=0, j=0; i 255) + throw TypeError("Illegal str: Contains non-hex characters"); + bb.view.setUint8(j++, b); + } + bb.limit = j; + return bb; + }; + + // utfx-embeddable + + /** + * utfx-embeddable (c) 2014 Daniel Wirtz + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/utfx for details + */ + var utfx = function() { + "use strict"; + + /** + * utfx namespace. + * @inner + * @type {!Object.} + */ + var utfx = {}; + + /** + * Maximum valid code point. + * @type {number} + * @const + */ + utfx.MAX_CODEPOINT = 0x10FFFF; + + /** + * Encodes UTF8 code points to UTF8 bytes. + * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point + * respectively `null` if there are no more code points left or a single numeric code point. + * @param {!function(number)} dst Bytes destination as a function successively called with the next byte + */ + utfx.encodeUTF8 = function(src, dst) { + var cp = null; + if (typeof src === 'number') + cp = src, + src = function() { return null; }; + while (cp !== null || (cp = src()) !== null) { + if (cp < 0x80) + dst(cp&0x7F); + else if (cp < 0x800) + dst(((cp>>6)&0x1F)|0xC0), + dst((cp&0x3F)|0x80); + else if (cp < 0x10000) + dst(((cp>>12)&0x0F)|0xE0), + dst(((cp>>6)&0x3F)|0x80), + dst((cp&0x3F)|0x80); + else + dst(((cp>>18)&0x07)|0xF0), + dst(((cp>>12)&0x3F)|0x80), + dst(((cp>>6)&0x3F)|0x80), + dst((cp&0x3F)|0x80); + cp = null; + } + }; + + /** + * Decodes UTF8 bytes to UTF8 code points. + * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there + * are no more bytes left. + * @param {!function(number)} dst Code points destination as a function successively called with each decoded code point. + * @throws {RangeError} If a starting byte is invalid in UTF8 + * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the + * remaining bytes. + */ + utfx.decodeUTF8 = function(src, dst) { + var a, b, c, d, fail = function(b) { + b = b.slice(0, b.indexOf(null)); + var err = Error(b.toString()); + err.name = "TruncatedError"; + err['bytes'] = b; + throw err; + }; + while ((a = src()) !== null) { + if ((a&0x80) === 0) + dst(a); + else if ((a&0xE0) === 0xC0) + ((b = src()) === null) && fail([a, b]), + dst(((a&0x1F)<<6) | (b&0x3F)); + else if ((a&0xF0) === 0xE0) + ((b=src()) === null || (c=src()) === null) && fail([a, b, c]), + dst(((a&0x0F)<<12) | ((b&0x3F)<<6) | (c&0x3F)); + else if ((a&0xF8) === 0xF0) + ((b=src()) === null || (c=src()) === null || (d=src()) === null) && fail([a, b, c ,d]), + dst(((a&0x07)<<18) | ((b&0x3F)<<12) | ((c&0x3F)<<6) | (d&0x3F)); + else throw RangeError("Illegal starting byte: "+a); + } + }; + + /** + * Converts UTF16 characters to UTF8 code points. + * @param {!function():number|null} src Characters source as a function returning the next char code respectively + * `null` if there are no more characters left. + * @param {!function(number)} dst Code points destination as a function successively called with each converted code + * point. + */ + utfx.UTF16toUTF8 = function(src, dst) { + var c1, c2 = null; + while (true) { + if ((c1 = c2 !== null ? c2 : src()) === null) + break; + if (c1 >= 0xD800 && c1 <= 0xDFFF) { + if ((c2 = src()) !== null) { + if (c2 >= 0xDC00 && c2 <= 0xDFFF) { + dst((c1-0xD800)*0x400+c2-0xDC00+0x10000); + c2 = null; continue; + } + } + } + dst(c1); + } + if (c2 !== null) dst(c2); + }; + + /** + * Converts UTF8 code points to UTF16 characters. + * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point + * respectively `null` if there are no more code points left or a single numeric code point. + * @param {!function(number)} dst Characters destination as a function successively called with each converted char code. + * @throws {RangeError} If a code point is out of range + */ + utfx.UTF8toUTF16 = function(src, dst) { + var cp = null; + if (typeof src === 'number') + cp = src, src = function() { return null; }; + while (cp !== null || (cp = src()) !== null) { + if (cp <= 0xFFFF) + dst(cp); + else + cp -= 0x10000, + dst((cp>>10)+0xD800), + dst((cp%0x400)+0xDC00); + cp = null; + } + }; + + /** + * Converts and encodes UTF16 characters to UTF8 bytes. + * @param {!function():number|null} src Characters source as a function returning the next char code respectively `null` + * if there are no more characters left. + * @param {!function(number)} dst Bytes destination as a function successively called with the next byte. + */ + utfx.encodeUTF16toUTF8 = function(src, dst) { + utfx.UTF16toUTF8(src, function(cp) { + utfx.encodeUTF8(cp, dst); + }); + }; + + /** + * Decodes and converts UTF8 bytes to UTF16 characters. + * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there + * are no more bytes left. + * @param {!function(number)} dst Characters destination as a function successively called with each converted char code. + * @throws {RangeError} If a starting byte is invalid in UTF8 + * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the remaining bytes. + */ + utfx.decodeUTF8toUTF16 = function(src, dst) { + utfx.decodeUTF8(src, function(cp) { + utfx.UTF8toUTF16(cp, dst); + }); + }; + + /** + * Calculates the byte length of an UTF8 code point. + * @param {number} cp UTF8 code point + * @returns {number} Byte length + */ + utfx.calculateCodePoint = function(cp) { + return (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4; + }; + + /** + * Calculates the number of UTF8 bytes required to store UTF8 code points. + * @param {(!function():number|null)} src Code points source as a function returning the next code point respectively + * `null` if there are no more code points left. + * @returns {number} The number of UTF8 bytes required + */ + utfx.calculateUTF8 = function(src) { + var cp, l=0; + while ((cp = src()) !== null) + l += utfx.calculateCodePoint(cp); + return l; + }; + + /** + * Calculates the number of UTF8 code points respectively UTF8 bytes required to store UTF16 char codes. + * @param {(!function():number|null)} src Characters source as a function returning the next char code respectively + * `null` if there are no more characters left. + * @returns {!Array.} The number of UTF8 code points at index 0 and the number of UTF8 bytes required at index 1. + */ + utfx.calculateUTF16asUTF8 = function(src) { + var n=0, l=0; + utfx.UTF16toUTF8(src, function(cp) { + ++n; l += utfx.calculateCodePoint(cp); + }); + return [n,l]; + }; + + return utfx; + }(); + + // encodings/utf8 + + /** + * Encodes this ByteBuffer's contents between {@link ByteBuffer#offset} and {@link ByteBuffer#limit} to an UTF8 encoded + * string. + * @returns {string} Hex encoded string + * @throws {RangeError} If `offset > limit` + * @expose + */ + ByteBufferPrototype.toUTF8 = function(begin, end) { + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + var sd; try { + utfx.decodeUTF8toUTF16(function() { + return begin < end ? this.view.getUint8(begin++) : null; + }.bind(this), sd = stringDestination()); + } catch (e) { + if (begin !== end) + throw RangeError("Illegal range: Truncated data, "+begin+" != "+end); + } + return sd(); + }; + + /** + * Decodes an UTF8 encoded string to a ByteBuffer. + * @param {string} str String to decode + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} ByteBuffer + * @expose + */ + ByteBuffer.fromUTF8 = function(str, littleEndian, noAssert) { + if (!noAssert) + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + var bb = new ByteBuffer(utfx.calculateUTF16asUTF8(stringSource(str), true)[1], littleEndian, noAssert), + i = 0; + utfx.encodeUTF16toUTF8(stringSource(str), function(b) { + bb.view.setUint8(i++, b); + }); + bb.limit = i; + return bb; + }; + + + return ByteBuffer; + } + + /* CommonJS */ if (typeof require === 'function' && typeof module === 'object' && module && typeof exports === 'object' && exports) + module['exports'] = (function() { + var Long; try { Long = require("long"); } catch (e) {} + return loadByteBuffer(Long); + })(); + /* AMD */ else if (typeof define === 'function' && define["amd"]) + define("ByteBuffer", ["Long"], function(Long) { return loadByteBuffer(Long); }); + /* Global */ else + (global["dcodeIO"] = global["dcodeIO"] || {})["ByteBuffer"] = loadByteBuffer(global["dcodeIO"]["Long"]); + +})(this); + +},{"long":3}],2:[function(require,module,exports){ +/* + Copyright 2013 Daniel Wirtz + Copyright 2009 The Closure Library Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS-IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/** + * @license Long.js (c) 2013 Daniel Wirtz + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/Long.js for details + */ +(function(global) { + "use strict"; + + /** + * Constructs a 64 bit two's-complement integer, given its low and high 32 bit values as *signed* integers. + * See the from* functions below for more convenient ways of constructing Longs. + * @exports Long + * @class A Long class for representing a 64 bit two's-complement integer value. + * @param {number} low The low (signed) 32 bits of the long + * @param {number} high The high (signed) 32 bits of the long + * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed + * @constructor + */ + var Long = function(low, high, unsigned) { + + /** + * The low 32 bits as a signed value. + * @type {number} + * @expose + */ + this.low = low|0; + + /** + * The high 32 bits as a signed value. + * @type {number} + * @expose + */ + this.high = high|0; + + /** + * Whether unsigned or not. + * @type {boolean} + * @expose + */ + this.unsigned = !!unsigned; + }; + + // The internal representation of a long is the two given signed, 32-bit values. + // We use 32-bit pieces because these are the size of integers on which + // Javascript performs bit-operations. For operations like addition and + // multiplication, we split each number into 16 bit pieces, which can easily be + // multiplied within Javascript's floating-point representation without overflow + // or change in sign. + // + // In the algorithms below, we frequently reduce the negative case to the + // positive case by negating the input(s) and then post-processing the result. + // Note that we must ALWAYS check specially whether those values are MIN_VALUE + // (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as + // a positive number, it overflows back into a negative). Not handling this + // case would often result in infinite recursion. + // + // Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the from* + // methods on which they depend. + + /** + * Tests if the specified object is a Long. + * @param {*} obj Object + * @returns {boolean} + * @expose + */ + Long.isLong = function(obj) { + return (obj && obj instanceof Long) === true; + }; + + /** + * A cache of the Long representations of small integer values. + * @type {!Object} + * @inner + */ + var INT_CACHE = {}; + + /** + * A cache of the Long representations of small unsigned integer values. + * @type {!Object} + * @inner + */ + var UINT_CACHE = {}; + + /** + * Returns a Long representing the given 32 bit integer value. + * @param {number} value The 32 bit integer in question + * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed + * @returns {!Long} The corresponding Long value + * @expose + */ + Long.fromInt = function(value, unsigned) { + var obj, cachedObj; + if (!unsigned) { + value = value | 0; + if (-128 <= value && value < 128) { + cachedObj = INT_CACHE[value]; + if (cachedObj) + return cachedObj; + } + obj = new Long(value, value < 0 ? -1 : 0, false); + if (-128 <= value && value < 128) + INT_CACHE[value] = obj; + return obj; + } else { + value = value >>> 0; + if (0 <= value && value < 256) { + cachedObj = UINT_CACHE[value]; + if (cachedObj) + return cachedObj; + } + obj = new Long(value, (value | 0) < 0 ? -1 : 0, true); + if (0 <= value && value < 256) + UINT_CACHE[value] = obj; + return obj; + } + }; + + /** + * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. + * @param {number} value The number in question + * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed + * @returns {!Long} The corresponding Long value + * @expose + */ + Long.fromNumber = function(value, unsigned) { + unsigned = !!unsigned; + if (isNaN(value) || !isFinite(value)) + return Long.ZERO; + if (!unsigned && value <= -TWO_PWR_63_DBL) + return Long.MIN_VALUE; + if (!unsigned && value + 1 >= TWO_PWR_63_DBL) + return Long.MAX_VALUE; + if (unsigned && value >= TWO_PWR_64_DBL) + return Long.MAX_UNSIGNED_VALUE; + if (value < 0) + return Long.fromNumber(-value, unsigned).negate(); + return new Long((value % TWO_PWR_32_DBL) | 0, (value / TWO_PWR_32_DBL) | 0, unsigned); + }; + + /** + * Returns a Long representing the 64 bit integer that comes by concatenating the given low and high bits. Each is + * assumed to use 32 bits. + * @param {number} lowBits The low 32 bits + * @param {number} highBits The high 32 bits + * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed + * @returns {!Long} The corresponding Long value + * @expose + */ + Long.fromBits = function(lowBits, highBits, unsigned) { + return new Long(lowBits, highBits, unsigned); + }; + + /** + * Returns a Long representation of the given string, written using the specified radix. + * @param {string} str The textual representation of the Long + * @param {(boolean|number)=} unsigned Whether unsigned or not, defaults to `false` for signed + * @param {number=} radix The radix in which the text is written (2-36), defaults to 10 + * @returns {!Long} The corresponding Long value + * @expose + */ + Long.fromString = function(str, unsigned, radix) { + if (str.length === 0) + throw Error('number format error: empty string'); + if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity") + return Long.ZERO; + if (typeof unsigned === 'number') // For goog.math.long compatibility + radix = unsigned, + unsigned = false; + radix = radix || 10; + if (radix < 2 || 36 < radix) + throw Error('radix out of range: ' + radix); + + var p; + if ((p = str.indexOf('-')) > 0) + throw Error('number format error: interior "-" character: ' + str); + else if (p === 0) + return Long.fromString(str.substring(1), unsigned, radix).negate(); + + // Do several (8) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = Long.fromNumber(Math.pow(radix, 8)); + + var result = Long.ZERO; + for (var i = 0; i < str.length; i += 8) { + var size = Math.min(8, str.length - i); + var value = parseInt(str.substring(i, i + size), radix); + if (size < 8) { + var power = Long.fromNumber(Math.pow(radix, size)); + result = result.multiply(power).add(Long.fromNumber(value)); + } else { + result = result.multiply(radixToPower); + result = result.add(Long.fromNumber(value)); + } + } + result.unsigned = unsigned; + return result; + }; + + /** + * Converts the specified value to a Long. + * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value + * @returns {!Long} + * @expose + */ + Long.fromValue = function(val) { + if (typeof val === 'number') + return Long.fromNumber(val); + if (typeof val === 'string') + return Long.fromString(val); + if (Long.isLong(val)) + return val; + // Throws for not an object (undefined, null): + return new Long(val.low, val.high, val.unsigned); + }; + + // NOTE: the compiler should inline these constant values below and then remove these variables, so there should be + // no runtime penalty for these. + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_16_DBL = 1 << 16; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_24_DBL = 1 << 24; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_63_DBL = TWO_PWR_64_DBL / 2; + + /** + * @type {!Long} + * @const + * @inner + */ + var TWO_PWR_24 = Long.fromInt(TWO_PWR_24_DBL); + + /** + * Signed zero. + * @type {!Long} + * @expose + */ + Long.ZERO = Long.fromInt(0); + + /** + * Unsigned zero. + * @type {!Long} + * @expose + */ + Long.UZERO = Long.fromInt(0, true); + + /** + * Signed one. + * @type {!Long} + * @expose + */ + Long.ONE = Long.fromInt(1); + + /** + * Unsigned one. + * @type {!Long} + * @expose + */ + Long.UONE = Long.fromInt(1, true); + + /** + * Signed negative one. + * @type {!Long} + * @expose + */ + Long.NEG_ONE = Long.fromInt(-1); + + /** + * Maximum signed value. + * @type {!Long} + * @expose + */ + Long.MAX_VALUE = Long.fromBits(0xFFFFFFFF|0, 0x7FFFFFFF|0, false); + + /** + * Maximum unsigned value. + * @type {!Long} + * @expose + */ + Long.MAX_UNSIGNED_VALUE = Long.fromBits(0xFFFFFFFF|0, 0xFFFFFFFF|0, true); + + /** + * Minimum signed value. + * @type {!Long} + * @expose + */ + Long.MIN_VALUE = Long.fromBits(0, 0x80000000|0, false); + + /** + * Converts the Long to a 32 bit integer, assuming it is a 32 bit integer. + * @returns {number} + * @expose + */ + Long.prototype.toInt = function() { + return this.unsigned ? this.low >>> 0 : this.low; + }; + + /** + * Converts the Long to a the nearest floating-point representation of this value (double, 53 bit mantissa). + * @returns {number} + * @expose + */ + Long.prototype.toNumber = function() { + if (this.unsigned) { + return ((this.high >>> 0) * TWO_PWR_32_DBL) + (this.low >>> 0); + } + return this.high * TWO_PWR_32_DBL + (this.low >>> 0); + }; + + /** + * Converts the Long to a string written in the specified radix. + * @param {number=} radix Radix (2-36), defaults to 10 + * @returns {string} + * @override + * @throws {RangeError} If `radix` is out of range + * @expose + */ + Long.prototype.toString = function(radix) { + radix = radix || 10; + if (radix < 2 || 36 < radix) + throw RangeError('radix out of range: ' + radix); + if (this.isZero()) + return '0'; + var rem; + if (this.isNegative()) { // Unsigned Longs are never negative + if (this.equals(Long.MIN_VALUE)) { + // We need to change the Long value before it can be negated, so we remove + // the bottom-most digit in this base and then recurse to do the rest. + var radixLong = Long.fromNumber(radix); + var div = this.div(radixLong); + rem = div.multiply(radixLong).subtract(this); + return div.toString(radix) + rem.toInt().toString(radix); + } else + return '-' + this.negate().toString(radix); + } + + // Do several (6) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = Long.fromNumber(Math.pow(radix, 6), this.unsigned); + rem = this; + var result = ''; + while (true) { + var remDiv = rem.div(radixToPower), + intval = rem.subtract(remDiv.multiply(radixToPower)).toInt() >>> 0, + digits = intval.toString(radix); + rem = remDiv; + if (rem.isZero()) + return digits + result; + else { + while (digits.length < 6) + digits = '0' + digits; + result = '' + digits + result; + } + } + }; + + /** + * Gets the high 32 bits as a signed integer. + * @returns {number} Signed high bits + * @expose + */ + Long.prototype.getHighBits = function() { + return this.high; + }; + + /** + * Gets the high 32 bits as an unsigned integer. + * @returns {number} Unsigned high bits + * @expose + */ + Long.prototype.getHighBitsUnsigned = function() { + return this.high >>> 0; + }; + + /** + * Gets the low 32 bits as a signed integer. + * @returns {number} Signed low bits + * @expose + */ + Long.prototype.getLowBits = function() { + return this.low; + }; + + /** + * Gets the low 32 bits as an unsigned integer. + * @returns {number} Unsigned low bits + * @expose + */ + Long.prototype.getLowBitsUnsigned = function() { + return this.low >>> 0; + }; + + /** + * Gets the number of bits needed to represent the absolute value of this Long. + * @returns {number} + * @expose + */ + Long.prototype.getNumBitsAbs = function() { + if (this.isNegative()) // Unsigned Longs are never negative + return this.equals(Long.MIN_VALUE) ? 64 : this.negate().getNumBitsAbs(); + var val = this.high != 0 ? this.high : this.low; + for (var bit = 31; bit > 0; bit--) + if ((val & (1 << bit)) != 0) + break; + return this.high != 0 ? bit + 33 : bit + 1; + }; + + /** + * Tests if this Long's value equals zero. + * @returns {boolean} + * @expose + */ + Long.prototype.isZero = function() { + return this.high === 0 && this.low === 0; + }; + + /** + * Tests if this Long's value is negative. + * @returns {boolean} + * @expose + */ + Long.prototype.isNegative = function() { + return !this.unsigned && this.high < 0; + }; + + /** + * Tests if this Long's value is positive. + * @returns {boolean} + * @expose + */ + Long.prototype.isPositive = function() { + return this.unsigned || this.high >= 0; + }; + + /** + * Tests if this Long's value is odd. + * @returns {boolean} + * @expose + */ + Long.prototype.isOdd = function() { + return (this.low & 1) === 1; + }; + + /** + * Tests if this Long's value is even. + * @returns {boolean} + * @expose + */ + Long.prototype.isEven = function() { + return (this.low & 1) === 0; + }; + + /** + * Tests if this Long's value equals the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + Long.prototype.equals = function(other) { + if (!Long.isLong(other)) + other = Long.fromValue(other); + if (this.unsigned !== other.unsigned && (this.high >>> 31) === 1 && (other.high >>> 31) === 1) + return false; + return this.high === other.high && this.low === other.low; + }; + + /** + * Tests if this Long's value differs from the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + Long.prototype.notEquals = function(other) { + if (!Long.isLong(other)) + other = Long.fromValue(other); + return !this.equals(other); + }; + + /** + * Tests if this Long's value is less than the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + Long.prototype.lessThan = function(other) { + if (!Long.isLong(other)) + other = Long.fromValue(other); + return this.compare(other) < 0; + }; + + /** + * Tests if this Long's value is less than or equal the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + Long.prototype.lessThanOrEqual = function(other) { + if (!Long.isLong(other)) + other = Long.fromValue(other); + return this.compare(other) <= 0; + }; + + /** + * Tests if this Long's value is greater than the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + Long.prototype.greaterThan = function(other) { + if (!Long.isLong(other)) + other = Long.fromValue(other); + return this.compare(other) > 0; + }; + + /** + * Tests if this Long's value is greater than or equal the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + Long.prototype.greaterThanOrEqual = function(other) { + return this.compare(other) >= 0; + }; + + /** + * Compares this Long's value with the specified's. + * @param {!Long|number|string} other Other value + * @returns {number} 0 if they are the same, 1 if the this is greater and -1 + * if the given one is greater + * @expose + */ + Long.prototype.compare = function(other) { + if (this.equals(other)) + return 0; + var thisNeg = this.isNegative(), + otherNeg = other.isNegative(); + if (thisNeg && !otherNeg) + return -1; + if (!thisNeg && otherNeg) + return 1; + // At this point the sign bits are the same + if (!this.unsigned) + return this.subtract(other).isNegative() ? -1 : 1; + // Both are positive if at least one is unsigned + return (other.high >>> 0) > (this.high >>> 0) || (other.high === this.high && (other.low >>> 0) > (this.low >>> 0)) ? -1 : 1; + }; + + /** + * Negates this Long's value. + * @returns {!Long} Negated Long + * @expose + */ + Long.prototype.negate = function() { + if (!this.unsigned && this.equals(Long.MIN_VALUE)) + return Long.MIN_VALUE; + return this.not().add(Long.ONE); + }; + + /** + * Returns the sum of this and the specified Long. + * @param {!Long|number|string} addend Addend + * @returns {!Long} Sum + * @expose + */ + Long.prototype.add = function(addend) { + if (!Long.isLong(addend)) + addend = Long.fromValue(addend); + + // Divide each number into 4 chunks of 16 bits, and then sum the chunks. + + var a48 = this.high >>> 16; + var a32 = this.high & 0xFFFF; + var a16 = this.low >>> 16; + var a00 = this.low & 0xFFFF; + + var b48 = addend.high >>> 16; + var b32 = addend.high & 0xFFFF; + var b16 = addend.low >>> 16; + var b00 = addend.low & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 + b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 + b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 + b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 + b48; + c48 &= 0xFFFF; + return Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); + }; + + /** + * Returns the difference of this and the specified Long. + * @param {!Long|number|string} subtrahend Subtrahend + * @returns {!Long} Difference + * @expose + */ + Long.prototype.subtract = function(subtrahend) { + if (!Long.isLong(subtrahend)) + subtrahend = Long.fromValue(subtrahend); + return this.add(subtrahend.negate()); + }; + + /** + * Returns the product of this and the specified Long. + * @param {!Long|number|string} multiplier Multiplier + * @returns {!Long} Product + * @expose + */ + Long.prototype.multiply = function(multiplier) { + if (this.isZero()) + return Long.ZERO; + if (!Long.isLong(multiplier)) + multiplier = Long.fromValue(multiplier); + if (multiplier.isZero()) + return Long.ZERO; + if (this.equals(Long.MIN_VALUE)) + return multiplier.isOdd() ? Long.MIN_VALUE : Long.ZERO; + if (multiplier.equals(Long.MIN_VALUE)) + return this.isOdd() ? Long.MIN_VALUE : Long.ZERO; + + if (this.isNegative()) { + if (multiplier.isNegative()) + return this.negate().multiply(multiplier.negate()); + else + return this.negate().multiply(multiplier).negate(); + } else if (multiplier.isNegative()) + return this.multiply(multiplier.negate()).negate(); + + // If both longs are small, use float multiplication + if (this.lessThan(TWO_PWR_24) && multiplier.lessThan(TWO_PWR_24)) + return Long.fromNumber(this.toNumber() * multiplier.toNumber(), this.unsigned); + + // Divide each long into 4 chunks of 16 bits, and then add up 4x4 products. + // We can skip products that would overflow. + + var a48 = this.high >>> 16; + var a32 = this.high & 0xFFFF; + var a16 = this.low >>> 16; + var a00 = this.low & 0xFFFF; + + var b48 = multiplier.high >>> 16; + var b32 = multiplier.high & 0xFFFF; + var b16 = multiplier.low >>> 16; + var b00 = multiplier.low & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 * b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 * b00; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c16 += a00 * b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 * b00; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a16 * b16; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a00 * b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48; + c48 &= 0xFFFF; + return Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); + }; + + /** + * Returns this Long divided by the specified. + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Quotient + * @expose + */ + Long.prototype.div = function(divisor) { + if (!Long.isLong(divisor)) + divisor = Long.fromValue(divisor); + if (divisor.isZero()) + throw(new Error('division by zero')); + if (this.isZero()) + return this.unsigned ? Long.UZERO : Long.ZERO; + var approx, rem, res; + if (this.equals(Long.MIN_VALUE)) { + if (divisor.equals(Long.ONE) || divisor.equals(Long.NEG_ONE)) + return Long.MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE + else if (divisor.equals(Long.MIN_VALUE)) + return Long.ONE; + else { + // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|. + var halfThis = this.shiftRight(1); + approx = halfThis.div(divisor).shiftLeft(1); + if (approx.equals(Long.ZERO)) { + return divisor.isNegative() ? Long.ONE : Long.NEG_ONE; + } else { + rem = this.subtract(divisor.multiply(approx)); + res = approx.add(rem.div(divisor)); + return res; + } + } + } else if (divisor.equals(Long.MIN_VALUE)) + return this.unsigned ? Long.UZERO : Long.ZERO; + if (this.isNegative()) { + if (divisor.isNegative()) + return this.negate().div(divisor.negate()); + return this.negate().div(divisor).negate(); + } else if (divisor.isNegative()) + return this.div(divisor.negate()).negate(); + + // Repeat the following until the remainder is less than other: find a + // floating-point that approximates remainder / other *from below*, add this + // into the result, and subtract it from the remainder. It is critical that + // the approximate value is less than or equal to the real value so that the + // remainder never becomes negative. + res = Long.ZERO; + rem = this; + while (rem.greaterThanOrEqual(divisor)) { + // Approximate the result of division. This may be a little greater or + // smaller than the actual value. + approx = Math.max(1, Math.floor(rem.toNumber() / divisor.toNumber())); + + // We will tweak the approximate result by changing it in the 48-th digit or + // the smallest non-fractional digit, whichever is larger. + var log2 = Math.ceil(Math.log(approx) / Math.LN2), + delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48), + + // Decrease the approximation until it is smaller than the remainder. Note + // that if it is too large, the product overflows and is negative. + approxRes = Long.fromNumber(approx), + approxRem = approxRes.multiply(divisor); + while (approxRem.isNegative() || approxRem.greaterThan(rem)) { + approx -= delta; + approxRes = Long.fromNumber(approx, this.unsigned); + approxRem = approxRes.multiply(divisor); + } + + // We know the answer can't be zero... and actually, zero would cause + // infinite recursion since we would make no progress. + if (approxRes.isZero()) + approxRes = Long.ONE; + + res = res.add(approxRes); + rem = rem.subtract(approxRem); + } + return res; + }; + + /** + * Returns this Long modulo the specified. + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Remainder + * @expose + */ + Long.prototype.modulo = function(divisor) { + if (!Long.isLong(divisor)) + divisor = Long.fromValue(divisor); + return this.subtract(this.div(divisor).multiply(divisor)); + }; + + /** + * Returns the bitwise NOT of this Long. + * @returns {!Long} + * @expose + */ + Long.prototype.not = function() { + return Long.fromBits(~this.low, ~this.high, this.unsigned); + }; + + /** + * Returns the bitwise AND of this Long and the specified. + * @param {!Long|number|string} other Other Long + * @returns {!Long} + * @expose + */ + Long.prototype.and = function(other) { + if (!Long.isLong(other)) + other = Long.fromValue(other); + return Long.fromBits(this.low & other.low, this.high & other.high, this.unsigned); + }; + + /** + * Returns the bitwise OR of this Long and the specified. + * @param {!Long|number|string} other Other Long + * @returns {!Long} + * @expose + */ + Long.prototype.or = function(other) { + if (!Long.isLong(other)) + other = Long.fromValue(other); + return Long.fromBits(this.low | other.low, this.high | other.high, this.unsigned); + }; + + /** + * Returns the bitwise XOR of this Long and the given one. + * @param {!Long|number|string} other Other Long + * @returns {!Long} + * @expose + */ + Long.prototype.xor = function(other) { + if (!Long.isLong(other)) + other = Long.fromValue(other); + return Long.fromBits(this.low ^ other.low, this.high ^ other.high, this.unsigned); + }; + + /** + * Returns this Long with bits shifted to the left by the given amount. + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + * @expose + */ + Long.prototype.shiftLeft = function(numBits) { + if (Long.isLong(numBits)) + numBits = numBits.toInt(); + if ((numBits &= 63) === 0) + return this; + else if (numBits < 32) + return Long.fromBits(this.low << numBits, (this.high << numBits) | (this.low >>> (32 - numBits)), this.unsigned); + else + return Long.fromBits(0, this.low << (numBits - 32), this.unsigned); + }; + + /** + * Returns this Long with bits arithmetically shifted to the right by the given amount. + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + * @expose + */ + Long.prototype.shiftRight = function(numBits) { + if (Long.isLong(numBits)) + numBits = numBits.toInt(); + if ((numBits &= 63) === 0) + return this; + else if (numBits < 32) + return Long.fromBits((this.low >>> numBits) | (this.high << (32 - numBits)), this.high >> numBits, this.unsigned); + else + return Long.fromBits(this.high >> (numBits - 32), this.high >= 0 ? 0 : -1, this.unsigned); + }; + + /** + * Returns this Long with bits logically shifted to the right by the given amount. + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + * @expose + */ + Long.prototype.shiftRightUnsigned = function(numBits) { + if (Long.isLong(numBits)) + numBits = numBits.toInt(); + numBits &= 63; + if (numBits === 0) + return this; + else { + var high = this.high; + if (numBits < 32) { + var low = this.low; + return Long.fromBits((low >>> numBits) | (high << (32 - numBits)), high >>> numBits, this.unsigned); + } else if (numBits === 32) + return Long.fromBits(high, 0, this.unsigned); + else + return Long.fromBits(high >>> (numBits - 32), 0, this.unsigned); + } + }; + + /** + * Converts this Long to signed. + * @returns {!Long} Signed long + * @expose + */ + Long.prototype.toSigned = function() { + if (!this.unsigned) + return this; + return new Long(this.low, this.high, false); + }; + + /** + * Converts this Long to unsigned. + * @returns {!Long} Unsigned long + * @expose + */ + Long.prototype.toUnsigned = function() { + if (this.unsigned) + return this; + return new Long(this.low, this.high, true); + }; + + /* CommonJS */ if (typeof require === 'function' && typeof module === 'object' && module && typeof exports === 'object' && exports) + module["exports"] = Long; + /* AMD */ else if (typeof define === 'function' && define["amd"]) + define(function() { return Long; }); + /* Global */ else + (global["dcodeIO"] = global["dcodeIO"] || {})["Long"] = Long; + +})(this); + +},{}],3:[function(require,module,exports){ +/* + Copyright 2013 Daniel Wirtz + Copyright 2009 The Closure Library Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS-IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +module.exports = require("./dist/Long.js"); + +},{"./dist/Long.js":2}],4:[function(require,module,exports){ +module.exports=require('bytebuffer') + +},{"bytebuffer":1}]},{},[4])(4) +}); \ No newline at end of file diff --git a/dl/package.json b/dl/package.json new file mode 100644 index 0000000000..747b14ab90 --- /dev/null +++ b/dl/package.json @@ -0,0 +1,40 @@ +{ + "name": "graphene-dl", + "description": "data layer for use with graphene powered UIs", + "homepage": "https://github.com/cryptonomex/graphene_ui", + "author": "Cryptonomex, Inc.", + "license" : "LicenseRef-LICENSE", + "version": "0.0.1", + "description": "graphene data layer powered by flux", + "engines": { + "node": "0.12.2", + "npm": "2.8.3" + }, + "repository": { + "type": "git", + "url": "git://github.com/....git" + }, + "scripts": { + "postinstall": "", + "test": "./node_modules/.bin/mocha --compilers js:mocha-traceur,coffee:coffee-script --require coffee-script/register" + }, + "dependencies": { + "alt": "~0.15.3", + "immutable": "~3.7.1", + "rx": "^2.5.2", + "tcomb": "~1.0.1", + "websocket": "^1.0.18", + "bigi": "1.1.0", + "bs58": "~2.0.0", + "crypto-js": "3.1.2-3", + "ecurve": "1.0.0", + "secure-random": "~1.1.1" + }, + "devDependencies": { + "coffee-script": "~1.8.0", + "coffeeify": "~0.7.0", + "mocha": "1.21.5", + "mocha-traceur": "^2.1.0", + "node-localstorage": "~0.3.4" + } +} diff --git a/dl/src/actions/AccountActions.js b/dl/src/actions/AccountActions.js new file mode 100644 index 0000000000..cf073519e4 --- /dev/null +++ b/dl/src/actions/AccountActions.js @@ -0,0 +1,141 @@ +import alt from "../alt-instance"; +import utils from "common/utils"; +import api from "../api/accountApi"; +import WalletApi from "rpc_api/WalletApi"; + +let accountSubs = {}; +let wallet_api = new WalletApi(); + +class AccountActions { + + getAllAccounts() { + return api.lookupAccounts("", 100) + .then(result => { + this.dispatch(result); + return result[0][1]; + }).catch(error => { + console.log("Error in AccountActions.getAllAccounts: ", error); + }); + } + + unSubscribe(id) { + api.unSubscribeAccount(accountSubs[id]).then(result => { + console.log("unsub result:", result); + console.log("unSubscribe from:", id); + delete accountSubs[id]; + }); + } + + getAccount(name_or_id) { + let subscription = (result) => { + console.log("sub result:", result); + let accountId = null; + for (let id in accountSubs) { + if (accountSubs[id] === result[0].id) { + accountId = id; + break; + } + } + if (accountId) { + Promise.all([ + api.getHistory(accountId, 10), + api.getBalances(accountId) + ]) + .then(results => { + this.dispatch({ + sub: true, + history: results[0], + balances: results[1], + account: accountId + }); + }); + } + + }; + + if (utils.is_object_id(name_or_id)) { + return Promise.all([ + api.getAccountsByID(name_or_id), + api.getBalances(name_or_id), + api.getHistory(name_or_id, 10) + ]) + .then(result => { + if (!accountSubs[name_or_id]) { + let statObject = result[0][0].statistics; + api.subscribeAccount(subscription, statObject) + .then(subResult => { + accountSubs[name_or_id] = statObject; // subResult now returns null, unable to verify success.. + console.log("subscribed to account", name_or_id, ":", subResult); + }); + } + this.dispatch(result); + }).catch((error) => { + console.log("Error in AccountActions.getAccount: ", error); + }); + } else { + return api.lookupAccounts(name_or_id, 1) + .then((account) => { + let id = account[0][1]; + Promise.all([ + api.getAccountsByID(id), + api.getBalances(id), + api.getHistory(id, 10) + ]).then((results) => { + if (!accountSubs[name_or_id]) { + let statObject = results[0][0].statistics; + api.subscribeAccount(subscription, statObject) + .then(subResult => { + accountSubs[id] = statObject; // subResult now returns null, unable to verify success.. + console.log("subscribed to account", id, ":", subResult); + }); + } + + this.dispatch(results); + }).catch((error) => { + console.log("Error in AccountActions.getAccount: ", error); + }); + }); + } + + } + + setCurrentAccount(name) { + this.dispatch(name); + } + + transfer(from_account_name_or_id, to_account_name_or_id, amount, asset_name_or_id, memo) { + //console.log("[AccountActions.js:68] ----- transfer ----->", from_account_name_or_id, to_account_name_or_id, amount, asset_name_or_id, memo); + var from_account_id = from_account_name_or_id; + var to_account_id = to_account_name_or_id; + var asset_id = asset_name_or_id; + let promise; + + try { + promise = wallet_api.transfer( + from_account_id, to_account_id, + amount, asset_id, memo + ); + promise.then(result => { + this.dispatch(result); + }); + } catch (error) { + console.log("[AccountActions.js:90] ----- transfer error ----->", error); + return new Promise((resolve, reject) => { + reject(error); + }); + } + return promise; + } + + createAccount(name) { + return wallet_api.create_account_with_brain_key("brainkey", name, 11, 0, 0); + // return api.createAccount(name).then( () => { + // this.dispatch(name); + // }).catch(error => { + // console.log("Error in AccountActions.createAccount: ", error); + // }); + } + +} + +module.exports = alt.createActions(AccountActions); diff --git a/dl/src/actions/AssetActions.js b/dl/src/actions/AssetActions.js new file mode 100644 index 0000000000..5249b4ed74 --- /dev/null +++ b/dl/src/actions/AssetActions.js @@ -0,0 +1,31 @@ +var alt = require("../alt-instance"); +import Apis from "rpc_api/ApiInstances"; +import utils from "common/utils"; + +class AssetActions { + + createAsset(assetObject) { + // Create asset action here... + } + + getAsset(id) { + let assetPromise; + if (utils.is_object_id(id)) { + assetPromise = Apis.instance().db_api().exec("get_objects", [ + [id] + ]); + } else { + assetPromise = Apis.instance().db_api().exec("list_assets", [ + id, 1 + ]); + } + + return assetPromise.then((result) => { + this.dispatch(result[0]); + }).catch((error) => { + console.log("Error in AssetStore.updateAsset: ", error); + }); + } +} + +module.exports = alt.createActions(AssetActions); diff --git a/dl/src/actions/BlockchainActions.js b/dl/src/actions/BlockchainActions.js new file mode 100644 index 0000000000..221380e58d --- /dev/null +++ b/dl/src/actions/BlockchainActions.js @@ -0,0 +1,87 @@ +var alt = require("../alt-instance"); +import Apis from "rpc_api/ApiInstances"; + +let subs = { + globals: false +}; + +let latestBlocks = {}; + +class BlockchainActions { + + unSubscribeGlobals() { + if (subs.globals) { + Apis.instance().db_api().exec("unsubscribe_from_objects", [ + ["2.0.0", "2.1.0"] + ]) + .then((result) => { + console.log("unsubscription success:", result); + if (result) { + subs.globals = false; + } + }).catch((error) => { + console.log("Error in BlockchainActions.subscribeDynGlobal unsubscribe: ", error); + }); + } + } + + subscribeGlobals() { + let subscription = (result) => { + this.dispatch(result); + }; + + if (!subs.globals) { + subs.globals = true; + return Promise.all([ + Apis.instance().db_api().exec("subscribe_to_objects", [ + subscription, ["2.0.0", "2.1.0"] + ]), + Apis.instance().db_api().exec("get_objects", [ + ["2.0.0", "2.1.0"] + ]) + ]) + .then((result) => { + console.log("subscription success:", result[0]); + this.dispatch(result[1]); + }).catch((error) => { + console.log("Error in BlockchainActions.subscribeDynGlobal subscribe: ", error); + }); + } + return Promise.resolve(true); + } + + getLatest(height) { + if (!latestBlocks[height]) { + latestBlocks[height] = true; + Apis.instance().db_api().exec("get_block", [ + height + ]) + .then((result) => { + if (!result) { + return; + } + result.id = height; // The returned object for some reason does not include the block height.. + this.dispatch(result); + }).catch((error) => { + console.log("Error in BlockchainActions.getBlock: ", error); + }); + } + } + + getBlock(height) { + Apis.instance().db_api().exec("get_block", [ + height + ]) + .then((result) => { + if (!result) { + return; + } + result.id = height; // The returned object for some reason does not include the block height.. + this.dispatch(result); + }).catch((error) => { + console.log("Error in BlockchainActions.getBlock: ", error); + }); + } +} + +module.exports = alt.createActions(BlockchainActions); diff --git a/dl/src/actions/IntlActions.js b/dl/src/actions/IntlActions.js new file mode 100644 index 0000000000..d6b42ae0d4 --- /dev/null +++ b/dl/src/actions/IntlActions.js @@ -0,0 +1,15 @@ +var alt = require("../alt-instance"); + +class IntlActions { + + switchLocale(locale) { + this.dispatch(locale); + } + + getLocale(locale) { + this.dispatch(locale); + } + +} + +module.exports = alt.createActions(IntlActions); diff --git a/dl/src/actions/KeyActions.js b/dl/src/actions/KeyActions.js new file mode 100644 index 0000000000..6bc37cd7be --- /dev/null +++ b/dl/src/actions/KeyActions.js @@ -0,0 +1,12 @@ +var alt = require("../alt-instance"); +import Apis from "rpc_api/ApiInstances"; + +class KeyActions { + + addKey(key) { + this.dispatch(key); + } + +} + +module.exports = alt.createActions(KeyActions); diff --git a/dl/src/actions/MarketsActions.js b/dl/src/actions/MarketsActions.js new file mode 100644 index 0000000000..76df83c1a2 --- /dev/null +++ b/dl/src/actions/MarketsActions.js @@ -0,0 +1,68 @@ +var alt = require("../alt-instance"); +import Apis from "rpc_api/ApiInstances"; + +let subs = {}; + +class MarketsActions { + + subscribeMarket(idA, idB) { + let subID = idA + "_" + idB; + let subscription = (result) => { + console.log("markets subscription result:", result); + this.dispatch({ + sub: result + }); + }; + + if (!subs[subID]) { + subs[subID] = true; + return Promise.all([ + Apis.instance().db_api().exec("subscribe_to_market", [ + subscription, idA, idB + ]), + Apis.instance().db_api().exec("get_limit_orders", [ + idA, idB, 100 + ]) + ]) + .then((result) => { + console.log("market subscription success:", result[0]); + this.dispatch({ + limits: result[1], + shorts: result[2] + }); + }).catch((error) => { + console.log("Error in MarketsActions.subscribeMarket: ", error); + }); + } + return Promise.resolve(true); + } + + unSubscribeMarket(idA, idB) { + let subID = idA + "_" + idB; + + if (subs[subID]) { + return Apis.instance().db_api().exec("unsubscribe_from_market", [ + idA, idB + ]) + .then((result) => { + console.log(subID, "market unsubscription success:", result); + delete subs[subID]; + }).catch((error) => { + console.log("Error in MarketsActions.unSubscribeMarket: ", error); + }); + } + return Promise.resolve(true); + } + + getMarkets() { + // return Apis.instance().db_api().exec("get_objects", [ + // [id] + // ]).then((result) => { + // this.dispatch(result[0]); + // }).catch((error) => { + // console.log("Error in AssetStore.updateAsset: ", error); + // }); + } +} + +module.exports = alt.createActions(MarketsActions); \ No newline at end of file diff --git a/dl/src/actions/SessionActions.js b/dl/src/actions/SessionActions.js new file mode 100644 index 0000000000..6308526b04 --- /dev/null +++ b/dl/src/actions/SessionActions.js @@ -0,0 +1,40 @@ +var alt = require("../alt-instance"); +import mockUtils from "./mockApi"; + +class SessionActions { + + unlock(input) { + // Dispatch here for possible loading state + this.dispatch({inProgress: true}); + + // Fire rpc call to unlock (this will be a rpc lib call) + let mockApiCall = mockUtils.getMock(); + + mockApiCall.then((result) => { + if (result === true) { + this.dispatch({inProgress: false, unlocked: true, failed: false}); + } + }) + .catch((error) => { + this.dispatch({inProgress: false, unlocked: false, failed: true, error: error}); + }); + } + + lock() { + this.dispatch({lockInProgress: true}); + // Fire rpc call to lock (this will be a rpc lib call) + let mockApiCall = mockUtils.getMock(); + + mockApiCall.then((result) => { + // Unlock successful + if (result === true) { + this.dispatch({lockInProgress: false, unlocked: false, lockError: null}); + } + }) + .catch((error) => { + this.dispatch({lockInProgress: false, lockError: true, error: error}); + }); + } +} + +module.exports = alt.createActions(SessionActions); diff --git a/dl/src/actions/WitnessActions.js b/dl/src/actions/WitnessActions.js new file mode 100644 index 0000000000..6226eae507 --- /dev/null +++ b/dl/src/actions/WitnessActions.js @@ -0,0 +1,43 @@ +var alt = require("../alt-instance"); +import Apis from "rpc_api/ApiInstances"; + +let witness_in_prog = false; +let account_in_prog = false; + +class WitnessActions { + getWitnesses(ids) { + if (!witness_in_prog) { + witness_in_prog = true; + if (!Array.isArray(ids)) { + ids = [ids]; + } + Apis.instance().db_api().exec("get_objects", [ids]) + .then((result) => { + witness_in_prog = false; + this.dispatch(result); + }).catch((error) => { + witness_in_prog = false; + console.log("Error in WitnessActions.getWitnesses: ", error); + }); + } + } + + getWitnessAccounts(ids) { + if (!account_in_prog) { + account_in_prog = true; + if (!Array.isArray(ids)) { + ids = [ids]; + } + Apis.instance().db_api().exec("get_objects", [ids]) + .then((result) => { + account_in_prog = false; + this.dispatch(result); + }).catch((error) => { + account_in_prog = false; + console.log("Error in WitnessActions.getWitnessAccounts: ", error); + }); + } + } +} + +module.exports = alt.createActions(WitnessActions); diff --git a/dl/src/actions/mockApi.js b/dl/src/actions/mockApi.js new file mode 100644 index 0000000000..c53644e997 --- /dev/null +++ b/dl/src/actions/mockApi.js @@ -0,0 +1,223 @@ +// Fire rpc call to unlock (this will be a rpc lib call) +let accounts = { + bytemaster: { + id: 1, + name: "bytemaster", + isMyAccount: true, + primeMember: true, + realName: "Dan Larimer", + org: ["Bitshares", "Graphene", "FollowMyVote", "Peertracks"], + blackList: ["Ethereum", "Counterparty"], + registration: { + date: new Date(1431682554238), + referrer: "Invictus", + registrar: "Invictus" + }, + referals: { + members: 10, + users: 30 + }, + rewards: { + cashback: 5123, + vested: 4300 + }, + balances: [{ + id: 0, + asset: "BTS", + amount: 11210 + }, { + id: 22, + asset: "USD", + amount: 112 + }, { + id: 17, + asset: "CNY", + amount: 9889494 + }] + }, + Account_B: { + id: 2, + name: "Account_B", + isMyAccount: true, + primeMember: false, + balances: [{ + id: 0, + asset: "BTS", + amount: 0 + }] + }, + nakamoto: { + id: 3, + name: "nakamoto", + isMyAccount: false, + primeMember: false, + org: ["Bitcoin"], + balances: [{ + id: 0, + asset: "BTS", + amount: 0 + }, { + id: 14, + asset: "BTC", + amount: 1000000 + }] + }, + Invictus: { + id: 5, + name: "Invictus", + isMyAccount: false, + primeMember: true, + org: ["Bitshares"], + registration: { + date: new Date(1430482554238), + referrer: "", + registrar: "Invictus" + }, + referals: { + members: 354, + users: 35 + }, + rewards: { + cashback: 46547, + vested: 15400 + }, + balances: [{ + id: 0, + asset: "BTS", + amount: 24644750 + }] + } +}; + +let blockExample = { + "previous": "000000a2f22ba89f7f20b48967c5c59dfd69d242", + "timestamp": "2015-04-28T15:35:45", + "witness": "1.7.5", + "next_secret_hash": "752a5f6e39e2b9b62391c042b7f8f237b1c99a9f982ece955af9d120", + "previous_secret": "0a62ac0c6ed80c1ec78f25f575803dd6e4d5e865d1e23191934197f2", + "transactions": [{ + "ref_block_num": 0, + "ref_block_prefix": 0, + "relative_expiration": 1, + "operations": [[ + 6,{ + "fee_paying_account": "1.3.11", + "fee": { + "amount": 0, + "asset_id": "1.4.0" + }, + "key_data": [ + 1, + "BTS6KZmQMPKz2ztNFAqye4Hcmh5iN8kj2qXsw56sCHifmTRAeGqrJ" + ] + } + ],[ + 6,{ + "fee_paying_account": "1.3.11", + "fee": { + "amount": 0, + "asset_id": "1.4.0" + }, + "key_data": [ + 1, + "BTS7QYTULyoaLDo5Gc9yn4nFAEUECjpewevfBpdSwCxbUYbUJ5ynY" + ] + } + ],[ + 7,{ + "registrar": "1.3.11", + "fee": { + "amount": 0, + "asset_id": "1.4.0" + }, + "referrer": "1.3.0", + "referrer_percent": 100, + "name": "slave", + "owner": { + "weight_threshold": 1, + "auths": [[ + "0.2.0", + 1 + ] + ] + }, + "active": { + "weight_threshold": 1, + "auths": [[ + "0.2.1", + 1 + ] + ] + }, + "voting_account": "1.3.0", + "memo_key": "0.2.1", + "num_witness": 101, + "num_committee": 11, + "vote": [] + } + ] + ], + "signatures": [ + "204e401553fca4479ef638af83aa3b9a31c6892445d9d8715fc79717ed06a8a84b5f41ad53a0b36c539095d68f21e46250f1512d515d8941d6cf01a0f821ba1e72" + ], + "operation_results": [[ + 0, + "1.2.4" + ],[ + 0, + "1.2.5" + ],[ + 0, + "1.3.13" + ] + ] + } + ], + "delegate_signature": "2019d8cd00455afd029e1c65e578a84472178330a92265659271cd104626179eef3f0b2897c6fc228c26e8d8bce78eed922ef9d61cbc48a35c5b3c5c9a7d5ca5d8" +} + +module.exports = { + getMock: function() { + return new Promise((resolve) => { + // simulate an asynchronous action where data is fetched on + // a remote server somewhere. + setTimeout(() => { + // resolve with some mock data + resolve(true); + }, 500); + }); + }, + + getMockBlock: function(height) { + return new Promise((resolve) => { + // simulate an asynchronous action where data is fetched on + // a remote server somewhere. + setTimeout(() => { + // resolve with some mock data + resolve(blockExample); + }, 100); + }); + }, + + getMockAccount: function(name) { + return new Promise((resolve) => { + // simulate an asynchronous action where data is fetched on + // a remote server somewhere. + setTimeout(() => { + // resolve with some mock data + resolve(accounts[name]); + }, 100); + }); + }, + + getMockAccounts: function() { + return new Promise((resolve) => { + // simulate an asynchronous action where data is fetched on + // a remote server somewhere. + setTimeout(() => { + // resolve with some mock data + resolve([accounts["bytemaster"], accounts["Account_B"]]); + }, 500); + }); + } +}; diff --git a/dl/src/alt-instance.js b/dl/src/alt-instance.js new file mode 100644 index 0000000000..0db4dc94ec --- /dev/null +++ b/dl/src/alt-instance.js @@ -0,0 +1,4 @@ +var Alt = require("alt"); +var alt = new Alt(); + +module.exports = alt; diff --git a/dl/src/api/accountApi.js b/dl/src/api/accountApi.js new file mode 100644 index 0000000000..d91b96d94b --- /dev/null +++ b/dl/src/api/accountApi.js @@ -0,0 +1,47 @@ +import Apis from "rpc_api/ApiInstances"; +var PrivateKey = require("../ecc/key_private"); + +class Api { + + getAccountsByID(ids) { + if (!Array.isArray(ids)) { + ids = [ids]; + } + return Apis.instance().db_api().exec("get_objects", [ids]); + } + + lookupAccounts(startChar, limit) { + return Apis.instance().db_api().exec("lookup_accounts", [ + startChar, limit + ]); + } + + getBalances(id) { + return Apis.instance().db_api().exec("get_account_balances", [id, ["1.4.0"]]); + } + + getHistory(id, count) { + return Apis.instance().history_api().exec("get_account_history", [id, "1.13.0", count, "1.13.9999"]); + } + + subscribeAccount(subscription, statID) { + return Apis.instance().db_api().exec("subscribe_to_objects", [ + subscription, [statID] + ]); + } + + unSubscribeAccount(statID) { + return Apis.instance().db_api().exec("unsubscribe_from_objects", [ + [statID] + ]); + } + + createAccount(name) { + var expire_minutes = 10; + var signer_private_key_id = 1; + var signer_private_key = PrivateKey.fromSeed("nathan"); + return Apis.instance().app_api().create_account_with_brain_key("brainkey", name, 11, 0, 0, expire_minutes, signer_private_key_id, signer_private_key, true); + } +} + +export default new Api(); diff --git a/dl/src/chain/chain_types.coffee b/dl/src/chain/chain_types.coffee new file mode 100644 index 0000000000..a4f00ec802 --- /dev/null +++ b/dl/src/chain/chain_types.coffee @@ -0,0 +1,72 @@ +module.exports = ChainTypes = {} + +ChainTypes.reserved_spaces= + relative_protocol_ids: 0 + protocol_ids: 1 + implementation_ids: 2 + +ChainTypes.object_type= + null: 0 + base: 1 + key: 2 + account: 3 + asset: 4 + force_settlement: 5 + delegate: 6 + witness: 7 + limit_order: 8 + short_order: 9 + call_order: 10 + custom: 11 + proposal: 12 + operation_history: 13 + withdraw_permission: 14 + bond_offer: 15 + bond: 16 + file: 17 + vesting_balance_object_type:18 + worker_object_type: 19 + +ChainTypes.operations= + transfer: 0 + limit_order_create: 1 + short_order_create: 2 + limit_order_cancel: 3 + short_order_cancel: 4 + call_order_update: 5 + key_create: 6 + account_create: 7 + account_update: 8 + account_whitelist: 9 + account_transfer: 10 + asset_create: 11 + asset_update: 12 + asset_update_bitasset: 13 + asset_update_feed_producers: 14 + asset_issue: 15 + asset_burn: 16 + asset_fund_fee_pool: 17 + asset_settle: 18 + asset_global_settle: 19 + asset_publish_feed: 20 + delegate_create: 21 + witness_create: 22 + witness_withdraw_pay: 23 + proposal_create: 24 + proposal_update: 25 + proposal_delete: 26 + withdraw_permission_create: 27 + withdraw_permission_update: 28 + withdraw_permission_claim: 29 + withdraw_permission_delete: 30 + fill_order: 31 + global_parameters_update: 32 + file_write: 33 + vesting_balance_create: 34 + vesting_balance_withdraw: 35 + bond_create_offer: 36 + bond_cancel_offer: 37 + bond_accept_offer: 38 + bond_claim_collateral: 39 + worker_create: 40 + custom: 41 diff --git a/dl/src/chain/config.coffee b/dl/src/chain/config.coffee new file mode 100644 index 0000000000..92e87a0ae2 --- /dev/null +++ b/dl/src/chain/config.coffee @@ -0,0 +1,3 @@ +module.exports = + address_prefix: "GPH" + diff --git a/dl/src/chain/lookup.coffee b/dl/src/chain/lookup.coffee new file mode 100644 index 0000000000..477116b1b1 --- /dev/null +++ b/dl/src/chain/lookup.coffee @@ -0,0 +1,207 @@ +Promise = require '../common/Promise' +chain_config = require '../chain/config' +ChainTypes = require '../chain/chain_types' +PublicKey = require '../ecc/key_public' + +#storage = require '../common/storage' +vt = require '../chain/serializer_validation' +is_empty = vt.is_empty +is_digits = vt.is_digits +get_protocol_instance = vt.get_protocol_instance + +api = require('../rpc_api/ApiInstances').instance() + +### Makes account, key, assset instance ID lookups very easy. All functions + return an object with an attribute 'resolved' ( ex: {resolve: 1} ). The + resolve value may not be available until after the resolve() promise + is finished (ex: resolve().then(...) ). +### +class Lookup + + constructor:-> + @_private = new Private() + + ###* + Resolve an account id from an account name. An account id resolves unchanged. + ### + account_id:(name_or_id)-> + return resolve: name_or_id if is_empty name_or_id + # account instance or account id + i = @_private.try_simple_resolve "account", name_or_id + return i unless i is undefined + account_name = name_or_id + @_private.deferred_property "accountname", "id", account_name + + ###* + Resolve an asset id from an asset name. An asset id resolves unchanged. + ### + asset_id:(name_or_id)-> + return resolve: name_or_id if is_empty name_or_id + i = @_private.try_simple_resolve "asset", name_or_id + return i unless i is undefined + asset_name = name_or_id + return resolve: 0 if ( + asset_name is "CORE" or + asset_name is chain_config.address_suffix + ) + @_private.deferred_property "assetname", "id", asset_name + + ###* + Resolve a memo key id from an account name or account id. A key id + resolves unchanged. + ### + memo_key_id:(name_or_id)-> + return resolve: name_or_id if is_empty name_or_id + # key instance or key id + i = @_private.try_simple_resolve "key", name_or_id + return i unless i is undefined + + if name_or_id.indexOf("1.3.") is 0 + account_id = name_or_id + return @_private.deferred_property "object", "memo_key", account_id + + account_name = name_or_id + return @_private.deferred_property "accountname", "memo_key", account_name + + ###* + Resolves a memo public key from an account name or account id. A key id or + public key resolves as expected. + ### + memo_public_key:(name_key_or_id)-> + + return resolve: name_key_or_id if is_empty name_key_or_id + return resolve: name_key_or_id if name_key_or_id.Q # typeof is PublicKey + if name_key_or_id.indexOf(chain_config.address_prefix) is 0 + return resolve: PublicKey.fromBtsPublic name_key_or_id + + _private = @_private + + # key instance or key id + key_id = _private.try_simple_resolve "key", name_key_or_id + if key_id isnt undefined + return _private.public_key key_id.resolve + + index_name = if name_key_or_id.indexOf("1.3.") is 0 + "object" + else + "accountname" + + promise_memo_key = new Promise (resolve)-> + _private.deferred_lookup index_name, name_key_or_id, (account)-> + resolve(account.memo_key) + + ret = resolve: undefined + ((ret)-> + promise_memo_key.then (memo_key)-> + _private.public_key memo_key, ret + return + , (e)-> throw e + )(ret) + ret + + resolve:-> + @_private.resolve() + +module.exports = Lookup + +class Private + + ### + WARNING: Lookup map is erased (re-pointed to an empty map) before callback + function are called. This allows the resolve to find additional + dependencies (they will appear in the new lookup_map). + ### + constructor: -> + @lookup_map = {} + + try_simple_resolve:(type, name_or_id)-> + # is_digits is true when name_or_id is what we needed + return resolve: name_or_id if is_empty(name_or_id) or is_digits(name_or_id) + type_id = ChainTypes.object_type[type] + if name_or_id.indexOf("1.#{type_id}.") is 0 + return resolve: name_or_id + return undefined + + get_group_by:(key, map = @lookup_map)-> + # use non-numeric index into 'map' (ensures key order) + if (value = map[""+key]) isnt undefined + return value + map[key] = {} + + get_list:(key)-> + if (value = @lookup_map[key]) isnt undefined + return value + @lookup_map[key] = [] + + deferred_property: (index_name, result_property_name, lookup_value)-> + ret = resolve: undefined + ((ret, result_property_name)=> + @deferred_lookup index_name, lookup_value, (result)-> + ret.resolve = result[result_property_name] + )(ret, result_property_name) + ret + + public_key: (key_id, ret = resolve: undefined)-> + @deferred_lookup "object", key_id, (key)-> + unless key.key_data[0] is 1 + throw new Error "missing public_key type 1: #{key.key_data}" + ret.resolve = PublicKey.fromBtsPublic key.key_data[1] + return + ret + + deferred_lookup:(index_name, lookup_value, lookup_callback)-> + if lookup_value.resolve + throw new Error "Invalid lookup value #{lookup_value}" + index_group = @get_group_by index_name + value_group = @get_group_by lookup_value, index_group + #console.log('... index_name + lookup_value\t',index_name + "\t" + lookup_value) + defers = @get_list(index_name + "\t" + lookup_value) + defers.push lookup_callback + #console.log '... defers', defers + return + + resolve:()-> + db = api.db_api() + lookup_map = null + promises = null + query=(index_name, api_call, unique_key)-> + paramMap = lookup_map[index_name] + if paramMap + params = Object.keys paramMap + #console.log('... api_call',api_call) + #console.log '... params', JSON.stringify params + promise_call = db.exec( api_call, [params] ) + ((params,index_name,unique_key)-> + promises.push promise_call.then (results)-> + for i in [0...results.length] by 1 + result = results[i] + for lookup_callback in lookup_map[ index_name + "\t" + params[i] ] + lookup_callback(result) + return + , (e)-> + console.error( + "lookup_callback error" + JSON.stringify(api_call) + JSON.stringify(params) + index_name + unique_key + ) + throw e + )(params,index_name,unique_key) + return + + _resolve= => + promises = [] + lookup_map = @lookup_map + @lookup_map = {} # clean slate, captures dependencies + query "accountname", "lookup_account_names", "name" + query "assetname", "lookup_asset_symbols", "symbol" + query "object", "get_objects", "id" + Promise.all(promises).then ()=> + #console.log('... Object.keys(@lookup_map).length',Object.keys(@lookup_map).length) + if Object.keys(@lookup_map).length isnt 0 + _resolve() + else + @lookup_map = {} + Promise.resolve() + _resolve() diff --git a/dl/src/chain/market_operations.coffee b/dl/src/chain/market_operations.coffee new file mode 100644 index 0000000000..e807ff7a4d --- /dev/null +++ b/dl/src/chain/market_operations.coffee @@ -0,0 +1,31 @@ +ChainTypes = require './chain_types' + +lookup = new (require './lookup')() + +module.exports = _my = {} + +class _my.limit_order_create + _template = -> + fee : + amount : "0" + asset_id : 0#1.4.0 + seller : 0 #1.3.0 + amount_to_sell : + amount : "0" + asset_id : 0 #1.4.0 + min_to_receive : + amount : "0" + asset_id : 0 #1.4.0 + expiration : 0 + fill_or_kill : no + + constructor:()-> + for key in Object.keys _tmp = _template() + @[key] = _tmp[key] + + get_operations:-> + @fee.asset_id = lookup.asset_id(@fee.asset_id) + @seller = lookup.account_id(@seller) + @amount_to_sell.asset_id = lookup.asset_id(@amount_to_sell.asset_id) + @min_to_receive.asset_id = lookup.asset_id(@min_to_receive.asset_id) + [[ ChainTypes.operations.limit_order_create, @]] diff --git a/dl/src/chain/object_id.coffee b/dl/src/chain/object_id.coffee new file mode 100644 index 0000000000..5d96a88f6e --- /dev/null +++ b/dl/src/chain/object_id.coffee @@ -0,0 +1,55 @@ +Long = (require '../common/bytebuffer').Long +ChainTypes = require './chain_types' +vt = require './serializer_validation' +required = vt.required +require_match = vt.require_match + +class ObjectId + + DB_MAX_INSTANCE_ID = Long.fromNumber ((Math.pow 2,48)-1) + + constructor:(@space,@type,@instance)-> + instance_string = @instance.toString() + object_id = "#{@space}.#{@type}.#{instance_string}" + unless vt.is_digits instance_string + throw new "Invalid object id #{object_id}" + + ObjectId.fromString=(value)-> + if ( + value.space isnt undefined and + value.type isnt undefined and + value.instance isnt undefined + ) + return value + params = require_match( + /^([0-9]+)\.([0-9]+)\.([0-9]+)$/ + required value, "object_id" + "object_id" + ) + new ObjectId( + parseInt params[1] + parseInt params[2] + Long.fromString params[3] + ) + + ObjectId.fromLong=(long)-> + space = long.shiftRight(56).toInt() + type = long.shiftRight(48).toInt() & 0x00ff + instance = long.and DB_MAX_INSTANCE_ID + new ObjectId space, type, instance + + ObjectId.fromByteBuffer=(b)-> + ObjectId.fromLong b.readUint64() + + toLong:-> + Long.fromNumber(@space).shiftLeft(56).or( + Long.fromNumber(@type).shiftLeft(48).or @instance + ) + + appendByteBuffer:(b)-> + b.writeUint64 @toLong() + + toString:-> + "#{@space}.#{@type}.#{@instance.toString()}" + +module.exports = ObjectId diff --git a/dl/src/chain/serializer.coffee b/dl/src/chain/serializer.coffee new file mode 100644 index 0000000000..b6e679fe61 --- /dev/null +++ b/dl/src/chain/serializer.coffee @@ -0,0 +1,87 @@ +ByteBuffer = require '../common/bytebuffer' +EC = require '../common/error_with_cause' + +class Serializer + + constructor: (@operation_name, @types) -> + + fromByteBuffer: (b) -> + object = {} + field = null + try + for field in Object.keys @types + type = @types[field] + object[field] = type.fromByteBuffer b + + catch error + EC.throw @operation_name+'.'+field, error + + object + + appendByteBuffer: (b, object) -> + field = null + try + for field in Object.keys @types + type = @types[field] + type.appendByteBuffer b, object[field] + + catch error + try + EC.throw @operation_name+'.'+field+" = "+ + JSON.stringify(object[field]), error + catch e # circular ref + EC.throw @operation_name+'.'+field+" = "+ + object[field], error + return + + fromObject: (serialized_object)-> + result = {} + field = null + try + for field in Object.keys @types + type = @types[field] + object = type.fromObject serialized_object[field] + result[field] = object + + catch error + EC.throw @operation_name+'.'+field, error + + result + + toObject: (serialized_object, use_default = no)-> + result = {} + field = null + try + for field in Object.keys @types + type = @types[field] + object = type.toObject serialized_object?[field], use_default + result[field] = object + + catch error + EC.throw @operation_name+'.'+field, error + + result + + # + + fromHex: (hex) -> + b = ByteBuffer.fromHex hex, ByteBuffer.LITTLE_ENDIAN + @fromByteBuffer b + + toHex: (object) -> + b=@toByteBuffer object + b.toHex() + + toByteBuffer: (object) -> + b = new ByteBuffer ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN + @appendByteBuffer b, object + b.copy 0, b.offset + + toBuffer: (object)-> + new Buffer(@toByteBuffer(object).toBinary(), 'binary') + + # + + +module.exports = Serializer + diff --git a/dl/src/chain/serializer_convert.coffee b/dl/src/chain/serializer_convert.coffee new file mode 100644 index 0000000000..b6b207fe5b --- /dev/null +++ b/dl/src/chain/serializer_convert.coffee @@ -0,0 +1,30 @@ +ByteBuffer = require '../common/bytebuffer' + +module.exports=(type)-> + + fromHex: (hex) -> + b = ByteBuffer.fromHex hex, ByteBuffer.LITTLE_ENDIAN + type.fromByteBuffer b + + toHex: (object) -> + b=toByteBuffer type, object + b.toHex() + + fromBuffer:(buffer)-> + b = ByteBuffer.fromBinary buffer.toString(), ByteBuffer.LITTLE_ENDIAN + type.fromByteBuffer b + + toBuffer: (object)-> + new Buffer(toByteBuffer(type, object).toBinary(), 'binary') + + fromBinary:(string)-> + b = ByteBuffer.fromBinary string, ByteBuffer.LITTLE_ENDIAN + type.fromByteBuffer b + + toBinary: (object) -> + toByteBuffer(type, object).toBinary() + +toByteBuffer=(type, object)-> + b = new ByteBuffer ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN + type.appendByteBuffer b, object + b.copy 0, b.offset diff --git a/dl/src/chain/serializer_operation_types.coffee b/dl/src/chain/serializer_operation_types.coffee new file mode 100644 index 0000000000..277b897339 --- /dev/null +++ b/dl/src/chain/serializer_operation_types.coffee @@ -0,0 +1,732 @@ +## Generated code below + +SerializerImpl = require './serializer' + +sr_type = require './serializer_types' +uint8 = sr_type.uint8 +uint16 = sr_type.uint16 +uint32 = sr_type.uint32 +varint32 = sr_type.varint32 +int64 = sr_type.int64 +uint64 = sr_type.uint64 +string = sr_type.string +bytes = sr_type.bytes +bool = sr_type.bool +array = sr_type.array +fixed_array = sr_type.fixed_array +#id_type = sr_type.id_type +protocol_id_type = sr_type.protocol_id_type +object_id_type = sr_type.object_id_type +vote_id = sr_type.vote_id +optional = sr_type.optional +static_variant = sr_type.static_variant +map = sr_type.map +set = sr_type.set +public_key = sr_type.public_key +address = sr_type.address +time_point_sec = sr_type.time_point_sec + +### +When updating generated code +Replace: operation = static_variant [ +with: operation.st_operations = [ + +Delete: +operation = new Serializer( + "operation " + op: operation +) +address = new Serializer( + "address" + addr: bytes 20 +) +public_key = new Serializer( + "public_key" + key_data: bytes 33 +) + +### + +# Place-holder, their are dependencies on "operation" .. The final list of +# operations is not avialble until the very end of the generated code. +# See: operation.st_operations = ... +operation = static_variant() +module.exports["operation"] = operation + +# For module.exports +Serializer=(operation_name, serilization_types_object)-> + s = new SerializerImpl operation_name, serilization_types_object + module.exports[operation_name] = s + +## ------------------------------- +## Generated code follows +# programs/js_operation_serializer +# / +$//g +## ------------------------------- + +void_header = new Serializer( + "void_header" +) + +header_extension = static_variant [ + void_header +] + +void_result = new Serializer( + "void_result" +) + +asset = new Serializer( + "asset" + amount: int64 + asset_id: protocol_id_type "asset" +) + +operation_result = static_variant [ + void_result + object_id_type + asset +] + +processed_transaction = new Serializer( + "processed_transaction" + ref_block_num: uint16 + ref_block_prefix: uint32 + relative_expiration: uint16 + operations: array operation + signatures: map (protocol_id_type "key"), (bytes 65) + operation_results: array operation_result +) + +signed_block = new Serializer( + "signed_block" + previous: bytes 20 + timestamp: time_point_sec + witness: protocol_id_type "witness" + next_secret_hash: bytes 28 + previous_secret: bytes 28 + transaction_merkle_root: bytes 20 + extensions: array header_extension + delegate_signature: bytes 65 + transactions: array processed_transaction +) + +block_header = new Serializer( + "block_header" + previous: bytes 20 + timestamp: time_point_sec + witness: protocol_id_type "witness" + next_secret_hash: bytes 28 + previous_secret: bytes 28 + transaction_merkle_root: bytes 20 + extensions: array header_extension +) + +signed_block_header = new Serializer( + "signed_block_header" + previous: bytes 20 + timestamp: time_point_sec + witness: protocol_id_type "witness" + next_secret_hash: bytes 28 + previous_secret: bytes 28 + transaction_merkle_root: bytes 20 + extensions: array header_extension + delegate_signature: bytes 65 +) + +memo_data = new Serializer( + "memo_data" + from: protocol_id_type "key" + to: protocol_id_type "key" + nonce: uint64 + message: bytes() +) + +transfer = new Serializer( + "transfer" + fee: asset + from: protocol_id_type "account" + to: protocol_id_type "account" + amount: asset + memo: optional memo_data +) + +limit_order_create = new Serializer( + "limit_order_create" + fee: asset + seller: protocol_id_type "account" + amount_to_sell: asset + min_to_receive: asset + expiration: time_point_sec + fill_or_kill: bool +) + +short_order_create = new Serializer( + "short_order_create" + fee: asset + seller: protocol_id_type "account" + amount_to_sell: asset + collateral: asset + initial_collateral_ratio: uint16 + maintenance_collateral_ratio: uint16 + expiration: time_point_sec +) + +limit_order_cancel = new Serializer( + "limit_order_cancel" + fee: asset + fee_paying_account: protocol_id_type "account" + order: protocol_id_type "limit_order" +) + +short_order_cancel = new Serializer( + "short_order_cancel" + fee: asset + fee_paying_account: protocol_id_type "account" + order: protocol_id_type "short_order" +) + +call_order_update = new Serializer( + "call_order_update" + fee: asset + funding_account: protocol_id_type "account" + collateral_to_add: asset + amount_to_cover: asset + maintenance_collateral_ratio: uint16 +) + +key_data = static_variant [ + address + public_key +] + +key_create = new Serializer( + "key_create" + fee: asset + fee_paying_account: protocol_id_type "account" + key_data: key_data +) + +authority = new Serializer( + "authority" + weight_threshold: uint32 + auths: map (object_id_type), (uint16) +) + +account_create = new Serializer( + "account_create" + fee: asset + registrar: protocol_id_type "account" + referrer: protocol_id_type "account" + referrer_percent: uint8 + name: string + owner: authority + active: authority + voting_account: protocol_id_type "account" + memo_key: object_id_type + num_witness: uint16 + num_committee: uint16 + vote: set vote_id +) + +account_update = new Serializer( + "account_update" + fee: asset + account: protocol_id_type "account" + owner: optional authority + active: optional authority + voting_account: optional protocol_id_type "account" + memo_key: optional object_id_type + num_witness: uint16 + num_committee: uint16 + vote: optional set vote_id + upgrade_to_prime: bool +) + +account_whitelist = new Serializer( + "account_whitelist" + fee: asset + authorizing_account: protocol_id_type "account" + account_to_list: protocol_id_type "account" + new_listing: uint8 +) + +account_transfer = new Serializer( + "account_transfer" + fee: asset + account_id: protocol_id_type "account" + new_owner: protocol_id_type "account" +) + +price = new Serializer( + "price" + base: asset + quote: asset +) + +asset_object_asset_options = new Serializer( + "asset_object_asset_options" + max_supply: int64 + market_fee_percent: uint16 + max_market_fee: int64 + min_market_fee: int64 + issuer_permissions: uint16 + flags: uint16 + core_exchange_rate: price + whitelist_authorities: set protocol_id_type "account" + blacklist_authorities: set protocol_id_type "account" + whitelist_markets: set protocol_id_type "asset" + blacklist_markets: set protocol_id_type "asset" +) + +asset_object_bitasset_options = new Serializer( + "asset_object_bitasset_options" + feed_lifetime_sec: uint32 + force_settlement_delay_sec: uint32 + force_settlement_offset_percent: uint16 + maximum_force_settlement_volume: uint16 + short_backing_asset: protocol_id_type "asset" +) + +asset_create = new Serializer( + "asset_create" + fee: asset + issuer: protocol_id_type "account" + symbol: string + precision: uint8 + common_options: asset_object_asset_options + bitasset_options: optional asset_object_bitasset_options + is_prediction_market: bool +) + +asset_update = new Serializer( + "asset_update" + fee: asset + issuer: protocol_id_type "account" + asset_to_update: protocol_id_type "asset" + new_issuer: optional protocol_id_type "account" + new_options: asset_object_asset_options +) + +asset_update_bitasset = new Serializer( + "asset_update_bitasset" + fee: asset + issuer: protocol_id_type "account" + asset_to_update: protocol_id_type "asset" + new_options: asset_object_bitasset_options +) + +asset_update_feed_producers = new Serializer( + "asset_update_feed_producers" + fee: asset + issuer: protocol_id_type "account" + asset_to_update: protocol_id_type "asset" + new_feed_producers: set protocol_id_type "account" +) + +asset_issue = new Serializer( + "asset_issue" + fee: asset + issuer: protocol_id_type "account" + asset_to_issue: asset + issue_to_account: protocol_id_type "account" + memo: optional memo_data +) + +asset_burn = new Serializer( + "asset_burn" + fee: asset + payer: protocol_id_type "account" + amount_to_burn: asset +) + +asset_fund_fee_pool = new Serializer( + "asset_fund_fee_pool" + fee: asset + from_account: protocol_id_type "account" + asset_id: protocol_id_type "asset" + amount: int64 +) + +asset_settle = new Serializer( + "asset_settle" + fee: asset + account: protocol_id_type "account" + amount: asset +) + +asset_global_settle = new Serializer( + "asset_global_settle" + fee: asset + issuer: protocol_id_type "account" + asset_to_settle: protocol_id_type "asset" + settle_price: price +) + +price_feed = new Serializer( + "price_feed" + call_limit: price + short_limit: price + settlement_price: price + max_margin_period_sec: uint32 + required_initial_collateral: uint16 + required_maintenance_collateral: uint16 +) + +asset_publish_feed = new Serializer( + "asset_publish_feed" + fee: asset + publisher: protocol_id_type "account" + asset_id: protocol_id_type "asset" + feed: price_feed +) + +delegate_create = new Serializer( + "delegate_create" + fee: asset + delegate_account: protocol_id_type "account" +) + +witness_create = new Serializer( + "witness_create" + fee: asset + witness_account: protocol_id_type "account" + block_signing_key: protocol_id_type "key" + initial_secret: bytes 28 +) + +witness_withdraw_pay = new Serializer( + "witness_withdraw_pay" + fee: asset + from_witness: protocol_id_type "witness" + to_account: protocol_id_type "account" + amount: int64 +) + +proposal_create = new Serializer( + "proposal_create" + fee: asset + fee_paying_account: protocol_id_type "account" + expiration_time: time_point_sec + proposed_ops: array operation + review_period_seconds: optional uint32 +) + +proposal_update = new Serializer( + "proposal_update" + fee: asset + fee_paying_account: protocol_id_type "account" + proposal: protocol_id_type "proposal" + active_approvals_to_add: set protocol_id_type "account" + active_approvals_to_remove: set protocol_id_type "account" + owner_approvals_to_add: set protocol_id_type "account" + owner_approvals_to_remove: set protocol_id_type "account" + key_approvals_to_add: set protocol_id_type "key" + key_approvals_to_remove: set protocol_id_type "key" +) + +proposal_delete = new Serializer( + "proposal_delete" + fee: asset + fee_paying_account: protocol_id_type "account" + using_owner_authority: bool + proposal: protocol_id_type "proposal" +) + +withdraw_permission_create = new Serializer( + "withdraw_permission_create" + fee: asset + withdraw_from_account: protocol_id_type "account" + authorized_account: protocol_id_type "account" + withdrawal_limit: asset + withdrawal_period_sec: uint32 + periods_until_expiration: uint32 + period_start_time: time_point_sec +) + +withdraw_permission_update = new Serializer( + "withdraw_permission_update" + fee: asset + withdraw_from_account: protocol_id_type "account" + authorized_account: protocol_id_type "account" + permission_to_update: protocol_id_type "withdraw_permission" + withdrawal_limit: asset + withdrawal_period_sec: uint32 + period_start_time: time_point_sec + periods_until_expiration: uint32 +) + +withdraw_permission_claim = new Serializer( + "withdraw_permission_claim" + fee: asset + withdraw_permission: protocol_id_type "withdraw_permission" + withdraw_from_account: protocol_id_type "account" + withdraw_to_account: protocol_id_type "account" + amount_to_withdraw: asset + memo: optional memo_data +) + +withdraw_permission_delete = new Serializer( + "withdraw_permission_delete" + fee: asset + withdraw_from_account: protocol_id_type "account" + authorized_account: protocol_id_type "account" + withdrawal_permission: protocol_id_type "withdraw_permission" +) + +fill_order = new Serializer( + "fill_order" + fee: asset + order_id: object_id_type + account_id: protocol_id_type "account" + pays: asset + receives: asset +) + +fee_schedule = new Serializer( + "fee_schedule" + key_create_fee: uint32 + account_create_fee: uint32 + account_len8_fee: uint32 + account_len7_fee: uint32 + account_len6_fee: uint32 + account_len5_fee: uint32 + account_len4_fee: uint32 + account_len3_fee: uint32 + account_premium_fee: uint32 + account_whitelist_fee: uint32 + delegate_create_fee: uint32 + witness_withdraw_pay_fee: uint32 + transfer_fee: uint32 + limit_order_fee: uint32 + short_order_fee: uint32 + publish_feed_fee: uint32 + asset_create_fee: uint32 + asset_update_fee: uint32 + asset_issue_fee: uint32 + asset_fund_fee_pool_fee: uint32 + asset_settle_fee: uint32 + market_fee: uint32 + transaction_fee: uint32 + data_fee: uint32 + signature_fee: uint32 + global_parameters_update_fee: uint32 + prime_upgrade_fee: uint32 + withdraw_permission_update_fee: uint32 + create_bond_offer_fee: uint32 + cancel_bond_offer_fee: uint32 + accept_bond_offer_fee: uint32 + claim_bond_collateral_fee: uint32 + file_storage_fee_per_day: uint32 + vesting_balance_create_fee: uint32 + vesting_balance_withdraw_fee: uint32 + global_settle_fee: uint32 + worker_create_fee: uint32 + worker_delete_fee: uint32 +) + +chain_parameters = new Serializer( + "chain_parameters" + current_fees: fee_schedule + witness_pay_percent_of_accumulated: uint32 + block_interval: uint8 + maintenance_interval: uint32 + maximum_transaction_size: uint32 + maximum_block_size: uint32 + maximum_undo_history: uint32 + maximum_time_until_expiration: uint32 + maximum_proposal_lifetime: uint32 + maximum_asset_whitelist_authorities: uint8 + maximum_asset_feed_publishers: uint8 + maximum_authority_membership: uint16 + burn_percent_of_fee: uint16 + witness_percent_of_fee: uint16 + max_bulk_discount_percent_of_fee: uint16 + cashback_vesting_period_seconds: uint32 + bulk_discount_threshold_min: int64 + bulk_discount_threshold_max: int64 + count_non_prime_votes: bool + allow_non_prime_whitelists: bool + witness_pay_per_block: int64 + worker_budget_per_day: int64 +) + +global_parameters_update = new Serializer( + "global_parameters_update" + fee: asset + new_parameters: chain_parameters +) + +file_write = new Serializer( + "file_write" + fee: asset + payer: protocol_id_type "account" + file_id: protocol_id_type "file" + owner: protocol_id_type "account" + group: protocol_id_type "account" + flags: uint8 + offset: uint16 + data: bytes() + lease_seconds: uint32 + file_size: uint16 + precondition_checksum: optional bytes 20 +) + +vesting_balance_create = new Serializer( + "vesting_balance_create" + fee: asset + creator: protocol_id_type "account" + owner: protocol_id_type "account" + amount: asset + vesting_seconds: uint32 +) + +vesting_balance_withdraw = new Serializer( + "vesting_balance_withdraw" + fee: asset + vesting_balance: protocol_id_type "vesting_balance" + owner: protocol_id_type "account" + amount: asset +) + +bond_create_offer = new Serializer( + "bond_create_offer" + fee: asset + creator: protocol_id_type "account" + offer_to_borrow: bool + amount: asset + min_match: int64 + collateral_rate: price + min_loan_period_sec: uint32 + loan_period_sec: uint32 + interest_apr: uint16 +) + +bond_cancel_offer = new Serializer( + "bond_cancel_offer" + fee: asset + creator: protocol_id_type "account" + offer_id: protocol_id_type "bond_offer" + refund: asset +) + +bond_accept_offer = new Serializer( + "bond_accept_offer" + fee: asset + claimer: protocol_id_type "account" + lender: protocol_id_type "account" + borrower: protocol_id_type "account" + offer_id: protocol_id_type "bond_offer" + amount_borrowed: asset + amount_collateral: asset +) + +bond_claim_collateral = new Serializer( + "bond_claim_collateral" + fee: asset + claimer: protocol_id_type "account" + lender: protocol_id_type "account" + bond_id: protocol_id_type "bond" + payoff_amount: asset + collateral_claimed: asset +) + +refund_worker_type_initializer = new Serializer( + "refund_worker_type_initializer" +) + +vesting_balance_worker_type_initializer = new Serializer( + "vesting_balance_worker_type_initializer" + pay_vesting_period_days: uint16 +) + +initializer_type = static_variant [ + refund_worker_type_initializer + vesting_balance_worker_type_initializer +] + +worker_create = new Serializer( + "worker_create" + fee: asset + owner: protocol_id_type "account" + work_begin_date: time_point_sec + work_end_date: time_point_sec + daily_pay: int64 + initializer: initializer_type +) + +custom = new Serializer( + "custom" + fee: asset + payer: protocol_id_type "account" + required_auths: set protocol_id_type "account" + id: uint16 + data: bytes() +) + +operation.st_operations = [ + transfer + limit_order_create + short_order_create + limit_order_cancel + short_order_cancel + call_order_update + key_create + account_create + account_update + account_whitelist + account_transfer + asset_create + asset_update + asset_update_bitasset + asset_update_feed_producers + asset_issue + asset_burn + asset_fund_fee_pool + asset_settle + asset_global_settle + asset_publish_feed + delegate_create + witness_create + witness_withdraw_pay + proposal_create + proposal_update + proposal_delete + withdraw_permission_create + withdraw_permission_update + withdraw_permission_claim + withdraw_permission_delete + fill_order + global_parameters_update + file_write + vesting_balance_create + vesting_balance_withdraw + bond_create_offer + bond_cancel_offer + bond_accept_offer + bond_claim_collateral + worker_create + custom +] + +transaction = new Serializer( + "transaction" + ref_block_num: uint16 + ref_block_prefix: uint32 + relative_expiration: uint16 + operations: array operation +) + +signed_transaction = new Serializer( + "signed_transaction" + ref_block_num: uint16 + ref_block_prefix: uint32 + relative_expiration: uint16 + operations: array operation + signatures: map (protocol_id_type "key"), (bytes 65) +) + +## ------------------------------- +## Generated code end +# programs/js_operation_serializer +## ------------------------------- diff --git a/dl/src/chain/serializer_types.coffee b/dl/src/chain/serializer_types.coffee new file mode 100644 index 0000000000..d27140f6ca --- /dev/null +++ b/dl/src/chain/serializer_types.coffee @@ -0,0 +1,382 @@ +ByteBuffer = require '../common/bytebuffer' +Long = ByteBuffer.Long + +vt = require './serializer_validation' +ObjectId = require './object_id' +Serializer = require './serializer' +ChainTypes = require './chain_types' + +fp = require '../common/fast_parser' +Address = require '../ecc/address' +PublicKey = require '../ecc/key_public' + +module.exports = Types = {} + +Types.uint8 = + fromByteBuffer:(b)-> b.readUint8() + appendByteBuffer:(b, object)-> + b.writeUint8 object; return + fromObject:(object)-> object + toObject:(object, use_default = no)-> + return 0 if use_default and object is undefined + int = parseInt object + vt.require_range 0,0xFF,int, "uint8 #{object}" + int +Types.uint16 = + fromByteBuffer:(b)-> b.readUint16() + appendByteBuffer:(b, object)-> + b.writeUint16 object + return + fromObject:(object)-> object + toObject:(object, use_default = no)-> + return 0 if use_default and object is undefined + int = parseInt object + vt.require_range 0,0xFFFF,int, "uint16 #{object}" + int +Types.uint32 = + fromByteBuffer:(b)-> b.readUint32() + appendByteBuffer:(b, object)-> + b.writeUint32 object + return + fromObject:(object)-> object + toObject:(object, use_default = no)-> + return 0 if use_default and object is undefined + int = parseInt object + vt.require_range 0,0xFFFFFFFF,int, "uint32 #{object}" + int +Types.varint32 = + fromByteBuffer:(b)-> b.readVarint32() + appendByteBuffer:(b, object)-> + b.writeVarint32 object; return + fromObject:(object)-> object + toObject:(object, use_default = no)-> + return 0 if use_default and object is undefined + int = parseInt object + vt.require_range 0,0xFFFFFFFF,int, "uint32 #{object}" + int +Types.int64 = + fromByteBuffer:(b)-> + b.readInt64() + appendByteBuffer:(b, object)-> + b.writeInt64 vt.to_long object + return + fromObject:(object)-> + Long.fromString "" + vt.require_digits object + toObject:(object, use_default = no)-> + return "0" if use_default and object is undefined + object.toString() +Types.uint64 = + fromByteBuffer:(b)-> + b.readUint64() + appendByteBuffer:(b, object)-> + b.writeUint64 vt.to_long object + return + fromObject:(object)-> + Long.fromString "" + vt.require_digits object + toObject:(object, use_default = no)-> + return "0" if use_default and object is undefined + object.toString() + +Types.string = + fromByteBuffer:(b)-> + len = b.readVarint32() + b_copy = b.copy(b.offset, b.offset + len); b.skip len + new Buffer(b_copy.toBinary(), 'binary') + appendByteBuffer:(b, object)-> + b.writeVarint32(object.length) + b.append(object.toString('binary'), 'binary') + return + fromObject:(object)-> + new Buffer object + toObject:(object, use_default = no)-> + return "" if use_default and object is undefined + object.toString() + +Types.bytes = (size)-> + fromByteBuffer:(b)-> + if size is undefined + len = b.readVarint32() + b_copy = b.copy(b.offset, b.offset + len); b.skip len + new Buffer(b_copy.toBinary(), 'binary') + else + b_copy = b.copy(b.offset, b.offset + size); b.skip size + new Buffer(b_copy.toBinary(), 'binary') + appendByteBuffer:(b, object)-> + if size is undefined + b.writeVarint32(object.length) + b.append(object.toString('binary'), 'binary') + else + b.append(object.toString('binary'), 'binary') + return + fromObject:(object)-> + new Buffer object, 'hex' + toObject:(object, use_default = no)-> + if use_default and object is undefined + zeros=(num)-> new Array( num ).join( "00" ) + return zeros size + object.toString 'hex' + +Types.bool = + fromByteBuffer:(b)-> + b.readUint8() + appendByteBuffer:(b, object)-> + # supports boolean or integer + b.writeUint8 if object then 1 else 0 + #b.writeUint8 object + return + fromObject:(object)-> + if object then 1 else 0 + toObject:(object, use_default = no)-> + return no if use_default and object is undefined + if object then yes else no + +Types.array = (st_operation)-> + fromByteBuffer:(b)-> + for i in [0...b.readVarint32()] by 1 + st_operation.fromByteBuffer b + appendByteBuffer:(b, object)-> + b.writeVarint32 object.length + for o in object + st_operation.appendByteBuffer b, o + return + fromObject:(object)-> + for o in object + st_operation.fromObject o + toObject:(object, use_default = no)-> + if use_default and object is undefined + return [ st_operation.toObject(undefined, yes) ] + for o in object + st_operation.toObject o + +Types.time_point_sec = + fromByteBuffer:(b)-> b.readUint32() + appendByteBuffer:(b, object)-> + b.writeUint32 object + return + fromObject:(object)-> + Math.round( (new Date(object)).getTime() / 1000 ) + toObject:(object, use_default = no)-> + if use_default and object is undefined + return (new Date(0)).toISOString().split('.')[0] + int = parseInt object + vt.require_range 0,0xFFFFFFFF,int, "uint32 #{object}" + (new Date(int*1000)).toISOString().split('.')[0] + +# todo, set is sorted and unique +Types.set = Types.array + +# global_parameters_update_operation current_fees +Types.fixed_array = (count, st_operation)-> + fromByteBuffer:(b)-> + for i in [0...count] by 1 + st_operation.fromByteBuffer b + appendByteBuffer:(b, object)-> + for i in [0...count] by 1 + st_operation.appendByteBuffer b, object[i] + return + fromObject:(object)-> + for i in [0...count] by 1 + st_operation.fromObject object[i] + toObject:(object, use_default = no)-> + return if use_default and object is undefined + for i in [0...count] by 1 + st_operation.toObject undefined, yes + for i in [0...count] by 1 + st_operation.toObject object[i] + +### Supports instance numbers (11) or object types (1.3.11). Object type +validation is enforced when an object type is used. ### +id_type = (reserved_spaces, object_type)-> + vt.required reserved_spaces, "reserved_spaces" + vt.required object_type, "object_type" + fromByteBuffer:(b)-> + b.readVarint32() + appendByteBuffer:(b, object)-> + object = object.resolve if object.resolve isnt undefined + # convert 1.3.n into just n + if /^[0-9]+\.[0-9]+\.[0-9]+$/.test object + object = vt.get_instance reserved_spaces, object_type, object + b.writeVarint32 object + return + fromObject:(object)-> + object = object.resolve if object.resolve isnt undefined + if vt.is_digits object + return vt.to_number object + vt.get_instance reserved_spaces, object_type, object + toObject:(object, use_default = no)-> + object_type_id = ChainTypes.object_type[object_type] + if use_default and object is undefined + return "#{reserved_spaces}.#{object_type_id}.0" + object = object.resolve if object.resolve isnt undefined + if /^[0-9]+\.[0-9]+\.[0-9]+$/.test object + object = vt.get_instance reserved_spaces, object_type, object + + "#{reserved_spaces}.#{object_type_id}."+object + +Types.protocol_id_type = (name)-> + id_type ChainTypes.reserved_spaces.protocol_ids, name + +Types.object_id_type = + fromByteBuffer:(b)-> + ObjectId.fromByteBuffer b + appendByteBuffer:(b, object)-> + object = object.resolve if object.resolve isnt undefined + object = ObjectId.fromString object + object.appendByteBuffer b + return + fromObject:(object)-> + object = object.resolve if object.resolve isnt undefined + ObjectId.fromString object + toObject:(object, use_default = no)-> + if use_default and object is undefined + return "0.0.0" + if object.resolve isnt undefined + object = object.resolve + object = ObjectId.fromString object + object.toString() + +Types.vote_id = + TYPE: 0x000000FF + ID: 0xFFFFFF00 + fromByteBuffer:(b)-> + value = b.readUint32() + type: value & @TYPE + id: value & @ID + appendByteBuffer:(b, object)-> + value = object.id << 8 | object.type + b.writeUint32 value + return + fromObject:(object)-> + vt.required object, "vote_id" + vt.require_test /^[0-9]+:[0-9]+$/, object, "vote_id format #{object}" + [type, id] = object.split ':' + vt.require_range 0,0xff,type,"vote type #{object}" + vt.require_range 0,0xffffff,id,"vote id #{object}" + type:type + id:id + toObject:(object, use_default = no)-> + if use_default and object is undefined + return "0:0" + object.id +":"+ object.type + +Types.optional = (st_operation)-> + vt.required st_operation, "st_operation" + fromByteBuffer:(b)-> + unless b.readUint8() is 1 + return undefined + st_operation.fromByteBuffer b + appendByteBuffer:(b, object)-> + if object + b.writeUint8 1 + st_operation.appendByteBuffer b, object + else + b.writeUint8 0 + return + fromObject:(object)-> + return undefined if object is undefined + st_operation.fromObject object + toObject:(object, use_default = no)-> + if use_default and object is undefined + object = st_operation.toObject undefined, yes + if typeof object is "object" + object.__optional = "parent is optional" + else + object = __optional: object + return object + return undefined if object is undefined + st_operation.toObject object + +Types.static_variant = (_st_operations)-> + st_operations: _st_operations + fromByteBuffer:(b)-> + type_id = b.readVarint32() + st_operation = @st_operations[type_id] + vt.required st_operation, "operation #{type_id}" + [ + type_id + st_operation.fromByteBuffer b + ] + appendByteBuffer:(b, object)-> + type_id = object[0] + st_operation = @st_operations[type_id] + vt.required st_operation, "operation #{type_id}" + b.writeVarint32 type_id + st_operation.appendByteBuffer b, object[1] + return + fromObject:(object)-> + type_id = object[0] + st_operation = @st_operations[type_id] + vt.required st_operation, "operation #{type_id}" + [ + type_id + st_operation.fromObject object[1] + ] + toObject:(object, use_default = no)-> + if use_default and object is undefined + return [0, @st_operations[0].toObject(undefined, yes)] + type_id = object[0] + st_operation = @st_operations[type_id] + vt.required st_operation, "operation #{type_id}" + [ + type_id + st_operation.toObject object[1] + ] + +# todo, map has unique keys +Types.map = (key_st_operation, value_st_operation)-> + fromByteBuffer:(b)-> + for i in [0...b.readVarint32()] by 1 + [ + key_st_operation.fromByteBuffer b + value_st_operation.fromByteBuffer b + ] + appendByteBuffer:(b, object)-> + b.writeVarint32 object.length + for o in object + key_st_operation.appendByteBuffer b, o[0] + value_st_operation.appendByteBuffer b, o[1] + return + fromObject:(object)-> + for o in object + [ + key_st_operation.fromObject o[0] + value_st_operation.fromObject o[1] + ] + toObject:(object, use_default = no)-> + if use_default and object is undefined + return [ + [ + key_st_operation.toObject(undefined, yes) + value_st_operation.toObject(undefined, yes) + ] + ] + for o in object + [ + key_st_operation.toObject o[0] + value_st_operation.toObject o[1] + ] + +Types.public_key = + fromByteBuffer:(b)-> + fp.public_key b + appendByteBuffer:(b, object)-> + fp.public_key b, object + fromObject:(object)-> + PublicKey.fromBtsPublic object + toObject:(object, use_default = no)-> + if use_default and object is undefined + return "GPHXyz...public_key" + object.toBtsPublic() + +Types.address = + fromByteBuffer:(b)-> + fp.ripemd160 b + appendByteBuffer:(b, object)-> + fp.ripemd160 b, object + fromObject:(object)-> + Address.fromString object + toObject:(object, use_default = no)-> + if use_default and object is undefined + return "GPHXyz...address" + new Address(object.public_key).toString() + diff --git a/dl/src/chain/serializer_validation.coffee b/dl/src/chain/serializer_validation.coffee new file mode 100644 index 0000000000..76c7a6460c --- /dev/null +++ b/dl/src/chain/serializer_validation.coffee @@ -0,0 +1,127 @@ +ChainTypes = require './chain_types.coffee' +Long = require('../common/bytebuffer').Long + +MAX_SAFE_INT = 9007199254740991 + +module.exports= + + is_empty: is_empty=(value)-> + value is null or value is undefined + + required: required=(value, field_name="")-> + if is_empty value + throw new Error "value required for #{field_name}: #{value}" + value + + require_string: require_string=(value)-> + return value if is_empty value + if typeof value isnt "string" + throw new Error "string required: #{value}" + value + + require_number: require_number=(value)-> + return value if is_empty value + if typeof value isnt "number" + throw new Error "number required: #{value}" + value + + ### + Allows a string of digits (potentially very large number) or + a number type. An empty string or empty value is allowed. + ### + require_digits: require_digits=(value, field_name="")-> + return value if typeof value is "numeric" + unless /^[0-9]*$/.test value + throw new Error "Only digits allowed in #{field_name}: #{value}" + value + + is_digits: is_digits=(value)-> + return yes if typeof value is "numeric" + /^[0-9]+$/.test value + + + to_number: to_number=(value)-> + return value if is_empty value + int_value = if typeof value is "number" + value + else + parseInt value + if int_value > MAX_SAFE_INT + throw new Error "overflow #{value}" + int_value + + to_long:(value, field_name="")-> + return value if is_empty value + return value if Long.isLong value + @require_digits value, field_name + if typeof value is "number" + if value > MAX_SAFE_INT + throw new Error "overflow #{value}" + value = ""+value + Long.fromString value + + require_test:(regex, value, field_name="")-> + return value if is_empty value + unless regex.test value + throw new Error "unmatched #{regex} #{field_name}: #{value}" + value + + require_match:(regex, value, field_name="")-> + return value if is_empty value + match = value.match regex + if match is null + throw new Error "unmatched #{regex} #{field_name}: #{value}" + match + + require_range:(min,max,value, field_name="")-> + return value if is_empty value + if value < min or value > max + throw new Error "out of range #{value} #{field_name}: #{value}" + value + + require_object_type: require_object_type=( + reserved_spaces = 1, type, value + field_name="" + )-> + return value if is_empty value + object_type = ChainTypes.object_type[type] + unless object_type + throw new Error "Unknown object type #{type} #{field_name}: #{value}" + re = new RegExp "#{reserved_spaces}\.#{object_type}\.[0-9]+$" + unless re.test require_string value + throw new Error "Expecting #{type} in format "+ + "#{reserved_spaces}.#{object_type}.[0-9]+ "+ + "instead of #{value} #{field_name}: #{value}" + value + + get_instance: get_instance=(reserve_spaces, type, value, field_name)-> + return value if is_empty value + require_object_type reserve_spaces, type, value, field_name + to_number value.split('.')[2] + + require_relative_type: require_relative_type=(type, value, field_name)-> + require_object_type 0, type, value, field_name + value + + get_relative_instance: get_relative_instance=(type, value, field_name)-> + return value if is_empty value + require_object_type 0, type, value, field_name + to_number value.split('.')[2] + + require_protocol_type: require_protocol_type=(type, value, field_name)-> + require_object_type 1, type, value, field_name + value + + get_protocol_instance: get_protocol_instance=(type, value, field_name)-> + return value if is_empty value + require_object_type 1, type, value, field_name + to_number value.split('.')[2] + + require_implementation_type: require_implementation_type=(type, value, field_name)-> + require_object_type 2, type, value, field_name + value + + get_implementation_instance: get_implementation_instance=(type, value, field_name)-> + return value if is_empty value + require_object_type 2, type, value, field_name + to_number value.split('.')[2] diff --git a/dl/src/chain/transaction_helper.coffee b/dl/src/chain/transaction_helper.coffee new file mode 100644 index 0000000000..01b1e9ae9c --- /dev/null +++ b/dl/src/chain/transaction_helper.coffee @@ -0,0 +1,103 @@ + +module.exports = helper = {} + +api = require('../rpc_api/ApiInstances').instance() + +secureRandom = require 'secure-random' + +hash = require '../common/hash' +Promise = require '../common/Promise' +ByteBuffer = require('../common/bytebuffer') +Long = ByteBuffer.Long + +PrivateKey = require '../ecc/key_private' +Signature = require '../ecc/signature' +Aes = require '../ecc/aes' + +ChainTypes = require './chain_types' +so_type = require './serializer_operation_types' + +helper.get_owner_private=get_owner_private=(brain_key)-> + normalize_brain_key=(brain_key)-> + brain_key = brain_key.trim() + brain_key = brain_key.toUpperCase() + brain_key.split(/[\t\n\v\f\r ]+/).join ' ' + + brain_key = normalize_brain_key brain_key + PrivateKey.fromBuffer( + hash.sha256 hash.sha512 brain_key + " 0" + ) + +helper.get_active_private=get_active_private=(owner_private)-> + PrivateKey.fromBuffer( + hash.sha256 hash.sha512 owner_private.toWif() + " 0" + ) + +helper.unique_nonce_entropy = null +helper.unique_nonce_uint64=-> + entropy = helper.unique_nonce_entropy = ( + if helper.unique_nonce_entropy is null + #console.log('... secureRandom.randomUint8Array(1)[0]',secureRandom.randomUint8Array(1)[0]) + parseInt secureRandom.randomUint8Array(1)[0] + else + ++helper.unique_nonce_entropy % 256 + ) + long = Long.fromNumber Date.now() + #console.log('unique_nonce_uint64 date\t',ByteBuffer.allocate(8).writeUint64(long).toHex(0)) + #console.log('unique_nonce_uint64 entropy\t',ByteBuffer.allocate(8).writeUint64(Long.fromNumber(entropy)).toHex(0)) + long = long.shiftLeft(8).or(Long.fromNumber(entropy)) + #console.log('unique_nonce_uint64 shift8\t',ByteBuffer.allocate(8).writeUint64(long).toHex(0)) + long + +### Todo, set fees ### +helper.to_json=( tr, broadcast = false ) -> + ((tr, broadcast)-> + tr_object = so_type.signed_transaction.toObject tr + if broadcast + net = api.network_api() + console.log '... tr_object', JSON.stringify tr_object + net.exec "broadcast_transaction", [tr_object] + else + tr_object + )(tr, broadcast) + +helper.signed_tr_json=(tr, key_ids, private_keys)-> + tr_buffer = so_type.transaction.toBuffer tr + tr = so_type.transaction.toObject tr + tr.signatures = for i in [0...private_keys.length] by 1 + key_id = key_ids[i] + private_key = private_keys[i] + [ + key_id + Signature.signBuffer( tr_buffer, private_key ).toHex() + ] + tr + +helper.expire_in_min=(min)-> + Math.round(Date.now() / 1000) + (min*60) + +helper.seconds_from_now=(timeout_sec)-> + Math.round(Date.now() / 1000) + timeout_sec + +###* + Print to the console a JSON representation of any object in + serializer_operation_types.coffee +### +helper.template=(serializer_operation_type_name)-> + so = so_type[serializer_operation_type_name] + unless so + throw new Error "unknown serializer_operation_type #{serializer_operation_type_name}" + object = so.toObject undefined, yes + console.log JSON.stringify object,null,4 + return + +helper.new_operation=(serializer_operation_type_name)-> + so = so_type[serializer_operation_type_name] + unless so + throw new Error "unknown serializer_operation_type #{serializer_operation_type_name}" + object = so.toObject undefined, yes + so.fromObject object + +helper.instance=(object_id)-> + object_id.substring("0.0.".length) + diff --git a/dl/src/chain/transaction_operations.coffee b/dl/src/chain/transaction_operations.coffee new file mode 100644 index 0000000000..92c0a6fe94 --- /dev/null +++ b/dl/src/chain/transaction_operations.coffee @@ -0,0 +1,187 @@ +ChainTypes = require './chain_types' +ObjectId = require './object_id' +Signature = require '../ecc/signature' +ByteBuffer = require('../common/bytebuffer') +Long = ByteBuffer.Long +Aes = require '../ecc/aes' + +so_type = require './serializer_operation_types' +is_empty_user_input = require('../common/validation').is_empty_user_input +vt = require './serializer_validation' +required = vt.required +lookup = new (require './lookup')() +api = require('../rpc_api/ApiInstances').instance() +helper = require('../chain/transaction_helper') + +module.exports = _my = {} + +_my.signed_transaction = -> + + ref_block_num: 0 + ref_block_prefix: 0 + relative_expiration: 0 + operations: [] + signatures: [] + + add_operation: (operation) -> + required operation, "operation" + results = operation.get_operations() + for result in results + unless Array.isArray result + throw new Error "Expecting array [operation_id, operation]" + @operations.push result + return + + add_operation_type: (operation, type) -> + required operation, "operation" + required type, "type" + required type.operation_name, "operation_name" + operation_id = ChainTypes.operations[type.operation_name] + if operation_id is undefined + throw new Error "unknown operation: #{type.operation_name}" + @operations.push [operation_id, operation] + return + + set_expire_minutes:(min)-> + @ref_block_prefix = Math.round(Date.now()/1000) + (min*60) + + finalize:(key_ids, private_keys, broadcast = no)-> + ((tr, key_ids, private_keys, broadcast)-> + lookup.resolve().then ()-> + for op in tr.operations + if op[1]["finalize"] + op[1].finalize() + + tr_buffer = so_type.transaction.toBuffer tr + # Debug + # ByteBuffer.fromBinary(tr_buffer.toString('binary')).printDebug() + key_ids = [ key_ids ] unless Array.isArray key_ids + private_keys = [ private_keys ] unless Array.isArray private_keys + for i in [0...private_keys.length] by 1 + key_id = key_ids[i] + private_key = private_keys[i] + sig = Signature.signBuffer tr_buffer, private_key + tr.signatures.push [ key_id, sig.toBuffer() ] + tr_object = so_type.signed_transaction.toObject(tr) + return tr_object unless broadcast + api.network_api().exec("broadcast_transaction", [tr_object]) + , (error)-> + console.error 'finalize error', error, error.stack + )(@, key_ids, private_keys, broadcast) + +_my.key_create = -> + fee: + amount: Long.ZERO + asset_id: 0 + fee_paying_account: null + key_data: [ 1, "GPHXyx...public_key" ] + +_my.key_create.fromPublicKey = (public_key)-> + required public_key.Q, "PublicKey" + kc = _my.key_create() + kc.key_data[0] = 1 + kc.key_data[1] = public_key + kc + +_my.key_create.fromAddress = (address)-> + required address.addy, "Address" + kc = _my.key_create() + kc.key_data[0] = 0 + kc.key_data[1] = address + kc + + +class _my.account_create + _template = -> + fee: + amount: Long.ZERO + asset_id: 0 + registrar: 0 + referrer: 0 + referrer_percent: 100 + name: "" + owner: + weight_threshold: 1 + auths: [ [ ObjectId.fromString("0.2.0"), 1 ] ] + active: + weight_threshold: 1 + auths: [ [ ObjectId.fromString("0.2.1"), 1 ] ] + voting_account: 0 # 1.3.0 + memo_key: ObjectId.fromString("0.2.1") + num_witness: 0 + num_committee: 0 + vote: [ ] # 0:0 + + constructor:(@owner_key_create, @active_key_create)-> + for key in Object.keys _tmp = _template() + @[key] = _tmp[key] + required @owner_key_create + unless @active_key_create + @active_key_create = @owner_key_create + + get_operations:-> + @fee.asset_id = lookup.asset_id(@fee.asset_id) + @registrar = lookup.account_id(@registrar) + @referrer = lookup.account_id(@referrer) + @voting_account = lookup.account_id(@voting_account) + if @owner_key_create.fee_paying_account is null + @owner_key_create.fee_paying_account = @registrar + if @active_key_create.fee_paying_account is null + @active_key_create.fee_paying_account = @registrar + [ + [ ChainTypes.operations.key_create, @owner_key_create ] + [ ChainTypes.operations.key_create, @active_key_create ] + [ ChainTypes.operations.account_create, @ ] + ] + +class _my.transfer + _template = -> + fee : + amount : "0" + asset_id : 0# 1.4.0 + from: null # 1.3.0 + to: null # 1.3.0 + amount: + amount: "0" + asset_id: 0 # 1.4.0 + memo: + from: null # 1.2.0 + to: null # 1.2.0 + nonce: "0" # "0" + message: null + + constructor:( @memo_from_privkey )-> + for key in Object.keys _tmp = _template() + @[key] = _tmp[key] + + get_operations:-> + @fee.asset_id = lookup.asset_id(@fee.asset_id) + @from = lookup.account_id(@from) + @to = lookup.account_id(@to) + @amount.asset_id = lookup.asset_id(@amount.asset_id) + if is_empty_user_input @memo.message + @memo = undefined + else + to = @memo.to + @memo.from = lookup.memo_key_id(@memo.from) + @memo.to = lookup.memo_key_id(@memo.to) + if @memo_from_privkey + @memo.nonce = helper.unique_nonce_uint64() + @memo_to_public = lookup.memo_public_key(to) + else + empty_checksum = "\x00\x00\x00\x00" + @memo.message = new Buffer(empty_checksum + @memo.message) + + [[ ChainTypes.operations.transfer, @]] + + finalize:-> + if @memo_from_privkey + ciphertext = Aes.encrypt_with_checksum( + @memo_from_privkey + @memo_to_public.resolve + @memo.nonce + @memo.message + ) + @memo.message = new Buffer(ciphertext) + return + diff --git a/dl/src/common/Counterpart.js b/dl/src/common/Counterpart.js new file mode 100644 index 0000000000..b4b8f8469e --- /dev/null +++ b/dl/src/common/Counterpart.js @@ -0,0 +1,3 @@ +var counterpart = require("counterpart"); + +module.exports = counterpart; diff --git a/dl/src/common/Promise.js b/dl/src/common/Promise.js new file mode 100644 index 0000000000..b7721a473b --- /dev/null +++ b/dl/src/common/Promise.js @@ -0,0 +1 @@ +module.exports = Promise diff --git a/dl/src/common/bytebuffer.js b/dl/src/common/bytebuffer.js new file mode 100644 index 0000000000..2ac8a802dd --- /dev/null +++ b/dl/src/common/bytebuffer.js @@ -0,0 +1,10 @@ +//From bytebuffer 3.5.4 index.js, removed used of __dirname. Webpack's environment changes __dirname. +/*var path = require("path"), + ByteBufferNB = require( "../../node_modules/bytebuffer/dist/ByteBufferNB.js"), + ByteBufferAB = require( "../../node_modules/bytebuffer/dist/ByteBufferAB.js"); + +module.exports = ByteBufferNB; +module.exports.ByteBufferNB = ByteBufferNB; // node Buffer backed +module.exports.ByteBufferAB = ByteBufferAB; // ArrayBuffer backed +*/ +module.exports = require('../../lib/bytebuffer_3.5.4.js') diff --git a/dl/src/common/error_with_cause.coffee b/dl/src/common/error_with_cause.coffee new file mode 100644 index 0000000000..0199f973c4 --- /dev/null +++ b/dl/src/common/error_with_cause.coffee @@ -0,0 +1,17 @@ +###* Exception nesting. ### +class ErrorWithCause + + constructor: (@message, cause)-> + if cause?.message + @message = "cause\t#{cause.message}\t" + @message + + stack = (new Error).stack + if cause?.stack + stack = "caused by\n\t#{cause.stack}\t" + stack + + @stack = @message + "\n" + stack + + ErrorWithCause.throw = (message, cause)-> + throw new ErrorWithCause message, cause + +module.exports = ErrorWithCause diff --git a/dl/src/common/fast_parser.coffee b/dl/src/common/fast_parser.coffee new file mode 100644 index 0000000000..db10026d1e --- /dev/null +++ b/dl/src/common/fast_parser.coffee @@ -0,0 +1,44 @@ +PublicKey = require '../ecc/key_public' + +class FastParser + + FastParser.fixed_data = (b, len, buffer) -> + return unless b + if buffer + data = buffer.slice(0, len).toString('binary') + b.append data, 'binary' + while len-- > data.length + b.writeUint8 0 + return + else + b_copy = b.copy(b.offset, b.offset + len); b.skip len + new Buffer(b_copy.toBinary(), 'binary') + + FastParser.public_key = (b, public_key) -> + return unless b + if public_key + buffer = public_key.toBuffer() + b.append(buffer.toString('binary'), 'binary') + return + else + buffer = FastParser.fixed_data b, 33 + PublicKey.fromBuffer buffer + + FastParser.ripemd160 = (b, ripemd160) -> + return unless b + if ripemd160 + FastParser.fixed_data b, 20, ripemd160 + return + else + FastParser.fixed_data b, 20 + + FastParser.time_point_sec = (b, epoch) -> + if epoch + epoch = Math.ceil(epoch / 1000) + b.writeInt32 epoch + return + else + epoch = b.readInt32() # fc::time_point_sec + new Date epoch * 1000 + +module.exports = FastParser diff --git a/dl/src/common/hash.js b/dl/src/common/hash.js new file mode 100644 index 0000000000..6c5b06ddb8 --- /dev/null +++ b/dl/src/common/hash.js @@ -0,0 +1,45 @@ +var crypto = require('crypto') // derived from https://github.com/bitcoinjs/bitcoinjs-lib + +function hash160(buffer) { + return ripemd160(sha256(buffer)) +} + +function hash256(buffer) { + return sha256(sha256(buffer)) +} + +function ripemd160(buffer) { + return crypto.createHash('rmd160').update(buffer).digest() +} + +function sha1(buffer) { + return crypto.createHash('sha1').update(buffer).digest() +} + +function sha256(buffer) { + return crypto.createHash('sha256').update(buffer).digest() +} + +function sha512(buffer) { + return crypto.createHash('sha512').update(buffer).digest() +} + +// FIXME: Name not consistent with others +function HmacSHA256(buffer, secret) { + return crypto.createHmac('sha256', secret).update(buffer).digest() +} + +function HmacSHA512(buffer, secret) { + return crypto.createHmac('sha512', secret).update(buffer).digest() +} + +module.exports = { + ripemd160: ripemd160, + sha1: sha1, + sha256: sha256, + sha512: sha512, + hash160: hash160, + hash256: hash256, + HmacSHA256: HmacSHA256, + HmacSHA512: HmacSHA512 +} diff --git a/dl/src/common/utils.js b/dl/src/common/utils.js new file mode 100644 index 0000000000..b97c6413dc --- /dev/null +++ b/dl/src/common/utils.js @@ -0,0 +1,20 @@ +let id_regex = /\b\d+(\.\d+){2}\b/; + +var Utils = { + get_object_id: (obj_id) => { + let id_regex_res = id_regex.exec(obj_id); + return id_regex_res ? Number.parseInt(id_regex_res[1]) : 0; + }, + + is_object_id: (obj_id) => { + let match = id_regex.exec(obj_id); + return (match !== null && obj_id.split(".").length === 3); + }, + + get_asset_precision: (precision) => { + return Math.pow(10, precision); + } + +}; + +module.exports = Utils; diff --git a/dl/src/common/validation.coffee b/dl/src/common/validation.coffee new file mode 100644 index 0000000000..ff2039d508 --- /dev/null +++ b/dl/src/common/validation.coffee @@ -0,0 +1,26 @@ +###* +Valid names are all lower case, start with [a-z] and may +have "." or "-" in the name along with a single '/'. The +next character after a "/", "." or "-" cannot be [0-9] or +another '.', '-'. +### +is_account_name = (value)-> + return no if is_empty value + length = value.length + return no if length < 3 or length > 64 + /^(([a-z][a-z0-9]*([\.-][a-z][a-z0-9]*)*)(\/([a-z][a-z0-9]*([\.-][a-z][a-z0-9]*)*))?)$/.test value + +is_empty = (value)-> + return yes if value is null or value is undefined + return yes if value.length is 0 + return no + +is_empty_user_input = (value)-> + return yes if is_empty value + return yes if (value+"").trim() is "" + return no + +module.exports = + is_account_name: is_account_name + is_empty: is_empty + is_empty_user_input: is_empty_user_input diff --git a/dl/src/ecc/address.coffee b/dl/src/ecc/address.coffee new file mode 100644 index 0000000000..758c3ec088 --- /dev/null +++ b/dl/src/ecc/address.coffee @@ -0,0 +1,48 @@ +assert = require 'assert' +ByteBuffer = require '../common/bytebuffer' +config = require '../chain/config' +hash = require '../common/hash' +base58 = require 'bs58' + +class Address + + constructor: (@addy) -> + + Address.fromBuffer = (buffer) -> + _hash = hash.sha512(buffer) + addy = hash.ripemd160(_hash) + new Address(addy) + + Address.fromString = (string) -> + prefix = string.slice 0, config.address_prefix.length + assert.equal config.address_prefix, prefix, "Expecting key to begin with #{config.address_prefix}, instead got #{prefix}" + addy = string.slice config.address_prefix.length + addy = new Buffer(base58.decode addy, 'binary') + checksum = addy.slice -4 + addy = addy.slice 0, -4 + new_checksum = hash.ripemd160 addy + new_checksum = new_checksum.slice 0, 4 + assert.deepEqual checksum, new_checksum, 'Checksum did not match' + new Address(addy) + + ###* @return Address - Compressed PTS format (by default) ### + Address.fromPublic = (public_key, compressed = true, version = 56) -> + sha2 = hash.sha256 public_key.toBuffer compressed + rep = hash.ripemd160 sha2 + versionBuffer = new Buffer(1) + versionBuffer.writeUInt8((0xFF & version), 0) + addr = Buffer.concat [versionBuffer, rep] + check = hash.sha256 addr + check = hash.sha256 check + buffer = Buffer.concat [addr, check.slice 0, 4] + new Address hash.ripemd160 buffer + + toBuffer: -> + @addy + + toString: -> + checksum = hash.ripemd160 @addy + addy = Buffer.concat [@addy, checksum.slice 0, 4] + config.address_prefix + base58.encode addy + +module.exports = Address diff --git a/dl/src/ecc/aes.coffee b/dl/src/ecc/aes.coffee new file mode 100644 index 0000000000..55624bdab4 --- /dev/null +++ b/dl/src/ecc/aes.coffee @@ -0,0 +1,120 @@ +# https://code.google.com/p/crypto-js +CryptoJS = require("crypto-js") +assert = require("assert") +ByteBuffer = require("../common/bytebuffer") +Long = ByteBuffer.Long +hash = require("../common/hash") + +class Aes + + constructor: (@iv, @key) -> + + clear:->@iv = @key = undefined + + # TODO arg should be a binary type... HEX works best with crypto-js + Aes.fromSha512 = (hash) -> + assert.equal hash.length, 128, "A Sha512 in HEX should be 128 characters long, instead got #{hash.length}" + #https://github.com/InvictusInnovations/fc/blob/978de7885a8065bc84b07bfb65b642204e894f55/src/crypto/aes.cpp#L330 + #Bitshares aes_decrypt uses part of the password hash as the initilization vector + iv = CryptoJS.enc.Hex.parse(hash.substring(64, 96)) + key = CryptoJS.enc.Hex.parse(hash.substring(0, 64)) + new Aes(iv, key) + + Aes.fromSeed = (seed) -> + assert seed, "seed is required" + _hash = hash.sha512 seed + _hash = _hash.toString('hex') + Aes.fromSha512(_hash) + + Aes.decrypt_with_checksum = (private_key, public_key, nonce, message) -> + unless Buffer.isBuffer message + message = new Buffer message + + S = private_key.get_shared_secret public_key + aes = Aes.fromSeed Buffer.concat [ + new Buffer(nonce) + new Buffer(S.toString('hex')) + ] + planebuffer = aes.decrypt message + unless planebuffer.length >= 4 + throw new Error "Invalid key, could not decrypt message" + + #console.log('... planebuffer',planebuffer) + checksum = planebuffer.slice 0, 4 + plaintext = planebuffer.slice(4).toString() + + #console.log('... checksum',checksum.toString('hex')) + #console.log('... plaintext',plaintext) + + # reverse() converts to big-endian (matches the c++ memo) + new_checksum = hash.sha256 plaintext + new_checksum = new_checksum.slice 0, 4 + new_checksum = new_checksum.toString('binary').split("").reverse().join("") + + unless checksum.toString('binary') is new_checksum + throw new Error "Invalid key, could not decrypt message" + + plaintext + + Aes.encrypt_with_checksum = (private_key, public_key, nonce, message) -> + unless Buffer.isBuffer message + message = new Buffer message + + S = private_key.get_shared_secret public_key + aes = Aes.fromSeed Buffer.concat [ + new Buffer(nonce) + new Buffer(S.toString('hex')) + ] + # reverse() converts to big-endian (matches the c++ memo) + checksum = hash.sha256(message).slice 0,4 + checksum = checksum.toString('binary').split("").reverse().join("") + checksum = new Buffer(checksum, 'binary') + payload = Buffer.concat [checksum, message] + aes.encrypt payload + + _decrypt_word_array: (cipher) -> + # https://code.google.com/p/crypto-js/#Custom_Key_and_IV + # see wallet_records.cpp master_key::decrypt_key + CryptoJS.AES.decrypt( + ciphertext: cipher + salt: null + , @key, + iv: @iv + ) + + _encrypt_word_array: (plaintext) -> + #https://code.google.com/p/crypto-js/issues/detail?id=85 + cipher = CryptoJS.AES.encrypt plaintext, @key, {iv: @iv} + CryptoJS.enc.Base64.parse cipher.toString() + + decrypt: (cipher_buffer) -> + assert cipher_buffer, "Missing cipher text" + # hex is the only common format + hex = @decryptHex(cipher_buffer.toString('hex')) + new Buffer(hex, 'hex') + + encrypt: (plaintext_buffer) -> + #assert plaintext_buffer, "Missing plain text" + # hex is the only common format + hex = @encryptHex(plaintext_buffer.toString('hex')) + new Buffer(hex, 'hex') + + ### ### + + decryptHex: (cipher) -> + assert cipher, "Missing cipher text" + # Convert data into word arrays (used by Crypto) + cipher_array = CryptoJS.enc.Hex.parse cipher + plainwords = @_decrypt_word_array cipher_array + CryptoJS.enc.Hex.stringify plainwords + + encryptHex: (plainhex) -> + #assert plainhex, "Missing plain text" + plain_array = CryptoJS.enc.Hex.parse plainhex + cipher_array = @_encrypt_word_array plain_array + CryptoJS.enc.Hex.stringify cipher_array + + ### ### + +module.exports = Aes + diff --git a/dl/src/ecc/ecdsa.js b/dl/src/ecc/ecdsa.js new file mode 100644 index 0000000000..b737085057 --- /dev/null +++ b/dl/src/ecc/ecdsa.js @@ -0,0 +1,202 @@ +var assert = require('assert') // from github.com/bitcoinjs/bitcoinjs-lib from github.com/cryptocoinjs/ecdsa +var crypto = require('../common/hash') +var enforceType = require('./types') + +var BigInteger = require('bigi') +var ECSignature = require('./ecsignature') + +// https://tools.ietf.org/html/rfc6979#section-3.2 +function deterministicGenerateK(curve, hash, d, nonce) { + + enforceType('Buffer', hash) + enforceType(BigInteger, d) + + if (nonce) { + hash = crypto.sha256(Buffer.concat([hash, new Buffer(nonce)])) + } + + // sanity check + assert.equal(hash.length, 32, 'Hash must be 256 bit') + + var x = d.toBuffer(32) + var k = new Buffer(32) + var v = new Buffer(32) + + // Step B + v.fill(1) + + // Step C + k.fill(0) + + // Step D + k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k) + + // Step E + v = crypto.HmacSHA256(v, k) + + // Step F + k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k) + + // Step G + v = crypto.HmacSHA256(v, k) + + // Step H1/H2a, ignored as tlen === qlen (256 bit) + // Step H2b + v = crypto.HmacSHA256(v, k) + + var T = BigInteger.fromBuffer(v) + + // Step H3, repeat until T is within the interval [1, n - 1] + while ((T.signum() <= 0) || (T.compareTo(curve.n) >= 0)) { + k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0])]), k) + v = crypto.HmacSHA256(v, k) + + T = BigInteger.fromBuffer(v) + } + + return T + +} +// bitshares-js using nonce to find canonically valid signature +function sign(curve, hash, d, nonce) { + var k = deterministicGenerateK(curve, hash, d, nonce) + + var n = curve.n + var G = curve.G + var Q = G.multiply(k) + var e = BigInteger.fromBuffer(hash) + + var r = Q.affineX.mod(n) + assert.notEqual(r.signum(), 0, 'Invalid R value') + + var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n) + assert.notEqual(s.signum(), 0, 'Invalid S value') + + var N_OVER_TWO = n.shiftRight(1) + + // enforce low S values, see bip62: 'low s values in signatures' + if (s.compareTo(N_OVER_TWO) > 0) { + s = n.subtract(s) + } + + return new ECSignature(r, s) +} + +function verifyRaw(curve, e, signature, Q) { + var n = curve.n + var G = curve.G + + var r = signature.r + var s = signature.s + + // 1.4.1 Enforce r and s are both integers in the interval [1, n − 1] + if (r.signum() <= 0 || r.compareTo(n) >= 0) return false + if (s.signum() <= 0 || s.compareTo(n) >= 0) return false + + // c = s^-1 mod n + var c = s.modInverse(n) + + // 1.4.4 Compute u1 = es^−1 mod n + // u2 = rs^−1 mod n + var u1 = e.multiply(c).mod(n) + var u2 = r.multiply(c).mod(n) + + // 1.4.5 Compute R = (xR, yR) = u1G + u2Q + var R = G.multiplyTwo(u1, Q, u2) + var v = R.affineX.mod(n) + + // 1.4.5 (cont.) Enforce R is not at infinity + if (curve.isInfinity(R)) return false + + // 1.4.8 If v = r, output "valid", and if v != r, output "invalid" + return v.equals(r) +} + +function verify(curve, hash, signature, Q) { + // 1.4.2 H = Hash(M), already done by the user + // 1.4.3 e = H + var e = BigInteger.fromBuffer(hash) + + return verifyRaw(curve, e, signature, Q) +} + +/** + * Recover a public key from a signature. + * + * See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public + * Key Recovery Operation". + * + * http://www.secg.org/download/aid-780/sec1-v2.pdf + */ +function recoverPubKey(curve, e, signature, i) { + assert.strictEqual(i & 3, i, 'Recovery param is more than two bits') + + var n = curve.n + var G = curve.G + + var r = signature.r + var s = signature.s + + assert(r.signum() > 0 && r.compareTo(n) < 0, 'Invalid r value') + assert(s.signum() > 0 && s.compareTo(n) < 0, 'Invalid s value') + + // A set LSB signifies that the y-coordinate is odd + var isYOdd = i & 1 + + // The more significant bit specifies whether we should use the + // first or second candidate key. + var isSecondKey = i >> 1 + + // 1.1 Let x = r + jn + var x = isSecondKey ? r.add(n) : r + var R = curve.pointFromX(isYOdd, x) + + // 1.4 Check that nR is at infinity + var nR = R.multiply(n) + assert(curve.isInfinity(nR), 'nR is not a valid curve point') + + // Compute -e from e + var eNeg = e.negate().mod(n) + + // 1.6.1 Compute Q = r^-1 (sR - eG) + // Q = r^-1 (sR + -eG) + var rInv = r.modInverse(n) + + var Q = R.multiplyTwo(s, G, eNeg).multiply(rInv) + curve.validate(Q) + + return Q +} + +/** + * Calculate pubkey extraction parameter. + * + * When extracting a pubkey from a signature, we have to + * distinguish four different cases. Rather than putting this + * burden on the verifier, Bitcoin includes a 2-bit value with the + * signature. + * + * This function simply tries all four cases and returns the value + * that resulted in a successful pubkey recovery. + */ +function calcPubKeyRecoveryParam(curve, e, signature, Q) { + for (var i = 0; i < 4; i++) { + var Qprime = recoverPubKey(curve, e, signature, i) + + // 1.6.2 Verify Q + if (Qprime.equals(Q)) { + return i + } + } + + throw new Error('Unable to find valid recovery factor') +} + +module.exports = { + calcPubKeyRecoveryParam: calcPubKeyRecoveryParam, + deterministicGenerateK: deterministicGenerateK, + recoverPubKey: recoverPubKey, + sign: sign, + verify: verify, + verifyRaw: verifyRaw +} diff --git a/dl/src/ecc/ecsignature.js b/dl/src/ecc/ecsignature.js new file mode 100644 index 0000000000..98f258e099 --- /dev/null +++ b/dl/src/ecc/ecsignature.js @@ -0,0 +1,126 @@ +var assert = require('assert') // from https://github.com/bitcoinjs/bitcoinjs-lib +var enforceType = require('./types') + +var BigInteger = require('bigi') + +function ECSignature(r, s) { + enforceType(BigInteger, r) + enforceType(BigInteger, s) + + this.r = r + this.s = s +} + +// Import operations +ECSignature.parseCompact = function(buffer) { + assert.equal(buffer.length, 65, 'Invalid signature length') + var i = buffer.readUInt8(0) - 27 + + // At most 3 bits + assert.equal(i, i & 7, 'Invalid signature parameter') + var compressed = !!(i & 4) + + // Recovery param only + i = i & 3 + + var r = BigInteger.fromBuffer(buffer.slice(1, 33)) + var s = BigInteger.fromBuffer(buffer.slice(33)) + + return { + compressed: compressed, + i: i, + signature: new ECSignature(r, s) + } +} + +ECSignature.fromDER = function(buffer) { + assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence') + assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length') + assert.equal(buffer.readUInt8(2), 0x02, 'Expected a DER integer') + + var rLen = buffer.readUInt8(3) + assert(rLen > 0, 'R length is zero') + + var offset = 4 + rLen + assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a DER integer (2)') + + var sLen = buffer.readUInt8(offset + 1) + assert(sLen > 0, 'S length is zero') + + var rB = buffer.slice(4, offset) + var sB = buffer.slice(offset + 2) + offset += 2 + sLen + + if (rLen > 1 && rB.readUInt8(0) === 0x00) { + assert(rB.readUInt8(1) & 0x80, 'R value excessively padded') + } + + if (sLen > 1 && sB.readUInt8(0) === 0x00) { + assert(sB.readUInt8(1) & 0x80, 'S value excessively padded') + } + + assert.equal(offset, buffer.length, 'Invalid DER encoding') + var r = BigInteger.fromDERInteger(rB) + var s = BigInteger.fromDERInteger(sB) + + assert(r.signum() >= 0, 'R value is negative') + assert(s.signum() >= 0, 'S value is negative') + + return new ECSignature(r, s) +} + +// FIXME: 0x00, 0x04, 0x80 are SIGHASH_* boundary constants, importing Transaction causes a circular dependency +ECSignature.parseScriptSignature = function(buffer) { + var hashType = buffer.readUInt8(buffer.length - 1) + var hashTypeMod = hashType & ~0x80 + + assert(hashTypeMod > 0x00 && hashTypeMod < 0x04, 'Invalid hashType') + + return { + signature: ECSignature.fromDER(buffer.slice(0, -1)), + hashType: hashType + } +} + +// Export operations +ECSignature.prototype.toCompact = function(i, compressed) { + if (compressed) i += 4 + i += 27 + + var buffer = new Buffer(65) + buffer.writeUInt8(i, 0) + + this.r.toBuffer(32).copy(buffer, 1) + this.s.toBuffer(32).copy(buffer, 33) + + return buffer +} + +ECSignature.prototype.toDER = function() { + var rBa = this.r.toDERInteger() + var sBa = this.s.toDERInteger() + + var sequence = [] + + // INTEGER + sequence.push(0x02, rBa.length) + sequence = sequence.concat(rBa) + + // INTEGER + sequence.push(0x02, sBa.length) + sequence = sequence.concat(sBa) + + // SEQUENCE + sequence.unshift(0x30, sequence.length) + + return new Buffer(sequence) +} + +ECSignature.prototype.toScriptSignature = function(hashType) { + var hashTypeBuffer = new Buffer(1) + hashTypeBuffer.writeUInt8(hashType, 0) + + return Buffer.concat([this.toDER(), hashTypeBuffer]) +} + +module.exports = ECSignature diff --git a/dl/src/ecc/key_private.coffee b/dl/src/ecc/key_private.coffee new file mode 100644 index 0000000000..925c907c9d --- /dev/null +++ b/dl/src/ecc/key_private.coffee @@ -0,0 +1,90 @@ +ecurve = require 'ecurve' +Point = ecurve.Point +secp256k1 = ecurve.getCurveByName 'secp256k1' +BigInteger = require 'bigi' +base58 = require 'bs58' +assert = require 'assert' +hash = require '../common/hash' + +PublicKey = require './key_public' +Aes = require './aes' + +class PrivateKey + + ###* + @param {BigInteger} + ### + constructor: (@d) -> + + PrivateKey.fromBuffer = (buf) -> + assert.equal 32, buf.length, "Expecting 32 bytes, instead got #{buf.length}" + new PrivateKey BigInteger.fromBuffer(buf) + + PrivateKey.fromSeed = (seed) -> # generate_private_key + PrivateKey.fromBuffer hash.sha256 seed + + PrivateKey.fromWif = (private_wif) -> + private_wif = new Buffer base58.decode private_wif + version = private_wif.readUInt8(0) + assert.equal 0x80, version, "Expected version #{0x80}, instead got #{version}" + # checksum includes the version + private_key = private_wif.slice 0, -4 + checksum = private_wif.slice -4 + new_checksum = hash.sha256 private_key + new_checksum = hash.sha256 new_checksum + new_checksum = new_checksum.slice 0, 4 + assert.deepEqual checksum, new_checksum#, 'Invalid checksum' + PrivateKey.fromBuffer private_key.slice 1 + + toWif: -> + private_key = @toBuffer() + # checksum includes the version + private_key = Buffer.concat [new Buffer([0x80]), private_key] + checksum = hash.sha256 private_key + checksum = hash.sha256 checksum + checksum = checksum.slice 0, 4 + private_wif = Buffer.concat [private_key, checksum] + base58.encode private_wif + + ###* + @return {Point} + ### + toPublicKeyPoint: -> + Q = secp256k1.G.multiply(@d) + + toPublicKey: -> + PublicKey.fromPoint @toPublicKeyPoint() + + toBuffer: -> + @d.toBuffer() + + ###* ECIES ### + get_shared_secret:(public_key)-> + KB = public_key.toUncompressed().toBuffer() + KBP = Point.fromAffine( + secp256k1 + x = BigInteger.fromBuffer KB.slice 1,33 + y = BigInteger.fromBuffer KB.slice 33,65 + ) + r = @toBuffer() + P = KBP.multiply BigInteger.fromBuffer r + S = P.affineX.toBuffer {size: 32} + # SHA512 used in ECIES + hash.sha512 S + + ### ### + + toByteBuffer: () -> + b = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN) + @appendByteBuffer(b) + b.copy 0, b.offset + + PrivateKey.fromHex = (hex) -> + PrivateKey.fromBuffer new Buffer hex, 'hex' + + toHex: -> + @toBuffer().toString 'hex' + + ### ### + +module.exports = PrivateKey diff --git a/dl/src/ecc/key_public.coffee b/dl/src/ecc/key_public.coffee new file mode 100644 index 0000000000..af845218ad --- /dev/null +++ b/dl/src/ecc/key_public.coffee @@ -0,0 +1,110 @@ +BigInteger = require 'bigi' +ecurve = require('ecurve') +secp256k1 = ecurve.getCurveByName 'secp256k1' +BigInteger = require 'bigi' +base58 = require 'bs58' +hash = require '../common/hash' +config = require '../chain/config' +assert = require 'assert' + +# !!! Importing Address here will break transactions in: npm test +#{Address} = require './address' + +class PublicKey + + ###* @param {ecurve.Point} public key ### + constructor: (@Q) -> + + PublicKey.fromBinary = (bin) -> + PublicKey.fromBuffer new Buffer bin, 'binary' + + PublicKey.fromBuffer = (buffer) -> + new PublicKey ecurve.Point.decodeFrom secp256k1, buffer + + toBuffer:(compressed = @Q.compressed) -> + @Q.getEncoded compressed + + PublicKey.fromPoint = (point) -> + new PublicKey point + + toUncompressed: -> + buf = @Q.getEncoded(false) + point = ecurve.Point.decodeFrom secp256k1, buf + PublicKey.fromPoint point + + ###* bts::blockchain::address (unique but not a full public key) ### + toBlockchainAddress: -> + #address = Address.fromBuffer(@toBuffer()) + #assert.deepEqual address.toBuffer(), h + pub_buf = @toBuffer() + pub_sha = hash.sha512 pub_buf + hash.ripemd160 pub_sha + + ###* + Full public key + {return} string + ### + toBtsPublic: -> + pub_buf = @toBuffer() + checksum = hash.ripemd160 pub_buf + addy = Buffer.concat [pub_buf, checksum.slice 0, 4] + config.address_prefix + base58.encode addy + + ###* + {param1} public_key string + {return} PublicKey + ### + PublicKey.fromBtsPublic = (public_key) -> + prefix = public_key.slice 0, config.address_prefix.length + assert.equal config.address_prefix, prefix, "Expecting key to begin with #{config.address_prefix}, instead got #{prefix}" + public_key = public_key.slice config.address_prefix.length + + public_key = new Buffer(base58.decode public_key, 'binary') + checksum = public_key.slice -4 + public_key = public_key.slice 0, -4 + new_checksum = hash.ripemd160 public_key + new_checksum = new_checksum.slice 0, 4 + assert.deepEqual checksum, new_checksum, 'Checksum did not match' + PublicKey.fromBuffer public_key + + toBtsAddy: -> + pub_buf = @toBuffer() + pub_sha = hash.sha512 pub_buf + addy = hash.ripemd160 pub_sha + checksum = hash.ripemd160 addy + addy = Buffer.concat [addy, checksum.slice 0, 4] + config.address_prefix + base58.encode addy + + toPtsAddy: -> + pub_buf = @toBuffer() + pub_sha = hash.sha256 pub_buf + addy = hash.ripemd160 pub_sha + addy = Buffer.concat [new Buffer([0x38]), addy] #version 56(decimal) + + checksum = hash.sha256 addy + checksum = hash.sha256 checksum + + addy = Buffer.concat [addy, checksum.slice 0, 4] + base58.encode addy + + + ### ### + + toByteBuffer: () -> + b = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN) + @appendByteBuffer(b) + b.copy 0, b.offset + + PublicKey.fromHex = (hex) -> + PublicKey.fromBuffer new Buffer hex, 'hex' + + toHex: -> + @toBuffer().toString 'hex' + + PublicKey.fromBtsPublicHex = (hex) -> + PublicKey.fromBtsPublic new Buffer hex, 'hex' + + ### ### + + +module.exports = PublicKey diff --git a/dl/src/ecc/signature.coffee b/dl/src/ecc/signature.coffee new file mode 100644 index 0000000000..f379155464 --- /dev/null +++ b/dl/src/ecc/signature.coffee @@ -0,0 +1,115 @@ +ecdsa = require './ecdsa' +hash = require '../common/hash' +curve = require('ecurve').getCurveByName 'secp256k1' +assert = require 'assert' +BigInteger = require 'bigi' +PublicKey = require './key_public' + +class Signature + + constructor: (@r, @s, @i) -> + assert.equal @r isnt null, true, 'Missing parameter' + assert.equal @s isnt null, true, 'Missing parameter' + assert.equal @i isnt null, true, 'Missing parameter' + + Signature.fromBuffer = (buf) -> + assert.equal buf.length, 65, 'Invalid signature length' + + i = buf.readUInt8(0) + + # At most 3 bits (bitcoinjs-lib, ecsignature.js::parseCompact) + assert.equal i - 27, i - 27 & 7, 'Invalid signature parameter' + + #compressed = !!(i & 4) + + r = BigInteger.fromBuffer buf.slice 1, 33 + s = BigInteger.fromBuffer buf.slice 33 + new Signature r, s, i + + toBuffer: () -> + buf = new Buffer 65 + buf.writeUInt8(@i, 0) + @r.toBuffer(32).copy buf, 1 + @s.toBuffer(32).copy buf, 33 + buf + + recoverPublicKeyFromBuffer: (buffer) -> + @recoverPublicKey hash.sha256 buffer + + recoverPublicKey: (sha256_buffer) -> + e = BigInteger.fromBuffer(sha256_buffer) + i = @i + i = i & 3 # Recovery param only + Q = ecdsa.recoverPubKey(curve, e, this, i) + PublicKey.fromPoint Q + + ### + @param {Buffer} + @param {./PrivateKey} + @return {./Signature} + ### + Signature.signBuffer = (buf, private_key) -> + _hash = hash.sha256 buf + nonce = 0 + i = null + # https://github.com/bitcoinjs/bitcoinjs-lib/issues/334 + while true + ecsignature = ecdsa.sign curve, _hash, private_key.d, nonce++ + der = ecsignature.toDER() + lenR = der[3] + lenS = der[5+lenR] + #console.log 'len r',lenR, 'len s',lenS + if lenR is 32 and lenS is 32 # canonical + e = BigInteger.fromBuffer(_hash); + i = ecdsa.calcPubKeyRecoveryParam curve, e, ecsignature, private_key.toPublicKey().Q + i += 4 #compressed + i += 27 #compact + break + + # signing is slow, keep an eye out for this... + console.log "WARN: at least 10 attempts to find canonical signature" if nonce is 10 + throw new Error "Too many attempts: "+nonce if nonce is 100 + + new Signature ecsignature.r, ecsignature.s, i + + Signature.sign = (string, private_key)-> + Signature.signBuffer new Buffer(string), private_key + + ###* + @param {Buffer} un-hashed + @param {./PublicKey} + @return {boolean} + ### + verifyBuffer: (buf, public_key) -> + _hash = hash.sha256(buf) + @verifyHash(_hash, public_key) + + verifyHash: (hash, public_key) -> + assert.equal hash.length, 32, "A SHA 256 should be 32 bytes long, instead got #{hash.length}" + ecdsa.verify curve, hash, {r:@r, s:@s}, public_key.Q + + ### ### + + toByteBuffer: () -> + b = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN) + @appendByteBuffer(b) + b.copy 0, b.offset + + Signature.fromHex = (hex) -> + Signature.fromBuffer new Buffer hex, "hex" + + toHex: () -> + @toBuffer().toString "hex" + + Signature.signHex = (hex, private_key) -> + buf = new Buffer hex, 'hex' + @signBuffer buf, private_key + + verifyHex: (hex, public_key) -> + buf = new Buffer hex, 'hex' + @verifyBuffer buf, public_key + + ### ### + + +module.exports = Signature diff --git a/dl/src/ecc/types.js b/dl/src/ecc/types.js new file mode 100644 index 0000000000..f4e507b02f --- /dev/null +++ b/dl/src/ecc/types.js @@ -0,0 +1,40 @@ +module.exports = function enforce(type, value) { // Copied from https://github.com/bitcoinjs/bitcoinjs-lib + switch (type) { + case 'Array': { + if (Array.isArray(value)) return + break + } + + case 'Boolean': { + if (typeof value === 'boolean') return + break + } + + case 'Buffer': { + if (Buffer.isBuffer(value)) return + break + } + + case 'Number': { + if (typeof value === 'number') return + break + } + + case 'String': { + if (typeof value === 'string') return + break + } + + default: { + if (getName(value.constructor) === getName(type)) return + } + } + + throw new TypeError('Expected ' + (getName(type) || type) + ', got ' + value) +} + +function getName(fn) { + // Why not fn.name: https://kangax.github.io/compat-table/es6/#function_name_property + var match = fn.toString().match(/function (.*?)\(/) + return match ? match[1] : null +} diff --git a/dl/src/rpc_api/ApiInstances.js b/dl/src/rpc_api/ApiInstances.js new file mode 100644 index 0000000000..17671dc1dc --- /dev/null +++ b/dl/src/rpc_api/ApiInstances.js @@ -0,0 +1,50 @@ +var WebSocketRpc = require("./WebSocketRpc"); +var GrapheneApi = require("./GrapheneApi"); + +var Apis = (function () { + + var apis_instance; + var ws_rpc; + var db_api; + var network_api; + var history_api; + + function init() { + //console.log("[ApiInstances.js] ----- init ----->"); + ws_rpc = new WebSocketRpc("ws://localhost:8090"); + var init_promise = ws_rpc.login("", "").then(() => { + db_api = new GrapheneApi(ws_rpc, "database"); + network_api = new GrapheneApi(ws_rpc, "network"); + history_api = new GrapheneApi(ws_rpc, "history"); + return Promise.all([db_api.init(), network_api.init(), history_api.init()]); + }); + return { + init_promise: init_promise, + close: function () { + ws_rpc.close(); + apis_instance = null; + }, + db_api: function () { + return db_api; + }, + network_api: function () { + return network_api; + }, + history_api: function () { + return history_api; + } + }; + } + + return { + instance: function () { + if ( !apis_instance ) { + apis_instance = init(); + } + return apis_instance; + } + }; + +})(); + +module.exports = Apis; diff --git a/dl/src/rpc_api/ApplicationApi.js b/dl/src/rpc_api/ApplicationApi.js new file mode 100644 index 0000000000..9c97e8a243 --- /dev/null +++ b/dl/src/rpc_api/ApplicationApi.js @@ -0,0 +1,149 @@ + +var Aes = require('../ecc/aes') +var PrivateKey = require('../ecc/key_private') +var PublicKey = require('../ecc/key_public') +var ChainTypes = require('../chain/chain_types') +var chain_config = require('../chain/config') +var Long = require('../common/bytebuffer').Long + +var helper = require('../chain/transaction_helper'), + get_owner_private = helper.get_owner_private, + get_active_private = helper.get_active_private + +var tr_op = require('../chain/transaction_operations') + +var so_type = require('../chain/serializer_operation_types'), + signed_transaction_type = so_type.signed_transaction, + transfer_type = so_type.transfer + +var is_empty_user_input = require('../common/validation'). + is_empty_user_input + +var api = require('./ApiInstances').instance() + +class ApplicationApi { + + create_account_with_brain_key( + brain_key, + new_account_name, + registrar_id, + referrer_id = 0, + referrer_percent = 100, + expire_minutes = 10, + signer_private_key_id, + signer_private_key, + broadcast = false + ) { + var owner_privkey = get_owner_private(brain_key) + var active_privkey = get_active_private(owner_privkey) + + var owner_pubkey = owner_privkey.toPublicKey() + var active_pubkey = active_privkey.toPublicKey() + + var tr = new tr_op.signed_transaction() + tr.set_expire_minutes(expire_minutes) + { + var cop = new tr_op.account_create( + tr_op.key_create.fromPublicKey(owner_pubkey), + tr_op.key_create.fromPublicKey(active_pubkey) + ) + cop.name = new_account_name + cop.registrar = registrar_id + cop.referrer = referrer_id + cop.referrer_percent = referrer_percent + tr.add_operation(cop) + } + return tr.finalize( + signer_private_key_id, + signer_private_key, + broadcast + ) + } + + transfer( + from_account_id, + to_account_id, + amount, + asset_id, + memo_message, + expire_minutes, + signer_private_key_id, + signer_private_key, + broadcast = false + ) { + var memo = {} + if( ! is_empty_user_input(memo_message)) { + memo.from = from_account_id + memo.from_privkey = signer_private_key + memo.to = to_account_id + } + return this.transfer_extended( + from_account_id, + to_account_id, + amount, + asset_id, + memo_message, + memo.from, + memo.from_privkey, + memo.to, + expire_minutes, + signer_private_key_id, + signer_private_key, + broadcast + ) + } + + /** + Plain-text memo is used unless memo_from_privkey is provided. + */ + transfer_extended( + from_account_id, + to_account_id, + amount, + asset, + memo_message, + memo_from, + memo_from_privkey, + memo_to, + expire_minutes, + signer_private_key_id, + signer_private_key, + broadcast = false + ) { + var tr = new tr_op.signed_transaction() + tr.set_expire_minutes(expire_minutes) + { + var top = new tr_op.transfer( memo_from_privkey ) + top.from = from_account_id + top.to = to_account_id + top.amount.amount = amount + top.amount.asset_id = asset + top.memo.from = memo_from + top.memo.to = memo_to + top.memo.message = memo_message + tr.add_operation(top) + } + return tr.finalize( + signer_private_key_id, + signer_private_key, + broadcast + ) + } + + //account_name_for_id(account_ids) { + // if( ! Array.isArray(account_ids)) + // account_ids = [account_ids] + // + // promise = api.database_api().exec("get_accounts", [account_ids]).then(response => { + // console.log("----- get_accounts response ----->\n", response) + // return response + // }) + // + // return promise + //} + //return this.api.exec("lookup_account_names", [accounts]).then(response => { + // return response + // }) + +} +module.exports = ApplicationApi diff --git a/dl/src/rpc_api/DatabaseApi.js b/dl/src/rpc_api/DatabaseApi.js new file mode 100644 index 0000000000..a96b64d3d6 --- /dev/null +++ b/dl/src/rpc_api/DatabaseApi.js @@ -0,0 +1,54 @@ + +var AccountActions = require('../actions/AccountActions') + +class DatabaseApi { + + constructor(api) { + this.api = api; + this.accounts = { + add: AccountActions.addAccount, + get: function() { return AccountStore.getState().accounts; } + }; + } + + get_block(block_id) { + return this.api.exec("get_block", [block_id]).then(response => { + console.log("----- get_block response ----->\n", response); + }); + } + + get_accounts(accounts) { + if ( ! Array.isArray(accounts)) + accounts = [accounts]; + + var is_id = /^\d/.test(accounts[0]) + if(is_id) + return this.api.exec("get_accounts", [accounts]).then(response => { + console.log("----- get_accounts response ----->\n", response); + }); + else + return this.api.exec("lookup_account_names", [accounts]).then(response => { + console.log("----- lookup_account_names response ----->\n", response); + }); + } + + get_account_balances(account_id, assets) { + return this.api.exec("get_account_balances", [[account_id, assets]]).then(response => { + console.log("----- get_account_balances response ----->\n", response); + }); + } + + get_objects(ids) { + return this.api.exec("get_objects", [ids]).then(response => { + return response; + }); + } + + object(id) { + return this.get_objects([id]).then(response => { + return response && response.length > 0 ? response[0] : null; + }); + } + +} +module.exports = DatabaseApi diff --git a/dl/src/rpc_api/GrapheneApi.js b/dl/src/rpc_api/GrapheneApi.js new file mode 100644 index 0000000000..f275ce2344 --- /dev/null +++ b/dl/src/rpc_api/GrapheneApi.js @@ -0,0 +1,25 @@ +class GrapheneApi { + + constructor(ws_rpc, api_name) { + this.ws_rpc = ws_rpc; + this.api_name = api_name; + } + + init() { + var self = this + return this.ws_rpc.call([1, this.api_name, []]).then( response => { + //console.log("[GrapheneApi.js:11] ----- api_id ----->", response); + self.api_id = response; + return self; + }); + } + + exec(method, params) { + return this.ws_rpc.call([this.api_id, method, params]).catch(error => { + console.log("!!! GrapheneApi error: ", error); + throw error; + }); + } +} + +module.exports = GrapheneApi; diff --git a/dl/src/rpc_api/MarketApi.js b/dl/src/rpc_api/MarketApi.js new file mode 100644 index 0000000000..dff460580e --- /dev/null +++ b/dl/src/rpc_api/MarketApi.js @@ -0,0 +1,46 @@ + +var Long = require('../common/bytebuffer').Long + +var Lookup = require('../chain/lookup') +var helper = require('../chain/transaction_helper') +var tr_op = require('../chain/transaction_operations') +var mr_op = require('../chain/market_operations') + +class MarketApi { + + sell_asset( + seller_account, + amount_to_sell, + symbol_to_sell, + min_amount_to_receive, + symbol_to_receive, + timeout_sec = 0, + fill_or_kill = false, + transaction_expire_min = 10, + signer_private_key_id, + signer_private_key, + broadcast = false + ) { + var lookup = new Lookup(); + var tr = new tr_op.signed_transaction() + tr.set_expire_minutes(transaction_expire_min) + { + var op = new mr_op.limit_order_create() + op.seller = seller_account + op.amount_to_sell.amount = amount_to_sell + op.amount_to_sell.asset_id = symbol_to_sell + op.min_to_receive.amount = min_amount_to_receive + op.min_to_receive.asset_id = symbol_to_receive + op.expiration = timeout_sec ? helper.seconds_from_now(timeout_sec) : 0 + op.fill_or_kill = fill_or_kill + tr.add_operation(op) + } + return tr.finalize( + signer_private_key_id, + signer_private_key, + broadcast + ) + } +} + +module.exports = MarketApi diff --git a/dl/src/rpc_api/NetworkApi.js b/dl/src/rpc_api/NetworkApi.js new file mode 100644 index 0000000000..7059e7cefa --- /dev/null +++ b/dl/src/rpc_api/NetworkApi.js @@ -0,0 +1,25 @@ +class NetworkApi { + + constructor(api) { + this.api = api; + } + + broadcast_transaction(signed_transaction) { + return this.api.exec("broadcast_transaction", [signed_transaction]).then(response => { + return response; + }); + } + + add_node(ip_endpoint) { + return this.api.exec("add_node", [ip_endpoint]).then(response => { + return response; + }); + } + + get_connected_peers() { + return this.api.exec("get_connected_peers", []).then(response => { + return response; + }); + } +} +module.exports = NetworkApi \ No newline at end of file diff --git a/dl/src/rpc_api/WalletApi.js b/dl/src/rpc_api/WalletApi.js new file mode 100644 index 0000000000..8a6fd56078 --- /dev/null +++ b/dl/src/rpc_api/WalletApi.js @@ -0,0 +1,79 @@ +var tr_helper = require('../chain/transaction_helper'), + get_owner_private = tr_helper.get_owner_private, + get_active_private = tr_helper.get_active_private + +var tr_op = require('../chain/transaction_operations'), + signed_transaction = tr_op.signed_transaction, + key_create = tr_op.key_create, + account_create = tr_op.account_create + +var so_type = require('../chain/serializer_operation_types'), + signed_transaction_type = so_type.signed_transaction + +var vt = require('../chain/serializer_validation'), + get_protocol_instance = vt.get_protocol_instance + +var PrivateKey = require('../ecc/key_private') +var ApplicationApi = require('./ApplicationApi') + +class WalletApi { + + constructor() { + this.application_api = new ApplicationApi() + } + + /** Console print any transaction object with zero default values. */ + template(transaction_object_name) { + tr_helper.template(transaction_object_name) + } + + create_account_with_brain_key( + brain_key, + new_account_name, + registrar_id, + referrer_id = 0, + referrer_percent = 100, + broadcast = true + ) { + var expire_minutes = 10 + var signer_private_key_id = 1 + var signer_private_key = PrivateKey.fromSeed("nathan") + return this.application_api.create_account_with_brain_key( + brain_key, + new_account_name, + registrar_id, + referrer_id, + referrer_percent, + expire_minutes, + signer_private_key_id, + signer_private_key, + broadcast + ) + } + + transfer( + from_account_id, + to_account_id, + amount, + asset_id, + memo, + broadcast = true + ) { + var expire_minutes = 10 + var signer_private_key_id = 1 + var signer_private_key = PrivateKey.fromSeed("nathan") + return this.application_api.transfer( + from_account_id, + to_account_id, + amount, + asset_id, + memo, + expire_minutes, + signer_private_key_id, + signer_private_key, + broadcast + ) + } + +} +module.exports = WalletApi \ No newline at end of file diff --git a/dl/src/rpc_api/WebSocketRpc.js b/dl/src/rpc_api/WebSocketRpc.js new file mode 100644 index 0000000000..7c5f1e057a --- /dev/null +++ b/dl/src/rpc_api/WebSocketRpc.js @@ -0,0 +1,110 @@ +var Immutable = require("immutable"); + +class WebSocketRpc { + + constructor(ws_server) { + var WebSocketClient = typeof (WebSocket) !== "undefined" ? WebSocket : require("websocket").w3cwebsocket; + this.web_socket = new WebSocketClient(ws_server); + this.current_reject = null; + var self = this; + this.connect_promise = new Promise((resolve, reject) => { + //console.log("[WebSocketRpc.js:9] ----- connect_promise ----->", this); + self.current_reject = reject; + self.web_socket.onopen = () => resolve("con!"); + self.web_socket.onerror = (error) => { + console.log("!!! WebSocket Error ", ws_server); + if (self.current_reject) { + self.current_reject(error); + } + }; + self.web_socket.onmessage = (message) => self.listener(JSON.parse(message.data)); + }); + this.current_callback_id = 0; + this.callbacks = {}; + this.subscriptions = {}; + this.unsub = {}; + } + + call(params) { + // console.log("[websocketrpc] ----- call -----> id:",this.current_callback_id+1, params); + this.current_callback_id += 1; + var self = this; + + if (params[1] === "subscribe_to_objects" || params[1] === "subscribe_to_market") { + self.subscriptions[self.current_callback_id] = {callback: params[2][0], params: Immutable.fromJS(params[2][1])}; + params[2][0] = this.current_callback_id; + } + if (params[1] === "unsubscribe_from_objects" || params[1] === "unsubscribe_from_market") { + let unSubParams = Immutable.fromJS(params[2][0]); + for (let id in self.subscriptions) { + if (Immutable.is(self.subscriptions[id].params, unSubParams)) { + self.unsub[this.current_callback_id] = id; + break; + } + } + } + + var request = {method: "call", params: params}; + request.id = this.current_callback_id; + + + return new Promise((resolve, reject) => { + self.callbacks[self.current_callback_id] = {time: new Date(), resolve: resolve, reject: reject}; + self.web_socket.onerror = (error) => { + console.log("!!! WebSocket Error ", error); + reject(error); + }; + self.web_socket.send(JSON.stringify(request)); + }); + + } + + listener(response) { + //console.log("[websocketrpc] <--- reply ----", response); + let sub = false, callback = null; + + if (response.method === "notice") { + sub = true; + response.id = response.params[0]; + } + + if (!sub) { + callback = this.callbacks[response.id]; + } else { + callback = this.subscriptions[response.id].callback; + } + + if (callback && !sub) { + if (response.error) { + callback.reject(response.error); + } else { + callback.resolve(response.result); + } + delete this.callbacks[response.id]; + + if (this.unsub[response.id]) { + delete this.subscriptions[this.unsub[response.id]]; + delete this.unsub[response.id]; + } + + } else if(callback && sub) { + callback(response.params[1]); + } else { + console.log("Warning: unknown websocket response: ", response); + } + } + + login(user, password) { + var self = this; + return this.connect_promise.then( () => { + return self.call([1, "login", [user, password]]); + }); + } + + close() { + this.web_socket.close(); + } + +} + +module.exports = WebSocketRpc; diff --git a/dl/src/stores/AccountStore.js b/dl/src/stores/AccountStore.js new file mode 100644 index 0000000000..0ae28b4fcf --- /dev/null +++ b/dl/src/stores/AccountStore.js @@ -0,0 +1,126 @@ +var BaseStore = require("./BaseStore"); +var Immutable = require("immutable"); +var alt = require("../alt-instance"); +var AccountActions = require("../actions/AccountActions"); +import { + Account +} +from "./tcomb_structs"; + +//var Utils = require("../common/utils"); + +function json_to_account(json) { + return Account({ + id: json.id, + name: json.name, + balances: [] + }); +} + +class AccountStore extends BaseStore { + constructor() { + super(); + this.currentAccount = null; + this.browseAccounts = Immutable.Map(); + this.accounts = Immutable.Map(); + this.balances = Immutable.Map(); + this.accountHistories = Immutable.Map(); + this.account_name_to_id = {}; + this.account_id_to_name = {}; + this.bindListeners({ + onGetAllAccounts: AccountActions.getAllAccounts, + onGetAccount: AccountActions.getAccount, + onSetCurrentAccount: AccountActions.setCurrentAccount, + onTransfer: AccountActions.transfer, + onCreateAccount: AccountActions.createAccount + }); + + // this._export("getAccount", "getCurrent"); + } + + onGetAllAccounts(accounts) { + accounts.forEach((account, index) => { + this.account_id_to_name[account[1]] = account[0]; + this.account_name_to_id[account[0]] = account[1]; + if (index === 0) { + this.currentAccount = { + name: account[0], + id: account[1] + }; + } + }); + } + + onGetAccount(result) { + if (result.sub) { + this.accountHistories = this.accountHistories.set( + result.account, + result.history + ); + + this.balances = this.balances.set( + result.account, + result.balances + ); + + + } else { + let account = result[0][0]; + + if (account.id) { + let balances = result[1].length > 0 ? result[1] : [{ + amount: 0, + asset_id: "1.4.0" + }]; + + this.balances = this.balances.set( + account.id, + balances + ); + + this.account_name_to_id[account.name] = account.id; + + let newAccount = Account(account); + + this.browseAccounts = this.browseAccounts.set( + account.id, + newAccount + ); + + this.accounts = this.accounts.set( + account.id, + newAccount + ); + + this.accountHistories = this.accountHistories.set( + account.id, + result[2] + ); + } + } + } + + getCurrent() { + this.getAccount(this.currentAccount.name); + } + + onSetCurrentAccount(name) { + // let account_id = json.id; + // this.accounts = this.accounts.set(account_id, json_to_account(json)); + this.currentAccount = { + name: name, + id: this.account_name_to_id[name] + }; + } + + onTransfer(result) { + console.log("[AccountStore.js:90] ----- onTransfer ----->", result); + } + + onCreateAccount(name) { + console.log("[AccountStore.js:121] ----- onCreateAccount ----->", name); + } + +} + +module.exports = alt.createStore(AccountStore, "AccountStore"); diff --git a/dl/src/stores/AssetStore.js b/dl/src/stores/AssetStore.js new file mode 100644 index 0000000000..f412af9f1f --- /dev/null +++ b/dl/src/stores/AssetStore.js @@ -0,0 +1,32 @@ +var BaseStore = require("./BaseStore"); +var Immutable = require("immutable"); +var alt = require("../alt-instance"); +var AssetActions = require("../actions/AssetActions"); +import {Asset} from "./tcomb_structs"; + +class AssetStore extends BaseStore { + constructor() { + super(); + this.assets = Immutable.Map(); + this.asset_symbol_to_id = {}; + + this.bindListeners({ + onCreateAsset: AssetActions.createAsset, + onGetAsset: AssetActions.getAsset + }); + } + + onCreateAsset() { + // Handle asset creation + } + + onGetAsset(asset) { + this.assets = this.assets.set( + asset.id, + Asset(asset) + ); + this.asset_symbol_to_id[asset.symbol] = asset.id; + } +} + +module.exports = alt.createStore(AssetStore, "AssetStore"); diff --git a/dl/src/stores/BaseStore.js b/dl/src/stores/BaseStore.js new file mode 100644 index 0000000000..7b9e15671c --- /dev/null +++ b/dl/src/stores/BaseStore.js @@ -0,0 +1,13 @@ +class BaseStore { + + _export(...methods) { + let publicMethods = {}; + methods.forEach((method) => { + this[method] = this[method].bind(this); + publicMethods[method] = this[method]; + }); + this.exportPublicMethods(publicMethods); + } +} + +export default BaseStore; diff --git a/dl/src/stores/BlockchainStore.js b/dl/src/stores/BlockchainStore.js new file mode 100644 index 0000000000..64e63c1a58 --- /dev/null +++ b/dl/src/stores/BlockchainStore.js @@ -0,0 +1,60 @@ +var Immutable = require("immutable"); +var alt = require("../alt-instance"); +var BlockchainActions = require("../actions/BlockchainActions"); +import { + Block, GlobalObject, DynGlobalObject +} +from "./tcomb_structs"; + + +class BlockchainStore { + constructor() { + // This might not need to be an immutable map, a normal structure might suffice.. + this.blocks = Immutable.Map(); + this.latestBlocks = Immutable.List(); + this.dynGlobalObject = {}; + this.globalObject = {}; + + this.bindListeners({ + onGetBlock: BlockchainActions.getBlock, + onGetGlobals: BlockchainActions.subscribeGlobals, + onGetLatest: BlockchainActions.getLatest + }); + } + + onGetBlock(block) { + if (!this.blocks.get(block.id)) { + block.timestamp = new Date(block.timestamp); + this.blocks = this.blocks.set( + block.id, + Block(block) + ); + } + } + + onGetLatest(block) { + block.timestamp = new Date(block.timestamp); + if (block.id > this.dynGlobalObject.head_block_number - 10) { + this.latestBlocks = this.latestBlocks.unshift(Block(block)); + if (this.latestBlocks.size > 10) { + this.latestBlocks = this.latestBlocks.pop(); + } + } + + } + + onGetGlobals(objectArray) { + objectArray.forEach(object => { + if (object.id === "2.0.0") { + this.globalObject = GlobalObject(object); + } else if (object.id === "2.1.0") { + object.time = new Date(object.time); + object.next_maintenance_time = new Date(object.next_maintenance_time); + this.dynGlobalObject = DynGlobalObject(object); + } + }); + } + +} + +module.exports = alt.createStore(BlockchainStore, "BlockchainStore"); \ No newline at end of file diff --git a/dl/src/stores/IntlStore.js b/dl/src/stores/IntlStore.js new file mode 100644 index 0000000000..aef39d097d --- /dev/null +++ b/dl/src/stores/IntlStore.js @@ -0,0 +1,59 @@ +var alt = require("../alt-instance"); +var IntlActions = require("../actions/IntlActions"); +var BaseStore = require("./BaseStore"); +var counterpart = require("counterpart-instance"); +var locale_en = require("assets/locales/locale-en"); +counterpart.registerTranslations("en", locale_en); + +class IntlStore extends BaseStore { + constructor() { + super(); + this.currentLocale = "en"; + this.locales = ["en"]; + this.localesObject = {en: locale_en}; + + this.bindListeners({ + onSwitchLocale: IntlActions.switchLocale, + onGetLocale: IntlActions.getLocale + }); + + this._export("getCurrentLocale", "hasLocale"); + } + + hasLocale(locale) { + console.log("hasLocale:", this.locales.indexOf(locale)); + return this.locales.indexOf(locale) !== -1; + } + + getCurrentLocale() { + return this.currentLocale; + } + + onSwitchLocale(locale) { + switch (locale) { + case "en": + counterpart.registerTranslations("en", this.localesObject.en); + break; + + default: + let newLocale = this.localesObject[locale]; + if (!newLocale) { + newLocale = require("assets/locales/locale-" + locale); + this.localesObject[locale] = newLocale; + } + counterpart.registerTranslations(locale, newLocale); + break; + } + + counterpart.setLocale(locale); + this.currentLocale = locale; + } + + onGetLocale(locale) { + if (this.locales.indexOf(locale) === -1) { + this.locales.push(locale); + } + } +} + +module.exports = alt.createStore(IntlStore, "IntlStore"); diff --git a/dl/src/stores/KeyStore.js b/dl/src/stores/KeyStore.js new file mode 100644 index 0000000000..fa66f642aa --- /dev/null +++ b/dl/src/stores/KeyStore.js @@ -0,0 +1,21 @@ +var Immutable = require("immutable"); +var alt = require("../alt-instance"); +var KeyActions = require("../actions/KeyActions"); +var Utils = require("../common/utils"); +import {Key} from "./tcomb_structs"; + + +class KeyStore { + constructor() { + this.bindListeners({ + onAddKey: KeyActions.addKey + }); + } + + onAddKey(key) { + console.log("handling onAddKey:", key); + } + +} + +module.exports = alt.createStore(KeyStore, "KeyStore"); diff --git a/dl/src/stores/MarketsStore.js b/dl/src/stores/MarketsStore.js new file mode 100644 index 0000000000..1d1fd80f46 --- /dev/null +++ b/dl/src/stores/MarketsStore.js @@ -0,0 +1,56 @@ +var Immutable = require("immutable"); +var alt = require("../alt-instance"); +var MarketsActions = require("../actions/MarketsActions"); +import { + LimitOrder, + ShortOrder +} +from "./tcomb_structs"; + +class MarketsStore { + constructor() { + this.markets = Immutable.Map(); + this.asset_symbol_to_id = {}; + this.activeMarketShorts = Immutable.Map(); + this.activeMarketLimits = Immutable.Map(); + + this.bindListeners({ + onSubscribeMarket: MarketsActions.subscribeMarket, + onGetMarkets: MarketsActions.getMarkets + }); + } + + + onSubscribeMarket(result) { + console.log("onSubscribeMarket:", result); + if (result.limits) { + result.limits.forEach(order => { + order.expiration = new Date(order.expiration); + this.activeMarketLimits = this.activeMarketLimits.set( + order.id, + LimitOrder(order) + ); + }); + } + + if (result.shorts) { + result.shorts.forEach(short => { + short.expiration = new Date(short.expiration); + this.activeMarketLimits = this.activeMarketLimits.set( + short.id, + ShortOrder(short) + ); + }); + } + } + + onGetMarkets(markets) { + markets.forEach(market => { + this.markets = this.markets.set( + market.id, + market); + }); + } +} + +module.exports = alt.createStore(MarketsStore, "MarketsStore"); diff --git a/dl/src/stores/SessionStore.js b/dl/src/stores/SessionStore.js new file mode 100644 index 0000000000..6fae2ad477 --- /dev/null +++ b/dl/src/stores/SessionStore.js @@ -0,0 +1,52 @@ +var alt = require("../alt-instance"); +var SessionActions = require("../actions/SessionActions"); + +class SessionStore { + constructor() { + this.isUnlocked = true; + this.inProgress = false; + this.lockInProgress = false; + this.failed = false; + this.lockError = null; + + this.bindListeners({ + unlock: SessionActions.unlock, + lock: SessionActions.lock + }); + + this.exportPublicMethods({ + getUnlockState: this.getUnlockState + }); + } + + getUnlockState() { + return this.getState().isUnlocked; + } + + unlock(payload) { + this.inProgress = payload.inProgress; + + if (!this.inProgress) { + if (payload.unlocked) { + this.isUnlocked = payload.unlocked; + } + + if (payload.failed) { + this.failed = payload.failed; + } + } + } + + lock(payload) { + this.lockError = payload.lockError; + this.lockInProgress = payload.lockInProgress; + + if (!this.lockError && !this.lockInProgress) { + this.isUnlocked = payload.unlocked; + } else { + // TODO handle error if any + } + } +} + +module.exports = alt.createStore(SessionStore, "SessionStore"); diff --git a/dl/src/stores/WitnessStore.js b/dl/src/stores/WitnessStore.js new file mode 100644 index 0000000000..89c0f56acc --- /dev/null +++ b/dl/src/stores/WitnessStore.js @@ -0,0 +1,50 @@ +var Immutable = require("immutable"); +var alt = require("../alt-instance"); +var WitnessActions = require("../actions/WitnessActions"); +import { + Account, Witness +} +from "./tcomb_structs"; + +class WitnessStore { + + constructor() { + this.witnesses = Immutable.Map(); + this.witnessAccounts = Immutable.Map(); + this.witness_id_to_name = Immutable.Map(); + this.account_id_to_witness_id = {}; + + this.bindListeners({ + onGetWitnesses: WitnessActions.getWitnesses, + onGetWitnessAccounts: WitnessActions.getWitnessAccounts + }); + } + + onGetWitnesses(witnesses) { + witnesses.forEach(witness => { + this.account_id_to_witness_id[witness.witness_account] = witness.id; + this.witnesses = this.witnesses.set( + witness.id, + Witness(witness) + ); + }); + } + + onGetWitnessAccounts(accounts) { + accounts.forEach(account => { + this.witness_id_to_name = this.witness_id_to_name.set( + this.account_id_to_witness_id[account.id], + account.name + ); + + account.balances = []; + this.witnessAccounts = this.witnessAccounts.set( + account.id, + Account(account) + ); + }); + } + +} + +module.exports = alt.createStore(WitnessStore, "WitnessStore"); diff --git a/dl/src/stores/__tests__/account-store-test.js b/dl/src/stores/__tests__/account-store-test.js new file mode 100644 index 0000000000..80562df476 --- /dev/null +++ b/dl/src/stores/__tests__/account-store-test.js @@ -0,0 +1,8 @@ +//jest.dontMock('../sum'); +// +//describe('sum', function() { +// it('adds 1 + 2 to equal 3', function() { +// var sum = require('../sum'); +// expect(sum(1, 2)).toBe(3); +// }); +//}); diff --git a/dl/src/stores/tcomb_structs.js b/dl/src/stores/tcomb_structs.js new file mode 100644 index 0000000000..4e60ef7afc --- /dev/null +++ b/dl/src/stores/tcomb_structs.js @@ -0,0 +1,124 @@ +var t = require("tcomb"); + +let Account = t.struct({ + id: t.Str, + annotations: t.Arr, + registrar: t.Str, + referrer: t.Str, + referrer_percent: t.Num, + name: t.Str, + owner: t.Obj, + active: t.Obj, + memo_key: t.Str, + voting_account: t.Str, + num_witness: t.Num, + num_committee: t.Num, + votes: t.Arr, + statistics: t.Str, + whitelisting_accounts: t.Arr, + blacklisting_accounts: t.Arr +}, "Account"); + +let Asset = t.struct({ + annotations: t.Arr, + dynamic_asset_data_id: t.Str, + id: t.Str, + issuer: t.Str, + options: t.Obj, + precision: t.Num, + symbol: t.Str +}, "Asset"); + +let Block = t.struct({ + delegate_signature: t.Str, + id: t.Num, + next_secret_hash: t.Str, + previous: t.Str, + previous_secret: t.Str, + timestamp: t.Dat, + transactions: t.Arr, + witness: t.Str +}, "Block"); + +let Key = t.struct({ + id: t.Num +}, "Key"); + +let Witness = t.struct({ + id: t.Str, + witness_account: t.Str, + signing_key: t.Str, + next_secret: t.Str, + last_secret: t.Str, + accumulated_income: t.Num, + vote_id: t.Str +}, "Block"); + +let GlobalObject = t.struct({ + active_delegates: t.Arr, + active_witnesses: t.Arr, + chain_id: t.Str, + id: t.Str, + next_available_vote_id: t.Num, + parameters: t.Obj +}); + +let DynGlobalObject = t.struct({ + current_witness: t.Str, + head_block_id: t.Str, + head_block_number: t.Num, + id: t.Str, + next_maintenance_time: t.Dat, + random: t.Str, + time: t.Dat, + witness_budget: t.Num +}); + +let LimitOrder = t.struct({ + expiration: t.Dat, + for_sale: t.Num, + id: t.Str, + sell_price: t.Obj, + seller: t.Str +}); + +let ShortOrder = t.struct({ + expiration: t.Dat, + for_sale: t.Num, + id: t.Str, + sell_price: t.Obj, + seller: t.Str +}); + +let LimitTrx = t.struct({ + amount_to_sell: t.Obj, + expiration: t.Dat, + fee: t.Obj, + fill_or_kill: t.Bool, + min_to_receive: t.Obj, + seller: t.Str +}); + +let ShortTrx = t.struct({ + amount_to_sell: t.Obj, + collateral: t.Obj, + expiration: t.Dat, + fee: t.Obj, + initial_collateral_ratio: t.Num, + maintenance_collateral_ratio: t.Num, + seller: t.Str +}); + +module.exports = { + Account: Account, + Asset: Asset, + Block: Block, + Key: Key, + Witness: Witness, + GlobalObject: GlobalObject, + DynGlobalObject: DynGlobalObject, + LimitTrx: LimitTrx, + ShortTrx: ShortTrx, + LimitOrder: LimitOrder, + ShortOrder: ShortOrder +}; diff --git a/dl/test/README.txt b/dl/test/README.txt new file mode 100644 index 0000000000..5d8d2d1bce --- /dev/null +++ b/dl/test/README.txt @@ -0,0 +1,65 @@ + + +== Account 1.3.0 +The Genesis account and contains a very large testing +balance. The funds may be transferred using the genesis_private key. + +Genesis Private Key: 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 +Genesis Public Key: GPH6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV + += Lookup the account's public keys +Graphene > $g.db.api.exec("get_objects",[["1.3.0"]]) +var o=_[0] +o.active +{ weight_threshold: 1, auths: [ [ '1.2.0', 1 ] ] } +o.owner +{ weight_threshold: 1, auths: [ [ '1.2.0', 1 ] ] } + +$g.db.api.exec("get_objects",[["1.2.0"]]) +[ { id: '1.2.0', + key_data: [ 1, 'GPH7Pz9Xfpn9rBbWvvmPgcbESGDmRTfoEt6E6gkrspwk2YX49VCUJ' ] } ] + +== Account 1.3.11 + +Built-in testnet account, this has a large test balance. + +In the cli_wallet importing the genesis key will unlock the balance: +>>> import_key "1.3.11" "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" +>>> list_account_balances "1.3.11" +>>> transfer "1.3.11" "1.3.0" 1 "GPH" "memo" true + +In these unit tests the "import_key" step is not necessary because the +genesis key is saved in the source code. + +An inspection of account 1.3.11 gives usefull information: +Graphene > $g.db.api.exec("get_objects",[["1.3.11"]]) +var o=_[0] +o.active +{ weight_threshold: 1, auths: [ [ '1.2.1', 1 ] ] } +o.owner +{ weight_threshold: 1, auths: [ [ '1.2.1', 1 ] ] } + +The same key (1.2.1) is used for active and owner. This object is storing +the actual public key: + +$g.db.api.exec("get_objects",[["1.2.1"]]) +[ { id: '1.2.1', + key_data: [ 1, 'GPH6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV' ] } ] + +Create market asset: + +dbg_make_mia ACCOUNT ASSET +dbg_make_mia 1.3.11 USD + +Place a limit order: + +sell_asset ACCOUNT_ID AMOUNT ASSET FOR_AMOUNT FOR_ASSET EXPIRATION fill_or_kill broadcast +ex: sell_asset 1.3.11 1000 CORE 100 SHILL 100000 false true + +Place a short order: + +short_sell_asset ACCOUNT_ID SHORT_AMOUNT ASSET_NAME COLLATERAL_AMOUNT broadcast +ex: short_sell_asset 1.3.11 1000 SHILL 1000 true + + + diff --git a/dl/test/crypto.coffee b/dl/test/crypto.coffee new file mode 100644 index 0000000000..330570745a --- /dev/null +++ b/dl/test/crypto.coffee @@ -0,0 +1,49 @@ +assert = require 'assert' +Long = require('../src/common/bytebuffer').Long + +Aes = require '../src/ecc/aes' +PrivateKey = require '../src/ecc/key_private' +PublicKey = require '../src/ecc/key_public' +hash = require '../src/common/hash' + +describe "crypto", -> + + it "memo encryption", -> + sender = PrivateKey.fromSeed "1" + receiver = PrivateKey.fromSeed "2" + S = sender.get_shared_secret receiver.toPublicKey() + nonce = "289662526069530675" + + #console.log '... senderpriv',sender.toBuffer().toString 'hex' + #console.log '... receiverpub',receiver.toPublicKey().toBuffer().toString 'hex' + #console.log '... S ecies',S.toString 'hex' + + ciphertext = Aes.encrypt_with_checksum( + sender + receiver.toPublicKey() + nonce + "Hello, world!" + ) + #console.log '... ciphertext',ciphertext + plaintext = Aes.decrypt_with_checksum( + receiver + sender.toPublicKey() + nonce + ciphertext + ) + #console.log '... plaintext',plaintext.toString() + assert.equal "Hello, world!", plaintext.toString() + + it "decrypt a graphene memo", -> + sender = PrivateKey.fromSeed "1" + receiver = PrivateKey.fromSeed "2" + S = sender.get_shared_secret receiver.toPublicKey() + nonce = "2523449132308737096" + cipherhex = "62f00737f603b6d822a189187747184fc533ae356c20c87ae19e32f8c01cac19" + plaintext = Aes.decrypt_with_checksum( + receiver + sender.toPublicKey() + nonce + new Buffer cipherhex,'hex' + ) + assert.equal "Hello, world!", plaintext.toString() diff --git a/dl/test/market_tests.coffee b/dl/test/market_tests.coffee new file mode 100644 index 0000000000..2b63731f5a --- /dev/null +++ b/dl/test/market_tests.coffee @@ -0,0 +1,47 @@ +secureRandom = require 'secure-random' + +PrivateKey = require '../src/ecc/key_private' + +ApiInstances = require('../src/rpc_api/ApiInstances') +MarketApi = require '../src/rpc_api/MarketApi' +market = new MarketApi() + +th = require './test_helper' + +describe "market_tests", -> + + api = null + + before (done)-> + api = ApiInstances.instance() + api.init_promise.then ()-> + done() + .catch th.log_error + + after -> + api.close() + + it "market.sell_asset", (done)-> + NEW_ASSET = "A" + secureRandom.randomBuffer(2).toString('hex').toUpperCase() + console.log '... new asset',NEW_ASSET + tr = market.sell_asset( + seller=11 + sell_amt = 1 + sell_symbol = "CORE" + min_amt_receive = 1 + min_symbol_receive = "CORE" #NEW_ASSET + timeout_sec = 10 + fill_kill = no + transaction_expire_min = 10 + signer_private_key_id = 1 + signer_private_key = PrivateKey.fromSeed("nathan") + broadcast = no + ) + #console.log '... transaction_type.toObject(tr)', JSON.stringify transaction_type.toObject(tr),null,4 + tr.then (result)-> + th.print_result result + #th.print_hex "" + done() + .catch th.log_error + return + diff --git a/dl/test/test_helper.coffee b/dl/test/test_helper.coffee new file mode 100644 index 0000000000..1f7f47ebe4 --- /dev/null +++ b/dl/test/test_helper.coffee @@ -0,0 +1,26 @@ + + +module.exports = th = {} + +th.print_result=(tr_object)-> + console.log 'print_result', JSON.stringify tr_object + try + tr = signed_transaction_type.fromObject tr_object + tr_hex = signed_transaction_type.toHex(tr) + ByteBuffer.fromHex(tr_hex).printDebug() + catch e + if tr_object and tr_object["ref_block_num"] + console.log "print_result: unparsed or non-transactoin object",e,e.stack + +th.print_hex=(hex)-> + console.log 'print_hex' + ByteBuffer.fromHex(hex).printDebug() + try + tr = signed_transaction_type.fromHex hex + tr_object = signed_transaction_type.toObject(tr) + console.log JSON.stringify tr_object + catch e + console.log "print_hex: unparsed or non-transactoin object",e,e.stack + +th.log_error = (error)-> + console.log 'log_error',error,error.stack diff --git a/dl/test/tr_tests.coffee b/dl/test/tr_tests.coffee new file mode 100644 index 0000000000..c73db83d6c --- /dev/null +++ b/dl/test/tr_tests.coffee @@ -0,0 +1,179 @@ +PrivateKey = require '../src/ecc/key_private' +PublicKey = require '../src/ecc/key_public' +Signature = require '../src/ecc/signature' +WebSocketRpc = require '../src/rpc_api/WebSocketRpc' +GrapheneApi = require '../src/rpc_api/GrapheneApi' +NetworkApi = require '../src/rpc_api/NetworkApi' + +Promise = require '../src/common/Promise' +ByteBuffer = require '../src/common/bytebuffer' +secureRandom = require 'secure-random' + +tr_helper = require '../src/chain/transaction_helper' +th = require './test_helper' + +so_type = require '../src/chain/serializer_operation_types' +account_create_type = so_type.account_create +transaction_type = so_type.transaction +signed_transaction_type = so_type.signed_transaction + +tr_op = require '../src/chain/transaction_operations' +signed_transaction = tr_op.signed_transaction +key_create = tr_op.key_create +account_create = tr_op.account_create + +ApiInstances = require('../src/rpc_api/ApiInstances') +WalletApi = require '../src/rpc_api/WalletApi' +ApplicationApi = require '../src/rpc_api/ApplicationApi' +wallet = new WalletApi() +app = new ApplicationApi() + +### +import_key "1.3.11" "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" +create_account_with_brain_key "brainkey" "newaccountname" "1.3.11" "1.3.0" 0 true +### +describe "tr_tests", -> + + genesis_private = PrivateKey.fromWif "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" + #genesis_active_1_3_0_public = PublicKey.fromBtsPublic "GPH6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" + api = null + + before (done)-> + api = ApiInstances.instance() + api.init_promise.then ()-> + done() + .catch th.log_error + + after -> + api.close() + + it "wallet.account_create", (done)-> + suffix = secureRandom.randomBuffer(2).toString('hex').toLowerCase() + account_name = "account-z"+suffix + console.log '... account_name',account_name + tr = wallet.create_account_with_brain_key( + "brainkey" + account_name + registrar = 11 + referrer = 0 + referrer_percent = 0 + broadcast = yes + ).then (result)-> + th.print_result result + #th.print_hex "" + done() + .catch th.log_error + return + + #it "update account transaction", -> + it "wallet.transfer nomemo", (done)-> + wallet.transfer( + "1.3.11", "1.3.0", 1, "1.4.0", memo = null + broadcast = yes + ).then (result)-> + th.print_result result + #th.print_hex "" + done() + .catch th.log_error + return + + it "wallet.transfer encmemo", (done)-> + wallet.transfer( + "1.3.11", "1.3.0", 1, "1.4.0", memo = "memo" + broadcast = yes + ).then (result)-> + th.print_result result + #th.print_hex "" + done() + .catch th.log_error + return + + it "app.transfer_extended textmemo", (done)-> + tr = app.transfer_extended( + "1.3.11", "1.3.0", 1, "1.4.0", "memo" + "1.2.1", null #genesis_private + "1.2.0" + 10, 1, PrivateKey.fromSeed("nathan") + broadcast = yes + ).then (result)-> + th.print_result result + #th.print_hex "" + done() + .catch th.log_error + return + + it "app.transfer_extended encmemo", (done)-> + app.transfer_extended( + from = "1.3.11" + to = "1.3.0" + amount = 1 + asset = "1.4.0" + memo = "memo" + memo_from = "1.2.1" + memo_from_private = genesis_private + memo_to = "1.2.0" + expire = 10 + signer_private_id = 1 + signer_private_key = PrivateKey.fromSeed("nathan") + broadcast = yes + ).then (result)-> + th.print_result result + #th.print_hex "" + done() + , (e)-> th.log_error(e) + return + + ### + assertHexEqual=(x1, x2, msg)-> + return if x1 is x2 + console.log "ERROR: Unmatched binary\t", msg + console.log "Original Transaction" + ByteBuffer.fromHex(x1).printDebug() + console.log "New Transaction" + ByteBuffer.fromHex(x2).printDebug() + throw new Error "Unmatched Transaction" + + check_trx=(description, trx)-> + # Match Transaction fromHex -> toHex + transaction = transaction_so.fromHex trx.hex + #console.log JSON.stringify transaction_so.toObject(transaction),null,4 + + assertHexEqual trx.hex,transaction_so.toHex(transaction),"initial construction\t" + description + + # Match Transaction toObject -> fromObject then match to the original hex + trx_object = transaction_so.toObject transaction + #console.log '... trx_object',JSON.stringify trx_object,null,2 + transaction2 = transaction_so.fromObject trx_object + assertHexEqual trx.hex,transaction_so.toHex(transaction2),"re-construction\t" + description + + # readability only (technically redundant) + if trx.json + try + transaction3 = transaction_so.fromObject trx.json + assertHexEqual trx.hex,transaction_so.toHex(transaction3),"'json' object\t" + description + catch error + console.log 'WARNING',error,error.stack + + transaction + + check_object=(description, transaction_object)-> + transaction = transaction_so.fromObject transaction_object + #console.log JSON.stringify transaction_so.toObject(transaction),null,4 + + hex1 = transaction_so.toHex(transaction) + #console.log '... transaction_hex', hex1 + + transaction2 = transaction_so.fromHex(hex1) + + hex2 = transaction_so.toHex(transaction2) + assertHexEqual hex1, hex2, "hex\t" + description + + serilized_object = transaction_so.toObject(transaction) + #console.log '... transaction', JSON.stringify serilized_object,null,2 + + transaction3 = transaction_so.fromObject serilized_object + hex3 = transaction_so.toHex transaction3 + assertHexEqual hex1, hex3, "object\t" + description + + ### + diff --git a/dl/test/types_test.coffee b/dl/test/types_test.coffee new file mode 100644 index 0000000000..a470994a21 --- /dev/null +++ b/dl/test/types_test.coffee @@ -0,0 +1,48 @@ +assert = require 'assert' +ByteBuffer = require '../src/common/bytebuffer' +ChainTypes = require '../src/chain/chain_types' +Serializer = require '../src/chain/serializer' +Convert = require '../src/chain/serializer_convert' +op_type = require '../src/chain/serializer_types' +#uint8 = op_type.uint8 +#uint16 = op_type.uint16 +#uint32 = op_type.uint32 +#varint32 = op_type.varint32 +#int64 = op_type.int64 +#uint64 = op_type.uint64 +#string = op_type.string +#bytes = op_type.bytes +bool = op_type.bool +#array = op_type.array +#fixed_array = op_type.fixed_array +#id_type = op_type.id_type +#protocol_id_type = op_type.protocol_id_type +#object_id_type = op_type.object_id_type +vote_id = op_type.vote_id +#optional = op_type.optional +static_variant = op_type.static_variant +#map = op_type.map +set = op_type.set + +#so_type = require '../src/chain/serializer_operation_types' + +describe "types", -> + + it "vote_id",-> + toHex=(id)-> + vote = vote_id.fromObject id + Convert(vote_id).toHex vote + assert.equal "ff000000", toHex "255:0" + assert.equal "00ffffff", toHex "0:"+0xffffff + out_of_range=(id)-> + try + toHex id + assert false, 'should have been out of range' + catch e + assert e.message.indexOf('out of range') isnt -1 + out_of_range "0:"+(0xffffff+1) + out_of_range "256:0" + + it "set", -> + bool_set = set bool + assert.equal "03010001", Convert(bool_set).toHex [1,0,1] diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000000..4afcdd0f57 --- /dev/null +++ b/doc/README.md @@ -0,0 +1 @@ +TODO: use jekyll to generate static help/doc pages http://jekyllrb.com diff --git a/ios/.flowconfig b/ios/.flowconfig new file mode 100644 index 0000000000..88e6b03321 --- /dev/null +++ b/ios/.flowconfig @@ -0,0 +1,3 @@ +[ignore] + +[include] diff --git a/ios/Graphene.xcodeproj/project.pbxproj b/ios/Graphene.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..aca87925b8 --- /dev/null +++ b/ios/Graphene.xcodeproj/project.pbxproj @@ -0,0 +1,614 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00481BE61AC0C7FA00671115 /* libRCTWebSocketDebugger.a */; }; + 008F07F31AC5B25A0029DE68 /* main.jsbundle in Resources */ = {isa = PBXBuildFile; fileRef = 008F07F21AC5B25A0029DE68 /* main.jsbundle */; }; + 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; + 00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302B41ABCB8E700DB3ED1 /* libRCTAdSupport.a */; }; + 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; + 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; + 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; + 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; + 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00481BE51AC0C7FA00671115 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; + remoteInfo = RCTWebSocketDebugger; + }; + 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTActionSheet; + }; + 00C302B31ABCB8E700DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; + remoteInfo = RCTAdSupport; + }; + 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTGeolocation; + }; + 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5115D1A9E6B3D00147676; + remoteInfo = RCTImage; + }; + 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B511DB1A9E6C8500147676; + remoteInfo = RCTNetwork; + }; + 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; + remoteInfo = RCTVibration; + }; + 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; + remoteInfo = React; + }; + 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTLinking; + }; + 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5119B1A9E6C1200147676; + remoteInfo = RCTText; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocketDebugger.xcodeproj; path = node_modules/react-native/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj; sourceTree = ""; }; + 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = main.jsbundle; path = iOS/main.jsbundle; sourceTree = ""; }; + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; + 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = node_modules/react-native/Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = ""; }; + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = ""; }; + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = node_modules/react-native/Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* Graphene.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Graphene.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = iOS/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = iOS/AppDelegate.m; sourceTree = ""; }; + 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = iOS/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = iOS/main.m; sourceTree = ""; }; + 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = node_modules/react-native/React/React.xcodeproj; sourceTree = ""; }; + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = node_modules/react-native/Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 146834051AC3E58100842450 /* libReact.a in Frameworks */, + 00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */, + 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, + 00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */, + 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, + 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, + 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, + 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, + 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, + 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00481BDC1AC0C7FA00671115 /* Products */ = { + isa = PBXGroup; + children = ( + 00481BE61AC0C7FA00671115 /* libRCTWebSocketDebugger.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302A81ABCB8CE00DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302B01ABCB8E700DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302B41ABCB8E700DB3ED1 /* libRCTAdSupport.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302B61ABCB90400DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302BC1ABCB91800DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302D41ABCB9D200DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302E01ABCB9EE00DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, + ); + name = Products; + sourceTree = ""; + }; + 13B07FAE1A68108700A75B9A /* Graphene */ = { + isa = PBXGroup; + children = ( + 008F07F21AC5B25A0029DE68 /* main.jsbundle */, + 13B07FAF1A68108700A75B9A /* AppDelegate.h */, + 13B07FB01A68108700A75B9A /* AppDelegate.m */, + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, + 13B07FB71A68108700A75B9A /* main.m */, + ); + name = Graphene; + sourceTree = ""; + }; + 146834001AC3E56700842450 /* Products */ = { + isa = PBXGroup; + children = ( + 146834041AC3E56700842450 /* libReact.a */, + ); + name = Products; + sourceTree = ""; + }; + 78C398B11ACF4ADC00677621 /* Products */ = { + isa = PBXGroup; + children = ( + 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, + ); + name = Products; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, + 146833FF1AC3E56700842450 /* React.xcodeproj */, + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, + 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */, + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, + 00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */, + ); + name = Libraries; + sourceTree = ""; + }; + 832341B11AAA6A8300B99B32 /* Products */ = { + isa = PBXGroup; + children = ( + 832341B51AAA6A8300B99B32 /* libRCTText.a */, + ); + name = Products; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* Graphene */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 83CBBA001A601CBA00E9B192 /* Products */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* Graphene.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 13B07F861A680F5B00A75B9A /* Graphene */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Graphene" */; + buildPhases = ( + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Graphene; + productName = "Hello World"; + productReference = 13B07F961A680F5B00A75B9A /* Graphene.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = Facebook; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Graphene" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; + ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; + }, + { + ProductGroup = 00C302B01ABCB8E700DB3ED1 /* Products */; + ProjectRef = 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */; + }, + { + ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; + ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; + }, + { + ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; + ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + }, + { + ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; + ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + }, + { + ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; + ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + }, + { + ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; + ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + }, + { + ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; + ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; + }, + { + ProductGroup = 00481BDC1AC0C7FA00671115 /* Products */; + ProjectRef = 00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */; + }, + { + ProductGroup = 146834001AC3E56700842450 /* Products */; + ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* Graphene */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 00481BE61AC0C7FA00671115 /* libRCTWebSocketDebugger.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTWebSocketDebugger.a; + remoteRef = 00481BE51AC0C7FA00671115 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTActionSheet.a; + remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302B41ABCB8E700DB3ED1 /* libRCTAdSupport.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAdSupport.a; + remoteRef = 00C302B31ABCB8E700DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTGeolocation.a; + remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTImage.a; + remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTNetwork.a; + remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTVibration.a; + remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 146834041AC3E56700842450 /* libReact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libReact.a; + remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTLinking.a; + remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTText.a; + remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 008F07F31AC5B25A0029DE68 /* main.jsbundle in Resources */, + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { + isa = PBXVariantGroup; + children = ( + 13B07FB21A68108700A75B9A /* Base */, + ); + name = LaunchScreen.xib; + path = iOS; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/node_modules/react-native/React/**", + ); + INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = Graphene; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/node_modules/react-native/React/**", + ); + INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = Graphene; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/node_modules/react-native/React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/node_modules/react-native/React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Graphene" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Graphene" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/ios/Graphene.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Graphene.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..919434a625 --- /dev/null +++ b/ios/Graphene.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Graphene.xcodeproj/project.xcworkspace/xcshareddata/Graphene.xccheckout b/ios/Graphene.xcodeproj/project.xcworkspace/xcshareddata/Graphene.xccheckout new file mode 100644 index 0000000000..c69be45412 --- /dev/null +++ b/ios/Graphene.xcodeproj/project.xcworkspace/xcshareddata/Graphene.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + 696786E9-8D8E-4DA4-97B2-D3C4EDB6C2FB + IDESourceControlProjectName + Graphene + IDESourceControlProjectOriginsDictionary + + E83F97895B41B6E3F817C65802AA2CC613D4437B + gitlab.bitshares.org:valzav/gui.git + + IDESourceControlProjectPath + ios/Graphene.xcodeproj + IDESourceControlProjectRelativeInstallPathDictionary + + E83F97895B41B6E3F817C65802AA2CC613D4437B + ../../.. + + IDESourceControlProjectURL + gitlab.bitshares.org:valzav/gui.git + IDESourceControlProjectVersion + 111 + IDESourceControlProjectWCCIdentifier + E83F97895B41B6E3F817C65802AA2CC613D4437B + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + E83F97895B41B6E3F817C65802AA2CC613D4437B + IDESourceControlWCCName + gui + + + + diff --git a/ios/Graphene.xcodeproj/project.xcworkspace/xcuserdata/vz.xcuserdatad/UserInterfaceState.xcuserstate b/ios/Graphene.xcodeproj/project.xcworkspace/xcuserdata/vz.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..7115e47aebc68197d9874e392b943d41ab0aea8b GIT binary patch literal 20894 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 literal 0 HcmV?d00001 diff --git a/ios/Graphene.xcodeproj/xcshareddata/xcschemes/Graphene.xcscheme b/ios/Graphene.xcodeproj/xcshareddata/xcschemes/Graphene.xcscheme new file mode 100644 index 0000000000..798baa4e0d --- /dev/null +++ b/ios/Graphene.xcodeproj/xcshareddata/xcschemes/Graphene.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Graphene.xcodeproj/xcuserdata/vz.xcuserdatad/xcschemes/xcschememanagement.plist b/ios/Graphene.xcodeproj/xcuserdata/vz.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000000..b1e32989a1 --- /dev/null +++ b/ios/Graphene.xcodeproj/xcuserdata/vz.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + Graphene.xcscheme_^#shared#^_ + + orderHint + 0 + + + SuppressBuildableAutocreation + + 13B07F861A680F5B00A75B9A + + primary + + + + + diff --git a/ios/iOS/AppDelegate.h b/ios/iOS/AppDelegate.h new file mode 100644 index 0000000000..a9654d5e01 --- /dev/null +++ b/ios/iOS/AppDelegate.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface AppDelegate : UIResponder + +@property (nonatomic, strong) UIWindow *window; + +@end diff --git a/ios/iOS/AppDelegate.m b/ios/iOS/AppDelegate.m new file mode 100644 index 0000000000..e6277fd372 --- /dev/null +++ b/ios/iOS/AppDelegate.m @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "AppDelegate.h" + +#import "RCTRootView.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + NSURL *jsCodeLocation; + + // Loading JavaScript code - uncomment the one you want. + + // OPTION 1 + // Load from development server. Start the server from the repository root: + // + // $ npm start + // + // To run on device, change `localhost` to the IP address of your computer, and make sure your computer and + // iOS device are on the same Wi-Fi network. + jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"]; + + // OPTION 2 + // Load from pre-bundled file on disk. To re-generate the static bundle, run + // + // $ curl 'http://localhost:8081/index.ios.bundle?dev=false&minify=true' -o iOS/main.jsbundle + // + // and uncomment the next following line + // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation + moduleName:@"Graphene" + launchOptions:launchOptions]; + + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + UIViewController *rootViewController = [[UIViewController alloc] init]; + rootViewController.view = rootView; + self.window.rootViewController = rootViewController; + [self.window makeKeyAndVisible]; + return YES; +} + +@end diff --git a/ios/iOS/Base.lproj/LaunchScreen.xib b/ios/iOS/Base.lproj/LaunchScreen.xib new file mode 100644 index 0000000000..c9ec5a5ecc --- /dev/null +++ b/ios/iOS/Base.lproj/LaunchScreen.xib @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/iOS/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/iOS/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..118c98f746 --- /dev/null +++ b/ios/iOS/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/iOS/Info.plist b/ios/iOS/Info.plist new file mode 100644 index 0000000000..0a56a6ea80 --- /dev/null +++ b/ios/iOS/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + NSLocationWhenInUseUsageDescription + + + diff --git a/ios/iOS/main.jsbundle b/ios/iOS/main.jsbundle new file mode 100644 index 0000000000..4f50ea3379 --- /dev/null +++ b/ios/iOS/main.jsbundle @@ -0,0 +1,5 @@ +// Offline JS +// To re-generate the offline bundle, run this from root of your project +// $ curl 'http://localhost:8081/index.ios.bundle?dev=false&minify=true' -o iOS/main.jsbundle + +throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions'); diff --git a/ios/iOS/main.m b/ios/iOS/main.m new file mode 100644 index 0000000000..3d767fcbb9 --- /dev/null +++ b/ios/iOS/main.m @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/ios/index.ios.js b/ios/index.ios.js new file mode 100644 index 0000000000..ab2a3e991d --- /dev/null +++ b/ios/index.ios.js @@ -0,0 +1,171 @@ +var React = require('react-native'); +var { + AppRegistry, + StyleSheet, + Text, + TextInput, + View, + ScrollView, + ListView, + TouchableHighlight, + NavigatorIOS } = React; + +var AccountStore = require("stores/AccountStore.js"); +var AccountActions = require("stores/AccountActions.js"); + + +var Graphene = React.createClass({ + render: function () { + return ( + + ); + } +}); + +var AccountsList = React.createClass({ + getInitialState: function () { + this.store = AccountStore; + var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); + var accounts = AccountStore.getState().accounts; + // .map below didn't work for me - returning initial object + //var accounts_array = accounts.map(a => { return a.get("name")}); + var accounts_array = []; + accounts.forEach(a => accounts_array.push(a.get("name"))); + return { + dataSource: ds.cloneWithRows(accounts_array), + new_account_name_text: "" + }; + }, + + componentWillMount: function () { + if (this.store) this.store.listen(this.onChange); + }, + + componentWillUnmount: function () { + if (this.store) this.store.unlisten(this.onChange); + }, + + shouldComponentUpdate: function (nextProps, nextState) { + return this.props !== nextProps || this.state !== nextState; + }, + + onChange: function () { + if (this.store) this.setState(this.getInitialState()); + }, + + addAccount: function (account_name) { + if (account_name == "") return; + AccountActions.addAccount(account_name); + }, + + selectAccount: function (account_name) { + this.props.navigator.push({ + component: AccountScreen, + passProps: {account_name} + }); + }, + + render: function () { + return ( + + + + this.addAccount(event.nativeEvent.text) } + style={styles.text_input}/> + + + ); + }, + + _renderRow: function (account_name) { + return ( + this.selectAccount(account_name)} + account_name={account_name}/> + ); + } + +}); + +var AccountCell = React.createClass({ + render: function () { + return ( + + + + + + {this.props.account_name} + + + + + + + ); + } +}); + +var AccountScreen = React.createClass({ + render: function () { + return ( + + + {this.props.account_name} + + + ); + } +}); + +var styles = StyleSheet.create({ + nav_container: { + flex: 1, + }, + container: { + flex: 1, + }, + list_view: { + flex: 1, + }, + row: { + alignItems: 'center', + backgroundColor: 'white', + flexDirection: 'row', + padding: 5, + }, + separator: { + height: 1, + backgroundColor: '#CCCCCC', + }, + text: { + flex: 1, + }, + welcome: { + flex: 1, + color: "#ff0000", + fontSize: 20, + textAlign: 'center', + margin: 10, + }, + text_input_container: {}, + text_input: { + height: 26, + borderWidth: 0.5, + borderColor: '#0f0f0f', + padding: 4, + flex: 1, + fontSize: 13 + } +}); + +AppRegistry.registerComponent('Graphene', () => Graphene); + +module.exports = Graphene; diff --git a/ios/package.json b/ios/package.json new file mode 100644 index 0000000000..e3d1f8b9c5 --- /dev/null +++ b/ios/package.json @@ -0,0 +1,17 @@ +{ + "name": "iOS-gui", + "version": "0.0.1", + "description": "iOS gui powered by react and graphene", + "homepage": "https://github.com/cryptonomex/graphene_ui", + "author": "Cryptonomex, Inc.", + "license" : "LicenseRef-LICENSE", + "private": true, + "scripts": { + "start": "node_modules/react-native/packager/packager.sh" + }, + "dependencies": { + "react-native": "~0.3.11", + "immutable": "~3.7.2", + "alt": "~0.15.4" + } +} diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000000..cbe55262cb --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,4 @@ +.sass-cache +node_modules +bower_components +bundle diff --git a/web/__tests__/components/Account/Identicon-test.jsx b/web/__tests__/components/Account/Identicon-test.jsx new file mode 100644 index 0000000000..05b2c1e6a0 --- /dev/null +++ b/web/__tests__/components/Account/Identicon-test.jsx @@ -0,0 +1,41 @@ +import React from "react/addons"; +var TestUtils = React.addons.TestUtils; +jest.dontMock("../../../app/components/Account/Identicon.jsx"); + +describe("", function() { + var Identicon = require("../../../app/components/Account/Identicon.jsx"); + var identicon, result; + var size = {height: 100, width: 100}; + + beforeEach(function() { + identicon = TestUtils.renderIntoDocument( + + ); + result = TestUtils.findRenderedDOMComponentWithTag( + identicon, "canvas"); + }); + + it("renders a canvas when given an account name", function() { + expect(typeof result).toBe("object"); + }); + + it("only renders divs when given undefined account name", function() { + var divicon = TestUtils.renderIntoDocument( + + ); + var divs = TestUtils.scryRenderedDOMComponentsWithTag( + divicon, "div"); + var canvas = TestUtils.scryRenderedDOMComponentsWithTag( + divicon, "canvas"); + expect(divs.length === 2 && canvas.length === 0).toBeTruthy(); + }); + + it("has props style of width/height equal to size", function() { + expect(result.props.style.width === size.width && result.props.style.height === size.height).toBeTruthy(); + }); + + it("has props of width/height equal to 2 * size", function() { + expect(result.props.width === size.width * 2 && result.props.height === size.height * 2).toBeTruthy(); + }); +}); + diff --git a/web/__tests__/utils/stub_router_context.js b/web/__tests__/utils/stub_router_context.js new file mode 100644 index 0000000000..5ae1f6d523 --- /dev/null +++ b/web/__tests__/utils/stub_router_context.js @@ -0,0 +1,41 @@ +import React from "react"; + +let stubRouterContext = (Component, props, stubs) => { + function RouterStub() { } + + Object.assign(RouterStub, { + makePath () {}, + makeHref () {}, + transitionTo () {}, + replaceWith () {}, + goBack () {}, + getCurrentPath () {}, + getCurrentRoutes () {}, + getCurrentPathname () {}, + getCurrentParams () {}, + getCurrentQuery () {}, + isActive () {}, + getRouteAtDepth() {}, + setRouteComponentAtDepth() {} + }, stubs); + + return React.createClass({ + childContextTypes: { + router: React.PropTypes.func, + routeDepth: React.PropTypes.number + }, + + getChildContext () { + return { + router: RouterStub, + routeDepth: 0 + }; + }, + + render () { + return ; + } + }); +}; + +export default stubRouterContext; diff --git a/web/app/App.jsx b/web/app/App.jsx new file mode 100644 index 0000000000..72029a2777 --- /dev/null +++ b/web/app/App.jsx @@ -0,0 +1,127 @@ +import React from "react"; +import Router from "react-router"; + +const { Route, RouteHandler, DefaultRoute } = Router; + +import IntlStore from "stores/IntlStore"; +import Apis from "rpc_api/ApiInstances"; +import DashboardContainer from "./components/Dashboard/DashboardContainer"; +import Discover from "./components/Discover/DiscoverContainer"; +import Header from "./components/Header/Header"; +import Footer from "./components/Footer/Footer"; +import AccountContainer from "./components/Account/AccountContainer"; +import Wallet from "./components/Wallet/Wallet"; +import Accounts from "./components/Wallet/Accounts"; +import Receive from "./components/Wallet/Receive"; +import Assets from "./components/Wallet/Assets"; +import History from "./components/Wallet/History"; +import Exchange from "components/Exchange/ExchangeContainer"; +import Markets from "components/Exchange/MarketsContainer"; +import TransferPage from "./components/Transfer/TransferPage"; +import Settings from "./components/Settings/Settings"; +import Logout from "./components/Logout"; +import Login from "./components/Login"; +import BlockContainer from "./components/Blockchain/BlockContainer"; +import Asset from "./components/Blockchain/AssetContainer"; +import Transaction from "./components/Blockchain/Transaction"; +import CreateAccount from "./components/CreateAccount"; +import BaseComponent from "./components/BaseComponent"; +import SessionStore from "stores/SessionStore"; +import AccountActions from "actions/AccountActions"; +import AssetActions from "actions/AssetActions"; +import BlockchainActions from "actions/BlockchainActions"; +import IntlActions from "actions/IntlActions"; +import MobileMenu from "./components/Header/MobileMenu"; +import LoadingIndicator from "./components/LoadingIndicator/LoadingIndicator"; +import Notifier from "./components/Notifier/NotifierContainer"; +import cookies from "cookies-js"; + +require("./assets/loader"); + +class App extends BaseComponent { + constructor(props) { + super(props, SessionStore); + this.state.loading = true; + } + + componentDidMount() { + let locale; + if (cookies) { + locale = cookies.get("graphene_locale"); + console.log("cookie locale:", locale); + } + + Apis.instance().init_promise.then(() => { + AccountActions.getAllAccounts().then(current_account_id => { + return current_account_id; + }).then(current_account_id => { + let localePromise = (locale) ? IntlActions.switchLocale(locale) : null; + return Promise.all([ + AccountActions.getAccount(current_account_id), + AssetActions.getAsset("1.4.0"), + BlockchainActions.subscribeGlobals(), + localePromise + ]); + }).then(() => { + this.setState({loading: false}); + }); + }).catch(error => { + console.log("[App.jsx] ----- ERROR ----->", error, error.stack); + this.setState({loading: false}); + }); + } + + componentWillUpdate(nextProps, nextState) { + // Fire any actions that are needed on a successful unlock here + //if (nextState.isUnlocked === true && this.state.isUnlocked === false) { + // console.log("just unlocked, firing actions"); + //} + } + + render() { + if (this.state.loading) { + return ; + } else { + return ( +
+
+ + + +
+
+ ); + } + } +} + +let routes = ( + + + + + + + + + + + + + + + + + + + + + + + +); + + +Router.run(routes, function (Handler) { + React.render(, document.getElementById("content")); +}); diff --git a/web/app/assets/fonts/Roboto-Bold.eot b/web/app/assets/fonts/Roboto-Bold.eot new file mode 100644 index 0000000000000000000000000000000000000000..e58dbfab3402f5c3fc102e64df229dda647128d1 GIT binary patch literal 34266 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/app/assets/fonts/Roboto-Bold.ttf b/web/app/assets/fonts/Roboto-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7f9f9edf5f72b82c31b8e722facb0d412a056345 GIT binary patch literal 82584 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 IBm5`-A0R2y-2eap literal 0 HcmV?d00001 diff --git a/web/app/assets/fonts/Roboto-Bold.woff b/web/app/assets/fonts/Roboto-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..ddc8e3208195df919ccfbdb3752e43f5c0671226 GIT binary patch literal 40816 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/app/assets/fonts/Roboto-Light.ttf b/web/app/assets/fonts/Roboto-Light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..af39cdaf06dd36c075dc232bb0b930f32b0629eb GIT binary patch literal 84588 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 CM4AHt literal 0 HcmV?d00001 diff --git a/web/app/assets/fonts/Roboto-Light.woff b/web/app/assets/fonts/Roboto-Light.woff new file mode 100644 index 0000000000000000000000000000000000000000..a4666fef85ef0479fabe69803b2463f3416d9134 GIT binary patch literal 40892 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 OmF>A6m0fW_6Rp5$!4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/app/assets/fonts/Roboto-Regular.ttf b/web/app/assets/fonts/Roboto-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d716911ea0635234285955a842c29b1259bcdedd GIT binary patch literal 83600 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