Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
cmd committed Jan 28, 2024
1 parent d23c58f commit ca4770a
Show file tree
Hide file tree
Showing 20 changed files with 385 additions and 310 deletions.
2 changes: 1 addition & 1 deletion src/client/api/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function list_contract_api (client : EscrowClient) {
token : string
) : Promise<ApiResponse<ContractListResponse>> => {
// Formulate the request.
const url = `${client.host}/api/contract/list?pubkey=${pubkey}`
const url = `${client.host}/api/contract/list/${pubkey}`
// Return the response.
return client.fetcher<ContractListResponse>({ url, token })
}
Expand Down
56 changes: 26 additions & 30 deletions src/client/api/deposit.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@

import { EscrowClient } from '../class/client.js'
import { validate_registration } from '@/validators/index.js'
import { validate_register_req } from '@/validators/index.js'

import {
CovenantData,
ReturnData,
ApiResponse,
DepositRequest,
AccountRequest,
AccountDataResponse,
DepositDataResponse,
DepositListResponse,
FundingDataResponse
FundingDataResponse,
RegisterRequest
} from '@/types/index.js'

import * as assert from '@/assert.js'

/**
* Request a deposit account from the provider.
*/
function request_deposit_api (client : EscrowClient) {
function request_account_api (client : EscrowClient) {
return async (
req : DepositRequest
request : AccountRequest
) : Promise<ApiResponse<AccountDataResponse>> => {
// Ensure params are string values.
const arr = Object.entries(req)
// Build a query string with params.
const qry = new URLSearchParams(arr).toString()
// Formulate the request.
const url = `${client.host}/api/deposit/request?${qry}`
const url = `${client.host}/api/deposit/request`
// Formulate the request.
const init = {
method : 'POST',
body : JSON.stringify(request),
headers : { 'content-type' : 'application/json' }
}
// Return the response.
return client.fetcher<AccountDataResponse>({ url })
return client.fetcher<AccountDataResponse>({ url, init })
}
}

Expand All @@ -38,19 +41,16 @@ function request_deposit_api (client : EscrowClient) {
*/
function register_deposit_api (client : EscrowClient) {
return async (
agent_id : string,
return_tx : string
request : RegisterRequest
) : Promise<ApiResponse<DepositDataResponse>> => {
// Create template
const tmpl = { agent_id, return_tx }
// Validate the deposit template.
validate_registration({ agent_id, return_tx })
// Validate the request.
validate_register_req(request)
// Configure the url.
const url = `${client.host}/api/deposit/register`
// Formulate the request.
const init = {
method : 'POST',
body : JSON.stringify(tmpl),
body : JSON.stringify(request),
headers : { 'content-type' : 'application/json' }
}
// Return the response.
Expand All @@ -63,22 +63,18 @@ function register_deposit_api (client : EscrowClient) {
*/
function register_funds_api (client : EscrowClient) {
return async (
agent_id : string,
return_tx : string,
covenant : CovenantData
request : RegisterRequest
) : Promise<ApiResponse<FundingDataResponse>> => {
// Assert that a covenant is defined.
assert.ok(covenant !== undefined, 'covenant is undefined')
// Create a deposit template.
const templ = { agent_id, return_tx, covenant }
// Validate the deposit template.
validate_registration(templ)
assert.ok(request.covenant !== undefined, 'covenant is undefined')
// Validate the request.
validate_register_req(request)
// Formulate the request url.
const url = `${client.host}/api/deposit/register`
// Forulate the request body.
const init = {
method : 'POST',
body : JSON.stringify(templ),
method : 'POST',
body : JSON.stringify(request),
headers : { 'content-type' : 'application/json' }
}
// Return the response.
Expand Down Expand Up @@ -108,7 +104,7 @@ function list_deposit_api (client : EscrowClient) {
token : string
) : Promise<ApiResponse<DepositListResponse>> => {
// Formulate the request.
const url = `${client.host}/api/deposit/list?pubkey=${pubkey}`
const url = `${client.host}/api/deposit/list/${pubkey}`
// Return the response.
return client.fetcher<DepositListResponse>({ url, token })
}
Expand Down Expand Up @@ -153,7 +149,7 @@ export default function (client : EscrowClient) {
commit : commit_funds_api(client),
fund : register_funds_api(client),
register : register_deposit_api(client),
request : request_deposit_api(client),
request : request_account_api(client),
close : close_deposit_api(client)
}
}
109 changes: 49 additions & 60 deletions src/client/api/depositor.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Buff } from '@cmdcode/buff'
import { create_return_tx } from '@/lib/return.js'
import { parse_extkey } from '@cmdcode/crypto-tools/hd'
import { EscrowSigner } from '@/client/class/signer.js'
import { get_deposit_ctx } from '@/lib/deposit.js'
import { verify_account } from '@/validators/deposit.js'
import { verify_account } from '@/client/validators/deposit.js'

import {
create_covenant,
create_return
create_return_psig
} from '@/lib/session.js'

import {
Expand All @@ -16,113 +16,102 @@ import {
CovenantData,
DepositAccount,
DepositData,
ReturnData,
TxOutput
} from '@/types/index.js'

export function request_account_api (client : EscrowSigner) {
export function request_account_api (signer : EscrowSigner) {
return async (
locktime : number
locktime : number,
index ?: number
) : Promise<ApiResponse<AccountDataResponse>> => {
const pubkey = client.pubkey
return client.client.deposit.request({ pubkey, locktime })
const deposit_pk = signer.pubkey
const spend_xpub = signer.get_account(index).xpub
const req = { deposit_pk, locktime, spend_xpub }
return signer.client.deposit.request(req)
}
}

export function verify_account_api (signer : EscrowSigner) {
return (account : DepositAccount) : void => {
const host_pub = signer.host_pub
const network = signer.client.network
if (host_pub === undefined) {
throw new Error('host pubkey is not set on device')
}
verify_account(account, signer.pubkey, host_pub, network)
}
}

/**
* Create a deposit template for registration.
*/
export function register_utxo_api (client : EscrowSigner) {
return async (
account : DepositAccount,
utxo : TxOutput,
txfee ?: number
) : Promise<string> => {
// Unpack the deposit object.
const { agent_pk, sequence } = account
// Define our pubkey.
const pub = client.pubkey
const idx = Buff.hex(utxo.txid).slice(0, 4).num
const addr = client._wallet.get_account(idx).new_address()
// Get the context object for our deposit account.
const ctx = get_deposit_ctx(agent_pk, pub, sequence)
// Create the return transaction.
return create_return_tx(addr, ctx, client._signer, utxo, txfee)
verify_account(account, signer)
}
}

export function commit_utxo_api (client : EscrowSigner) {
export function commit_utxo_api (signer : EscrowSigner) {
return async (
account : DepositAccount,
contract : ContractData,
utxo : TxOutput
) : Promise<CovenantData> => {
// Unpack the deposit object.
const { agent_pk, sequence } = account
// Define our pubkey.
const pub = client.pubkey
const { agent_pk, sequence, spend_xpub } = account
// Check if account xpub is valid.
if (!signer.has_account(spend_xpub)) {
throw new Error('account xpub is not recognized by master wallet')
}
// Define our pubkey as the deposit pubkey.
const deposit_pk = signer.pubkey
// Define our xpub as the return pubkey.
const return_pk = parse_extkey(spend_xpub).pubkey
// Get the context object for our deposit account.
const ctx = get_deposit_ctx(agent_pk, pub, sequence)
const ctx = get_deposit_ctx(agent_pk, deposit_pk, return_pk, sequence)
// Create a covenant with the contract and deposit.
return create_covenant(ctx, contract, client._signer, utxo)
return create_covenant(ctx, contract, signer._signer, utxo)
}
}

export function commit_deposit_api (client : EscrowSigner) {
export function commit_deposit_api (signer : EscrowSigner) {
return async (
contract : ContractData,
deposit : DepositData
) : Promise<CovenantData> => {
// Unpack the deposit object.
const { agent_pk, sequence, txid, vout, value, scriptkey } = deposit
// Define our pubkey.
const pub = client.pubkey
const {
agent_pk, sequence, txid, vout,
value, scriptkey, spend_xpub
} = deposit
// Check if account xpub is valid.
if (!signer.has_account(spend_xpub)) {
throw new Error('account xpub is not recognized by master wallet')
}
// Define our pubkey as the deposit pubkey.
const deposit_pk = signer.pubkey
// Define our xpub as the return pubkey.
const return_pk = parse_extkey(spend_xpub).pubkey
// Get the context object for our deposit account.
const ctx = get_deposit_ctx(agent_pk, pub, sequence)
const ctx = get_deposit_ctx(agent_pk, deposit_pk, return_pk, sequence)
// Define utxo object from deposit data.
const utxo = { txid, vout, value, scriptkey }
// Create a covenant with the contract and deposit.
return create_covenant(ctx, contract, client._signer, utxo)
return create_covenant(ctx, contract, signer._signer, utxo)
}
}

export function close_deposit_api (client : EscrowSigner) {
export function close_deposit_api (signer : EscrowSigner) {
return async (
deposit : DepositData,
txfee : number,
address ?: string
) : Promise<ReturnData> => {
// Unpack client object.
) : Promise<string> => {
// Unpack signer object.
const { txid } = deposit
if (address === undefined) {
// Compute an index value from the deposit txid.
const acct = Buff.hex(txid).slice(0, 4).num
// Generate refund address.
address = client._wallet.get_account(acct).new_address()
address = signer.get_account(acct).new_address()
}
// Create the return transaction.
return create_return(address, deposit, client._signer, txfee)
return create_return_psig(deposit, signer._signer, txfee)
}
}

export default function (client : EscrowSigner) {
export default function (signer : EscrowSigner) {
return {
request_account : request_account_api(client),
verify_account : verify_account_api(client),
register_utxo : register_utxo_api(client),
commit_utxo : commit_utxo_api(client),
commit_deposit : commit_deposit_api(client),
close_deposit : close_deposit_api(client)
request_account : request_account_api(signer),
verify_account : verify_account_api(signer),
close_account : close_deposit_api(signer),
commit_utxo : commit_utxo_api(signer),
commit_deposit : commit_deposit_api(signer)
}
}
13 changes: 13 additions & 0 deletions src/client/class/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ export class EscrowSigner {
return this._host_pub
}

get network () {
return this._client.network
}

get pubkey () {
return this._signer.pubkey
}
Expand All @@ -81,6 +85,15 @@ export class EscrowSigner {
request = request_api(this)
witness = witness_api(this)

has_account (xpub : string) {
return this._wallet.has_account(xpub)
}

get_account (idx ?: number) {
idx = idx ?? this._gen_idx()
return this._wallet.get_account(idx)
}

save (password : string) {
const pass = Buff.str(password)
const encdata = this._signer.backup(pass)
Expand Down
40 changes: 40 additions & 0 deletions src/client/validators/deposit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { parse_extkey } from '@cmdcode/crypto-tools/hd'
import { verify_sig } from '@cmdcode/crypto-tools/signer'
import { EscrowSigner } from '@/client/index.js'
import { get_object_id } from '@/lib/util.js'

import {
get_deposit_address,
get_deposit_ctx
} from '@/lib/deposit.js'

import { DepositAccount } from '@/types/index.js'

import * as assert from '@/assert.js'

export function verify_account (
account : DepositAccount,
signer : EscrowSigner
) {
const { acct_id, acct_sig, ...rest } = account
const { host_pub, network, pubkey } = signer

const {
address, agent_pk, deposit_pk,
sequence, spend_xpub
} = rest

assert.ok(host_pub !== undefined, 'host pubkey is not set on device')
assert.ok(pubkey === deposit_pk, 'deposit pubkey does not match device')
assert.ok(signer.has_account(spend_xpub), 'account xpub is not recognized by master wallet')

const return_pk = parse_extkey(spend_xpub).pubkey
const context = get_deposit_ctx(agent_pk, deposit_pk, return_pk, sequence)
const depo_addr = get_deposit_address(context, network)
const digest = get_object_id(rest)

assert.ok(address === depo_addr, 'account address does not match context')
assert.ok(digest.hex === acct_id, 'account id does not match digest')

verify_sig(acct_sig, acct_id, host_pub, { throws : true })
}
Loading

0 comments on commit ca4770a

Please sign in to comment.