From df81d85231f3a602ec629391c258ec0d40f6f88c Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sat, 18 May 2024 17:53:57 -0500 Subject: [PATCH 01/27] update --- README.md | 2 +- src/client/api/client/contract.ts | 9 +- src/client/api/client/deposit.ts | 3 - src/client/api/client/vm.ts | 3 - src/core/lib/tx.ts | 6 +- src/core/module/account/data.ts | 4 +- src/core/module/contract/data.ts | 54 +++++++++--- src/core/module/contract/state.ts | 79 +++++++++++++---- src/core/module/contract/util.ts | 51 ++++------- src/core/module/deposit/data.ts | 51 +++++------ src/core/module/deposit/state.ts | 68 ++++++++++++--- src/core/module/deposit/util.ts | 57 +++++-------- src/core/module/witness/data.ts | 18 ++-- src/core/schema/account.ts | 2 +- src/core/schema/contract.ts | 43 ++++++---- src/core/schema/deposit.ts | 25 ++++-- src/core/schema/tx.ts | 18 ++-- src/core/schema/witness.ts | 16 ++-- src/core/types/account.ts | 2 +- src/core/types/contract.ts | 45 +++++++--- src/core/types/deposit.ts | 28 ++++--- src/core/types/signer.ts | 5 -- src/core/types/tx.ts | 8 +- src/core/types/witness.ts | 4 +- src/core/util/notarize.ts | 22 ++--- src/core/util/oracle.ts | 2 +- src/core/validation/account.ts | 4 +- src/core/validation/contract.ts | 135 ++++++++++++++++++------------ src/core/validation/deposit.ts | 17 ++-- src/core/validation/witness.ts | 6 +- test/src/e2e/return.test.ts | 2 +- test/src/e2e/settle.test.ts | 18 ++-- 32 files changed, 474 insertions(+), 333 deletions(-) diff --git a/README.md b/README.md index 92b627fa..71b9bcca 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ Each contract specifies a "script engine" or interpreter to use when evaluating When the contract activates, a virtual machine is created using the specified engine. A hash-chain is also created, starting with the machine's identifier (vmid). ```ts -vm_state: { +vmdata: { commits : [], error : null, head : 'b70704c41e27d5f35a11ae7c6e5976501aa1380195714007197d7f47934dcf69', diff --git a/src/client/api/client/contract.ts b/src/client/api/client/contract.ts index e7c2cbd2..65cd2b06 100644 --- a/src/client/api/client/contract.ts +++ b/src/client/api/client/contract.ts @@ -80,12 +80,11 @@ function read_contract_api (client : EscrowClient) { */ function list_contract_api (client : EscrowClient) { return async ( - pubkey : string, - token : string + token : string ) : Promise> => { // Define the request url. const host = client.server_url - const url = `${host}/api/contract/list?pk=${pubkey}` + const url = `${host}/api/contract/list` // Define the request config. const init = { method : 'GET', @@ -117,9 +116,7 @@ function list_funds_api (client : EscrowClient) { /** * Cancel a contract that is not active. */ -function cancel_contract_api ( - client : EscrowClient -) { +function cancel_contract_api (client : EscrowClient) { return async ( cid : string, token : string diff --git a/src/client/api/client/deposit.ts b/src/client/api/client/deposit.ts index ceed1a0a..490155c4 100644 --- a/src/client/api/client/deposit.ts +++ b/src/client/api/client/deposit.ts @@ -37,11 +37,8 @@ function read_deposit_api (client : EscrowClient) { function list_deposit_api (client : EscrowClient) { return async ( - pubkey : string, token : string ) : Promise> => { - // Validate the pubkey. - assert.is_hash(pubkey) // Define the request url. const host = client.server_url const url = `${host}/api/deposit/list` diff --git a/src/client/api/client/vm.ts b/src/client/api/client/vm.ts index b8387c73..6e47810c 100644 --- a/src/client/api/client/vm.ts +++ b/src/client/api/client/vm.ts @@ -17,11 +17,8 @@ import { EscrowClient } from '../../class/client.js' */ function list_machines_api (client : EscrowClient) { return async ( - pubkey : string, token : string ) : Promise> => { - // Validate the pubkey. - assert.is_hash(pubkey) // Define the request url. const host = client.server_url const url = `${host}/api/vm/list` diff --git a/src/core/lib/tx.ts b/src/core/lib/tx.ts index c332bee9..2b2c1c40 100644 --- a/src/core/lib/tx.ts +++ b/src/core/lib/tx.ts @@ -63,6 +63,7 @@ export function INIT_SPEND_STATE () : TxSpendState { return { spent : false as const, spent_at : null, + spent_sig : null, spent_txhex : null, spent_txid : null } @@ -73,8 +74,9 @@ export function INIT_SPEND_STATE () : TxSpendState { */ export function INIT_SETTLE_STATE () : TxSettleState { return { - settled : false as const, - settled_at : null + settled : false as const, + settled_at : null, + settled_sig : null } } diff --git a/src/core/module/account/data.ts b/src/core/module/account/data.ts index b4f1b779..2cf26840 100644 --- a/src/core/module/account/data.ts +++ b/src/core/module/account/data.ts @@ -44,7 +44,7 @@ export function create_account ( // Compute the id for the account data. const account_id = util.get_account_id(deposit_addr, account_hash, server_pk, created_at, server_tkn) // Sign the account identifier. - const server_sig = signer.sign(account_id) + const account_sig = signer.sign(account_id) // Return the complete account data object. - return sort_record({ ...request, account_hash, account_id, created_at, server_sig, deposit_addr, server_pk, server_tkn }) + return sort_record({ ...request, account_id, account_hash, account_sig, created_at, deposit_addr, server_pk, server_tkn }) } diff --git a/src/core/module/contract/data.ts b/src/core/module/contract/data.ts index dbddbc54..986aab85 100644 --- a/src/core/module/contract/data.ts +++ b/src/core/module/contract/data.ts @@ -23,7 +23,7 @@ import { get_contract_id, get_deadline, get_max_vout_size, - update_contract + notarize_contract } from './util.js' /** @@ -48,8 +48,10 @@ export function create_contract ( const subtotal = proposal.value + get_pay_total(fees) // Calculate the vout size of the tx output. const tx_bsize = get_max_vout_size(outputs) + // Calculate the transaction fees. + const tx_fees = tx_bsize * feerate // Return a completed contract. - const template = { + const template = { ...GET_PUBLISH_STATE(), cid, created_at, @@ -65,10 +67,14 @@ export function create_contract ( subtotal, terms : sort_record(proposal), tx_bsize, + tx_fees, + tx_total : subtotal + tx_fees, + tx_vsize : tx_bsize, updated_at : created_at, vin_txfee : feerate * SPEND_TXIN_SIZE } - return update_contract(template, signer, 'published') + const proof = notarize_contract(template, signer, 'published') + return sort_record({ ...template, created_sig: proof }) } export function cancel_contract ( @@ -80,35 +86,52 @@ export function cancel_contract ( const status = 'canceled' as ContractStatus const changes = { canceled, canceled_at } const updated = { ...contract, ...changes, status, updated_at: canceled_at } - return update_contract(updated, signer, status) + const proof = notarize_contract(updated, signer, status) + return sort_record({ ...updated, canceled_sig: proof }) } export function add_contract_funds ( contract : ContractData, deposit : DepositData ) { - let { funds_pend, funds_conf, vin_count } = contract + let { funds_pend, funds_conf, vin_count, vin_txfee, tx_fees, tx_total, tx_vsize } = contract if (deposit.confirmed) { funds_conf += deposit.utxo.value + tx_fees += vin_txfee + tx_total += deposit.utxo.value + vin_txfee + tx_vsize += SPEND_TXIN_SIZE vin_count += 1 } else { funds_pend += deposit.utxo.value } - return sort_record({ ...contract, funds_pend, funds_conf, vin_count }) + return sort_record({ ...contract, funds_pend, funds_conf, tx_fees, tx_total, tx_vsize, vin_count }) +} + +export function confirm_contract_funds ( + contract : ContractData, + deposit : DepositData +) { + assert.ok(deposit.confirmed, 'deposit is not confirmed') + const updated = add_contract_funds(contract, deposit) + updated.funds_pend -= deposit.utxo.value + return updated } export function rem_contract_funds ( contract : ContractData, deposit : DepositData ) { - let { funds_pend, funds_conf, vin_count } = contract + let { funds_pend, funds_conf, vin_count, vin_txfee, tx_fees, tx_total, tx_vsize } = contract if (deposit.confirmed) { funds_conf -= deposit.utxo.value + tx_fees -= vin_txfee + tx_total -= deposit.utxo.value + vin_txfee + tx_vsize -= SPEND_TXIN_SIZE vin_count -= 1 } else { funds_pend -= deposit.utxo.value } - return sort_record({ ...contract, funds_pend, funds_conf, vin_count }) + return sort_record({ ...contract, funds_pend, funds_conf, tx_fees, tx_total, tx_vsize, vin_count }) } export function secure_contract ( @@ -136,7 +159,8 @@ export function secure_contract ( const tabs = { vin_count, funds_pend: 0, funds_conf } const changes = { secured, effective_at, tx_fees, tx_total, tx_vsize } const updated = { ...contract, ...tabs, ...changes, status, updated_at } - return update_contract(updated, signer, status) + const proof = notarize_contract(updated, signer, status) + return sort_record({ ...updated, secured_sig: proof }) } /** @@ -158,7 +182,8 @@ export function activate_contract ( // Return the new contract and vm config. const changes = { activated, active_at, expires_at, engine_head, engine_vmid } const updated = { ...contract, ...changes, status, updated_at } - return update_contract(updated, signer, status) + const proof = notarize_contract(updated, signer, status) + return sort_record({ ...updated, active_sig: proof }) } export function close_contract ( @@ -176,7 +201,8 @@ export function close_contract ( const updated_at = closed_at const changes = { closed, closed_at, engine_head, engine_vout } const updated = { ...contract, ...changes, status, updated_at } - return update_contract(updated, signer, status) + const proof = notarize_contract(updated, signer, status) + return sort_record({ ...updated, closed_sig: proof }) } export function spend_contract ( @@ -191,7 +217,8 @@ export function spend_contract ( const updated_at = spent_at const changes = { spent, spent_txhex, spent_txid, spent_at } const updated = { ...contract, ...changes, status, updated_at } - return update_contract(updated, signer, status) + const proof = notarize_contract(updated, signer, status) + return sort_record({ ...updated, spent_sig: proof }) } export function settle_contract ( @@ -203,5 +230,6 @@ export function settle_contract ( const status = 'settled' as ContractStatus const changes = { settled, settled_at } const updated = { ...contract, ...changes, status, updated_at: settled_at } - return update_contract(updated, signer, status) + const proof = notarize_contract(updated, signer, status) + return sort_record({ ...updated, settled_sig: proof }) } diff --git a/src/core/module/contract/state.ts b/src/core/module/contract/state.ts index d5f54179..f3c40129 100644 --- a/src/core/module/contract/state.ts +++ b/src/core/module/contract/state.ts @@ -1,29 +1,31 @@ -import { ContractData, ContractStatus } from '@/core/types/index.js' -import { INIT_SPEND_STATE, INIT_SETTLE_STATE } from '@/core/lib/tx.js' -import { assert, sort_record } from '@/core/util/index.js' +import { ContractData, ContractPreImage, ContractStatus } from '@/core/types/index.js' +import { INIT_SPEND_STATE, INIT_SETTLE_STATE } from '@/core/lib/tx.js' +import { assert, parse_proof, sort_record } from '@/core/util/index.js' export const INIT_PUBLISH_STATE = () => { return { funds_conf : 0, funds_pend : 0, + tx_fees : 0, + tx_total : 0, + tx_vsize : 0, vin_count : 0 } } export const INIT_CANCEL_STATE = () => { return { - canceled : false as const, - canceled_at : null + canceled : false as const, + canceled_at : null, + canceled_sig : null } } export const INIT_FUNDING_STATE = () => { return { secured : false as const, - effective_at : null, - tx_fees : null, - tx_total : null, - tx_vsize : null + secured_sig : null, + effective_at : null } } @@ -31,6 +33,7 @@ export const INIT_ACTIVE_STATE = () => { return { activated : false as const, active_at : null, + active_sig : null, engine_head : null, engine_vmid : null, expires_at : null @@ -41,6 +44,7 @@ export const INIT_CLOSE_STATE = () => { return { closed : false as const, closed_at : null, + closed_sig : null, engine_vout : null } } @@ -52,14 +56,16 @@ export const GET_PUBLISH_STATE = () => { ...INIT_CANCEL_STATE(), ...INIT_PUBLISH_STATE(), ...INIT_FUNDING_STATE(), - status : 'published' as ContractStatus + created_sig : null, + status : 'published' as ContractStatus } } export const GET_CANCEL_STATE = () => { return { ...INIT_PUBLISH_STATE(), - status : 'canceled' as ContractStatus + canceled_sig : null, + status : 'canceled' as ContractStatus } } @@ -67,7 +73,8 @@ export const GET_FUNDING_STATE = () => { return { ...GET_ACTIVE_STATE(), ...INIT_ACTIVE_STATE(), - status : 'secured' as ContractStatus + secured_sig : null, + status : 'secured' as ContractStatus } } @@ -75,6 +82,7 @@ export const GET_ACTIVE_STATE = () => { return { ...GET_CLOSE_STATE(), ...INIT_CLOSE_STATE(), + active_sig : null, engine_head : null, status : 'active' as ContractStatus } @@ -84,22 +92,31 @@ export const GET_CLOSE_STATE = () => { return { ...GET_SPEND_STATE(), ...INIT_SPEND_STATE(), - status : 'closed' as ContractStatus + closed_sig : null, + status : 'closed' as ContractStatus } } export const GET_SPEND_STATE = () => { return { ...INIT_SETTLE_STATE(), - status : 'spent' as ContractStatus + spent_sig : null, + status : 'spent' as ContractStatus + } +} + +export const GET_SETTLE_STATE = () => { + return { + settled_sig : null, + status : 'settled' as ContractStatus } } export function get_contract_state ( - contract : ContractData, + contract : ContractPreImage, status : ContractStatus ) { - const { sigs, updated_at, ...rest } = contract + const { updated_at, ...rest } = contract let state, stamp switch (status) { case 'published': @@ -127,7 +144,7 @@ export function get_contract_state ( stamp = contract.spent_at break case 'settled': - state = rest + state = { ...rest, ...GET_SETTLE_STATE() } stamp = contract.settled_at break default: @@ -136,3 +153,31 @@ export function get_contract_state ( assert.exists(stamp) return { content: JSON.stringify(sort_record(state)), created_at: stamp } } + +export function get_contract_proof ( + contract : ContractData, + status : ContractStatus +) : { id : string, sig : string } { + let sig + switch (status) { + case 'published': + sig = contract.created_sig; break + case 'canceled': + sig = contract.canceled_sig; break + case 'secured': + sig = contract.secured_sig; break + case 'active': + sig = contract.active_sig; break + case 'closed': + sig = contract.closed_sig; break + case 'spent': + sig = contract.spent_sig; break + case 'settled': + sig = contract.settled_sig; break + default: + throw new Error('unrecognized signature status: ' + status) + } + assert.exists(sig, 'contract signature returned null for status: ' + status) + const proof = parse_proof(sig) + return { id: proof[0], sig: proof[1] } +} diff --git a/src/core/module/contract/util.ts b/src/core/module/contract/util.ts index 0900b94f..66140e5f 100644 --- a/src/core/module/contract/util.ts +++ b/src/core/module/contract/util.ts @@ -1,25 +1,24 @@ import { Buff } from '@cmdcode/buff' import { decode_tx, encode_tx } from '@scrow/tapscript/tx' import { verify_sig } from '@cmdcode/crypto-tools/signer' -import { assert, sort_record } from '@/core/util/index.js' -import { get_contract_state } from './state.js' +import { assert } from '@/core/util/index.js' +import { get_proof_id } from '@/core/util/notarize.js' import { CONTRACT_KIND, SPEND_TXIN_SIZE } from '@/core/const.js' import { - get_proof_id, - parse_proof, - update_proof -} from '@/core/util/notarize.js' + get_contract_proof, + get_contract_state +} from './state.js' import { ContractData, + ContractPreImage, ContractStatus, DepositData, FundingData, NoteTemplate, PaymentEntry, - ProofEntry, ProposalData, SignerAPI, SpendTemplate @@ -181,7 +180,7 @@ export function get_contract_balance (contract : ContractData) { } export function get_contract_preimg ( - contract : ContractData, + contract : ContractPreImage, status : ContractStatus ) : NoteTemplate { const { cid, server_pk: pubkey } = contract @@ -196,45 +195,31 @@ export function get_contract_note ( status : ContractStatus ) { const tmpl = get_contract_preimg(contract, status) - const proof = contract.sigs.find(e => e[0] === status) - assert.exists(proof, 'signature no found for status: ' + status) - const id = proof[1].slice(0, 64) - const sig = proof[1].slice(64) - return { ...tmpl, id, sig } + const proof = get_contract_proof(contract, status) + return { ...tmpl, ...proof } } export function notarize_contract ( - contract : ContractData, + contract : ContractPreImage, signer : SignerAPI, status : ContractStatus -) : ProofEntry { +) : string { const img = get_contract_preimg(contract, status) const dig = get_proof_id(img) const sig = signer.sign(dig) - return [ contract.status, Buff.join([ dig, sig ]).hex ] + return Buff.join([ dig, sig ]).hex } -export function update_contract ( +export function verify_contract_sig ( contract : ContractData, - signer : SignerAPI, + pubkey : string, status : ContractStatus ) { - const proof = notarize_contract(contract, signer, status) - contract.sigs = update_proof(contract.sigs, proof) - return sort_record(contract) -} - -export function verify_contract_sig ( - contract : ContractData, - pubkey : string, - signature : ProofEntry -) { - const [ status, proof ] = signature - const [ id, sig ] = parse_proof(proof) const pub = contract.server_pk + const prf = get_contract_proof(contract, status) const img = get_contract_preimg(contract, status) const dig = get_proof_id(img) - assert.ok(pubkey === pub, 'pubkey does not match: ' + status) - assert.ok(dig === id, 'digest does not match: ' + status) - assert.ok(verify_sig(sig, id, pub), 'invalid signature: ' + status) + assert.ok(pubkey === pub, 'pubkey does not match: ' + status) + assert.ok(dig === prf.id, 'digest does not match: ' + status) + assert.ok(verify_sig(prf.sig, prf.id, pub), 'invalid signature: ' + status) } diff --git a/src/core/module/deposit/data.ts b/src/core/module/deposit/data.ts index 918d5334..040e53a2 100644 --- a/src/core/module/deposit/data.ts +++ b/src/core/module/deposit/data.ts @@ -1,5 +1,5 @@ -import { assert, now, sort_record } from '../../util/index.js' -import { GET_REGISTER_STATE } from './state.js' +import { assert, now, sort_record } from '../../util/index.js' +import { GET_REGISTER_STATE, INIT_LOCK_STATE } from './state.js' import { DepositData, @@ -23,7 +23,7 @@ import { import { get_deposit_id, - update_deposit + notarize_deposit } from './util.js' /** @@ -51,13 +51,13 @@ export function create_deposit ( sigs : [], updated_at : created_at } - return update_deposit(template, signer, 'registered') + const proof = notarize_deposit(template, signer, 'registered') + return sort_record({ ...template, created_sig: proof }) } export function confirm_deposit ( deposit : DepositData, - txstate : TxConfirmState, - signer : SignerAPI + txstate : TxConfirmState ) : DepositData { assert.ok(!deposit.confirmed, 'deposit is already confirmed') assert.ok(txstate.confirmed, 'transaction is not confirmed') @@ -65,7 +65,7 @@ export function confirm_deposit ( ? 'confirmed' as DepositStatus : 'locked' as DepositStatus const updated = { ...deposit, ...txstate, status, updated_at: txstate.block_time } - return update_deposit(updated, signer, status) + return sort_record(updated) } export function close_deposit ( @@ -78,12 +78,13 @@ export function close_deposit ( assert.ok(!deposit.locked, 'deposit is already locked') assert.ok(!deposit.closed, 'deposit is already closed') assert.ok(!deposit.spent, 'deposit is already spent') - const return_txid = get_txid(txhex) + const txid = get_txid(txhex) const closed = true as const const status = 'closed' as DepositStatus - const changes = { closed, closed_at, return_txid, return_txhex: txhex } + const changes = { closed, closed_at, return_txid: txid, return_txhex: txhex } const updated = { ...deposit, ...changes, status, updated_at: closed_at } - return update_deposit(updated, signer, status) + const proof = notarize_deposit(updated, signer, status) + return sort_record({ ...updated, closed_sig: proof }) } export function lock_deposit ( @@ -99,7 +100,8 @@ export function lock_deposit ( const status = 'locked' as DepositStatus const changes = { locked, locked_at, covenant } const updated = { ...deposit, ...changes, status, updated_at: locked_at } - return update_deposit(updated, signer, status) + const proof = notarize_deposit(updated, signer, status) + return sort_record({ ...updated, locked_sig: proof }) } export function release_deposit ( @@ -109,28 +111,28 @@ export function release_deposit ( assert.ok(deposit.locked, 'deposit is not locked') assert.ok(!deposit.closed, 'deposit is already closed') assert.ok(!deposit.spent, 'deposit is already spent') - const sigs = deposit.sigs.filter(e => e[0] !== 'locked') const status = (deposit.confirmed) ? 'confirmed' as DepositStatus : 'registered' as DepositStatus - const changes = { locked: false as const, locked_at: null, covenant: null, sigs } + const changes = INIT_LOCK_STATE() return sort_record({ ...deposit, ...changes, status, updated_at }) } export function spend_deposit ( - deposit : DepositData, - spent_txhex : string, - signer : SignerAPI, - spent_at = now() + deposit : DepositData, + txhex : string, + signer : SignerAPI, + spent_at = now() ) : DepositData { assert.ok(deposit.confirmed, 'deposit is not confirmed') assert.ok(!deposit.spent, 'deposit is already spent') - const spent = true as const - const spent_txid = get_txid(spent_txhex) - const status = 'spent' as DepositStatus - const changes = { spent, spent_txhex, spent_txid, spent_at } - const updated = { ...deposit, ...changes, status, updated_at: spent_at } - return update_deposit(updated, signer, status) + const spent = true as const + const txid = get_txid(txhex) + const status = 'spent' as DepositStatus + const changes = { spent, spent_txhex: txhex, spent_txid: txid, spent_at } + const updated = { ...deposit, ...changes, status, updated_at: spent_at } + const proof = notarize_deposit(updated, signer, status) + return sort_record({ ...updated, spent_sig: proof }) } export function settle_deposit ( @@ -144,5 +146,6 @@ export function settle_deposit ( const status = 'settled' as DepositStatus const changes = { settled, settled_at } const updated = { ...deposit, ...changes, status, updated_at: settled_at } - return update_deposit(updated, signer, status) + const proof = notarize_deposit(updated, signer, status) + return sort_record({ ...updated, settled_sig: proof }) } diff --git a/src/core/module/deposit/state.ts b/src/core/module/deposit/state.ts index a5b1a1bb..92ab7d0c 100644 --- a/src/core/module/deposit/state.ts +++ b/src/core/module/deposit/state.ts @@ -1,18 +1,24 @@ -import { DepositData, DepositStatus } from '@/core/types/index.js' +import { sort_record } from '@/core/util/base.js' +import { assert, parse_proof } from '@/core/util/index.js' + +import { + DepositData, + DepositPreImage, + DepositStatus +} from '@/core/types/index.js' import { INIT_CONF_STATE, INIT_SPEND_STATE, INIT_SETTLE_STATE } from '@/core/lib/tx.js' -import { sort_record } from '@/core/util/base.js' -import { assert } from '@/core/util/index.js' export const INIT_LOCK_STATE = () => { return { - locked : false as const, - locked_at : null, - covenant : null + locked : false as const, + locked_at : null, + locked_sig : null, + covenant : null } } @@ -20,6 +26,7 @@ export const INIT_CLOSE_STATE = () => { return { closed : false as const, closed_at : null, + closed_sig : null, return_txhex : null, return_txid : null } @@ -32,7 +39,8 @@ export const GET_REGISTER_STATE = () => { ...INIT_LOCK_STATE(), ...INIT_SPEND_STATE(), ...INIT_SETTLE_STATE(), - status : 'registered' as DepositStatus + created_sig : null, + status : 'registered' as DepositStatus } } @@ -47,7 +55,8 @@ export const GET_CONFIRMED_STATE = () => { export const GET_CLOSED_STATE = () => { return { ...INIT_SETTLE_STATE(), - status : 'closed' as DepositStatus + closed_sig : null, + status : 'closed' as DepositStatus } } @@ -56,22 +65,31 @@ export const GET_LOCKED_STATE = () => { ...GET_SPEND_STATE(), ...INIT_SPEND_STATE(), ...INIT_CONF_STATE(), - status : 'locked' as DepositStatus + locked_sig : null, + status : 'locked' as DepositStatus } } export const GET_SPEND_STATE = () => { return { ...INIT_SETTLE_STATE(), - status : 'spent' as DepositStatus + spent_sig : null, + status : 'spent' as DepositStatus + } +} + +export const GET_SETTLE_STATE = () => { + return { + settled_sig : null, + status : 'settled' as DepositStatus } } export function get_deposit_state ( - deposit : DepositData, + deposit : DepositPreImage, status : DepositStatus ) { - const { sigs, updated_at, ...rest } = deposit + const { updated_at, ...rest } = deposit let state, stamp switch (status) { case 'registered': @@ -95,7 +113,7 @@ export function get_deposit_state ( stamp = deposit.spent_at break case 'settled': - state = rest + state = { ...rest, ...GET_SETTLE_STATE() } stamp = deposit.settled_at break default: @@ -104,3 +122,27 @@ export function get_deposit_state ( assert.exists(stamp) return { content: JSON.stringify(sort_record(state)), created_at: stamp } } + +export function get_deposit_proof ( + deposit : DepositData, + status : DepositStatus +) : { id : string, sig : string } { + let sig + switch (status) { + case 'registered': + sig = deposit.created_sig; break + case 'locked': + sig = deposit.locked_sig; break + case 'closed': + sig = deposit.closed_sig; break + case 'spent': + sig = deposit.spent_sig; break + case 'settled': + sig = deposit.settled_sig; break + default: + throw new Error('unrecognized signature status: ' + status) + } + assert.exists(sig, 'deposit signature returned null for status: ' + status) + const proof = parse_proof(sig) + return { id: proof[0], sig: proof[1] } +} diff --git a/src/core/module/deposit/util.ts b/src/core/module/deposit/util.ts index a8aad773..1c5c4afd 100644 --- a/src/core/module/deposit/util.ts +++ b/src/core/module/deposit/util.ts @@ -1,19 +1,18 @@ -import { Buff } from '@cmdcode/buff' -import { verify_sig } from '@cmdcode/crypto-tools/signer' -import { INIT_CONF_STATE } from '@/core/lib/tx.js' -import { DEPOSIT_KIND } from '@/core/const.js' -import { sort_record } from '@/core/util/base.js' -import { get_deposit_state } from './state.js' -import * as assert from '@/core/util/assert.js' +import { Buff } from '@cmdcode/buff' +import { verify_sig } from '@cmdcode/crypto-tools/signer' +import { INIT_CONF_STATE } from '@/core/lib/tx.js' +import { DEPOSIT_KIND } from '@/core/const.js' +import { get_proof_id } from '@/core/util/notarize.js' +import * as assert from '@/core/util/assert.js' -import { get_proof_id, parse_proof, update_proof } from '@/core/util/notarize.js' +import { get_deposit_proof, get_deposit_state } from './state.js' import { DepositData, + DepositPreImage, DepositStatus, NoteTemplate, OracleTxSpendData, - ProofEntry, SignerAPI, TxConfirmState } from '@/core/types/index.js' @@ -45,11 +44,11 @@ export function get_confirm_state ( } export function get_deposit_preimg ( - deposit : DepositData, + deposit : DepositPreImage, status : DepositStatus ) : NoteTemplate { const { dpid, server_pk: pubkey } = deposit - const { content, created_at } = get_deposit_state(deposit, status) + const { content, created_at } = get_deposit_state(deposit, status) const kind = DEPOSIT_KIND const tags = [ [ 'i', dpid ] ] return { content, created_at, kind, pubkey, tags } @@ -60,45 +59,31 @@ export function get_deposit_note ( status : DepositStatus ) { const tmpl = get_deposit_preimg(deposit, status) - const proof = deposit.sigs.find(e => e[0] === status) - assert.exists(proof, 'signature no found for status: ' + status) - const id = proof[1].slice(0, 64) - const sig = proof[1].slice(64) - return { ...tmpl, id, sig } + const proof = get_deposit_proof(deposit, status) + return { ...tmpl, ...proof } } export function notarize_deposit ( - deposit : DepositData, + deposit : DepositPreImage, signer : SignerAPI, status : DepositStatus -) : ProofEntry { +) : string { const img = get_deposit_preimg(deposit, status) const dig = get_proof_id(img) const sig = signer.sign(dig) - return [ status, Buff.join([ dig, sig ]).hex ] + return Buff.join([ dig, sig ]).hex } -export function update_deposit ( +export function verify_deposit_sig ( deposit : DepositData, - signer : SignerAPI, + pubkey : string, status : DepositStatus ) { - const proof = notarize_deposit(deposit, signer, status) - deposit.sigs = update_proof(deposit.sigs, proof) - return sort_record(deposit) -} - -export function verify_deposit_sig ( - deposit : DepositData, - pubkey : string, - signature : ProofEntry -) { - const [ status, proof ] = signature - const [ id, sig ] = parse_proof(proof) const pub = deposit.server_pk + const prf = get_deposit_proof(deposit, status) const img = get_deposit_preimg(deposit, status) const dig = get_proof_id(img) - assert.ok(pubkey === pub, 'pubkey does not match: ' + status) - assert.ok(dig === id, 'digest does not match: ' + status) - assert.ok(verify_sig(sig, id, pub), 'invalid signature: ' + status) + assert.ok(pubkey === pub, 'pubkey does not match: ' + status) + assert.ok(dig === prf.id, 'digest does not match: ' + status) + assert.ok(verify_sig(prf.sig, prf.id, pub), 'invalid signature: ' + status) } diff --git a/src/core/module/witness/data.ts b/src/core/module/witness/data.ts index 3a5fa3a0..e56bfc33 100644 --- a/src/core/module/witness/data.ts +++ b/src/core/module/witness/data.ts @@ -18,13 +18,13 @@ export function create_receipt ( witness : WitnessData, receipt_at = now() ) : WitnessReceipt { - const server_pk = signer.pubkey - const vm_closed = data.closed - const vm_hash = data.head - const vm_output = data.output - const vm_step = data.step - const preimg = { ...witness, receipt_at, server_pk, vm_closed, vm_hash, vm_output, vm_step } - const receipt_id = get_receipt_id(preimg) - const server_sig = signer.sign(receipt_id) - return sort_record({ ...preimg, receipt_id, server_sig }) + const server_pk = signer.pubkey + const vm_closed = data.closed + const vm_hash = data.head + const vm_output = data.output + const vm_step = data.step + const preimg = { ...witness, receipt_at, server_pk, vm_closed, vm_hash, vm_output, vm_step } + const receipt_id = get_receipt_id(preimg) + const receipt_sig = signer.sign(receipt_id) + return sort_record({ ...preimg, receipt_id, receipt_sig }) } diff --git a/src/core/schema/account.ts b/src/core/schema/account.ts index 916e978a..744c6b04 100644 --- a/src/core/schema/account.ts +++ b/src/core/schema/account.ts @@ -34,6 +34,7 @@ const commit_req = register_req.extend({ covenant }) const data = z.object({ account_hash : hash, account_id : hash, + account_sig : nonce, created_at : stamp, deposit_pk : hash, deposit_addr : str, @@ -41,7 +42,6 @@ const data = z.object({ network, return_addr : str, server_pk : hash, - server_sig : nonce, server_tkn : token }) diff --git a/src/core/schema/contract.ts b/src/core/schema/contract.ts index 01385c81..a3644cdb 100644 --- a/src/core/schema/contract.ts +++ b/src/core/schema/contract.ts @@ -18,47 +18,45 @@ const publish_req = z.object({ /* ------------------- [ State Schema ] ------------------- */ const publish_info = z.object({ - canceled : bool, - canceled_at : stamp.nullable() + canceled : bool, + canceled_at : stamp.nullable(), + canceled_sig : hex.nullable() }) const ct_published = z.object({ - canceled : z.literal(false), - canceled_at : z.null() + canceled : z.literal(false), + canceled_at : z.null(), + canceled_sig : z.null() }) const ct_canceled = z.object({ - canceled : z.literal(true), - canceled_at : stamp + canceled : z.literal(true), + canceled_at : stamp, + canceled_sig : hex }) const funding_info = z.object({ secured : bool, - effective_at : stamp.nullable(), - tx_fees : num.nullable(), - tx_total : num.nullable(), - tx_vsize : num.nullable() + secured_sig : hex.nullable(), + effective_at : stamp.nullable() }) const ct_pending = z.object({ secured : z.literal(false), - effective_at : z.null(), - tx_fees : z.null(), - tx_total : z.null(), - tx_vsize : z.null() + secured_sig : z.null(), + effective_at : z.null() }) const ct_secured = z.object({ secured : z.literal(true), - effective_at : stamp, - tx_fees : num, - tx_total : num, - tx_vsize : num + secured_sig : hex, + effective_at : stamp }) const engine_info = z.object({ activated : bool, active_at : stamp.nullable(), + active_sig : hex.nullable(), engine_head : hash.nullable(), engine_vmid : hash.nullable(), expires_at : stamp.nullable() @@ -67,6 +65,7 @@ const engine_info = z.object({ const ct_active = z.object({ activated : z.literal(true), active_at : stamp, + active_sig : hex, engine_head : hash, engine_vmid : hash, expires_at : stamp @@ -75,6 +74,7 @@ const ct_active = z.object({ const ct_inactive = z.object({ activated : z.literal(false), active_at : z.null(), + active_sig : z.null(), engine_head : z.null(), engine_vmid : z.null(), expires_at : z.null() @@ -83,18 +83,21 @@ const ct_inactive = z.object({ const close_info = z.object({ closed : bool, closed_at : stamp.nullable(), + closed_sig : hex.nullable(), engine_vout : str.nullable() }) const ct_open = z.object({ closed : z.literal(false), closed_at : z.null(), + closed_sig : z.null(), engine_vout : z.null() }) const ct_closed = z.object({ closed : z.literal(true), closed_at : stamp, + closed_sig : hex, engine_vout : str.nullable() }) @@ -110,6 +113,7 @@ const close_state = z.discriminatedUnion('closed', [ ct_open, ct_close const base_data = z.object({ cid : hash, created_at : stamp, + created_sig : hex, deadline_at : stamp, endorsements : hex.array(), feerate : num, @@ -125,6 +129,9 @@ const base_data = z.object({ subtotal : num, terms : proposal.data, tx_bsize : num, + tx_fees : num, + tx_total : num, + tx_vsize : num, updated_at : stamp, vin_count : num, vin_txfee : num diff --git a/src/core/schema/deposit.ts b/src/core/schema/deposit.ts index ee362446..8314ebac 100644 --- a/src/core/schema/deposit.ts +++ b/src/core/schema/deposit.ts @@ -22,6 +22,7 @@ const close_req = z.object({ const close_info = z.object({ closed : bool, closed_at : stamp.nullable(), + closed_sig : hex.nullable(), return_txhex : hex.nullable(), return_txid : hash.nullable() }) @@ -29,6 +30,7 @@ const close_info = z.object({ const dp_open = z.object({ closed : z.literal(false), closed_at : z.null(), + closed_sig : z.null(), return_txhex : z.null(), return_txid : z.null() }) @@ -36,26 +38,30 @@ const dp_open = z.object({ const dp_closed = z.object({ closed : z.literal(true), closed_at : stamp, + closed_sig : hex, return_txhex : hex, return_txid : hash }) const lock_info = z.object({ - locked : bool, - locked_at : stamp.nullable(), - covenant : acct.covenant.nullable() + locked : bool, + locked_at : stamp.nullable(), + locked_sig : hex.nullable(), + covenant : acct.covenant.nullable() }) const dp_unlocked = z.object({ - locked : z.literal(false), - locked_at : z.null(), - covenant : z.null() + locked : z.literal(false), + locked_at : z.null(), + locked_sig : z.null(), + covenant : z.null() }) const dp_locked = z.object({ - locked : z.literal(true), - locked_at : stamp, - covenant : acct.covenant + locked : z.literal(true), + locked_at : stamp, + locked_sig : hex, + covenant : acct.covenant }) const lock_state = z.discriminatedUnion('locked', [ dp_locked, dp_unlocked ]) @@ -72,6 +78,7 @@ const base_data = z.object({ status, account_hash : hash, created_at : stamp, + created_sig : hex, dpid : hash, deposit_pk : hash, deposit_addr : str, diff --git a/src/core/schema/tx.ts b/src/core/schema/tx.ts index a09e4100..ed054b9b 100644 --- a/src/core/schema/tx.ts +++ b/src/core/schema/tx.ts @@ -30,6 +30,7 @@ const tx_unconfirmed = z.object({ const spend_info = z.object({ spent : z.boolean(), spent_at : stamp.nullable(), + spent_sig : hex.nullable(), spent_txhex : hex.nullable(), spent_txid : hash.nullable() }) @@ -37,6 +38,7 @@ const spend_info = z.object({ const tx_spent = z.object({ spent : z.literal(true), spent_at : stamp, + spent_sig : hex, spent_txhex : hex, spent_txid : hash }) @@ -44,23 +46,27 @@ const tx_spent = z.object({ const tx_unspent = z.object({ spent : z.literal(false), spent_at : z.null(), + spent_sig : z.null(), spent_txhex : z.null(), spent_txid : z.null() }) const settle_info = z.object({ - settled : z.boolean(), - settled_at : stamp.nullable() + settled : z.boolean(), + settled_at : stamp.nullable(), + settled_sig : hex.nullable() }) const tx_settled = z.object({ - settled : z.literal(true), - settled_at : stamp + settled : z.literal(true), + settled_at : stamp, + settled_sig : hex }) const tx_unsettled = z.object({ - settled : z.literal(false), - settled_at : z.null() + settled : z.literal(false), + settled_at : z.null(), + settled_sig : z.null() }) const confirm_state = z.discriminatedUnion('confirmed', [ tx_confirmed, tx_unconfirmed ]) diff --git a/src/core/schema/witness.ts b/src/core/schema/witness.ts index e51a7f36..6e5f9c32 100644 --- a/src/core/schema/witness.ts +++ b/src/core/schema/witness.ts @@ -17,14 +17,14 @@ const data = z.object({ }) const receipt = data.extend({ - receipt_at : stamp, - receipt_id : hash, - server_pk : hash, - server_sig : signature, - vm_closed : bool, - vm_hash : hash, - vm_output : str.nullable(), - vm_step : num + receipt_at : stamp, + receipt_id : hash, + receipt_sig : signature, + server_pk : hash, + vm_closed : bool, + vm_hash : hash, + vm_output : str.nullable(), + vm_step : num }) export default { data, receipt } diff --git a/src/core/types/account.ts b/src/core/types/account.ts index 79f7f574..430469f2 100644 --- a/src/core/types/account.ts +++ b/src/core/types/account.ts @@ -31,6 +31,7 @@ export interface AccountContext { export interface AccountData { account_hash : string account_id : string + account_sig : string created_at : number deposit_addr : string deposit_pk : string @@ -38,7 +39,6 @@ export interface AccountData { network : Network return_addr : string server_pk : string - server_sig : string server_tkn : string } diff --git a/src/core/types/contract.ts b/src/core/types/contract.ts index 19bd2cb9..96ab93b1 100644 --- a/src/core/types/contract.ts +++ b/src/core/types/contract.ts @@ -1,4 +1,5 @@ -import { ProofEntry } from './signer.js' +import { FundingData } from './deposit.js' +import { ScriptEngineAPI, VMData } from './machine.js' import { PaymentEntry, @@ -9,6 +10,7 @@ import { TxSettleState, TxSpendState } from './tx.js' +import { WitnessData } from './witness.js' export type ContractStatus = 'published' | // Contract is published and awaiting funds. @@ -36,40 +38,44 @@ export type ContractData = TxSpendState & TxSettleState +export type ContractSignatures = 'created_sig' | 'canceled_sig' | 'secured_sig' | 'active_sig' | + 'closed_sig' | 'spent_sig' | 'settled_sig' + +export type ContractPreImage = Omit + export type SpendTemplate = [ label : string, txhex : string ] interface ContractIsPublished { - canceled : false - canceled_at : null + canceled : false + canceled_at : null + canceled_sig : null } interface ContractIsCanceled { - canceled : true - canceled_at : number + canceled : true + canceled_at : number + canceled_sig : string } interface ContractIsSecured { secured : true + secured_sig : string effective_at : number - tx_fees : number - tx_vsize : number - tx_total : number } interface ContractIsPending { secured : false + secured_sig : null effective_at : null - tx_fees : null - tx_vsize : null - tx_total : null } interface ContractIsActive { activated : true active_at : number + active_sig : string engine_head : string engine_vmid : string expires_at : number @@ -78,6 +84,7 @@ interface ContractIsActive { interface ContractIsInactive { activated : false active_at : null + active_sig : null engine_head : null engine_vmid : null expires_at : null @@ -86,12 +93,14 @@ interface ContractIsInactive { interface ContractIsClosed { closed : true closed_at : number + closed_sig : string engine_vout : string | null } interface ContractIsOpen { closed : false closed_at : null + closed_sig : null engine_vout : null } @@ -106,9 +115,19 @@ export interface ContractCreateConfig { feerate : number } +export interface ContractVerifyConfig { + contract : ContractData + commits ?: WitnessData[] + engine ?: ScriptEngineAPI + funds ?: FundingData[] + pubkey : string + vmdata ?: VMData +} + export interface ContractBase { cid : string created_at : number + created_sig : string deadline_at : number endorsements : string[] fees : PaymentEntry[] @@ -119,11 +138,13 @@ export interface ContractBase { outputs : SpendTemplate[] prop_id : string server_pk : string - sigs : ProofEntry[] status : ContractStatus subtotal : number terms : ProposalData tx_bsize : number + tx_fees : number + tx_vsize : number + tx_total : number vin_count : number vin_txfee : number updated_at : number diff --git a/src/core/types/deposit.ts b/src/core/types/deposit.ts index a7706e26..771a8b61 100644 --- a/src/core/types/deposit.ts +++ b/src/core/types/deposit.ts @@ -1,6 +1,5 @@ import { Network } from './base.js' import { CovenantData } from './covenant.js' -import { ProofEntry } from './signer.js' import { TxConfirmState, @@ -14,6 +13,9 @@ export type CloseState = DepositIsClosed | DepositIsOpen export type DepositData = DepositBase & TxConfirmState & LockState & CloseState & TxSettleState & TxSpendState export type DepositStatus = 'registered' | 'confirmed' | 'closed' | 'locked' | 'spent' | 'settled' | 'expired' | 'error' +export type DepositSignatures = 'created_sig' | 'locked_sig' | 'closed_sig' | 'spent_sig' | 'settled_sig' +export type DepositPreImage = Omit + export type FundingData = TxConfirmState & TxSettleState & TxSpendState & { covenant : CovenantData | null status : DepositStatus @@ -26,27 +28,30 @@ export interface LockRequest { covenant : CovenantData } +export interface CloseRequest { + dpid : string + return_rate : number + return_psig : string +} + export interface DepositIsLocked { - covenant : CovenantData - locked : true - locked_at : number + covenant : CovenantData + locked : true + locked_at : number + locked_sig : string } export interface DepositIsUnlocked { covenant : null locked : false locked_at : null -} - -export interface CloseRequest { - dpid : string - return_rate : number - return_psig : string + locked_sig : null } export interface DepositIsClosed { closed : true closed_at : number + closed_sig : string return_txid : string return_txhex : string } @@ -54,6 +59,7 @@ export interface DepositIsClosed { export interface DepositIsOpen { closed : false closed_at : null + closed_sig : null return_txid : null return_txhex : null } @@ -66,6 +72,7 @@ export interface DepositConfig { export interface DepositBase { account_hash : string created_at : number + created_sig : string deposit_pk : string deposit_addr : string dpid : string @@ -77,7 +84,6 @@ export interface DepositBase { satpoint : string server_pk : string server_tkn : string - sigs : ProofEntry[] status : DepositStatus updated_at : number utxo : TxOutput diff --git a/src/core/types/signer.ts b/src/core/types/signer.ts index 3bad9762..111f74d7 100644 --- a/src/core/types/signer.ts +++ b/src/core/types/signer.ts @@ -4,11 +4,6 @@ import { MusigContext } from '@cmdcode/musig2' export type HmacTypes = '256' | '512' export type SignDevice = (msg : Bytes) => string -export type ProofEntry = [ - label : T, - proof : string -] - export type MusignDevice = ( ctx : MusigContext, aux : Bytes, diff --git a/src/core/types/tx.ts b/src/core/types/tx.ts index 005a2a2d..69c02ae5 100644 --- a/src/core/types/tx.ts +++ b/src/core/types/tx.ts @@ -23,16 +23,19 @@ export interface TxIsUnconfirmed { export interface TxIsSettled { settled : true settled_at : number + settled_sig : string } export interface TxNotSettled { - settled : false - settled_at : null + settled : false + settled_at : null + settled_sig : null } export interface TxIsSpent { spent : true spent_at : number + spent_sig : string spent_txhex : string spent_txid : string } @@ -40,6 +43,7 @@ export interface TxIsSpent { export interface TxIsUnspent { spent : false spent_at : null + spent_sig : null spent_txhex : null spent_txid : null } diff --git a/src/core/types/witness.ts b/src/core/types/witness.ts index 381739bc..aa3a15f1 100644 --- a/src/core/types/witness.ts +++ b/src/core/types/witness.ts @@ -35,6 +35,6 @@ export interface ReceiptPreImage extends WitnessData { } export interface WitnessReceipt extends ReceiptPreImage { - receipt_id : string - server_sig : string + receipt_id : string + receipt_sig : string } diff --git a/src/core/util/notarize.ts b/src/core/util/notarize.ts index 3203cc69..364f7555 100644 --- a/src/core/util/notarize.ts +++ b/src/core/util/notarize.ts @@ -1,12 +1,8 @@ -import { Buff } from '@cmdcode/buff' -import { SignedEvent } from '@cmdcode/signer' -import { sha256 } from '@cmdcode/crypto-tools/hash' -import * as assert from '@/core/util/assert.js' - -import { - NoteTemplate, - ProofEntry -} from '@/core/types/index.js' +import { Buff } from '@cmdcode/buff' +import { SignedEvent } from '@cmdcode/signer' +import { sha256 } from '@cmdcode/crypto-tools/hash' +import { NoteTemplate } from '@/core/types/index.js' +import * as assert from '@/core/util/assert.js' export function get_record_id (obj : T) : Buff { if ( @@ -31,14 +27,6 @@ export function parse_proof (proof : string) { return [ proof.slice(0, 64), proof.slice(64) ] } -export function update_proof ( - entries : ProofEntry[], - proof : ProofEntry -) : ProofEntry[] { - const filtered = entries.filter(e => e[0] !== proof[0]) - return [ ...filtered, proof ] -} - export function create_note ( content : string, kind : number, diff --git a/src/core/util/oracle.ts b/src/core/util/oracle.ts index 61c1cc43..81d1e095 100644 --- a/src/core/util/oracle.ts +++ b/src/core/util/oracle.ts @@ -146,7 +146,7 @@ export async function get_latest_utxo ( host : string, addr : string ) : Promise { - const utxos = await get_address_utxos(host, addr) + const utxos = await get_address_utxos(host, addr) return utxos.at(0) ?? null } diff --git a/src/core/validation/account.ts b/src/core/validation/account.ts index 36cc4fe7..2808f11d 100644 --- a/src/core/validation/account.ts +++ b/src/core/validation/account.ts @@ -120,7 +120,7 @@ export function verify_account_data ( account : AccountData, signer : SignerAPI ) { - const { account_id, created_at, deposit_addr, server_pk, server_sig, server_tkn } = account + const { account_id, created_at, account_sig, deposit_addr, server_pk, server_tkn } = account // Create a context object for the account. const ctx = get_account_ctx(account) // @@ -135,7 +135,7 @@ export function verify_account_data ( assert.ok(signer.pubkey === pk, 'deposit pubkey does not match signing device') assert.ok(ctx.deposit_addr === addr, 'deposit address does not match signing device') assert.ok(id === account_id, 'account id does not match computed id') - assert.ok(verify_sig(server_sig, id, server_pk), 'server signature is invalid') + assert.ok(verify_sig(account_sig, id, server_pk), 'server signature is invalid') } export function verify_session_token ( diff --git a/src/core/validation/contract.ts b/src/core/validation/contract.ts index 6e2076f2..134a2ad2 100644 --- a/src/core/validation/contract.ts +++ b/src/core/validation/contract.ts @@ -34,7 +34,9 @@ import { ProposalData, ServerPolicy, VMData, - ScriptEngineAPI + ScriptEngineAPI, + ContractVerifyConfig, + WitnessData } from '../types/index.js' import ContractSchema from '../schema/contract.js' @@ -45,6 +47,7 @@ import { validate_proposal_data, verify_proposal_data } from './proposal.js' +import { get_vm_config } from '../lib/vm.js' export function validate_publish_req ( contract : unknown @@ -69,6 +72,23 @@ export function verify_contract_req ( verify_endorsements(proposal, endorsements) } +export function verify_contract_publishing ( + contract : ContractData, + proposal : ProposalData +) { + const { created_at, fees } = contract + const out = create_spend_templates(proposal, fees) + const pid = get_proposal_id(proposal) + const cid = get_contract_id(out, pid, created_at) + assert.ok(pid === contract.prop_id, 'computed proposal id does not match contract') + assert.ok(cid === contract.cid, 'computed contract id id does not match contract') + for (const [ label, txhex ] of out) { + const tmpl = contract.outputs.find(e => e[0] === label) + assert.ok(tmpl !== undefined, 'output template does not exist for label: ' + label) + assert.ok(tmpl[1] === txhex, 'tx hex does not match output for label: ' + label) + } +} + export function verify_endorsements ( proposal : ProposalData, signatures : string[] = [] @@ -82,6 +102,33 @@ export function verify_endorsements ( } } +export function verify_contract ( + config : ContractVerifyConfig +) { + const { contract, commits, engine, funds, pubkey, vmdata } = config + verify_contract_data(contract) + verify_contract_sigs(contract, pubkey) + if (contract.secured) { + assert.exists(funds, 'you must provide a list of funds to verify') + verify_contract_funding(contract, funds) + } + if (contract.activated) { + assert.exists(vmdata, 'you must provide a vmdata object to verify.') + verify_contract_activation(contract, vmdata) + } + if (contract.closed) { + assert.exists(commits, 'you must provide a list of witness commits to verify.') + assert.exists(engine, 'you must provide a script engine to use for verification.') + assert.exists(vmdata, 'you must provide a vmdata object to verify.') + verify_contract_close(contract, vmdata) + } + if (contract.spent) { + assert.exists(funds, 'you must provide a list of funds to verify') + assert.exists(vmdata, 'you must provide a vmdata object to verify.') + verify_contract_spending(contract, funds, vmdata) + } +} + export function verify_contract_data (contract : ContractData) { const { created_at, outputs, terms } = contract const pid = get_proposal_id(terms) @@ -97,37 +144,15 @@ export function verify_contract_data (contract : ContractData) { export function verify_contract_sigs ( contract : ContractData, - pubkey : string + pubkey : string ) { - const labels = contract.sigs.map(e => e[0]) - - assert.ok(labels.includes('published'), 'contract signature missing: published') - assert.ok(!contract.canceled || labels.includes('canceled'), 'contract signature missing: canceled') - assert.ok(!contract.activated || labels.includes('active'), 'contract signature missing: active') - assert.ok(!contract.closed || labels.includes('closed'), 'contract signature missing: closed') - assert.ok(!contract.spent || labels.includes('spent'), 'contract signature missing: spent') - assert.ok(!contract.settled || labels.includes('settled'), 'contract signature missing: settled') - - contract.sigs.forEach(sig => { - verify_contract_sig(contract, pubkey, sig) - }) -} - -export function verify_contract_publishing ( - contract : ContractData, - proposal : ProposalData -) { - const { created_at, fees } = contract - const out = create_spend_templates(proposal, fees) - const pid = get_proposal_id(proposal) - const cid = get_contract_id(out, pid, created_at) - assert.ok(pid === contract.prop_id, 'computed proposal id does not match contract') - assert.ok(cid === contract.cid, 'computed contract id id does not match contract') - for (const [ label, txhex ] of out) { - const tmpl = contract.outputs.find(e => e[0] === label) - assert.ok(tmpl !== undefined, 'output template does not exist for label: ' + label) - assert.ok(tmpl[1] === txhex, 'tx hex does not match output for label: ' + label) - } + verify_contract_sig(contract, pubkey, 'published') + if (contract.canceled) verify_contract_sig(contract, pubkey, 'canceled') + if (contract.secured) verify_contract_sig(contract, pubkey, 'secured') + if (contract.activated) verify_contract_sig(contract, pubkey, 'active') + if (contract.closed) verify_contract_sig(contract, pubkey, 'closed') + if (contract.spent) verify_contract_sig(contract, pubkey, 'spent') + if (contract.settled) verify_contract_sig(contract, pubkey, 'settled') } export function verify_contract_funding ( @@ -146,17 +171,38 @@ export function verify_contract_funding ( export function verify_contract_activation ( contract : ContractData, - vmstate : VMData + vmdata : VMData ) { const { activated, active_at, canceled, expires_at, engine_vmid } = contract assert.ok(!canceled, 'contract is flagged as canceled') assert.ok(activated, 'contract is not flagged as active') - assert.ok(active_at === vmstate.active_at, 'contract activated date does not match vm') - assert.ok(engine_vmid === vmstate.vmid, 'contract vmid does not match vm internal id') + assert.ok(active_at === vmdata.active_at, 'contract activated date does not match vm') + assert.ok(engine_vmid === vmdata.vmid, 'contract vmid does not match vm internal id') const expires_chk = active_at + contract.terms.duration assert.ok(expires_at === expires_chk, 'computed expiration date does not match contract') } +export function verify_contract_execution ( + contract : ContractData, + engine : ScriptEngineAPI, + vmdata : VMData, + witness : WitnessData[] +) { + const config = get_vm_config(contract) + let vmstate = engine.init(config) + witness.forEach(wit => { vmstate = engine.eval(vmstate, wit) }) + assert.ok(vmstate.active_at === vmdata.active_at, 'vmdata.active_at does not match vm result') + assert.ok(vmstate.closed === vmdata.closed, 'vmdata.closed does not match vm result') + assert.ok(vmstate.closed_at === vmdata.closed_at, 'vmdata.closed_at does not match vm result') + assert.ok(vmstate.commit_at === vmdata.commit_at, 'vmdata.commit_at does not match vm result') + assert.ok(vmstate.engine === vmdata.engine, 'vmdata.engine does not match vm result') + assert.ok(vmstate.expires_at === vmdata.expires_at, 'vmdata.expires_at does not match vm result') + assert.ok(vmstate.head === vmdata.head, 'vmdata.head does not match vm result') + assert.ok(vmstate.output === vmdata.output, 'vmdata.output does not match vm result') + assert.ok(vmstate.step === vmdata.step, 'vmdata.step does not match vm result') + assert.ok(vmstate.vmid === vmdata.vmid, 'vmdata.vmid does not match vm result') +} + export function verify_contract_close ( contract : ContractData, vmstate : VMData @@ -195,20 +241,6 @@ export function verify_contract_spending ( assert.ok(txid === contract.spent_txid, 'spent txid does not match contract') } -export function verify_contract_settlement ( - contract : ContractData, - funds : FundingData[], - proposal : ProposalData, - vmstate : VMData -) { - verify_contract_data(contract) - verify_contract_publishing(contract, proposal) - verify_contract_funding(contract, funds) - verify_contract_activation(contract, vmstate) - verify_contract_close(contract, vmstate) - verify_contract_spending(contract, funds, vmstate) -} - export default { validate : { request : validate_publish_req, @@ -216,13 +248,8 @@ export default { }, verify : { request : verify_contract_req, - data : verify_contract_data, publish : verify_contract_publishing, - funding : verify_contract_funding, - activation : verify_contract_activation, - closing : verify_contract_close, - spending : verify_contract_spending, - settlement : verify_contract_settlement, + data : verify_contract, signatures : verify_contract_sigs } } diff --git a/src/core/validation/deposit.ts b/src/core/validation/deposit.ts index 9137ebc4..1fffddfd 100644 --- a/src/core/validation/deposit.ts +++ b/src/core/validation/deposit.ts @@ -103,18 +103,11 @@ export function verify_deposit_sigs ( deposit : DepositData, pubkey : string ) { - const labels = deposit.sigs.map(e => e[0]) - - assert.ok(labels.includes('registered'), 'deposit signature missing: registered') - assert.ok(!deposit.confirmed || labels.includes('confirmed'), 'deposit signature missing: confirmed') - assert.ok(!deposit.locked || labels.includes('locked'), 'deposit signature missing: locked') - assert.ok(!deposit.closed || labels.includes('closed'), 'deposit signature missing: closed') - assert.ok(!deposit.spent || labels.includes('spent'), 'deposit signature missing: spent') - assert.ok(!deposit.settled || labels.includes('settled'), 'deposit signature missing: settled') - - deposit.sigs.forEach(sig => { - verify_deposit_sig(deposit, pubkey, sig) - }) + verify_deposit_sig(deposit, pubkey, 'registered') + if (deposit.locked) verify_deposit_sig(deposit, pubkey, 'locked') + if (deposit.closed) verify_deposit_sig(deposit, pubkey, 'closed') + if (deposit.spent) verify_deposit_sig(deposit, pubkey, 'spent') + if (deposit.settled) verify_deposit_sig(deposit, pubkey, 'settled') } export function verify_feerate ( diff --git a/src/core/validation/witness.ts b/src/core/validation/witness.ts index c04c00b8..1d736610 100644 --- a/src/core/validation/witness.ts +++ b/src/core/validation/witness.ts @@ -74,7 +74,7 @@ export function verify_witness_data ( assert.ok(pathnames.includes(path), 'path does not exist in vm') assert.ok(stamp >= active_at, 'stamp exists before active date') assert.ok(stamp >= commit_at, 'stamp exists before latest commit') - assert.ok(stamp < expires_at, 'stamp exists on or after close date') + assert.ok(stamp < expires_at, 'stamp exists on or after expiration date') assert.ok(output === null, 'vm has already closed on an output') verify_witness_sigs(program, witness) } @@ -100,7 +100,7 @@ export function verify_witness_receipt ( vmdata : VMData, witness : WitnessData ) { - const { receipt_id, server_pk, server_sig } = receipt + const { receipt_id, receipt_sig, server_pk } = receipt // Don't forget to check that vm matches receipt. assert.ok(witness.vmid === vmdata.vmid, 'provided vmdata and witness vmid does not match') @@ -116,7 +116,7 @@ export function verify_witness_receipt ( assert.ok(int_wid === witness.wid, 'internal witness id does not match receipt') assert.ok(int_rid === receipt.receipt_id, 'internal receipt id does not match receipt') - const is_valid = verify_sig(server_sig, receipt_id, server_pk) + const is_valid = verify_sig(receipt_sig, receipt_id, server_pk) assert.ok(is_valid, 'receipt signature is invalid') } diff --git a/test/src/e2e/return.test.ts b/test/src/e2e/return.test.ts index f3f0277b..e4bf1a7e 100644 --- a/test/src/e2e/return.test.ts +++ b/test/src/e2e/return.test.ts @@ -102,7 +102,7 @@ export default async function ( await client.mine_blocks(1) const utxo_state = await get_spend_state(client, deposit.locktime, deposit.utxo) - deposit = confirm_deposit(deposit, utxo_state, escrow_dev) + deposit = confirm_deposit(deposit, utxo_state) if (VERBOSE) { console.log(banner('deposit')) diff --git a/test/src/e2e/settle.test.ts b/test/src/e2e/settle.test.ts index ba0d370a..b41c6a69 100644 --- a/test/src/e2e/settle.test.ts +++ b/test/src/e2e/settle.test.ts @@ -49,10 +49,10 @@ import { verify_commit_req, verify_deposit_data, verify_witness_receipt, - verify_contract_settlement, verify_witness_data, verify_contract_sigs, - verify_deposit_sigs + verify_deposit_sigs, + verify_contract } from '@/core/validation/index.js' import { @@ -205,7 +205,7 @@ export default async function ( const pending = deposits.map(async deposit => { const utxo_state = await get_spend_state(client, deposit.locktime, deposit.utxo) - return confirm_deposit(deposit, utxo_state, escrow_dev) + return confirm_deposit(deposit, utxo_state) }) deposits = await Promise.all(pending) @@ -301,11 +301,17 @@ export default async function ( const txid = await client.publish_tx(txhex, true) contract = spend_contract(contract, txhex, txid, escrow_dev) - - verify_contract_settlement(contract, deposits, proposal, vm_data) - contract = settle_contract(contract, escrow_dev) + verify_contract({ + contract, + commits : [ witness ], + engine : CVM, + funds : deposits, + pubkey : escrow_dev.pubkey, + vmdata : vm_data + }) + if (VERBOSE) { console.log(banner('settled contract')) console.dir(contract, { depth : null }) From 4d33f759ed292b59061c9b8fb1520159b59a8a2b Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sat, 18 May 2024 18:02:49 -0500 Subject: [PATCH 02/27] v0.12.32-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6bbb7650..3bcf258f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.31-dev", + "version": "0.12.32-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From 04a05f2babececc92aa72a89bc5228e162e658c6 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 03:22:13 -0500 Subject: [PATCH 03/27] v0.12.33-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3bcf258f..a47c8a36 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.32-dev", + "version": "0.12.33-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From 4910834356edd4eef9c883a9d41c9975e541db96 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 13:15:05 -0500 Subject: [PATCH 04/27] update --- src/client/api/client/account.ts | 26 ++++--- src/client/api/client/contract.ts | 23 +++---- src/client/api/client/deposit.ts | 11 +-- src/client/api/client/oracle.ts | 4 +- src/client/api/client/server.ts | 2 +- src/client/api/client/vm.ts | 9 ++- src/client/api/client/witness.ts | 2 +- src/client/api/signer/draft.ts | 2 +- src/client/class/client.ts | 15 ++-- src/client/class/signer.ts | 4 +- src/client/index.ts | 4 +- src/client/lib/enrollment.ts | 2 +- src/client/lib/membership.ts | 2 +- src/client/lib/oracle.ts | 25 +++++++ src/client/lib/session.ts | 4 +- src/client/schema/base.ts | 16 +++++ src/client/{schema.ts => schema/draft.ts} | 14 +--- src/client/schema/index.ts | 5 ++ src/{core => client}/schema/oracle.ts | 4 +- .../types/api => client/types}/account.ts | 2 +- src/client/{types.ts => types/base.ts} | 54 --------------- src/client/types/contract.ts | 24 +++++++ .../types/api => client/types}/deposit.ts | 4 +- src/client/types/draft.ts | 56 +++++++++++++++ src/{core/types/api => client/types}/index.ts | 3 + src/{core => client}/types/oracle.ts | 2 +- src/client/types/server.ts | 40 +++++++++++ src/{core/types/api => client/types}/vm.ts | 4 +- .../types/api => client/types}/witness.ts | 2 +- src/{core => client}/util/fetch.ts | 2 +- src/{core => client}/util/oracle.ts | 18 ++--- src/client/validation/contract.ts | 40 +++++++++++ src/client/validation/deposit.ts | 15 ++++ src/client/validation/index.ts | 2 + src/core/module/deposit/util.ts | 32 ++------- src/core/schema/account.ts | 11 ++- src/core/schema/index.ts | 4 -- src/core/schema/proposal.ts | 15 ++++ src/core/schema/server.ts | 31 --------- src/core/types/account.ts | 9 +++ src/core/types/api/contract.ts | 14 ---- src/core/types/api/server.ts | 13 ---- src/core/types/base.ts | 22 ------ src/core/types/contract.ts | 13 ---- src/core/types/index.ts | 3 - src/core/types/proposal.ts | 14 ++++ src/core/types/server.ts | 23 ------- src/core/util/index.ts | 4 +- src/core/util/parse.ts | 15 ++-- src/core/validation/account.ts | 10 +-- src/core/validation/contract.ts | 68 +++++++++---------- src/core/validation/deposit.ts | 20 +++--- src/core/validation/proposal.ts | 12 ++-- test/src/core.ts | 2 +- test/src/e2e/recover.test.ts | 4 +- test/src/e2e/return.test.ts | 4 +- test/src/e2e/settle.test.ts | 26 +++---- 57 files changed, 441 insertions(+), 370 deletions(-) create mode 100644 src/client/lib/oracle.ts create mode 100644 src/client/schema/base.ts rename src/client/{schema.ts => schema/draft.ts} (73%) create mode 100644 src/client/schema/index.ts rename src/{core => client}/schema/oracle.ts (94%) rename src/{core/types/api => client/types}/account.ts (54%) rename src/client/{types.ts => types/base.ts} (55%) create mode 100644 src/client/types/contract.ts rename src/{core/types/api => client/types}/deposit.ts (67%) create mode 100644 src/client/types/draft.ts rename src/{core/types/api => client/types}/index.ts (67%) rename src/{core => client}/types/oracle.ts (97%) create mode 100644 src/client/types/server.ts rename src/{core/types/api => client/types}/vm.ts (63%) rename src/{core/types/api => client/types}/witness.ts (71%) rename src/{core => client}/util/fetch.ts (95%) rename src/{core => client}/util/oracle.ts (92%) create mode 100644 src/client/validation/contract.ts create mode 100644 src/client/validation/deposit.ts create mode 100644 src/client/validation/index.ts delete mode 100644 src/core/schema/server.ts delete mode 100644 src/core/types/api/contract.ts delete mode 100644 src/core/types/api/server.ts delete mode 100644 src/core/types/server.ts diff --git a/src/client/api/client/account.ts b/src/client/api/client/account.ts index 7fbf3c57..09d04ab9 100644 --- a/src/client/api/client/account.ts +++ b/src/client/api/client/account.ts @@ -1,33 +1,37 @@ -/* Global Imports */ +import { EscrowClient } from '@/client/class/client.js' +import { DEFAULT_POLICY } from '@/client/config.js' import { validate_account_req, validate_register_req, - validate_commit_req + validate_commit_req, + verify_account_req } from '@/core/validation/index.js' import { - ApiResponse, AccountRequest, - AccountDataResponse, - DepositDataResponse, - FundingDataResponse, RegisterRequest, - CommitRequest + CommitRequest, + AccountPolicy } from '@/core/types/index.js' -/* Module Imports */ - -import { EscrowClient } from '../../class/client.js' +import { + ApiResponse, + AccountDataResponse, + DepositDataResponse, + FundingDataResponse +} from '@/client/types/index.js' /** * Request a deposit account from the provider. */ function request_account_api (client : EscrowClient) { return async ( - request : AccountRequest + request : AccountRequest, + policy : AccountPolicy = DEFAULT_POLICY.account ) : Promise> => { validate_account_req(request) + verify_account_req(policy, request) // Formulate the request. const host = client.server_url const url = `${host}/api/account/request` diff --git a/src/client/api/client/contract.ts b/src/client/api/client/contract.ts index 65cd2b06..7e7c2b5f 100644 --- a/src/client/api/client/contract.ts +++ b/src/client/api/client/contract.ts @@ -1,7 +1,7 @@ -/* Global Imports */ - import { assert, parse_proposal } from '@/core/util/index.js' import { create_publish_req } from '@/core/module/contract/index.js' +import { EscrowClient } from '@/client/class/client.js' +import { DEFAULT_POLICY } from '@/client/config.js' import { verify_endorsements, @@ -9,18 +9,17 @@ import { } from '@/core/validation/index.js' import { - ApiResponse, - ContractDataResponse, - ContractListResponse, - FundListResponse, ContractRequest, - ServerPolicy, + ProposalPolicy, ScriptEngineAPI } from '@/core/types/index.js' -/* Module Imports */ - -import { EscrowClient } from '../../class/client.js' +import { + ApiResponse, + ContractDataResponse, + ContractListResponse, + FundListResponse +} from '@/client/types/index.js' /** * Create a contract from a proposal document. @@ -29,9 +28,9 @@ function create_contract_api ( client : EscrowClient ) { return async ( + request : ContractRequest, engine : ScriptEngineAPI, - policy : ServerPolicy, - request : ContractRequest + policy : ProposalPolicy = DEFAULT_POLICY.proposal ) : Promise> => { // Unpack configurations from client. const { endorsements, proposal } = request diff --git a/src/client/api/client/deposit.ts b/src/client/api/client/deposit.ts index 490155c4..ff7f9ddb 100644 --- a/src/client/api/client/deposit.ts +++ b/src/client/api/client/deposit.ts @@ -6,14 +6,17 @@ import { } from '@/core/validation/index.js' import { - ApiResponse, - DepositDataResponse, - DepositListResponse, - FundingDataResponse, CloseRequest, LockRequest } from '@/core/types/index.js' +import { + ApiResponse, + DepositDataResponse, + DepositListResponse, + FundingDataResponse +} from '@/client/types/index.js' + /* Module Imports */ import { EscrowClient } from '../../class/client.js' diff --git a/src/client/api/client/oracle.ts b/src/client/api/client/oracle.ts index 311c6060..f7c2602f 100644 --- a/src/client/api/client/oracle.ts +++ b/src/client/api/client/oracle.ts @@ -10,12 +10,12 @@ import { get_tx_data, get_address_utxos, get_latest_utxo -} from '@/core/util/oracle.js' +} from '../../util/oracle.js' import { OracleQuery, OracleTxSpendData -} from '@/core/types/index.js' +} from '@/client/types/index.js' /* Module Imports */ diff --git a/src/client/api/client/server.ts b/src/client/api/client/server.ts index ec007bac..8dc7cd62 100644 --- a/src/client/api/client/server.ts +++ b/src/client/api/client/server.ts @@ -3,7 +3,7 @@ import { ServerKeysResponse, ServerPolicyResponse, ServerStatusResponse -} from '@/core/types/index.js' +} from '@/client/types/index.js' import { EscrowClient } from '../../class/client.js' diff --git a/src/client/api/client/vm.ts b/src/client/api/client/vm.ts index 6e47810c..24d3fa61 100644 --- a/src/client/api/client/vm.ts +++ b/src/client/api/client/vm.ts @@ -1,15 +1,14 @@ -import { assert } from '@/core/util/index.js' +import { assert } from '@/core/util/index.js' +import { WitnessData } from '@/core/types/index.js' +import { EscrowClient } from '@/client/class/client.js' import { ApiResponse, VMDataResponse, VMListResponse, VMSubmitResponse, - WitnessData, WitnessListResponse -} from '@/core/types/index.js' - -import { EscrowClient } from '../../class/client.js' +} from '@/client/types/index.js' /** * Return a list of verified witnesses diff --git a/src/client/api/client/witness.ts b/src/client/api/client/witness.ts index 5ba19c2d..3431627d 100644 --- a/src/client/api/client/witness.ts +++ b/src/client/api/client/witness.ts @@ -4,7 +4,7 @@ import { EscrowClient } from '../../class/client.js' import { ApiResponse, WitnessDataResponse -} from '@/core/types/index.js' +} from '@/client/types/index.js' function read_witness_api (client : EscrowClient) { return async ( diff --git a/src/client/api/signer/draft.ts b/src/client/api/signer/draft.ts index dded6a1b..871cf276 100644 --- a/src/client/api/signer/draft.ts +++ b/src/client/api/signer/draft.ts @@ -8,7 +8,7 @@ import { import { CredentialConfig, DraftSession -} from '../../types.js' +} from '../../types/base.js' import { claim_membership, diff --git a/src/client/class/client.ts b/src/client/class/client.ts index 35e638d9..c33686a3 100644 --- a/src/client/class/client.ts +++ b/src/client/class/client.ts @@ -1,12 +1,17 @@ -import { resolve_json } from '@/core/util/fetch.js' -import { ApiResponse, Network } from '@/core/types/index.js' -import { DEFAULT_CONFIG, get_client_config } from '../config.js' +import { resolve_json } from '../util/fetch.js' +import { Network } from '@/core/types/index.js' +import { ApiResponse } from '@/client/types/index.js' + +import { + DEFAULT_CONFIG, + get_client_config +} from '../config.js' import { ClientConfig, ClientOptions, FetchConfig -} from '../types.js' +} from '../types/base.js' import account_api from '../api/client/account.js' import contract_api from '../api/client/contract.js' @@ -17,7 +22,7 @@ import server_api from '../api/client/server.js' import vmachine_api from '../api/client/vm.js' import witness_api from '../api/client/witness.js' -import ClientSchema from '../schema.js' +import ClientSchema from '../schema/base.js' type Resolver = ReturnType diff --git a/src/client/class/signer.ts b/src/client/class/signer.ts index b17eff90..081f984e 100644 --- a/src/client/class/signer.ts +++ b/src/client/class/signer.ts @@ -14,7 +14,7 @@ import { SignerConfig, SignerOptions, WalletAPI -} from '../types.js' +} from '../types/base.js' import account_api from '../api/signer/account.js' import contract_api from '../api/signer/contract.js' @@ -24,7 +24,7 @@ import machine_api from '../api/signer/vm.js' import wallet_api from '../api/signer/wallet.js' import witness_api from '../api/signer/witness.js' -import ClientSchema from '../schema.js' +import ClientSchema from '../schema/base.js' export class EscrowSigner { static create ( diff --git a/src/client/index.ts b/src/client/index.ts index 9ae354a8..1dbcfa54 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,9 +1,9 @@ export * from './class/client.js' export * from './class/signer.js' -export * from './types.js' +export * from './types/base.js' export * as ClientConfig from './config.js' export * as ClientLib from './lib/index.js' -export * as ClientSchema from './schema.js' +export * as ClientSchema from './schema/base.js' export { DraftUtil } from './lib/session.js' diff --git a/src/client/lib/enrollment.ts b/src/client/lib/enrollment.ts index 026b9d36..fa52d34b 100644 --- a/src/client/lib/enrollment.ts +++ b/src/client/lib/enrollment.ts @@ -9,7 +9,7 @@ import { RoleTemplate, DraftSession, CredentialData -} from '../types.js' +} from '../types/base.js' const GET_ROLE_DEFAULTS = () => { return { diff --git a/src/client/lib/membership.ts b/src/client/lib/membership.ts index 268c7f07..3fa54b6f 100644 --- a/src/client/lib/membership.ts +++ b/src/client/lib/membership.ts @@ -5,7 +5,7 @@ import { CredentialData, DraftSession, MemberData -} from '../types.js' +} from '../types/base.js' export function create_credential ( signer : SignerAPI, diff --git a/src/client/lib/oracle.ts b/src/client/lib/oracle.ts new file mode 100644 index 00000000..3b450591 --- /dev/null +++ b/src/client/lib/oracle.ts @@ -0,0 +1,25 @@ +import { TxConfirmState } from '@/core/types/index.js' +import { OracleTxSpendData } from '@/client/types/oracle.js' + +/** + * Compute the spending state of a deposit, + * using transaction data from an oracle. + */ +export function get_confirm_state ( + locktime : number, + utxo_data : OracleTxSpendData +) : TxConfirmState { + const { status } = utxo_data + if (status.confirmed) { + const expires_at = status.block_time + locktime + return { ...status, expires_at } + } else { + return { + confirmed : false as const, + block_hash : null, + block_height : null, + block_time : null, + expires_at : null + } + } +} diff --git a/src/client/lib/session.ts b/src/client/lib/session.ts index 2cfb1f3e..849bd0af 100644 --- a/src/client/lib/session.ts +++ b/src/client/lib/session.ts @@ -20,9 +20,9 @@ import { DraftSession, CredentialData, DraftTemplate -} from '../types.js' +} from '../types/base.js' -import ClientSchema from '../schema.js' +import ClientSchema from '../schema/base.js' import { add_member_data, diff --git a/src/client/schema/base.ts b/src/client/schema/base.ts new file mode 100644 index 00000000..2a96dcbc --- /dev/null +++ b/src/client/schema/base.ts @@ -0,0 +1,16 @@ +import { z } from 'zod' +import base from '@/core/schema/base.js' + +const { hash, network, str } = base + +const signer_config = z.object({ + network, + server_pk : hash, + server_url : str +}) + +const client_config = signer_config.extend({ + oracle_url : str +}) + +export default { client_config, signer_config } diff --git a/src/client/schema.ts b/src/client/schema/draft.ts similarity index 73% rename from src/client/schema.ts rename to src/client/schema/draft.ts index 929ee8bf..ce746f68 100644 --- a/src/client/schema.ts +++ b/src/client/schema/draft.ts @@ -2,17 +2,7 @@ import { z } from 'zod' import base from '@/core/schema/base.js' import prop from '@/core/schema/proposal.js' -const { bool, hash, hex, label, network, num, str } = base - -const signer_config = z.object({ - network, - server_pk : hash, - server_url : str -}) - -const client_config = signer_config.extend({ - oracle_url : str -}) +const { bool, hash, hex, label, num, str } = base const cred = z.object({ pub : hash, @@ -52,4 +42,4 @@ const session = z.object({ sigs : hex.array() }) -export default { client_config, cred, mship, path, policy, role, session, signer_config } +export default { cred, mship, path, policy, role, session } diff --git a/src/client/schema/index.ts b/src/client/schema/index.ts new file mode 100644 index 00000000..5a4074e6 --- /dev/null +++ b/src/client/schema/index.ts @@ -0,0 +1,5 @@ +import base from './base.js' +import draft from './draft.js' +import oracle from './oracle.js' + +export default { base, draft, oracle } diff --git a/src/core/schema/oracle.ts b/src/client/schema/oracle.ts similarity index 94% rename from src/core/schema/oracle.ts rename to src/client/schema/oracle.ts index e369bf45..b180050e 100644 --- a/src/core/schema/oracle.ts +++ b/src/client/schema/oracle.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import base from './base.js' -import tx from './tx.js' +import base from '@/core/schema/base.js' +import tx from '@/core/schema/tx.js' const { bool, hash, hex, num, stamp, str } = base diff --git a/src/core/types/api/account.ts b/src/client/types/account.ts similarity index 54% rename from src/core/types/api/account.ts rename to src/client/types/account.ts index 6f65f528..30b72c9f 100644 --- a/src/core/types/api/account.ts +++ b/src/client/types/account.ts @@ -1,4 +1,4 @@ -import { AccountData } from '../account.js' +import { AccountData } from '@/core/types/account.js' export interface AccountDataResponse { account : AccountData diff --git a/src/client/types.ts b/src/client/types/base.ts similarity index 55% rename from src/client/types.ts rename to src/client/types/base.ts index ea862aa3..aa09b9c0 100644 --- a/src/client/types.ts +++ b/src/client/types/base.ts @@ -4,9 +4,6 @@ import { AddressConfig } from '@cmdcode/signer' import { Network, - ProgramEntry, - ProposalData, - ProposalTemplate, SignerAPI } from '@/core/types/index.js' @@ -37,57 +34,6 @@ export interface FetchConfig { token ?: string } -export type PathTemplate = [ - path : string, - amount : number -] - -export interface RoleTemplate { - title : string - id ?: string - moderator ?: boolean - paths ?: PathTemplate[] - payment ?: number - programs ?: ProgramEntry[] - seats ?: number -} - -export interface RolePolicy { - id : string - title : string - moderator : boolean - paths : [ string, number ][] - payment ?: number - programs : ProgramEntry[] - seats : number -} - -export interface CredentialConfig { - hid ?: string - idx ?: number -} - -export interface CredentialData { - pub : string - xpub : string -} - -export interface MemberData extends CredentialData { - pid : string -} - -export interface DraftTemplate { - proposal : ProposalTemplate - roles : RoleTemplate[] -} - -export interface DraftSession { - proposal : ProposalData - members : MemberData[] - roles : RolePolicy[] - sigs : string[] -} - export interface ClientSignerAPI extends SignerAPI { xpub : string backup : (password : Bytes) => Bytes diff --git a/src/client/types/contract.ts b/src/client/types/contract.ts new file mode 100644 index 00000000..4f4a6c5e --- /dev/null +++ b/src/client/types/contract.ts @@ -0,0 +1,24 @@ +import { + ContractData, + FundingData, + VMData +} from '@/core/types/index.js' + +export interface ContractDataResponse { + contract : ContractData +} + +export interface ContractListResponse { + contracts : ContractData[] +} + +export interface FundListResponse { + funds : FundingData[] +} + +export interface ContractVerifyConfig { + contract : ContractData + funds ?: FundingData[] + pubkey : string + vmdata ?: VMData +} diff --git a/src/core/types/api/deposit.ts b/src/client/types/deposit.ts similarity index 67% rename from src/core/types/api/deposit.ts rename to src/client/types/deposit.ts index 76199e81..06d7a828 100644 --- a/src/core/types/api/deposit.ts +++ b/src/client/types/deposit.ts @@ -1,5 +1,5 @@ -import { ContractData } from '../contract.js' -import { DepositData } from '../deposit.js' +import { ContractData } from '@/core/types/contract.js' +import { DepositData } from '@/core/types/deposit.js' export interface DepositDataResponse { deposit : DepositData diff --git a/src/client/types/draft.ts b/src/client/types/draft.ts new file mode 100644 index 00000000..15870db3 --- /dev/null +++ b/src/client/types/draft.ts @@ -0,0 +1,56 @@ +import { + ProgramEntry, + ProposalData, + ProposalTemplate +} from '@/core/types/index.js' + +export type PathTemplate = [ + path : string, + amount : number +] + +export interface RoleTemplate { + title : string + id ?: string + moderator ?: boolean + paths ?: PathTemplate[] + payment ?: number + programs ?: ProgramEntry[] + seats ?: number +} + +export interface RolePolicy { + id : string + title : string + moderator : boolean + paths : [ string, number ][] + payment ?: number + programs : ProgramEntry[] + seats : number +} + +export interface CredentialConfig { + hid ?: string + idx ?: number +} + +export interface CredentialData { + pub : string + xpub : string +} + +export interface MemberData extends CredentialData { + pid : string +} + +export interface DraftTemplate { + proposal : ProposalTemplate + roles : RoleTemplate[] +} + +export interface DraftSession { + proposal : ProposalData + members : MemberData[] + roles : RolePolicy[] + sigs : string[] +} diff --git a/src/core/types/api/index.ts b/src/client/types/index.ts similarity index 67% rename from src/core/types/api/index.ts rename to src/client/types/index.ts index 5c87dade..2049af0b 100644 --- a/src/core/types/api/index.ts +++ b/src/client/types/index.ts @@ -1,6 +1,9 @@ export * from './account.js' +export * from './base.js' export * from './contract.js' export * from './deposit.js' +export * from './draft.js' +export * from './oracle.js' export * from './server.js' export * from './vm.js' export * from './witness.js' diff --git a/src/core/types/oracle.ts b/src/client/types/oracle.ts similarity index 97% rename from src/core/types/oracle.ts rename to src/client/types/oracle.ts index 88179187..da42f76f 100644 --- a/src/core/types/oracle.ts +++ b/src/client/types/oracle.ts @@ -1,4 +1,4 @@ -import { TxOutput } from './tx.js' +import { TxOutput } from '@/core/types/tx.js' export type OracleFeeEstimate = Record export type OracleTxRecvStatus = TxConfirmed | TxUnconfirmed diff --git a/src/client/types/server.ts b/src/client/types/server.ts new file mode 100644 index 00000000..13305d6e --- /dev/null +++ b/src/client/types/server.ts @@ -0,0 +1,40 @@ +import { AccountPolicy, Network, ProposalPolicy } from '@/core/types/index.js' + +export type ApiResponse = DataResponse | ErrorResponse + +export interface DataResponse { + ok : true + data : T + error ?: string + status : number +} + +export interface ErrorResponse { + ok : false + error : string + status : number +} + +export interface ServerConfig { + oracle_url : string + server_pk : string + server_url : string + network : Network +} + +export interface ServerKeysResponse { + pubkeys : string[] +} + +export interface ServerPolicyResponse { + policy : ServerPolicy +} + +export interface ServerStatusResponse { + status : string +} + +export interface ServerPolicy { + account : AccountPolicy + proposal : ProposalPolicy +} diff --git a/src/core/types/api/vm.ts b/src/client/types/vm.ts similarity index 63% rename from src/core/types/api/vm.ts rename to src/client/types/vm.ts index accb29bf..db699f3b 100644 --- a/src/core/types/api/vm.ts +++ b/src/client/types/vm.ts @@ -1,5 +1,5 @@ -import { VMData } from '../machine.js' -import { WitnessReceipt } from '../witness.js' +import { VMData } from '@/core/types/machine.js' +import { WitnessReceipt } from '@/core/types/witness.js' export interface VMDataResponse { vmdata : VMData diff --git a/src/core/types/api/witness.ts b/src/client/types/witness.ts similarity index 71% rename from src/core/types/api/witness.ts rename to src/client/types/witness.ts index 8d35ea01..7dc2fd26 100644 --- a/src/core/types/api/witness.ts +++ b/src/client/types/witness.ts @@ -1,4 +1,4 @@ -import { WitnessReceipt } from '../witness.js' +import { WitnessReceipt } from '@/core/types/witness.js' export interface WitnessDataResponse { witness : WitnessReceipt diff --git a/src/core/util/fetch.ts b/src/client/util/fetch.ts similarity index 95% rename from src/core/util/fetch.ts rename to src/client/util/fetch.ts index c9fc5c49..900f1612 100644 --- a/src/core/util/fetch.ts +++ b/src/client/util/fetch.ts @@ -1,4 +1,4 @@ -import { ApiResponse } from '../types/base.js' +import { ApiResponse } from '@/client/types/index.js' export async function fetcher ( input : URL | RequestInfo, diff --git a/src/core/util/oracle.ts b/src/client/util/oracle.ts similarity index 92% rename from src/core/util/oracle.ts rename to src/client/util/oracle.ts index 81d1e095..c2510c9c 100644 --- a/src/core/util/oracle.ts +++ b/src/client/util/oracle.ts @@ -4,7 +4,7 @@ import { parse_addr } from '@scrow/tapscript/address' /* Module Imports */ -import { exists } from './validate.js' +import { exists } from '../../core/util/validate.js' import { fetcher, resolve_json } from './fetch.js' /* Local Imports */ @@ -17,9 +17,9 @@ import { OracleTxSpendState, OracleTxData, OracleUtxo -} from '../types/index.js' +} from '@/client/types/index.js' -import CoreSchema from '../schema/index.js' +import ClientSchema from '@/client/schema/index.js' /** * Fetch transaction data from the oracle. @@ -39,9 +39,9 @@ export async function get_tx_data ( // If the response failed, throw. if (!json.ok) throw new Error(json.error) // Parse the returned data. - const parsed = await CoreSchema.oracle.txdata.spa(json.data) + const parsed = await ClientSchema.oracle.txdata.spa(json.data) // If data fails validation, throw. - if (!parsed.success) throw new Error(parsed.error.toString()) + if (!parsed.success) throw parsed.error // Return the parsed data. return parsed.data } @@ -66,9 +66,9 @@ export async function get_utxo_state ( // If the response failed, throw. if (!json.ok) throw new Error(json.error) // Parse the returned data. - const parsed = await CoreSchema.oracle.txostate.spa(json.data) + const parsed = await ClientSchema.oracle.txostate.spa(json.data) // If data fails validation, throw. - if (!parsed.success) throw new Error(parsed.error.toString()) + if (!parsed.success) throw parsed.error // Return the parsed data. return parsed.data } @@ -130,9 +130,9 @@ export async function get_address_utxos ( // If response failed, throw error. if (!res.ok) throw new Error(res.error) // Parse the returned data. - const parsed = await CoreSchema.oracle.utxo.array().spa(res.data) + const parsed = await ClientSchema.oracle.utxo.array().spa(res.data) // If data fails validation, throw. - if (!parsed.success) throw new Error(parsed.error.toString()) + if (!parsed.success) throw parsed.error // Return the parsed data. return parsed.data.map(({ txid, status, value, vout }) => { const scriptkey = parse_addr(addr).hex diff --git a/src/client/validation/contract.ts b/src/client/validation/contract.ts new file mode 100644 index 00000000..e8c4ca9d --- /dev/null +++ b/src/client/validation/contract.ts @@ -0,0 +1,40 @@ +import { assert } from '@/core/util/index.js' + +import { ContractVerifyConfig } from '@/client/types/contract.js' + +import { + verify_contract_activation, + verify_contract_close, + verify_contract_data, + verify_contract_funding, + verify_contract_sigs, + verify_contract_spending +} from '@/core/validation/contract.js' + +export function verify_contract ( + config : ContractVerifyConfig +) { + const { contract, funds, pubkey, vmdata } = config + + verify_contract_data(contract) + verify_contract_sigs(contract, pubkey) + + if (contract.secured && funds !== undefined) { + verify_contract_funding(contract, funds) + } + + if (contract.activated && vmdata !== undefined) { + verify_contract_activation(contract, vmdata) + } + + if (contract.closed && vmdata !== undefined) { + assert.exists(vmdata, 'you must provide a vmdata object to verify.') + verify_contract_close(contract, vmdata) + } + + if (contract.spent) { + assert.exists(funds, 'you must provide a list of funds to verify') + assert.exists(vmdata, 'you must provide a vmdata object to verify.') + verify_contract_spending(contract, funds, vmdata) + } +} diff --git a/src/client/validation/deposit.ts b/src/client/validation/deposit.ts new file mode 100644 index 00000000..8493073b --- /dev/null +++ b/src/client/validation/deposit.ts @@ -0,0 +1,15 @@ +import { DepositData, SignerAPI } from '@/core/types/index.js' + +import { + verify_deposit_data, + verify_deposit_sigs +} from '@/core/validation/deposit.js' + +export function verify_deposit ( + deposit : DepositData, + pubkey : string, + signer : SignerAPI +) { + verify_deposit_data(deposit, signer) + verify_deposit_sigs(deposit, pubkey) +} diff --git a/src/client/validation/index.ts b/src/client/validation/index.ts new file mode 100644 index 00000000..4626a60a --- /dev/null +++ b/src/client/validation/index.ts @@ -0,0 +1,2 @@ +export * from './contract.js' +export * from './deposit.js' diff --git a/src/core/module/deposit/util.ts b/src/core/module/deposit/util.ts index 1c5c4afd..c5323dd4 100644 --- a/src/core/module/deposit/util.ts +++ b/src/core/module/deposit/util.ts @@ -1,9 +1,8 @@ -import { Buff } from '@cmdcode/buff' -import { verify_sig } from '@cmdcode/crypto-tools/signer' -import { INIT_CONF_STATE } from '@/core/lib/tx.js' -import { DEPOSIT_KIND } from '@/core/const.js' -import { get_proof_id } from '@/core/util/notarize.js' -import * as assert from '@/core/util/assert.js' +import { Buff } from '@cmdcode/buff' +import { verify_sig } from '@cmdcode/crypto-tools/signer' +import { DEPOSIT_KIND } from '@/core/const.js' +import { get_proof_id } from '@/core/util/notarize.js' +import * as assert from '@/core/util/assert.js' import { get_deposit_proof, get_deposit_state } from './state.js' @@ -12,9 +11,7 @@ import { DepositPreImage, DepositStatus, NoteTemplate, - OracleTxSpendData, - SignerAPI, - TxConfirmState + SignerAPI } from '@/core/types/index.js' export function get_deposit_id ( @@ -26,23 +23,6 @@ export function get_deposit_id ( return Buff.join([ cat, hash ]).digest.hex } -/** - * Compute the spending state of a deposit, - * using transaction data from an oracle. - */ -export function get_confirm_state ( - locktime : number, - utxo_data : OracleTxSpendData -) : TxConfirmState { - const { status } = utxo_data - if (status.confirmed) { - const expires_at = status.block_time + locktime - return { ...status, expires_at } - } else { - return INIT_CONF_STATE() - } -} - export function get_deposit_preimg ( deposit : DepositPreImage, status : DepositStatus diff --git a/src/core/schema/account.ts b/src/core/schema/account.ts index 744c6b04..5f682970 100644 --- a/src/core/schema/account.ts +++ b/src/core/schema/account.ts @@ -45,4 +45,13 @@ const data = z.object({ server_tkn : token }) -export default { data, account_req, commit_req, covenant, register_req, token } +const policy = z.object({ + FEERATE_MIN : num, + FEERATE_MAX : num, + GRACE_PERIOD : num, + LOCKTIME_MIN : num, + LOCKTIME_MAX : num, + TOKEN_EXPIRY : num +}) + +export default { data, account_req, commit_req, covenant, policy, register_req, token } diff --git a/src/core/schema/index.ts b/src/core/schema/index.ts index a01f7b58..b47a51af 100644 --- a/src/core/schema/index.ts +++ b/src/core/schema/index.ts @@ -2,9 +2,7 @@ import account from './account.js' import base from './base.js' import contract from './contract.js' import deposit from './deposit.js' -import oracle from './oracle.js' import proposal from './proposal.js' -import server from './server.js' import tx from './tx.js' import vm from './vm.js' import witness from './witness.js' @@ -14,9 +12,7 @@ export default { base, contract, deposit, - oracle, proposal, - server, tx, vm, witness diff --git a/src/core/schema/proposal.ts b/src/core/schema/proposal.ts index 89411dd0..a0e635a1 100644 --- a/src/core/schema/proposal.ts +++ b/src/core/schema/proposal.ts @@ -40,12 +40,27 @@ const template = data.partial().extend({ value : num }) +const policy = z.object({ + FEERATE_MIN : num, + FEERATE_MAX : num, + DEADLINE_MIN : num, + DEADLINE_MAX : num, + DURATION_MIN : num, + DURATION_MAX : num, + EFFECTIVE_MAX : num, + MULTISIG_MAX : num, + TXTIMEOUT_MIN : num, + TXTIMEOUT_MAX : num, + VALID_ENGINES : str.array() +}) + export default { data, paypath, paths, payment, payments, + policy, program, programs, schedule, diff --git a/src/core/schema/server.ts b/src/core/schema/server.ts deleted file mode 100644 index b220e6dd..00000000 --- a/src/core/schema/server.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { z } from 'zod' -import base from './base.js' - -const { num, str } = base - -const account = z.object({ - FEERATE_MIN : num, - FEERATE_MAX : num, - GRACE_PERIOD : num, - LOCKTIME_MIN : num, - LOCKTIME_MAX : num, - TOKEN_EXPIRY : num -}) - -const proposal = z.object({ - FEERATE_MIN : num, - FEERATE_MAX : num, - DEADLINE_MIN : num, - DEADLINE_MAX : num, - DURATION_MIN : num, - DURATION_MAX : num, - EFFECTIVE_MAX : num, - MULTISIG_MAX : num, - TXTIMEOUT_MIN : num, - TXTIMEOUT_MAX : num, - VALID_ENGINES : str.array() -}) - -const policy = z.object({ account, proposal }) - -export default { account, policy, proposal } diff --git a/src/core/types/account.ts b/src/core/types/account.ts index 430469f2..2ceca8ca 100644 --- a/src/core/types/account.ts +++ b/src/core/types/account.ts @@ -62,3 +62,12 @@ export interface RegisterRequest extends RegisterTemplate { export interface CommitRequest extends RegisterRequest { covenant : CovenantData } + +export interface AccountPolicy { + FEERATE_MIN : number + FEERATE_MAX : number + GRACE_PERIOD : number + LOCKTIME_MIN : number + LOCKTIME_MAX : number + TOKEN_EXPIRY : number +} diff --git a/src/core/types/api/contract.ts b/src/core/types/api/contract.ts deleted file mode 100644 index 70327078..00000000 --- a/src/core/types/api/contract.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ContractData } from '../contract.js' -import { FundingData } from '../deposit.js' - -export interface ContractDataResponse { - contract : ContractData -} - -export interface ContractListResponse { - contracts : ContractData[] -} - -export interface FundListResponse { - funds : FundingData[] -} diff --git a/src/core/types/api/server.ts b/src/core/types/api/server.ts deleted file mode 100644 index 32d68be8..00000000 --- a/src/core/types/api/server.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ServerPolicy } from '../server.js' - -export interface ServerKeysResponse { - pubkeys : string[] -} - -export interface ServerPolicyResponse { - policy : ServerPolicy -} - -export interface ServerStatusResponse { - status : string -} diff --git a/src/core/types/base.ts b/src/core/types/base.ts index dce406cd..5ae918ea 100644 --- a/src/core/types/base.ts +++ b/src/core/types/base.ts @@ -2,28 +2,6 @@ export type Literal = string | number | boolean | null export type Json = Literal | { [key : string] : Json } | Json[] export type Network = 'regtest' | 'main' | 'testnet' | 'signet' | 'mutiny' -export type ApiResponse = DataResponse | ErrorResponse - -export interface DataResponse { - ok : true - data : T - error ?: string - status : number -} - -export interface ErrorResponse { - ok : false - error : string - status : number -} - -export interface ServerConfig { - oracle_url : string - server_pk : string - server_url : string - network : Network -} - export interface NoteTemplate { content : string created_at : number diff --git a/src/core/types/contract.ts b/src/core/types/contract.ts index 96ab93b1..afc7cc08 100644 --- a/src/core/types/contract.ts +++ b/src/core/types/contract.ts @@ -1,6 +1,3 @@ -import { FundingData } from './deposit.js' -import { ScriptEngineAPI, VMData } from './machine.js' - import { PaymentEntry, ProposalData @@ -10,7 +7,6 @@ import { TxSettleState, TxSpendState } from './tx.js' -import { WitnessData } from './witness.js' export type ContractStatus = 'published' | // Contract is published and awaiting funds. @@ -115,15 +111,6 @@ export interface ContractCreateConfig { feerate : number } -export interface ContractVerifyConfig { - contract : ContractData - commits ?: WitnessData[] - engine ?: ScriptEngineAPI - funds ?: FundingData[] - pubkey : string - vmdata ?: VMData -} - export interface ContractBase { cid : string created_at : number diff --git a/src/core/types/index.ts b/src/core/types/index.ts index 1a09c65d..20e0f0b3 100644 --- a/src/core/types/index.ts +++ b/src/core/types/index.ts @@ -1,13 +1,10 @@ export * from './account.js' -export * from './api/index.js' export * from './base.js' export * from './contract.js' export * from './covenant.js' export * from './deposit.js' -export * from './oracle.js' export * from './proposal.js' export * from './recovery.js' -export * from './server.js' export * from './signer.js' export * from './tx.js' export * from './machine.js' diff --git a/src/core/types/proposal.ts b/src/core/types/proposal.ts index fd1abc28..7cc118d8 100644 --- a/src/core/types/proposal.ts +++ b/src/core/types/proposal.ts @@ -50,3 +50,17 @@ export interface ProposalData { value : number version : number } + +export interface ProposalPolicy { + FEERATE_MIN : number + FEERATE_MAX : number + DEADLINE_MIN : number + DEADLINE_MAX : number + DURATION_MIN : number + DURATION_MAX : number + EFFECTIVE_MAX : number + MULTISIG_MAX : number + TXTIMEOUT_MIN : number + TXTIMEOUT_MAX : number + VALID_ENGINES : string[] +} diff --git a/src/core/types/server.ts b/src/core/types/server.ts deleted file mode 100644 index 301d3b80..00000000 --- a/src/core/types/server.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface ServerPolicy { - account : { - FEERATE_MIN : number - FEERATE_MAX : number - GRACE_PERIOD : number - LOCKTIME_MIN : number - LOCKTIME_MAX : number - TOKEN_EXPIRY : number - } - proposal : { - FEERATE_MIN : number - FEERATE_MAX : number - DEADLINE_MIN : number - DEADLINE_MAX : number - DURATION_MIN : number - DURATION_MAX : number - EFFECTIVE_MAX : number - MULTISIG_MAX : number - TXTIMEOUT_MIN : number - TXTIMEOUT_MAX : number - VALID_ENGINES : string[] - } -} diff --git a/src/core/util/index.ts b/src/core/util/index.ts index 4c2c7e60..71911185 100644 --- a/src/core/util/index.ts +++ b/src/core/util/index.ts @@ -1,8 +1,8 @@ export * from './base.js' -export * from './fetch.js' +export * from '../../client/util/fetch.js' export * from './notarize.js' export * from './parse.js' export * from './validate.js' export * as assert from './assert.js' -export * as oracle from './oracle.js' +export * as oracle from '../../client/util/oracle.js' diff --git a/src/core/util/parse.ts b/src/core/util/parse.ts index b259eef9..5f289141 100644 --- a/src/core/util/parse.ts +++ b/src/core/util/parse.ts @@ -7,7 +7,8 @@ import { WitnessData, ProgramData, FundingData, - ServerPolicy + ProposalPolicy, + AccountPolicy } from '../types/index.js' import CoreSchema from '../schema/index.js' @@ -60,10 +61,16 @@ export function parse_proposal ( return CoreSchema.proposal.data.parse(proposal) } -export function parse_server_policy ( +export function parse_account_policy ( policy : unknown -) : ServerPolicy { - return CoreSchema.server.policy.parse(policy) +) : AccountPolicy { + return CoreSchema.account.policy.parse(policy) +} + +export function parse_proposal_policy ( + policy : unknown +) : ProposalPolicy { + return CoreSchema.proposal.policy.parse(policy) } export function parse_witness ( diff --git a/src/core/validation/account.ts b/src/core/validation/account.ts index 2808f11d..215d652b 100644 --- a/src/core/validation/account.ts +++ b/src/core/validation/account.ts @@ -26,7 +26,7 @@ import { CommitRequest, ContractData, RegisterRequest, - ServerPolicy, + AccountPolicy, SignerAPI } from '../types/index.js' @@ -66,11 +66,11 @@ export function validate_session_token ( } export function verify_account_req ( - policy : ServerPolicy, + policy : AccountPolicy, request : AccountRequest ) { // Unpack policy object. - const { LOCKTIME_MIN, LOCKTIME_MAX } = policy.account + const { LOCKTIME_MIN, LOCKTIME_MAX } = policy // Unpack request object. const { deposit_pk, locktime, network, return_addr } = request // Normalize the test network label before verification. @@ -89,7 +89,7 @@ export function verify_account_req ( } export function verify_register_req ( - policy : ServerPolicy, + policy : AccountPolicy, request : RegisterRequest, signer : SignerAPI ) { @@ -107,7 +107,7 @@ export function verify_register_req ( export function verify_commit_req ( contract : ContractData, - policy : ServerPolicy, + policy : AccountPolicy, request : CommitRequest, server_sd : SignerAPI ) { diff --git a/src/core/validation/contract.ts b/src/core/validation/contract.ts index 134a2ad2..3f8efc5e 100644 --- a/src/core/validation/contract.ts +++ b/src/core/validation/contract.ts @@ -32,11 +32,10 @@ import { ContractRequest, FundingData, ProposalData, - ServerPolicy, VMData, ScriptEngineAPI, - ContractVerifyConfig, - WitnessData + WitnessData, + ProposalPolicy } from '../types/index.js' import ContractSchema from '../schema/contract.js' @@ -63,7 +62,7 @@ export function validate_contract_data ( export function verify_contract_req ( machine : ScriptEngineAPI, - policy : ServerPolicy, + policy : ProposalPolicy, request : ContractRequest ) { const { endorsements, proposal } = request @@ -73,8 +72,8 @@ export function verify_contract_req ( } export function verify_contract_publishing ( - contract : ContractData, - proposal : ProposalData + contract : ContractData, + proposal : ProposalData ) { const { created_at, fees } = contract const out = create_spend_templates(proposal, fees) @@ -102,33 +101,6 @@ export function verify_endorsements ( } } -export function verify_contract ( - config : ContractVerifyConfig -) { - const { contract, commits, engine, funds, pubkey, vmdata } = config - verify_contract_data(contract) - verify_contract_sigs(contract, pubkey) - if (contract.secured) { - assert.exists(funds, 'you must provide a list of funds to verify') - verify_contract_funding(contract, funds) - } - if (contract.activated) { - assert.exists(vmdata, 'you must provide a vmdata object to verify.') - verify_contract_activation(contract, vmdata) - } - if (contract.closed) { - assert.exists(commits, 'you must provide a list of witness commits to verify.') - assert.exists(engine, 'you must provide a script engine to use for verification.') - assert.exists(vmdata, 'you must provide a vmdata object to verify.') - verify_contract_close(contract, vmdata) - } - if (contract.spent) { - assert.exists(funds, 'you must provide a list of funds to verify') - assert.exists(vmdata, 'you must provide a vmdata object to verify.') - verify_contract_spending(contract, funds, vmdata) - } -} - export function verify_contract_data (contract : ContractData) { const { created_at, outputs, terms } = contract const pid = get_proposal_id(terms) @@ -241,15 +213,37 @@ export function verify_contract_spending ( assert.ok(txid === contract.spent_txid, 'spent txid does not match contract') } +export function verify_contract_settlement ( + contract : ContractData, + engine : ScriptEngineAPI, + funds : FundingData[], + statements : WitnessData[], + vmdata : VMData +) { + verify_contract_data(contract) + verify_contract_funding(contract, funds) + verify_contract_activation(contract, vmdata) + verify_contract_execution(contract, engine, vmdata, statements) + verify_contract_close(contract, vmdata) + verify_contract_spending(contract, funds, vmdata) +} + export default { validate : { request : validate_publish_req, data : validate_contract_data }, verify : { - request : verify_contract_req, - publish : verify_contract_publishing, - data : verify_contract, - signatures : verify_contract_sigs + request : verify_contract_req, + published : verify_contract_publishing, + endorsements : verify_endorsements, + data : verify_contract_data, + secured : verify_contract_funding, + active : verify_contract_activation, + execution : verify_contract_execution, + closed : verify_contract_close, + spend : verify_contract_spending, + signatures : verify_contract_sigs, + settlement : verify_contract_settlement } } diff --git a/src/core/validation/deposit.ts b/src/core/validation/deposit.ts index 1fffddfd..9ebafc00 100644 --- a/src/core/validation/deposit.ts +++ b/src/core/validation/deposit.ts @@ -18,16 +18,16 @@ import { } from '../module/deposit/util.js' import { + AccountPolicy, CloseRequest, CommitRequest, ContractData, DepositData, DepositStatus, LockRequest, - OracleTxRecvStatus, RegisterRequest, - ServerPolicy, - SignerAPI + SignerAPI, + TxConfirmState } from '../types/index.js' import DepositSchema from '../schema/deposit.js' @@ -72,7 +72,7 @@ export function verify_lock_req ( export function verify_close_req ( deposit : DepositData, - policy : ServerPolicy, + policy : AccountPolicy, request : CloseRequest ) { const psig = request.return_psig @@ -112,10 +112,10 @@ export function verify_deposit_sigs ( export function verify_feerate ( feerate : number, - policy : ServerPolicy + policy : AccountPolicy ) { // - const { FEERATE_MIN, FEERATE_MAX } = policy.account + const { FEERATE_MIN, FEERATE_MAX } = policy // Assert that all terms are valid. assert.ok(feerate >= FEERATE_MIN, `feerate is below threshold: ${feerate} < ${FEERATE_MIN}`) assert.ok(feerate <= FEERATE_MAX, `feerate is above threshold: ${feerate} > ${FEERATE_MAX}`) @@ -146,12 +146,12 @@ export function verify_utxo ( export function verify_utxo_lock ( locktime : number, - policy : ServerPolicy, - status : OracleTxRecvStatus, + policy : AccountPolicy, + state : TxConfirmState, current = now() ) { - const limit = current - policy.account.GRACE_PERIOD - if (status.confirmed && status.block_time + locktime <= limit) { + const limit = current - policy.GRACE_PERIOD + if (state.confirmed && state.block_time + locktime <= limit) { throw new Error('Deposit lock is expiring within the grace period.') } } diff --git a/src/core/validation/proposal.ts b/src/core/validation/proposal.ts index c5cc1957..44e33345 100644 --- a/src/core/validation/proposal.ts +++ b/src/core/validation/proposal.ts @@ -15,8 +15,8 @@ import { import { ProposalData, + ProposalPolicy, ProposalTemplate, - ServerPolicy, ScriptEngineAPI } from '../types/index.js' @@ -45,7 +45,7 @@ export function validate_proposal_data ( export function verify_proposal_data ( machine : ScriptEngineAPI, - policy : ServerPolicy, + policy : ProposalPolicy, proposal : ProposalData ) { // Check if feerate is valid. @@ -61,14 +61,14 @@ export function verify_proposal_data ( } function check_feerate ( - policy : ServerPolicy, + policy : ProposalPolicy, proposal : ProposalData ) { const { feerate } = proposal // if (feerate !== undefined) { // - const { FEERATE_MIN, FEERATE_MAX } = policy.proposal + const { FEERATE_MIN, FEERATE_MAX } = policy // Assert that all terms are valid. assert.ok(feerate >= FEERATE_MIN, `feerate is below threshold: ${feerate} < ${FEERATE_MIN}`) assert.ok(feerate <= FEERATE_MAX, `feerate is above threshold: ${feerate} > ${FEERATE_MAX}`) @@ -126,13 +126,13 @@ function check_schedule ( } function check_stamps ( - policy : ServerPolicy, + policy : ProposalPolicy, proposal : ProposalData ) { const { effective, duration } = proposal const current = now() const deadline = proposal.deadline - const terms = policy.proposal + const terms = policy if (duration < terms.DURATION_MIN) { throw new Error(`The specified contract duration is below the minimum allowed: ${duration} < ${terms.DURATION_MIN}`) diff --git a/test/src/core.ts b/test/src/core.ts index 8199faa5..5262188f 100644 --- a/test/src/core.ts +++ b/test/src/core.ts @@ -1,6 +1,6 @@ import { Buff } from '@cmdcode/buff' import { Signer, Wallet } from '@cmdcode/signer' -import { get_confirm_state } from '@/core/module/deposit/index.js' +import { get_confirm_state } from '@/client/lib/oracle.js' import { TxOutput } from '@/core/types/index.js' import { CoreSigner } from './types.js' diff --git a/test/src/e2e/recover.test.ts b/test/src/e2e/recover.test.ts index b01bf2a3..274780db 100644 --- a/test/src/e2e/recover.test.ts +++ b/test/src/e2e/recover.test.ts @@ -71,7 +71,7 @@ export default async function ( // Client: Create account request. const acct_req = create_account_req(funder_dev.pubkey, LOCKTIME, NETWORK, return_addr) // Server: Verify account request. - verify_account_req(server_pol, acct_req) + verify_account_req(server_pol.account, acct_req) // Server: Create account data. const account = create_account(acct_req, escrow_dev) // Client: Verify account data. @@ -93,7 +93,7 @@ export default async function ( // Client: Create the commit request. const reg_req = create_register_req(FEERATE, account, funder_dev, utxo) // Server: Verify the registration request. - verify_register_req(server_pol, reg_req, escrow_dev) + verify_register_req(server_pol.account, reg_req, escrow_dev) // Server: Create the deposit data. const deposit = create_deposit(reg_req, escrow_dev) // Client: Verify the deposit data. diff --git a/test/src/e2e/return.test.ts b/test/src/e2e/return.test.ts index e4bf1a7e..2b8062d2 100644 --- a/test/src/e2e/return.test.ts +++ b/test/src/e2e/return.test.ts @@ -71,7 +71,7 @@ export default async function ( // Client: Create account request. const acct_req = create_account_req(funder_dev.pubkey, LOCKTIME, NETWORK, return_addr) // Server: Verify account request. - verify_account_req(server_pol, acct_req) + verify_account_req(server_pol.account, acct_req) // Server: Create account data. const account = create_account(acct_req, escrow_dev) // Client: Verify account data. @@ -93,7 +93,7 @@ export default async function ( // Client: Create the commit request. const reg_req = create_register_req(FEERATE, account, funder_dev, utxo) // Server: Verify the registration request. - verify_register_req(server_pol, reg_req, escrow_dev) + verify_register_req(server_pol.account, reg_req, escrow_dev) // Server: Create the deposit data. let deposit = create_deposit(reg_req, escrow_dev) // Client: Verify the deposit data. diff --git a/test/src/e2e/settle.test.ts b/test/src/e2e/settle.test.ts index b41c6a69..5c412d6c 100644 --- a/test/src/e2e/settle.test.ts +++ b/test/src/e2e/settle.test.ts @@ -52,7 +52,7 @@ import { verify_witness_data, verify_contract_sigs, verify_deposit_sigs, - verify_contract + verify_contract_settlement } from '@/core/validation/index.js' import { @@ -109,7 +109,7 @@ export default async function ( // Construct a proposal from the template. const proposal = await get_proposal(members) // Verify the proposal - verify_proposal_data(CVM, server_pol, proposal) + verify_proposal_data(CVM, server_pol.proposal, proposal) // Have each member endorse the proposal. const signatures = members.map(e => endorse_proposal(proposal, e.signer)) @@ -125,7 +125,7 @@ export default async function ( // Client: Create a contract request. const pub_req = create_publish_req(proposal, signatures) // Server: Verify contract request. - CoreAssert.contract.verify.request(CVM, server_pol, pub_req) + CoreAssert.contract.verify.request(CVM, server_pol.proposal, pub_req) // Server: Create contract data. let contract = create_contract(ct_config, pub_req, escrow_dev) @@ -148,7 +148,7 @@ export default async function ( // Client: Create account request. const acct_req = create_account_req(funder.signer.pubkey, LOCKTIME, NETWORK, return_addr) // Server: Verify account request. - verify_account_req(server_pol, acct_req) + verify_account_req(server_pol.account, acct_req) // Server: Create account data. const account = create_account(acct_req, escrow_dev) // Client: Verify account data. @@ -176,7 +176,7 @@ export default async function ( // Client: Create the commit request. const commit_req = create_commit_req(FEERATE, contract, account, funder.signer, utxo) // Server: Verify the registration request. - verify_commit_req(contract, server_pol, commit_req, escrow_dev) + verify_commit_req(contract, server_pol.account, commit_req, escrow_dev) // Server: Create the deposit data. const deposit = create_deposit(commit_req, escrow_dev) // Client: Verify the deposit data. @@ -297,20 +297,14 @@ export default async function ( /* ------------------- [ Settle Contract ] ------------------- */ - const txhex = get_settlement_tx(contract, deposits, escrow_dev) - const txid = await client.publish_tx(txhex, true) + const commits = [ witness ] + const txhex = get_settlement_tx(contract, deposits, escrow_dev) + const txid = await client.publish_tx(txhex, true) contract = spend_contract(contract, txhex, txid, escrow_dev) contract = settle_contract(contract, escrow_dev) - - verify_contract({ - contract, - commits : [ witness ], - engine : CVM, - funds : deposits, - pubkey : escrow_dev.pubkey, - vmdata : vm_data - }) + + verify_contract_settlement(contract, CVM, deposits, commits, vm_data) if (VERBOSE) { console.log(banner('settled contract')) From 3c0a38b1acbe5fdedf55c5897fe017bce5fb4c9d Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 13:31:41 -0500 Subject: [PATCH 05/27] v0.12.34-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a47c8a36..85d8742a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.33-dev", + "version": "0.12.34-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From e386426fae047cf03cfb675c67d6072c433c2d84 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 13:40:44 -0500 Subject: [PATCH 06/27] update --- src/client/api/client/account.ts | 2 +- src/client/api/client/contract.ts | 2 +- src/client/api/client/draft.ts | 4 ++-- src/client/api/client/oracle.ts | 7 +------ src/client/api/client/server.ts | 4 ++-- src/client/api/client/witness.ts | 2 +- src/client/api/signer/account.ts | 13 ++++++++----- src/client/api/signer/contract.ts | 2 +- src/client/api/signer/deposit.ts | 2 +- src/client/api/signer/draft.ts | 8 ++++---- src/client/api/signer/vm.ts | 2 +- src/client/api/signer/wallet.ts | 2 +- src/client/api/signer/witness.ts | 2 +- src/client/class/client.ts | 12 ++++++------ src/client/class/signer.ts | 9 ++++++--- src/client/config.ts | 15 --------------- src/client/config/index.ts | 15 +++++++++++++++ src/client/index.ts | 8 +++++--- src/client/lib/enrollment.ts | 2 +- src/client/lib/index.ts | 1 + src/client/lib/membership.ts | 2 +- src/client/lib/session.ts | 14 +++++++------- src/client/util/index.ts | 2 ++ src/client/util/oracle.ts | 2 +- src/core/lib/covenant.ts | 4 ++-- src/core/lib/recovery.ts | 4 ++-- src/core/module/account/data.ts | 10 +++++----- src/core/module/account/util.ts | 10 +++++----- src/core/module/contract/data.ts | 2 +- src/core/module/contract/util.ts | 4 ++-- src/core/module/deposit/data.ts | 2 +- src/core/module/deposit/util.ts | 4 ++-- src/core/module/witness/data.ts | 4 ++-- src/core/module/witness/util.ts | 4 ++-- src/core/schema/account.ts | 8 ++++---- src/core/schema/contract.ts | 2 +- src/core/schema/deposit.ts | 4 ++-- src/core/schema/witness.ts | 2 +- src/core/types/account.ts | 6 +++--- src/core/types/contract.ts | 2 +- src/core/types/deposit.ts | 4 ++-- src/core/types/witness.ts | 2 +- src/core/validation/account.ts | 18 +++++++++--------- src/core/validation/covenant.ts | 6 +++--- src/core/validation/deposit.ts | 4 ++-- src/core/validation/witness.ts | 4 ++-- 46 files changed, 125 insertions(+), 119 deletions(-) delete mode 100644 src/client/config.ts create mode 100644 src/client/config/index.ts create mode 100644 src/client/util/index.ts diff --git a/src/client/api/client/account.ts b/src/client/api/client/account.ts index 09d04ab9..a2b3df45 100644 --- a/src/client/api/client/account.ts +++ b/src/client/api/client/account.ts @@ -1,5 +1,5 @@ import { EscrowClient } from '@/client/class/client.js' -import { DEFAULT_POLICY } from '@/client/config.js' +import { DEFAULT_POLICY } from '@/client/config/index.js' import { validate_account_req, diff --git a/src/client/api/client/contract.ts b/src/client/api/client/contract.ts index 7e7c2b5f..a243b886 100644 --- a/src/client/api/client/contract.ts +++ b/src/client/api/client/contract.ts @@ -1,7 +1,7 @@ import { assert, parse_proposal } from '@/core/util/index.js' import { create_publish_req } from '@/core/module/contract/index.js' import { EscrowClient } from '@/client/class/client.js' -import { DEFAULT_POLICY } from '@/client/config.js' +import { DEFAULT_POLICY } from '@/client/config/index.js' import { verify_endorsements, diff --git a/src/client/api/client/draft.ts b/src/client/api/client/draft.ts index 1f21176b..0f533e68 100644 --- a/src/client/api/client/draft.ts +++ b/src/client/api/client/draft.ts @@ -1,11 +1,11 @@ -import { EscrowClient } from '../../class/client.js' +import { EscrowClient } from '@/client/class/client.js' import { create_session, decode_session, encode_session, publish_session -} from '../../lib/session.js' +} from '@/client/lib/session.js' export default function (_client : EscrowClient) { return { diff --git a/src/client/api/client/oracle.ts b/src/client/api/client/oracle.ts index f7c2602f..0be1ac78 100644 --- a/src/client/api/client/oracle.ts +++ b/src/client/api/client/oracle.ts @@ -1,6 +1,5 @@ -/* Global Imports */ - import { assert, sleep } from '@/core/util/index.js' +import { EscrowClient } from '@/client/class/client.js' import { broadcast_tx, @@ -17,10 +16,6 @@ import { OracleTxSpendData } from '@/client/types/index.js' -/* Module Imports */ - -import { EscrowClient } from '@/client/class/client.js' - function broadcast_tx_api (client : EscrowClient) { return async (txhex : string) => { assert.is_hex(txhex) diff --git a/src/client/api/client/server.ts b/src/client/api/client/server.ts index 8dc7cd62..2597a21b 100644 --- a/src/client/api/client/server.ts +++ b/src/client/api/client/server.ts @@ -1,3 +1,5 @@ +import { EscrowClient } from '@/client/class/client.js' + import { ApiResponse, ServerKeysResponse, @@ -5,8 +7,6 @@ import { ServerStatusResponse } from '@/client/types/index.js' -import { EscrowClient } from '../../class/client.js' - function server_keys_api (client : EscrowClient) { return async () : Promise> => { // Formulate the request. diff --git a/src/client/api/client/witness.ts b/src/client/api/client/witness.ts index 3431627d..df2db96f 100644 --- a/src/client/api/client/witness.ts +++ b/src/client/api/client/witness.ts @@ -1,5 +1,5 @@ import { assert } from '@/core/util/index.js' -import { EscrowClient } from '../../class/client.js' +import { EscrowClient } from '@/client/class/client.js' import { ApiResponse, diff --git a/src/client/api/signer/account.ts b/src/client/api/signer/account.ts index 5ac41ee7..a116e456 100644 --- a/src/client/api/signer/account.ts +++ b/src/client/api/signer/account.ts @@ -1,9 +1,12 @@ import { create_account_req } from '@/core/module/account/index.js' import { verify_account_data } from '@/core/validation/account.js' import { assert, parse_err } from '@/core/util/index.js' -import { EscrowSigner } from '../../class/signer.js' +import { EscrowSigner } from '@/client/class/signer.js' -import { create_commit_req, create_register_req } from '@/core/module/account/api.js' +import { + create_commit_req, + create_register_req +} from '@/core/module/account/api.js' import { AccountRequest, @@ -31,7 +34,7 @@ export function verify_account_api (esigner : EscrowSigner) { // Unpack terms from the esigner. const { server_pk, _signer } = esigner // Assert the correct pubkey is used by the server. - assert.ok(server_pk === account.server_pk, 'invalid server pubkey') + assert.ok(server_pk === account.agent_pk, 'invalid server pubkey') // Verify the account data. verify_account_data(account, _signer) // Return null on success. @@ -51,7 +54,7 @@ export function register_funds_api (esigner : EscrowSigner) { ) : RegisterRequest => { const { server_pk, _signer } = esigner // Assert the correct pubkey is used by the server. - assert.ok(server_pk === account.server_pk, 'invalid server pubkey') + assert.ok(server_pk === account.agent_pk, 'invalid server pubkey') verify_account_data(account, _signer) return create_register_req(feerate, account, _signer, utxo) } @@ -66,7 +69,7 @@ export function commit_funds_api (esigner : EscrowSigner) { ) : CommitRequest => { const { server_pk, _signer } = esigner // Assert the correct pubkey is used by the server. - assert.ok(server_pk === account.server_pk, 'invalid server pubkey') + assert.ok(server_pk === account.agent_pk, 'invalid server pubkey') verify_account_data(account, _signer) return create_commit_req(feerate, contract, account, _signer, utxo) } diff --git a/src/client/api/signer/contract.ts b/src/client/api/signer/contract.ts index b747b5d8..1b1bcef0 100644 --- a/src/client/api/signer/contract.ts +++ b/src/client/api/signer/contract.ts @@ -1,4 +1,4 @@ -import { EscrowSigner } from '../../class/signer.js' +import { EscrowSigner } from '@/client/class/signer.js' export function request_contracts_api (esigner : EscrowSigner) { return () => { diff --git a/src/client/api/signer/deposit.ts b/src/client/api/signer/deposit.ts index 0a965c37..0b53fd70 100644 --- a/src/client/api/signer/deposit.ts +++ b/src/client/api/signer/deposit.ts @@ -1,5 +1,5 @@ import { assert } from '@/core/util/index.js' -import { EscrowSigner } from '../../class/signer.js' +import { EscrowSigner } from '@/client/class/signer.js' import { verify_contract_data, diff --git a/src/client/api/signer/draft.ts b/src/client/api/signer/draft.ts index 871cf276..59214ce5 100644 --- a/src/client/api/signer/draft.ts +++ b/src/client/api/signer/draft.ts @@ -1,4 +1,4 @@ -import { EscrowSigner } from '../../class/signer.js' +import { EscrowSigner } from '@/client/class/signer.js' import { get_proposal_id, @@ -8,19 +8,19 @@ import { import { CredentialConfig, DraftSession -} from '../../types/base.js' +} from '@/client/types/index.js' import { claim_membership, create_credential -} from '../../lib/membership.js' +} from '@/client/lib/membership.js' import { endorse_session, join_session, leave_session, verify_session -} from '../../lib/session.js' +} from '@/client/lib/session.js' export function join_session_api (esigner : EscrowSigner) { return ( diff --git a/src/client/api/signer/vm.ts b/src/client/api/signer/vm.ts index 466a831a..4987586c 100644 --- a/src/client/api/signer/vm.ts +++ b/src/client/api/signer/vm.ts @@ -1,4 +1,4 @@ -import { EscrowSigner } from '../../class/signer.js' +import { EscrowSigner } from '@/client/class/signer.js' export function list_machines_api (esigner : EscrowSigner) { return () => { diff --git a/src/client/api/signer/wallet.ts b/src/client/api/signer/wallet.ts index 36eb894a..0e51d6f1 100644 --- a/src/client/api/signer/wallet.ts +++ b/src/client/api/signer/wallet.ts @@ -1,5 +1,5 @@ import { AddressConfig } from '@cmdcode/signer' -import { EscrowSigner } from '../../class/signer.js' +import { EscrowSigner } from '@/client/class/signer.js' export function has_address_api (esigner : EscrowSigner) { return ( diff --git a/src/client/api/signer/witness.ts b/src/client/api/signer/witness.ts index d76dca3e..431d5b50 100644 --- a/src/client/api/signer/witness.ts +++ b/src/client/api/signer/witness.ts @@ -1,5 +1,5 @@ import { verify_witness_data } from '@/core/validation/witness.js' -import { EscrowSigner } from '../../class/signer.js' +import { EscrowSigner } from '@/client/class/signer.js' import { can_endorse, diff --git a/src/client/class/client.ts b/src/client/class/client.ts index c33686a3..cbfee714 100644 --- a/src/client/class/client.ts +++ b/src/client/class/client.ts @@ -1,17 +1,17 @@ -import { resolve_json } from '../util/fetch.js' +import { resolve_json } from '@/client/util/fetch.js' import { Network } from '@/core/types/index.js' -import { ApiResponse } from '@/client/types/index.js' import { DEFAULT_CONFIG, get_client_config -} from '../config.js' +} from '@/client/config/index.js' import { + ApiResponse, ClientConfig, ClientOptions, FetchConfig -} from '../types/base.js' +} from '@/client/types/index.js' import account_api from '../api/client/account.js' import contract_api from '../api/client/contract.js' @@ -22,7 +22,7 @@ import server_api from '../api/client/server.js' import vmachine_api from '../api/client/vm.js' import witness_api from '../api/client/witness.js' -import ClientSchema from '../schema/base.js' +import ClientSchema from '../schema/index.js' type Resolver = ReturnType @@ -34,7 +34,7 @@ export class EscrowClient { const options = { ...DEFAULT_CONFIG, ...opt } const client = get_client_config(opt.network as Network) const config = { ...client, ...options } - this._config = ClientSchema.client_config.parse(config) + this._config = ClientSchema.base.client_config.parse(config) this._fetcher = get_fetcher(opt.fetcher ?? fetch) } diff --git a/src/client/class/signer.ts b/src/client/class/signer.ts index 081f984e..4477e1bd 100644 --- a/src/client/class/signer.ts +++ b/src/client/class/signer.ts @@ -1,7 +1,10 @@ import { Buff, Bytes } from '@cmdcode/buff' import { Network } from '@/core/types/index.js' -import { DEFAULT_CONFIG, get_client_config } from '../config.js' +import { + DEFAULT_CONFIG, + get_client_config +} from '@/client/config/index.js' import { Seed, @@ -24,7 +27,7 @@ import machine_api from '../api/signer/vm.js' import wallet_api from '../api/signer/wallet.js' import witness_api from '../api/signer/witness.js' -import ClientSchema from '../schema/base.js' +import ClientSchema from '../schema/index.js' export class EscrowSigner { static create ( @@ -90,7 +93,7 @@ export class EscrowSigner { const config = { ...client, ...opt } const xpub = options.xpub ?? signer.xpub - this._config = ClientSchema.signer_config.parse(config) + this._config = ClientSchema.base.signer_config.parse(config) this._signer = signer this._wallet = new Wallet(xpub) } diff --git a/src/client/config.ts b/src/client/config.ts deleted file mode 100644 index ac458edc..00000000 --- a/src/client/config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Network } from '@/core/types/index.js' -import ServerPolicy from './config/policy.js' -import ServerConfigs from './config/settings.js' - -export const DEFAULT_NETWORK = 'mutiny' - -export const DEFAULT_CONFIG = { - network : DEFAULT_NETWORK -} - -export const DEFAULT_POLICY = ServerPolicy - -export function get_client_config (network : Network) { - return ServerConfigs[network] -} diff --git a/src/client/config/index.ts b/src/client/config/index.ts new file mode 100644 index 00000000..2d951183 --- /dev/null +++ b/src/client/config/index.ts @@ -0,0 +1,15 @@ +import { Network } from '@/core/types/index.js' +import DEFAULT_POLICY from './policy.js' +import DEFAULT_SERVERS from './settings.js' + +export const DEFAULT_NETWORK = 'mutiny' + +export const DEFAULT_CONFIG = { + network : DEFAULT_NETWORK +} + +export function get_client_config (network : Network) { + return DEFAULT_SERVERS[network] +} + +export { DEFAULT_POLICY, DEFAULT_SERVERS } diff --git a/src/client/index.ts b/src/client/index.ts index 1dbcfa54..91533909 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,9 +1,11 @@ export * from './class/client.js' export * from './class/signer.js' -export * from './types/base.js' +export * from './config/index.js' +export * from './types/index.js' -export * as ClientConfig from './config.js' export * as ClientLib from './lib/index.js' -export * as ClientSchema from './schema/base.js' +export * as ClientSchema from './schema/index.js' +export * as ClientUtil from './util/index.js' +export * as ClientVerify from './validation/index.js' export { DraftUtil } from './lib/session.js' diff --git a/src/client/lib/enrollment.ts b/src/client/lib/enrollment.ts index fa52d34b..1e1c659a 100644 --- a/src/client/lib/enrollment.ts +++ b/src/client/lib/enrollment.ts @@ -9,7 +9,7 @@ import { RoleTemplate, DraftSession, CredentialData -} from '../types/base.js' +} from '@/client/types/index.js' const GET_ROLE_DEFAULTS = () => { return { diff --git a/src/client/lib/index.ts b/src/client/lib/index.ts index 09a2f169..035bb95e 100644 --- a/src/client/lib/index.ts +++ b/src/client/lib/index.ts @@ -1,3 +1,4 @@ export * as members from './membership.js' +export * as oracle from './oracle.js' export * as roles from './enrollment.js' export * as session from './session.js' diff --git a/src/client/lib/membership.ts b/src/client/lib/membership.ts index 3fa54b6f..f2a1abe4 100644 --- a/src/client/lib/membership.ts +++ b/src/client/lib/membership.ts @@ -5,7 +5,7 @@ import { CredentialData, DraftSession, MemberData -} from '../types/base.js' +} from '@/client/types/index.js' export function create_credential ( signer : SignerAPI, diff --git a/src/client/lib/session.ts b/src/client/lib/session.ts index 849bd0af..35752c86 100644 --- a/src/client/lib/session.ts +++ b/src/client/lib/session.ts @@ -20,9 +20,9 @@ import { DraftSession, CredentialData, DraftTemplate -} from '../types/base.js' +} from '@/client/types/index.js' -import ClientSchema from '../schema/base.js' +import ClientSchema from '@/client/schema/index.js' import { add_member_data, @@ -62,7 +62,7 @@ export function join_session ( // Add member to proposal. const proposal = add_member_data(cred, policy, session.proposal) // - return ClientSchema.session.parse({ ...session, members, proposal }) + return ClientSchema.draft.session.parse({ ...session, members, proposal }) } export function leave_session ( @@ -74,11 +74,11 @@ export function leave_session ( // Add member to proposal. const proposal = rem_member_data(cred, session.proposal) // - return ClientSchema.session.parse({ ...session, members, proposal }) + return ClientSchema.draft.session.parse({ ...session, members, proposal }) } export function reset_session (session : DraftSession) { - return ClientSchema.session.parse({ ...session, sigs: [] }) + return ClientSchema.draft.session.parse({ ...session, sigs: [] }) } export function endorse_session ( @@ -87,7 +87,7 @@ export function endorse_session ( ) : DraftSession { const sig = endorse_proposal(session.proposal, signer) const sigs = [ ...session.sigs, sig ] - return ClientSchema.session.parse({ ...session, sigs }) + return ClientSchema.draft.session.parse({ ...session, sigs }) } export function tabualte_session (session : DraftSession) { @@ -158,7 +158,7 @@ export function encode_session (session : DraftSession) { export function decode_session (session_str : string) : DraftSession { const json = Buff.b64url(session_str).str const data = JSON.parse(json) - return ClientSchema.session.parse(data) + return ClientSchema.draft.session.parse(data) } export const DraftUtil = { diff --git a/src/client/util/index.ts b/src/client/util/index.ts new file mode 100644 index 00000000..a3433f4c --- /dev/null +++ b/src/client/util/index.ts @@ -0,0 +1,2 @@ +export * from './fetch.js' +export * from './oracle.js' diff --git a/src/client/util/oracle.ts b/src/client/util/oracle.ts index c2510c9c..b873bf5b 100644 --- a/src/client/util/oracle.ts +++ b/src/client/util/oracle.ts @@ -4,7 +4,7 @@ import { parse_addr } from '@scrow/tapscript/address' /* Module Imports */ -import { exists } from '../../core/util/validate.js' +import { exists } from '@/core/util/validate.js' import { fetcher, resolve_json } from './fetch.js' /* Local Imports */ diff --git a/src/core/lib/covenant.ts b/src/core/lib/covenant.ts index 81532497..a02ee69e 100644 --- a/src/core/lib/covenant.ts +++ b/src/core/lib/covenant.ts @@ -70,7 +70,7 @@ export function settle_covenant ( deposit : DepositData, output : string, psig : string, - server_sd : SignerAPI + agent_dev : SignerAPI ) : string { // assert.exists(deposit.covenant) @@ -79,7 +79,7 @@ export function settle_covenant ( // const { covenant, utxo } = deposit // Get signing agent for account. - const agent = get_account_agent(deposit, server_sd) + const agent = get_account_agent(deposit, agent_dev) // Get account context object. const ctx = get_account_ctx(deposit) // Compute covenant id diff --git a/src/core/lib/recovery.ts b/src/core/lib/recovery.ts index d7680675..2a80367a 100644 --- a/src/core/lib/recovery.ts +++ b/src/core/lib/recovery.ts @@ -74,8 +74,8 @@ export function get_recovery_tx ( export function get_recovery_config ( account : AccountTemplate ) : RecoveryConfig { - const { server_tkn, deposit_pk, locktime, network, return_addr } = account - const session = parse_session_token(server_tkn) + const { agent_tkn, deposit_pk, locktime, network, return_addr } = account + const session = parse_session_token(agent_tkn) return { agent_pk: session.pk, deposit_pk, locktime, network, return_addr } } diff --git a/src/core/module/account/data.ts b/src/core/module/account/data.ts index 2cf26840..f829d558 100644 --- a/src/core/module/account/data.ts +++ b/src/core/module/account/data.ts @@ -32,19 +32,19 @@ export function create_account ( // Get signing agent for account. const agent = util.get_account_agent(request, signer) // Generate a session token. - const server_tkn = gen_session_token(agent, created_at).tkn + const agent_tkn = gen_session_token(agent, created_at).tkn // Create a context object for the account. - const acct_ctx = util.create_account_ctx(deposit_pk, locktime, network, return_addr, server_tkn) + const acct_ctx = util.create_account_ctx(deposit_pk, locktime, network, return_addr, agent_tkn) // Compute the deposit address from the account context. const deposit_addr = acct_ctx.deposit_addr // Compute the hash for the account request. const account_hash = util.get_account_hash(request) // Set the server pubkey. - const server_pk = signer.pubkey + const agent_pk = signer.pubkey // Compute the id for the account data. - const account_id = util.get_account_id(deposit_addr, account_hash, server_pk, created_at, server_tkn) + const account_id = util.get_account_id(deposit_addr, account_hash, agent_pk, created_at, agent_tkn) // Sign the account identifier. const account_sig = signer.sign(account_id) // Return the complete account data object. - return sort_record({ ...request, account_id, account_hash, account_sig, created_at, deposit_addr, server_pk, server_tkn }) + return sort_record({ ...request, account_id, account_hash, account_sig, created_at, deposit_addr, agent_pk, agent_tkn }) } diff --git a/src/core/module/account/util.ts b/src/core/module/account/util.ts index 700b40ba..71b83fbd 100644 --- a/src/core/module/account/util.ts +++ b/src/core/module/account/util.ts @@ -77,9 +77,9 @@ export function create_account_ctx ( */ export function get_account_ctx (template : AccountTemplate) { // Unpack the account object. - const { deposit_pk, locktime, network, return_addr, server_tkn } = template + const { deposit_pk, locktime, network, return_addr, agent_tkn } = template // Return the account context object. - return create_account_ctx(deposit_pk, locktime, network, return_addr, server_tkn) + return create_account_ctx(deposit_pk, locktime, network, return_addr, agent_tkn) } /** @@ -100,7 +100,7 @@ export function get_deposit_hash ( request : RegisterTemplate | DepositData ) { const hash = get_account_hash(request) - const agnt = Buff.hex(request.server_tkn) + const agnt = Buff.hex(request.agent_tkn) const rate = Buff.num(request.return_rate, 4) const utxo = get_utxo_bytes(request.utxo) const pimg = Buff.join([ hash, agnt, rate, utxo ]) @@ -127,10 +127,10 @@ export function get_account_id ( export function get_account_agent ( request : AccountRequest, - server_sd : SignerAPI + agent : SignerAPI ) { // Get account request hash. const hash = get_account_hash(request) // Get signing agent for account. - return server_sd.get_id(hash) + return agent.get_id(hash) } diff --git a/src/core/module/contract/data.ts b/src/core/module/contract/data.ts index 986aab85..be930bcf 100644 --- a/src/core/module/contract/data.ts +++ b/src/core/module/contract/data.ts @@ -53,6 +53,7 @@ export function create_contract ( // Return a completed contract. const template = { ...GET_PUBLISH_STATE(), + agent_pk : signer.pubkey, cid, created_at, fees, @@ -62,7 +63,6 @@ export function create_contract ( moderator : request.proposal.moderator ?? null, outputs, prop_id, - server_pk : signer.pubkey, sigs : [], subtotal, terms : sort_record(proposal), diff --git a/src/core/module/contract/util.ts b/src/core/module/contract/util.ts index 66140e5f..df98746d 100644 --- a/src/core/module/contract/util.ts +++ b/src/core/module/contract/util.ts @@ -183,7 +183,7 @@ export function get_contract_preimg ( contract : ContractPreImage, status : ContractStatus ) : NoteTemplate { - const { cid, server_pk: pubkey } = contract + const { cid, agent_pk: pubkey } = contract const { content, created_at } = get_contract_state(contract, status) const kind = CONTRACT_KIND const tags = [ [ 'i', cid ] ] @@ -215,7 +215,7 @@ export function verify_contract_sig ( pubkey : string, status : ContractStatus ) { - const pub = contract.server_pk + const pub = contract.agent_pk const prf = get_contract_proof(contract, status) const img = get_contract_preimg(contract, status) const dig = get_proof_id(img) diff --git a/src/core/module/deposit/data.ts b/src/core/module/deposit/data.ts index 040e53a2..2c9f5878 100644 --- a/src/core/module/deposit/data.ts +++ b/src/core/module/deposit/data.ts @@ -43,11 +43,11 @@ export function create_deposit ( ...GET_REGISTER_STATE(), ...request, account_hash : get_account_hash(request), + agent_pk : signer.pubkey, created_at, deposit_addr, dpid : get_deposit_id(created_at, dp_hash), satpoint : get_satpoint(request.utxo), - server_pk : signer.pubkey, sigs : [], updated_at : created_at } diff --git a/src/core/module/deposit/util.ts b/src/core/module/deposit/util.ts index c5323dd4..b5f13d6a 100644 --- a/src/core/module/deposit/util.ts +++ b/src/core/module/deposit/util.ts @@ -27,7 +27,7 @@ export function get_deposit_preimg ( deposit : DepositPreImage, status : DepositStatus ) : NoteTemplate { - const { dpid, server_pk: pubkey } = deposit + const { dpid, agent_pk: pubkey } = deposit const { content, created_at } = get_deposit_state(deposit, status) const kind = DEPOSIT_KIND const tags = [ [ 'i', dpid ] ] @@ -59,7 +59,7 @@ export function verify_deposit_sig ( pubkey : string, status : DepositStatus ) { - const pub = deposit.server_pk + const pub = deposit.agent_pk const prf = get_deposit_proof(deposit, status) const img = get_deposit_preimg(deposit, status) const dig = get_proof_id(img) diff --git a/src/core/module/witness/data.ts b/src/core/module/witness/data.ts index e56bfc33..04d95b91 100644 --- a/src/core/module/witness/data.ts +++ b/src/core/module/witness/data.ts @@ -18,12 +18,12 @@ export function create_receipt ( witness : WitnessData, receipt_at = now() ) : WitnessReceipt { - const server_pk = signer.pubkey + const agent_pk = signer.pubkey const vm_closed = data.closed const vm_hash = data.head const vm_output = data.output const vm_step = data.step - const preimg = { ...witness, receipt_at, server_pk, vm_closed, vm_hash, vm_output, vm_step } + const preimg = { ...witness, receipt_at, agent_pk, vm_closed, vm_hash, vm_output, vm_step } const receipt_id = get_receipt_id(preimg) const receipt_sig = signer.sign(receipt_id) return sort_record({ ...preimg, receipt_id, receipt_sig }) diff --git a/src/core/module/witness/util.ts b/src/core/module/witness/util.ts index 0ad3ce47..00ee8599 100644 --- a/src/core/module/witness/util.ts +++ b/src/core/module/witness/util.ts @@ -20,7 +20,7 @@ export function get_witness_id ( export function get_receipt_id ( preimage : ReceiptPreImage ) { - const { receipt_at, server_pk, vm_hash, wid } = preimage + const { receipt_at, agent_pk, vm_hash, wid } = preimage const rat = Buff.num(receipt_at, 4) - return Buff.join([ wid, rat, server_pk, vm_hash ]).digest.hex + return Buff.join([ wid, rat, agent_pk, vm_hash ]).digest.hex } diff --git a/src/core/schema/account.ts b/src/core/schema/account.ts index 5f682970..e38748ce 100644 --- a/src/core/schema/account.ts +++ b/src/core/schema/account.ts @@ -21,11 +21,11 @@ const account_req = z.object({ }) const register_req = account_req.extend({ + agent_tkn : token, covenant : covenant.optional(), network, return_psig : hex, return_rate : num, - server_tkn : token, utxo : tx.txout }) @@ -35,14 +35,14 @@ const data = z.object({ account_hash : hash, account_id : hash, account_sig : nonce, + agent_pk : hash, + agent_tkn : token, created_at : stamp, deposit_pk : hash, deposit_addr : str, locktime : num, network, - return_addr : str, - server_pk : hash, - server_tkn : token + return_addr : str }) const policy = z.object({ diff --git a/src/core/schema/contract.ts b/src/core/schema/contract.ts index a3644cdb..2beafc9b 100644 --- a/src/core/schema/contract.ts +++ b/src/core/schema/contract.ts @@ -111,6 +111,7 @@ const close_state = z.discriminatedUnion('closed', [ ct_open, ct_close /* ------------------- [ Contract Schema ] ------------------- */ const base_data = z.object({ + agent_pk : hash, cid : hash, created_at : stamp, created_sig : hex, @@ -123,7 +124,6 @@ const base_data = z.object({ outputs : output.array(), moderator : hash.nullable(), prop_id : hash, - server_pk : hash, sigs : z.tuple([ status, hex ]).array(), status, subtotal : num, diff --git a/src/core/schema/deposit.ts b/src/core/schema/deposit.ts index 8314ebac..fb9e0955 100644 --- a/src/core/schema/deposit.ts +++ b/src/core/schema/deposit.ts @@ -77,6 +77,8 @@ const fund = z.object({ const base_data = z.object({ status, account_hash : hash, + agent_pk : hash, + agent_tkn : hex, created_at : stamp, created_sig : hex, dpid : hash, @@ -88,8 +90,6 @@ const base_data = z.object({ return_psig : hex, return_rate : num, satpoint : str, - server_pk : hash, - server_tkn : hex, sigs : z.tuple([ status, hex ]).array(), updated_at : stamp, utxo : tx.txout diff --git a/src/core/schema/witness.ts b/src/core/schema/witness.ts index 6e5f9c32..85164380 100644 --- a/src/core/schema/witness.ts +++ b/src/core/schema/witness.ts @@ -17,10 +17,10 @@ const data = z.object({ }) const receipt = data.extend({ + agent_pk : hash, receipt_at : stamp, receipt_id : hash, receipt_sig : signature, - server_pk : hash, vm_closed : bool, vm_hash : hash, vm_output : str.nullable(), diff --git a/src/core/types/account.ts b/src/core/types/account.ts index 2ceca8ca..7ea86e43 100644 --- a/src/core/types/account.ts +++ b/src/core/types/account.ts @@ -32,14 +32,14 @@ export interface AccountData { account_hash : string account_id : string account_sig : string + agent_pk : string + agent_tkn : string created_at : number deposit_addr : string deposit_pk : string locktime : number network : Network return_addr : string - server_pk : string - server_tkn : string } export interface AccountTemplate { @@ -47,7 +47,7 @@ export interface AccountTemplate { locktime : number network : Network return_addr : string - server_tkn : string + agent_tkn : string } export interface RegisterTemplate extends AccountTemplate { diff --git a/src/core/types/contract.ts b/src/core/types/contract.ts index afc7cc08..7ea78b5a 100644 --- a/src/core/types/contract.ts +++ b/src/core/types/contract.ts @@ -124,7 +124,7 @@ export interface ContractBase { moderator : string | null outputs : SpendTemplate[] prop_id : string - server_pk : string + agent_pk : string status : ContractStatus subtotal : number terms : ProposalData diff --git a/src/core/types/deposit.ts b/src/core/types/deposit.ts index 771a8b61..16f69d81 100644 --- a/src/core/types/deposit.ts +++ b/src/core/types/deposit.ts @@ -82,8 +82,8 @@ export interface DepositBase { return_rate : number return_psig : string satpoint : string - server_pk : string - server_tkn : string + agent_pk : string + agent_tkn : string status : DepositStatus updated_at : number utxo : TxOutput diff --git a/src/core/types/witness.ts b/src/core/types/witness.ts index aa3a15f1..c9166413 100644 --- a/src/core/types/witness.ts +++ b/src/core/types/witness.ts @@ -27,7 +27,7 @@ export interface WitnessData extends WitnessPreImage { export interface ReceiptPreImage extends WitnessData { receipt_at : number - server_pk : string + agent_pk : string vm_closed : boolean vm_hash : string vm_output : string | null diff --git a/src/core/validation/account.ts b/src/core/validation/account.ts index 215d652b..0933ef22 100644 --- a/src/core/validation/account.ts +++ b/src/core/validation/account.ts @@ -109,24 +109,24 @@ export function verify_commit_req ( contract : ContractData, policy : AccountPolicy, request : CommitRequest, - server_sd : SignerAPI + agent : SignerAPI ) { const covenant = request.covenant - verify_register_req(policy, request, server_sd) - verify_covenant_data(contract, covenant, request, server_sd) + verify_register_req(policy, request, agent) + verify_covenant_data(contract, covenant, request, agent) } export function verify_account_data ( account : AccountData, signer : SignerAPI ) { - const { account_id, created_at, account_sig, deposit_addr, server_pk, server_tkn } = account + const { account_id, created_at, account_sig, deposit_addr, agent_pk, agent_tkn } = account // Create a context object for the account. const ctx = get_account_ctx(account) // const hash = get_account_hash(account) // Compute the id for the account data. - const id = get_account_id(deposit_addr, hash, server_pk, created_at, server_tkn) + const id = get_account_id(deposit_addr, hash, agent_pk, created_at, agent_tkn) // Define the deposit address const addr = account.deposit_addr // Define the deposit pubkey. @@ -135,15 +135,15 @@ export function verify_account_data ( assert.ok(signer.pubkey === pk, 'deposit pubkey does not match signing device') assert.ok(ctx.deposit_addr === addr, 'deposit address does not match signing device') assert.ok(id === account_id, 'account id does not match computed id') - assert.ok(verify_sig(account_sig, id, server_pk), 'server signature is invalid') + assert.ok(verify_sig(account_sig, id, agent_pk), 'server signature is invalid') } export function verify_session_token ( request : RegisterRequest, - server_sd : SignerAPI + agent_dev : SignerAPI ) { - const agent = get_account_agent(request, server_sd) - const sess = parse_session_token(request.server_tkn) + const agent = get_account_agent(request, agent_dev) + const sess = parse_session_token(request.agent_tkn) const seed = get_session_seed(sess.id, agent, sess.ts) const agpn = get_session_pnonce(seed, agent).hex diff --git a/src/core/validation/covenant.ts b/src/core/validation/covenant.ts index bd253cd0..193b9a43 100644 --- a/src/core/validation/covenant.ts +++ b/src/core/validation/covenant.ts @@ -39,14 +39,14 @@ export function verify_covenant_data ( contract : ContractData, covenant : CovenantData, request : RegisterTemplate, - server_sd : SignerAPI + agent_dev : SignerAPI ) { // Unpack contract object. const { activated, cid, outputs, status } = contract // Get the account session agent. - const agent = get_account_agent(request, server_sd) + const agent = get_account_agent(request, agent_dev) // Parse the session token from the request. - const session = parse_session_token(request.server_tkn) + const session = parse_session_token(request.agent_tkn) // Make the following assertions. assert.ok(!activated, 'contract is already active') assert.ok(status === 'published', 'contract is not in a fundable state') diff --git a/src/core/validation/deposit.ts b/src/core/validation/deposit.ts index 9ebafc00..d67429d9 100644 --- a/src/core/validation/deposit.ts +++ b/src/core/validation/deposit.ts @@ -61,13 +61,13 @@ export function verify_lock_req ( contract : ContractData, deposit : DepositData, request : LockRequest, - server_sd : SignerAPI + agent : SignerAPI ) { assert.ok(request.dpid === deposit.dpid) assert.ok(deposit.covenant === null) const covenant = request.covenant verify_lockable(deposit.status) - verify_covenant_data(contract, covenant, deposit, server_sd) + verify_covenant_data(contract, covenant, deposit, agent) } export function verify_close_req ( diff --git a/src/core/validation/witness.ts b/src/core/validation/witness.ts index 1d736610..22a4d931 100644 --- a/src/core/validation/witness.ts +++ b/src/core/validation/witness.ts @@ -100,7 +100,7 @@ export function verify_witness_receipt ( vmdata : VMData, witness : WitnessData ) { - const { receipt_id, receipt_sig, server_pk } = receipt + const { receipt_id, receipt_sig, agent_pk } = receipt // Don't forget to check that vm matches receipt. assert.ok(witness.vmid === vmdata.vmid, 'provided vmdata and witness vmid does not match') @@ -116,7 +116,7 @@ export function verify_witness_receipt ( assert.ok(int_wid === witness.wid, 'internal witness id does not match receipt') assert.ok(int_rid === receipt.receipt_id, 'internal receipt id does not match receipt') - const is_valid = verify_sig(receipt_sig, receipt_id, server_pk) + const is_valid = verify_sig(receipt_sig, receipt_id, agent_pk) assert.ok(is_valid, 'receipt signature is invalid') } From 782431c55e80d057d594024de0fcb3bc1a8228a3 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 13:40:59 -0500 Subject: [PATCH 07/27] v0.12.35-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 85d8742a..31de6509 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.34-dev", + "version": "0.12.35-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From e419020a34b8cb00b0f7afae993f6ccd1066a858 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 16:27:11 -0500 Subject: [PATCH 08/27] update --- demo/05_create_contract.ts | 2 +- demo/09_settle_contract.ts | 4 +- src/client/api/client/oracle.ts | 103 -------------- src/client/api/signer/account.ts | 4 +- src/client/api/signer/contract.ts | 26 +++- src/client/api/signer/deposit.ts | 20 ++- src/client/class/client.ts | 13 +- src/client/class/oracle.ts | 221 ++++++++++++++++++++++++++++++ src/client/class/signer.ts | 2 +- src/client/config/index.ts | 14 +- src/client/index.ts | 12 +- src/client/{util => lib}/fetch.ts | 0 src/client/lib/index.ts | 15 +- src/client/lib/oracle.ts | 25 ---- src/client/schema/base.ts | 16 --- src/client/schema/config.ts | 16 +++ src/client/schema/index.ts | 4 +- src/client/types/contract.ts | 10 +- src/client/util/index.ts | 2 - src/client/util/oracle.ts | 218 ----------------------------- src/client/validation/contract.ts | 40 ------ src/client/validation/deposit.ts | 15 -- src/client/validation/index.ts | 2 - src/core/types/witness.ts | 2 +- src/core/util/index.ts | 2 - src/core/validation/contract.ts | 47 ++++++- src/core/validation/witness.ts | 20 +-- test/src/core.ts | 13 +- 28 files changed, 383 insertions(+), 485 deletions(-) delete mode 100644 src/client/api/client/oracle.ts create mode 100644 src/client/class/oracle.ts rename src/client/{util => lib}/fetch.ts (100%) delete mode 100644 src/client/lib/oracle.ts delete mode 100644 src/client/schema/base.ts create mode 100644 src/client/schema/config.ts delete mode 100644 src/client/util/index.ts delete mode 100644 src/client/util/oracle.ts delete mode 100644 src/client/validation/contract.ts delete mode 100644 src/client/validation/deposit.ts delete mode 100644 src/client/validation/index.ts diff --git a/demo/05_create_contract.ts b/demo/05_create_contract.ts index 6722e9e1..150f3483 100644 --- a/demo/05_create_contract.ts +++ b/demo/05_create_contract.ts @@ -8,7 +8,7 @@ const DEMO_MODE = process.env.VERBOSE === 'true' // Unpack the default script engine and server policy. const { engine, policy } = config // Deliver proposal and endorsements to server. -const res = await client.contract.create(engine, policy, publish_req) +const res = await client.contract.create(publish_req, engine, policy.proposal) // Check if response is valid. if (!res.ok) throw new Error(res.error) diff --git a/demo/09_settle_contract.ts b/demo/09_settle_contract.ts index 04bcd898..438f8ebf 100644 --- a/demo/09_settle_contract.ts +++ b/demo/09_settle_contract.ts @@ -77,13 +77,13 @@ if (DEMO_MODE) { await sleep(5000) // Fetch the settlement tx from the oracle. - const txdata = await client.oracle.get_txdata(txid) + const txdata = await client.oracle.get_tx_data(txid) // Print the transaction data to console. console.dir(txdata, { depth : null }) print_banner('demo complete!') console.log('view your transaction here:') - console.log(`\n${client.oracle_url}/tx/${txid}\n`) + console.log(`\n${client._config.oracle_url}/tx/${txid}\n`) } await sleep(2000) diff --git a/src/client/api/client/oracle.ts b/src/client/api/client/oracle.ts deleted file mode 100644 index 0be1ac78..00000000 --- a/src/client/api/client/oracle.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { assert, sleep } from '@/core/util/index.js' -import { EscrowClient } from '@/client/class/client.js' - -import { - broadcast_tx, - get_fee_estimates, - get_fee_target, - get_utxo_data, - get_tx_data, - get_address_utxos, - get_latest_utxo -} from '../../util/oracle.js' - -import { - OracleQuery, - OracleTxSpendData -} from '@/client/types/index.js' - -function broadcast_tx_api (client : EscrowClient) { - return async (txhex : string) => { - assert.is_hex(txhex) - return broadcast_tx(client.oracle_url, txhex) - } -} - -function fee_estimates_api (client : EscrowClient) { - return async () => { - return get_fee_estimates(client.oracle_url) - } -} - -function get_fee_target_api (client : EscrowClient) { - return async (target : number) => { - return get_fee_target(client.oracle_url, target) - } -} - -function get_txdata_api (client : EscrowClient) { - return async (txid : string) => { - assert.is_hash(txid) - return get_tx_data(client.oracle_url, txid) - } -} - -function get_utxo_api (client : EscrowClient) { - return async (query : OracleQuery) => { - assert.is_hash(query.txid) - return get_utxo_data(client.oracle_url, query) - } -} - -function get_addr_utxos_api (client : EscrowClient) { - return async (address : string) => { - return get_address_utxos(client.oracle_url, address) - } -} - -function get_latest_utxo_api (client : EscrowClient) { - return async (address : string) => { - return get_latest_utxo(client.oracle_url, address) - } -} - -function poll_address_api (client : EscrowClient) { - return async ( - address : string, - interval : number, - retries : number, - verbose = false - ) : Promise => { - let tries = 0, - utxos : OracleTxSpendData[] = [] - for (let i = 0; i < retries; i++) { - if (utxos.length > 0) { - const utxo = utxos.at(-1) - assert.exists(utxo) - return utxo - } else { - utxos = await get_address_utxos(client.oracle_url, address) - tries += 1 - if (verbose) { - const msg = `[${tries}/${retries}] checking address in ${interval} seconds...` - console.log(msg) - } - await sleep(interval * 1000) - } - } - throw new Error('polling timed out') - } -} - -export default function (client : EscrowClient) { - return { - broadcast_tx : broadcast_tx_api(client), - fee_estimates : fee_estimates_api(client), - fee_target : get_fee_target_api(client), - get_txdata : get_txdata_api(client), - get_utxo : get_utxo_api(client), - get_address_utxos : get_addr_utxos_api(client), - get_latest_utxo : get_latest_utxo_api(client), - poll_address : poll_address_api(client) - } -} diff --git a/src/client/api/signer/account.ts b/src/client/api/signer/account.ts index a116e456..18c39266 100644 --- a/src/client/api/signer/account.ts +++ b/src/client/api/signer/account.ts @@ -77,9 +77,9 @@ export function commit_funds_api (esigner : EscrowSigner) { export default function (esigner : EscrowSigner) { return { + commit : commit_funds_api(esigner), request : request_account_api(esigner), - verify : verify_account_api(esigner), register : register_funds_api(esigner), - commit : commit_funds_api(esigner) + verify : verify_account_api(esigner) } } diff --git a/src/client/api/signer/contract.ts b/src/client/api/signer/contract.ts index 1b1bcef0..bf2f7023 100644 --- a/src/client/api/signer/contract.ts +++ b/src/client/api/signer/contract.ts @@ -1,6 +1,13 @@ import { EscrowSigner } from '@/client/class/signer.js' -export function request_contracts_api (esigner : EscrowSigner) { +import { ContractData, FundingData, VMData } from '@/core/types/index.js' + +import { + verify_contract_sigs, + verify_contract_state +} from '@/core/validation/contract.js' + +function request_contracts_api (esigner : EscrowSigner) { return () => { const host = esigner.server_url const url = `${host}/api/contract/list` @@ -9,7 +16,7 @@ export function request_contracts_api (esigner : EscrowSigner) { } } -export function cancel_contract_api (esigner : EscrowSigner) { +function cancel_contract_api (esigner : EscrowSigner) { return (cid : string) => { const host = esigner.server_url const url = `${host}/api/contract/${cid}/cancel` @@ -18,9 +25,22 @@ export function cancel_contract_api (esigner : EscrowSigner) { } } +function verify_contract_api (esigner : EscrowSigner) { + return ( + contract : ContractData, + funds ?: FundingData[], + vmdata ?: VMData + ) => { + const pubkey = esigner.server_pk + verify_contract_state(contract, funds, vmdata) + verify_contract_sigs(contract, pubkey) + } +} + export default function (esigner : EscrowSigner) { return { list : request_contracts_api(esigner), - cancel : cancel_contract_api(esigner) + cancel : cancel_contract_api(esigner), + verify : verify_contract_api(esigner) } } diff --git a/src/client/api/signer/deposit.ts b/src/client/api/signer/deposit.ts index 0b53fd70..e6d96e3b 100644 --- a/src/client/api/signer/deposit.ts +++ b/src/client/api/signer/deposit.ts @@ -7,6 +7,7 @@ import { } from '@/core/validation/contract.js' import { + validate_deposit_data, verify_deposit_data, verify_deposit_sigs } from '@/core/validation/deposit.js' @@ -23,7 +24,7 @@ import { LockRequest } from '@/core/types/index.js' -export function request_deposits_api (esigner : EscrowSigner) { +function request_deposits_api (esigner : EscrowSigner) { return () => { const host = esigner.server_url const url = `${host}/api/deposit/list` @@ -32,7 +33,7 @@ export function request_deposits_api (esigner : EscrowSigner) { } } -export function lock_funds_api (esigner : EscrowSigner) { +function lock_funds_api (esigner : EscrowSigner) { return ( contract : ContractData, deposit : DepositData @@ -46,7 +47,7 @@ export function lock_funds_api (esigner : EscrowSigner) { } } -export function cancel_deposit_api (esigner : EscrowSigner) { +function cancel_deposit_api (esigner : EscrowSigner) { return (dpid : string) => { assert.is_hash(dpid) const host = esigner.server_url @@ -56,7 +57,7 @@ export function cancel_deposit_api (esigner : EscrowSigner) { } } -export function close_deposit_api (esigner : EscrowSigner) { +function close_deposit_api (esigner : EscrowSigner) { return ( deposit : DepositData, feerate : number @@ -69,11 +70,20 @@ export function close_deposit_api (esigner : EscrowSigner) { } } +function verify_deposit_api (esigner : EscrowSigner) { + return (deposit : DepositData) => { + validate_deposit_data(deposit) + verify_deposit_data(deposit, esigner._signer) + verify_deposit_sigs(deposit, esigner.server_pk) + } +} + export default function (esigner : EscrowSigner) { return { cancel : cancel_deposit_api(esigner), close : close_deposit_api(esigner), list : request_deposits_api(esigner), - lock : lock_funds_api(esigner) + lock : lock_funds_api(esigner), + verify : verify_deposit_api(esigner) } } diff --git a/src/client/class/client.ts b/src/client/class/client.ts index cbfee714..04672615 100644 --- a/src/client/class/client.ts +++ b/src/client/class/client.ts @@ -1,5 +1,6 @@ -import { resolve_json } from '@/client/util/fetch.js' +import { resolve_json } from '@/client/lib/fetch.js' import { Network } from '@/core/types/index.js' +import { ChainOracle } from './oracle.js' import { DEFAULT_CONFIG, @@ -17,7 +18,6 @@ import account_api from '../api/client/account.js' import contract_api from '../api/client/contract.js' import deposit_api from '../api/client/deposit.js' import draft_api from '../api/client/draft.js' -import oracle_api from '../api/client/oracle.js' import server_api from '../api/client/server.js' import vmachine_api from '../api/client/vm.js' import witness_api from '../api/client/witness.js' @@ -29,13 +29,15 @@ type Resolver = ReturnType export class EscrowClient { readonly _config : ClientConfig readonly _fetcher : Resolver + readonly _oracle : ChainOracle constructor (opt : ClientOptions = {}) { const options = { ...DEFAULT_CONFIG, ...opt } const client = get_client_config(opt.network as Network) const config = { ...client, ...options } - this._config = ClientSchema.base.client_config.parse(config) + this._config = ClientSchema.config.client.parse(config) this._fetcher = get_fetcher(opt.fetcher ?? fetch) + this._oracle = new ChainOracle(config.oracle_url) } get fetcher () { @@ -46,8 +48,8 @@ export class EscrowClient { return this._config.network } - get oracle_url () : string { - return this._config.oracle_url + get oracle () : ChainOracle { + return this._oracle } get server_pk () : string { @@ -62,7 +64,6 @@ export class EscrowClient { contract = contract_api(this) deposit = deposit_api(this) draft = draft_api(this) - oracle = oracle_api(this) server = server_api(this) vm = vmachine_api(this) witness = witness_api(this) diff --git a/src/client/class/oracle.ts b/src/client/class/oracle.ts new file mode 100644 index 00000000..b208fa69 --- /dev/null +++ b/src/client/class/oracle.ts @@ -0,0 +1,221 @@ +import { parse_addr } from '@scrow/tapscript/address' +import { exists } from '@/core/util/validate.js' +import { TxConfirmState } from '@/core/types/index.js' +import { fetcher, resolve_json } from '@/client/lib/fetch.js' + +import { + ApiResponse, + OracleFeeEstimate, + OracleQuery, + OracleTxSpendData, + OracleTxSpendState, + OracleTxData, + OracleUtxo +} from '@/client/types/index.js' + +import ClientSchema from '@/client/schema/index.js' + +export class ChainOracle { + /** + * Compute the spending state of a deposit, + * using transaction data from an oracle. + */ + static async get_deposit_state ( + data : OracleTxSpendData, + locktime : number + ) : Promise { + if (data.status.confirmed) { + const expires_at = data.status.block_time + locktime + return { ...data.status, expires_at } + } else { + return { + confirmed : false as const, + block_hash : null, + block_height : null, + block_time : null, + expires_at : null + } + } + } + + readonly _host : string + + constructor (host : string) { + this._host = host + } + + /** + * Fetch transaction data from the oracle. + */ + async get_tx_data (txid : string) : Promise { + // Define the url to use for fetching. + const url = `${this._host}/tx/${txid}` + // Fetch a response from the oracle. + const res = await fetch(url) + // If status is 404, return null. + if (res.status === 404) return null + // Resolve the response into json. + const json = await resolve_json(res) + // If the response failed, throw. + if (!json.ok) throw new Error(json.error) + // Parse the returned data. + const parsed = await ClientSchema.oracle.txdata.spa(json.data) + // If data fails validation, throw. + if (!parsed.success) throw parsed.error + // Return the parsed data. + return parsed.data + } + + /** + * Fetch the spending state of a transaction + * output from the oracle. + */ + async get_utxo_state ( + txid : string, + vout : number + ) : Promise { + // Define the url to use for fetching. + const url = `${this._host}/tx/${txid}/outspend/${vout}` + // Fetch a response from the oracle. + const res = await fetch(url) + // If status is 404, return null. + if (res.status === 404) return null + // Resolve the response into json. + const json = await resolve_json(res) + // If the response failed, throw. + if (!json.ok) throw new Error(json.error) + // Parse the returned data. + const parsed = await ClientSchema.oracle.txostate.spa(json.data) + // If data fails validation, throw. + if (!parsed.success) throw parsed.error + // Return the parsed data. + return parsed.data + } + + /** + * Fetch the full status and state of a + * transaction output from the oracle. + */ + async get_utxo_data (query : OracleQuery) : Promise { + // Unpack the query object. + const { txid, vout, address } = query + // Fetch transaction data from the oracle. + const tx = await this.get_tx_data(txid) + // If the transaction is null, return null. + if (tx === null) return null + // Define an index variable. + let idx : number + // Search the transaction outputs (via address or index). + if (!exists(vout)) { + if (!exists(address)) { + throw new Error('You must specify an address or vout!') + } + idx = tx.vout.findIndex(e => e.scriptpubkey_address === address) + } else { + idx = vout + } + // If the index is -1, return null. + if (idx === -1) return null + // Set the txout based on the resulting index. + const txo = tx.vout.at(idx) + // If txout is undefined, return null. + if (txo === undefined) return null + // Get the spend state of the txout from the oracle. + const state = await this.get_utxo_state(txid, idx) + // If the spend state is null, return null. + if (state === null) return null + // Construct the returned txout. + const txout = { + txid, + vout : idx, + value : txo.value, + scriptkey : txo.scriptpubkey + } + // Return the txout aling with its state and status. + return { txout, status: tx.status, state } + } + + async get_address_utxos (addr : string) : Promise { + // Define the url to use for fetching. + const url = `${this._host}/address/${addr}/utxo` + // Fetch a response from the oracle. + const res = await fetcher(url) + // If response failed, throw error. + if (!res.ok) throw new Error(res.error) + // Parse the returned data. + const parsed = await ClientSchema.oracle.utxo.array().spa(res.data) + // If data fails validation, throw. + if (!parsed.success) throw parsed.error + // Return the parsed data. + return parsed.data.map(({ txid, status, value, vout }) => { + const scriptkey = parse_addr(addr).hex + const state = { spent: false as const } + const txout = { txid, vout, value, scriptkey } + return { state, status, txout } + }) + } + + async get_latest_utxo (addr : string) : Promise { + const utxos = await this.get_address_utxos(addr) + return utxos.at(0) ?? null + } + + /** + * Broadcast a transaction through the oracle. + */ + async broadcast_tx (txhex : string) : Promise> { + // Define the url to use for fetching. + const url = `${this._host}/tx` + // Configure the request. + const req = { + body : txhex, + headers : { 'content-type': 'text/plain' }, + method : 'POST' + } + // Fetch a response from the oracle. + const res = await fetch(url, req) + // Unpack response object. + const { status, statusText } = res + // Return a data object based on the oracle response. + return (res.ok) + ? { status, ok: true, data: await res.text() } + : { status, ok: false, error: statusText } + } + + /** + * Fetch a range of fee estimates from the oracle. + */ + async get_fee_estimates () : Promise { + // Define the url to use for fetching. + const url = `${this._host}/fee-estimates` + // Fetch a response from the oracle. + const res = await fetch(url) + // Resolve the response into json. + const json = await resolve_json(res) + // If the response failed, throw. + if (!json.ok) throw new Error(json.error) + // Return the parsed data with rounded values. + const ent = Object.entries(json.data) + const rnd = ent.map(([ k, v ]) => [ k, Math.ceil(v) ]) + return Object.fromEntries(rnd) + } + + /** + * Fetch a fee quote from the oracle, + * based on a target block height. + */ + async get_fee_target (target : number) : Promise { + // Fetch an array of quotes from the oracle. + const quotes = await this.get_fee_estimates() + // Convert target height to an index. + const index = String(target) + // Retrieve a specific quote based on index. + const feerate = quotes[index] + // If feerate does not exist, throw an error. + if (typeof feerate !== 'number') { + throw new Error('No quote available for fee target: ' + index) + } + // Else, return feerate from oracle. + return feerate + } +} diff --git a/src/client/class/signer.ts b/src/client/class/signer.ts index 4477e1bd..b9a5a922 100644 --- a/src/client/class/signer.ts +++ b/src/client/class/signer.ts @@ -93,7 +93,7 @@ export class EscrowSigner { const config = { ...client, ...opt } const xpub = options.xpub ?? signer.xpub - this._config = ClientSchema.base.signer_config.parse(config) + this._config = ClientSchema.config.signer.parse(config) this._signer = signer this._wallet = new Wallet(xpub) } diff --git a/src/client/config/index.ts b/src/client/config/index.ts index 2d951183..573adb11 100644 --- a/src/client/config/index.ts +++ b/src/client/config/index.ts @@ -2,14 +2,20 @@ import { Network } from '@/core/types/index.js' import DEFAULT_POLICY from './policy.js' import DEFAULT_SERVERS from './settings.js' -export const DEFAULT_NETWORK = 'mutiny' +const DEFAULT_NETWORK = 'mutiny' -export const DEFAULT_CONFIG = { +const DEFAULT_CONFIG = { network : DEFAULT_NETWORK } -export function get_client_config (network : Network) { +function get_client_config (network : Network) { return DEFAULT_SERVERS[network] } -export { DEFAULT_POLICY, DEFAULT_SERVERS } +export { + DEFAULT_CONFIG, + DEFAULT_NETWORK, + DEFAULT_POLICY, + DEFAULT_SERVERS, + get_client_config +} diff --git a/src/client/index.ts b/src/client/index.ts index 91533909..772946bc 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,11 +1,11 @@ +import { DraftUtil } from './lib/session.js' +import ClientLib from './lib/index.js' +import ClientSchema from './schema/index.js' + export * from './class/client.js' +export * from './class/oracle.js' export * from './class/signer.js' export * from './config/index.js' export * from './types/index.js' -export * as ClientLib from './lib/index.js' -export * as ClientSchema from './schema/index.js' -export * as ClientUtil from './util/index.js' -export * as ClientVerify from './validation/index.js' - -export { DraftUtil } from './lib/session.js' +export { ClientLib, ClientSchema, DraftUtil } diff --git a/src/client/util/fetch.ts b/src/client/lib/fetch.ts similarity index 100% rename from src/client/util/fetch.ts rename to src/client/lib/fetch.ts diff --git a/src/client/lib/index.ts b/src/client/lib/index.ts index 035bb95e..0824b615 100644 --- a/src/client/lib/index.ts +++ b/src/client/lib/index.ts @@ -1,4 +1,11 @@ -export * as members from './membership.js' -export * as oracle from './oracle.js' -export * as roles from './enrollment.js' -export * as session from './session.js' +import * as fetch from './fetch.js' +import * as members from './membership.js' +import * as roles from './enrollment.js' +import * as session from './session.js' + +export * from './fetch.js' +export * from './membership.js' +export * from './enrollment.js' +export * from './session.js' + +export default { fetch, members, roles, session } diff --git a/src/client/lib/oracle.ts b/src/client/lib/oracle.ts deleted file mode 100644 index 3b450591..00000000 --- a/src/client/lib/oracle.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { TxConfirmState } from '@/core/types/index.js' -import { OracleTxSpendData } from '@/client/types/oracle.js' - -/** - * Compute the spending state of a deposit, - * using transaction data from an oracle. - */ -export function get_confirm_state ( - locktime : number, - utxo_data : OracleTxSpendData -) : TxConfirmState { - const { status } = utxo_data - if (status.confirmed) { - const expires_at = status.block_time + locktime - return { ...status, expires_at } - } else { - return { - confirmed : false as const, - block_hash : null, - block_height : null, - block_time : null, - expires_at : null - } - } -} diff --git a/src/client/schema/base.ts b/src/client/schema/base.ts deleted file mode 100644 index 2a96dcbc..00000000 --- a/src/client/schema/base.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { z } from 'zod' -import base from '@/core/schema/base.js' - -const { hash, network, str } = base - -const signer_config = z.object({ - network, - server_pk : hash, - server_url : str -}) - -const client_config = signer_config.extend({ - oracle_url : str -}) - -export default { client_config, signer_config } diff --git a/src/client/schema/config.ts b/src/client/schema/config.ts new file mode 100644 index 00000000..69aa589b --- /dev/null +++ b/src/client/schema/config.ts @@ -0,0 +1,16 @@ +import { z } from 'zod' +import CoreSchema from '@/core/schema/index.js' + +const { hash, network, str } = CoreSchema.base + +const signer = z.object({ + network, + server_pk : hash, + server_url : str +}) + +const client = signer.extend({ + oracle_url : str +}) + +export default { client, signer } diff --git a/src/client/schema/index.ts b/src/client/schema/index.ts index 5a4074e6..de5fe2ed 100644 --- a/src/client/schema/index.ts +++ b/src/client/schema/index.ts @@ -1,5 +1,5 @@ -import base from './base.js' +import config from './config.js' import draft from './draft.js' import oracle from './oracle.js' -export default { base, draft, oracle } +export default { config, draft, oracle } diff --git a/src/client/types/contract.ts b/src/client/types/contract.ts index 4f4a6c5e..ee095e68 100644 --- a/src/client/types/contract.ts +++ b/src/client/types/contract.ts @@ -1,7 +1,6 @@ import { ContractData, - FundingData, - VMData + FundingData } from '@/core/types/index.js' export interface ContractDataResponse { @@ -15,10 +14,3 @@ export interface ContractListResponse { export interface FundListResponse { funds : FundingData[] } - -export interface ContractVerifyConfig { - contract : ContractData - funds ?: FundingData[] - pubkey : string - vmdata ?: VMData -} diff --git a/src/client/util/index.ts b/src/client/util/index.ts deleted file mode 100644 index a3433f4c..00000000 --- a/src/client/util/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './fetch.js' -export * from './oracle.js' diff --git a/src/client/util/oracle.ts b/src/client/util/oracle.ts deleted file mode 100644 index b873bf5b..00000000 --- a/src/client/util/oracle.ts +++ /dev/null @@ -1,218 +0,0 @@ -/* Global Imports */ - -import { parse_addr } from '@scrow/tapscript/address' - -/* Module Imports */ - -import { exists } from '@/core/util/validate.js' -import { fetcher, resolve_json } from './fetch.js' - -/* Local Imports */ - -import { - ApiResponse, - OracleFeeEstimate, - OracleQuery, - OracleTxSpendData, - OracleTxSpendState, - OracleTxData, - OracleUtxo -} from '@/client/types/index.js' - -import ClientSchema from '@/client/schema/index.js' - -/** - * Fetch transaction data from the oracle. - */ -export async function get_tx_data ( - host : string, - txid : string -) : Promise { - // Define the url to use for fetching. - const url = `${host}/tx/${txid}` - // Fetch a response from the oracle. - const res = await fetch(url) - // If status is 404, return null. - if (res.status === 404) return null - // Resolve the response into json. - const json = await resolve_json(res) - // If the response failed, throw. - if (!json.ok) throw new Error(json.error) - // Parse the returned data. - const parsed = await ClientSchema.oracle.txdata.spa(json.data) - // If data fails validation, throw. - if (!parsed.success) throw parsed.error - // Return the parsed data. - return parsed.data -} - -/** - * Fetch the spending state of a transaction - * output from the oracle. - */ -export async function get_utxo_state ( - host : string, - txid : string, - vout : number -) : Promise { - // Define the url to use for fetching. - const url = `${host}/tx/${txid}/outspend/${vout}` - // Fetch a response from the oracle. - const res = await fetch(url) - // If status is 404, return null. - if (res.status === 404) return null - // Resolve the response into json. - const json = await resolve_json(res) - // If the response failed, throw. - if (!json.ok) throw new Error(json.error) - // Parse the returned data. - const parsed = await ClientSchema.oracle.txostate.spa(json.data) - // If data fails validation, throw. - if (!parsed.success) throw parsed.error - // Return the parsed data. - return parsed.data -} - -/** - * Fetch the full status and state of a - * transaction output from the oracle. - */ -export async function get_utxo_data ( - host : string, - query : OracleQuery -) : Promise { - // Unpack the query object. - const { txid, vout, address } = query - // Fetch transaction data from the oracle. - const tx = await get_tx_data(host, txid) - // If the transaction is null, return null. - if (tx === null) return null - // Define an index variable. - let idx : number - // Search the transaction outputs (via address or index). - if (!exists(vout)) { - if (!exists(address)) { - throw new Error('You must specify an address or vout!') - } - idx = tx.vout.findIndex(e => e.scriptpubkey_address === address) - } else { - idx = vout - } - // If the index is -1, return null. - if (idx === -1) return null - // Set the txout based on the resulting index. - const txo = tx.vout.at(idx) - // If txout is undefined, return null. - if (txo === undefined) return null - // Get the spend state of the txout from the oracle. - const state = await get_utxo_state(host, txid, idx) - // If the spend state is null, return null. - if (state === null) return null - // Construct the returned txout. - const txout = { - txid, - vout : idx, - value : txo.value, - scriptkey : txo.scriptpubkey - } - // Return the txout aling with its state and status. - return { txout, status: tx.status, state } -} - -export async function get_address_utxos ( - host : string, - addr : string -) : Promise { - // Define the url to use for fetching. - const url = `${host}/address/${addr}/utxo` - // Fetch a response from the oracle. - const res = await fetcher(url) - // If response failed, throw error. - if (!res.ok) throw new Error(res.error) - // Parse the returned data. - const parsed = await ClientSchema.oracle.utxo.array().spa(res.data) - // If data fails validation, throw. - if (!parsed.success) throw parsed.error - // Return the parsed data. - return parsed.data.map(({ txid, status, value, vout }) => { - const scriptkey = parse_addr(addr).hex - const state = { spent: false as const } - const txout = { txid, vout, value, scriptkey } - return { state, status, txout } - }) -} - -export async function get_latest_utxo ( - host : string, - addr : string -) : Promise { - const utxos = await get_address_utxos(host, addr) - return utxos.at(0) ?? null -} - -/** - * Broadcast a transaction through the oracle. - */ -export async function broadcast_tx ( - host : string, - txhex : string -) : Promise> { - // Define the url to use for fetching. - const url = `${host}/tx` - // Configure the request. - const req = { - body : txhex, - headers : { 'content-type': 'text/plain' }, - method : 'POST' - } - // Fetch a response from the oracle. - const res = await fetch(url, req) - // Unpack response object. - const { status, statusText } = res - // Return a data object based on the oracle response. - return (res.ok) - ? { status, ok: true, data: await res.text() } - : { status, ok: false, error: statusText } -} - -/** - * Fetch a range of fee estimates from the oracle. - */ -export async function get_fee_estimates ( - host : string -) : Promise { - // Define the url to use for fetching. - const url = `${host}/fee-estimates` - // Fetch a response from the oracle. - const res = await fetch(url) - // Resolve the response into json. - const json = await resolve_json(res) - // If the response failed, throw. - if (!json.ok) throw new Error(json.error) - // Return the parsed data with rounded values. - const ent = Object.entries(json.data) - const rnd = ent.map(([ k, v ]) => [ k, Math.ceil(v) ]) - return Object.fromEntries(rnd) -} - -/** - * Fetch a fee quote from the oracle, - * based on a target block height. - */ -export async function get_fee_target ( - host : string, - target : number -) : Promise { - // Fetch an array of quotes from the oracle. - const quotes = await get_fee_estimates(host) - // Convert target height to an index. - const index = String(target) - // Retrieve a specific quote based on index. - const feerate = quotes[index] - // If feerate does not exist, throw an error. - if (typeof feerate !== 'number') { - throw new Error('No quote available for fee target: ' + index) - } - // Else, return feerate from oracle. - return feerate -} diff --git a/src/client/validation/contract.ts b/src/client/validation/contract.ts deleted file mode 100644 index e8c4ca9d..00000000 --- a/src/client/validation/contract.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { assert } from '@/core/util/index.js' - -import { ContractVerifyConfig } from '@/client/types/contract.js' - -import { - verify_contract_activation, - verify_contract_close, - verify_contract_data, - verify_contract_funding, - verify_contract_sigs, - verify_contract_spending -} from '@/core/validation/contract.js' - -export function verify_contract ( - config : ContractVerifyConfig -) { - const { contract, funds, pubkey, vmdata } = config - - verify_contract_data(contract) - verify_contract_sigs(contract, pubkey) - - if (contract.secured && funds !== undefined) { - verify_contract_funding(contract, funds) - } - - if (contract.activated && vmdata !== undefined) { - verify_contract_activation(contract, vmdata) - } - - if (contract.closed && vmdata !== undefined) { - assert.exists(vmdata, 'you must provide a vmdata object to verify.') - verify_contract_close(contract, vmdata) - } - - if (contract.spent) { - assert.exists(funds, 'you must provide a list of funds to verify') - assert.exists(vmdata, 'you must provide a vmdata object to verify.') - verify_contract_spending(contract, funds, vmdata) - } -} diff --git a/src/client/validation/deposit.ts b/src/client/validation/deposit.ts deleted file mode 100644 index 8493073b..00000000 --- a/src/client/validation/deposit.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { DepositData, SignerAPI } from '@/core/types/index.js' - -import { - verify_deposit_data, - verify_deposit_sigs -} from '@/core/validation/deposit.js' - -export function verify_deposit ( - deposit : DepositData, - pubkey : string, - signer : SignerAPI -) { - verify_deposit_data(deposit, signer) - verify_deposit_sigs(deposit, pubkey) -} diff --git a/src/client/validation/index.ts b/src/client/validation/index.ts deleted file mode 100644 index 4626a60a..00000000 --- a/src/client/validation/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './contract.js' -export * from './deposit.js' diff --git a/src/core/types/witness.ts b/src/core/types/witness.ts index c9166413..aa92bcd2 100644 --- a/src/core/types/witness.ts +++ b/src/core/types/witness.ts @@ -26,8 +26,8 @@ export interface WitnessData extends WitnessPreImage { } export interface ReceiptPreImage extends WitnessData { + agent_pk : string receipt_at : number - agent_pk : string vm_closed : boolean vm_hash : string vm_output : string | null diff --git a/src/core/util/index.ts b/src/core/util/index.ts index 71911185..67f3cf30 100644 --- a/src/core/util/index.ts +++ b/src/core/util/index.ts @@ -1,8 +1,6 @@ export * from './base.js' -export * from '../../client/util/fetch.js' export * from './notarize.js' export * from './parse.js' export * from './validate.js' export * as assert from './assert.js' -export * as oracle from '../../client/util/oracle.js' diff --git a/src/core/validation/contract.ts b/src/core/validation/contract.ts index 3f8efc5e..601613a8 100644 --- a/src/core/validation/contract.ts +++ b/src/core/validation/contract.ts @@ -39,6 +39,7 @@ import { } from '../types/index.js' import ContractSchema from '../schema/contract.js' +import DepositSchema from '../schema/deposit.js' /* Local Imports */ @@ -47,6 +48,7 @@ import { verify_proposal_data } from './proposal.js' import { get_vm_config } from '../lib/vm.js' +import { validate_vm_data } from './witness.js' export function validate_publish_req ( contract : unknown @@ -60,6 +62,12 @@ export function validate_contract_data ( void ContractSchema.data.parse(contract) } +export function validate_contract_funds ( + funds : FundingData[] +) { + void DepositSchema.fund.array().parse(funds) +} + export function verify_contract_req ( machine : ScriptEngineAPI, policy : ProposalPolicy, @@ -228,6 +236,42 @@ export function verify_contract_settlement ( verify_contract_spending(contract, funds, vmdata) } +export function verify_contract_state ( + contract : ContractData, + funds ?: FundingData[], + vmdata ?: VMData +) { + validate_contract_data(contract) + verify_contract_data(contract) + + if (funds !== undefined) { + validate_contract_funds(funds) + } + + if (vmdata !== undefined) { + validate_vm_data(vmdata) + } + + if (contract.secured && funds !== undefined) { + verify_contract_funding(contract, funds) + } + + if (contract.activated && vmdata !== undefined) { + verify_contract_activation(contract, vmdata) + } + + if (contract.closed && vmdata !== undefined) { + assert.exists(vmdata, 'you must provide a vmdata object to verify.') + verify_contract_close(contract, vmdata) + } + + if (contract.spent) { + assert.exists(funds, 'you must provide a list of funds to verify') + assert.exists(vmdata, 'you must provide a vmdata object to verify.') + verify_contract_spending(contract, funds, vmdata) + } +} + export default { validate : { request : validate_publish_req, @@ -244,6 +288,7 @@ export default { closed : verify_contract_close, spend : verify_contract_spending, signatures : verify_contract_sigs, - settlement : verify_contract_settlement + settlement : verify_contract_settlement, + state : verify_contract_state } } diff --git a/src/core/validation/witness.ts b/src/core/validation/witness.ts index 22a4d931..e01d5426 100644 --- a/src/core/validation/witness.ts +++ b/src/core/validation/witness.ts @@ -1,10 +1,5 @@ -/* Local Imports */ - -import { verify_sig } from '@cmdcode/crypto-tools/signer' - -/* Module Imports */ - -import { assert, regex } from '../util/index.js' +import { verify_sig } from '@cmdcode/crypto-tools/signer' +import { assert, regex } from '../util/index.js' import { get_receipt_id, @@ -22,18 +17,25 @@ import { } from '../types/index.js' import PropSchema from '../schema/proposal.js' +import VMSchema from '../schema/vm.js' import WitSchema from '../schema/witness.js' export function validate_program_entry ( program : unknown ) : asserts program is ProgramEntry { - PropSchema.program.parse(program) + void PropSchema.program.parse(program) +} + +export function validate_vm_data ( + vmdata : VMData +) { + void VMSchema.data.parse(vmdata) } export function validate_witness_data ( witness : unknown ) : asserts witness is WitnessData { - WitSchema.data.parse(witness) + void WitSchema.data.parse(witness) } export function verify_program_entry ( diff --git a/test/src/core.ts b/test/src/core.ts index 5262188f..a3eb097c 100644 --- a/test/src/core.ts +++ b/test/src/core.ts @@ -1,8 +1,8 @@ -import { Buff } from '@cmdcode/buff' -import { Signer, Wallet } from '@cmdcode/signer' -import { get_confirm_state } from '@/client/lib/oracle.js' -import { TxOutput } from '@/core/types/index.js' -import { CoreSigner } from './types.js' +import { Buff } from '@cmdcode/buff' +import { Signer, Wallet } from '@cmdcode/signer' +import { TxOutput } from '@/core/types/index.js' +import { ChainOracle } from '@/client/index.js' +import { CoreSigner } from './types.js' import { CoreClient, @@ -106,5 +106,6 @@ export async function get_spend_state ( if (tx_input === null) throw new Error('utxo not found') if (!tx_input.status.confirmed) throw new Error('utxo not confirmed') const data = { txout : utxo, status : tx_input.status, state : { spent : false as const }} - return get_confirm_state(locktime, data) + + return ChainOracle.get_deposit_state(data, locktime) } \ No newline at end of file From d004e204f4935a3c478a953ffeb664f7ac6f5979 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 16:47:31 -0500 Subject: [PATCH 09/27] update --- package.json | 34 +++++++++++++-------------- src/client/class/oracle.ts | 40 +++++++++++--------------------- src/client/lib/fetch.ts | 22 +++++++++++++++++- src/core/module/deposit/state.ts | 4 ++-- test/src/core.ts | 12 +++++----- 5 files changed, 59 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index 31de6509..1fe01f08 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,26 @@ "import": "./dist/client/index.js", "types": "./dist/client/index.d.ts" }, + "./client/lib": { + "import": "./dist/client/lib/index.js", + "types": "./dist/client/lib/index.d.ts" + }, "./core": { "import": "./dist/core/index.js", "types": "./dist/core/index.d.ts" }, + "./core/lib": { + "import": "./dist/core/lib/index.js", + "types": "./dist/core/lib/index.d.ts" + }, + "./core/util": { + "import": "./dist/core/util/index.js", + "types": "./dist/core/util/index.d.ts" + }, + "./core/validate": { + "import": "./dist/core/validate/index.js", + "types": "./dist/core/validate/index.d.ts" + }, "./cvm": { "import": "./dist/vm/index.js", "types": "./dist/vm/index.d.ts" @@ -53,22 +69,6 @@ "./witness": { "import": "./dist/core/module/witness/index.js", "types": "./dist/core/module/witness/index.d.ts" - }, - "./lib": { - "import": "./dist/core/lib/index.js", - "types": "./dist/core/lib/index.d.ts" - }, - "./schema": { - "import": "./dist/core/schema/index.js", - "types": "./dist/core/schema/index.d.ts" - }, - "./util": { - "import": "./dist/core/util/index.js", - "types": "./dist/core/util/index.d.ts" - }, - "./validate": { - "import": "./dist/core/validation/index.js", - "types": "./dist/core/validation/index.d.ts" } }, "scripts": { @@ -77,8 +77,6 @@ "demo:vm": "yarn load demo/vm/eval.ts", "lint": "eslint . --ext .ts", "load": "tsx --tsconfig ./test/tsconfig.json", - "pub:dev": "yarn publish --tag dev", - "pub:rel": "yarn publish --tag latest", "release": "yarn lint && yarn test && yarn build", "run:regtest": "NETWORK=regtest yarn load", "run:mutiny": "NETWORK=mutiny yarn load", diff --git a/src/client/class/oracle.ts b/src/client/class/oracle.ts index b208fa69..2f85c253 100644 --- a/src/client/class/oracle.ts +++ b/src/client/class/oracle.ts @@ -1,7 +1,8 @@ -import { parse_addr } from '@scrow/tapscript/address' -import { exists } from '@/core/util/validate.js' -import { TxConfirmState } from '@/core/types/index.js' -import { fetcher, resolve_json } from '@/client/lib/fetch.js' +import { parse_addr } from '@scrow/tapscript/address' +import { exists } from '@/core/util/validate.js' +import { TxOutput } from '@/core/types/index.js' + +import { fetcher, get_confirm_state, resolve_json } from '@/client/lib/fetch.js' import { ApiResponse, @@ -16,28 +17,6 @@ import { import ClientSchema from '@/client/schema/index.js' export class ChainOracle { - /** - * Compute the spending state of a deposit, - * using transaction data from an oracle. - */ - static async get_deposit_state ( - data : OracleTxSpendData, - locktime : number - ) : Promise { - if (data.status.confirmed) { - const expires_at = data.status.block_time + locktime - return { ...data.status, expires_at } - } else { - return { - confirmed : false as const, - block_hash : null, - block_height : null, - block_time : null, - expires_at : null - } - } - } - readonly _host : string constructor (host : string) { @@ -135,6 +114,15 @@ export class ChainOracle { return { txout, status: tx.status, state } } + async get_confirm_state ( + locktime : number, + utxo : TxOutput + ) { + const { txid, vout } = utxo + const res = await this.get_utxo_data({ txid, vout }) + return (res === null) ? null : get_confirm_state(res, locktime) + } + async get_address_utxos (addr : string) : Promise { // Define the url to use for fetching. const url = `${this._host}/address/${addr}/utxo` diff --git a/src/client/lib/fetch.ts b/src/client/lib/fetch.ts index 900f1612..22323af2 100644 --- a/src/client/lib/fetch.ts +++ b/src/client/lib/fetch.ts @@ -1,4 +1,6 @@ -import { ApiResponse } from '@/client/types/index.js' +import { ApiResponse, OracleTxSpendData } from '@/client/types/index.js' + +import { TxConfirmState } from '@/core/types/index.js' export async function fetcher ( input : URL | RequestInfo, @@ -51,3 +53,21 @@ export async function resolve_json ( // Return response with data. return { status, ok: true, data } } + +export function get_confirm_state ( + data : OracleTxSpendData, + locktime : number +) : TxConfirmState { + if (data.status.confirmed) { + const expires_at = data.status.block_time + locktime + return { ...data.status, expires_at } + } else { + return { + confirmed : false as const, + block_hash : null, + block_height : null, + block_time : null, + expires_at : null + } + } +} diff --git a/src/core/module/deposit/state.ts b/src/core/module/deposit/state.ts index 92ab7d0c..2e970e11 100644 --- a/src/core/module/deposit/state.ts +++ b/src/core/module/deposit/state.ts @@ -1,5 +1,5 @@ -import { sort_record } from '@/core/util/base.js' -import { assert, parse_proof } from '@/core/util/index.js' +import { sort_record } from '@/core/util/base.js' +import { assert, parse_proof } from '@/core/util/index.js' import { DepositData, diff --git a/test/src/core.ts b/test/src/core.ts index a3eb097c..3bb6bf90 100644 --- a/test/src/core.ts +++ b/test/src/core.ts @@ -1,8 +1,8 @@ -import { Buff } from '@cmdcode/buff' -import { Signer, Wallet } from '@cmdcode/signer' -import { TxOutput } from '@/core/types/index.js' -import { ChainOracle } from '@/client/index.js' -import { CoreSigner } from './types.js' +import { Buff } from '@cmdcode/buff' +import { Signer, Wallet } from '@cmdcode/signer' +import { TxOutput } from '@/core/types/index.js' +import { get_confirm_state } from '@/client/lib/fetch.js' +import { CoreSigner } from './types.js' import { CoreClient, @@ -107,5 +107,5 @@ export async function get_spend_state ( if (!tx_input.status.confirmed) throw new Error('utxo not confirmed') const data = { txout : utxo, status : tx_input.status, state : { spent : false as const }} - return ChainOracle.get_deposit_state(data, locktime) + return get_confirm_state(data, locktime) } \ No newline at end of file From 275182c6ae72235c23dc76a19f2815dca1b97ed0 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 16:48:54 -0500 Subject: [PATCH 10/27] v0.12.36-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1fe01f08..dd262563 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.35-dev", + "version": "0.12.36-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From 415a578bc829f4abe31bb1312c62af519a69148a Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 17:09:49 -0500 Subject: [PATCH 11/27] v0.12.37-dev --- package.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index dd262563..3f68c9e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.36-dev", + "version": "0.12.37-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", @@ -42,14 +42,6 @@ "import": "./dist/core/lib/index.js", "types": "./dist/core/lib/index.d.ts" }, - "./core/util": { - "import": "./dist/core/util/index.js", - "types": "./dist/core/util/index.d.ts" - }, - "./core/validate": { - "import": "./dist/core/validate/index.js", - "types": "./dist/core/validate/index.d.ts" - }, "./cvm": { "import": "./dist/vm/index.js", "types": "./dist/vm/index.d.ts" @@ -66,6 +58,14 @@ "import": "./dist/core/module/deposit/index.js", "types": "./dist/core/module/deposit/index.d.ts" }, + "./util": { + "import": "./dist/core/util/index.js", + "types": "./dist/core/util/index.d.ts" + }, + "./verify": { + "import": "./dist/core/validation/index.js", + "types": "./dist/core/validation/index.d.ts" + }, "./witness": { "import": "./dist/core/module/witness/index.js", "types": "./dist/core/module/witness/index.d.ts" From 85bffde4796661bfedd1d92f3d340aac192cc299 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 17:23:32 -0500 Subject: [PATCH 12/27] v0.12.38-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3f68c9e4..3d9ecb92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.37-dev", + "version": "0.12.38-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From c0f334de4d696c862aacee2a02317ced9b87d9d0 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 17:35:03 -0500 Subject: [PATCH 13/27] v0.12.39-dev --- package.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d9ecb92..8c09d972 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.38-dev", + "version": "0.12.39-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", @@ -34,6 +34,10 @@ "import": "./dist/client/lib/index.js", "types": "./dist/client/lib/index.d.ts" }, + "./client/schema": { + "import": "./dist/client/schema/index.js", + "types": "./dist/client/schema/index.d.ts" + }, "./core": { "import": "./dist/core/index.js", "types": "./dist/core/index.d.ts" @@ -42,6 +46,10 @@ "import": "./dist/core/lib/index.js", "types": "./dist/core/lib/index.d.ts" }, + "./core/schema": { + "import": "./dist/core/schema/index.js", + "types": "./dist/core/schema/index.d.ts" + }, "./cvm": { "import": "./dist/vm/index.js", "types": "./dist/vm/index.d.ts" From 109f754012ce744bfea446406a9b385c3e648609 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 17:56:50 -0500 Subject: [PATCH 14/27] update --- src/client/api/client/contract.ts | 10 +++++----- src/core/index.ts | 10 +++++----- src/core/lib/proposal.ts | 5 ++--- src/core/module/account/data.ts | 8 ++++---- src/core/module/contract/data.ts | 2 +- src/core/module/contract/state.ts | 6 +++--- src/core/module/deposit/data.ts | 2 +- src/core/module/deposit/state.ts | 6 +++--- src/core/module/witness/data.ts | 16 ++++++++-------- src/core/schema/account.ts | 2 +- src/core/schema/contract.ts | 5 ++--- src/core/schema/deposit.ts | 3 +-- src/core/schema/witness.ts | 16 ++++++++-------- src/core/types/account.ts | 2 +- src/core/types/contract.ts | 6 +++--- src/core/types/deposit.ts | 8 ++++---- src/core/types/witness.ts | 4 ++-- src/core/util/base.ts | 9 +++++++++ src/core/util/index.ts | 4 ++-- src/core/util/notarize.ts | 9 --------- src/core/util/validate.ts | 2 +- src/core/validation/account.ts | 4 ++-- src/core/validation/covenant.ts | 8 ++++---- src/core/validation/proposal.ts | 8 ++------ src/core/validation/witness.ts | 4 ++-- src/vm/lib/methods/hashlock.ts | 8 ++++---- test/src/e2e/settle.test.ts | 6 +++--- 27 files changed, 83 insertions(+), 90 deletions(-) diff --git a/src/client/api/client/contract.ts b/src/client/api/client/contract.ts index a243b886..a022a25b 100644 --- a/src/client/api/client/contract.ts +++ b/src/client/api/client/contract.ts @@ -1,7 +1,7 @@ -import { assert, parse_proposal } from '@/core/util/index.js' -import { create_publish_req } from '@/core/module/contract/index.js' -import { EscrowClient } from '@/client/class/client.js' -import { DEFAULT_POLICY } from '@/client/config/index.js' +import { assert, parser } from '@/core/util/index.js' +import { create_publish_req } from '@/core/module/contract/index.js' +import { EscrowClient } from '@/client/class/client.js' +import { DEFAULT_POLICY } from '@/client/config/index.js' import { verify_endorsements, @@ -35,7 +35,7 @@ function create_contract_api ( // Unpack configurations from client. const { endorsements, proposal } = request // Parse and validate the proposal. - const prop = parse_proposal(proposal) + const prop = parser.parse_proposal(proposal) // Verify the proposal's terms. verify_proposal_data(engine, policy, prop) // Verify any signatures. diff --git a/src/core/index.ts b/src/core/index.ts index a0e53e69..1906c269 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,11 +1,11 @@ -import CoreAssert from './validation/index.js' import CoreLib from './lib/index.js' import CoreSchema from './schema/index.js' +import CoreVerify from './validation/index.js' -export * as CONST from './const.js' -export * as CoreMod from './module/index.js' -export * as CoreUtil from './util/index.js' +import * as CONST from './const.js' +import * as CoreMod from './module/index.js' +import * as CoreUtil from './util/index.js' export * from './types/index.js' -export { CoreAssert, CoreLib, CoreSchema } +export { CONST, CoreMod, CoreLib, CoreSchema, CoreUtil, CoreVerify } diff --git a/src/core/lib/proposal.ts b/src/core/lib/proposal.ts index 034b1586..2a799230 100644 --- a/src/core/lib/proposal.ts +++ b/src/core/lib/proposal.ts @@ -9,10 +9,9 @@ import { TxOutput } from '@scrow/tapscript' /* Module Imports */ import { - get_record_id, now, - parse_proposal, + parser, sort_record } from '../util/index.js' @@ -44,7 +43,7 @@ const PROPOSAL_DEFAULTS = () => { } export function create_proposal (template : ProposalTemplate) { - return parse_proposal({ ...PROPOSAL_DEFAULTS(), ...template }) + return parser.parse_proposal({ ...PROPOSAL_DEFAULTS(), ...template }) } /** diff --git a/src/core/module/account/data.ts b/src/core/module/account/data.ts index f829d558..1dbaed0b 100644 --- a/src/core/module/account/data.ts +++ b/src/core/module/account/data.ts @@ -32,7 +32,7 @@ export function create_account ( // Get signing agent for account. const agent = util.get_account_agent(request, signer) // Generate a session token. - const agent_tkn = gen_session_token(agent, created_at).tkn + const agent_tkn = gen_session_token(agent, created_at).tkn // Create a context object for the account. const acct_ctx = util.create_account_ctx(deposit_pk, locktime, network, return_addr, agent_tkn) // Compute the deposit address from the account context. @@ -40,11 +40,11 @@ export function create_account ( // Compute the hash for the account request. const account_hash = util.get_account_hash(request) // Set the server pubkey. - const agent_pk = signer.pubkey + const agent_pk = signer.pubkey // Compute the id for the account data. const account_id = util.get_account_id(deposit_addr, account_hash, agent_pk, created_at, agent_tkn) // Sign the account identifier. - const account_sig = signer.sign(account_id) + const agent_sig = signer.sign(account_id) // Return the complete account data object. - return sort_record({ ...request, account_id, account_hash, account_sig, created_at, deposit_addr, agent_pk, agent_tkn }) + return sort_record({ ...request, account_id, account_hash, agent_sig, created_at, deposit_addr, agent_pk, agent_tkn }) } diff --git a/src/core/module/contract/data.ts b/src/core/module/contract/data.ts index be930bcf..1179c4c8 100644 --- a/src/core/module/contract/data.ts +++ b/src/core/module/contract/data.ts @@ -74,7 +74,7 @@ export function create_contract ( vin_txfee : feerate * SPEND_TXIN_SIZE } const proof = notarize_contract(template, signer, 'published') - return sort_record({ ...template, created_sig: proof }) + return sort_record({ ...template, agent_sig: proof }) } export function cancel_contract ( diff --git a/src/core/module/contract/state.ts b/src/core/module/contract/state.ts index f3c40129..d5208550 100644 --- a/src/core/module/contract/state.ts +++ b/src/core/module/contract/state.ts @@ -56,8 +56,8 @@ export const GET_PUBLISH_STATE = () => { ...INIT_CANCEL_STATE(), ...INIT_PUBLISH_STATE(), ...INIT_FUNDING_STATE(), - created_sig : null, - status : 'published' as ContractStatus + agent_sig : null, + status : 'published' as ContractStatus } } @@ -161,7 +161,7 @@ export function get_contract_proof ( let sig switch (status) { case 'published': - sig = contract.created_sig; break + sig = contract.agent_sig; break case 'canceled': sig = contract.canceled_sig; break case 'secured': diff --git a/src/core/module/deposit/data.ts b/src/core/module/deposit/data.ts index 2c9f5878..e5ff0c28 100644 --- a/src/core/module/deposit/data.ts +++ b/src/core/module/deposit/data.ts @@ -52,7 +52,7 @@ export function create_deposit ( updated_at : created_at } const proof = notarize_deposit(template, signer, 'registered') - return sort_record({ ...template, created_sig: proof }) + return sort_record({ ...template, agent_sig: proof }) } export function confirm_deposit ( diff --git a/src/core/module/deposit/state.ts b/src/core/module/deposit/state.ts index 2e970e11..70083a53 100644 --- a/src/core/module/deposit/state.ts +++ b/src/core/module/deposit/state.ts @@ -39,8 +39,8 @@ export const GET_REGISTER_STATE = () => { ...INIT_LOCK_STATE(), ...INIT_SPEND_STATE(), ...INIT_SETTLE_STATE(), - created_sig : null, - status : 'registered' as DepositStatus + agent_sig : null, + status : 'registered' as DepositStatus } } @@ -130,7 +130,7 @@ export function get_deposit_proof ( let sig switch (status) { case 'registered': - sig = deposit.created_sig; break + sig = deposit.agent_sig; break case 'locked': sig = deposit.locked_sig; break case 'closed': diff --git a/src/core/module/witness/data.ts b/src/core/module/witness/data.ts index 04d95b91..89dcbde8 100644 --- a/src/core/module/witness/data.ts +++ b/src/core/module/witness/data.ts @@ -19,12 +19,12 @@ export function create_receipt ( receipt_at = now() ) : WitnessReceipt { const agent_pk = signer.pubkey - const vm_closed = data.closed - const vm_hash = data.head - const vm_output = data.output - const vm_step = data.step - const preimg = { ...witness, receipt_at, agent_pk, vm_closed, vm_hash, vm_output, vm_step } - const receipt_id = get_receipt_id(preimg) - const receipt_sig = signer.sign(receipt_id) - return sort_record({ ...preimg, receipt_id, receipt_sig }) + const vm_closed = data.closed + const vm_hash = data.head + const vm_output = data.output + const vm_step = data.step + const preimg = { ...witness, receipt_at, agent_pk, vm_closed, vm_hash, vm_output, vm_step } + const receipt_id = get_receipt_id(preimg) + const agent_sig = signer.sign(receipt_id) + return sort_record({ ...preimg, receipt_id, agent_sig }) } diff --git a/src/core/schema/account.ts b/src/core/schema/account.ts index e38748ce..4b1844ae 100644 --- a/src/core/schema/account.ts +++ b/src/core/schema/account.ts @@ -34,8 +34,8 @@ const commit_req = register_req.extend({ covenant }) const data = z.object({ account_hash : hash, account_id : hash, - account_sig : nonce, agent_pk : hash, + agent_sig : hex, agent_tkn : token, created_at : stamp, deposit_pk : hash, diff --git a/src/core/schema/contract.ts b/src/core/schema/contract.ts index 2beafc9b..00bb8017 100644 --- a/src/core/schema/contract.ts +++ b/src/core/schema/contract.ts @@ -112,9 +112,9 @@ const close_state = z.discriminatedUnion('closed', [ ct_open, ct_close const base_data = z.object({ agent_pk : hash, + agent_sig : hex, cid : hash, created_at : stamp, - created_sig : hex, deadline_at : stamp, endorsements : hex.array(), feerate : num, @@ -124,7 +124,6 @@ const base_data = z.object({ outputs : output.array(), moderator : hash.nullable(), prop_id : hash, - sigs : z.tuple([ status, hex ]).array(), status, subtotal : num, terms : proposal.data, @@ -137,7 +136,7 @@ const base_data = z.object({ vin_txfee : num }) -const data = base_data +const data = base_data .and(publish_state) .and(funding_state) .and(engine_state) diff --git a/src/core/schema/deposit.ts b/src/core/schema/deposit.ts index fb9e0955..b95e113a 100644 --- a/src/core/schema/deposit.ts +++ b/src/core/schema/deposit.ts @@ -78,9 +78,9 @@ const base_data = z.object({ status, account_hash : hash, agent_pk : hash, + agent_sig : hex, agent_tkn : hex, created_at : stamp, - created_sig : hex, dpid : hash, deposit_pk : hash, deposit_addr : str, @@ -90,7 +90,6 @@ const base_data = z.object({ return_psig : hex, return_rate : num, satpoint : str, - sigs : z.tuple([ status, hex ]).array(), updated_at : stamp, utxo : tx.txout }) diff --git a/src/core/schema/witness.ts b/src/core/schema/witness.ts index 85164380..4061bcb3 100644 --- a/src/core/schema/witness.ts +++ b/src/core/schema/witness.ts @@ -17,14 +17,14 @@ const data = z.object({ }) const receipt = data.extend({ - agent_pk : hash, - receipt_at : stamp, - receipt_id : hash, - receipt_sig : signature, - vm_closed : bool, - vm_hash : hash, - vm_output : str.nullable(), - vm_step : num + agent_pk : hash, + agent_sig : signature, + receipt_at : stamp, + receipt_id : hash, + vm_closed : bool, + vm_hash : hash, + vm_output : str.nullable(), + vm_step : num }) export default { data, receipt } diff --git a/src/core/types/account.ts b/src/core/types/account.ts index 7ea86e43..4962538e 100644 --- a/src/core/types/account.ts +++ b/src/core/types/account.ts @@ -31,8 +31,8 @@ export interface AccountContext { export interface AccountData { account_hash : string account_id : string - account_sig : string agent_pk : string + agent_sig : string agent_tkn : string created_at : number deposit_addr : string diff --git a/src/core/types/contract.ts b/src/core/types/contract.ts index 7ea78b5a..d975e055 100644 --- a/src/core/types/contract.ts +++ b/src/core/types/contract.ts @@ -34,7 +34,7 @@ export type ContractData = TxSpendState & TxSettleState -export type ContractSignatures = 'created_sig' | 'canceled_sig' | 'secured_sig' | 'active_sig' | +export type ContractSignatures = 'agent_sig' | 'canceled_sig' | 'secured_sig' | 'active_sig' | 'closed_sig' | 'spent_sig' | 'settled_sig' export type ContractPreImage = Omit @@ -112,9 +112,10 @@ export interface ContractCreateConfig { } export interface ContractBase { + agent_pk : string + agent_sig : string cid : string created_at : number - created_sig : string deadline_at : number endorsements : string[] fees : PaymentEntry[] @@ -124,7 +125,6 @@ export interface ContractBase { moderator : string | null outputs : SpendTemplate[] prop_id : string - agent_pk : string status : ContractStatus subtotal : number terms : ProposalData diff --git a/src/core/types/deposit.ts b/src/core/types/deposit.ts index 16f69d81..79b451a5 100644 --- a/src/core/types/deposit.ts +++ b/src/core/types/deposit.ts @@ -13,7 +13,7 @@ export type CloseState = DepositIsClosed | DepositIsOpen export type DepositData = DepositBase & TxConfirmState & LockState & CloseState & TxSettleState & TxSpendState export type DepositStatus = 'registered' | 'confirmed' | 'closed' | 'locked' | 'spent' | 'settled' | 'expired' | 'error' -export type DepositSignatures = 'created_sig' | 'locked_sig' | 'closed_sig' | 'spent_sig' | 'settled_sig' +export type DepositSignatures = 'agent_sig' | 'locked_sig' | 'closed_sig' | 'spent_sig' | 'settled_sig' export type DepositPreImage = Omit export type FundingData = TxConfirmState & TxSettleState & TxSpendState & { @@ -71,8 +71,10 @@ export interface DepositConfig { export interface DepositBase { account_hash : string + agent_pk : string + agent_sig : string + agent_tkn : string created_at : number - created_sig : string deposit_pk : string deposit_addr : string dpid : string @@ -82,8 +84,6 @@ export interface DepositBase { return_rate : number return_psig : string satpoint : string - agent_pk : string - agent_tkn : string status : DepositStatus updated_at : number utxo : TxOutput diff --git a/src/core/types/witness.ts b/src/core/types/witness.ts index aa92bcd2..ac0e510b 100644 --- a/src/core/types/witness.ts +++ b/src/core/types/witness.ts @@ -35,6 +35,6 @@ export interface ReceiptPreImage extends WitnessData { } export interface WitnessReceipt extends ReceiptPreImage { - receipt_id : string - receipt_sig : string + receipt_id : string + agent_sig : string } diff --git a/src/core/util/base.ts b/src/core/util/base.ts index 252681d7..6f6e3d1a 100644 --- a/src/core/util/base.ts +++ b/src/core/util/base.ts @@ -1,4 +1,5 @@ import { Buff, Bytes } from '@cmdcode/buff' +import { sha256 } from '@cmdcode/crypto-tools/hash' export function get_entry ( label : string, @@ -71,6 +72,14 @@ export function sort_record > ( }, {}) as T } +export function get_record_id (obj : T) : Buff { + if (Array.isArray(obj) || obj === null || typeof obj !== 'object') { + throw new Error('not a valid record') + } + const sorted = sort_record(obj) + return sha256(Buff.json(sorted)) +} + export function stringify (content : any) : string { switch (typeof content) { case 'object': diff --git a/src/core/util/index.ts b/src/core/util/index.ts index 67f3cf30..3a2e387d 100644 --- a/src/core/util/index.ts +++ b/src/core/util/index.ts @@ -1,6 +1,6 @@ export * from './base.js' export * from './notarize.js' -export * from './parse.js' -export * from './validate.js' export * as assert from './assert.js' +export * as check from './validate.js' +export * as parser from './parse.js' diff --git a/src/core/util/notarize.ts b/src/core/util/notarize.ts index 364f7555..ef80ff6d 100644 --- a/src/core/util/notarize.ts +++ b/src/core/util/notarize.ts @@ -4,15 +4,6 @@ import { sha256 } from '@cmdcode/crypto-tools/hash' import { NoteTemplate } from '@/core/types/index.js' import * as assert from '@/core/util/assert.js' -export function get_record_id (obj : T) : Buff { - if ( - Array.isArray(obj) || - obj === null || - typeof obj !== 'object' - ) { throw new Error('not an object') } - return sha256(Buff.json(obj)) -} - export function get_proof_id ( template : NoteTemplate ) { diff --git a/src/core/util/validate.ts b/src/core/util/validate.ts index 8dd3038c..5f8c9cc9 100644 --- a/src/core/util/validate.ts +++ b/src/core/util/validate.ts @@ -1,4 +1,4 @@ -import { Point } from '@cmdcode/crypto-tools' +import { Point } from '@cmdcode/crypto-tools' import { Literal } from '../types/index.js' export function exists ( diff --git a/src/core/validation/account.ts b/src/core/validation/account.ts index 0933ef22..902076e3 100644 --- a/src/core/validation/account.ts +++ b/src/core/validation/account.ts @@ -120,7 +120,7 @@ export function verify_account_data ( account : AccountData, signer : SignerAPI ) { - const { account_id, created_at, account_sig, deposit_addr, agent_pk, agent_tkn } = account + const { account_id, created_at, agent_sig, deposit_addr, agent_pk, agent_tkn } = account // Create a context object for the account. const ctx = get_account_ctx(account) // @@ -135,7 +135,7 @@ export function verify_account_data ( assert.ok(signer.pubkey === pk, 'deposit pubkey does not match signing device') assert.ok(ctx.deposit_addr === addr, 'deposit address does not match signing device') assert.ok(id === account_id, 'account id does not match computed id') - assert.ok(verify_sig(account_sig, id, agent_pk), 'server signature is invalid') + assert.ok(verify_sig(agent_sig, id, agent_pk), 'server signature is invalid') } export function verify_session_token ( diff --git a/src/core/validation/covenant.ts b/src/core/validation/covenant.ts index 193b9a43..45deb218 100644 --- a/src/core/validation/covenant.ts +++ b/src/core/validation/covenant.ts @@ -4,9 +4,9 @@ import { verify_psig } from '@cmdcode/musig2' /* Module Imports */ -import { get_account_agent } from '../module/account/util.js' -import { parse_session_token } from '../lib/session.js' -import { assert, parse_covenant } from '../util/index.js' +import { get_account_agent } from '../module/account/util.js' +import { parse_session_token } from '../lib/session.js' +import { assert, parser } from '../util/index.js' import { get_covenant_id, @@ -32,7 +32,7 @@ import { export function validate_covenant_data ( covenant : unknown ) : asserts covenant is CovenantData { - parse_covenant(covenant) + parser.parse_covenant(covenant) } export function verify_covenant_data ( diff --git a/src/core/validation/proposal.ts b/src/core/validation/proposal.ts index 44e33345..71c1fe5d 100644 --- a/src/core/validation/proposal.ts +++ b/src/core/validation/proposal.ts @@ -7,11 +7,7 @@ import { get_path_names } from '../lib/proposal.js' -import { - assert, - now, - parse_proposal -} from '../util/index.js' +import { assert, now, parser } from '../util/index.js' import { ProposalData, @@ -40,7 +36,7 @@ export function validate_proposal_tmpl ( export function validate_proposal_data ( proposal : unknown ) : asserts proposal is ProposalData { - parse_proposal(proposal) + parser.parse_proposal(proposal) } export function verify_proposal_data ( diff --git a/src/core/validation/witness.ts b/src/core/validation/witness.ts index e01d5426..dc0c1d48 100644 --- a/src/core/validation/witness.ts +++ b/src/core/validation/witness.ts @@ -102,7 +102,7 @@ export function verify_witness_receipt ( vmdata : VMData, witness : WitnessData ) { - const { receipt_id, receipt_sig, agent_pk } = receipt + const { receipt_id, agent_sig, agent_pk } = receipt // Don't forget to check that vm matches receipt. assert.ok(witness.vmid === vmdata.vmid, 'provided vmdata and witness vmid does not match') @@ -118,7 +118,7 @@ export function verify_witness_receipt ( assert.ok(int_wid === witness.wid, 'internal witness id does not match receipt') assert.ok(int_rid === receipt.receipt_id, 'internal receipt id does not match receipt') - const is_valid = verify_sig(receipt_sig, receipt_id, agent_pk) + const is_valid = verify_sig(agent_sig, receipt_id, agent_pk) assert.ok(is_valid, 'receipt signature is invalid') } diff --git a/src/vm/lib/methods/hashlock.ts b/src/vm/lib/methods/hashlock.ts index 05ecc764..6fcdabb2 100644 --- a/src/vm/lib/methods/hashlock.ts +++ b/src/vm/lib/methods/hashlock.ts @@ -1,6 +1,6 @@ -import { assert, is_hash } from '@/core/util/index.js' +import { assert, check } from '@/core/util/index.js' import { Literal, WitnessData } from '@/core/types/index.js' -import { VMError } from '../../util/base.js' +import { VMError } from '@/vm/util/base.js' import { sha256 } from '@cmdcode/crypto-tools/hash' /** @@ -21,7 +21,7 @@ function exec ( const { action, args, path, sigs } = witness // const preimg = args.at(0) - if (!is_hash(preimg)) { + if (!check.is_hash(preimg)) { throw new VMError('invalid pre image provided') } // @@ -53,7 +53,7 @@ function verify (params : Literal[]) { const thold = Number(threshold) const pubs = pubkeys.map(e => String(e)) - if (!is_hash(hashlock)) { + if (!check.is_hash(hashlock)) { return 'invalid hashlock' } else if (typeof thold !== 'number') { return 'invalid threshold value: ' + String(thold) diff --git a/test/src/e2e/settle.test.ts b/test/src/e2e/settle.test.ts index 5c412d6c..49cb2f02 100644 --- a/test/src/e2e/settle.test.ts +++ b/test/src/e2e/settle.test.ts @@ -12,7 +12,6 @@ import { get_vm_config } from '@scrow/sdk/vm' import CVM from '@scrow/sdk/cvm' import { - CoreAssert, DepositData, PaymentEntry, TxOutput @@ -52,7 +51,8 @@ import { verify_witness_data, verify_contract_sigs, verify_deposit_sigs, - verify_contract_settlement + verify_contract_settlement, + verify_contract_req } from '@/core/validation/index.js' import { @@ -125,7 +125,7 @@ export default async function ( // Client: Create a contract request. const pub_req = create_publish_req(proposal, signatures) // Server: Verify contract request. - CoreAssert.contract.verify.request(CVM, server_pol.proposal, pub_req) + verify_contract_req(CVM, server_pol.proposal, pub_req) // Server: Create contract data. let contract = create_contract(ct_config, pub_req, escrow_dev) From 27f5a0647549a7a093e7f9b5b7975be64917724a Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Sun, 19 May 2024 17:57:51 -0500 Subject: [PATCH 15/27] v0.12.40-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8c09d972..7d588dcc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.39-dev", + "version": "0.12.40-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From 241dee8693953f0e552455fc23b539452fa09a30 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 20 May 2024 13:56:10 -0500 Subject: [PATCH 16/27] v0.12.41-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7d588dcc..e7825f95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.40-dev", + "version": "0.12.41-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From 62f5fad1a0f2e58b690df855d644136f94671d4f Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 20 May 2024 14:05:40 -0500 Subject: [PATCH 17/27] v0.12.42-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e7825f95..42f294d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.41-dev", + "version": "0.12.42-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From 3e33e877ec63a90c267c977fb396f177b611930f Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 20 May 2024 14:12:32 -0500 Subject: [PATCH 18/27] v0.12.43-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 42f294d0..ab30f44a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.42-dev", + "version": "0.12.43-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From 7b473ae9184f8d82b652d61cb0a08f811756560f Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 20 May 2024 14:14:58 -0500 Subject: [PATCH 19/27] v0.12.44-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ab30f44a..14c36583 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scrow/sdk", - "version": "0.12.43-dev", + "version": "0.12.44-dev", "description": "Development SDK for the BitEscrow API.", "author": "Christopher Scott", "license": "MIT", From 83a7c301d45eb569464b3d92e8ea40a9caaaa680 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Tue, 21 May 2024 13:35:06 -0500 Subject: [PATCH 20/27] update --- README.md | 90 +--- demo/00_demo_config.ts | 11 +- demo/01_create_client.ts | 11 +- demo/02_create_signer.ts | 22 +- demo/03_create_proposal.ts | 8 +- demo/04_finish_proposal.ts | 36 +- demo/05_create_contract.ts | 28 +- demo/06_request_account.ts | 38 +- demo/07_deposit_funds.ts | 81 ++-- ...eck_contract.ts => 08_check_activation.ts} | 17 +- demo/09_settle_contract.ts | 91 ---- demo/09_submit_statements.ts | 78 +++ demo/10_settle_contract.ts | 58 +++ demo/README.md | 1 - demo/api/account/commit.ts | 38 +- demo/api/account/register.ts | 36 +- demo/api/account/request.ts | 4 +- demo/api/contract/cancel.ts | 6 +- demo/api/contract/create.ts | 15 +- demo/api/contract/funds.ts | 2 +- demo/api/contract/list.ts | 6 +- demo/api/contract/read.ts | 2 +- demo/api/deposit/cancel.ts | 8 +- demo/api/deposit/close.ts | 24 +- demo/api/deposit/list.ts | 8 +- demo/api/deposit/lock.ts | 5 +- demo/api/deposit/read.ts | 4 +- demo/api/witness/read.ts | 2 +- demo/run.sh | 2 +- docs/api/account.md | 117 ++--- docs/api/contract.md | 132 ++--- docs/api/deposit.md | 124 ++--- docs/api/machine.md | 197 ++------ docs/api/witness.md | 59 ++- docs/class/client.md | 31 +- docs/class/signer.md | 2 +- docs/{interfaces => data}/_category_.json | 2 +- docs/{interfaces => data}/account.md | 10 +- docs/{interfaces => data}/contract.md | 0 docs/{interfaces => data}/deposit.md | 2 +- docs/{interfaces => data}/draft.md | 2 +- docs/{interfaces => data}/machine.md | 0 docs/{interfaces => data}/oracle.md | 0 docs/{interfaces => data}/server.md | 0 docs/{interfaces => data}/witness.md | 0 docs/development.md | 88 ++++ docs/examples/AccountData.md | 17 + docs/examples/ContractData.md | 123 +++++ docs/examples/DepositData.md | 64 +++ .../{contract_funds.md => FundingData.md} | 0 docs/examples/MachineData.md | 64 +++ docs/examples/WitnessData.md | 19 + docs/examples/WitnessReceipt.md | 0 docs/examples/_category_.json | 2 +- docs/examples/contract_active.md | 230 --------- docs/examples/contract_canceled.md | 154 ------ docs/examples/contract_data.md | 154 ------ docs/examples/contract_vm.md | 85 ---- docs/examples/deposit_account.md | 15 - docs/examples/deposit_closed.md | 32 -- docs/examples/deposit_commit.md | 281 ----------- docs/examples/deposit_data.md | 32 -- docs/examples/deposit_locked.md | 53 -- docs/examples/witness_data.md | 17 - docs/examples/witness_list.md | 19 - docs/get-started.md | 452 +++++++++++------- docs/intro.md | 76 ++- package.json | 8 +- src/client/api/client/contract.ts | 14 +- src/client/api/client/witness.ts | 25 +- src/client/api/signer/contract.ts | 22 +- src/client/api/signer/witness.ts | 20 +- src/client/class/client.ts | 13 +- src/client/class/oracle.ts | 39 +- src/client/class/signer.ts | 6 +- src/client/config/index.ts | 6 +- src/client/types/base.ts | 6 +- src/client/types/server.ts | 4 +- src/client/types/vm.ts | 12 +- src/client/types/witness.ts | 6 +- src/core/lib/tx.ts | 4 +- src/core/module/account/api.ts | 4 +- src/core/module/account/data.ts | 4 +- src/core/module/account/util.ts | 4 +- src/core/module/contract/data.ts | 7 +- src/core/module/contract/state.ts | 6 +- src/core/module/contract/util.ts | 2 +- src/core/module/deposit/data.ts | 3 +- src/core/module/deposit/state.ts | 6 +- src/core/module/witness/api.ts | 4 +- src/core/module/witness/data.ts | 24 +- src/core/module/witness/util.ts | 16 +- src/core/schema/account.ts | 2 +- src/core/schema/contract.ts | 15 +- src/core/schema/deposit.ts | 2 +- src/core/schema/vm.ts | 2 +- src/core/schema/witness.ts | 12 +- src/core/types/account.ts | 18 +- src/core/types/base.ts | 2 +- src/core/types/contract.ts | 17 +- src/core/types/deposit.ts | 8 +- src/core/types/machine.ts | 12 +- src/core/types/proposal.ts | 4 +- src/core/types/recovery.ts | 4 +- src/core/types/witness.ts | 24 +- src/core/validation/account.ts | 4 +- src/core/validation/contract.ts | 119 +++-- src/core/validation/witness.ts | 46 +- src/vm/lib/main.ts | 12 +- src/vm/types.ts | 4 +- src/vm/util/parse.ts | 6 +- test/src/e2e/settle.test.ts | 16 +- test/src/util.ts | 4 +- test/src/vm/util.ts | 6 +- test/wip/spec.yml | 4 +- {docs => test/wip}/wiki/_category_.json | 0 {docs => test/wip}/wiki/client.md | 4 +- {docs => test/wip}/wiki/contract.md | 0 {docs => test/wip}/wiki/deposit.md | 0 {docs => test/wip}/wiki/draft.md | 4 +- {docs => test/wip}/wiki/servers.md | 0 {docs => test/wip}/wiki/signer.md | 0 122 files changed, 1702 insertions(+), 2301 deletions(-) rename demo/{08_check_contract.ts => 08_check_activation.ts} (80%) delete mode 100644 demo/09_settle_contract.ts create mode 100644 demo/09_submit_statements.ts create mode 100644 demo/10_settle_contract.ts rename docs/{interfaces => data}/_category_.json (81%) rename docs/{interfaces => data}/account.md (87%) rename docs/{interfaces => data}/contract.md (100%) rename docs/{interfaces => data}/deposit.md (98%) rename docs/{interfaces => data}/draft.md (97%) rename docs/{interfaces => data}/machine.md (100%) rename docs/{interfaces => data}/oracle.md (100%) rename docs/{interfaces => data}/server.md (100%) rename docs/{interfaces => data}/witness.md (100%) create mode 100644 docs/development.md create mode 100644 docs/examples/AccountData.md create mode 100644 docs/examples/ContractData.md create mode 100644 docs/examples/DepositData.md rename docs/examples/{contract_funds.md => FundingData.md} (100%) create mode 100644 docs/examples/MachineData.md create mode 100644 docs/examples/WitnessData.md create mode 100644 docs/examples/WitnessReceipt.md delete mode 100644 docs/examples/contract_active.md delete mode 100644 docs/examples/contract_canceled.md delete mode 100644 docs/examples/contract_data.md delete mode 100644 docs/examples/contract_vm.md delete mode 100644 docs/examples/deposit_account.md delete mode 100644 docs/examples/deposit_closed.md delete mode 100644 docs/examples/deposit_commit.md delete mode 100644 docs/examples/deposit_data.md delete mode 100644 docs/examples/deposit_locked.md delete mode 100644 docs/examples/witness_data.md delete mode 100644 docs/examples/witness_list.md rename {docs => test/wip}/wiki/_category_.json (100%) rename {docs => test/wip}/wiki/client.md (95%) rename {docs => test/wip}/wiki/contract.md (100%) rename {docs => test/wip}/wiki/deposit.md (100%) rename {docs => test/wip}/wiki/draft.md (99%) rename {docs => test/wip}/wiki/servers.md (100%) rename {docs => test/wip}/wiki/signer.md (100%) diff --git a/README.md b/README.md index 71b9bcca..ca1a55a4 100644 --- a/README.md +++ b/README.md @@ -237,8 +237,6 @@ vm_state: { } ``` -It - ### Settlement Once the machine has settled on a final outcome, the escrow agent will close the contract. If the machine specifies a spending path, the escrow agent will co-sign the relevant transaction and broadcast it to the Bitcoin network. @@ -354,6 +352,8 @@ The current chains available are `mutiny`, `signet`, and `testnet`. The default You can read more info about the demo [here](demo/README.md). +--- + ## Development / Testing To get started, make sure you are running `v19+` of node, then install the project dependencies using your node manager of choice: @@ -363,94 +363,22 @@ node --version # should be v19+ npm install # install dependencies ``` -This project uses the following scripts: - -```md - build : Compiles the contents of `src` folder to `dist`. - demo : Runs the protocol demo for the provided chain. - load