├── .DS_Store ├── .ebextensions ├── 01_add_cors.config.yaml └── nodecommand.config ├── .gitignore ├── README.md ├── libs ├── .DS_Store ├── channel.js ├── iota.flash.js │ ├── .DS_Store │ ├── .eslintrc │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── examples │ │ ├── flash.js │ │ └── functions.js │ ├── gulpfile.js │ ├── lib │ │ ├── constants.js │ │ ├── flash.js │ │ ├── helpers.js │ │ ├── multisig.js │ │ └── transfer.js │ ├── package.json │ └── test │ │ ├── mocha.opts │ │ └── transfer │ │ └── transfer.getDiff.js ├── serve.js └── storage.js ├── package.json └── static └── index.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Evanfeenstra/fognet-server/1d28348495e8f63400666b21988313daaca3d3df/.DS_Store -------------------------------------------------------------------------------- /.ebextensions/01_add_cors.config.yaml: -------------------------------------------------------------------------------- 1 | container_commands: 2 | 01_fix_static_cors: 3 | command: "/tmp/fix_static_cors.sh" 4 | files: 5 | "/tmp/fix_static_cors.sh": 6 | mode: "000755" 7 | owner: root 8 | group: root 9 | content: | 10 | #!/bin/bash 11 | pushd $(/opt/elasticbeanstalk/bin/get-config container -k config_staging_dir) 12 | echo "Adding CORS Config" 13 | PROXY_CONF="#etc#nginx#conf.d#00_elastic_beanstalk_proxy.conf" 14 | grep static_cors.config $PROXY_CONF || sed -i '/location \/static {/a \ \ \ \ include /etc/nginx/conf.d/static_cors.config;' $PROXY_CONF 15 | "/etc/nginx/conf.d/static_cors.config": 16 | mode: "000644" 17 | owner: root 18 | group: root 19 | content: | 20 | # 21 | # Wide-open CORS config for nginx 22 | # 23 | if ($request_method = 'OPTIONS') { 24 | add_header 'Access-Control-Allow-Origin' '*'; 25 | # 26 | # Om nom nom cookies 27 | # 28 | add_header 'Access-Control-Allow-Credentials' 'true'; 29 | add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 30 | # 31 | # Custom headers and headers various browsers *should* be OK with but aren't 32 | # 33 | add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 34 | # 35 | # Tell client that this pre-flight info is valid for 20 days 36 | # 37 | add_header 'Access-Control-Max-Age' 1728000; 38 | add_header 'Content-Type' 'text/plain charset=UTF-8'; 39 | add_header 'Content-Length' 0; 40 | return 204; 41 | } 42 | if ($request_method = 'POST') { 43 | add_header 'Access-Control-Allow-Origin' '*'; 44 | add_header 'Access-Control-Allow-Credentials' 'true'; 45 | add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 46 | add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 47 | } 48 | if ($request_method = 'GET') { 49 | add_header 'Access-Control-Allow-Origin' '*'; 50 | add_header 'Access-Control-Allow-Credentials' 'true'; 51 | add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 52 | add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 53 | } -------------------------------------------------------------------------------- /.ebextensions/nodecommand.config: -------------------------------------------------------------------------------- 1 | option_settings: 2 | aws:elasticbeanstalk:container:nodejs: 3 | NodeCommand: "npm start" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | 30 | # Elastic Beanstalk Files 31 | .elasticbeanstalk/* 32 | !.elasticbeanstalk/*.cfg.yml 33 | !.elasticbeanstalk/*.global.yml 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FogNet server 2 | 3 | The backend for a decentralized internet built with bluetooth and IOTA 4 | 5 | ## What's inside 6 | 7 | - [**Express**](https://expressjs.com/) 8 | - [**Redis**](https://redis.io/) 9 | - [**IOTA Flash**](https://github.com/iotaledger/iota.flash.js) 10 | 11 | ## How to use 12 | 13 | In your command line tool 14 | 15 | 1. Clone this repo: 16 | 17 | ``` 18 | git clone https://github.com/l3wi/satoshiSever.git 19 | ``` 20 | 21 | 1. Install and run 22 | 23 | ``` 24 | yarn 25 | yarn start 26 | ``` 27 | 28 | A dev server will be served at http://localhost:8081 29 | 30 | ## -------------------------------------------------------------------------------- /libs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Evanfeenstra/fognet-server/1d28348495e8f63400666b21988313daaca3d3df/libs/.DS_Store -------------------------------------------------------------------------------- /libs/channel.js: -------------------------------------------------------------------------------- 1 | const add = require("iota.crypto.js").add 2 | const converter = require("iota.crypto.js").converter 3 | const storage = require("./storage") 4 | const multisig = require("./iota.flash.js").multisig 5 | const transfer = require("./iota.flash.js").transfer 6 | 7 | function getSubseed(seed, callback) { 8 | storage.incr("index", (err, index) => { 9 | const subseedTrits = subseed(converter.trits(seed), index) 10 | //console.log(subseedTrits); 11 | callback(err, err ? null : converter.trytes(subseedTrits)) 12 | }) 13 | } 14 | 15 | function subseed(seed, index) { 16 | var indexTrits = converter.fromValue( index ); 17 | return add( seed.slice( ), indexTrits ); 18 | } 19 | 20 | function getDigest(seed, index, security) { 21 | return multisig.getDigest(seed, index, security) 22 | } 23 | 24 | function getNewDigest(id, callback) { 25 | const state = storage.get("channel_" + id, (err, channel) => { 26 | if (err) { 27 | callback(err) 28 | } else { 29 | callback( 30 | null, 31 | multisig.getDigest( 32 | channel.seed, 33 | channel.flash.index, 34 | channel.flash.security 35 | ) 36 | ) 37 | } 38 | }) 39 | } 40 | 41 | function getAddress(digests) { 42 | return multisig.composeAddress(digests) 43 | } 44 | 45 | function processTransfer(id, bundles, callback) { 46 | storage.get("channel_" + id, (err, channel) => { 47 | if (err) { 48 | callback(err) 49 | return 50 | } 51 | try { 52 | const flashState = channel.flash 53 | const signatures = transfer.sign( 54 | flashState.root, 55 | channel.seed, 56 | bundles 57 | ) 58 | 59 | let signedBundles = transfer.appliedSignatures(bundles, signatures) 60 | 61 | transfer.applyTransfers( 62 | flashState.root, 63 | flashState.deposit, 64 | flashState.outputs, 65 | flashState.remainderAddress, 66 | flashState.transfers, 67 | signedBundles 68 | ) 69 | storage.set("channel_" + id, channel, (err, res) => { 70 | if (err) { 71 | callback(err) 72 | } else { 73 | callback(null, signedBundles) 74 | } 75 | }) 76 | // }) 77 | } catch (err) { 78 | console.log(err) 79 | callback(null, false) 80 | } 81 | }) 82 | } 83 | 84 | function validateOutputs(addresses, callback) { 85 | // TODO: replace with storage 86 | const whitelist = [ 87 | "TRPSU9DSNROHLCPIXBXGDXPOLKPUOYZZBZJCEILRJNSIFZASLPKHCIDIDBRCJHASMENZMTICJMBZRANKM", 88 | "AGGXW9LBTUORZLBTIQCPUOCCHIJJE9EFXYOHIIJMXRALPUWWJGRGTTAUCJJXVMYETNQVTTYDDEVCJRPZT" 89 | ] 90 | // callback(null, !!addresses.filter((a) => whitelist.indexOf(a) !== -1).length); 91 | callback(null, true) 92 | } 93 | 94 | module.exports = { 95 | getSubseed: getSubseed, 96 | getDigest: getDigest, 97 | getNewDigest: getNewDigest, 98 | getAddress: getAddress, 99 | processTransfer: processTransfer 100 | } 101 | -------------------------------------------------------------------------------- /libs/iota.flash.js/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Evanfeenstra/fognet-server/1d28348495e8f63400666b21988313daaca3d3df/libs/iota.flash.js/.DS_Store -------------------------------------------------------------------------------- /libs/iota.flash.js/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | }, 5 | "parserOptions": { 6 | "ecmaVersion": 6 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /libs/iota.flash.js/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Ignore dist builds 15 | dist 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | -------------------------------------------------------------------------------- /libs/iota.flash.js/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 IOTA AS, IOTA Foundation & Developers (https://iotatoken.com) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /libs/iota.flash.js/README.md: -------------------------------------------------------------------------------- 1 | # Flash Channels Javascript Library 2 | 3 | It should be noted that this library as it stands right now is an **early beta release**. As such, there might be some unexpected results. Please join the community (see links below) and post issues, to ensure that the developers of the library can improve it. 4 | 5 | > **Join the Discussion** 6 | 7 | > If you want to get involved in the community, need help with getting setup, have any issues related with the library or just want to discuss Blockchain, Distributed Ledgers and IoT with other people, feel free to join our Slack. [Slack](http://slack.iota.org/) You can also ask questions on our dedicated forum at: [IOTA Forum](https://forum.iota.org/). 8 | 9 | ## Installation 10 | 11 | ### Node.js 12 | 13 | ``` 14 | npm install iotaledger/iota.flash.js 15 | ``` 16 | 17 | ## Concepts 18 | 19 | Flash Channels use a binary tree topology reduce the number of transactions to be attached. Usually each transfer would have to be attached to the tangle, Flash enables realtime streaming of tokens off-tangle. 20 | 21 | #### Tree topology 22 | 23 | ![Binary Tree](https://cdn-images-1.medium.com/max/1600/1*KIEDG01OP4EGKHiKuKW-dA.png) 24 | 25 | A binary tree approach takes advantage of the fact that a signature can be used up-to 2 times while remaining reasonably secure. This lets us build a tree, where the terminating nodes (Leaves) are the individual transactions that will occur in the Flash channel. The path from the Leaf the Root acts to transfer the **total** value of the Root down to the current Leaf. 26 | 27 | Given we are constructing a tree, we must determine the number of transactions in the channel when opening it. If we want a **15** transaction channel, we must have a tree with the depth of 5 (`Math.ceil(log2(15)) + 1 `). 28 | 29 | As this uses log2, the larger the number of channel transactions the proportionally lower depth we need: ie. **1000** transactions require a depth of **10**, where a **2000** transaction channel require a depth of **11**. 30 | 31 | #### **Address Reuse** 32 | 33 | A central tenant of IOTA's winternitz one time signature is that the address is not reused. In the case of Flash Channels, addresses can be reused **one** time. This degrades the security however given the `realtime` nature of the channel there is a reasonable expectation that the channel will have progressed before an attack could take place. Please see a quick calculation relating to security [here](https://public.tangle.works/winternitz.pdf). 34 | 35 | A common practice would be to chain Flash Channels in order to keep the 'channel' open. This is feesible 😉 given the lack of cost and the time required to open channels. 36 | 37 | #### Transferring Data 38 | 39 | In Flash Channels there are two main type of data that is transferred. This is a proposed bundles and signatures related to that bundle. This the **ONLY** that should be transferred during the channel's operation. This allows a user to maintain their own Flash object (outlined below) and only mutates the state via the Flash library, preserving the integrity of the channel from their perspective. 40 | 41 | #### **Integrity Checking** 42 | 43 | Each proposed transfer replicates and then advances the whole state of the channel. It is imperative that each participant in the channel does a **diff **to check that the proposed transfer is not malicious and only contains the correct changes. This functionality is offered in the `getDiff()` function within the library. 44 | 45 | > *Example*: A malicious user could craft a transfer that carries the correct values but change the remainder address, thus allowing them to take a large portion of the funds early in the channel's life. 46 | 47 | #### **Multiparty channels** 48 | 49 | Flash, at its core, is a set of rules governing how parties interact with the Multi-signature features of IOTA. Given this there is no limit to the number of users in a Flash Channel. This means you can have a **consensus** channel (all users agree) or a **M of N** channel (some users agree). 50 | 51 | - Consensus would be good for logistically complex interactions with a large number of interrelated parties. 52 | - M of N would work well for interactions where an arbiter could be present to help resolve disputes in the channel. 53 | 54 | #### Staking the Channel 55 | 56 | When users enter the channel they deposit funds into the newly generated deposit address (root of the binary tree). The amount they deposit is the amount of tokens they are able to spend in the channel. This amount also relates to trust as the amount they have deposit into this address is now under the control of all the parties that have signed the address. 57 | 58 | It is common practice to deposit equal amounts into a channel **([100,100])** as both users now have an **equal** amount of tokens at stake. Therefore any misbehaviour or disagreements would result in both users being in an equally bad position. This would be useful for transacting with unknown users or channels with bidirectional payments. 59 | 60 | For situations with reputation involved, a more one-sided channel can be created **([100,0])**. This means that there is little incentive for the user with zero stake in the channel to be honest apart for a hit to their reputation. This is useful for one directional user **(100)** to machine **(0)** services where a business would otherwise have to hold large amounts of collateral in each of the Flash Channels open. ie EV Charging stations, an instant payment broker, or a streaming service. 61 | 62 | ## API 63 | 64 | ### Flash Object 65 | 66 | ------ 67 | 68 | For the Flash Channel to operate, a state object should be constructed to manage it. Below describes the Flash object that is recommended for use. 69 | 70 | Each user has a copy of this object, however they should **never** transfer the object to each other. Each time a transfer occurs in the channel this object is updated by the `applyTransfers` function which updates all the required values to the latest channel state. 71 | 72 | **Example Flash Object** 73 | 74 | ```javascript 75 | { 76 | signersCount: 2, // Number of signers in a channel 77 | balance: 2000, // total channel balance 78 | deposits: [1000,1000], // individual user deposits 79 | settlementAddresses: ['ADKHAKXIW..', 'MAOODHQNA...'] // user's output addresses 80 | depositAddress: 'AJDGAJDJS...', // Address at index 1 w/ checksum 81 | remainderAddress: {...}, // Index 0 of the multisig addresses generated 82 | root: {...}, // Index 1+ of the multisig addresses generated 83 | outputs: {...}, 84 | transfers: [...] // History of transfers within the channel 85 | } 86 | ``` 87 | 88 | 1. **signersCount**: `Int` Number of people partaking in the channel 89 | 2. **balance**: `Int` Index of the private key. 90 | 3. **deposits**: `Array` An array of the deposits of each channel user. 91 | 4. **settlementAddresses**: `Array` Array of user's settlement addresses for use when closing the channel. 92 | 5. **depositAddress**: `String` Tryte encoded address string. This is the address at index `[1]` of the channel addresses 93 | 6. **remainderAddress**: `Object` Multisig object at address 94 | 7. **root**: `Object` Multisig object at index `[1]` 95 | 8. **outputs**: `Object` Channel's outputs history is appended to this object 96 | 9. **transfers**: `Array` Channel's transfer history is appended to this array. 97 | 98 | ### Multisig 99 | 100 | ------ 101 | 102 | #### `getDigest()` 103 | 104 | Generates the digest value of a key. 105 | 106 | **Input** 107 | 108 | ```javascript 109 | multisig.getDigest(seed, index, security) 110 | ``` 111 | 112 | 1. **seed**: `String` Tryte encoded seed 113 | 2. **index**: 'Int' Index of the private key. 114 | 3. **security**: `Int` Security level to be used for the private key 115 | 116 | **Returns** 117 | 118 | 1. `String` - digest represented in trytes. 119 | 120 | ------ 121 | 122 | #### `composeAddress()` 123 | 124 | Wraps the compose address related functions of `iota.lib.js`. Returns a 81-tryte address from an Array of digests. 125 | 126 | *Note: The order of the digests in this function **must** be noted, as this order is required when signing the bundles.* 127 | 128 | **Input** 129 | 130 | ```javascript 131 | multisig.composeAddress([...digests]) 132 | ``` 133 | 134 | 1. **digests**: `Array` Array of digests represented in trytes 135 | 136 | **Returns** 137 | 138 | 1. `String` - 81-tryte multisig address 139 | 140 | ------ 141 | 142 | #### `updateLeafToRoot` 143 | 144 | Taking the `root` of the tree, this function walks from the Leaf up to the root and finds the first node from the Leaf which is able to be used to sign a new transaction from. If an address can't be reused it increments a counter which is used to generate new addresses from. 145 | 146 | **Input** 147 | 148 | ```javascript 149 | multisig.updateLeafToRoot(root) 150 | ``` 151 | 152 | 1. **root**: `Object` Representation of the current state of the Flash tree 153 | 154 | **Returns** 155 | 156 | 1. `Object` - An object containing the modified `multisigs` object to be used in the transactions and the number of new addresses `generate` required to complete the transaction. 157 | 158 | ### Transfer 159 | 160 | ------ 161 | 162 | #### `prepare()` 163 | 164 | This function checks for sufficient funds and return a transfer array that will correctly transfer the right amount of IOTA, in relation to channel stake, into the users settlement address . 165 | 166 | **Input** 167 | 168 | ```javascript 169 | transfer.prepare( 170 | flash.settlementAddresses, 171 | flash.deposit, 172 | index, 173 | transfers 174 | ) 175 | ``` 176 | 177 | 1. **settlementAddresses**: `array` the settlement addresses for each user 178 | 2. **deposit**: `array` the amount each user can still spend 179 | 3. **index**: `int` the index of the user used as an input 180 | 4. **transfers**: `array` the `{value, address}` destination of the output bundle (excluding remainder) 181 | 182 | **Return** 183 | 184 | 1. `Array` - Transfer object 185 | 186 | ------ 187 | 188 | #### `compose()` 189 | 190 | This method takes the `transfers` object from the `transfer.prepare` function and takes the flash object then constructs the required bundle for the next state in the channel. 191 | 192 | **Input** 193 | 194 | ```javascript 195 | transfer.compose( 196 | flash.balance, 197 | flash.deposit, 198 | flash.outputs, 199 | multisig, 200 | flash.remainderAddress, 201 | flash.transfers, 202 | newTansfers, 203 | close 204 | ) 205 | ``` 206 | 207 | 1. **balance**: `Int` The total amount of iotas in the channel 208 | 2. **deposit**: `Array` The amount of iotas still available to each user to spend from 209 | 3. **outputs**: `String` The accrued outputs through the channel 210 | 4. **multisig**: `Object` history the leaf bundles 211 | 5. **remainderAddress**: `String` The remainder address of the Flash channel 212 | 6. **history**:`Array` Transfer history of the channel 213 | 7. **newTransfers**:`Array` transfers the array of outputs for the transfer 214 | 8. **close**:`Boolean` whether to use the minimum tree or not 215 | 216 | **Return** 217 | 218 | `Array` Array of constructed bundle representing the latest state of the Flash channel. These bundles do not have signatures. 219 | 220 | ------ 221 | 222 | #### `sign()` 223 | 224 | This takes the constructed bundles from `compose` and then generates an `array` of ordered signatures to be applied to the bundle. 225 | 226 | **Input** 227 | 228 | ```javascript 229 | transfer.sign( 230 | multisig, 231 | seed, 232 | bundles 233 | ) 234 | ``` 235 | 236 | 1. **multisig**: `Object` history the leaf bundles 237 | 2. **seed**: `String` Tryte encoded seed 238 | 3. **bundles**: `Array` Array of bundles that require signatures. 239 | 240 | **Return** 241 | 242 | 1. `Array` - An ordered array of signatures to be applied to the transfer bundle array. 243 | 244 | ------ 245 | 246 | #### `appliedSignatures()` 247 | 248 | This takes the bundles that have been generated by `compose` and the signatures of **one** user and then applies them to the bundle. Use this function to apply all the signatures to the bundle. 249 | 250 | The signatures **must** be applied in the order the address was generated. Otherwise the signatures will be invalid. 251 | 252 | *Note: You use the output of this function to apply the next set of signatures.* 253 | 254 | **Input** 255 | 256 | ```javascript 257 | transfer.appliedSignatures( 258 | bundles, 259 | signatures 260 | ) 261 | ``` 262 | 263 | 1. **bundles**: `Array` Array of bundles that require signatures. 264 | 2. **signatures**: `Array` Ordered set of **one** users signatures to be applied to the proposed bundle. 265 | 266 | **Return** 267 | 268 | 1. `Array` - An ordered array of bundles that have had the user's signatures applied. 269 | 270 | ------ 271 | 272 | #### `getDiff()` 273 | 274 | This takes the channel history and latest bundles and runs checks to see where the changes are in the transfer. 275 | 276 | *Note: this is already called in the `applyTransfers` function* 277 | 278 | **Input** 279 | 280 | ```javascript 281 | transfer.getDiff( 282 | root, 283 | remainder, 284 | history, 285 | bundles 286 | ) 287 | ``` 288 | 289 | 1. **root**: `Array` multisig object starting at the root of the tree 290 | 2. **remainder**: `Object` multisig object of the remainder address 291 | 3. **history**:`Array` Transfer history of the channel 292 | 4. **bundles**: `Array` Array of bundles for the proposed transfer 293 | 294 | **Return** 295 | 296 | 1. `Array` - An array of diffs per address 297 | 298 | ------ 299 | 300 | #### `applyTransfers()` 301 | 302 | **Input** 303 | 304 | ```javascript 305 | transfer.applyTransfers( 306 | flash.root, 307 | flash.deposit, 308 | flash.outputs, 309 | flash.remainderAddress, 310 | flash.transfers, 311 | signedBundles 312 | ) 313 | ``` 314 | 315 | 1. **root**: `Object` Representation of the current state of the Flash tree 316 | 2. **deposit**: `Array` The amount of iotas still available to each user to spend from 317 | 3. **outputs**: `String` The accrued outputs through the channel 318 | 4. **remainderAddress**: `String` The remainder address of the Flash channel 319 | 5. **history**:`Array` Transfer history of the channel 320 | 6. **signedBundles**:`Array` Signed bundle 321 | 322 | **Return** 323 | 324 | This function mutates the flash objects that are passed to it. If there is an error in applying the transfers ie. Signatures aren't valid, the function will throw an error. 325 | 326 | ------ 327 | 328 | #### `close()` 329 | 330 | Used in place of the `prepare` function when closing the channel. This is used to generate the closing transfers to each settlement address. It does this by correctly dividing the remaining channel balance amongst the channel's users. 331 | 332 | **Input** 333 | 334 | ```javascript 335 | transfer.close( 336 | flash.settlementAddresses, 337 | flash.deposit 338 | ) 339 | ``` 340 | 341 | 1. **settlementAddresses**: `array` the settlement addresses for each user 342 | 2. **deposit**: `array` the amount each user can still spend 343 | 344 | **Return** 345 | 346 | 1. `Array` - Closing transfer object -------------------------------------------------------------------------------- /libs/iota.flash.js/examples/flash.js: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////// 2 | ////////////// FLASH EXAMPLE ///////////////// 3 | //////////////////////////////////////////////// 4 | // This example sets up a channel ////// 5 | // then makes a transfer from USER ONE ////// 6 | // to USER TWO. ////// 7 | //////////////////////////////////////////////// 8 | 9 | const IOTACrypto = require("iota.crypto.js") 10 | const transfer = require("../lib/transfer") 11 | const multisig = require("../lib/multisig") 12 | const Helpers = require("./functions") 13 | 14 | const oneSeed = 15 | "USERONEUSERONEUSERONEUSERONEUSERONEUSERONEUSERONEUSERONEUSERONEUSERONEUSERONEUSER" 16 | const oneSettlement = 17 | "USERONE9ADDRESS9USERONE9ADDRESS9USERONE9ADDRESS9USERONE9ADDRESS9USERONE9ADDRESS9U" 18 | const twoSeed = 19 | "USERTWOUSERTWOUSERTWOUSERTWOUSERTWOUSERTWOUSERTWOUSERTWOUSERTWOUSERTWOUSERTWOUSER" 20 | const twoSettlement = 21 | "USERTWO9ADDRESS9USERTWO9ADDRESS9USERTWO9ADDRESS9USERTWO9ADDRESS9USERTWO9ADDRESS9U" 22 | 23 | ////////////////////////////////// 24 | // INITIAL CHANNEL CONDITIONS 25 | 26 | // Security level 27 | const SECURITY = 2 28 | // Number of parties taking signing part in the channel 29 | const SIGNERS_COUNT = 2 30 | // Flash tree depth 31 | const TREE_DEPTH = 4 32 | // Total channel Balance 33 | const CHANNEL_BALANCE = 2000 34 | // Users deposits 35 | const DEPOSITS = [1000, 1000] 36 | 37 | ////////////////////////////////// 38 | // INITAL FLASH OBJECTS 39 | 40 | // USER ONE - Initial Flash Object 41 | var oneFlash = { 42 | userIndex: 0, 43 | userSeed: oneSeed, 44 | index: 0, 45 | security: SECURITY, 46 | depth: TREE_DEPTH, 47 | bundles: [], 48 | partialDigests: [], 49 | flash: { 50 | signersCount: SIGNERS_COUNT, 51 | balance: CHANNEL_BALANCE, 52 | deposit: DEPOSITS.slice(), // Clone correctly 53 | outputs: {}, 54 | transfers: [] 55 | } 56 | } 57 | 58 | ////////////////////////////////// 59 | // USER TWO - Initial Flash Object 60 | var twoFlash = { 61 | userIndex: 1, 62 | userSeed: twoSeed, 63 | index: 0, 64 | security: SECURITY, 65 | depth: TREE_DEPTH, 66 | bundles: [], 67 | partialDigests: [], 68 | flash: { 69 | signersCount: SIGNERS_COUNT, 70 | balance: CHANNEL_BALANCE, 71 | deposit: DEPOSITS.slice(), // Clone correctly 72 | outputs: {}, 73 | transfers: [] 74 | } 75 | } 76 | console.log("Flash objects created!") 77 | 78 | ////////////////////////////// 79 | ////// SETUP CHANNEL ////// 80 | 81 | ////////////////////////////// 82 | // GENERATE DIGESTS 83 | 84 | // USER ONE 85 | // Create digests for the start of the channel 86 | for (let i = 0; i < TREE_DEPTH + 1; i++) { 87 | // Create new digest 88 | const digest = multisig.getDigest( 89 | oneFlash.userSeed, 90 | oneFlash.index, 91 | oneFlash.security 92 | ) 93 | // Increment key index 94 | oneFlash.index++ 95 | oneFlash.partialDigests.push(digest) 96 | } 97 | 98 | // USER TWO 99 | // Create digests for the start of the channel 100 | for (let i = 0; i < TREE_DEPTH + 1; i++) { 101 | // Create new digest 102 | const digest = multisig.getDigest( 103 | twoFlash.userSeed, 104 | twoFlash.index, 105 | twoFlash.security 106 | ) 107 | // Increment key index 108 | twoFlash.index++ 109 | twoFlash.partialDigests.push(digest) 110 | } 111 | console.log("Inital digests generated!") 112 | 113 | ////////////////////////////////// 114 | // INITAL MULTISIG 115 | 116 | // Make an array of digests 117 | let allDigests = [] 118 | allDigests[oneFlash.userIndex] = oneFlash.partialDigests 119 | allDigests[twoFlash.userIndex] = twoFlash.partialDigests 120 | 121 | // Generate the first addresses 122 | let oneMultisigs = oneFlash.partialDigests.map((digest, index) => { 123 | // Create address 124 | let addy = multisig.composeAddress( 125 | allDigests.map(userDigests => userDigests[index]) 126 | ) 127 | // Add key index in 128 | addy.index = digest.index 129 | // Add the signing index to the object IMPORTANT 130 | addy.signingIndex = oneFlash.userIndex * digest.security 131 | // Get the sum of all digest security to get address security sum 132 | addy.securitySum = allDigests 133 | .map(userDigests => userDigests[index]) 134 | .reduce((acc, v) => acc + v.security, 0) 135 | // Add Security 136 | addy.security = digest.security 137 | return addy 138 | }) 139 | 140 | let twoMultisigs = twoFlash.partialDigests.map((digest, index) => { 141 | // Create address 142 | let addy = multisig.composeAddress( 143 | allDigests.map(userDigests => userDigests[index]) 144 | ) 145 | // Add key index in 146 | addy.index = digest.index 147 | // Add the signing index to the object IMPORTANT 148 | addy.signingIndex = twoFlash.userIndex * digest.security 149 | // Get the sum of all digest security to get address security sum 150 | addy.securitySum = allDigests 151 | .map(userDigests => userDigests[index]) 152 | .reduce((acc, v) => acc + v.security, 0) 153 | // Add Security 154 | addy.security = digest.security 155 | return addy 156 | }) 157 | 158 | console.log("Multisigs generated!") 159 | 160 | ////////////////////////////////// 161 | // CONSUME & ORGANISE ADDRESSES FOR USE 162 | 163 | // Set remainder address (Same on both users) 164 | oneFlash.flash.remainderAddress = oneMultisigs.shift() 165 | twoFlash.flash.remainderAddress = twoMultisigs.shift() 166 | 167 | // Nest trees 168 | for (let i = 1; i < oneMultisigs.length; i++) { 169 | oneMultisigs[i - 1].children.push(oneMultisigs[i]) 170 | } 171 | for (let i = 1; i < twoMultisigs.length; i++) { 172 | twoMultisigs[i - 1].children.push(twoMultisigs[i]) 173 | } 174 | 175 | // Set deposit address (Same on both users) 176 | // NOTE: Checksum added so users can consume manually 177 | // let depositAddress = iota.utils.addChecksum(multisigs[0].address) 178 | // oneFlash.flash.depositAddress = depositAddress 179 | // twoFlash.flash.depositAddress = depositAddress 180 | 181 | // Set Flash root 182 | oneFlash.flash.root = oneMultisigs.shift() 183 | twoFlash.flash.root = twoMultisigs.shift() 184 | 185 | // Set settlement addresses (Usually sent over when the digests are.) 186 | let settlementAddresses = [oneSettlement, twoSettlement] 187 | oneFlash.flash.settlementAddresses = settlementAddresses 188 | twoFlash.flash.settlementAddresses = settlementAddresses 189 | 190 | // Set digest/key index 191 | oneFlash.index = oneFlash.partialDigests.length 192 | twoFlash.index = twoFlash.partialDigests.length 193 | 194 | console.log("Channel Setup!") 195 | console.log( 196 | "Transactable tokens: ", 197 | oneFlash.flash.deposit.reduce((acc, v) => acc + v) 198 | ) 199 | 200 | ////////////////////////////// 201 | ////// TRANSACTING ////// 202 | 203 | ////////////////////////////// 204 | // COMPOSE TX from USER ONE 205 | 206 | console.log("Creating Transaction") 207 | console.log("Sending 200 tokens to ", twoSettlement) 208 | 209 | // Create transfer array pointing to USER TWO 210 | let transfers = [ 211 | { 212 | value: 200, 213 | address: twoSettlement 214 | } 215 | ] 216 | 217 | // Create TX 218 | var bundles = Helpers.createTransaction(oneFlash, transfers, false) 219 | 220 | ///////////////////////////////// 221 | /// SIGN BUNDLES 222 | 223 | // Get signatures for the bundles 224 | let oneSignatures = Helpers.signTransaction(oneFlash, bundles) 225 | 226 | // Generate USER TWO'S Singatures 227 | let twoSignatures = Helpers.signTransaction(twoFlash, bundles) 228 | 229 | // Sign bundle with your USER ONE'S signatures 230 | let signedBundles = transfer.appliedSignatures(bundles, oneSignatures) 231 | 232 | // ADD USER TWOS'S signatures to the partially signed bundles 233 | signedBundles = transfer.appliedSignatures(signedBundles, twoSignatures) 234 | 235 | ///////////////////////////////// 236 | /// APPLY SIGNED BUNDLES 237 | 238 | // Apply transfers to User ONE 239 | oneFlash = Helpers.applyTransfers(oneFlash, signedBundles) 240 | // Save latest channel bundles 241 | oneFlash.bundles = signedBundles 242 | 243 | // Apply transfers to User TWO 244 | twoFlash = Helpers.applyTransfers(twoFlash, signedBundles) 245 | // Save latest channel bundles 246 | twoFlash.bundles = signedBundles 247 | 248 | console.log("Transaction Applied!") 249 | console.log( 250 | "Transactable tokens: ", 251 | oneFlash.flash.deposit.reduce((acc, v) => acc + v) 252 | ) 253 | 254 | // TO DO: ADD 2 MORE TXs to demo branching 255 | 256 | ////////////////////////////// 257 | // CLOSE Channel 258 | 259 | // Supplying the CORRECT varibles to create a closing bundle 260 | bundles = Helpers.createTransaction( 261 | oneFlash, 262 | oneFlash.flash.settlementAddresses, 263 | true 264 | ) 265 | 266 | ///////////////////////////////// 267 | /// SIGN BUNDLES 268 | 269 | // Get signatures for the bundles 270 | oneSignatures = Helpers.signTransaction(oneFlash, bundles) 271 | 272 | // Generate USER TWO'S Singatures 273 | twoSignatures = Helpers.signTransaction(twoFlash, bundles) 274 | 275 | // Sign bundle with your USER ONE'S signatures 276 | signedBundles = transfer.appliedSignatures(bundles, oneSignatures) 277 | 278 | // ADD USER TWOS'S signatures to the partially signed bundles 279 | signedBundles = transfer.appliedSignatures(signedBundles, twoSignatures) 280 | 281 | ///////////////////////////////// 282 | /// APPLY SIGNED BUNDLES 283 | 284 | // Apply transfers to User ONE 285 | oneFlash = Helpers.applyTransfers(oneFlash, signedBundles) 286 | // Save latest channel bundles 287 | oneFlash.bundles = signedBundles 288 | 289 | // Apply transfers to User TWO 290 | twoFlash = Helpers.applyTransfers(twoFlash, signedBundles) 291 | // Save latest channel bundles 292 | twoFlash.bundles = signedBundles 293 | 294 | console.log("Channel Closed") 295 | console.log("Final Bundle to be attached: ") 296 | console.log(signedBundles[0]) 297 | -------------------------------------------------------------------------------- /libs/iota.flash.js/examples/functions.js: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////// 2 | ////////////// FLASH EXAMPLE ///////////////// 3 | //////////////////////////////////////////////// 4 | // This provides access to basic functions ///// 5 | // required for the channels use. ///// 6 | //////////////////////////////////////////////// 7 | 8 | const IOTACrypto = require("iota.crypto.js") 9 | const transfer = require("../lib/transfer") 10 | const multisig = require("../lib/multisig") 11 | 12 | const createTransaction = (user, actions, close) => { 13 | ////////////////////////////// 14 | /// Check for a Branch 15 | // From the LEAF recurse up the tree to the ROOT 16 | // and find how many new addresses need to be 17 | // generated if any. 18 | let toUse = multisig.updateLeafToRoot(user.flash.root) 19 | if (toUse.generate != 0) { 20 | // Tell the server to generate new addresses, attach to the multisig you give 21 | // await Channel.getNewBranch(toUse.multisig, toUse.generate) 22 | } 23 | ///////////////////////////////// 24 | /// CONSTRUCT BUNDLES 25 | let bundles 26 | let newTansfers 27 | try { 28 | // Check if its closing the channel 29 | if (!close) { 30 | // Prepare the transfer. 31 | newTansfers = transfer.prepare( 32 | user.flash.settlementAddresses, 33 | user.flash.deposit, 34 | user.userIndex, 35 | actions 36 | ) 37 | } else { 38 | // Distribute the remaining channel balance amongst the channel users 39 | // NOTE: YOU MUST PASS THE SETTLEMENT ADDRESSES ARRAY as 'actions' 40 | newTansfers = transfer.close(actions, user.flash.deposit) 41 | } 42 | 43 | // Compose the transfer bundles 44 | bundles = transfer.compose( 45 | user.flash.balance, 46 | user.flash.deposit, 47 | user.flash.outputs, 48 | toUse.multisig, 49 | user.flash.remainderAddress, 50 | user.flash.transfers, 51 | newTansfers, 52 | close 53 | ) 54 | } catch (e) { 55 | console.log("Error: ", e) 56 | return false 57 | } 58 | return bundles 59 | } 60 | const signTransaction = (user, bundles) => { 61 | return transfer.sign(user.flash.root, user.userSeed, bundles, user.userIndex) 62 | } 63 | const applyTransfers = (user, bundles) => { 64 | transfer.applyTransfers( 65 | user.flash.root, 66 | user.flash.deposit, 67 | user.flash.outputs, 68 | user.flash.remainderAddress, 69 | user.flash.transfers, 70 | bundles 71 | ) 72 | return user 73 | } 74 | 75 | module.exports = { 76 | createTransaction: createTransaction, 77 | signTransaction: signTransaction, 78 | applyTransfers: applyTransfers 79 | } 80 | -------------------------------------------------------------------------------- /libs/iota.flash.js/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const eslint = require('gulp-eslint'); 3 | const del = require('del'); 4 | const gulpNSP = require('gulp-nsp'); 5 | const webpack = require('webpack-stream'); 6 | 7 | const DEST = './dist/'; 8 | 9 | // Lint the JS code 10 | gulp.task('lint', [], function(){ 11 | return gulp.src(['**/*.js','!node_modules/**']) 12 | .pipe(eslint()) 13 | .pipe(eslint.format()) 14 | .pipe(eslint.failAfterError()) 15 | }); 16 | 17 | // Remove existing dist folder 18 | gulp.task('clean', ['lint'], function(cb) { 19 | del([DEST]).then(cb.bind(null, null)); 20 | }); 21 | 22 | // Check for vulns with nsp 23 | gulp.task('nsp', function (cb) { 24 | gulpNSP({package: __dirname + '/package.json'}, cb); 25 | }); 26 | 27 | gulp.task('dist', () => { 28 | return gulp.src('lib/flash.js') 29 | .pipe(webpack({ 30 | output: { 31 | filename: 'iota.flash.js' 32 | } 33 | })) 34 | .pipe(gulp.dest(DEST)) 35 | }); 36 | 37 | gulp.task('default', ['lint', 'clean', 'nsp', 'dist']); 38 | -------------------------------------------------------------------------------- /libs/iota.flash.js/lib/constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'MAX_USES': 3, 3 | 'IRI_PROVIDER': 'http://localhost:14265' 4 | } 5 | -------------------------------------------------------------------------------- /libs/iota.flash.js/lib/flash.js: -------------------------------------------------------------------------------- 1 | const IOTACrypto = require('iota.crypto.js'); 2 | const multisig = require('./multisig'); 3 | const transfer = require('./transfer'); 4 | 5 | /** 6 | * @constructor Flash 7 | * @param {object} options 8 | */ 9 | function Flash(options) { 10 | if (!options) { 11 | options = {}; 12 | } 13 | if (!(this instanceof Flash)) { 14 | return new Flash(options); 15 | } 16 | this.signersCount = 'signersCount' in options ? options.signersCount : 2; 17 | this.state = { 18 | 'index': 0, 19 | 'security': 'security' in options ? options.security : 2, 20 | 'balance': 'balance' in options ? options.balance : 0, 21 | 'deposit': 'deposit' in options ? options.deposit : Array(this.signersCount).fill(0), 22 | 'stakes': 'stakes' in options ? options.stakes : Array(this.signersCount).fill(0.5), 23 | 'outputs': 'outputs' in options ? options.outputs : {}, 24 | 'transfers': 'transfers' in options ? options.transfers : [], 25 | 'remainderAddress': 'remainderAddress' in options ? options.remainderAddress : '' 26 | }; 27 | } 28 | 29 | Flash.multisig = multisig; 30 | Flash.transfer = transfer; 31 | 32 | module.exports = Flash; 33 | -------------------------------------------------------------------------------- /libs/iota.flash.js/lib/helpers.js: -------------------------------------------------------------------------------- 1 | function deepClone(from) { 2 | let copy; 3 | if (Object.prototype.toString.call(from) === '[object Object]') { 4 | copy = {}; 5 | for(const x in from) { 6 | copy[x] = deepClone(from[x]); 7 | } 8 | } 9 | else if (Array.isArray(from)) { 10 | let i = -1; 11 | copy = []; 12 | while (++i < from.length) { 13 | copy[i] = deepClone(from[i]); 14 | } 15 | } 16 | else { 17 | copy = from; 18 | } 19 | return copy; 20 | } 21 | 22 | module.exports = { 23 | deepClone 24 | }; 25 | -------------------------------------------------------------------------------- /libs/iota.flash.js/lib/multisig.js: -------------------------------------------------------------------------------- 1 | const IOTACrypto = require('iota.crypto.js'); 2 | const constants = require('./constants'); 3 | 4 | function getDigest(seed, index, security) { 5 | return { 6 | 'digest': IOTACrypto.multisig.getDigest(seed, index, security), 7 | 'security': security, 8 | 'index': index 9 | }; 10 | } 11 | 12 | function composeAddress(digests) { 13 | const multisig = initializeAddress(digests); 14 | return finalizeAddress(multisig); 15 | } 16 | 17 | function initializeAddress(digests) { 18 | const address = new IOTACrypto.multisig.address(); 19 | const multisig = { 20 | 'address': address, 21 | 'securitySum': 0, 22 | 'children': [], 23 | 'bundles': [], 24 | } 25 | if (digests) { 26 | return absorbAddressDigests(multisig, digests); 27 | } 28 | return multisig; 29 | } 30 | 31 | function absorbAddressDigests(multisig, digests) { 32 | const toAbsorb = digests.map(digest => digest.digest); 33 | multisig.address = multisig.address.absorb(toAbsorb); 34 | multisig.securitySum += digests.reduce((sum, digest) => sum + digest.security, 0); 35 | return multisig; 36 | } 37 | 38 | function finalizeAddress(multisig) { 39 | if (multisig.securitySum <= 0) { 40 | throw new Error('Could not finalize address'); 41 | } 42 | multisig.address = multisig.address.finalize(); 43 | return multisig; 44 | } 45 | 46 | function initTreeWithCount(flash, txCount) { 47 | return initializeTree(flash, Math.ceil(Math.log(txCount)/Math.log(constants.MAX_USES))) 48 | } 49 | 50 | function initializeTree(flash, depth) { 51 | const root = initializeAddress(); 52 | root.index = flash.state.index++; 53 | let node = root; 54 | for(let i = depth; i-- > 0;) { 55 | node.children.push(initializeAddress()); 56 | node.index = flash.state.index++; 57 | node = node.children[0]; 58 | } 59 | return root 60 | } 61 | 62 | function addBranch(root, start, branch) { 63 | let done = false; 64 | let node = root; 65 | for(node = root; node.address != start.address && node.children.length != 0; node = node.children[node.children.length - 1]) {} 66 | node.children.push(branch); 67 | } 68 | 69 | function updateLeafToRoot(root) { 70 | const multisigs = getLastBranch(root); 71 | let i; 72 | // get the first one that doesn't pass all the way down 73 | for(i = 0; i < multisigs.length-1; i++){ 74 | if(!multisigs[i].bundles.find(bundle => bundle.find(tx => tx.value > 0 && tx.address == multisigs[i+1].address))) { 75 | return { 76 | multisig: multisigs[i], 77 | generate: 0 78 | } 79 | } 80 | } 81 | // get the first from the bottom that has less than 3 usages 82 | let toGenerate = 0; 83 | for(i = multisigs.length; i-- > 0 && multisigs[i].bundles.length == constants.MAX_USES;) { 84 | toGenerate++; 85 | } 86 | let node = multisigs[i]; 87 | return { 88 | multisig: multisigs[i], 89 | generate: toGenerate 90 | } 91 | } 92 | 93 | function getLastBranch(root) { 94 | let multisigs = []; 95 | let node = root 96 | multisigs.push(node) 97 | 98 | while (node.children.length != 0) { 99 | node = node.children[node.children.length - 1] 100 | multisigs.push(node); 101 | } 102 | return multisigs; 103 | } 104 | 105 | function getMinimumBranch(root) { 106 | let multisigs = []; 107 | let node = root 108 | multisigs.push(node) 109 | 110 | while (node.children.length != 0 && node.bundles.length == constants.MAX_USES) { 111 | node = node.children[node.children.length - 1] 112 | multisigs.push(node); 113 | } 114 | return multisigs; 115 | } 116 | 117 | function getMultisig(knot, addy) { 118 | if(knot.address == addy) { 119 | return knot; 120 | } 121 | for(const child of knot.children.reverse()) { 122 | const multisig = getMultisig(child, addy); 123 | if(multisig) { 124 | return multisig; 125 | } 126 | } 127 | } 128 | 129 | module.exports = { 130 | getDigest : getDigest, 131 | composeAddress : composeAddress, 132 | initializeAddress : initializeAddress, 133 | initializeTree : initializeTree, 134 | initTreeWithCount : initTreeWithCount, 135 | absorbAddressDigests : absorbAddressDigests, 136 | finalizeAddress : finalizeAddress, 137 | updateLeafToRoot : updateLeafToRoot, 138 | getLastBranch : getLastBranch, 139 | getMinimumBranch : getMinimumBranch, 140 | getMultisig : getMultisig, 141 | }; 142 | -------------------------------------------------------------------------------- /libs/iota.flash.js/lib/transfer.js: -------------------------------------------------------------------------------- 1 | const IOTACrypto = require('iota.crypto.js'); 2 | const MAX_USES = require('./constants').MAX_USES; 3 | const helpers = require('./helpers'); 4 | const getLastBranch = require('./multisig').getLastBranch; 5 | const getMinimumBranch = require('./multisig').getMinimumBranch; 6 | 7 | const TransferErrors = { 8 | NULL_VALUE: -1, 9 | REMAINDER_INCREASED: 0, 10 | INVALID_TRANSFER_OBJECT: 1, 11 | INSUFFICIENT_FUNDS: 2, 12 | INVALID_TRANSFERS_ARRAY: 3, 13 | INVALID_SIGNATURES: 4, 14 | ADDRESS_OVERUSE: 5, 15 | ADDRESS_NOT_FOUND: 6, 16 | INPUT_UNDEFINED: 7, 17 | INVALID_INPUT: 8, 18 | BALANCE_NOT_PASSED: 9 19 | }; 20 | 21 | /** 22 | * Prepare transfers object 23 | * 24 | * @method prepare 25 | * @param {array} settlement the settlement addresses for each user 26 | * @param {array} deposits the amount each user can still spend 27 | * @param {number} fromIndex the index of the user used as an input 28 | * @param {destinations} the `{value, address}` destination of the output bundle (excluding remainder) 29 | * @returns {array} transfers 30 | */ 31 | function prepare(settlement, deposits, fromIndex, destinations) { 32 | const total = destinations.reduce((acc, tx) => acc + tx.value, 0); 33 | if(total > deposits[fromIndex]) { 34 | throw new Error(TransferErrors.INSUFFICIENT_FUNDS); 35 | } 36 | const transfer = helpers.deepClone(destinations); 37 | settlement.map((s,i) => { 38 | if(i != fromIndex) { 39 | const current = transfer.find(tx => tx.address == s); 40 | const stake = total * deposits[i] / deposits.filter((e,i) => i != fromIndex).reduce((acc, s) => acc + s, 0); 41 | if(current) { 42 | current.value += stake; 43 | current.obsoleteTag = '' 44 | } else { 45 | transfer.push({ address: s, value: stake, obsoleteTag: '' 46 | }) 47 | } 48 | } 49 | }) 50 | return transfer.filter(tx => tx.value > 0); 51 | } 52 | 53 | /** 54 | * Composes a Transfer 55 | * 56 | * @method compose 57 | * @param {number} balance The total amount of iotas in the channel 58 | * @param {array} deposit the amount of iotas still available to each user to spend from 59 | * @param {array} outputs the accrued outputs through the channel 60 | * @param {array} history the leaf bundles 61 | * @param {array<{addy, val}>} transfers the array of outputs for the transfer 62 | * @param {bool} close whether to use the minimum tree or not 63 | * @return {array} prepared bundles 64 | */ 65 | function compose(balance, deposit, outputs, root, remainder, history, transfers, close) { 66 | const valueTransfersLength = transfers.filter( transfer => transfer.value < 0 ).length; 67 | if (valueTransfersLength != 0 && valueTransfersLength > deposit.length) { 68 | throw new Error(TransferErrors.INVALID_TRANSFER_OBJECT); 69 | } 70 | const amount = transfers.reduce((a,b) => a + b.value, 0); 71 | 72 | const deposits = deposit.reduce((a,b) => a + b, 0); 73 | if( amount > deposits || deposits < 0) { 74 | throw new Error(TransferErrors.INSUFFICIENT_FUNDS); 75 | } 76 | 77 | transfers = transfers.map( transfer => { 78 | if (transfer.address in outputs) { 79 | transfer.value += outputs[transfer.address]; 80 | } 81 | return transfer; 82 | }); 83 | for(const addy in outputs) { 84 | if (!transfers.find(tx => tx.address == addy)) { 85 | transfers.push ({address: addy, value: outputs[addy]}); 86 | } 87 | } 88 | const bundles = []; 89 | let multisigs = close ? getMinimumBranch(root) : getLastBranch(root); 90 | if(multisigs[0].bundles.length == MAX_USES) { 91 | throw new Error(TransferErrors.ADDRESS_OVERUSE); 92 | } 93 | for(let i = 0; i < multisigs.length - 1; i++) { 94 | if(multisigs[i].bundles.find(bundle => bundle.find(tx => tx.value > 0 && tx.address == remainder))) { 95 | multisigs = multisigs.slice(i+1); 96 | } else { 97 | break; 98 | } 99 | } 100 | if(multisigs.length == 0) { 101 | throw new Error(TransferErrors.ADDRESS_OVERUSE); 102 | } 103 | multisigs.slice(0,multisigs.length-1).map((multisig, i) => { 104 | const input = { 105 | address: multisig.address, 106 | securitySum: multisig.securitySum, 107 | balance: balance 108 | }; 109 | const remainderAddress = remainder.address 110 | const transfers = [{ 111 | address: multisigs[i + 1].address, 112 | value: balance, 113 | obsoleteTag: '' 114 | }]; 115 | 116 | IOTACrypto.multisig.initiateTransfer( 117 | input, 118 | remainder.address, 119 | transfers, 120 | (err, success) => { 121 | bundles.push(success) 122 | } 123 | ) 124 | }) 125 | const multisig = multisigs[multisigs.length - 1]; 126 | const input = { 127 | address: multisig.address, 128 | securitySum: multisig.securitySum, 129 | balance: balance 130 | }; 131 | IOTACrypto.multisig.initiateTransfer( 132 | input, 133 | remainder.address, 134 | transfers, 135 | (err, success) => { 136 | bundles.push(success) 137 | } 138 | ) 139 | return bundles; 140 | } 141 | 142 | /** 143 | * creates transactions to close the channel 144 | * 145 | * @method close 146 | * @param {array} settlement the settlement addresses for each user 147 | * @param {array} deposits the amount each user can still spend 148 | * @returns {array} transfers 149 | */ 150 | function close(settlement, deposits) { 151 | return settlement.filter(tx => tx).map((s, i) => { 152 | return { address: s, value: deposits[i] }; 153 | }).filter(tx => tx.value > 0); 154 | } 155 | 156 | /** 157 | * Applies Transfers to State 158 | * 159 | * @method apply 160 | * @param {object} state 161 | * @param {array} transfers 162 | */ 163 | function applyTransfers(root, deposit, outputs, remainder, history, transfers) { 164 | if (transfers.filter(transfer => 165 | transfer.filter(tx => tx.value < 0) 166 | .filter(tx => !IOTACrypto.utils.validateSignatures(transfer, tx.address)) 167 | .length != 0).length != 0) { 168 | throw new Error(TransferErrors.INVALID_SIGNATURES); 169 | } 170 | let multisigs = getMultisigs(root, transfers); 171 | if(multisigs.filter(node => node.bundles.length == 3).length != 0) { 172 | throw new Error(TransferErrors.ADDRESS_OVERUSE); 173 | } 174 | if(multisigs.length != transfers.length ) { 175 | throw new Error(TransferErrors.ADDRESS_NOT_FOUND); 176 | } 177 | try { 178 | let diff = getDiff(root, remainder, history, transfers); 179 | let remaining = deposit.reduce((a,b) => a+b, 0); 180 | let total = diff.filter(v => v.value > 0).reduce((acc,tx) => acc + tx.value, 0); 181 | if (total > remaining) { 182 | throw new Error(TransferErrors.INSUFFICIENT_FUNDS); 183 | } 184 | const depositTotal = deposit.reduce((acc, d) => acc + d, 0); 185 | const depositDiff = deposit.map((d) => total * d / depositTotal); 186 | for(const i in deposit) { 187 | deposit[i] -= depositDiff[i]; 188 | } 189 | for(let i = 0; i < diff.length; i++) { 190 | if(diff[i].address in outputs) { 191 | outputs[diff[i].address] += diff[i].value; 192 | } else { 193 | outputs[diff[i].address] = diff[i].value; 194 | } 195 | } 196 | transfers.map((transfer, i) => { 197 | multisigs[i].bundles.push(transfer); 198 | }); 199 | history.push(transfers[transfers.length - 1]); 200 | } catch (e) { 201 | throw e; 202 | } 203 | } 204 | 205 | function getMultisigs(root, transfers) { 206 | let node = root; 207 | let firstTransfer = transfers[0].find(tx => tx.value < 0) 208 | while(node.address != firstTransfer.address && node.children.length != 0) { 209 | node = node.children[node.children.length - 1]; 210 | } 211 | if(node.address != firstTransfer.address) { 212 | throw new Error(TransferErrors.ADDRESS_NOT_FOUND); 213 | } 214 | let multisigs = []; 215 | let i = 0; 216 | multisigs.push(node) 217 | while (node.children.length != 0 && ++i < transfers.length) { 218 | node = node.children.find(m => m.address == transfers[i].find(tx => tx.value < 0).address); 219 | if(node.bundles.length == MAX_USES) { 220 | throw new Error(TransferErrors.ADDRESS_OVERUSE); 221 | } 222 | if(!node) { 223 | throw new Error(TransferErrors.ADDRESS_NOT_FOUND); 224 | } 225 | multisigs.push(node); 226 | } 227 | return multisigs; 228 | } 229 | 230 | /** 231 | * 232 | * @return {[{object}]} signatures 233 | */ 234 | function sign(root, seed, bundles) { 235 | const multisigs = getMultisigs(root, bundles); 236 | return helpers.deepClone(bundles).map((bundle, i) => { 237 | const multisig = multisigs[i]; 238 | // multisig has member signingIndex 239 | bundle 240 | .filter(tx => tx.address == multisig.address) 241 | .slice(0,multisig.signingIndex) 242 | .map(tx => { 243 | if( 244 | IOTACrypto 245 | .utils 246 | .inputValidator 247 | .isNinesTrytes(tx.signatureMessageFragment) 248 | ) { 249 | tx.signatureMessageFragment = 250 | tx.signatureMessageFragment.replace(/^9/,'A'); 251 | } 252 | }); 253 | 254 | var sigs = [] 255 | IOTACrypto.multisig.addSignature(bundle, multisig.address, IOTACrypto.multisig.getKey(seed, multisig.index, multisig.security), (err, suc) => { 256 | sigs = { bundle: bundle[0].bundle, 257 | address: multisig.address, 258 | index: multisig.signingIndex, 259 | signatureFragments: suc 260 | .filter(tx => tx.address == multisig.address) 261 | .map(tx => tx.signatureMessageFragment) 262 | .slice(multisig.signingIndex, multisig.signingIndex + multisig.security) 263 | } 264 | }) 265 | return sigs 266 | }); 267 | } 268 | 269 | /** 270 | * signatures is an array of signatures for this bundle 271 | */ 272 | function appliedSignatures(bundles, signatures) { 273 | return helpers.deepClone(bundles).map((bundle, i) => { 274 | let userSignature = signatures[i];//.find(s => s.bundle == bundle[0].bundle); 275 | if (userSignature) { 276 | let addy = bundle.find(tx => tx.value < 0 ).address; 277 | bundle 278 | .filter(tx => tx.address == addy) 279 | .slice(userSignature.index, userSignature.index + userSignature.signatureFragments.length) 280 | .map((tx,j) => tx.signatureMessageFragment = userSignature.signatureFragments[j]); 281 | // add signature 282 | } 283 | return bundle; 284 | }); 285 | } 286 | 287 | /** 288 | * Adds signatures to bundles 289 | * 290 | * @param {object} bundle the bundle to add signatures to 291 | * @param {string} address the address for the signatures 292 | * @param {array} signatures a 2d array of signatures for each bundle 293 | * 294 | * example usage: 295 | * bundles.map((bundle, i) => 296 | * addSignatures( 297 | * bundle, 298 | * bundle.find(tx => tx.value < 0).address, 299 | * signatures[i] 300 | * ) 301 | * ) 302 | */ 303 | function addSignatures(bundle, address, signatures) { 304 | bundle.filter(tx => tx.address == address).map((tx, i) => { 305 | tx.signatureMessageFragment = signatures[i]; 306 | }); 307 | } 308 | 309 | function getDiff(root, remainder, history, bundles) { 310 | if(!root) { 311 | throw new Error(TransferErrors.NULL_VALUE); 312 | } 313 | if(!remainder) { 314 | throw new Error(TransferErrors.NULL_VALUE); 315 | } 316 | if(!history) { 317 | throw new Error(TransferErrors.NULL_VALUE); 318 | } 319 | if(!bundles) { 320 | throw new Error(TransferErrors.NULL_VALUE); 321 | } 322 | const initialInputTransaction = bundles[0].filter(bundle => bundle.value < 0)[0]; 323 | if (!initialInputTransaction) { 324 | throw new Error(TransferErrors.INPUT_UNDEFINED); 325 | } 326 | const multisigs = getMultisigs(root, bundles); 327 | if (bundles.length != multisigs.length) { 328 | throw new Error(TransferErrors.TOO_MANY_BUNDLES); 329 | } 330 | const initialIndex = multisigs.filter(m => m.address == initialInputTransaction.address).map((m,i) => i)[0]; 331 | for(let i = initialIndex; i < multisigs.length - 1 && (i - initialIndex) < bundles.length - 1; i++) { 332 | const bundle = bundles[i - initialIndex]; 333 | const inputTransaction = bundle.filter(tx => tx.value < 0)[0]; 334 | if(!inputTransaction || inputTransaction.address != multisigs[i].address) { 335 | throw new Error(TransferErrors.INVALID_INPUT); 336 | } 337 | // TODO 338 | // Check if entire amount is being passed to next multisig 339 | if(bundle.find(tx => tx.value > 0 && tx.address != multisigs[i + 1].address)) { 340 | throw new Error(TransferErrors.BALANCE_NOT_PASSED); 341 | } 342 | } 343 | let previousTransfer = history.length == 0 ? []: history[history.length - 1]; 344 | const lastTransfer = bundles[bundles.length - 1]; 345 | const previousRemainder = previousTransfer.filter(tx => tx.address == remainder.address && tx.value > 0).reduce((acc, v) => acc + v.value, 0); 346 | const newRemainder = lastTransfer.filter(tx => tx.address == remainder.address) 347 | .map(tx => tx.value ) 348 | .reduce((acc, v) => acc + v, 0) 349 | if(newRemainder.value > previousRemainder.value) { 350 | throw new Error(TransferErrors.REMAINDER_INCREASED); 351 | } 352 | const newCopy = helpers.deepClone(lastTransfer 353 | .filter(tx => tx.value > 0) 354 | .map(tx => Object({address: tx.address, value: tx.value}))) 355 | .filter(tx => tx.address !== remainder.address) 356 | for(const tx of previousTransfer.filter(tx => tx.value > 0)) { 357 | const existing = newCopy.find(t => t.address == tx.address); 358 | if(existing) { 359 | existing.value -= tx.value; 360 | } else { 361 | newCopy.push({address: tx.address, value: tx.value}); 362 | } 363 | } 364 | const negatives = newCopy.filter(tx => tx.value < 0); 365 | if(negatives.length != 0 ) { 366 | throw new Error(TransferErrors.INVALID_INPUT); 367 | } 368 | 369 | var minusRemainder = newCopy.filter(tx => tx.address !== remainder.address) 370 | 371 | return minusRemainder; 372 | } 373 | 374 | module.exports = { 375 | 'prepare' : prepare, 376 | 'compose' : compose, 377 | 'close' : close, 378 | 'getDiff' : getDiff, 379 | 'sign' : sign, 380 | 'appliedSignatures': appliedSignatures, 381 | 'applyTransfers' : applyTransfers, 382 | 'TransferErrors' : TransferErrors 383 | } 384 | -------------------------------------------------------------------------------- /libs/iota.flash.js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iota.flash.js", 3 | "version": "0.0.1", 4 | "description": "IOTA Flash Payment Channels", 5 | "main": "./lib/flash.js", 6 | "scripts": { 7 | "test": "mocha", 8 | "build": "gulp" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/l3wi/flashLib.git" 13 | }, 14 | "keywords": ["payment", "channel", "tangle", "iota"], 15 | "files": ["/lib/multisig.js", "/lib/transfer.js"], 16 | "authors": [ 17 | "Paul Handy ", 18 | "Chris Dukakis ", 19 | "Lewis Freiberg <>", 20 | "Peter Willemsen " 21 | ], 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/iotaledger/iota.flash.js/issues" 25 | }, 26 | "homepage": "https://github.com/iotaledger/iota.flash.js#readme", 27 | "devDependencies": { 28 | "chai": "^4.1.1", 29 | "del": "^3.0.0", 30 | "eslint": "^4.5.0", 31 | "gulp": "^3.9.1", 32 | "gulp-eslint": "^4.0.0", 33 | "gulp-nsp": "^2.4.2", 34 | "mocha": "^3.5.0", 35 | "webpack-stream": "^4.0.0" 36 | }, 37 | "dependencies": { 38 | "iota.crypto.js": "github:iotaledger/iota.crypto.js" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /libs/iota.flash.js/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec --recursive 2 | -------------------------------------------------------------------------------- /libs/iota.flash.js/test/transfer/transfer.getDiff.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var assert = chai.assert; 3 | var transfer = require('../../lib/transfer'); 4 | 5 | describe('kerl.absorb-multi-squeeze', function() { 6 | 7 | var tests = [{ 8 | bundles: [ 9 | [{ 10 | "address": "VEIHWDYXSJSEUGJFKJTZDKDSVXTNDJUVOGNNUBUHRAMUWFKVLFMYGHBTTMHGCKEYWRNGKCNXFYPOLKJHA", 11 | "value": 300, 12 | "tag": "999999999999999999999999999", 13 | "timestamp": 1503255453, 14 | "currentIndex": 0, 15 | "lastIndex": 4, 16 | "bundle": "X9OUDA9WMHUMNOCWNICCADLKBSXGNUHVQGUUGHUVEEACSXDHQYBDEVBADVUXJNVXCTTEFSOVUPAJGPVLC", 17 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 18 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 19 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 20 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 21 | }, { 22 | "address": "LWHATLLT9TAFPWZVRNPWLXJLUDXPHQYIGMRQALRAAF9OIEIUKHMTYXLGSJGZBFAVELQFPQ9SGZWEIQHZX", 23 | "value": -300, 24 | "tag": "999999999999999999999999999", 25 | "timestamp": 1503255453, 26 | "currentIndex": 1, 27 | "lastIndex": 4, 28 | "bundle": "X9OUDA9WMHUMNOCWNICCADLKBSXGNUHVQGUUGHUVEEACSXDHQYBDEVBADVUXJNVXCTTEFSOVUPAJGPVLC", 29 | "signatureMessageFragment": "REZOTKN9RNVBJIHBMCDXLBJJBGHWWCOSAMPWJIIAROQBG9HDBLV9EAGWRLXQEFWZTJLMYTIQDWUEQYZIDPLG9NYLYAAEAXLHHMVRKXVNHZBL9ITNMLHPXHOUQUEKHCEAVAVBNNNEJ9KLZYRZB9EPBYWR9YQGRJUMMDUXVIBSNXELBI9BAXZYE9DJBVUGYGO9AERMM9QCMEDMUXV9LLMREJPWABTFOBZM9KRQDAVOCTQWVFNEZSWLWIGNRGTIZEGBHXSWQOGIYJI9Q9XLVDYWAOFWNIEKZWSLRCCGFXLOYVVR9JJXKENVZELC9ZFFZQVAXFIYFDXWX99CWVUKGPCHEMGYDXMLSJJBNCZYQRROVRKUZZTFFJP9PHI99U9YVTGDVROVYYYKQZDXRVPHLQG9CSTMLTUEATPXOUBTF9GUJYSZKAPARL9XE9FWK9FXOTRTBCFWXYMWUAHDLOWBKHURNX9NAVFZEUZKTUCFWCXJBFDBPTRRBPNVEEZFHJLKSYWYXLZVGVMDDXLQ9BZXBAUQWAUIXSBYJCPAGZU9IWZMLLWJYLVHKYXWOOZWBZ9XXFMRHZLUECVJ9WARMXLOXFWMQXZRLUADGM9BEKBQ9ECQYZYHLK9CYCWXGFTNY9BLJFSF9IBDVVOXCVNADUZEGQCZGUA9KVAFIZBSRHCPXFFPLHDZMGQBATRBXULEKSIDGOR9NLCOYJHUOHC9HFJT9ZEDBWDJAEMCJZEFEOJFMBWGPHBOYWNMZKWGPHDCOAFOBRKENDZGYWDVGEAJJPDUKHZJGBHXPWCYJKCQOMGTGODKZYMQZHYYBQGKICRFB9FWPXNAFT9E9UNLKLRITXJWPLLZAEGTUGVUVBSXBIKVKHDYSQMLJUYILPMXKZEJULXPPQCSHGPCMBSHFBFTOEQYAZDMEZUYLGKTEMFPCAYRBQYXU9PVE9DQJGSAPRRBNPGEQDH9MJIBIDOOLUEXVVQOKECMUAPXHYAUNMOOIDBXFBVCCXV9IIMGQQRQOVEWZWOITOWIVDBWQYXAUYGITJCJQJTKCSJZSAVABUQ9QMEOGYQROVNZNEALLKAWBNS9SUIWLOQYSNNWUQHWVUSIUQNMZDOMHOMCKEBYCSCVWRCPDN9FBTJMCWRAIZCKYIMKUZQVMNNOSOTTMLJJLGFLZPQFM9BKBLJLOVZ9SAQRJBDMDCUHBCWEQKSELPZJZYNTDFALUABOMSBUWKNPC9UDCIVVS9LHSUJSQNBHXYTWKPPTOQ9PWBQEOYXYOYPNZQKEJQZPXQLYEGJWANFCXAITTDCXSC9QBYYUZZJCVKHZOGC9WWVNLVHDJ9QED9AELNPANLMLRHIHGPX9WHTWJXKEFEBGNRBKVYGTBWQSHWWTBJNJNMLKSJUOKZPERHWLKLIIEBUTCUSJQVYAADPONBCA9EHJMHQULEWEOEKOTBMTIBGQCAQY9QKKBPC9CZZ9HRTZGSWOIKUWWRD9G9OPXDPEKA9RYEANNUSI9VZLYEDOR9GCWEW9KQC9WLD9KSPBMOBUMGUHQKADYFCJJWFZJZCJEPBKYMCZYQIJD9NWHKDILEVKOHFJJQWEDJUWIBQKOYTPVPOLNLRDZQ9GM9OOFNSE9EXJCFHDRAO9NM9DMSVXCWWLGKYDNTTJGXFAHZQFAUWIER9DGLYSAZXUKEVTCGHJBFZSRLA9AXOUZLYGZMXYO9PRZFDVOVDQ9AB9QEQMRMTENOTRYWVUMSASRIPDALUPQCOOPOBNYSDFXBVQKBWOCNJCXCXUIETIFZCBEEOBOWQHWMROOYIQJUUWUMEELWLKXGTQYMRWYIZJ9ZXYRJDIDBUOAUDXRAUVKJAAPMFWWEGLLWLBGQYEQDCZAGDSHLMGUJMIZVYZKIGPFHJSXNLPOTIMKLKZIYMJIFXFVCPGRRFWGGLPGXAAEKKYIKSUECDSUHGBEXWJD9GCXFAZWDIWOU9STT9JODBPBIGSRRGMLMOXSCSXXVMIHRTAFJNHDCUJUAROXBYJRHRQWBLFKKVAMINTZYLWXTVTTHZNWSCKCPBFJH9UN9NGOALEYCSGAWEZEECDSWCPXQMEVPWLAD9BZXMPBETSRTHCXFRQDMPVYPWCTANTLDVFIZVLSBEKNKDCQHZRVKKQKJSNDRHXTKAOQMMNOYTEPJDOKBU9PHJUQZ9ZWVHGQZHDWORRUSTDYPZ", 30 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 31 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 32 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 33 | }, { 34 | "address": "LWHATLLT9TAFPWZVRNPWLXJLUDXPHQYIGMRQALRAAF9OIEIUKHMTYXLGSJGZBFAVELQFPQ9SGZWEIQHZX", 35 | "value": 0, 36 | "tag": "999999999999999999999999999", 37 | "timestamp": 1503255453, 38 | "currentIndex": 2, 39 | "lastIndex": 4, 40 | "bundle": "X9OUDA9WMHUMNOCWNICCADLKBSXGNUHVQGUUGHUVEEACSXDHQYBDEVBADVUXJNVXCTTEFSOVUPAJGPVLC", 41 | "signatureMessageFragment": "APNCQYGCMZVPVEDNZGEPBWOCOVR9DMGA9EJ9YIKLUBQEUAPPMLVWZWMOWTMDYJNEPFBIPLFKQPEMBP9RXDGNSCOPJCTYKAISTTCQDXUHUMXJVGAXXNRGNLRMWTKRYTELFVIDGQRTJGMPEGLZUEOIGJK9DWAB9OZUGDFWF9ZYADHBA9JRYTOUUXCQGMOVBPKPCXGVUTEFGARQAZIFOPOTYAOKZI9ZPZFMEFM9ABKBCWPLJONYZRASIMIWIAGFENNYDFUSEEMTNZOBETSGXDTOERBNTPATSBQRXYNAFRBKLCBZVQKQNYKXBMEQNHUXKGWOEF9YPSVUGN9W9SDWYNDIUZVXOIQOYPNK9PJIIYPBFYAJCVWGNNAYMHCYZDLOYMYY9MAFDDC9FQMTZEQXGFPDCZDON9OFZGHAMZZ9FQDEZ9HASZKGLYUUZYY9BOTFHFBFZFASTZWILSFP9KPSBIAKNOMNRSAMTZTLCVBYYAFGOACSYOGDWDSTZKMTUDVNKTVTVIWTVYZLMMUQRPFYMJBAZYGMJH9GCVSOFIPXEARUZBSXHMNVMETVNIDPRREIBVIXUBE9TQKGVTFZRSTRKEKXVKZJBXDCVFSTPPESSQSIWZPWYHSUZBK9QAIMYTGKVXKZSKUSJYGZSEOLOUXDODZIDTCJNNLOSDQEQVAHLWOUA9NVSMKOKKHTZNECGYSVKIFWRLMRSBBBKWLBXEGDCFD9YRRIAUJKLHTFKTULGKZSREQZXPIHQLYFCHDMDONOMY9LNHRBSGLKGFXKIMWHPNIEZM9DCVKWTMGDBNARSMQSOBDTJSJWPTCJRUDCUUPKDHKSJQERTCIHNUIWSROMVRUCFJSZNC9LUMZJBORUFDFKYGMDLCTSLP9SMGVYDAZYSVXGFKOFAQSZHSNYFTDPXTCEHUQFAMXXRCEXUOVISEQXKRXZMIWSA9HVSOMTBYTGVMWRGQNWXCURVRPWKS9TBDUVG9OIGZONCNMDQOBQILYTUTLPNDSIQKSHMIOBCZ9QMTQQMNKMFEFQKWLGC9VHW9RRQIDDQCYFBYABJTCTEUQZHTXFNC9HOOPSVBTYASO9MQWOGFTBUZLQWOSVDDFWACHSIPOFTEYYOQPY9IRHTMRXQIYWTXKFZYGIFCPAKDHZPKDLACYI9IVWEETBYNHGKMBNIXPNJYTWKUEPQFUBGZISLBOYQJEEJCKXKVO9N9XXICCDZDGEYJQMEB9CWVYYXRAUCPQEKBHJLBPYTSPMZTVYOTJZHXHIDAMGHEPMOYQIRNVCGLOATSAXJTCJFDUW9DIXAX9FFDYGMRCTSIXIVKUS9ZIRHIKMSEXGHVGJIWMMEODPENTYHDUHONUFBNIFO9LHBFDOQXGLASLQDZMESKCAWIIZMIFCEBLMYTWJMT9KAVZWNYKDOPXRBDBZCMAMHQLWOMPMUQMTKGJJEXHJJ9WHQ9FOHOISLDNUFJQWBRCWM9NLU9VNWHVYIAPGWKCHIXM9BPDFOWWWJGLOHJUFBDYRYGYNJOFDMZE9GREKTE9VCHMWKZW9FLJPPFBXAMOZPDSDXP9ZXKZVSRHAKUKOIINXNSBVTTQBROFOLULR9SJA9RNNBQSLIJZBOGVT99SGAJUBGCYWRFMSNMECLBFKMXCAHLSUEECSNWRQETZGALAVHJBFXIHVJGHYWLVELJZCTAGATDOUZVJWPLRMODHKYLASGOON9UXWIACPPWRJQICFIOPBECUWF9XFXBMNZD9RFHDZTU99XJGSEYUMOKJPPIRGNFFVLJQVKBFVKB9WNLNJBPVW9MMNZEMQWLXYRGIBARNKJLCCGDJUVDQCMAFNGNEKJ9XBDWGINPIVADQTH9QXL9SYFDFQUBAIIJIACUPKMINQNEGNDNGHARUDUUYKKPIATKEAOCVMWILLTFSGXHWZWSOMYGUQYPCHALJVOFMLDMIUVLRDJAV9DXMXHWXAIHQJTBSOWLJFVTGUHRQGQVII9EHVOJQKOOCYTTSOQPIBCWCGDDBPHGUHPIDAIJWD9QJGYFRQFPWWUMDKDTPFYDIYQUOFLSGC9ZKVUMTOTHWOGPSWUNEH9DRISFHWQTAYXYTHDKFMSYDOVZICMSBRVYQJJWWNZPXGBLAPKSCCFITGEBNBSQSOACGQUVSNISWHB9XEZVLVFSFDRVCLQDWFBSVUZLAXJPPLWDABIFZ", 42 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 43 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 44 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 45 | }, { 46 | "address": "LWHATLLT9TAFPWZVRNPWLXJLUDXPHQYIGMRQALRAAF9OIEIUKHMTYXLGSJGZBFAVELQFPQ9SGZWEIQHZX", 47 | "value": 0, 48 | "tag": "999999999999999999999999999", 49 | "timestamp": 1503255453, 50 | "currentIndex": 3, 51 | "lastIndex": 4, 52 | "bundle": "X9OUDA9WMHUMNOCWNICCADLKBSXGNUHVQGUUGHUVEEACSXDHQYBDEVBADVUXJNVXCTTEFSOVUPAJGPVLC", 53 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 54 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 55 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 56 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 57 | }, { 58 | "address": "LWHATLLT9TAFPWZVRNPWLXJLUDXPHQYIGMRQALRAAF9OIEIUKHMTYXLGSJGZBFAVELQFPQ9SGZWEIQHZX", 59 | "value": 0, 60 | "tag": "999999999999999999999999999", 61 | "timestamp": 1503255453, 62 | "currentIndex": 4, 63 | "lastIndex": 4, 64 | "bundle": "X9OUDA9WMHUMNOCWNICCADLKBSXGNUHVQGUUGHUVEEACSXDHQYBDEVBADVUXJNVXCTTEFSOVUPAJGPVLC", 65 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 66 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 67 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 68 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 69 | }], 70 | [{ 71 | "address": "LZCKXDCQR9UMGEPWNLVAPDOHYYFXXOKNRVLCNPCZOADOXRB9KAYQEVLORAKJFEFIWP9PAUIBOELYOOLED", 72 | "value": 300, 73 | "tag": "999999999999999999999999999", 74 | "timestamp": 1503255453, 75 | "currentIndex": 0, 76 | "lastIndex": 4, 77 | "bundle": "IRUFOGFPN9DIQXSCQKIIXXWEBLFAXHGSRTHCHTTTIASIOJMRCN9FHHPTVGGPXIUWJJEBBOER9UJYDBRW9", 78 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 79 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 80 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 81 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 82 | }, { 83 | "address": "VEIHWDYXSJSEUGJFKJTZDKDSVXTNDJUVOGNNUBUHRAMUWFKVLFMYGHBTTMHGCKEYWRNGKCNXFYPOLKJHA", 84 | "value": -300, 85 | "tag": "999999999999999999999999999", 86 | "timestamp": 1503255453, 87 | "currentIndex": 1, 88 | "lastIndex": 4, 89 | "bundle": "IRUFOGFPN9DIQXSCQKIIXXWEBLFAXHGSRTHCHTTTIASIOJMRCN9FHHPTVGGPXIUWJJEBBOER9UJYDBRW9", 90 | "signatureMessageFragment": "SKIZYY9YXOKVEGGZHCUQAOU99IDJODNKDASPNTMW9XGDXJDQUNUINPHGHNUAMZSHQOLAA9XDZGSKOQJKDUFZEQKLYIQAEKOCEAWWFSDNLPESRLEVKMMGVVBFBNOXICUAYJAPUWOIXFBGAZIIOAQ9VQUAFUMRJM9XAYRGUCAZQOQLRJAEWLXAQEVSYSUFOIUIFTPYGFTZCTZUG9GUTMHJKJOSWBGRGUMKKHAQVXEOFPLCWYRL9WYLQCTSEPUGV9DTSJHDODXSE9XUHAZYKPSVXLNNPQEAOWMHSRWCPBHFKSUHWAUGCXOGZQLLCX9EBXCI9DZ9KVCIOTQXHJPNGTLRGNPXZDJDLVTDWFVVNRIJSRHUKSSWFDMUOUJNL9UCSBYPRPPEFOMFPLWKBQOZIABHBCJWJHTAWLFHPIGXELOXCAFJSUEPCKHOKEXWBJKKYDETKBIMYUDVUNGXBNSYLFEZVWKVBY9DQDZEYTRBYWKBVTMVHQDASFADKJPWANEVYWNOGTOIAEXWV9AYAJUMKWFJOIQFTAKOQLUVATDZRNARQJJ9FISC9YUAQVCHWUTWRGACHSPNDCPIXQ9BEELZXRWJLYGAVOTGNAWFAJBLBOHNTM9QVIJWTOERGSDGIAKPVPSAHERWNSYCFAGGVCZOMVNAHPYEOAJGDRMIQVLKTXMZLDWRWBJRGHYIVFDHAVULGEXRVSTXCSPBGRDHJJ9WEGGTMGQVZKTERLBQPYRFZFHMYRVZGGPQBKVTUWPMRWUIBJBJRYWHKTWGYIKXGCBAQOOLKCUQJKZNYXBWQCCVOMIZWWGHZB9ADKLACOOLEKERJDIIJJCUBKGAIGE9MAZESGBYEROTKOEVLCLQMMIQGSLLCNDRGQWQUCKXMHWUZXDLBZXHGO9NYE9JITIWDQRHTQJXDXTPK9BNGTYDPDHQPOE9ERBFCPIVOYVDTUDQJHUXPZHAKUXIK9KNWAKZGZPWPYGAGENIHKGZJNQLBZLRLASXQIDT9KQHOZOTWVTROLRGRVDWWTZGWGZCPGMSFTA9OYEINQEDY9SUWWYWYHYMLPWVXILBIGCRYBSBPKVAISEGAUPUWA99SFDMEQSWTUTNJIII9EQJKVWWOCLMRQPHGUVPPMRFQCFAQFOPBIYCGNTTVIDJGKARYKQWSLXJ9VZXGVLWMGLOZMLLGLLPXGIEBVVCEKUMRFZFRTTWY9POLQWVKODXOXCVZMZZEGBNHTENOJHGQFQQLQIULVOLAMYQOQ9LXPTFXOAWNFKZBDNZUCURDXLTHDMQUGXF9MPYLHHWCTOKIMURGQBMFCFNQMHPHK9BDDIZQSEMZLLVCXNWRZPTAYMCFDH9TKEPRIUHGECEMVHGSUXT9HSRCHCVWWSBBLFIHVXWGBGMDNLOQDXJWKL9BBSRKQRAEOOCZFOJOXWXSOWSSQEPHRPJ9NQDIHWKUAQIJ9X9FYUJ9CVLSYSMJNGVSM9JBOGABKO9KV9XACLO9MQGDUXMMFO9MVYEZGCPJUWKNCVBYYIL9RLOKUEFYWNQBHVBDQ9MVUIWT9V9WVZLIXVFFYUQUXSMIGOVTDTQPCOILNGLRIJSQDGDLCRAZVQ99IK9H9UYVVWQNEXMIFCI9WKWVFJZWPPPVADKHDBQGKGMDMMZKHSISS9TLER99KXWQXMBYYYJWOR9FSCWB9NOACJVIQXYDGRQWF9FGNP9ZLKWRYJTHLWKHRWWKFJT9TX9LSLAMYIEZBLPHGLEDTPAY9WLVKAUPRZRBGVRXMRDA9UIJLVSDTMPCASQLCADSOOCETCHKROTKDF9TWIHKOJZZNKPF9UDXAPRJZGWTQHAMRWFKYAFUFGVHYFNCGNFIMNY9PYWJFGICDZAE9BWUIPFIWIWSOFHRKXXSFHCLLAURDU9ADDBERREPCEMTFKHRSBUMY9EWZSYIIBSBRRXWBPGVCRPAAO9ZAPVPDJJKNOXKIDEJORMGFSTZJCOCIEXOBU9UWGJWEVYVGHUYCTBFGAXHNVHAJICNJKMJXJICTIVWXWFBCUBPZZDLPOWLABFVSOSZILKYOO9YRV9REJAUW9FTXYLGVJFCLOJGLUXZFRUHYCXWZWUZZQLESCQAVPGOBXPNXBWEQFYNCVGBEKAQUJFCIZACCZASHHDAGHGGOMJDEOEIHODVWUZMN9ADLBIR9VDQAAQJNGOZK9YEPY", 91 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 92 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 93 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 94 | }, { 95 | "address": "VEIHWDYXSJSEUGJFKJTZDKDSVXTNDJUVOGNNUBUHRAMUWFKVLFMYGHBTTMHGCKEYWRNGKCNXFYPOLKJHA", 96 | "value": 0, 97 | "tag": "999999999999999999999999999", 98 | "timestamp": 1503255453, 99 | "currentIndex": 2, 100 | "lastIndex": 4, 101 | "bundle": "IRUFOGFPN9DIQXSCQKIIXXWEBLFAXHGSRTHCHTTTIASIOJMRCN9FHHPTVGGPXIUWJJEBBOER9UJYDBRW9", 102 | "signatureMessageFragment": "HFNSLZEDFOWKICNWEUJDWMVPMBIESRKBFVZ9RMCRZGIPRUQOHQOLNTCVTZNPHYXXNGNUNHYIPMQPBQTWCIJ9EEU9PXWIHOE9JUIEMKIVZIVWEPVXTXIM9NIVZIIAJNBNBWPXVXTXEQPOKJAFTZQYXXVGCF9WIXISTZQXBUNPOQVTPXYRFDQKZCNYYVHIHUDGUHPGKPIXFOYLHIWUCAHQAFNLZQINVPVYUPWEAGZCWFLSANDOBMXVIM9GNZNPXAHNKEGT9KPCUXDMONZLDDNSIYRZNIMVSOQRLHLGRGK9BQQJTKOREUJSNWXVNNGGPY9BPIWCQQNVOPFCIUNPTQUGDLCHBWIVWEQFURWQ9CHNSTFYXZTCRHOBTMRFJENKYAOEOYIOUJNVJVUAWUTOK9TKBNGAKKWPSLIDFBOYKJMIGMPCYXYEXCCKOCXUFGIJVOZLOYRJTIIXISNNXWXLXZSOUSCZDDLSAKLDXXSIYYVTMQUUFE9BQPBHTPNGVWU9OI99ZCZABQ9MIFKJDAHDDPFUQAOA9HXTYPGITXVFLCLBDQLQBDMKS99XJKWBRQIQIRYUNMUEANPEEZKEMDHMYBWVIZJNRKGLIGZAFERBKLQ9BSPIAHFSIVCYWTNRYBWZVRN9CRCEEKHZVOUCOBTVCUNUFM9LUPLLBGXNYNBFO999NVEWMXUUCVSFMZMUSAKPKUGQHKXVQZIJBGDB9BBBLHPJOTIGDKTPYFXGCHHSPWXBDBHOXRSFHOYOSVLKADWLVZGFPBDXOKMOEFTMBSTT9UXMJENFDSFEJWLDKCSJVLCQZASBWPCTKRABLZDVXNKKCKTGOGRT9PARZHPSURBWKPDBBWANVNPRXUT9TPCLWYKYIISNDMOVY9SMUYVOKWZFWFZIKPTOULNBPKLBFKZUUFPFSCBWZGAQJFRTE9FEQZELNQBTKKLOPGBBVCOPXTPZBORTNNPTEZGWMKRWCLYVDMTSDFWIIJOQVSXCC9TUDHMGVBVNYMHXRVNYGHMHNGVUCOFHIRJOKZQJEYMSURQBTSPRU9OHYCYICOLCEM9TYWYFRRFYQ9HX99MMHCTLMCVITMTLJOFBEKPRADJYARSDUBIQASEHAKNQLOFVHNORYHDGPSCVDAVPW9LCVPJUYAESHKPMJEMAIYEWRXGJNGHG9BK9LRVNLFZVD9DPSXYHYSBQOJCQXL9IUTLUXPNGXUSDEDBNZIXBQUXUTPNYUDF9T9NYYBHDDJI99PKQZNYD9WLOQBDUZZNIEIW9MRGXWDYEKLATMLGYEBWEWRWBOCBDYPYWMMKXNATH9NFYGWFKEVRGRZSQYD9XAUTNZOWXLBMLTIB9EHULHHSSQXKYSXZJVYBAOKDIDTFDIFXU9QUXMDW9DNTAUSIJYYKGBRYWGJ9ZWPPZELT9ZIKYTUBPYIJOJDMELMILVNWTPQTHHPSLMWNDNQFEDQJXBDWFI9GHOAKXLOXKZNRTCNIFNVFHVFJYDJYMMBNDKCZKWRMSORR9XDOYQKA9JVPUBZZGYIFHWIJTXHCUCQBIAIYJIJIAPMSAGFDNUWURZDVTQITHTNMNCWFPSYDEGIIVOZBQGLTZNCWOFBWALHVCESXWEZOXKKXPPWYCSVMJPRKXRBHET9PTGQZMJNRKDRQODTBXBHKQRTSVGMISIDEN9ULBUIDFVDYUZBTYQTYPDYJYLKQLQVK9DF9LRURSPYUZNXG99KKPXJUFPV9PZ9RRWDRCUOFNVDLHF9GAFRBDVBRS9GUQXDVD9KEGTKJERPJXVCQFZWGVGJOH9Q9NALITSPGREHGKNAUDDBGPKOAHLWFPLJUAKNIFDO9ENMKRNYZ9TCYYVLCJIZXHTYTFFGEGREYIDHPUOU9NDRC9GFZTBSHFCAXEFZNOIDVNODMDGGQTSICQJTNGHYFPBJRAVVXHYJRVQC9LOHPCLCOQQMUCGENVT9XZCYKNKCHHHSR9KJIYJJKPKUM9PJA9HMPHBGLBPLPWJNTCKOXTOQGXFUNW9PBQPNBDXYYWKDDGWUJAYOIKMQGWV9VTFZWFQVEUYNJLFJPOORTPMKLRZYTWPWSRSRSGMSLQYNARUVARR9ZHDHCTIPPTTEKGICQQOM9PDFBFSMYXWCUKWJWPKKCRRHWORRYTYR9CLKPKLYOEKHYCKHCMPVCDGR9KMTR9YFWWKTAQKFNLWFB", 103 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 104 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 105 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 106 | }, { 107 | "address": "VEIHWDYXSJSEUGJFKJTZDKDSVXTNDJUVOGNNUBUHRAMUWFKVLFMYGHBTTMHGCKEYWRNGKCNXFYPOLKJHA", 108 | "value": 0, 109 | "tag": "999999999999999999999999999", 110 | "timestamp": 1503255453, 111 | "currentIndex": 3, 112 | "lastIndex": 4, 113 | "bundle": "IRUFOGFPN9DIQXSCQKIIXXWEBLFAXHGSRTHCHTTTIASIOJMRCN9FHHPTVGGPXIUWJJEBBOER9UJYDBRW9", 114 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 115 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 116 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 117 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 118 | }, { 119 | "address": "VEIHWDYXSJSEUGJFKJTZDKDSVXTNDJUVOGNNUBUHRAMUWFKVLFMYGHBTTMHGCKEYWRNGKCNXFYPOLKJHA", 120 | "value": 0, 121 | "tag": "999999999999999999999999999", 122 | "timestamp": 1503255453, 123 | "currentIndex": 4, 124 | "lastIndex": 4, 125 | "bundle": "IRUFOGFPN9DIQXSCQKIIXXWEBLFAXHGSRTHCHTTTIASIOJMRCN9FHHPTVGGPXIUWJJEBBOER9UJYDBRW9", 126 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 127 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 128 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 129 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 130 | }], 131 | [{ 132 | "address": "UEBPGORCZJEKHGQDPSSDBLUFLCMPQSUMBAELPWJAOFULUHRYYPQILHEUMOQVQWJQHKW9YOEMOLOKXFUSX", 133 | "value": 300, 134 | "tag": "999999999999999999999999999", 135 | "timestamp": 1503255453, 136 | "currentIndex": 0, 137 | "lastIndex": 4, 138 | "bundle": "THMSAIAZZIFUQHFADXTPRZGMLQHLWTJAXKBYNTLGCDLVZDIFE9XAHVCPNDHJKSHYONYBCEAHIKFIZJYYA", 139 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 140 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 141 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 142 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 143 | }, { 144 | "address": "LZCKXDCQR9UMGEPWNLVAPDOHYYFXXOKNRVLCNPCZOADOXRB9KAYQEVLORAKJFEFIWP9PAUIBOELYOOLED", 145 | "value": -300, 146 | "tag": "999999999999999999999999999", 147 | "timestamp": 1503255453, 148 | "currentIndex": 1, 149 | "lastIndex": 4, 150 | "bundle": "THMSAIAZZIFUQHFADXTPRZGMLQHLWTJAXKBYNTLGCDLVZDIFE9XAHVCPNDHJKSHYONYBCEAHIKFIZJYYA", 151 | "signatureMessageFragment": "ZAQGKLHYRXINJNVUAMGGYGSIGAW9ELWSCWCFCZETDJBRAHBRGOHFAZCEFVZTXVABGWZHOHJNVKCKMEOKZCTYKYZIUGSVQNBUTFERREHEWDOAIEAXGTP9GDXF9BIJGFEVGEMF9UYVKRH9DNCECSBIMYZAGEZWRTQ9ICDWVHAKNSPIGUJCQTUXITOVEMGFQD9LFGHEHPXBXR9SNBHBIDSOIILAONGJED9YTPIGWVNLJWLCHYBEUDWNVYLLOPXKWIHBNIHUZEOLQJDNKTXDEAVCKJCIAJBI9STPDHNPDPYYHNKLZCRILYWLANNUYSAUJLP9VPEWSNHLJCQBQAJOYKHCTWTJMBYTKHYICQHTWXWRE9NDFMW9IPCKRHKPWFKDMZR9XSNBNISWEORFQPANPYQICSGCLGEWMCEQIGLAPKZ9WAPCDVO9VHREYLGBULDTGFWSDSIXTBRJFLZSPOZJIRDZJOVGPPWWUZLJQKZUGCKBITIDRBNMVMFKSWEOHDUWHLFFEZVIRDBFFIAVZSWYR9NMMILHMUZDPHUKKOXGXNKWFUEMCKNULPZMBTZCRVVRJP9KUSMXDIJHZLOJAJASNQOBXQDIHFBCA9ONDNDLAGAPUZYTQFDNMZ9BJNDHCVOGPCIPNGSCWTZWTRKGYVAWHQWSSWVBGRIEFFQBCGTRP9MAHRQFVJNDRAZEPL9UBWVZA9AAAND9DJJAXJQPFLXFVWFEDOUKWCZIYYJUFFSRZIZSXTOIGRQFFOVQFXGFLOIGNBLJTGTVNAOAFWCXUWDVLB9JXUAYYBLHBJIJDRYDLRUMRYHKNCOEJSJUBHLAKJHBJGGCKOFTMVYAWPEISRANFILNCALFOEQCFVSIFYMXJKYOLURJH9BNCFQDWGCOXECDAOORYGGPA9KAIPCCCZNSGCHCFVABGETQMS9HKNVNWDE9OQCGOEIJKWMNGNJGGGTQM9FNWUVFBHVLOXABOVKDRUOMETA9KTCZGCBEIURIYVTNCVBBZFJWZAOZLUCNHQLHDQKDPAVAFSOAVGCAFVEJMFDWGYVTQMMDWTTYRVEMFGYKZKGGIJPCASZYHOAZXVONPGLV9IXUQAGGXNTRHGGAEE9ISG9FGWFPEIJPBISQXNJCITUXRZKNIPJYWNTZVVONGQVEDBPZBWIYVNUNKE9TBQQUQCHQEBVYPGPFPRZGOCBRDLCJGC9TOJM9FINYWLVKIHCCXKOGRDWGVQZTTUE99ACQZBX9URTRZMBNHLICDSKMYMWUTLCIRWAAFYSWXTCDY9HWSIREUBMTPGDYMQDXWJWJJSAKQRXEJWDPJFGVTYYB9GILBITWEBBEHFHJOGJSHNYNNKFYJOMBIZBLYHCRIQWMY9ZXR9RQMDPYBCYQDURQFYKHYTBPLSSQLSBAMKYHQLPOGKVHAI9GICM9CAIRLDBDJKQNMJKXSGIQW9GUNKGHJHWESNGDKCO9YYJZNOHGKLVAYOBYNCPIG9AOFMUTOJ9PQXKCACXRVIQFQFROWLYGLMIBBGYJPCKZUVBXWNRVWWRBSDBO9UUNYRYYUMUZRNNSADPAFRXDSBNVLTSAHDFLGOSWKGNTYIWMJBTDUMQNQLJCFBHOLIWRAR9QUMUICRHYVYDUMYFXJMILDAXFLROCHOSRB9YNDWI9WGFNOQSFHMPUUMXNCFZYPLECZQVCEJIVOXCESQPXTQNMRAOOXSPIUQJGXMSFKBGBX9YRH9BWIJVEUBYWHAZWISEMYCFRSYRMVUZZBPRGALZKG9WANVWYMVOKXEE9EB9VHBVZCXHBPJXLMENZVPHCIZBT9RYOCYRYSDNBSAUYEV9BPBEZ9GBHPOAAVTWXNWLWYGEABBRSSUBIHTDDUDXAZ9XKMXNQXB9YGCNBDYTDD9HWZWIPFDFYEHXGP9SCQTNBZASUJTVCJFFJ9R9JTXPURLIKNJETPMS9KDLYGFZRIQIWYTXEZY9UHMPPB9AXNUJZXJOZBAXNRKNKSIUC9CJOPNWEHAVTJMHRRLYFCYMHVETLZLIXIYWUDKHGF9PFMUU9DHUW9ERKFNQXMDZHRRAHXTBGRMXYAXYEQBCNPCZYFQIIIDPXEZSBGNKCIPMFFZVZTCHHIZSQLFUABVAYYERVILSDEIWNKFCAEQOWDNZ9HTUTVJRCRWMMXNSAJCV9PLITAKVOBNRKSPAAXLXDCTNZZYD", 152 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 153 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 154 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 155 | }, { 156 | "address": "LZCKXDCQR9UMGEPWNLVAPDOHYYFXXOKNRVLCNPCZOADOXRB9KAYQEVLORAKJFEFIWP9PAUIBOELYOOLED", 157 | "value": 0, 158 | "tag": "999999999999999999999999999", 159 | "timestamp": 1503255453, 160 | "currentIndex": 2, 161 | "lastIndex": 4, 162 | "bundle": "THMSAIAZZIFUQHFADXTPRZGMLQHLWTJAXKBYNTLGCDLVZDIFE9XAHVCPNDHJKSHYONYBCEAHIKFIZJYYA", 163 | "signatureMessageFragment": "CHYEYPITIVGTZRBZGWSUAX9C9FUMP9UEPMQLYKAFLWOLRCJEP9WRIO9XVNQBKNXVYOXYWPTUHRZORLNTXCTXBFESSTDOPQBFMODCDJCGCOJDNNBIOILUAAICWAOGGNNKFQCHSGQJUZVRCMHABOLUSUPINWCYU9HVRDHUAETQTPBNQRHGOZO9GOFXOCYQXAPFDVPUHYYVVZFZCMNBXAXHOXLPGBQXCV9SCLPOUO9RIPWGMHTAZHCHCBGHHWSJSETETRFWMJEKMECRKDOERXF9JWUDICADGYFWB9FOFSUQZPAAQGBOO9VYWQTNBOADXGKMIKGDNWBELMSICXBZISREJMUPNCQEECPZUAYSURQOTOG9SBJBGTEXXQDXCLWLOAUIQCL9HOVYIKWVUSFOMEWBBMMPQQWCUHGWJSJOEELMQKGXNKT9DYWPAMDYCGSXQRQZPIRJNOBCKKLAABAAQTYINPLRAQOUKYKHXPF9PYOBSPBMXEGTE9ANUXXYXKTAA9KDPISIRTXKCFAQMPXIGHZMDVGKTNBKDOUPHA9JMZZPLVZJHBXYFKWUSH9PDJWGLUVZAXGD9TMUXTCKFDGYTEOS9FHNFTDFMRSTAGXT9GDMSETMNDGGHXNO9VERLJFAIBPYAFSDLRABPEAQXNBTDWVHCTILXTZYYLRNPFLKPKETUJYVEKJBBUQNHKUOMLKPCMUMAIKRCVGSOAHEDGJEW9OSII9CWVVKHLEWBXGHRKMUYTBZIPMXJACOVUMQNDKMMGPRGRGZKNKTQSVLHGS9NZNAJLODWKSK9PCOUHKYJPUMMACNWYHWJDFHZOJKZUVNYNUQVMXTVJMDPUCDAVYJPJUWLAFSKA9FPDSWSMCWLVYEWFAJTCOIDBUJZWUCIPCPOUQOUEBPHCRNQIDKMLLLUTMNCDGUCOXCAMPGGFEYAGBLHTBMXISBIGWGCFZOTIZJMLNFHDOTH9FAKYWZOWLQQNBJMLPCKZOWZEDU9LYOQZELLMSBLIZZ9AEHCJSKAMUECETKMPASRBHISYNTYPLTGSEYHQRIJILLWNRV9NBBE9QQT9AZNYWQQUCGMDAOGBUJKJXFDZZXNKZDXB9PVJGBQSSBTTGAQM9LSVPUJMMRDVQISSSVVXYT9XGHDXGLULPEWULMKWIVONNBQBSF9RISO9WPEIVSRJZZW9BHVEOAYOP9DUPCUGRPCAECBBHQGIFAUMAPCJ9Z9OTANUIVSYDJSGHPAFNZVGCETBUWZIILBHAWSLJT9PAZGRRIHDDGOQGHIGELNOZZEFQEKGWNDUFCLSJICPWMBXCBVZSQQOWHVCWHJZOUMLCJBOZWZBVKOLXWYXIUMRRDAVJF9HEVPWIJPGYIMUVISBYHF9FNDBMTWAVPKVBTTCOPJKLW9IZNPXJLETHQJHNSFSVJNVTEBWXMQEZHCHQKWTZIKQIQWLEHWBZIUZ9TWCXDKWXVBCUCSMGIHROJO9OTXGQ9RXSHPRYYDWVTFBOYFHVDSDRKPSZEKABOX9KWCUVJFBYCAXRBAWFCCLESOVZOWSDQMXNEZBIBNBNXLPTQQIBVCNJDSHZSADREOOAWRU9J9FLWZXXMIHELFRPBGASE9KXESYMPTOAFDIJWQOHCZRKQFJGWNGI9BGOKBEWGYFBNHFLXAWHODGSQCLRPJFV9EO9WSZUISLCMOZAJFFO99MVYKCS9HNVMCGPVGYEWCINWMVQSILIAGEGMQFULOWIJOCUZEIDWGJYCGRQLCRIUUGQMNHXTJAHSSNVXJAQVB9LHEZSGRYZDKRXZEDRSMHPFAPRHKZONDKGJGDTOSARSDLE9SDZGABFXXGFRTJDPFJIFDOSNRTYBQXYDGPQYMJXSTSDHWIUZDRW9IKUXKFBB9BZJVPMPZSKQZHXDWPLRYXLFFRLFNVGEZDGHKUYPLZHZDPDDLBXAWSNJKYGJZOWWZARPOJXXOQXHSLEGP9ISQSKACRIAKWGRRVJSMOUQYMNVYRNZZEVJINHNISPGFYRPMGWLVYLT9NKXUCECASFOWGS9DDJTUACDQQHYZRAADFEYIALOUVYRBDUKMBZUR9PBJZX9QHTXFBTCMFHIS9IQVFYZYMNJHEOLWZPVIDDXZXSXDEAMOSEGVVDPER9ZBW9REUFBBEESPDLMCMXCGSKQWYOTCVU9AOPZPRFVSTJJZAYPJXCONHW", 164 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 165 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 166 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 167 | }, { 168 | "address": "LZCKXDCQR9UMGEPWNLVAPDOHYYFXXOKNRVLCNPCZOADOXRB9KAYQEVLORAKJFEFIWP9PAUIBOELYOOLED", 169 | "value": 0, 170 | "tag": "999999999999999999999999999", 171 | "timestamp": 1503255453, 172 | "currentIndex": 3, 173 | "lastIndex": 4, 174 | "bundle": "THMSAIAZZIFUQHFADXTPRZGMLQHLWTJAXKBYNTLGCDLVZDIFE9XAHVCPNDHJKSHYONYBCEAHIKFIZJYYA", 175 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 176 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 177 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 178 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 179 | }, { 180 | "address": "LZCKXDCQR9UMGEPWNLVAPDOHYYFXXOKNRVLCNPCZOADOXRB9KAYQEVLORAKJFEFIWP9PAUIBOELYOOLED", 181 | "value": 0, 182 | "tag": "999999999999999999999999999", 183 | "timestamp": 1503255453, 184 | "currentIndex": 4, 185 | "lastIndex": 4, 186 | "bundle": "THMSAIAZZIFUQHFADXTPRZGMLQHLWTJAXKBYNTLGCDLVZDIFE9XAHVCPNDHJKSHYONYBCEAHIKFIZJYYA", 187 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 188 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 189 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 190 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 191 | }], 192 | [{ 193 | "address": "TRPSU9DSNROHLCPIXBXGDXPOLKPUOYZZBZJCEILRJNSIFZASLPKHCIDIDBRCJHASMENZMTICJMBZRANKM", 194 | "value": 10, 195 | "tag": "999999999999999999999999999", 196 | "timestamp": 1503255453, 197 | "currentIndex": 0, 198 | "lastIndex": 5, 199 | "bundle": "LORGTCUOEP9RHFAWEEWCLJEPZ9RTXORANBZSEPTFGWCNJLZBKTKTROKDKCFQQJWVGHDNBSY9RCMYHGJLC", 200 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 201 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 202 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 203 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 204 | }, { 205 | "address": "UEBPGORCZJEKHGQDPSSDBLUFLCMPQSUMBAELPWJAOFULUHRYYPQILHEUMOQVQWJQHKW9YOEMOLOKXFUSX", 206 | "value": -300, 207 | "tag": "999999999999999999999999999", 208 | "timestamp": 1503255453, 209 | "currentIndex": 1, 210 | "lastIndex": 5, 211 | "bundle": "LORGTCUOEP9RHFAWEEWCLJEPZ9RTXORANBZSEPTFGWCNJLZBKTKTROKDKCFQQJWVGHDNBSY9RCMYHGJLC", 212 | "signatureMessageFragment": "GQZJNOLQYILPQLP9CUOPJQFKOHRSXIDCPFRHOOKLAHOTKZVLLPKPNHKFZW9UCUPFVKXPRLP99KTBYGWUYIKLEVKDOPLAFSBNYKLRJMXQTVHYRCXQKEFUZIOJNEPYB9DBMV9GSLEFZ9MJ9ZKQWFIWDXBYVF9OUXNXIWAUFPDD9VBWZ9YFIBVHRJLUQQFHTHWRIWJZ9VBHNSLR9WJ9LEZPYIQ9RGK9LOBIZEYGZAGOWCJUGYCAIG9AVXSTKCYWFUYCYBO9LFSOPAJ9T9CJWQFPJAPJJHNTZZPWMERHMQMWLHJVFDRTRPRWQLAQDQFUYEKVJ9F9ANKMH9HHEQCFYSZHFRLZGTAHARU9OUABHBHHYU9RAPEEZOUEUGHTMJOWDKQNSSLPHPKMQEZDKDGJSTECAKRPJENJ9YTKBPABFK9VPST9WZVBCE9WRCTOYDFASEEJPUQFQQAGSCR9LVRZIOQLKLCZOIFLYSA9QKUPEBOEIJNCEVHJSQECAFB9XREBRSFNZDMADGYDGGYMNJBESPWMTUZQDLARKJUILVTXWGVKMJIHOEWQBHREGECAADGHYZLTXJBLREGTKEJXOVIEIHFUEBLPBDZIHLNYTEUUHGYYHPDSEOKYYV9O9RXWCOYUBXIJRDXHPABCHAJSXPZRNKUBLICBJDHMTA9JBQLPETJTJNHXUVIFWPJUGKIDBKOTQPEBAHTHHMXJIVLTELIZAMNLWLBRCHGSFCHER9NLCZFCB9BELYALQKUYKBHKNHAXBCZQYXKZLXTDCUQHZQSUXQKWPPBHMCKKXMYIVABXFT9OMYYYH9Y9VVCHBXXFXNMYQKXFSXLHWRB9LHFASYVZNABFVVEFDKUCQJOYVNJJ9MVKJYRSQXHOAZPNIZJIVDWSSDSJBTXZJRRRJBWWTYXJUHIAVEUXYONBMUSYMBFTCP9VAXD9HHSTYAYVNMBQJXDAVPLJWALVV9XZQWMWQJWKEYIBISLZYDKOUWXGCEWTUCBG9CHQBMKFQNGBYTCXWUGGMCZSEQQDEUDLF9VVSEYMAIHKLLBEGWWDCVTGOJRHVHPZQXFAFXYVYR9DMQD9UYAPPWILWJCWBD9TXNREIWQKQYWYHUNAH9WSUMZENQZTATAUAGTZ9ZHCICIGWTWLGUSABCUHOOMXDPXCLFLNGVRNVGSUDVQVHDXYDZTUJEQ9DJKJTWG9XFC9CGSOIV9NFENSIYBNFDEFPWTKL9LKQUSYNTHYIFHTRJNGBJXDIHEBJZRCRTSOQGUQENLOATQWGGNESHNNZUQC9COMFLNYJDKBZGPNCNNQPDKZWWEBCTEQMEDL9AJXE9DKPOUEMTPQMVNSCJZWMCUGZ9CPGEZWFMJZQDRWJDVSMDWVGSHHXKCXXVRQBGUIEIXWDPYDDAAOCMIHAVYBTHQAXQYKMQF9XXXOXMZJJQEOJCRCWBBVZMPAVQLVKKNNCXTNXXBHADNRXQPVRJUYOCLRMBTBHEVTLHEVRCGWRMYC9LAFYNRFNIMGQHIEPNFZYCVTASROPUDADYFNTEQRRQXFQISILYULTECZWWRIQFCNLPETSHSDGFD9LB9IHHGWHOXCTHFFNOMGZZPC9L9UIXBKVCX9ZTUICFLGIBHDCPVDWLTMEMQYZCHO9RBIGRAJFTDUPCIMIAUEIMFATPGJTIOLIE9G9WISDEYZQYPGWGQLKLBFRVSKXVFEYHQRDSERAZICADRVVFTNKIVOGCIDZTETHP9KFOSBSIOVLWL9QZ9UYVGFXHJYNVHUQXULPHJCUHMCYHHMHDEGMYZTISZVZJFVZLKQUPIPICRGUJLDBKWQQSNRGMQC9OZLNORNPLSOSMUBJHOJFGMTABURHYRLCOTOLSXTOIKMGMLLJMXWPOGDVSDDDGCWIK9VIWRKWQZ9KDJDIHOWMELGPPLMENFHZCAIZMSGKPEWUYGYOFAUIOAZAVVSIQUMSNGVGBVVFRFAHJTITQ9MIPHIZOEHQUOOGLMFUSQLDAJRCYFBBRBTQZOIEANSUFHDUCK9BOEWYLMNFVCYINBKTKRWLXKVZTWDRFHRQTJLDEPYJRJ9PHJCPFPDAPEEFIXUVS9BJCQFYRXRODFSURHAYWXGGDYCMVNJKAZECY9TUUBQXXZSR9AVSUFIBQZWLV9KOFDLHCESYDQXFBZIPDLO9QLOPKMO9PLUVPOPNQIMTQZ", 213 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 214 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 215 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 216 | }, { 217 | "address": "UEBPGORCZJEKHGQDPSSDBLUFLCMPQSUMBAELPWJAOFULUHRYYPQILHEUMOQVQWJQHKW9YOEMOLOKXFUSX", 218 | "value": 0, 219 | "tag": "999999999999999999999999999", 220 | "timestamp": 1503255453, 221 | "currentIndex": 2, 222 | "lastIndex": 5, 223 | "bundle": "LORGTCUOEP9RHFAWEEWCLJEPZ9RTXORANBZSEPTFGWCNJLZBKTKTROKDKCFQQJWVGHDNBSY9RCMYHGJLC", 224 | "signatureMessageFragment": "FINWHXNYPAHPCTRVOBQBSHRVTLLJPBHHOG9PHRNDTYOGY9HWAYOSXDNFOFCTJVCQHDDUGEIEVQBJZPZYCKSMBTIOFBV9JP9BWD9JCBEQTRRMQSIOM9OVNOWQJ9PGHJOIDWBOEWENGKZYVKMWRXUYSTHIDZDMFZUNEWZDVITGVHSSRKDTRIDOSFKMBPJJPTGSHGWLTBKOTESJWMJAZHCRBOTEZLHGXDBYPCVMQZTIERMVPJNYAFCBGJLJRZEWHAKECAACGM9LSQICKADEPXRPEVQBCIEEKPZWCHWPKETNSUCIYWPNHIXQFCTJAYBEROCCVFV9ZPQTMAATWMGSNLDCVUN9P9GSFRWBSE9A9MUPPSFDVOJSASGAWIGQWCEWRZSBOQPBFJSNDRXSSPPCOKFY9BSISDVXCZCIQHHMUICCSMPRRXUHCJQIMGTOJJPTJEQNXBZQTNNIN9NZTZLZPWGNNOQDWJMYNTRT9UKS9DZEQVJKFSOMNDAARAPDZVZTPKYXRKZFQCUMZNPEAJLSTGGALASZBYCEOYFSLNUVIT9RFKCJTIOMXQXNTOWBKQ9VLPTLVJSORDIO99FJXPJJSYSEXLEACSLNIZFBPHBQTLYYJYAWJJGYBLCBDMQPVSQYUUIREMWKYTQYVVMHZFCUOFDGKSRVABHOXPYJWLABOSLEOKISSMUDOMCOCQHMUTRVGPJGRKYJHAKCZBTOQKDFPORYA9CU9XFDHOWO9LPTOZFRKFDFRNJFLQLYFHSSRXDWJTDCNZSISJUEAMTZNQMOGAJPNZATWIAJXNNQMVGAUBPOV9RSTLPAUTMI9JCNWBPME9JJCVENJEXSUPVYDYVXKH9XJREXVBPES9NBLJRRACJYPJMMCEOOTBSXETVQYHCQDYFXJMSDO9SAXTVXLATFJHWTDVCWIYJNBMJ9JPKLCH9JON9GDWCKUBQHKHDGDATACK9GTTIUYDDYXOPDFUBBPKVDOODUNZFPHJCSCANBAOLO9GYOINTMXCGUFUTPTRHXXDNP9NPJFG9TOYRM9BBAIOMMWEYMBUZQCUGPCIFTNTQWRQNQPVQKNIBZPQGELTA9IAVGLJXAEWZVRBMTGOXTIANJVECVH9QCSXZUTZOBDDBMCXCERWZKI9UOTGLFIZLXPSYPEENTAVSAWOIX9IXDCTKIZQLMIUAADWCOYPPTGDKYKCXH9UEYQJPGAKUMTWGJERZHFPJFNEVPHLNSBBXRLRNT9BY9KYMKQAFFJONNWHLUT9QBV9COCSGRUOHZFPK9EIVZNDVCWSXFWQLPCBWAMXEKFVUQCTDONGAEBPWXZLHKHUJJHYVCRZMQCGORICOBQHCXSDNHEXJVKKZA9GYPHDBXDF9CSOYUPLD9DNJRMXTMMFLQOCQQTJQLLBCEOPZQDFLGEPRCHNMTNSKWJBUINLJTQAAOWBNJMOS9WDGXACLSO9JPDI9OPXLTQPVJCYGLOTYTUEFQXATMKQVIFYDVSYWQVHOJHNA9W9YPGVUFVG9NSREAIHBZITNMFNBGASAFHIOAXVWLIDBOMHUMXULEJQYOHBHKDQSYKJMXMWOFHYJGHFMR9QQOSJIIXGIQHOPBFAMTEOZIWPN9TLIMNGTDCPQ9UUCM9LPQKMMKOSGANYFCWIEPYNXHMNNNRGHRRYLNYLDIGUXXQHJNETF9GOGZVUEHYYKFUONHIKWMNBDAZZJHWT99FECCXQDNCQPHDXM9KINKGSENZOHGLYNQDKXUOXQUHEIBA9ZIXKOQHFWUKEUZZNYUVPXKON9SWYQBZA9OKFRQMULKLSQNCCZDICVEYSWIIKEMHP9KDTHYWKCBFWJWR9CCNWOGZMVYBTMMBGDSUCZRGGJNMRDLIGYYQJZWRZYPOQFDQF9RMNZZXWPJZ9UNBMRWUSXUFLNRESUBAAH9YVVUCPXANFDVWCMZDFKQPOAFLZAYETJYKIUO9CWNSICGKHNGFPDQIP9ATUB9NFOALYLSBASIZXMJLCMBBSITJPM9CWEAOVMATBUPWDQKCVEBCSGLAR9YRJTCIBS9HSEPUVGLELQYSRRGSRWDEVKWZKRHZOOCDSDKCYFZKOMWCNCOOZM9BYYYKSPCAOFAYZZKTUKEHXM9HLWCPETTWKDMPNIIDFCC9VALGTVSPHMKGWCTKIVDUISBHFWEUSBNIOZVCCWDQPNDRZANOV9", 225 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 226 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 227 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 228 | }, { 229 | "address": "UEBPGORCZJEKHGQDPSSDBLUFLCMPQSUMBAELPWJAOFULUHRYYPQILHEUMOQVQWJQHKW9YOEMOLOKXFUSX", 230 | "value": 0, 231 | "tag": "999999999999999999999999999", 232 | "timestamp": 1503255453, 233 | "currentIndex": 3, 234 | "lastIndex": 5, 235 | "bundle": "LORGTCUOEP9RHFAWEEWCLJEPZ9RTXORANBZSEPTFGWCNJLZBKTKTROKDKCFQQJWVGHDNBSY9RCMYHGJLC", 236 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 237 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 238 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 239 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 240 | }, { 241 | "address": "UEBPGORCZJEKHGQDPSSDBLUFLCMPQSUMBAELPWJAOFULUHRYYPQILHEUMOQVQWJQHKW9YOEMOLOKXFUSX", 242 | "value": 0, 243 | "tag": "999999999999999999999999999", 244 | "timestamp": 1503255453, 245 | "currentIndex": 4, 246 | "lastIndex": 5, 247 | "bundle": "LORGTCUOEP9RHFAWEEWCLJEPZ9RTXORANBZSEPTFGWCNJLZBKTKTROKDKCFQQJWVGHDNBSY9RCMYHGJLC", 248 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 249 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 250 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 251 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 252 | }, { 253 | "address": "UUIWPRBYMJDXAWEIQLPGGSBAN9MVXTWTVSXC9IPXISSBYOUSFJDFRWUNAVBBEHFJWIFYHCXJIFTBZTYSW", 254 | "value": 290, 255 | "tag": "999999999999999999999999999", 256 | "timestamp": 1503255453, 257 | "currentIndex": 5, 258 | "lastIndex": 5, 259 | "bundle": "LORGTCUOEP9RHFAWEEWCLJEPZ9RTXORANBZSEPTFGWCNJLZBKTKTROKDKCFQQJWVGHDNBSY9RCMYHGJLC", 260 | "signatureMessageFragment": "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 261 | "trunkTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 262 | "branchTransaction": "999999999999999999999999999999999999999999999999999999999999999999999999999999999", 263 | "nonce": "999999999999999999999999999999999999999999999999999999999999999999999999999999999" 264 | }] 265 | ], 266 | root: { 267 | address: "LWHATLLT9TAFPWZVRNPWLXJLUDXPHQYIGMRQALRAAF9OIEIUKHMTYXLGSJGZBFAVELQFPQ9SGZWEIQHZX", 268 | bundles: [], 269 | children: [ 270 | { 271 | address: "VEIHWDYXSJSEUGJFKJTZDKDSVXTNDJUVOGNNUBUHRAMUWFKVLFMYGHBTTMHGCKEYWRNGKCNXFYPOLKJHA", 272 | bundles: [], 273 | children: [ 274 | { 275 | address: "LZCKXDCQR9UMGEPWNLVAPDOHYYFXXOKNRVLCNPCZOADOXRB9KAYQEVLORAKJFEFIWP9PAUIBOELYOOLED", 276 | bundles: [], 277 | children: [ 278 | { 279 | address: "UEBPGORCZJEKHGQDPSSDBLUFLCMPQSUMBAELPWJAOFULUHRYYPQILHEUMOQVQWJQHKW9YOEMOLOKXFUSX", 280 | bundles: [], 281 | children: [ 282 | { 283 | } 284 | ] 285 | } 286 | ] 287 | } 288 | ] 289 | } 290 | ] 291 | }, 292 | remainder: { address: "UUIWPRBYMJDXAWEIQLPGGSBAN9MVXTWTVSXC9IPXISSBYOUSFJDFRWUNAVBBEHFJWIFYHCXJIFTBZTYSW" }, 293 | history: [], 294 | expected: [ 295 | { 296 | address: "TRPSU9DSNROHLCPIXBXGDXPOLKPUOYZZBZJCEILRJNSIFZASLPKHCIDIDBRCJHASMENZMTICJMBZRANKM", 297 | value: 10, 298 | }, 299 | { 300 | address: "UUIWPRBYMJDXAWEIQLPGGSBAN9MVXTWTVSXC9IPXISSBYOUSFJDFRWUNAVBBEHFJWIFYHCXJIFTBZTYSW", 301 | value: 290, 302 | } 303 | ] 304 | }]; 305 | 306 | tests.forEach(function(test){ 307 | 308 | it('Should produce valid hash: ' + test.expected, function() { 309 | var diff = transfer.getDiff(test.root, test.remainder, test.history, test.bundles); 310 | assert.deepEqual(test.expected, diff); 311 | }); 312 | 313 | }); 314 | 315 | }); 316 | -------------------------------------------------------------------------------- /libs/serve.js: -------------------------------------------------------------------------------- 1 | //const queryString = require("query-string") 2 | const parseurl = require("parseurl") 3 | 4 | module.exports = (req, res) => { 5 | // Get url info 6 | // Check is there is a hash 7 | //if (!queries.key) return res.send(`

Please attach a key :)

`) 8 | 9 | // Put authChecker in here 10 | //if (false) return res.send(`

Invalid Key

`) 11 | 12 | var options = { 13 | root: __dirname + "/../public" 14 | } 15 | // Respond with the file 16 | return res.sendFile(parseurl(req).pathname, options, function(err) { 17 | // Throw if file doesn't exist 18 | if (err) { 19 | res.send(`

File not found

`) 20 | } 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /libs/storage.js: -------------------------------------------------------------------------------- 1 | const redis = require('redis'); 2 | const client = redis.createClient({ 3 | url: 'redis://redis-12727.c1.us-west-2-2.ec2.cloud.redislabs.com', 4 | port: '12727', 5 | password: '***********', 6 | }); 7 | 8 | function get(key, callback) { 9 | client.get(key, (err, res) => { 10 | callback(err, JSON.parse(res)); 11 | }); 12 | } 13 | 14 | function set(key, obj, callback) { 15 | client.set(key, JSON.stringify(obj), (err, res) => { 16 | callback(err, res); 17 | }); 18 | } 19 | 20 | function incr(key, callback) { 21 | client.incr(key, (err, res) => { 22 | callback(err, res); 23 | }); 24 | } 25 | 26 | module.exports = { 27 | 'get': get, 28 | 'set': set, 29 | 'incr': incr 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "index.js", 3 | "scripts": { 4 | "start": "node static/index.js" 5 | }, 6 | "dependencies": { 7 | "body-parser": "^1.17.2", 8 | "cors": "^2.8.4", 9 | "express": "^4.15.4", 10 | "inliner": "^1.13.1", 11 | "iota.crypto.js": "github:l3wi/iota.crypto.js.git", 12 | "iota.flash.js": "github:l3wi/iota.flash.js.git", 13 | "iota.lib.js": "^0.4.1", 14 | "micro": "^8.0.2", 15 | "parseurl": "^1.3.1", 16 | "query-string": "^5.0.0", 17 | "redis": "^2.8.0", 18 | "serialport": "^6.0.4", 19 | "shortid": "^2.2.8" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /static/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const bodyParser = require("body-parser") 3 | const storage = require("../libs/storage") 4 | const Flash = require("../libs/iota.flash.js") 5 | const multisig = Flash.multisig 6 | const channel = require("../libs/channel") 7 | const cors = require("cors") 8 | const crypto = require("crypto") 9 | var Inliner = require('inliner') 10 | var serialport = require('serialport'); 11 | 12 | const app = express() 13 | app.use(cors()) 14 | app.use(bodyParser.json()) 15 | app.use(bodyParser.urlencoded({ extended: true })) 16 | var Serial = {} 17 | 18 | serialport.list(function (err, ports) { 19 | var port = ports.find(p=>p.comName.includes('/dev/tty.usbmodem')) 20 | if(port){ 21 | console.log('found teensy ' + port.comName) 22 | Serial = new serialport(port.comName, 9600) 23 | Serial.on('data', unChunk) 24 | } else { 25 | console.log('no nRF52832 found') 26 | } 27 | }) 28 | 29 | function str2ab(str) { 30 | var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char 31 | var bufView = new Uint16Array(buf); 32 | for (var i=0, strLen=str.length; i')){ 43 | const cmd = text.substr(3,15).replace(/\s/g, '') 44 | packets = [] 45 | } else if (text.includes('<^>')){ 46 | const cmd = text.substr(3,15).replace(/\s/g, '') 47 | var s = '' 48 | packets.forEach(function(p){ 49 | s += p.substr(0,18) 50 | }) 51 | BleActions[cmd](s) 52 | packets = [] 53 | } else { 54 | packets.push(text) 55 | } 56 | } 57 | 58 | function serialWrite(data){ 59 | return new Promise(function(resolve,reject){ 60 | let wait = setTimeout(() => { 61 | clearTimeout(wait) 62 | Serial.write(data, function(err) { 63 | console.log('Send BLE', data) 64 | if (err) { 65 | reject('Error on write: ', err.message) 66 | } 67 | resolve('message written') 68 | }) 69 | }, 100) // todo: remove this delay, use value request instead 70 | }) 71 | } 72 | 73 | function BleAPI(cmd, data) { 74 | console.log(`/${cmd} ${data.length}`) 75 | const num = Math.ceil(data.length / 18) 76 | const packets = [] 77 | packets.push(`<*>${cmd}<*>${num}`) 78 | for(var i=0; i${cmd}`) 82 | console.log("==========PUSH BACK TO TEENSY==========") 83 | packets.reduce((prev, val) => { 84 | return prev.then(() => serialWrite(val + '\r\n')) 85 | }, Promise.resolve()) 86 | } 87 | 88 | const BleActions = { 89 | web:function(s){ 90 | // sending "testing" right back to teensy 91 | //BleAPI('web', s.replace(/ /g,'')) 92 | new Inliner(s.replace(/ /g,''), (error, html) => { 93 | BleAPI('web', html) 94 | //const byteArray = chunk(s) 95 | //console.log(byteArray) 96 | }) 97 | } 98 | } 99 | 100 | app.get('/test', (req, res, next) => { 101 | console.log('/test!') 102 | return "hi" 103 | }) 104 | 105 | app.post('/fognetdemo', (req, res, next) => { 106 | console.log('/fognetdemo ', req.body.url) 107 | 108 | new Inliner(req.body.url, (error, html) => { 109 | // compressed and inlined HTML page 110 | return res.json({ 111 | html: html 112 | }) 113 | }) 114 | }) 115 | 116 | const SEED = 117 | "DDVZVZ9QJPUGMDAKGPTEUBOS9AWWVWF99MCKNIXALMKJRBGSQMXOVBRKHSJNOVMBZJRRRMVNXJCKPXPXJ" 118 | 119 | app.post("/register", (req, res, next) => { 120 | console.log('/register ', req.body.id) 121 | storage.get("channel_" + req.body.id, (err, state) => { 122 | if (state) { 123 | return res.json({ error: "Channel already exists" }) 124 | } 125 | channel.getSubseed(SEED, (err, seed) => { 126 | if (err) { 127 | return res.send(500).json({ error: "Internal server error" }) 128 | } 129 | const digests = req.body.digests 130 | flash = { 131 | index: 0, 132 | security: 2, 133 | deposit: [req.body.amount, 0], 134 | outputs: {}, 135 | transfers: [], 136 | signersCount: 2 137 | } 138 | let myDigests = digests.map(() => 139 | multisig.getDigest(seed, flash.index++, flash.security) 140 | ) 141 | { 142 | // compose multisigs, write to remainderAddress and root 143 | let multisigs = digests.map((digest, i) => { 144 | let addy = multisig.composeAddress([digest, myDigests[i]]) 145 | addy.index = myDigests[i].index 146 | addy.security = 2 147 | addy.signingIndex = 2 148 | addy.securitySum = 4 149 | return addy 150 | }) 151 | flash.remainderAddress = multisigs.shift() 152 | for (let i = 1; i < multisigs.length; i++) { 153 | multisigs[i - 1].children.push(multisigs[i]) 154 | } 155 | flash.root = multisigs.shift() 156 | } 157 | storage.set( 158 | "channel_" + req.body.id, 159 | { 160 | seed: seed, 161 | flash: flash 162 | }, 163 | err => { 164 | if (err) { 165 | return res.send(500).end() 166 | } 167 | return res.json({ 168 | digests: myDigests 169 | }) 170 | } 171 | ) 172 | }) 173 | }) 174 | }) 175 | 176 | app.post("/branch", (req, res, next) => { 177 | console.log('/branch ', req.body.id) 178 | storage.get("channel_" + req.body.id, (err, state) => { 179 | if (!state) { 180 | return res.status(404).json({ error: "Channel not registered" }) 181 | } 182 | const clientDigests = req.body.digests 183 | let myDigests = clientDigests.map(() => 184 | multisig.getDigest(state.seed, state.flash.index++, state.flash.security) 185 | ) 186 | { 187 | // compose multisigs, write to remainderAddress and root 188 | let multisigs = clientDigests.map((digest, i) => { 189 | let addy = multisig.composeAddress([digest, myDigests[i]]) 190 | addy.index = myDigests[i].index 191 | addy.security = 2 192 | addy.signingIndex = 2 193 | addy.securitySum = 4 194 | return addy 195 | }) 196 | for (let i = 1; i < multisigs.length; i++) { 197 | multisigs[i - 1].children.push(multisigs[i]) 198 | } 199 | let node = state.flash.root 200 | while (node.address != req.body.address) { 201 | node = node.children[node.children.length - 1] 202 | } 203 | node.children.push(multisigs[0]) 204 | } 205 | storage.set("channel_" + req.body.id, state, err => { 206 | if (err) { 207 | return res.send(500).end() 208 | } 209 | return res.json({ 210 | digests: myDigests 211 | }) 212 | }) 213 | }) 214 | }) 215 | 216 | app.post("/address", (req, res, next) => { 217 | console.log('/address ', req.body.id) 218 | const clientDigest = req.body.digest 219 | const digest = channel.getNewDigest(req.body.id, (err, digest) => { 220 | if (err) { 221 | return res.status(404).json({ error: "Unknown channel" }) 222 | } 223 | return res.json({ 224 | address: channel.getAddress([clientDigest, digest]) 225 | }) 226 | }) 227 | }) 228 | 229 | app.post("/purchase", (req, res, next) => { 230 | console.log('/purchase', req.body.id) 231 | const bundles = req.body.bundles 232 | channel.processTransfer(req.body.id, bundles, (err, signatures) => { 233 | if (err) { 234 | return res.status(404).json({ error: "Unknown channel" }) 235 | } 236 | if (!signatures) { 237 | return res.status(403).json({ error: "Invalid transfer" }) 238 | } 239 | const key = crypto.randomBytes(50).toString("hex") 240 | return res.json({ bundles: signatures }) 241 | }) 242 | }) 243 | 244 | app.post("/close", (req, res, next) => { 245 | console.log('/close ', req.body.id) 246 | const bundles = req.body.bundles 247 | channel.processTransfer( 248 | req.body.id, 249 | { value: 0 }, 250 | bundles, 251 | (err, signatures) => { 252 | if (err) { 253 | return res.status(404).json({ error: "Unknown channel" }) 254 | } 255 | if (!signatures) { 256 | return res.status(403).json({ error: "Invalid transfer" }) 257 | } 258 | 259 | storage.set(req.body.id + "_close", signatures, err => { 260 | if (err) { 261 | return res.status(500).json({ error: "Internal server error" }) 262 | } 263 | return res.json({ bundles: signatures }) 264 | }) 265 | } 266 | ) 267 | }) 268 | 269 | app.post("/item", (req, res) => { 270 | console.log('/item ', req.body.id) 271 | /* if (req.get('Authorization') !== '') { 272 | res.status(403).end(); 273 | return; 274 | }*/ 275 | var item = { 276 | id: req.body.id, 277 | value: req.body.value 278 | } 279 | if (req.body.content) item.content = req.body.content 280 | storage.set("item_" + item.id, item, err => { 281 | if (err) { 282 | return res.status(500).end() 283 | } 284 | return res.json(item) 285 | }) 286 | }) 287 | 288 | app.get("/item/:item/:key", (req, res, next) => { 289 | storage.get(req.params.item + "_" + req.params.key, (err, exists) => { 290 | if (err) { 291 | return res.status(500).json({ error: "Internal server error" }) 292 | } 293 | if (exists !== 1) { 294 | return res.status(403).json({ error: "Unauthorized" }) 295 | } 296 | 297 | storage.get("item_" + req.params.item, (err, data) => { 298 | if (err) { 299 | return res.status(500).end() 300 | } 301 | if (data.content) return res.json(data.content) 302 | 303 | var options = { 304 | root: __dirname + "/public" 305 | } 306 | // Respond with the file 307 | return res.sendFile(req.params.item, options, function(err) { 308 | // Throw if file doesn't exist 309 | if (err) { 310 | return res.status(403).json({ error: "File not found" }) 311 | } 312 | }) 313 | }) 314 | }) 315 | }) 316 | 317 | app.listen(8081, function() { 318 | console.log("Listening on port 8081!") 319 | }) 320 | --------------------------------------------------------------------------------