diff --git a/etc/tupelo-wasm-sdk.api.md b/etc/tupelo-wasm-sdk.api.md index 3176b4d..e2120a4 100644 --- a/etc/tupelo-wasm-sdk.api.md +++ b/etc/tupelo-wasm-sdk.api.md @@ -4,6 +4,7 @@ ```ts +import { AddBlockRequest } from 'tupelo-messages/services/services_pb'; import CID from 'cids'; import EventEmitter from 'events'; import { NotaryGroup } from 'tupelo-messages/config/config_pb'; @@ -322,6 +323,7 @@ export namespace Tupelo { export function getTip(did: string): Promise; // (undocumented) export function keyFromPrivateBytes(bytes: Uint8Array): Promise; + export function newAddBlockRequest(tree: ChainTree, transactions: Transaction[]): Promise; // (undocumented) export function newEmptyTree(store: IBlockService, publicKey: Uint8Array): Promise; // (undocumented) @@ -329,6 +331,8 @@ export namespace Tupelo { // (undocumented) export function playTransactions(tree: ChainTree, transactions: Transaction[]): Promise; // (undocumented) + export function sendAddBlockRequest(addBlockRequest: AddBlockRequest, timeout?: number): Promise; + // (undocumented) export function setLogLevel(name: string, level: string): Promise; // (undocumented) export function startClient(pubsub: IPubSub, group: NotaryGroup, store: IBlockService): Promise; diff --git a/src/tupelo.spec.ts b/src/tupelo.spec.ts index 66bf5ad..7d58577 100644 --- a/src/tupelo.spec.ts +++ b/src/tupelo.spec.ts @@ -10,6 +10,7 @@ import { Community } from './community/community'; import Repo from './repo'; import debug from 'debug'; import CID from 'cids'; +import { AddBlockRequest } from 'tupelo-messages/services/services_pb'; // import {LocalCommunity} from 'local-tupelo'; @@ -46,6 +47,37 @@ describe('Tupelo', () => { expect(addr).to.have.lengthOf(42) }) + it('creates a new AddBlockRequest', async () => { + const c = await Community.getDefault() + const key = await EcdsaKey.generate() + + let tree = await ChainTree.newEmptyTree(c.blockservice, key) + debugLog("created empty tree") + const trans = setDataTransaction("/hi", "hihi") + + const abr = await Tupelo.newAddBlockRequest(tree, [trans]) + expect(abr).instanceOf(AddBlockRequest) + return true + }) + + it('sends an ABR', async ()=> { + const c = await Community.getDefault() + const key = await EcdsaKey.generate() + + let tree = await ChainTree.newEmptyTree(c.blockservice, key) + debugLog("created empty tree") + const trans = setDataTransaction("/hi", "hihi") + + const abr = await Tupelo.newAddBlockRequest(tree, [trans]) + expect(abr).instanceOf(AddBlockRequest) + + const proof = await Tupelo.sendAddBlockRequest(abr) + tree.tip = new CID(Buffer.from(proof.getTip_asU8())) + const resolved = await tree.resolve("tree/data/hi") + expect(resolved.value).to.equal("hihi") + return true + }) + // requires a running tupelo it('plays transactions on a new tree', async () => { const c = await Community.getDefault() @@ -81,7 +113,7 @@ describe('Tupelo', () => { return p }) - it('gets tip', async ()=> { + it('gets tip', async () => { const c = await Community.getDefault() const key = await EcdsaKey.generate() @@ -98,7 +130,7 @@ describe('Tupelo', () => { expect(playProof.getTip_asB64()).to.equal(tipProof.getTip_asB64()) }) - it('gets latest', async ()=> { + it('gets latest', async () => { const c = await Community.getDefault() const key = await EcdsaKey.generate() @@ -133,7 +165,7 @@ describe('Tupelo', () => { throw new Error("unknown sender id") } const tokenName = "testtoken" - await c.playTransactions(senderTree, [establishTokenTransaction(tokenName, 10),mintTokenTransaction(tokenName, 5)]) + await c.playTransactions(senderTree, [establishTokenTransaction(tokenName, 10), mintTokenTransaction(tokenName, 5)]) const sendId = "anewsendid" let resp = await c.playTransactions(senderTree, [sendTokenTransaction(sendId, tokenName, 5, receiverId)]) @@ -151,17 +183,17 @@ describe('Tupelo', () => { return p }) - it('verifies a returned proof', async ()=> { + it('verifies a returned proof', async () => { const c = await Community.getDefault() - const p = new Promise(async (resolve, reject)=> { + const p = new Promise(async (resolve, reject) => { const k = await EcdsaKey.generate() const tree = await ChainTree.newEmptyTree(c.blockservice, k) const resp = await c.playTransactions(tree, [setDataTransaction("hi", "hi")]) try { let verified = await Tupelo.verifyProof(resp) expect(verified).to.be.true - } catch(e) { + } catch (e) { reject(e) } resolve() diff --git a/src/tupelo.ts b/src/tupelo.ts index 2b72f40..1741fd9 100644 --- a/src/tupelo.ts +++ b/src/tupelo.ts @@ -2,8 +2,9 @@ import CID from 'cids'; const go = require('./js/go') import { Transaction } from 'tupelo-messages' +import {AddBlockRequest} from 'tupelo-messages/services/services_pb' import { TokenPayload } from 'tupelo-messages/transactions/transactions_pb' -import { IBlockService, IBlock } from './chaintree/dag/dag' +import { IBlockService } from './chaintree/dag/dag' import ChainTree from './chaintree/chaintree'; import { Proof } from 'tupelo-messages/gossip/gossip_pb'; import { NotaryGroup } from 'tupelo-messages/config/config_pb'; @@ -27,6 +28,11 @@ interface IPlayTransactionOptions { transactions: Uint8Array[], } +interface ISendAddBlockRequestOptions { + addBlockRequest: Uint8Array, + timeout?: number, +} + interface IClientOptions { pubsub: IPubSub, notaryGroup: Uint8Array // protobuf encoded config.NotaryGroup @@ -77,6 +83,12 @@ class UnderlyingWasm { playTransactions(opts: IPlayTransactionOptions): Promise { return new Promise((res, rej) => { }) // replaced by wasm } + newAddBlockRequest(opts: IPlayTransactionOptions): Promise { + return new Promise((res, rej) => { }) // replaced by wasm + } + sendAddBlockRequest(opts: ISendAddBlockRequestOptions): Promise { + return new Promise((res, rej) => { }) // replaced by wasm + } startClient(opts: IClientOptions): void { // replaced by wasm } @@ -236,6 +248,55 @@ export namespace Tupelo { }) } + /** + * + * @param addBlockRequest - the AddBlockRequest to send to the network + * @param timeout - the timeout to wait for the transaction to complete + */ + export async function sendAddBlockRequest(addBlockRequest:AddBlockRequest, timeout?:number):Promise { + const tw = await TupeloWasm.get() + const abrBits = addBlockRequest.serializeBinary() + const resp = await tw.sendAddBlockRequest({ + addBlockRequest: abrBits, + timeout: timeout, + }) + + const proof = Proof.deserializeBinary(resp) + return proof + } + + /** + * newAddBlockRequest is mostly desigend in order to play transactions *locally* before sending these off to the network + * useful when you want your UI to update immediately + * @param tree - the tree to create the AddBlockRequest + * @param transactions - the list of transactions + */ + export async function newAddBlockRequest(tree:ChainTree, transactions: Transaction[]): Promise { + logger("newAddBlockRequest") + if (tree.key == undefined) { + throw new Error("playing transactions on a tree requires the tree to have a private key, use tree.key = ") + } + const tw = await TupeloWasm.get() + let transBits: Uint8Array[] = new Array() + for (var t of transactions) { + const serialized = t.serializeBinary() + transBits = transBits.concat(serialized) + } + + const privateKey: Uint8Array = tree.key.privateKey ? tree.key.privateKey : new Uint8Array() + if (privateKey.length == 0) { + throw new Error("can only play transactions on a tree with a private key attached") + } + + const resp = await tw.newAddBlockRequest({ + privateKey: privateKey, + tip: tree.tip, + transactions: transBits, + }) + const abr = AddBlockRequest.deserializeBinary(resp) + return abr + } + export async function playTransactions(tree: ChainTree, transactions: Transaction[]): Promise { logger("playTransactions") if (tree.key == undefined) { diff --git a/tupelo b/tupelo index cea27ac..735e873 160000 --- a/tupelo +++ b/tupelo @@ -1 +1 @@ -Subproject commit cea27ac673bd0cf5d232766672b55ef3a8a94927 +Subproject commit 735e87353c1a176d54d1a416834b0f77df8bd755