Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
cmd committed Oct 13, 2023
1 parent 6402b75 commit fba7619
Show file tree
Hide file tree
Showing 39 changed files with 1,984 additions and 530 deletions.
206 changes: 169 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,54 +12,162 @@ Features:

## Overview

The core protocol is divided into three stages: `proposal`, `deposit`, and `settlement`.
The life-cycle of a contract has three stages: `creation`, `deposits` and `settlement`.

## The Protocol

### Examples

**Scenario:** Sales agreement between buyer (alice) and seller (bob) with third-party arbitration.

Step 0 (draft proposal) :
* Alice coordinates on a proposal with Bob.
Step 1 (create contract) :
* Bob submits his proposal to the platform and receives a contract.
* Bob shares this contract with Alice.
Step 2 (deposit funds) :
* Alice deposits her funds into a 2-of 2 account with the contract agent.
* Alice signs a covenant with the agent that funds the contract.
* Once the deposit is confirmed, the contract becomes active.
Step 3 (settle contract) :
* Alice and Bob supply arguments to the CVM, and each receive a receipt.
* Based on the supplied arguments, the CVM will select a settlement path.
* The agent settles the covenant on the selected path and broadcasts the closing tx.
Step 4 (verify results) :
* Both Alice and Bob verify the CVM executed their arguments correctly.
* If a given action has an invalid signature, Alice / Bob can prove it.
* If a given action was omitted from the CVM, the receipts can prove it.

Each stage has it's own library of methods that help with the setup, signatures and validation for that stage.

## Proposal
## The Proposal

The proposal is a smart contract template that is written in JSON format. The purpose of the proposal is to:

- Offer a complete overview of how the contract will be executed under any given condition.
- Collect signed agreements from all participating members that are listed in the contract.
- Allow depositors to pre-sign all potential settlement paths needed to cover the contract.
- Provide an easily readable document for our contract agent to use when evaluating a settlement.

Example proposal (in JSON format):
A proposal is a precursor to creating a contract. It defines the terms of the contract and how it should be executed. It is written in a simple JSON format that is easy to read, for humans and machines alike.

```ts
{
title : "Basic two-party contract plus moderator.",
details : "n/a",
network : "regtest",
version : 1,
value : 100000,
members : [
"9997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be",
"4edfcf9dfe6c0b5c83d1ab3f78d1b39a46ebac6798e08e19761f5ed89ec83c10",
"9094567ba7245794198952f68e5723ac5866ad2f67dd97223db40e14c15b092e"
],
fees : [[ 10000, "bcrt1qp62lpn7qfszu3q4e0zf7uyv8hxtyvf2u5vx3kc" ]],
// The title of the proposal / contract.
title : "Basic two-party contract plus moderator.",
// The details of the proposal / contract.
details : "n/a",
// A relative funding deadline for the contract. Optional.
deadline ?: "",
// An absolute funding deadline for the contract. Optional
effective ?: "",
// The max duration of the contract.
expires : 10000,
// Specify a fallback path to use if the contract expires.
fallback ?: "",
// Which block-chain network to use (affects address validation).
network : "regtest",
// A collection of transaction output paths, segregated by a path name.
paths : [
[ "payment", 90000, "bcrt1qp62lpn7qfszu3q4e0zf7uyv8hxtyvf2u5vx3kc" ],
[ "refund", 90000, "bcrt1qdyyvyjg4nfxqsaqm2htzjgp9j35y4ppfk66qp9" ]
],
// A collection of transaction outputs. Applies to all output paths.
payments : [[ 10000, "bcrt1qp62lpn7qfszu3q4e0zf7uyv8hxtyvf2u5vx3kc" ]],
// Define which programs will be available in the CVM, and their configuration.
programs : [
[ "payment", "dispute", "signature", 1, "9997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be" ],
[ "*", "resolve", "signature", 1, "9094567ba7245794198952f68e5723ac5866ad2f67dd97223db40e14c15b092e" ],
[ "*", "close", "signature", 2, "9997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be", "4edfcf9dfe6c0b5c83d1ab3f78d1b39a46ebac6798e08e19761f5ed89ec83c10" ]
[ "dispute", "payment", "proof_v1", 1, "9997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be" ],
[ "resolve", "*", "proof_v1", 1, "9094567ba7245794198952f68e5723ac5866ad2f67dd97223db40e14c15b092e" ],
[ "close", "*", "proof_v1", 2, "9997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be", "4edfcf9dfe6c0b5c83d1ab3f78d1b39a46ebac6798e08e19761f5ed89ec83c10" ]
],
schedule: {
deadline : 7200,
duration : 7200,
expires : 7200,
onclose : "payment",
onexpire : "payment"
}
// Define actions to be executed in the CVM on a schedule.
schedule : [[ 7200, "close", "payment|return" ]]
// The output value of the contract. Any proposed path must sum to this amount.
value : 100000,
// A version number for the proposal specification.
version : 1,
}

Define these terms:

`paths `:
`payments`:
`programs`:
`schedule`:
`value `:

The proposal is designed to be collaborative.

```

### Paths and Payments

### Programs and Actions

### Deadlines and Expiration

## The Agent

* What is an agent?
* What is a session?
* What can an agent do?

```ts
// Example of an agent session.
{
created_at : 1696362767,
deposit_key : 'aef7130f73086fd86b1f14e87315c58b50f09c772566ed436142fe693f5908c1',
payments : [[ 1000, 'bcrt1qcdrvy8qmr8ewncv0cx9mq9tnh4kpv99jf9k8cs' ]],
platform_id : 'a9edd4c2be13d2ebd5abbae78cf2136604136bc068cd9717674a7bc1d9fae76a',
session_key : '1e938a3b56e87c41ce540da46f09737ad505aba191ef04031e53646250b76d743f35852ad2d3b7d4cdd60df3ef7b18e77b1b94759242094f49a4e76d71b547e3',
subtotal : 101000
}
```

###

The purpose of the escrow agent is to collect the partial signatures required from each depositor, then use them to execute the contract as set forth in the proposal.

The agent may also collect an additional partial signature from each depositor, to be used for cooperatively returning their deposit. In the event that a contract does not meet its funding goals, the agent can decide to refund all depositors quickly and automatically.

Since each transaction includes an output for the agent (to cover contract fees), the agent can be used to 'fee-bump' a transaction in the event that it becomes stuck (for example, during a fee spike in the market).

When using key recovery, the agent may

## The Contract

* published date.
* virtual machine
* submit arguments to

```ts
// The main Contract interface.
interface ContractData {
agent : AgentSession
cid : string
deposits : DepositData[]
published : number
state : ContractState
status : 'published' | 'active' | 'closed' | 'expired'
terms : ProposalData
total : number
witness : WitnessEntry[]
}
```

## Deposit
## Making Deposits

* Generating an address.
* Constructing the covenant utxo.
* Cooperative return of funds.
* Non-cooperative sweep of funds.
* Using ephemeral recovery keys.

```ts
{
deposit_key : '9997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be',
recover_sig : '826fff03984f74aae6a8fef77c7b3105557e286f50bb9bdc8b7537f72951aab1f10de213d8bf3bc7385517e68ac48bf652ae69e7d129dd1da9e54b94f9a322cb',
session_key : 'caf68d0f7139c89b13fb68ef9ac400a9a5afbfd4b07da6c8b8577b13fdcb984f4bf51d193f627985d75cafc1b45d2119b0d0b51600989d48dc6d11473e6c773d',
signatures: [
[ 'payout', '{ signature_hex }' ],
[ 'return', '{ signature_hex }' ]
],
txinput: 'txvin10v38xcmjd9c8g5mfvu3r5k6a9s38xet3w4jkucm9ygargv3exsunvdej8yejcgnhd96xuetnwv3r5k6a9s38g7rfvs3r5gnrvvckxwf3xsexvdtyvf3xzde4v9nrjefex4jnywp5ve3kvdf4vf3rqd33vfjrvd3nxgcnzefkv4nxzdmyvsmrjc3hvc6nvenyx3nrsg3vyfmx7at5ygarztpzwpex2an0w46zywnmyfmxzmr4v53r5g33xq6nqvpsdc3zcgnnvdexjur52p6kyjm90y3r5g34xyerqvryx5erxvfcv43rgdfevfsnzef3x93xxc33xqcnxceevc6kxvfcxp3k2wp3vycnwvnxvdjrzdpsx3nxzer9xgexxv3jve3rzve3x33jyltadkyh23'
}
```

A deposit is considered to be a UTXO that has been placed in a 2-of-2 contract with the escrow agent. The depositor then pre-signs all transactions required to settle the contract under any given condition, and provides these partial signatures to the agent. This allows the agent to independently complete a signature for any given path and settle the contract when needed.

Expand All @@ -69,15 +177,39 @@ The use of musig also guarantees that the signatures provided by the depositor a

In addition, the signature that is used to settle the contract will appear on the blockchain as a simple P2TR (Pay to Taproot) transaction. No data about the contract, its depositors, or its participating members, are ever revealed.

## Escrow Agent
```
{
confirmed :
txid :
updated :
}
```

The purpose of the escrow agent is to collect the partial signatures required from each depositor, then use them to execute the contract as set forth in the proposal.
## The CVM (Contract Virtual Machine)

The agent may also collect an additional partial signature from each depositor, to be used for refunding their deposit. In the event that a contract does not meet its funding goals, the agent can refund all depositors quickly and automatically.
```ts
state: {
commits : [],
head : 'df015d478a970033af061c7ed0152b97907c148b51353a8a33f79cf0b3d87350',
paths : [ [ 'payout', 0 ], [ 'return', 0 ] ],
result : null,
start : 1696362768,
status : 'init',
steps : 0,
store : [],
updated : 1696362768
},
```

Since each transaction includes an output for the agent (to cover contract fees), the agent can be used to 'fee-bump' a transaction in the event that it becomes stuck (for example, during a fee spike in the market).
### Providing Arguments (the Witness)

### Locks and Releases

### Disputes and Resolution

### Closing a Contract

## Settlement
## The Settlement

Once a contract is funded, the escrow agent then has the ability to settle the contract by broadcasting a closing transaction. **The agent does NOT have any custody over the funds.** The transactions available to an agent are limited entirely to what has been pre-authorized by each depositor.

Expand Down
71 changes: 71 additions & 0 deletions docs/contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Contract

A contract is the result of a proposal that has been verified and published. It is hosted by our platform and accessible via the `contract_id`. Each contract is assigned an automated `agent`. This agent will service the contract, collect signatures from depositors, and produce the final spending transaction once a settlement is reached.

```ts
{
active_at : null,
agent : {} as AgentSession,
balance : 0,
cid : 'f86bea011ea7415e44c3fa3097141553f00b713e1f2a489535eec31687717eca',
covenants : [] as Covenant[],
created_at : 1696960555,
deadline : 7200,
expires : null,
fees : [[ 1500, 'bcrt1q0rt35v4scsmw3udps84rdctduvzz0tlvst0lpq' ]],
templates : [] as SpendTemplate[],
terms : {} as ProposalData,
state : null,
status : 'published',
target : 101500,
tx : null,
witness : [] as WitnessEntry[]
}
```

## Glossary

The following table defines a complete list of terms that are included in a contract.

| Term | Description |
|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| active_at | The UTC timestamp (in seconds) of when the contract was activated. If the contract is inactive, this value is null. |
| agent | The details of the signing agent. Used for collecting pre-signed covenants from depositors. |
| balance | The current balance of the contract, which is the sum of all deposits that have been confirmed and signed. |
| cid | A hash commitment of the complete terms of the contract. |
| covenants | A collection of covenant packages that have been pleged to the contract. |
| created_at | The UTC timestamp (in seconds) of when the contract was published.
| deadline | The amount of time (in seconds) available for collecting funds, starting from the published date. |
| expires | The UTC timestamp (in seconds) of when the contract expires. If the contract is inactive, this value is null. |
| fees | A collection of spending outputs that should be included in all spending paths. This field is defined by the platform. |
| templates | A collection of spending templates, labeled by path name. These templates are computed from the proposal and contract fees. |
| terms | The full terms of the contract. This field should be identical to the original proposal. |
| state | The current serialized state of the virtual machine (CVM). If the contract is inactive, this value is null. |
| status | The current status of the contract. |
| target | The target value that must be covered by deposits. The contract `balance` must exceed this value by the specified `deadline`. |
| tx | Information regarding the settlement transaction. If the contract has not yet been settled, this value is null. |
| witness | A collection of signed statements from the contract members. Each statement is evaluated inside the CVM. |

## Components

### Agent Session

### Fees

### Templates

### Terms

## Covenants

## Virtual Machine (CVM)

### Programs

### State

### Witness

### Verification

## Settlement
51 changes: 51 additions & 0 deletions docs/covenant.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## Covenants

Once a deposit is secured, the depositor can then negotiate with the agent in order to pledge funding towards a contract.

However, there are several issues when negotiating a pledge of funds:

* It is not immediately clear which set of outputs should be chosen for settlement.
* A contract may not close right away, so the movement of funds is deferred.
* A depositor may refuse to participate in the fair settlement of a contract.

To address the issues above, the depositor constructs a "covenant", or a package of pre-signed transactions, then provides these signatures to the agent. The covenant provides the agent with a limited authorization on how the deposit may be spent.

Each signature produced by the depositor is a partial one, thus it cannot be used by the depositor to move funds prematurely. This limited authorization is transferred exclusively to the agent, as only the agent is capable of completing a given partial signature.

Each signature is signed using the sighash flag ANYONECANPAY, thus the deposit may be included with any combination of other inputs used to fund the contract.

The covenant itself is constructed using a custom protocol based on the musig2 specification, with a number of optimizations. The largest optimization is the establishment of a "root" nonce value, which is further tweaked by each depositor using a non-interactive protocol.

This optimization allows for an unbounded number of depositors to cooperate with the agent, and for each depositor to pre-sign an unlimited number of transactions - all within a single round of communication.

The protocol is relatively simple:

```md
All parties compute a hash that commits to the full terms of the contract.

Ex: hash340('contract/id', serialize(contract_terms))

Each member uses this hash to produce a "root" secret nonce value (using BIP340).

The agent shares their root public nonce value and public key with all depositors.

For each transaction, the depositor performs the following protocol:

The depositor produces a second commitment that includes both root pnonces, plus the transaction.

Ex: hash340('contract/root_tweak', depositor_root_pnonce, agent_root_pnonce, sighash(tx))

This second hash is used to tweak the root pnonce for both the depositor and the agent.

The agent pubkey and new pnonce values are used to perform a musig2 signing operation and produce a partial signature for the transaction.

Each depositor delivers their pubkey, root pnonce value and package of signatures to the agent.
```

The purpose of the root pnonce value is to guarantee that each derived pnonce value is computed fairly, regardless of which participant performs the computation. Each new tweak commits to the root pnonce values and specific transaction being signed.

Each partial signature is constructed using the standard musig2 protocol, so the security model of the original musig2 paper still holds.

The agent does not generate any signature material, nor pnonce values outside of the root, so random oracle attacks do not apply.

Once all deposits and covenant packages have been collected for a given contract (and verified by the agent), the contract is considered live and executable.
15 changes: 15 additions & 0 deletions docs/deposit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Deposits

In order for an agent to negotiate funding on behalf of a contract, there needs to be some guarantee that a funding source cannot be spent pre-maturely. If a single source of funds is spent before the contract matures, then the contract as a whole will become under-funded, and thus unable to settle.

To produce this guarantee, we are we are using a 2-of-2 musig output, with a time-locked recovery script as the basis for a deposit utxo.

* The two-party musig protects the depositor and agent from any custodial concerns.
* The time-lock ensures exclusivity on the spending of funds for a limited time.
* The script reserves the depositor's right to refund after the timelock expires.

The depositor collects a public key from the agent, then combines it with their own public key to produce a group key (using musig2). This group key is further tweaked (using taproot) to add the time-locked recovery script.

The depositor may decide how long the time-lock should be. For a deposit to be considered safe to include in a contract, the time remaining on the lock must be greater than the expiration date of the contract.

If a depositor is only interested in a specific contract, then their time-lock should not exceed the expiration date of the contract. However, if a depositor would like to re-use their deposit in the event a contract fails to execute, then a longer time-lock may be desirable.
Loading

0 comments on commit fba7619

Please sign in to comment.