Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
cmd committed Feb 23, 2024
1 parent e02d76b commit a1ec5fd
Show file tree
Hide file tree
Showing 9 changed files with 590 additions and 3 deletions.
8 changes: 8 additions & 0 deletions docs/examples/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "Example Data",
"position": 4,
"link": {
"type": "generated-index",
"description": "Example data for BitEscrow API interfaces."
}
}
5 changes: 5 additions & 0 deletions docs/examples/contract_active.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Active Contract

```json

```
5 changes: 5 additions & 0 deletions docs/examples/contract_canceled.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Canceled Contract

```json

```
5 changes: 5 additions & 0 deletions docs/examples/contract_digest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Contract Digest

```json

```
5 changes: 5 additions & 0 deletions docs/examples/contract_funds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Contract Funds

```json

```
5 changes: 5 additions & 0 deletions docs/examples/contract_status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Contract Status

```json

```
305 changes: 304 additions & 1 deletion docs/get-started.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,306 @@
# Getting Started

Getting started page coming soon!
Below is a step-by-step guide on how to use the protocol in an example scenario. This guide includes a live demo that you can run on your own machine, with no special software required.

To run the demo, simply clone this repository, then run the following commands:

```sh
npm install # Install all package dependencies.
npm run demo {chain} # Run the demo using the provided chain.
```

> Note: The current chains available are `mutiny`, `signet`, and `testnet`. The default chain is `mutiny`.
Read more info about the demo [here](demo/README.md).

### Create a Client

The `EscrowClient` is a basic client for consuming our API. It is designed to be used for any tasks which do not require an identity or signature.

```ts
import { EscrowClient } from '@scrow/core/client'

const client_config = {
// The URL to our escrow server.
hostname : 'https://bitescrow-signet.vercel.app',
// The URL to an electrum-based indexer of your choice.
oracle : 'https://mempool.space/signet',
// The network you are using.
network : 'signet'
}

// Create an EscrowClient using the above config.
const client = new EscrowClient(client_config)
```

For more info on the `EscrowClient` class, [click here](docs/client.md).

### Create a Signer

The `EscrowSigner` is used to setup deposits and interact with a contract.

The fastest way to setup a new signer is to generate one randomly:

```ts
import { EscrowSigner } from '@scrow/core/client'

// Generate a new EscrowSigner from scratch.
const signer = EscrowSigner.generate(client_config, xpub)
```

You can import your own key-pair using a raw seed or BIP39 seed words:

```ts
// Create an EscrowSigner using a raw seed.
const signer = EscrowSigner.create(client_config, seed, xpub)

// Import an EscrowSigner using a word list.
const signer = EscrowSigner
.import(client_config, xpub)
.from_words(seed_words, optional_password)
```

The `EscrowSigner` is designed to plug into a more basic `SignerAPI` and `WalletAPI`, which can be hosted outside the browser:

```ts
import { Signer, Wallet } from '@cmdcode/signer'
import { EscrowSigner } from '@scrow/core/client'

// These can be provided externally through
// the browser's global window object.
const signer_api = new Signer({ seed : 'your seed' })
const wallet_api = new Wallet('your_xpub')

const signer_config = {
...client_config,
signer : signer_api,
wallet : wallet_api
}

const signer = new EscrowSigner(signer_config)
```

The `EscrowSigner` is built for insecure environments. All addresses are derived from the xpub, and all signed transactions are verified before deposit. The signing key has no direct access to funds.

The signing key is also disposable, and can be tossed from memory once the contract and covenant are in place. All credentials generated by the signer are independently recoverable by the xpub.

For more info on the `EscrowSigner` class, [click here](docs/signer.md).

### Build a Proposal

The proposal is a simple JSON document, and it can be built any number of ways.

You can use the `create_proposal` tool to create a basic template:

```ts
import { create_proposal } from '@scrow/core'

// We start with a basic template, and pass it through
// a helper method to ensure we have the correct format.
const template = create_proposal({
title : 'Basic two-party contract with third-party arbitration.',
duration : 14400,
network : 'signet',
schedule : [[ 7200, 'close', 'draw' ]],
value : 15000,
})

```

To make the negotiation process easier, we have a tool for defining intended `roles` within a proposal:

```ts
// We can define roles that users can choose from. Each role policy
// instructs a member's device on how to complete the proposal.
const roles = {
buyer : create_policy({
paths : [
[ 'heads', 10000 ],
[ 'draw', 5000 ]
],
programs : [
[ 'endorse', 'close', 'heads|tails|draw', 2 ],
[ 'endorse', 'dispute', 'heads|tails', 1 ]
]
}),
seller : create_policy({
paths : [
[ 'tails', 10000 ],
[ 'draw', 5000 ]
],
programs : [
[ 'endorse', 'close', 'heads|tails|draw', 2 ],
[ 'endorse', 'dispute', 'heads|tails', 1 ]
]
}),
agent : create_policy({
payment : 5000,
programs : [
[ 'endorse', 'resolve', 'heads|tails|draw', 1 ]
]
})
}

```

With defined roles, we can invite each `EscrowSigner` to join the proposal as a given role. Members can review the `policy`, and their device will use it to fill out the proposal:

```ts
// Each member is an EscrowSigner object.
const [ a_signer, b_signer, c_signer ] = signers

// Use our template from earlier.
let proposal = template

// Call each signer to join the proposal as a given role.
proposal = a_signer.proposal.join(proposal, roles.buyer)
proposal = b_signer.proposal.join(proposal, roles.seller)
proposal = c_signer.proposal.join(proposal, roles.agent)
```

Once the proposal is completed, any member can deliver it to the escrow server.

**Optional: Endorsing a Proposal**

Members have the option to sign a complete proposal to signal their endorsement:

```ts
const signatures = signers.map(mbr => {
// Collect an endorsement from the user's signer.
return mbr.proposal.endorse(proposal)
})
```

Each endorsement provided to the server will tag the proposal with the signer's pubkey. This allows a member to search for contracts via their pubkey.

> Endorsing a proposal does not reveal which credential belongs to you.
For more information on building a `proposal`, [click here](docs/proposal.md).

### Create a Contract

Once you have a complete proposal, the next step is to create a [contract](docs/interfaces/contract.md#contractdata):

```ts
// Request to create a contract from the proposal (and optional signatures).
const res = await client.contract.create(proposal, signatures)
// Check that the response is valid.
if (!res.ok) throw new Error(res.error)
// Unpack and return the contract data.
const { contract } = res.data
```

The contract begins in a `published` state, and is ready for funding. You can share the contract with others by advertising its unique identifier, the `cid`.

For more info on how to use a contract, [click here](docs/contract.md).

### Deposit Funds

To make a deposit, we start by requesting a deposit [account](docs/interfaces/deposit.md#depositaccount) from the escrow server:

```ts
// Define our deposit locktime.
const locktime = 60 * 60 // 1 hour locktime
// Get an account request from the signing device.
const acct_req = signer.account.request(locktime)
// Submit the request to the server
const acct_res = await client.deposit.request(acct_req)
// Check the response is valid.
if (!res.ok) throw new Error(res.error)
// Unpack the account data.
const { account } = res.data
```

Each account is a time-locked 2-of-2 multi-signature address between the funder's signing device, and a random signing `agent` from the server.

It is important that funders verify the account information is correct:

```ts
// Verify the account.
const is_valid = signer.account.verify(account)
if (!is_valid) throw new Error('account is invalid!')
```

After verifying the account, funders can safely send funds to the account address. Once the transaction is visible in the mempool, we can grab the transaction's `utxo` data using an oracle:

```ts
// Unpack the address from the account.
const { address } = account
// Fetch all utxos from the address.
const utxos = await client.oracle.get_address_utxos(address)
// There should be a utxo present.
if (utxos.length === 0) throw new Error('utxo not found')
// Get the output data from the utxo.
const utxo_data = utxos[0].txspend
```

The final step is to register the `utxo` with the escrow server, and provide a `covenant` that locks it to the contract. We can perform both actions using the `commit` method:

```ts
// Create a commit request.
const commit_req = signer.account.commit(account, contract, utxo)
// Deliver the request to the server.
const res = await client.deposit.commit(commit_req)
// Check the response is valid.
if (!res.ok) throw new Error('failed')
// Unpack the response data, which should be the deposit and updated contract.
const { contract, deposit } = res.data
```

> For more info on managing a `deposit`, [click here](docs/deposit.md).
### Settle a Contract

Once all required funds are deposited and confirmed, the contract virtual machine (CVM) will activate.

Members of the contract can interact with the CVM by providing signed statements, called a [witness](./interfaces/witness.md):

```ts
// Start with a witness template.
const template : WitnessTemplate = {
action : 'close', // We want to close the contract.
method : 'endorse', // Using the 'endorse' (signature) method.
path : 'payout' // Settling on the 'payout' path.
}
```

Members can use their signing device to create a new statement, or endorse an existing statement from another member:

```ts
// Example list of signers.
const [ a_signer, b_signer ] : EscrowSigner[] = signers
// Initialize a variable for our witness data.
let witness : WitnessData
// Alice signs the initial statement.
witness = a_signer.witness.sign(contract, template)
// Bob endoreses the statement from Alice.
witness = b_signer.witness.endorse(contract, witness)
```

These statements are submitted to the contract, and evaluated by the CVM. If the statement is valid, then the CVM will update its [state](docs/interfaces/contract.md#statedata), and the server will deliver an updated contract:

```ts
// Submit the completed statement to the contract.
const res = await client.contract.submit(contract.cid, witness)
// Check the response is valid.
if (!res.ok) throw new Error(res.error)
// The returned contract should be settled.
const updated_contract = res.data.contract
```

If a spending condition is met within the CVM, the contract will be closed by the escrow server, and a settlement transaction will be broadcast.

The updated contract will record this information under `spent_txid`. We can view the settlement transaction using an oracle:

```ts
// Get the transaction id from the contract.
const txid = settled_contract.spent_txid
// Fetch the settlement tx from the oracle.
const txdata = await client.oracle.get_txdata(txid)
// Print the transaction data to console.
console.dir(txdata, { depth : null })
```

And that is it! The on-chain transaction will look like an anonymous coin-join of single-key spends, and it can be fee-bumped by any recipient of the contract funds using CPFP.

For more information on contracts, the CVM, and settlement process, [click here](docs/contract.md).
Loading

0 comments on commit a1ec5fd

Please sign in to comment.