move to new xrpl library
This commit is contained in:
@@ -12,61 +12,40 @@ xrpio is a library that allows you to write and read arbitrary data in the rippl
|
||||
npm i xrpio
|
||||
```
|
||||
|
||||
# How it works
|
||||
|
||||
Transactions on the ripple blockchain are allowed to carry up to 1kB of arbitrary data via the memo field.
|
||||
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.
|
||||
In order to generate these transactions xrpio sends payments with the minimum denomination between two wallets controlled by the user.
|
||||
|
||||
xrpio automatically takes care of the logistics behind this technique as well as compression of the data.
|
||||
|
||||
Highly simplified, you can visualize the process like this:
|
||||
|
||||

|
||||
|
||||
In practice each node does of course store significantly more data.
|
||||
|
||||
|
||||
# Caution
|
||||
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**.
|
||||
|
||||
## <span style="color:red"> Operation on the main-net is untested and should not be used in production! </span>
|
||||
## <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>
|
||||
<br />
|
||||
|
||||
# Quickstart
|
||||
```typescript
|
||||
import {RippleAPI} from 'ripple-lib'
|
||||
import {treeRead, treeWrite} from 'xrpio'
|
||||
import { xrpIO } from "xrpio"
|
||||
|
||||
const api = new RippleAPI({ server: "..." })
|
||||
const api = new xrpIO("wss://some_ripple_node.net:51233")
|
||||
await api.connect()
|
||||
|
||||
const dataRootHash = await treeWrite(
|
||||
api,
|
||||
"Arbitrary string data 123",
|
||||
{
|
||||
address: "Sender address",
|
||||
secret: "Sender private key"
|
||||
},
|
||||
"Receiver address"
|
||||
)
|
||||
txHash = await api.treeWrite("arbitrary text 123", receiveWallet.address, sendWallet.secret)
|
||||
data = await api.treeRead([txHash])
|
||||
|
||||
const data = await treeRead(api, [dataRootHash])
|
||||
console.log(data) //"Arbitrary string data 123"
|
||||
```
|
||||
console.log(data) //"arbitrary text 123"
|
||||
|
||||
# A simple ready-to-run example for the testnet
|
||||
```typescript
|
||||
import { treeRead, treeWrite, Wallet } from 'xrpio'
|
||||
import { RippleAPI } from 'ripple-lib'
|
||||
import fetch from 'node-fetch'
|
||||
|
||||
export const makeTestnetWallet = () : Promise<Wallet> => fetch('https://faucet.altnet.rippletest.net/accounts', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
}).then((raw:any) => {
|
||||
return raw.json().then(content => content.account)
|
||||
});
|
||||
|
||||
(async()=>{
|
||||
const api = new RippleAPI({server: 'wss://s.altnet.rippletest.net:51233'})
|
||||
await api.connect()
|
||||
const fromWallet = await makeTestnetWallet()
|
||||
const toWallet = await makeTestnetWallet()
|
||||
await new Promise((res, rej) => setTimeout(res, 10000)) //it takes a moment for the wallets to become active
|
||||
|
||||
const rootHash = await treeWrite(api, "test123", fromWallet, toWallet.address)
|
||||
const data = await treeRead(api, [rootHash])
|
||||
console.log(data)
|
||||
})()
|
||||
```
|
||||
|
||||
# [Full documentation](https://gitea.nitowa.xyz/docs/xrpio)
|
||||
|
||||
Generated
+137
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xrpio",
|
||||
"version": "0.0.7",
|
||||
"version": "0.0.10",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -174,6 +174,53 @@
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||
"dev": true
|
||||
},
|
||||
"bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"requires": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"bip32": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz",
|
||||
"integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==",
|
||||
"requires": {
|
||||
"@types/node": "10.12.18",
|
||||
"bs58check": "^2.1.1",
|
||||
"create-hash": "^1.2.0",
|
||||
"create-hmac": "^1.1.7",
|
||||
"tiny-secp256k1": "^1.1.3",
|
||||
"typeforce": "^1.11.5",
|
||||
"wif": "^2.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.12.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
|
||||
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"bip39": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.4.tgz",
|
||||
"integrity": "sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==",
|
||||
"requires": {
|
||||
"@types/node": "11.11.6",
|
||||
"create-hash": "^1.1.0",
|
||||
"pbkdf2": "^3.0.9",
|
||||
"randombytes": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "11.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz",
|
||||
"integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"bn.js": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
|
||||
@@ -290,6 +337,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"bs58": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
||||
"integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=",
|
||||
"requires": {
|
||||
"base-x": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"bs58check": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz",
|
||||
"integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==",
|
||||
"requires": {
|
||||
"bs58": "^4.0.0",
|
||||
"create-hash": "^1.1.0",
|
||||
"safe-buffer": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
|
||||
@@ -447,7 +512,6 @@
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cipher-base": "^1.0.3",
|
||||
"create-hash": "^1.1.0",
|
||||
@@ -653,6 +717,11 @@
|
||||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
@@ -1326,6 +1395,11 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
|
||||
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
|
||||
@@ -1445,7 +1519,6 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
|
||||
"integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"create-hash": "^1.1.2",
|
||||
"create-hmac": "^1.1.4",
|
||||
@@ -1504,7 +1577,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
@@ -1740,6 +1812,25 @@
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"tiny-secp256k1": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz",
|
||||
"integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==",
|
||||
"requires": {
|
||||
"bindings": "^1.3.0",
|
||||
"bn.js": "^4.11.8",
|
||||
"create-hmac": "^1.1.7",
|
||||
"elliptic": "^6.4.0",
|
||||
"nan": "^2.13.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@@ -1831,6 +1922,11 @@
|
||||
"handlebars": "^4.7.7"
|
||||
}
|
||||
},
|
||||
"typeforce": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
|
||||
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.5.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
|
||||
@@ -1941,6 +2037,14 @@
|
||||
"is-typed-array": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"wif": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz",
|
||||
"integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=",
|
||||
"requires": {
|
||||
"bs58check": "<3.0.0"
|
||||
}
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||
@@ -1975,6 +2079,35 @@
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz",
|
||||
"integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A=="
|
||||
},
|
||||
"wtfnode": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.9.1.tgz",
|
||||
"integrity": "sha512-Ip6C2KeQPl/F3aP1EfOnPoQk14Udd9lffpoqWDNH3Xt78svxPbv53ngtmtfI0q2Te3oTq79XKTnRNXVIn/GsPA==",
|
||||
"dev": true
|
||||
},
|
||||
"xrpl": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xrpl/-/xrpl-2.1.1.tgz",
|
||||
"integrity": "sha512-s8qg5ll0m1WuPji+qLOO02uhk8tu12zA68vspwwabH2XH4kzzDnABxTeQQlZisjr+B1EBalmlt0yMI+Gv7fnbg==",
|
||||
"requires": {
|
||||
"bignumber.js": "^9.0.0",
|
||||
"bip32": "^2.0.6",
|
||||
"bip39": "^3.0.4",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"ripple-address-codec": "^4.2.3",
|
||||
"ripple-binary-codec": "^1.3.0",
|
||||
"ripple-keypairs": "^1.1.3",
|
||||
"ws": "^8.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
|
||||
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
||||
+5
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xrpio",
|
||||
"version": "0.0.10",
|
||||
"version": "0.1.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitea.nitowa.xyz/npm-packages/xrpio.git"
|
||||
@@ -28,7 +28,8 @@
|
||||
"author": "nitowa",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ripple-lib": "^1.10.0"
|
||||
"ripple-lib": "^1.10.0",
|
||||
"xrpl": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.21",
|
||||
@@ -45,6 +46,7 @@
|
||||
"typedoc": "^0.22.11",
|
||||
"typedoc-plugin-markdown": "^3.11.12",
|
||||
"typescript": "^4.5.0",
|
||||
"utf8": "^3.0.0"
|
||||
"utf8": "^3.0.0",
|
||||
"wtfnode": "^0.9.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,7 @@ export type Secret = string
|
||||
export type PublicKey = string
|
||||
export type Amount = number
|
||||
export type TxHash = string
|
||||
export type Options = {
|
||||
debug: boolean,
|
||||
connectionTimeout: number
|
||||
}
|
||||
@@ -150,6 +150,7 @@ export const readRaw = async (api: RippleAPI, hash: string): Promise<Memo> => {
|
||||
} catch (e) {
|
||||
// if(debug){
|
||||
console.log("READRAW ERR", e)
|
||||
api.isConnected
|
||||
// }
|
||||
throw e
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
import { Memo, Options } from '../util/types'
|
||||
import { Client, Payment, TxResponse, Wallet } from 'xrpl'
|
||||
|
||||
import * as zlib from 'zlib'
|
||||
import * as util from 'util'
|
||||
|
||||
const compressB64 = async (data: string) => (await util.promisify(zlib.deflate)(Buffer.from(data, 'utf-8'))).toString('base64')
|
||||
const decompressB64 = async (data: string) => (await util.promisify(zlib.inflate)(Buffer.from(data, 'base64'))).toString('utf-8')
|
||||
const hexDecode = (str: string) => Buffer.from(str, 'hex').toString('utf8')
|
||||
const hexEncode = (str: string) => Buffer.from(str, 'utf8').toString('hex').toUpperCase()
|
||||
const chunkString = (str: string, length: number) => str.match(new RegExp('.{1,' + length + '}', 'gs'));
|
||||
const PAYLOAD_SIZE = 925
|
||||
|
||||
|
||||
|
||||
export class xrpIO {
|
||||
private api: Client
|
||||
|
||||
constructor(
|
||||
private server: string,
|
||||
private options: Options = {
|
||||
debug: false,
|
||||
connectionTimeout: 100000
|
||||
}
|
||||
) {
|
||||
this.api = new Client(server, {
|
||||
connectionTimeout: this.options.connectionTimeout
|
||||
})
|
||||
}
|
||||
|
||||
public async connect(): Promise<void> {
|
||||
if (!this.api.isConnected())
|
||||
await this.api.connect()
|
||||
}
|
||||
|
||||
public async disconnect(): Promise<void> {
|
||||
try{
|
||||
await this.api.disconnect()
|
||||
}catch(e){
|
||||
console.log("DISCONNECT ERROR", e)
|
||||
}
|
||||
}
|
||||
|
||||
private async cloneApi(): Promise<Client> {
|
||||
let _api = new Client(this.server, {
|
||||
connectionTimeout: this.options.connectionTimeout
|
||||
})
|
||||
|
||||
while(!_api.isConnected()){
|
||||
try{
|
||||
await _api.connect()
|
||||
return _api
|
||||
}catch(e){
|
||||
this.dbg('CLONEAPI ERR', 'Connection failed', String(e['message']))
|
||||
await _api.disconnect()
|
||||
_api = new Client(this.server, {
|
||||
connectionTimeout: this.options.connectionTimeout
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async sendPayment(data: Memo, to: string, secret: string, sequence?: number): Promise<TxResponse> {
|
||||
const wallet = Wallet.fromSecret(secret)
|
||||
this.dbg("Sending payment", wallet.address, '->', to)
|
||||
|
||||
const _api = await this.cloneApi()
|
||||
try {
|
||||
const payment: Payment = await _api.autofill({
|
||||
TransactionType: 'Payment',
|
||||
Account: wallet.address,
|
||||
Destination: to,
|
||||
Sequence: sequence,
|
||||
Amount: "1",
|
||||
Memos: [{
|
||||
Memo: {
|
||||
MemoData: hexEncode(data.data || ""),
|
||||
MemoFormat: hexEncode(data.format || ""),
|
||||
MemoType: hexEncode(data.type || "")
|
||||
}
|
||||
}]
|
||||
})
|
||||
|
||||
|
||||
const response = await _api.submitAndWait(payment, { wallet })
|
||||
await _api.disconnect()
|
||||
this.dbg("Tx finalized", response.result.hash, response.result.Sequence)
|
||||
return response
|
||||
} catch (error: any) {
|
||||
this.dbg("SENDPAYMENT ERROR", error)
|
||||
await _api.disconnect()
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public async writeRaw(data: Memo, to: string, secret: string, sequence?: number): Promise<string> {
|
||||
this.dbg("Writing data", data)
|
||||
const tx = await this.sendPayment(data, to, secret, sequence)
|
||||
return tx.result.hash
|
||||
}
|
||||
|
||||
private async getTransaction(hash: string): Promise<TxResponse> {
|
||||
this.dbg("Getting Tx", hash)
|
||||
let _api = await this.cloneApi()
|
||||
|
||||
while(true){
|
||||
try{
|
||||
const response = await _api.request({
|
||||
command: 'tx',
|
||||
transaction: hash,
|
||||
})
|
||||
await _api.disconnect()
|
||||
return response
|
||||
}catch(e){
|
||||
this.dbg("Retrying to get", hash)
|
||||
await _api.disconnect()
|
||||
_api = await this.cloneApi()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async readRaw(hash: string): Promise<Memo> {
|
||||
const tx = await this.getTransaction(hash)
|
||||
const memo = tx.result.Memos[0].Memo
|
||||
const memoParsed = {
|
||||
data: hexDecode(memo.MemoData),
|
||||
format: hexDecode(memo.MemoFormat),
|
||||
type: hexDecode(memo.MemoType)
|
||||
}
|
||||
this.dbg(hash, "data", memoParsed)
|
||||
return memoParsed
|
||||
}
|
||||
|
||||
public async treeWrite(data: string, to: string, secret: string, format: 'L' | 'N' = 'L'): Promise<string> {
|
||||
const wallet = Wallet.fromSecret(secret)
|
||||
data = await compressB64(data)
|
||||
const chunks = chunkString(data, PAYLOAD_SIZE)
|
||||
const latestSequence = await this.getAccountSequence(wallet.address)
|
||||
const hashes = await Promise.all(Object.entries(chunks).map(([i, chunk]) => this.writeRaw({ data: chunk, format: format }, to, secret, latestSequence+Number(i))))
|
||||
|
||||
if (hashes.length === 1) {
|
||||
return hashes[0]
|
||||
}
|
||||
|
||||
return await this.treeWrite(JSON.stringify(hashes), to, secret, 'N')
|
||||
}
|
||||
|
||||
public async treeRead(hashes: string[]): Promise<string>{
|
||||
const memos = await Promise.all(hashes.map(hash => this.readRaw(hash)))
|
||||
const payload: string = await decompressB64(memos.map(memo => memo.data).join(''))
|
||||
|
||||
if (memos.some(memo => memo.format === 'N')) {
|
||||
return await this.treeRead(JSON.parse(payload))
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
public async getAccountSequence(address: string): Promise<number>{
|
||||
this.dbg("Getting acc info for", address)
|
||||
const accountInfo = await this.api.request({
|
||||
command: 'account_info',
|
||||
account: address,
|
||||
strict: true,
|
||||
})
|
||||
this.dbg("Got account_info", accountInfo)
|
||||
return Number(accountInfo.result.account_data.Sequence)
|
||||
}
|
||||
|
||||
private dbg(...args: any[]) {
|
||||
if (this.options.debug) {
|
||||
console.log.apply(console, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
+58
-64
@@ -1,18 +1,18 @@
|
||||
import { RippleAPI } from 'ripple-lib'
|
||||
import { htmlTxt, longText, makeTestnetWallet, TEST_CONFIG, TEST_DATA } from './CONSTANTS'
|
||||
import { getLatestSequence, sendPayment, treeRead, treeWrite } from '../src/xrpIO/ripple-binding'
|
||||
import { xrpIO } from '../src/xrpIO/xrpl-binding'
|
||||
import * as chai from 'chai';
|
||||
import { Wallet } from '../src/util/types';
|
||||
const utf8 = require('utf8')
|
||||
const base64 = require('base-64')
|
||||
var wtf = require('wtfnode');
|
||||
const expect = chai.expect
|
||||
|
||||
let sendWallet: Wallet
|
||||
let receiveWallet: Wallet
|
||||
let api: RippleAPI
|
||||
let api: xrpIO
|
||||
|
||||
const ONLY_LARGE_TESTS = true
|
||||
|
||||
describe('XRPIO', () => {
|
||||
before(async function () {
|
||||
before(async function(){
|
||||
this.timeout(15000)
|
||||
sendWallet = await makeTestnetWallet()
|
||||
receiveWallet = await makeTestnetWallet()
|
||||
@@ -21,7 +21,7 @@ describe('XRPIO', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
api = new RippleAPI({ server: TEST_CONFIG.rippleNode })
|
||||
api = new xrpIO(TEST_CONFIG.rippleNode, {debug: false, connectionTimeout: 100000})
|
||||
return await api.connect()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
@@ -37,70 +37,64 @@ describe('XRPIO', () => {
|
||||
}
|
||||
})
|
||||
|
||||
let latestSeq
|
||||
describe('plumbing', () => {
|
||||
it('getLatestSequence', async () => {
|
||||
latestSeq = await getLatestSequence(api, sendWallet.address)
|
||||
expect(latestSeq).to.exist
|
||||
expect(latestSeq).to.be.a('number')
|
||||
expect(latestSeq).to.be.greaterThan(0)
|
||||
})
|
||||
it('getAccountSequence', async function(){
|
||||
// this.skip()
|
||||
this.timeout(10000)
|
||||
const seq = await api.getAccountSequence(sendWallet.address)
|
||||
expect(seq).to.exist
|
||||
expect(seq).to.be.a('number')
|
||||
})
|
||||
|
||||
describe('payment', () => {
|
||||
it('sendPayment', async function () {
|
||||
this.timeout(10000)
|
||||
const result = await sendPayment(api, [{ data: "123" }], sendWallet.address, receiveWallet.address, sendWallet.secret, latestSeq + 1)
|
||||
expect(result).to.exist
|
||||
expect(result.resultCode).to.be.equal('tesSUCCESS')
|
||||
})
|
||||
let txHash
|
||||
it('writeRaw', async function(){
|
||||
this.skip()
|
||||
this.timeout(15000)
|
||||
txHash = await api.writeRaw({data: TEST_DATA}, receiveWallet.address, sendWallet.secret);
|
||||
expect(txHash).to.exist
|
||||
expect(txHash).to.be.a('string')
|
||||
})
|
||||
|
||||
describe('I/O', () => {
|
||||
let treeRoot
|
||||
it(`can tree write (${TEST_DATA.length} bytes)`, async function () {
|
||||
this.timeout(10000)
|
||||
treeRoot = await treeWrite(api, TEST_DATA, sendWallet, receiveWallet.address)
|
||||
})
|
||||
it('readRaw', async function () {
|
||||
this.skip()
|
||||
this.timeout(15000)
|
||||
const memo = await api.readRaw(txHash)
|
||||
expect(memo).to.exist
|
||||
expect(memo.data).to.be.equal(TEST_DATA)
|
||||
})
|
||||
|
||||
it('can tree read', async function () {
|
||||
this.timeout(15000)
|
||||
const data = await treeRead(api, [treeRoot])
|
||||
expect(data).to.be.equal(TEST_DATA)
|
||||
})
|
||||
it('treeWrite', async function(){
|
||||
this.skip()
|
||||
this.timeout(45000)
|
||||
txHash = await api.treeWrite(longText, receiveWallet.address, sendWallet.secret)
|
||||
expect(txHash).to.exist
|
||||
expect(txHash).to.be.a('string')
|
||||
})
|
||||
|
||||
it(`can tree write large (${longText.length} bytes)`, async function () {
|
||||
this.timeout(30000)
|
||||
treeRoot = await treeWrite(api, longText, sendWallet, receiveWallet.address)
|
||||
})
|
||||
it('treeRead', async function(){
|
||||
this.skip()
|
||||
this.timeout(45000)
|
||||
txHash = await api.treeRead([txHash])
|
||||
expect(txHash).to.exist
|
||||
expect(txHash).to.be.equal(longText)
|
||||
})
|
||||
|
||||
it('can tree read large', async function () {
|
||||
this.timeout(30000)
|
||||
const data = await treeRead(api, [treeRoot])
|
||||
expect(data).to.be.equal(longText)
|
||||
})
|
||||
it('treeWrite XL', async function(){
|
||||
// this.skip()
|
||||
this.timeout(450000)
|
||||
txHash = await api.treeWrite(htmlTxt, receiveWallet.address, sendWallet.secret)
|
||||
expect(txHash).to.exist
|
||||
expect(txHash).to.be.a('string')
|
||||
})
|
||||
|
||||
it(`can tree write extra large (${htmlTxt.length} bytes)`, async function () {
|
||||
this.timeout(300000)
|
||||
treeRoot = await treeWrite(api, htmlTxt, sendWallet, receiveWallet.address)
|
||||
})
|
||||
|
||||
it('can tree read extra large', async function () {
|
||||
this.timeout(300000)
|
||||
const data = await treeRead(api, [treeRoot])
|
||||
expect(data).to.be.equal(htmlTxt)
|
||||
})
|
||||
|
||||
it("can r/w binary", async function () {
|
||||
this.timeout(10000);
|
||||
|
||||
const bytes = utf8.encode(TEST_DATA)
|
||||
const encoded = base64.encode(bytes)
|
||||
const txHash = await treeWrite(api, encoded, sendWallet, receiveWallet.address)
|
||||
const res = await treeRead(api, [txHash])
|
||||
const decoded_bytes = base64.decode(res)
|
||||
const text = utf8.decode(decoded_bytes)
|
||||
expect(text).to.be.equal(TEST_DATA)
|
||||
})
|
||||
it('treeRead XL', async function(){
|
||||
// this.skip()
|
||||
this.timeout(450000)
|
||||
txHash = await api.treeRead([txHash])
|
||||
expect(txHash).to.exist
|
||||
expect(txHash).to.be.equal(htmlTxt)
|
||||
})
|
||||
|
||||
it('print open handles', function(){
|
||||
wtf.dump()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user