From 04dab07fb599833e5f6e69675ef1ab22e8d2113c Mon Sep 17 00:00:00 2001 From: nitowa Date: Wed, 3 May 2023 22:07:18 +0200 Subject: [PATCH] Add functionality to verify sender on reads --- package.json | 2 +- src/util/errors.ts | 3 ++- src/xrpIO/xrpl-binding.ts | 17 +++++++++------- test/primitives.ts | 43 +++++++++++++++++++++++++++++---------- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index d927e46..bd45260 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xrpio", - "version": "0.2.0", + "version": "0.2.1", "repository": { "type": "git", "url": "https://gitea.nitowa.xyz/npm-packages/xrpio.git" diff --git a/src/util/errors.ts b/src/util/errors.ts index 9597ec1..971990d 100644 --- a/src/util/errors.ts +++ b/src/util/errors.ts @@ -1 +1,2 @@ -export const ERR_BAD_TX_HASH = (hash:string) => new Error(`Bad tx hash format: "${hash}"`) \ No newline at end of file +export const ERR_BAD_TX_HASH = (hash:string) => new Error(`Bad tx hash format: "${hash}"`) +export const ERR_NO_VERIFY_OWNER = (hash: string, actualAccount: string, desiredAccount: string) => new Error(`Expected tx "${hash}" to be initiated by ${desiredAccount} but was ${actualAccount}`) \ No newline at end of file diff --git a/src/xrpIO/xrpl-binding.ts b/src/xrpIO/xrpl-binding.ts index b55a3d8..cbe7259 100644 --- a/src/xrpIO/xrpl-binding.ts +++ b/src/xrpIO/xrpl-binding.ts @@ -4,7 +4,7 @@ import { Client, Payment, TxResponse, Wallet } from 'xrpl' import * as zlib from 'zlib' import * as util from 'util' import { NON_ZERO_TX_HASH } from '../util/protocol.constants' -import { ERR_BAD_TX_HASH } from '../util/errors' +import { ERR_BAD_TX_HASH, ERR_NO_VERIFY_OWNER } from '../util/errors' 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') @@ -125,13 +125,16 @@ export class xrpIO { } } - public async readRaw(hash: string): Promise { - + public async readRaw(hash: string, verifyOwner?: string): Promise { if (!NON_ZERO_TX_HASH.test(hash)) { throw ERR_BAD_TX_HASH(hash) } - const tx = await this.getTransaction(hash) + + if(verifyOwner && tx.result.Account != verifyOwner){ + throw ERR_NO_VERIFY_OWNER(hash, tx.result.Account, verifyOwner) + } + const memo = tx.result.Memos[0].Memo const memoParsed = { data: hexDecode(memo.MemoData), @@ -156,16 +159,16 @@ export class xrpIO { return await this.treeWrite(JSON.stringify(hashes), to, secret, 'N') } - public async treeRead(hashes: string[]): Promise { + public async treeRead(hashes: string[], verifyOwner?:string): Promise { const bad_hash = hashes.find(hash => !NON_ZERO_TX_HASH.test(hash)) if (bad_hash) throw ERR_BAD_TX_HASH(bad_hash) - const memos = await Promise.all(hashes.map(hash => this.readRaw(hash))) + const memos = await Promise.all(hashes.map(hash => this.readRaw(hash, verifyOwner))) 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 await this.treeRead(JSON.parse(payload), verifyOwner) } return payload diff --git a/test/primitives.ts b/test/primitives.ts index b1ace18..46723c2 100644 --- a/test/primitives.ts +++ b/test/primitives.ts @@ -36,7 +36,6 @@ describe('XRPIO', () => { }) it('getAccountSequence', async function(){ - //this.skip() this.timeout(10000) const seq = await api.getAccountSequence(sendWallet.address) expect(seq).to.exist @@ -44,7 +43,7 @@ describe('XRPIO', () => { }) it('estimateFee', async function () { - this.timeout(2000) + this.timeout(10000) const cost = await api.estimateFee(longText) expect(cost).to.be.a('number') expect(cost).to.be.lessThan(50) @@ -53,7 +52,6 @@ describe('XRPIO', () => { 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 @@ -61,13 +59,26 @@ describe('XRPIO', () => { }) 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('verifyOwner readRaw', async function (){ + this.timeout(15000) + const memo = await api.readRaw(txHash, sendWallet.address) + expect(memo).to.exist + expect(memo.data).to.be.equal(TEST_DATA) + }) + + it('verifyOwner readRaw bad owner', function (done){ + this.timeout(15000) + api.readRaw(txHash, "not the owner") + .then(_ => done(new Error('Expected error but succeeded'))) + .catch(_ => done()) + }) + it('readRaw bad hash', function (done){ this.timeout(150000) api.readRaw("123") @@ -76,7 +87,6 @@ describe('XRPIO', () => { }) it('treeWrite', async function(){ -// this.skip() this.timeout(45000) txHash = await api.treeWrite(longText, receiveWallet.address, sendWallet.secret) expect(txHash).to.exist @@ -84,15 +94,27 @@ describe('XRPIO', () => { }) it('treeRead', async function(){ -// this.skip() this.timeout(45000) - txHash = await api.treeRead([txHash]) - expect(txHash).to.exist - expect(txHash).to.be.equal(longText) + const data = await api.treeRead([txHash]) + expect(data).to.exist + expect(data).to.be.equal(longText) + }) + + it('treeRead verify owner', async function(){ + this.timeout(45000) + const data = await api.treeRead([txHash], sendWallet.address) + expect(data).to.exist + expect(data).to.be.equal(longText) + }) + + it('verifyOwner treeRead bad owner', function(done){ + this.timeout(45000) + api.treeRead([txHash], "not the owner") + .then(_ => done(new Error('Expected error but succeeded'))) + .catch(_ => done()) }) it('treeWrite XL', async function(){ - //this.skip() this.timeout(450000) txHash = await api.treeWrite(htmlTxt, receiveWallet.address, sendWallet.secret) expect(txHash).to.exist @@ -100,7 +122,6 @@ describe('XRPIO', () => { }) it('treeRead XL', async function(){ - //this.skip() this.timeout(450000) const data = await api.treeRead([txHash]) expect(data).to.exist