From e1207551c96dbfeba53efea76d49bad41387f015 Mon Sep 17 00:00:00 2001 From: cmd Date: Thu, 15 Feb 2024 12:12:19 -0600 Subject: [PATCH] update --- demo/draft/draft.html | 272 ++++++++++++++++++++++++++++++++++++++ src/client/class/draft.ts | 53 +++++++- src/client/class/store.ts | 8 +- src/lib/contract.ts | 12 +- src/lib/proposal.ts | 10 +- 5 files changed, 339 insertions(+), 16 deletions(-) create mode 100644 demo/draft/draft.html diff --git a/demo/draft/draft.html b/demo/draft/draft.html new file mode 100644 index 00000000..16eafab4 --- /dev/null +++ b/demo/draft/draft.html @@ -0,0 +1,272 @@ + + + + + +MVP Demo Page + + + + + +
+ + + + +
+ + + + + +
+

Members

+ +
+ +
+

Roles

+ + +
+ +
+

Terms

+

+
+ +
+

Signatures

+ +
+ +
+

Seats Available

+ +
+ +
+

Console

+
+
+ + + + + + + diff --git a/src/client/class/draft.ts b/src/client/class/draft.ts index dfefa843..05b0ed21 100644 --- a/src/client/class/draft.ts +++ b/src/client/class/draft.ts @@ -31,6 +31,7 @@ import { } from '@/lib/util.js' import { + create_draft, find_program, get_proposal_id } from '@/lib/proposal.js' @@ -42,6 +43,7 @@ import { import { DraftData, + DraftTemplate, MemberData, ProgramQuery, ProposalData, @@ -51,9 +53,13 @@ import { import * as assert from '@/assert.js' export class DraftSession extends EventEmitter <{ + 'approved' : DraftSession 'commit' : string + 'debug' : unknown[] 'endorse' : string 'error' : Error + 'full' : DraftSession + 'info' : unknown[] 'join' : MemberData 'leave' : MemberData 'members' : MemberData[] @@ -72,7 +78,9 @@ export class DraftSession extends EventEmitter <{ readonly _store : NostrStore readonly _sub : NostrSub - _init : boolean + _agreed : boolean + _full : boolean + _init : boolean constructor ( signer : EscrowSigner, @@ -88,7 +96,9 @@ export class DraftSession extends EventEmitter <{ this._signer = signer this._socket = new NostrSocket(signer._signer, socket_opt) this._store = new NostrStore(this._socket, store_opt) - this._sub = this._socket.subscribe({ selfsub : true }) + this._sub = this._socket.subscribe({ selfsub : false }) + this._agreed = false + this._full = false this._init = false this._socket.on('error', (err) => { this.emit('error', err) }) @@ -124,6 +134,14 @@ export class DraftSession extends EventEmitter <{ }) } + get is_approved () { + return ( + this.is_full && + this.signatures.length >= this.members.length && + this.signatures.every(sig => verify_endorsement(this.prop_id, sig)) + ) + } + get data () { return this._store.data } @@ -213,10 +231,16 @@ export class DraftSession extends EventEmitter <{ log = { debug : (...s : unknown[]) => { - return (this.opt.debug) ? console.log('[session]', ...s) : null + if (this.opt.debug) { + console.log('[session]', ...s) + this.emit('debug', s) + } }, info : (...s : unknown[]) => { - return (this.opt.verbose) ? console.log('[session]', ...s) : null + if (this.opt.verbose) { + console.log('[session]', ...s) + this.emit('info', s) + } } } @@ -292,6 +316,14 @@ export class DraftSession extends EventEmitter <{ validate_draft(data) verify_draft(data) this._store._update(data, created_at) + if (this.is_full && !this._agreed) { + this.emit('full', this) + } + if (this.is_approved && !this._agreed) { + this.emit('approved', this) + } + this._agreed = this.is_approved + this._full = this.is_full this.emit('update', this) } @@ -338,10 +370,13 @@ export class DraftSession extends EventEmitter <{ } async init ( - address : string, - secret : string, - store : DraftData + address : string, + secret : string, + template : DraftTemplate | DraftData ) { + const store = create_draft(template) + validate_draft(store) + verify_draft(store) return Promise.all([ this._store.init(address, secret, store), this.sub.when_ready() @@ -383,6 +418,10 @@ export class DraftSession extends EventEmitter <{ return contract } + async refresh () { + return this._store.refresh() + } + toJSON () { return this.data } diff --git a/src/client/class/store.ts b/src/client/class/store.ts index cb84a3a5..0695df13 100644 --- a/src/client/class/store.ts +++ b/src/client/class/store.ts @@ -281,11 +281,9 @@ export class NostrStore > extends EventEmitter<{ } async refresh () { - // If the data is stale, resub to the relay. - if (this.is_stale) { - this.sub.update() - } - return this.data + const receipt = this.sub.when_ready() + this.sub.update() + return receipt } keys () { diff --git a/src/lib/contract.ts b/src/lib/contract.ts index 341b9dd9..174af18b 100644 --- a/src/lib/contract.ts +++ b/src/lib/contract.ts @@ -58,7 +58,7 @@ export function create_contract ( // Calculate the subtotal. const subtotal = terms.value + agent_fee[0] // Calculate the vout size of the tx output. - const vout_size = outputs[0][1].length / 2 + const vout_size = get_max_vout_size(outputs) // Calculate the transaction fee. const txfee = vout_size * feerate // Return a completed contract. @@ -147,6 +147,16 @@ function get_deadline ( } } +/** + * Get the size (in bytes) of the largest tx template. + */ +export function get_max_vout_size ( + outputs : SpendTemplate[] +) { + const lens = outputs.map(e => e[1].length) + return Math.max(...lens) / 2 +} + /** * Convert each spending path in the proposal * into a transaction output template. diff --git a/src/lib/proposal.ts b/src/lib/proposal.ts index e618d1b2..ab541f10 100644 --- a/src/lib/proposal.ts +++ b/src/lib/proposal.ts @@ -25,6 +25,7 @@ import { DraftData, DraftTemplate } from '@/types/index.js' +import { create_policy } from './policy.js' type PathTotal = [ path: string, total : number ] @@ -44,9 +45,12 @@ const GET_DEFAULT_PROP = () => { export function create_draft ( template : DraftTemplate ) : DraftData { - const { members = [], proposal, roles = [], signatures = [] } = template - const prop = create_proposal(proposal) - return { members, proposal: prop, roles, signatures } + let { members = [], proposal, roles = [], signatures = [] } = template + const terms = create_proposal(proposal) + const policies = roles.map(e => { + return (e.id === undefined) ? create_policy(e) : e + }) + return { members, proposal : terms, roles : policies, signatures } } export function create_proposal (template : ProposalTemplate) {