From 8103b288eed3581f924d38bf0087c7c176bbd49a Mon Sep 17 00:00:00 2001 From: cmd Date: Sun, 14 Jan 2024 23:20:23 -0600 Subject: [PATCH] update --- src/client/api/deposit.ts | 6 +-- src/client/api/depositor.ts | 41 +++++++++--------- src/client/api/endorse.ts | 23 +++++----- src/client/api/member.ts | 24 +++++----- src/client/class/proposal.ts | 21 ++++++--- src/client/class/{member.ts => signer.ts} | 14 +----- src/client/index.ts | 2 +- src/lib/contract.ts | 53 ++++++++--------------- src/lib/deposit.ts | 6 +-- src/lib/proposal.ts | 4 +- src/lib/session.ts | 4 +- src/lib/util.ts | 2 +- src/schema/contract.ts | 6 ++- src/schema/deposit.ts | 11 ++--- src/schema/proposal.ts | 20 +++++---- src/types/contract.ts | 5 ++- src/types/deposit.ts | 2 +- src/types/proposal.ts | 2 + src/validators/covenant.ts | 4 +- src/validators/program.ts | 2 +- test/client/contract/create.ts | 37 +++++++++------- test/client/contract/status.ts | 5 +-- test/demo/03-contract.ts | 2 + test/demo/04-deposits.ts | 4 +- test/demo/utils.ts | 6 +-- test/src/core.ts | 26 ++++++++++- test/src/fund.ts | 28 ++++++------ test/src/tests/e2e.test.ts | 25 +++++------ test/src/types.ts | 13 +++--- test/src/vectors/basic_escrow.ts | 16 +++---- 30 files changed, 214 insertions(+), 200 deletions(-) rename src/client/class/{member.ts => signer.ts} (86%) diff --git a/src/client/api/deposit.ts b/src/client/api/deposit.ts index 0550ccb2..57662a1a 100644 --- a/src/client/api/deposit.ts +++ b/src/client/api/deposit.ts @@ -9,7 +9,7 @@ import { ReturnData, DepositData, ApiResponse, - DepositAccount, + DepositSession, DepositRequest } from '@/types/index.js' @@ -21,7 +21,7 @@ import * as assert from '@/assert.js' function request_deposit_api (client : EscrowClient) { return async ( req : DepositRequest - ) : Promise> => { + ) : Promise> => { // Ensure params are string values. const arr = Object.entries(req) // Build a query string with params. @@ -29,7 +29,7 @@ function request_deposit_api (client : EscrowClient) { // Formulate the request. const url = `${client.host}/api/deposit/request?${qry}` // Return the response. - return client.fetcher({ url }) + return client.fetcher({ url }) } } diff --git a/src/client/api/depositor.ts b/src/client/api/depositor.ts index 02b1d04b..bdf25ad7 100644 --- a/src/client/api/depositor.ts +++ b/src/client/api/depositor.ts @@ -1,17 +1,17 @@ import { Buff } from '@cmdcode/buff' import { create_return_tx } from '@/lib/return.js' import { get_deposit_ctx } from '@/lib/deposit.js' -import { EscrowMember } from '@/client/class/member.js' +import { EscrowSigner } from '@/client/class/signer.js' import { - create_spend_psigs, - create_return_psig + create_covenant, + create_return } from '@/lib/session.js' import { ContractData, CovenantData, - DepositAccount, + DepositSession, DepositData, DepositRegister, ReturnData, @@ -21,66 +21,65 @@ import { /** * Create a deposit template for registration. */ -export function register_deposit_api (client : EscrowMember) { +export function register_deposit_api (client : EscrowSigner) { return async ( - acct : DepositAccount, + sess : DepositSession, utxo : TxOutput ) : Promise => { // Unpack the deposit object. - const { agent_id, agent_pk, sequence } = acct + const { agent_id, agent_pk, sequence } = sess // Define our pubkey. - const pub = client.signer.pubkey + const pub = client.pubkey const idx = Buff.hex(utxo.txid).slice(0, 4).num - const addr = client.wallet.get_account(idx).new_address() + const addr = client._wallet.get_account(idx).new_address() // Get the context object for our deposit account. const ctx = get_deposit_ctx(agent_pk, pub, sequence) // Create the return transaction. - const rtx = create_return_tx(addr, ctx, client.signer, utxo) + const rtx = create_return_tx(addr, ctx, client._signer, utxo) return { agent_id, return_tx : rtx } } } -export function commit_deposit_api (client : EscrowMember) { +export function create_covenant_api (client : EscrowSigner) { return async ( - req : DepositAccount | DepositData, + req : DepositSession | DepositData, contract : ContractData, utxo : TxOutput ) : Promise => { // Unpack the deposit object. const { agent_pk, sequence } = req // Define our pubkey. - const pub = client.signer.pubkey + const pub = client.pubkey // Get the context object for our deposit account. const ctx = get_deposit_ctx(agent_pk, pub, sequence) // Create a covenant with the contract and deposit. - return create_spend_psigs(ctx, contract, client.signer, utxo) + return create_covenant(ctx, contract, client._signer, utxo) } } -export function close_deposit_api (client : EscrowMember) { +export function create_return_api (client : EscrowSigner) { return async ( deposit : DepositData, txfee : number, address ?: string ) : Promise => { // Unpack client object. - const { signer, wallet } = client const { txid } = deposit if (address === undefined) { // Compute an index value from the deposit txid. const acct = Buff.hex(txid).slice(0, 4).num // Generate refund address. - address = wallet.get_account(acct).new_address() + address = client._wallet.get_account(acct).new_address() } // Create the return transaction. - return create_return_psig(address, deposit, signer, txfee) + return create_return(address, deposit, client._signer, txfee) } } -export default function (client : EscrowMember) { +export default function (client : EscrowSigner) { return { create_registration : register_deposit_api(client), - create_covenant : commit_deposit_api(client), - create_refund : close_deposit_api(client) + create_covenant : create_covenant_api(client), + create_return : create_return_api(client) } } diff --git a/src/client/api/endorse.ts b/src/client/api/endorse.ts index ffa5e89e..1a004867 100644 --- a/src/client/api/endorse.ts +++ b/src/client/api/endorse.ts @@ -1,7 +1,7 @@ -import { get_proposal_id } from '@/lib/proposal.js' +import { endorse_proposal } from '@/lib/proposal.js' import { sign_witness } from '@/lib/witness.js' import { EscrowContract } from '@/client/class/contract.js' -import { EscrowMember } from '@/client/class/member.js' +import { EscrowSigner } from '@/client/class/signer.js' import { EscrowProposal } from '@/client/class/proposal.js' import { validate_witness } from '@/validators/program.js' @@ -16,30 +16,29 @@ import { WitnessData } from '@/types/index.js' -export function endorse_proposal_api (member : EscrowMember) { +export function endorse_proposal_api (client : EscrowSigner) { return (proposal : ProposalData | EscrowProposal) => { if (proposal instanceof EscrowProposal) { proposal = proposal.data } validate_proposal(proposal) verify_proposal(proposal) - const hash = get_proposal_id(proposal) - return member.signer.sign(hash) + return endorse_proposal(proposal, client._signer) } } - -export function endorse_request_api (member : EscrowMember) { + +export function endorse_request_api (client : EscrowSigner) { return ( url : string, body : string = '{}', method : string = 'GET' ) => { const content = method + url + body - return member.signer.gen_token(content) + return client._signer.gen_token(content) } } -export function endorse_witness_api (member : EscrowMember) { +export function endorse_witness_api (client : EscrowSigner) { return ( contract : ContractData | EscrowContract, witness : WitnessData @@ -47,16 +46,16 @@ export function endorse_witness_api (member : EscrowMember) { if (contract instanceof EscrowContract) { contract = contract.data } - const cred = member.get_membership(contract.terms) + const cred = client.get_membership(contract.terms) validate_witness(contract, witness) return sign_witness(cred.signer, witness) } } -export default function (client : EscrowMember) { +export default function (client : EscrowSigner) { return { proposal : endorse_proposal_api(client), request : endorse_request_api(client), witness : endorse_witness_api(client) } -} +} \ No newline at end of file diff --git a/src/client/api/member.ts b/src/client/api/member.ts index 2699e3c4..bfb2022c 100644 --- a/src/client/api/member.ts +++ b/src/client/api/member.ts @@ -1,5 +1,5 @@ import { Wallet } from '@cmdcode/signer' -import { EscrowMember } from '@/client/class/member.js' +import { EscrowSigner } from '@/client/class/signer.js' import { EscrowProposal } from '@/client/class/proposal.js' import { ProposalData } from '@/types/index.js' @@ -11,47 +11,45 @@ import { import { Membership } from '../types.js' -export function gen_membership_api (client : EscrowMember) { +export function gen_membership_api (client : EscrowSigner) { return (index ?: number) => { - const { signer, wallet } = client - const idx = index ?? client.new_idx - return gen_membership(idx, signer, wallet) + const idx = index ?? client._gen_idx() + return gen_membership(idx, client._signer, client._wallet) } } export function has_membership_api ( - client : EscrowMember + client : EscrowSigner ) { return (proposal : ProposalData | EscrowProposal) => { if (proposal instanceof EscrowProposal) { proposal = proposal.data } - return has_membership(proposal.members, client.signer) + return has_membership(proposal.members, client._signer) } } export function claim_membership_api ( - client : EscrowMember + client : EscrowSigner ) { - const { signer } = client return (proposal : ProposalData | EscrowProposal) : Membership => { if (proposal instanceof EscrowProposal) { proposal = proposal.data } - if (!has_membership(proposal.members, signer)) { + if (!has_membership(proposal.members, client._signer)) { throw new Error('not a member of the proposal') } - const mship = get_membership(proposal.members, signer) + const mship = get_membership(proposal.members, client._signer) // TODO: validate membership return { - signer : client.signer.get_id(mship.id), + signer : client._signer.get_id(mship.id), token : mship, wallet : new Wallet(mship.xpub) } } } -export default function (client : EscrowMember) { +export default function (client : EscrowSigner) { return { claim : claim_membership_api(client), create : gen_membership_api(client), diff --git a/src/client/class/proposal.ts b/src/client/class/proposal.ts index f8dbefc7..96c8d1ef 100644 --- a/src/client/class/proposal.ts +++ b/src/client/class/proposal.ts @@ -1,6 +1,10 @@ import { EventEmitter } from './emitter.js' -import { EscrowMember } from './member.js' -import { find_program } from '@/lib/proposal.js' +import { EscrowSigner } from './signer.js' + +import { + find_program, + get_proposal_id +} from '@/lib/proposal.js' import { MemberData, @@ -13,6 +17,7 @@ import { add_membership, rem_membership } from '../../lib/policy.js' +import { parse_proposal } from '@/lib/parse.js' export class EscrowProposal extends EventEmitter<{ 'update' : EscrowProposal @@ -22,7 +27,7 @@ export class EscrowProposal extends EventEmitter<{ constructor (data : ProposalData) { super() - this._data = data + this._data = parse_proposal(data) } get copy () { @@ -33,21 +38,25 @@ export class EscrowProposal extends EventEmitter<{ return this._data } + get id () { + return get_proposal_id(this.data).hex + } + _update (data : ProposalData) { - this._data = data + this._data = parse_proposal(data) this.emit('update', this) } join ( role : RolePolicy, - member : EscrowMember, + member : EscrowSigner, index ?: number ) { const mdata = member.gen_membership(index) this.add_membership(mdata, role) } - leave (member : EscrowMember) { + leave (member : EscrowSigner) { const mship = member.get_membership(this) this.rem_membership(mship.token) } diff --git a/src/client/class/member.ts b/src/client/class/signer.ts similarity index 86% rename from src/client/class/member.ts rename to src/client/class/signer.ts index b5210651..61835b74 100644 --- a/src/client/class/member.ts +++ b/src/client/class/signer.ts @@ -18,7 +18,7 @@ import { const DEFAULT_IDXGEN = () => Buff.now(4).num -export class EscrowMember { +export class EscrowSigner { readonly _client : EscrowClient readonly _gen_idx : () => number @@ -40,18 +40,6 @@ export class EscrowMember { return this._signer.pubkey } - get new_idx () { - return this._gen_idx() - } - - get signer () { - return this._signer - } - - get wallet () { - return this._wallet - } - deposit = deposit_api(this) endorse = endorse_api(this) diff --git a/src/client/index.ts b/src/client/index.ts index 4f55c1e8..274381f4 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -2,5 +2,5 @@ export * from './class/client.js' export * from './class/contract.js' export * from './class/deposit.js' export * from './class/proposal.js' -export * from './class/member.js' +export * from './class/signer.js' export * from './types.js' diff --git a/src/lib/contract.ts b/src/lib/contract.ts index 1f80cfab..68367491 100644 --- a/src/lib/contract.ts +++ b/src/lib/contract.ts @@ -1,5 +1,6 @@ import { create_txhex } from './tx.js' import { now, sort_record } from './util.js' +import { parse_program } from './parse.js' import { DEFAULT_DEADLINE } from '../config.js' import { init_vm } from '../vm/main.js' @@ -11,7 +12,6 @@ import { } from './proposal.js' import { - AgentSession, ContractConfig, ContractData, PaymentEntry, @@ -20,45 +20,41 @@ import { ProposalData, SpendTemplate } from '../types/index.js' -import { parse_program } from './parse.js' -import { verify_sig } from '@cmdcode/crypto-tools/signer' -import { Bytes } from '@cmdcode/buff' /** * Returns a new ContractData object using the provided params. */ export function create_contract ( - cid : string, - proposal : ProposalData, - session : AgentSession, - options ?: ContractConfig + config : ContractConfig ) : ContractData { - const { fees = [], moderator = null, published = now(), sigs = [] } = options ?? {} - const prop_id = get_proposal_id(proposal) + const fees = config.fees ?? [] + const terms = config.proposal + const published = config.published ?? now() + const prop_id = get_proposal_id(terms) return sort_record({ - ...session, + ...config.agent, activated : null, balance : 0, - cid, - deadline : get_deadline(proposal, published), + cid : config.cid, + deadline : get_deadline(terms, published), expires_at : null, - fees, - moderator, - outputs : get_spend_outputs(proposal, fees), + fees : fees, + moderator : config.moderator ?? null, + outputs : get_spend_outputs(terms, fees), pending : 0, prop_id : prop_id.hex, - programs : init_programs(proposal.programs), - pubkeys : init_pubkeys(prop_id, sigs), - published, + programs : init_programs(terms.programs), + pubkeys : config.pubkeys ?? [], + published : published, settled : false, settled_at : null, spent : false, spent_at : null, spent_txid : null, status : 'published', - terms : proposal, - total : proposal.value + get_pay_total(fees), + terms : terms, + total : terms.value + get_pay_total(fees), updated_at : published, vm_state : null }) @@ -136,18 +132,3 @@ function init_programs ( } return entries } - -function init_pubkeys ( - prop_id : Bytes, - sigs : string[] -) : string[] { - const pubkeys : string[] = [] - sigs.forEach(e => { - const pub = e.slice(0, 64) - const sig = e.slice(64) - if (verify_sig(sig, prop_id, pub)) { - pubkeys.push(pub) - } - }) - return pubkeys -} diff --git a/src/lib/deposit.ts b/src/lib/deposit.ts index f9ee3eaf..cae1fc7a 100644 --- a/src/lib/deposit.ts +++ b/src/lib/deposit.ts @@ -28,7 +28,7 @@ import { /** * Initialization object for deposit state. */ -const INIT_STATE = { +const INIT_SPEND_STATE = { confirmed : false as const, block_hash : null, block_height : null, @@ -40,7 +40,7 @@ const INIT_STATE = { * Initialization object for deposit data. */ const INIT_DEPOSIT = { - ...INIT_STATE, + ...INIT_SPEND_STATE, covenant : null, created_at : now(), settled : false as const, @@ -108,7 +108,7 @@ export function get_spend_state ( sequence : number, txstatus : OracleTxStatus ) { - let state : DepositState = INIT_STATE + let state : DepositState = INIT_SPEND_STATE if (txstatus !== undefined && txstatus.confirmed) { const timelock = parse_timelock(sequence) diff --git a/src/lib/proposal.ts b/src/lib/proposal.ts index 669bc44a..325419f8 100644 --- a/src/lib/proposal.ts +++ b/src/lib/proposal.ts @@ -144,8 +144,8 @@ export function find_program ( } export function endorse_proposal ( - signer : SignerAPI, - proposal : ProposalData + proposal : ProposalData, + signer : SignerAPI ) : string { const msg = get_proposal_id(proposal) const pub = signer.pubkey diff --git a/src/lib/session.ts b/src/lib/session.ts index ac16fdc8..0f73dccc 100644 --- a/src/lib/session.ts +++ b/src/lib/session.ts @@ -56,7 +56,7 @@ export function create_session ( * on the deposit context, contract data, and * unspent transaction output. */ -export function create_spend_psigs ( +export function create_covenant ( context : DepositContext, contract : ContractData, signer : SignerAPI, @@ -81,7 +81,7 @@ export function create_spend_psigs ( * signature, to be used for collaboratively * returning a deposit back to the sender. */ -export function create_return_psig ( +export function create_return ( address : string, deposit : DepositData, signer : SignerAPI, diff --git a/src/lib/util.ts b/src/lib/util.ts index 938cae81..eaaca79d 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -123,7 +123,7 @@ export function sort_record > ( export function stringify (content : any) : string { switch (typeof content) { case 'object': - return (content ==! null) + return (content !== null) ? JSON.stringify(content) : 'null' case 'string': diff --git a/src/schema/contract.ts b/src/schema/contract.ts index 2f104b50..bdf6560f 100644 --- a/src/schema/contract.ts +++ b/src/schema/contract.ts @@ -17,7 +17,11 @@ const agent = z.object({ const status = z.enum([ 'published', 'funded', 'secured', 'pending', 'active', 'closed', 'spent', 'settled', 'expired', 'canceled', 'error' ]) const output = z.tuple([ label, hex ]) const program = z.tuple([ hash, label, regex, regex ]).rest(literal) -const request = z.object({ proposal : terms, signatures : hex.array() }) + +const request = z.object({ + proposal : terms, + signatures : hex.array().default([]) +}) const data = z.object({ activated : stamp.nullable(), diff --git a/src/schema/deposit.ts b/src/schema/deposit.ts index 2b0d2e63..fe4abcfd 100644 --- a/src/schema/deposit.ts +++ b/src/schema/deposit.ts @@ -21,12 +21,13 @@ const unconfirmed = z.object({ expires_at : z.null() }) -const state = z.discriminatedUnion('confirmed', [ confirmed, unconfirmed ]) -const status = z.enum([ 'reserved', 'pending', 'stale', 'open', 'locked', 'spent', 'settled', 'expired', 'error' ]) +const locktime = z.union([ str, num ]).transform(e => Number(e)) +const state = z.discriminatedUnion('confirmed', [ confirmed, unconfirmed ]) +const status = z.enum([ 'reserved', 'pending', 'stale', 'open', 'locked', 'spent', 'settled', 'expired', 'error' ]) -const request = z.object({ - pubkey : hash, - locktime : num +const request = z.object({ + locktime : locktime.optional(), + pubkey : hash }) const account = z.object({ diff --git a/src/schema/proposal.ts b/src/schema/proposal.ts index fc3cf211..1f83a741 100644 --- a/src/schema/proposal.ts +++ b/src/schema/proposal.ts @@ -3,7 +3,7 @@ import base from './base.js' import vm from './vm.js' const { - hash, literal, nonce, num, payment, + hash, hex, literal, nonce, num, payment, network, paypath, regex, stamp, str } = base @@ -27,14 +27,16 @@ const data = z.object({ fallback : str.optional(), feerate : num.optional(), members : membership.array().default([]), - network : network.default('main'), - paths : paypath.array().default([]), - payments : payment.array(), - programs : terms.array().default([]), - schedule : task.array().default([]), - title : str, - value : num, - version : num + moderator : hash, + network : network.default('main'), + paths : paypath.array().default([]), + payments : payment.array(), + programs : terms.array().default([]), + schedule : task.array().default([]), + signatures : hex.array(), + title : str, + value : num, + version : num }) export default { data, terms } diff --git a/src/types/contract.ts b/src/types/contract.ts index 94e122dc..67cfb5d4 100644 --- a/src/types/contract.ts +++ b/src/types/contract.ts @@ -26,10 +26,13 @@ export type SpendTemplate = [ ] export interface ContractConfig { + agent : AgentSession + cid : string fees ?: PaymentEntry[] moderator ?: string + proposal : ProposalData published ?: number - sigs ?: string[] + pubkeys ?: string[] } export interface ContractBase { diff --git a/src/types/deposit.ts b/src/types/deposit.ts index 2792e0f9..ec0ae534 100644 --- a/src/types/deposit.ts +++ b/src/types/deposit.ts @@ -54,7 +54,7 @@ export interface ReturnContext { tx : TxData } -export interface DepositAccount { +export interface DepositSession { created_at : number address : string agent_id : string diff --git a/src/types/proposal.ts b/src/types/proposal.ts index 217df16e..ba2a50d7 100644 --- a/src/types/proposal.ts +++ b/src/types/proposal.ts @@ -33,11 +33,13 @@ export interface ProposalData { fallback ?: string feerate ?: number members : MemberData[] + moderator : string network : Network paths : PathEntry[] payments : PaymentEntry[] programs : ProgramTerms[] schedule : ScheduleTerms[] + signatures : string[] title : string value : number version : number diff --git a/src/validators/covenant.ts b/src/validators/covenant.ts index a8649903..1c542a9d 100644 --- a/src/validators/covenant.ts +++ b/src/validators/covenant.ts @@ -15,7 +15,7 @@ import { CovenantData, DepositContext, DepositData, - DepositReturn, + ReturnData, MutexEntry, SignerAPI } from '../types/index.js' @@ -55,7 +55,7 @@ export function verify_covenant ( export function verify_refund ( dp_agent : SignerAPI, deposit : DepositData, - refund : DepositReturn + refund : ReturnData ) { const { dpid, agent_pn } = deposit const { pnonce, psig, txhex } = refund diff --git a/src/validators/program.ts b/src/validators/program.ts index 1d82b124..5420029a 100644 --- a/src/validators/program.ts +++ b/src/validators/program.ts @@ -65,7 +65,7 @@ export function validate_witness ( assert.ok(pathnames.includes(path), 'path does not exist in contract') assert.exists(expires_at) - assert.ok(stamp > published, 'stamp exists on or before published date') + assert.ok(stamp >= published, 'stamp exists before published date') assert.ok(stamp < expires_at, 'stamp exists on or after expiration date') const { sigs, wid, ...tmpl } = witness diff --git a/test/client/contract/create.ts b/test/client/contract/create.ts index 5470a7cf..f851e72f 100644 --- a/test/client/contract/create.ts +++ b/test/client/contract/create.ts @@ -1,7 +1,12 @@ -import { CoreDaemon } from '@cmdcode/core-cmd' -import { EscrowClient } from '@scrow/core' -import { get_users } from 'test/src/core.js' -import CONST from '../const.js' +import { CoreDaemon } from '@cmdcode/core-cmd' +import { get_members } from 'test/src/core.js' +import CONST from '../const.js' + +import { + EscrowClient, + Network +} from '@scrow/core' +import { parse_proposal } from '@/lib/parse.js' const core = new CoreDaemon({ network : 'regtest', @@ -10,7 +15,7 @@ const core = new CoreDaemon({ }) const corecli = await core.startup() -const members = await get_users(corecli, [ 'alice', 'bob', 'carol' ]) +const members = await get_members(corecli, [ 'alice', 'bob', 'carol' ]) const client = new EscrowClient({ hostname : CONST.escrow, oracle : CONST.oracle @@ -18,31 +23,31 @@ const client = new EscrowClient({ const [ alice, bob, carol ] = members -const proposal = { +const proposal = parse_proposal({ title : 'Basic two-party contract with third-party dispute resolution.', expires : 14400, details : 'n/a', - network : 'regtest', - moderator : alice.client.signer.pubkey, + network : 'regtest' as Network, + moderator : alice.signer.pubkey, paths: [ - [ 'heads', 10000, await alice.wallet.new_address ], - [ 'tails', 10000, await bob.wallet.new_address ], - [ 'draw', 5000, await alice.wallet.new_address ], - [ 'draw', 5000, await bob.wallet.new_address ] + [ 'heads', 10000, await alice.core.new_address ], + [ 'tails', 10000, await bob.core.new_address ], + [ 'draw', 5000, await alice.core.new_address ], + [ 'draw', 5000, await bob.core.new_address ] ], payments : [ - [ 5000, await carol.wallet.new_address ] + [ 5000, await carol.core.new_address ] ], programs : [ - [ 'sign', 'close|dispute', '*', 2, alice.client.signer.pubkey, bob.client.signer.pubkey ], - [ 'sign', 'resolve', '*', 1, carol.client.signer.pubkey ] + [ 'sign', 'close|dispute', '*', 2, alice.signer.pubkey, bob.signer.pubkey ], + [ 'sign', 'resolve', '*', 1, carol.signer.pubkey ] ], schedule: [ [ 7200, 'close', 'draw' ] ], value : 15000, version : 1 -} +}) const res = await client.contract.create(proposal) diff --git a/test/client/contract/status.ts b/test/client/contract/status.ts index 2c759b0a..0138a6cf 100644 --- a/test/client/contract/status.ts +++ b/test/client/contract/status.ts @@ -1,7 +1,4 @@ -import { - EscrowClient, - Signer -} from '@scrow/core' +import { EscrowClient } from '@scrow/core' import ctx from '../const.js' diff --git a/test/demo/03-contract.ts b/test/demo/03-contract.ts index 38e39616..cada80e4 100644 --- a/test/demo/03-contract.ts +++ b/test/demo/03-contract.ts @@ -26,6 +26,8 @@ proposal.on('update', prop => { // Request the agent to join the proposal. proposal.join(roles.agent, c_mbr) +console.log('proposal id:', proposal.id) + // Have all memebers endorse the proposal. export const signatures = [ a_mbr.endorse.proposal(proposal), diff --git a/test/demo/04-deposits.ts b/test/demo/04-deposits.ts index 18e0402a..41691ae5 100644 --- a/test/demo/04-deposits.ts +++ b/test/demo/04-deposits.ts @@ -10,8 +10,8 @@ import { EscrowClient } from "@/index.js" import { DEFAULT_LOCKTIME } from "@/config.js" // Startup a local process of Bitcoin Core for testing. -const core = get_daemon() -const cli = await core.startup() +const core = get_daemon() +const cli = await core.startup() // Define a third-party client as a coordinator. const client = new EscrowClient({ diff --git a/test/demo/utils.ts b/test/demo/utils.ts index e4aa9c19..3e68e86a 100644 --- a/test/demo/utils.ts +++ b/test/demo/utils.ts @@ -1,6 +1,6 @@ import { Buff } from "@cmdcode/buff" import { Signer, Wallet } from "@cmdcode/signer" -import { EscrowMember } from "@/client/class/member.js" +import { EscrowSigner } from "@/client/class/signer.js" /** * Take a string label as input, and return an @@ -8,7 +8,7 @@ import { EscrowMember } from "@/client/class/member.js" */ export function get_member ( alias : string -) : EscrowMember { +) : EscrowSigner { // Freeze the idx generation at 0 for test purposes. const idxgen = () => 0 // Create a basic deterministic seed. @@ -18,7 +18,7 @@ export function get_member ( // Create a new wallet using the seed. const wallet = Wallet.create({ seed, network : 'regtest' }) // Return an escrow client. - return new EscrowMember({ idxgen, signer, wallet }) + return new EscrowSigner({ idxgen, signer, wallet }) } /** diff --git a/test/src/core.ts b/test/src/core.ts index 43fc2433..58af1305 100644 --- a/test/src/core.ts +++ b/test/src/core.ts @@ -1,11 +1,13 @@ import { assert } from '@scrow/core' -import { Wallet } from '@cmdcode/signer' +import { Signer, Wallet } from '@cmdcode/signer' import { CoreClient, CoreConfig, CoreDaemon } from '@cmdcode/core-cmd' +import { Buff } from '@cmdcode/buff' +import { CoreSigner } from './types.js' const DEFAULT_CONFIG = { core_params : [ '-txindex' ], @@ -30,6 +32,28 @@ export function get_daemon ( return daemon } +export async function get_members ( + client : CoreClient, + aliases : string[] +) { + const members = aliases.map(e => get_signer(client, e)) + return Promise.all(members) +} + +export async function get_signer ( + client : CoreClient, + label : string +) : Promise { + const seed = Buff.str(label) + const wdat = await client.load_wallet(label) + const xpub = await wdat.xpub + return { + core : wdat, + signer : new Signer({ seed }), + wallet : new Wallet(xpub) + } +} + export async function get_wallet ( client : CoreClient, label : string diff --git a/test/src/fund.ts b/test/src/fund.ts index f20eea52..aa18bd54 100644 --- a/test/src/fund.ts +++ b/test/src/fund.ts @@ -1,9 +1,9 @@ -import { ContractData } from '@scrow/core' -import { create_spend_psigs } from '@scrow/core/session' -import { create_return_tx } from '@scrow/core/return' -import { create_timelock } from '@scrow/core/tx' -import { get_utxo } from './core.js' -import { EscrowMember } from './types.js' +import { ContractData } from '@scrow/core' +import { create_covenant } from '@scrow/core/session' +import { create_return_tx } from '@scrow/core/return' +import { create_timelock } from '@scrow/core/tx' +import { get_utxo } from './core.js' +import { CoreSigner } from './types.js' import { get_deposit_address, @@ -14,22 +14,22 @@ const SEQUENCE = create_timelock(60 * 60 * 2) export function get_funds ( contract : ContractData, - members : EscrowMember[], + members : CoreSigner[], txfee = 1000 ) { const { agent_id, agent_pk } = contract - const cli = members[0].wallet.client + const cli = members[0].core.client const network = contract.terms.network const value = Math.ceil(contract.total / 3 + txfee) const templates = members.map(async mbr => { - const ctx = get_deposit_ctx(agent_pk, mbr.client.signer.pubkey, SEQUENCE) + const ctx = get_deposit_ctx(agent_pk, mbr.signer.pubkey, SEQUENCE) const addr = get_deposit_address(ctx, network) - await mbr.wallet.ensure_funds(value) - const txid = await mbr.wallet.send_funds(value, addr) + await mbr.core.ensure_funds(value) + const txid = await mbr.core.send_funds(value, addr) const txo = await get_utxo(cli, addr, txid) - const ret = await mbr.wallet.new_address - const rtx = create_return_tx(ret, ctx, mbr.client.signer, txo, txfee) - const cov = create_spend_psigs(ctx, contract, mbr.client.signer, txo) + const ret = await mbr.core.new_address + const rtx = create_return_tx(ret, ctx, mbr.signer, txo, txfee) + const cov = create_covenant(ctx, contract, mbr.signer, txo) return { agent_id, covenant : cov, return_tx : rtx } }) diff --git a/test/src/tests/e2e.test.ts b/test/src/tests/e2e.test.ts index a485026e..c68f7e36 100644 --- a/test/src/tests/e2e.test.ts +++ b/test/src/tests/e2e.test.ts @@ -1,12 +1,13 @@ import { Test } from 'tape' import { Buff } from '@cmdcode/buff' import { CoreClient } from '@cmdcode/core-cmd' +import { Signer } from '@cmdcode/signer' import { StateData } from '@scrow/core' import { get_return_ctx } from '@scrow/core/return' import { create_session } from '@scrow/core/session' import { now } from '@scrow/core/util' import { prevout_to_txspend } from '@scrow/core/tx' -import { get_users } from '../core.js' +import { get_members } from '../core.js' import { get_funds } from '../fund.js' import { create_settlment } from '../spend.js' @@ -46,8 +47,6 @@ import * as assert from '@scrow/core/assert' import { get_proposal } from '../vectors/basic_escrow.js' -import { Signer } from '@cmdcode/signer' - const VERBOSE = process.env.VERBOSE === 'true' export default async function (client : CoreClient, t : Test) { @@ -60,7 +59,7 @@ export default async function (client : CoreClient, t : Test) { const banner = (title : string) => `\n=== [ ${title} ] ===`.padEnd(80, '=') + '\n' const aliases = [ 'agent', 'alice', 'bob', 'carol' ] - const users = await get_users(client, aliases) + const users = await get_members(client, aliases) const [ agent, ...members ] = users @@ -79,8 +78,8 @@ export default async function (client : CoreClient, t : Test) { /* ------------------- [ Contract ] ------------------- */ const cid = Buff.random().hex - const session = create_session(agent.client.signer, cid) - const contract = create_contract(cid, proposal, session) + const session = create_session(agent.signer, cid) + const contract = create_contract({ cid, proposal, agent : session }) if (VERBOSE) { console.log(banner('contract')) @@ -97,7 +96,7 @@ export default async function (client : CoreClient, t : Test) { const return_ctx = get_return_ctx(tmpl.return_tx) const { pubkey, sequence } = return_ctx const { txid, vout } = return_ctx.tx.vin[0] - const deposit_key = agent.client.signer.pubkey + const deposit_key = agent.signer.pubkey const deposit_ctx = get_deposit_ctx(deposit_key, pubkey, sequence) const data = await client.get_txinput(txid, vout) assert.exists(data) @@ -105,11 +104,11 @@ export default async function (client : CoreClient, t : Test) { verify_deposit(deposit_ctx, return_ctx, spendout) const dpid = Buff.random(32).hex const state = get_spend_state(sequence, data.status) - const session = create_session(agent.client.signer, dpid) + const session = create_session(agent.signer, dpid) const agent_pn = session.agent_pn const deposit = create_deposit({ ...deposit_ctx, dpid, agent_pn, ...tmpl, ...spendout, ...state }) // const deposit = register_deposit(deposit_ctx, dep_id, pnonce, tmpl, spendout, state) - verify_covenant(deposit_ctx, contract, deposit, agent.client.signer, agent.client.signer) + verify_covenant(deposit_ctx, contract, deposit, agent.signer, agent.signer) return deposit }) @@ -129,12 +128,12 @@ export default async function (client : CoreClient, t : Test) { /* ------------------- [ Evaluation ] ------------------- */ - const signer = members[0].client.signer + const signer = members[0].signer const config = { action : 'dispute', - method: 'sign', - path : 'payout', + method : 'sign', + path : 'payout', pubkey : signer.pubkey } @@ -175,7 +174,7 @@ export default async function (client : CoreClient, t : Test) { const { result } = state if (result !== null) { - const txdata = create_settlment(agent.client.signer as Signer, contract, funds, result) + const txdata = create_settlment(agent.signer as Signer, contract, funds, result) if (VERBOSE) { console.log(banner('closing tx')) diff --git a/test/src/types.ts b/test/src/types.ts index f67a132a..4df0af5c 100644 --- a/test/src/types.ts +++ b/test/src/types.ts @@ -1,8 +1,9 @@ -import { CoreWallet } from '@cmdcode/core-cmd' -import { EscrowSigner } from '@/client/class/signer.js' +import { CoreWallet } from '@cmdcode/core-cmd' +import { Wallet } from '@cmdcode/signer' +import { SignerAPI } from '@/types/index.js' -export interface EscrowMember { - label : string, - client : EscrowSigner, - wallet : CoreWallet +export interface CoreSigner { + core : CoreWallet, + signer : SignerAPI, + wallet : Wallet } diff --git a/test/src/vectors/basic_escrow.ts b/test/src/vectors/basic_escrow.ts index 28cdadb2..c329e1b3 100644 --- a/test/src/vectors/basic_escrow.ts +++ b/test/src/vectors/basic_escrow.ts @@ -1,10 +1,10 @@ import { ProposalData } from '@scrow/core' -import { EscrowMember } from '../types.js' +import { CoreSigner } from '../types.js' const NETWORK = 'regtest' export async function get_proposal ( - members : EscrowMember[] + members : CoreSigner[] ) : Promise { const [ alice, bob, carol ] = members return { @@ -14,16 +14,16 @@ export async function get_proposal ( members : [], network : NETWORK, paths : [ - [ 'payout', 90000, await bob.wallet.new_address ], - [ 'return', 90000, await alice.wallet.new_address ] + [ 'payout', 90000, await bob.core.new_address ], + [ 'return', 90000, await alice.core.new_address ] ], payments : [ - [ 10000, await bob.wallet.new_address ] + [ 10000, await bob.core.new_address ] ], programs : [ - [ 'sign', 'dispute', 'payout', 1, alice.client.pubkey ], - [ 'sign', 'resolve', '*', 1, carol.client.pubkey ], - [ 'sign', 'close|resolve', '*', 2, alice.client.pubkey, bob.client.pubkey ] + [ 'sign', 'dispute', 'payout', 1, alice.signer.pubkey ], + [ 'sign', 'resolve', '*', 1, carol.signer.pubkey ], + [ 'sign', 'close|resolve', '*', 2, alice.signer.pubkey, bob.signer.pubkey ] ], schedule: [ [ 7200, 'close', 'payout|return' ]