Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

locally play transactions before sending #100

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions etc/tupelo-wasm-sdk.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -322,13 +323,16 @@ export namespace Tupelo {
export function getTip(did: string): Promise<Proof>;
// (undocumented)
export function keyFromPrivateBytes(bytes: Uint8Array): Promise<Uint8Array[]>;
export function newAddBlockRequest(tree: ChainTree, transactions: Transaction[]): Promise<AddBlockRequest>;
// (undocumented)
export function newEmptyTree(store: IBlockService, publicKey: Uint8Array): Promise<CID_2>;
// (undocumented)
export function passPhraseKey(phrase: Uint8Array, salt: Uint8Array): Promise<Uint8Array[]>;
// (undocumented)
export function playTransactions(tree: ChainTree, transactions: Transaction[]): Promise<Proof>;
// (undocumented)
export function sendAddBlockRequest(addBlockRequest: AddBlockRequest, timeout?: number): Promise<Proof>;
// (undocumented)
export function setLogLevel(name: string, level: string): Promise<void>;
// (undocumented)
export function startClient(pubsub: IPubSub, group: NotaryGroup, store: IBlockService): Promise<void>;
Expand Down
44 changes: 38 additions & 6 deletions src/tupelo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()

Expand All @@ -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()

Expand Down Expand Up @@ -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)])
Expand All @@ -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()
Expand Down
63 changes: 62 additions & 1 deletion src/tupelo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -27,6 +28,11 @@ interface IPlayTransactionOptions {
transactions: Uint8Array[],
}

interface ISendAddBlockRequestOptions {
addBlockRequest: Uint8Array,
timeout?: number,
}

interface IClientOptions {
pubsub: IPubSub,
notaryGroup: Uint8Array // protobuf encoded config.NotaryGroup
Expand Down Expand Up @@ -77,6 +83,12 @@ class UnderlyingWasm {
playTransactions(opts: IPlayTransactionOptions): Promise<Uint8Array> {
return new Promise<Uint8Array>((res, rej) => { }) // replaced by wasm
}
newAddBlockRequest(opts: IPlayTransactionOptions): Promise<Uint8Array> {
return new Promise<Uint8Array>((res, rej) => { }) // replaced by wasm
}
sendAddBlockRequest(opts: ISendAddBlockRequestOptions): Promise<Uint8Array> {
return new Promise<Uint8Array>((res, rej) => { }) // replaced by wasm
}
startClient(opts: IClientOptions): void {
// replaced by wasm
}
Expand Down Expand Up @@ -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<Proof> {
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<AddBlockRequest> {
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 = <ecdsaKey>")
}
const tw = await TupeloWasm.get()
let transBits: Uint8Array[] = new Array<Uint8Array>()
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<Proof> {
logger("playTransactions")
if (tree.key == undefined) {
Expand Down