Browse Source

move to new xrpl library

master
nitowa 2 years ago
parent
commit
33201638ad
9 changed files with 403 additions and 115 deletions
  1. 23
    44
      README.md
  2. 137
    4
      package-lock.json
  3. 5
    3
      package.json
  4. 4
    0
      src/util/types.ts
  5. 1
    0
      src/xrpIO/ripple-binding.ts
  6. 175
    0
      src/xrpIO/xrpl-binding.ts
  7. BIN
      test/julcMfc.gif
  8. 58
    64
      test/primitives.ts
  9. BIN
      xrpio.gif

+ 23
- 44
README.md View File

@@ -12,61 +12,40 @@ xrpio is a library that allows you to write and read arbitrary data in the rippl
12 12
 npm i xrpio
13 13
 ```
14 14
 
15
+# How it works
16
+
17
+Transactions on the ripple blockchain are allowed to carry up to 1kB of arbitrary data via the memo field. 
18
+We can use this to store data of any size by building a tree of references between these transactions that can then be reassembled by reading them back from the blockchain.
19
+In order to generate these transactions xrpio sends payments with the minimum denomination between two wallets controlled by the user. 
20
+
21
+xrpio automatically takes care of the logistics behind this technique as well as compression of the data.
22
+
23
+Highly simplified, you can visualize the process like this:
24
+
25
+![xrpio treewrite](https://i.imgur.com/G2HofSE.gif)
26
+
27
+In practice each node does of course store significantly more data.
28
+
29
+
15 30
 # Caution
16 31
 This library is in an early stage of development and **breaking changes may occur spontaneously and without regard of semantic versioning until the v1.0.0 release**.
17 32
 
18
-## <span style="color:red"> Operation on the main-net is untested and should not be used in production! </span>
33
+## <span style="color:red"> Operation on the main-net is untested and should not be used in production! If you want to deploy this library with the main-net please download the sources and modify them to your needs.</span>
19 34
 <br />
20 35
 
21 36
 # Quickstart
22 37
 ```typescript
23
-import {RippleAPI} from 'ripple-lib'
24
-import {treeRead, treeWrite} from 'xrpio'
38
+import { xrpIO } from "xrpio"
25 39
 
26
-const api = new RippleAPI({ server: "..." }) 
40
+const api = new xrpIO("wss://some_ripple_node.net:51233")
27 41
 await api.connect()
28 42
 
29
-const dataRootHash = await treeWrite(
30
-    api, 
31
-    "Arbitrary string data 123", 
32
-    { 
33
-        address: "Sender address", 
34
-        secret: "Sender private key"
35
-    }, 
36
-    "Receiver address"
37
-)
38
-
39
-const data = await treeRead(api, [dataRootHash])
40
-console.log(data) //"Arbitrary string data 123"
41
-```
43
+txHash = await api.treeWrite("arbitrary text 123", receiveWallet.address, sendWallet.secret)
44
+data = await api.treeRead([txHash])
45
+
46
+console.log(data) //"arbitrary text 123"
47
+
42 48
 
43
-# A simple ready-to-run example for the testnet
44
-```typescript
45
-import { treeRead, treeWrite, Wallet } from 'xrpio'
46
-import { RippleAPI } from 'ripple-lib'
47
-import fetch from 'node-fetch'
48
-
49
-export const makeTestnetWallet = () : Promise<Wallet> => fetch('https://faucet.altnet.rippletest.net/accounts', {
50
-    method: 'POST',
51
-    headers: {
52
-        'Accept': 'application/json',
53
-        'Content-Type': 'application/json'
54
-    },
55
-}).then((raw:any) => {
56
-    return raw.json().then(content => content.account)
57
-});
58
-
59
-(async()=>{
60
-    const api = new RippleAPI({server: 'wss://s.altnet.rippletest.net:51233'})
61
-    await api.connect()
62
-    const fromWallet = await makeTestnetWallet()
63
-    const toWallet = await makeTestnetWallet()
64
-    await new Promise((res, rej) => setTimeout(res, 10000)) //it takes a moment for the wallets to become active
65
-
66
-    const rootHash = await treeWrite(api, "test123", fromWallet, toWallet.address)
67
-    const data = await treeRead(api, [rootHash])
68
-    console.log(data)    
69
-})()
70 49
 ```
71 50
 
72 51
 # [Full documentation](https://gitea.nitowa.xyz/docs/xrpio)

+ 137
- 4
package-lock.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "xrpio",
3
-  "version": "0.0.7",
3
+  "version": "0.0.10",
4 4
   "lockfileVersion": 1,
5 5
   "requires": true,
6 6
   "dependencies": {
@@ -174,6 +174,53 @@
174 174
       "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
175 175
       "dev": true
176 176
     },
177
+    "bindings": {
178
+      "version": "1.5.0",
179
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
180
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
181
+      "requires": {
182
+        "file-uri-to-path": "1.0.0"
183
+      }
184
+    },
185
+    "bip32": {
186
+      "version": "2.0.6",
187
+      "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz",
188
+      "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==",
189
+      "requires": {
190
+        "@types/node": "10.12.18",
191
+        "bs58check": "^2.1.1",
192
+        "create-hash": "^1.2.0",
193
+        "create-hmac": "^1.1.7",
194
+        "tiny-secp256k1": "^1.1.3",
195
+        "typeforce": "^1.11.5",
196
+        "wif": "^2.0.6"
197
+      },
198
+      "dependencies": {
199
+        "@types/node": {
200
+          "version": "10.12.18",
201
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
202
+          "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ=="
203
+        }
204
+      }
205
+    },
206
+    "bip39": {
207
+      "version": "3.0.4",
208
+      "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.4.tgz",
209
+      "integrity": "sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==",
210
+      "requires": {
211
+        "@types/node": "11.11.6",
212
+        "create-hash": "^1.1.0",
213
+        "pbkdf2": "^3.0.9",
214
+        "randombytes": "^2.0.1"
215
+      },
216
+      "dependencies": {
217
+        "@types/node": {
218
+          "version": "11.11.6",
219
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz",
220
+          "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ=="
221
+        }
222
+      }
223
+    },
177 224
     "bn.js": {
178 225
       "version": "5.2.0",
179 226
       "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
@@ -290,6 +337,24 @@
290 337
         }
291 338
       }
292 339
     },
340
+    "bs58": {
341
+      "version": "4.0.1",
342
+      "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
343
+      "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=",
344
+      "requires": {
345
+        "base-x": "^3.0.2"
346
+      }
347
+    },
348
+    "bs58check": {
349
+      "version": "2.1.2",
350
+      "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz",
351
+      "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==",
352
+      "requires": {
353
+        "bs58": "^4.0.0",
354
+        "create-hash": "^1.1.0",
355
+        "safe-buffer": "^5.1.2"
356
+      }
357
+    },
293 358
     "buffer": {
294 359
       "version": "5.6.0",
295 360
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
@@ -447,7 +512,6 @@
447 512
       "version": "1.1.7",
448 513
       "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
449 514
       "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
450
-      "dev": true,
451 515
       "requires": {
452 516
         "cipher-base": "^1.0.3",
453 517
         "create-hash": "^1.1.0",
@@ -653,6 +717,11 @@
653 717
         "safe-buffer": "^5.1.1"
654 718
       }
655 719
     },
720
+    "file-uri-to-path": {
721
+      "version": "1.0.0",
722
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
723
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
724
+    },
656 725
     "fill-range": {
657 726
       "version": "7.0.1",
658 727
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -1326,6 +1395,11 @@
1326 1395
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1327 1396
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1328 1397
     },
1398
+    "nan": {
1399
+      "version": "2.15.0",
1400
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
1401
+      "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
1402
+    },
1329 1403
     "nanoid": {
1330 1404
       "version": "3.2.0",
1331 1405
       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
@@ -1445,7 +1519,6 @@
1445 1519
       "version": "3.1.1",
1446 1520
       "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
1447 1521
       "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==",
1448
-      "dev": true,
1449 1522
       "requires": {
1450 1523
         "create-hash": "^1.1.2",
1451 1524
         "create-hmac": "^1.1.4",
@@ -1504,7 +1577,6 @@
1504 1577
       "version": "2.1.0",
1505 1578
       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
1506 1579
       "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
1507
-      "dev": true,
1508 1580
       "requires": {
1509 1581
         "safe-buffer": "^5.1.0"
1510 1582
       }
@@ -1740,6 +1812,25 @@
1740 1812
         "has-flag": "^4.0.0"
1741 1813
       }
1742 1814
     },
1815
+    "tiny-secp256k1": {
1816
+      "version": "1.1.6",
1817
+      "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz",
1818
+      "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==",
1819
+      "requires": {
1820
+        "bindings": "^1.3.0",
1821
+        "bn.js": "^4.11.8",
1822
+        "create-hmac": "^1.1.7",
1823
+        "elliptic": "^6.4.0",
1824
+        "nan": "^2.13.2"
1825
+      },
1826
+      "dependencies": {
1827
+        "bn.js": {
1828
+          "version": "4.12.0",
1829
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
1830
+          "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
1831
+        }
1832
+      }
1833
+    },
1743 1834
     "to-regex-range": {
1744 1835
       "version": "5.0.1",
1745 1836
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -1831,6 +1922,11 @@
1831 1922
         "handlebars": "^4.7.7"
1832 1923
       }
1833 1924
     },
1925
+    "typeforce": {
1926
+      "version": "1.18.0",
1927
+      "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
1928
+      "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
1929
+    },
1834 1930
     "typescript": {
1835 1931
       "version": "4.5.5",
1836 1932
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
@@ -1941,6 +2037,14 @@
1941 2037
         "is-typed-array": "^1.1.7"
1942 2038
       }
1943 2039
     },
2040
+    "wif": {
2041
+      "version": "2.0.6",
2042
+      "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz",
2043
+      "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=",
2044
+      "requires": {
2045
+        "bs58check": "<3.0.0"
2046
+      }
2047
+    },
1944 2048
     "wordwrap": {
1945 2049
       "version": "1.0.0",
1946 2050
       "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
@@ -1975,6 +2079,35 @@
1975 2079
       "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz",
1976 2080
       "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A=="
1977 2081
     },
2082
+    "wtfnode": {
2083
+      "version": "0.9.1",
2084
+      "resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.9.1.tgz",
2085
+      "integrity": "sha512-Ip6C2KeQPl/F3aP1EfOnPoQk14Udd9lffpoqWDNH3Xt78svxPbv53ngtmtfI0q2Te3oTq79XKTnRNXVIn/GsPA==",
2086
+      "dev": true
2087
+    },
2088
+    "xrpl": {
2089
+      "version": "2.1.1",
2090
+      "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-2.1.1.tgz",
2091
+      "integrity": "sha512-s8qg5ll0m1WuPji+qLOO02uhk8tu12zA68vspwwabH2XH4kzzDnABxTeQQlZisjr+B1EBalmlt0yMI+Gv7fnbg==",
2092
+      "requires": {
2093
+        "bignumber.js": "^9.0.0",
2094
+        "bip32": "^2.0.6",
2095
+        "bip39": "^3.0.4",
2096
+        "https-proxy-agent": "^5.0.0",
2097
+        "lodash": "^4.17.4",
2098
+        "ripple-address-codec": "^4.2.3",
2099
+        "ripple-binary-codec": "^1.3.0",
2100
+        "ripple-keypairs": "^1.1.3",
2101
+        "ws": "^8.2.2"
2102
+      },
2103
+      "dependencies": {
2104
+        "ws": {
2105
+          "version": "8.5.0",
2106
+          "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
2107
+          "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg=="
2108
+        }
2109
+      }
2110
+    },
1978 2111
     "y18n": {
1979 2112
       "version": "5.0.8",
1980 2113
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

+ 5
- 3
package.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "xrpio",
3
-  "version": "0.0.10",
3
+  "version": "0.1.0",
4 4
   "repository": {
5 5
     "type": "git",
6 6
     "url": "https://gitea.nitowa.xyz/npm-packages/xrpio.git"
@@ -28,7 +28,8 @@
28 28
   "author": "nitowa",
29 29
   "license": "MIT",
30 30
   "dependencies": {
31
-    "ripple-lib": "^1.10.0"
31
+    "ripple-lib": "^1.10.0",
32
+    "xrpl": "^2.1.1"
32 33
   },
33 34
   "devDependencies": {
34 35
     "@types/chai": "^4.2.21",
@@ -45,6 +46,7 @@
45 46
     "typedoc": "^0.22.11",
46 47
     "typedoc-plugin-markdown": "^3.11.12",
47 48
     "typescript": "^4.5.0",
48
-    "utf8": "^3.0.0"
49
+    "utf8": "^3.0.0",
50
+    "wtfnode": "^0.9.1"
49 51
   }
50 52
 }

+ 4
- 0
src/util/types.ts View File

@@ -13,3 +13,7 @@ export type Secret = string
13 13
 export type PublicKey = string
14 14
 export type Amount = number
15 15
 export type TxHash = string
16
+export type Options = {
17
+    debug: boolean,
18
+    connectionTimeout: number
19
+}

+ 1
- 0
src/xrpIO/ripple-binding.ts View File

@@ -150,6 +150,7 @@ export const readRaw = async (api: RippleAPI, hash: string): Promise<Memo> => {
150 150
   } catch (e) {
151 151
     //    if(debug){
152 152
     console.log("READRAW ERR", e)
153
+    api.isConnected
153 154
     //    }
154 155
     throw e
155 156
   } finally {

+ 175
- 0
src/xrpIO/xrpl-binding.ts View File

@@ -0,0 +1,175 @@
1
+import { Memo, Options } from '../util/types'
2
+import { Client, Payment, TxResponse, Wallet } from 'xrpl'
3
+
4
+import * as zlib from 'zlib'
5
+import * as util from 'util'
6
+
7
+const compressB64 = async (data: string) => (await util.promisify(zlib.deflate)(Buffer.from(data, 'utf-8'))).toString('base64')
8
+const decompressB64 = async (data: string) => (await util.promisify(zlib.inflate)(Buffer.from(data, 'base64'))).toString('utf-8')
9
+const hexDecode = (str: string) => Buffer.from(str, 'hex').toString('utf8')
10
+const hexEncode = (str: string) => Buffer.from(str, 'utf8').toString('hex').toUpperCase()
11
+const chunkString = (str: string, length: number) => str.match(new RegExp('.{1,' + length + '}', 'gs'));
12
+const PAYLOAD_SIZE = 925
13
+
14
+
15
+
16
+export class xrpIO {
17
+  private api: Client
18
+
19
+  constructor(
20
+    private server: string,
21
+    private options: Options = {
22
+      debug: false,
23
+      connectionTimeout: 100000
24
+    }
25
+  ) {
26
+    this.api = new Client(server, {
27
+      connectionTimeout: this.options.connectionTimeout
28
+    })
29
+  }
30
+
31
+  public async connect(): Promise<void> {
32
+    if (!this.api.isConnected())
33
+      await this.api.connect()
34
+  }
35
+
36
+  public async disconnect(): Promise<void> {
37
+    try{
38
+      await this.api.disconnect()
39
+    }catch(e){
40
+      console.log("DISCONNECT ERROR", e)
41
+    }
42
+  }
43
+
44
+  private async cloneApi(): Promise<Client> {
45
+    let _api = new Client(this.server, {
46
+      connectionTimeout: this.options.connectionTimeout
47
+    })
48
+
49
+    while(!_api.isConnected()){
50
+      try{
51
+        await _api.connect()
52
+        return _api
53
+      }catch(e){
54
+        this.dbg('CLONEAPI ERR', 'Connection failed', String(e['message']))
55
+        await _api.disconnect()
56
+        _api = new Client(this.server, {
57
+          connectionTimeout: this.options.connectionTimeout
58
+        })
59
+      }
60
+    }
61
+  }
62
+
63
+  private async sendPayment(data: Memo, to: string, secret: string, sequence?: number): Promise<TxResponse> {
64
+    const wallet = Wallet.fromSecret(secret)
65
+    this.dbg("Sending payment", wallet.address, '->', to)
66
+
67
+    const _api = await this.cloneApi()
68
+    try {
69
+      const payment: Payment = await _api.autofill({
70
+        TransactionType: 'Payment',
71
+        Account: wallet.address,
72
+        Destination: to,
73
+        Sequence: sequence,
74
+        Amount: "1",
75
+        Memos: [{
76
+          Memo: {
77
+            MemoData: hexEncode(data.data || ""),
78
+            MemoFormat: hexEncode(data.format || ""),
79
+            MemoType: hexEncode(data.type || "")
80
+          }
81
+        }]
82
+      })
83
+
84
+
85
+      const response = await _api.submitAndWait(payment, { wallet })
86
+      await _api.disconnect()
87
+      this.dbg("Tx finalized", response.result.hash, response.result.Sequence)
88
+      return response
89
+    } catch (error: any) {
90
+      this.dbg("SENDPAYMENT ERROR", error)
91
+      await _api.disconnect()
92
+      throw error
93
+    }
94
+  }
95
+
96
+  public async writeRaw(data: Memo, to: string, secret: string, sequence?: number): Promise<string> {
97
+    this.dbg("Writing data", data)
98
+    const tx = await this.sendPayment(data, to, secret, sequence)
99
+    return tx.result.hash
100
+  }
101
+
102
+  private async getTransaction(hash: string): Promise<TxResponse> {
103
+    this.dbg("Getting Tx", hash)
104
+    let _api = await this.cloneApi()
105
+
106
+    while(true){
107
+      try{
108
+        const response = await _api.request({
109
+          command: 'tx',
110
+          transaction: hash,
111
+        })
112
+        await _api.disconnect()
113
+        return response
114
+      }catch(e){
115
+        this.dbg("Retrying to get", hash)
116
+        await _api.disconnect()
117
+        _api = await this.cloneApi()
118
+      }
119
+    }
120
+  }
121
+
122
+  public async readRaw(hash: string): Promise<Memo> {
123
+    const tx = await this.getTransaction(hash)
124
+    const memo = tx.result.Memos[0].Memo
125
+    const memoParsed = {
126
+      data: hexDecode(memo.MemoData),
127
+      format: hexDecode(memo.MemoFormat),
128
+      type: hexDecode(memo.MemoType)
129
+    }
130
+    this.dbg(hash, "data", memoParsed)
131
+    return memoParsed
132
+  }
133
+
134
+  public async treeWrite(data: string, to: string, secret: string, format: 'L' | 'N' = 'L'): Promise<string> {
135
+    const wallet = Wallet.fromSecret(secret)
136
+    data = await compressB64(data)
137
+    const chunks = chunkString(data, PAYLOAD_SIZE)
138
+    const latestSequence = await this.getAccountSequence(wallet.address)
139
+    const hashes = await Promise.all(Object.entries(chunks).map(([i, chunk]) => this.writeRaw({ data: chunk, format: format }, to, secret, latestSequence+Number(i))))
140
+
141
+    if (hashes.length === 1) {
142
+      return hashes[0]
143
+    }
144
+
145
+    return await this.treeWrite(JSON.stringify(hashes), to, secret, 'N')
146
+  }
147
+
148
+  public async treeRead(hashes: string[]): Promise<string>{
149
+    const memos = await Promise.all(hashes.map(hash => this.readRaw(hash)))
150
+    const payload: string = await decompressB64(memos.map(memo => memo.data).join(''))
151
+  
152
+    if (memos.some(memo => memo.format === 'N')) {
153
+      return await this.treeRead(JSON.parse(payload))
154
+    }
155
+  
156
+    return payload
157
+  }
158
+
159
+  public async getAccountSequence(address: string): Promise<number>{
160
+    this.dbg("Getting acc info for", address)
161
+    const accountInfo = await this.api.request({
162
+      command: 'account_info',
163
+      account: address,
164
+      strict: true,
165
+    })
166
+    this.dbg("Got account_info", accountInfo)
167
+    return Number(accountInfo.result.account_data.Sequence)
168
+  }
169
+
170
+  private dbg(...args: any[]) {
171
+    if (this.options.debug) {
172
+      console.log.apply(console, args)
173
+    }
174
+  }
175
+}

BIN
test/julcMfc.gif View File


+ 58
- 64
test/primitives.ts View File

@@ -1,18 +1,18 @@
1
-import { RippleAPI } from 'ripple-lib'
2 1
 import { htmlTxt, longText, makeTestnetWallet, TEST_CONFIG, TEST_DATA } from './CONSTANTS'
3
-import { getLatestSequence, sendPayment, treeRead, treeWrite } from '../src/xrpIO/ripple-binding'
2
+import { xrpIO } from '../src/xrpIO/xrpl-binding'
4 3
 import * as chai from 'chai';
5 4
 import { Wallet } from '../src/util/types';
6
-const utf8 = require('utf8')
7
-const base64 = require('base-64')
5
+var wtf = require('wtfnode');
8 6
 const expect = chai.expect
9 7
 
10 8
 let sendWallet: Wallet
11 9
 let receiveWallet: Wallet
12
-let api: RippleAPI
10
+let api: xrpIO
11
+
12
+const ONLY_LARGE_TESTS = true
13 13
 
14 14
 describe('XRPIO', () => {
15
-    before(async function () {
15
+    before(async function(){
16 16
         this.timeout(15000)
17 17
         sendWallet = await makeTestnetWallet()
18 18
         receiveWallet = await makeTestnetWallet()
@@ -21,7 +21,7 @@ describe('XRPIO', () => {
21 21
 
22 22
     beforeEach(async () => {
23 23
         try {
24
-            api = new RippleAPI({ server: TEST_CONFIG.rippleNode })
24
+            api = new xrpIO(TEST_CONFIG.rippleNode, {debug: false, connectionTimeout: 100000})
25 25
             return await api.connect()
26 26
         } catch (e) {
27 27
             console.log(e)
@@ -37,70 +37,64 @@ describe('XRPIO', () => {
37 37
         }
38 38
     })
39 39
 
40
-    let latestSeq
41
-    describe('plumbing', () => {
42
-        it('getLatestSequence', async () => {
43
-            latestSeq = await getLatestSequence(api, sendWallet.address)
44
-            expect(latestSeq).to.exist
45
-            expect(latestSeq).to.be.a('number')
46
-            expect(latestSeq).to.be.greaterThan(0)
47
-        })
40
+    it('getAccountSequence', async function(){
41
+//      this.skip()
42
+        this.timeout(10000)
43
+        const seq = await api.getAccountSequence(sendWallet.address)
44
+        expect(seq).to.exist
45
+        expect(seq).to.be.a('number')
48 46
     })
49 47
 
50
-    describe('payment', () => {
51
-        it('sendPayment', async function () {
52
-            this.timeout(10000)
53
-            const result = await sendPayment(api, [{ data: "123" }], sendWallet.address, receiveWallet.address, sendWallet.secret, latestSeq + 1)
54
-            expect(result).to.exist
55
-            expect(result.resultCode).to.be.equal('tesSUCCESS')
56
-        })
48
+    let txHash
49
+    it('writeRaw', async function(){
50
+        this.skip()
51
+        this.timeout(15000)
52
+        txHash = await api.writeRaw({data: TEST_DATA}, receiveWallet.address, sendWallet.secret);
53
+        expect(txHash).to.exist
54
+        expect(txHash).to.be.a('string')
57 55
     })
58 56
 
59
-    describe('I/O', () => {
60
-        let treeRoot
61
-        it(`can tree write (${TEST_DATA.length} bytes)`, async function () {
62
-            this.timeout(10000)
63
-            treeRoot = await treeWrite(api, TEST_DATA, sendWallet, receiveWallet.address)
64
-        })
65
-
66
-        it('can tree read', async function () {
67
-            this.timeout(15000)
68
-            const data = await treeRead(api, [treeRoot])
69
-            expect(data).to.be.equal(TEST_DATA)
70
-        })
71
-
72
-        it(`can tree write large (${longText.length} bytes)`, async function () {
73
-            this.timeout(30000)
74
-            treeRoot = await treeWrite(api, longText, sendWallet, receiveWallet.address)
75
-        })
76
-
77
-        it('can tree read large', async function () {
78
-            this.timeout(30000)
79
-            const data = await treeRead(api, [treeRoot])
80
-            expect(data).to.be.equal(longText)
81
-        })
57
+    it('readRaw', async function () {
58
+        this.skip()
59
+        this.timeout(15000)
60
+        const memo = await api.readRaw(txHash)
61
+        expect(memo).to.exist
62
+        expect(memo.data).to.be.equal(TEST_DATA)
63
+    })
82 64
 
83
-        it(`can tree write extra large (${htmlTxt.length} bytes)`, async function () {
84
-            this.timeout(300000)
85
-            treeRoot = await treeWrite(api, htmlTxt, sendWallet, receiveWallet.address)
86
-        })
65
+    it('treeWrite', async function(){
66
+        this.skip()
67
+        this.timeout(45000)
68
+        txHash = await api.treeWrite(longText, receiveWallet.address, sendWallet.secret)
69
+        expect(txHash).to.exist
70
+        expect(txHash).to.be.a('string')
71
+    })
87 72
 
88
-        it('can tree read extra large', async function () {
89
-            this.timeout(300000)
90
-            const data = await treeRead(api, [treeRoot])
91
-            expect(data).to.be.equal(htmlTxt)
92
-        })
73
+    it('treeRead', async function(){
74
+        this.skip()
75
+        this.timeout(45000)
76
+        txHash = await api.treeRead([txHash])
77
+        expect(txHash).to.exist
78
+        expect(txHash).to.be.equal(longText)
79
+    })
93 80
 
94
-        it("can r/w binary", async function () {
95
-            this.timeout(10000);
81
+    it('treeWrite XL', async function(){
82
+//        this.skip()
83
+        this.timeout(450000)
84
+        txHash = await api.treeWrite(htmlTxt, receiveWallet.address, sendWallet.secret)
85
+        expect(txHash).to.exist
86
+        expect(txHash).to.be.a('string')
87
+    })
96 88
 
97
-            const bytes = utf8.encode(TEST_DATA)
98
-            const encoded = base64.encode(bytes)
99
-            const txHash = await treeWrite(api, encoded, sendWallet, receiveWallet.address)
100
-            const res = await treeRead(api, [txHash])
101
-            const decoded_bytes = base64.decode(res)
102
-            const text = utf8.decode(decoded_bytes)
103
-            expect(text).to.be.equal(TEST_DATA)
104
-        })
89
+    it('treeRead XL', async function(){
90
+//        this.skip()  
91
+        this.timeout(450000)
92
+        txHash = await api.treeRead([txHash])
93
+        expect(txHash).to.exist
94
+        expect(txHash).to.be.equal(htmlTxt)
95
+    })
96
+    
97
+    it('print open handles', function(){
98
+        wtf.dump()
105 99
     })
106 100
 })

BIN
xrpio.gif View File


Loading…
Cancel
Save