├── fuzz ├── src │ ├── lib.rs │ └── bin │ │ ├── gen_msgs_fuzz.sh │ │ ├── msg_offer.rs │ │ ├── msg_template.txt │ │ ├── offerdlc_fuzz.rs │ │ ├── signdlc_fuzz.rs │ │ └── acceptdlc_fuzz.rs ├── Cargo.toml ├── .gitignore ├── Dockerfile └── Readme.md ├── rustfmt.toml ├── testconfig ├── pass │ └── pass.txt ├── oracle │ ├── certs │ │ ├── oracle │ │ │ ├── pass.txt │ │ │ └── key.pem │ │ └── db │ │ │ ├── db.key │ │ │ └── db.crt │ ├── oracledb.dockerfile │ └── integration.yaml └── config │ └── default.conf ├── .dockerignore ├── sample ├── .gitignore ├── examples │ ├── configurations │ │ ├── bob.yml │ │ └── alice.yml │ └── contracts │ │ └── numerical_contract_input.json ├── Cargo.toml ├── src │ ├── disk.rs │ └── hex_utils.rs ├── tests │ └── cli_tests.rs └── Readme.md ├── dlc-manager ├── test_inputs │ ├── Accepted │ ├── offer_channel.json │ ├── offer_contract.json │ ├── offer_enum_missing_payout.json │ ├── offer_numerical_empty_rounding_interval.json │ ├── offer_numerical_non_continuous.json │ ├── offer_numerical_bad_first_payout.json │ ├── offer_numerical_bad_last_payout.json │ ├── offer_numerical_collateral_less_than_payout.json │ ├── offer_numerical_invalid_rounding_interval.json │ ├── offer_enum_oracle_with_diff_payout.json │ └── offer_enum_collateral_not_equal_payout.json ├── Readme.md ├── benches │ └── Readme.md ├── src │ ├── contract │ │ ├── signed_contract.rs │ │ ├── utils.rs │ │ └── accepted_contract.rs │ ├── channel │ │ ├── accepted_channel.rs │ │ ├── utils.rs │ │ ├── party_points.rs │ │ ├── offered_channel.rs │ │ └── ser.rs │ └── error.rs ├── Cargo.toml └── CHANGELOG.md ├── simple-wallet ├── Readme.md └── Cargo.toml ├── mocks ├── Readme.md ├── src │ ├── lib.rs │ ├── mock_time.rs │ ├── mock_blockchain.rs │ ├── mock_wallet.rs │ └── mock_oracle_provider.rs └── Cargo.toml ├── dlc-messages ├── Readme.md ├── CHANGELOG.md ├── Cargo.toml └── src │ ├── serde_utils.rs │ ├── test_inputs │ └── dlc_fee_test_scripts.json │ └── segmentation │ └── mod.rs ├── dlc-sled-storage-provider ├── test_files │ ├── Closed │ ├── Offered │ ├── Signed │ ├── Signed1 │ ├── Accepted │ ├── Confirmed │ ├── Confirmed1 │ ├── PreClosed │ ├── AcceptedChannel │ ├── OfferedChannel │ ├── SignedChannelSettled │ └── SignedChannelEstablished ├── Readme.md ├── CHANGELOG.md └── Cargo.toml ├── scripts ├── wait_for_container.sh ├── generate_test_list.sh ├── stop_node.sh ├── run_loop.sh ├── generate_blocks.sh ├── generate_test_coverage.sh ├── get_test_list.sh ├── wait_for_electrs.sh ├── start_node.sh ├── run_integration_tests.sh ├── check_balances.sh ├── load_wallets.sh ├── generate_test_vectors.sh ├── create_wallets.sh ├── gen-sample-offer.sh ├── generate_integration_test_coverage.sh └── generate_serialized_contract_files.sh ├── dlc-trie ├── Readme.md ├── src │ ├── test_utils.rs │ └── combination_iterator.rs ├── Cargo.toml └── CHANGELOG.md ├── .clippy.toml ├── bitcoin-test-utils ├── Readme.md ├── Cargo.toml └── src │ ├── lib.rs │ └── rpc_helpers.rs ├── dlc ├── Readme.md ├── benches │ └── Readme.md ├── Cargo.toml ├── CHANGELOG.md └── src │ └── secp_utils.rs ├── p2pd-oracle-client ├── Readme.md ├── CHANGELOG.md └── Cargo.toml ├── bitcoin-rpc-provider ├── Readme.md └── Cargo.toml ├── Cargo.toml ├── .gitignore ├── docs ├── Contributing.md └── Development.md ├── electrs-blockchain-provider └── Cargo.toml ├── LICENSE ├── docker-compose.yml ├── README.md └── .github └── workflows └── rust.yml /fuzz/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | max_width = 100 3 | -------------------------------------------------------------------------------- /testconfig/pass/pass.txt: -------------------------------------------------------------------------------- 1 | lq6zequb-gYTdF2_ZEUtr8ywTXzLYtknzWU4nV8uVoo= 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | target 2 | Dockerfile 3 | .dockerignore 4 | .git 5 | .gitignore -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | dlc_sample* 2 | examples/contracts/sample_contract.json 3 | -------------------------------------------------------------------------------- /testconfig/oracle/certs/oracle/pass.txt: -------------------------------------------------------------------------------- 1 | Qj6Yb+GMC/MTRBO02Qal+sTDQRlvu2EfseHiDy9dANo= 2 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/Accepted: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-manager/test_inputs/Accepted -------------------------------------------------------------------------------- /simple-wallet/Readme.md: -------------------------------------------------------------------------------- 1 | # Simple Wallet 2 | 3 | A very simple wallet implementation mainly used for testing purposes. 4 | -------------------------------------------------------------------------------- /mocks/Readme.md: -------------------------------------------------------------------------------- 1 | # Mocks 2 | 3 | Mock implementations of some traits required by the [dlc-manager](../dlc-manager) used for testing purposes. -------------------------------------------------------------------------------- /dlc-messages/Readme.md: -------------------------------------------------------------------------------- 1 | # DLC Messages 2 | 3 | This crate provide the representation of DLC messages and functions to enable their serialization. -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/Closed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/Closed -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/Offered: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/Offered -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/Signed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/Signed -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/Signed1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/Signed1 -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/Accepted: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/Accepted -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/Confirmed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/Confirmed -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/Confirmed1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/Confirmed1 -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/PreClosed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/PreClosed -------------------------------------------------------------------------------- /scripts/wait_for_container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while [ "`docker inspect -f {{.State.Status}} $1`" != "running" ]; do 4 | sleep 2; 5 | done 6 | -------------------------------------------------------------------------------- /dlc-trie/Readme.md: -------------------------------------------------------------------------------- 1 | # DLC Trie 2 | 3 | This crate provide data structures for facilitating the storage and retrieval of contract information for numerical DLC. -------------------------------------------------------------------------------- /.clippy.toml: -------------------------------------------------------------------------------- 1 | # Fixing these would break API, allow for now 2 | enum-variant-size-threshold = 2000 3 | msrv = "1.43.0" 4 | too-many-arguments-threshold = 15 5 | -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/AcceptedChannel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/AcceptedChannel -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/OfferedChannel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/OfferedChannel -------------------------------------------------------------------------------- /scripts/generate_test_list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo $(echo $($(ls ./target/debug/deps/$1* | grep -v '\.d\|\.o') --list --format=terse | sed 's/\: test$//')) -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/SignedChannelSettled: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/SignedChannelSettled -------------------------------------------------------------------------------- /dlc-sled-storage-provider/test_files/SignedChannelEstablished: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p2pderivatives/rust-dlc/HEAD/dlc-sled-storage-provider/test_files/SignedChannelEstablished -------------------------------------------------------------------------------- /scripts/stop_node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if command -v docker &> /dev/null 4 | then 5 | CMD=docker 6 | else 7 | CMD=podman 8 | fi 9 | 10 | $CMD stop bitcoin-node 11 | -------------------------------------------------------------------------------- /bitcoin-test-utils/Readme.md: -------------------------------------------------------------------------------- 1 | # Bitcoin test utils 2 | 3 | Utility functions used throughout the tests providing: 4 | * Hex conversion utils, 5 | * Convenience functions for working with the Bitcoin core RPC. 6 | -------------------------------------------------------------------------------- /dlc-sled-storage-provider/Readme.md: -------------------------------------------------------------------------------- 1 | # Sled storage provider 2 | 3 | Implementation of the storage trait required by the [dlc-manager](../dlc-manager) using the [Sled](https://github.com/spacejam/sled) embedded data base. -------------------------------------------------------------------------------- /mocks/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod memory_storage_provider; 2 | pub mod mock_blockchain; 3 | pub mod mock_oracle_provider; 4 | pub mod mock_time; 5 | pub mod mock_wallet; 6 | 7 | pub use dlc_manager; 8 | pub use simple_wallet; 9 | -------------------------------------------------------------------------------- /fuzz/src/bin/gen_msgs_fuzz.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | MSGS=(OfferDlc AcceptDlc SignDlc) 4 | 5 | for msg in ${MSGS[@]}; do 6 | cat ./msg_template.txt | sed s/MSG_NAME/$msg/g > $(echo $msg | tr '[:upper:]' '[:lower:]')_fuzz.rs 7 | done 8 | -------------------------------------------------------------------------------- /dlc/Readme.md: -------------------------------------------------------------------------------- 1 | # DLC 2 | 3 | This crate provides base functionality for creation, signing and verification of transactions used in a DLC. 4 | 5 | See [the development docs](../docs/Development.md) for information about running integration tests. 6 | -------------------------------------------------------------------------------- /p2pd-oracle-client/Readme.md: -------------------------------------------------------------------------------- 1 | # P2P Derivatives Oracle Client 2 | 3 | Implementation of the `Oracle` trait from the [dlc-manager](../dlc-manager) providing interactions with instances of the [P2PDerivatives oracle](https://github.com/p2pderivatives/p2pderivatives-oracle). 4 | -------------------------------------------------------------------------------- /scripts/run_loop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | for i in {0..100} 6 | do 7 | echo "Start" 8 | ./scripts/start_node.sh 9 | RUST_LOG=info cargo test -- --ignored three_of_three_oracle_numerical_with_diff_test 10 | ./scripts/stop_node.sh 11 | done 12 | -------------------------------------------------------------------------------- /testconfig/config/default.conf: -------------------------------------------------------------------------------- 1 | deprecatedrpc=create_bdb 2 | 3 | [regtest] 4 | rpcallowip=0.0.0.0/0 5 | rpcbind=0.0.0.0 6 | rpcauth=testuser:ea8070e0acccb49670309dd6c7812e16$2a3487173f9f6b603d43a70e6ccb0aa671a16dbee1cf86b098e77532d2515370 7 | addresstype=bech32 8 | fallbackfee=0.0002 9 | -------------------------------------------------------------------------------- /testconfig/oracle/oracledb.dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:16.2 2 | 3 | RUN mkdir certs 4 | COPY ./testconfig/oracle/certs/db/db.crt /certs/ 5 | COPY ./testconfig/oracle/certs/db/db.key /certs/ 6 | RUN chmod 600 /certs/db.key 7 | RUN chown postgres /certs/db.key 8 | 9 | CMD ["postgres"] 10 | -------------------------------------------------------------------------------- /bitcoin-rpc-provider/Readme.md: -------------------------------------------------------------------------------- 1 | # Bitcoin RPC Provider 2 | 3 | Implementation of the `Wallet` and `Blockchain` traits from the [dlc-manager](../dlc-manager) using the bitcoind RPC. 4 | It is mainly a wrapper around the [rust-bitcoincore-rpc](https://github.com/rust-bitcoin/rust-bitcoincore-rpc) crate. 5 | -------------------------------------------------------------------------------- /bitcoin-test-utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Crypto Garage"] 3 | name = "bitcoin-test-utils" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | bitcoin = { version = "0.32.2", default-features = false } 8 | bitcoincore-rpc = {version = "0.19"} 9 | bitcoincore-rpc-json = {version = "0.19"} 10 | -------------------------------------------------------------------------------- /p2pd-oracle-client/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] -------------------------------------------------------------------------------- /dlc-sled-storage-provider/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "dlc-fuzz" 4 | publish = false 5 | version = "0.1.0" 6 | 7 | [package.metadata] 8 | cargo-fuzz = true 9 | 10 | [dependencies] 11 | dlc-messages = {path = "../dlc-messages"} 12 | honggfuzz = "0.5" 13 | lightning = {version = "0.0.125" } 14 | 15 | [workspace] 16 | members = ["."] 17 | -------------------------------------------------------------------------------- /scripts/generate_blocks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | bitcoincli=$(command -v bitcoin-cli) 4 | opts=( -rpcuser="testuser" -rpcpassword="lq6zequb-gYTdF2_ZEUtr8ywTXzLYtknzWU4nV8uVoo=" -regtest ) 5 | 6 | newaddress=$($bitcoincli "${opts[@]}" -rpcwallet=alice getnewaddress bec32) 7 | $bitcoincli "${opts[@]}" generatetoaddress 10 ${newaddress} &> /dev/null 8 | -------------------------------------------------------------------------------- /scripts/generate_test_coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export RUSTFLAGS="-Cinstrument-coverage" 6 | export LLVM_PROFILE_FILE="rust-dlc-%p-%m.profraw" 7 | 8 | cargo build 9 | 10 | cargo test 11 | 12 | grcov . --binary-path ./target/debug/deps/ -s . -t html --branch --ignore-not-existing -o ./coverage/ 13 | rm **/rust-dlc-* 14 | -------------------------------------------------------------------------------- /scripts/get_test_list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for TEST_PREFIX in "$@"; do 4 | TEST_BIN=$(ls ./target/debug/deps/${TEST_PREFIX}* | grep -v '\.d\|\.o') 5 | LIST=$(${TEST_BIN} --list --format=terse | sed 's/\: test$/,/' | sed 's@[^[:space:],]\+@"'${TEST_BIN}' &"@g') 6 | RES+=(${LIST}) 7 | done 8 | 9 | echo $(echo [${RES[@]}] | sed 's/,]/]/') 10 | 11 | -------------------------------------------------------------------------------- /sample/examples/configurations/bob.yml: -------------------------------------------------------------------------------- 1 | bitcoinInfo: 2 | rpcUsername: testuser 3 | rpcPassword: lq6zequb-gYTdF2_ZEUtr8ywTXzLYtknzWU4nV8uVoo= 4 | rpcPort: 18443 5 | rpcHost: localhost 6 | wallet: bob 7 | storageDirPath: './dlc_sample_bob' 8 | networkConfiguration: 9 | peerListeningPort: 9001 10 | oracleConfig: 11 | host: 'http://localhost:8080/' 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "bitcoin-test-utils", 5 | "bitcoin-rpc-provider", 6 | "p2pd-oracle-client", 7 | "dlc", 8 | "dlc-messages", 9 | "dlc-trie", 10 | "dlc-manager", 11 | "mocks", 12 | "sample", 13 | "simple-wallet", 14 | "dlc-sled-storage-provider", 15 | "electrs-blockchain-provider", 16 | ] 17 | 18 | resolver = "2" 19 | -------------------------------------------------------------------------------- /sample/examples/configurations/alice.yml: -------------------------------------------------------------------------------- 1 | bitcoinInfo: 2 | rpcUsername: testuser 3 | rpcPassword: lq6zequb-gYTdF2_ZEUtr8ywTXzLYtknzWU4nV8uVoo= 4 | rpcPort: 18443 5 | rpcHost: localhost 6 | wallet: alice 7 | storageDirPath: './dlc_sample_alice' 8 | networkConfiguration: 9 | peerListeningPort: 9000 10 | oracleConfig: 11 | host: 'http://localhost:8080/' 12 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /testconfig/oracle/certs/oracle/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: AES-256-CBC,4FB3A4FF77E83E33ADAE25754D937359 4 | 5 | JPp+2MkwFeHGH1rAntW3nQaFmur5vjpRdpQNKzHqMyOTzYunnCAAVeP1QqQJmcjz 6 | V/fkakJwOC0hCPxavgCYNFPwlhIpXEwopYaqAZ1ULZyzTrgbdLSv6ozYI8YrTLt8 7 | QSN2N1bzyW1y3NOPpT7/yP3wfhVkpPY+sJTwdJ6P6SE= 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /dlc-manager/Readme.md: -------------------------------------------------------------------------------- 1 | # DLC Manager 2 | 3 | This crate provides a manager structure that can be used to create and process DLC. 4 | The manager requires a number of traits which have basic implementation within this repository but that can be customized to fit specific needs. 5 | 6 | See [the development docs](../docs/Development.md) for information about running integration tests. 7 | -------------------------------------------------------------------------------- /scripts/wait_for_electrs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | until $(curl --output /dev/null --silent --fail http://localhost:3004/blocks/tip/height); do 4 | printf 'waiting for electrs to start' 5 | curl --user testuser:lq6zequb-gYTdF2_ZEUtr8ywTXzLYtknzWU4nV8uVoo= --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "generatetoaddress", "params": [1, "bcrt1qzqaernqlwetvahu59fwt3p38ezww7w95jqlxkg"]}' -H 'content-type: text/plain;' http://127.0.0.1:18443/ 6 | sleep 5 7 | done -------------------------------------------------------------------------------- /mocks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Crypto Garage"] 3 | edition = "2018" 4 | name = "mocks" 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | bitcoin = "0.32.2" 9 | dlc = {path = "../dlc"} 10 | dlc-manager = {path = "../dlc-manager"} 11 | dlc-messages = {path = "../dlc-messages"} 12 | lightning = {version = "0.0.125"} 13 | secp256k1-zkp = {version = "0.11.0", features = ["hashes", "global-context", "rand", "rand-std"]} 14 | simple-wallet = {path = "../simple-wallet"} 15 | -------------------------------------------------------------------------------- /fuzz/Dockerfile: -------------------------------------------------------------------------------- 1 | from rust as fuzzbase 2 | 3 | RUN apt-get update 4 | RUN apt-get update && apt-get -y install build-essential\ 5 | binutils-dev libunwind-dev libblocksruntime-dev liblzma-dev 6 | ENV WORKSPACE /fuzzing 7 | WORKDIR ${WORKSPACE} 8 | ADD . ${WORKSPACE}/ 9 | RUN cargo update 10 | RUN cargo install --force honggfuzz 11 | WORKDIR ${WORKSPACE}/fuzz 12 | RUN cargo hfuzz build 13 | ENV HFUZZ_RUN_ARGS='--exit_upon_crash' 14 | ENTRYPOINT ["cargo", "hfuzz", "run"] 15 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # Test coverage report files 13 | *.profraw 14 | **/*.profraw 15 | /coverage/ 16 | /integration_coverage/ 17 | .idea 18 | -------------------------------------------------------------------------------- /mocks/src/mock_time.rs: -------------------------------------------------------------------------------- 1 | extern crate dlc_manager; 2 | 3 | use dlc_manager::Time; 4 | use std::cell::RefCell; 5 | 6 | thread_local! { 7 | static MOCK_TIME: RefCell = RefCell::new(0); 8 | } 9 | 10 | pub struct MockTime {} 11 | 12 | impl Time for MockTime { 13 | fn unix_time_now(&self) -> u64 { 14 | MOCK_TIME.with(|f| *f.borrow()) 15 | } 16 | } 17 | 18 | pub fn set_time(time: u64) { 19 | MOCK_TIME.with(|f| { 20 | *f.borrow_mut() = time; 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /scripts/start_node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if command -v docker &> /dev/null 4 | then 5 | CMD=docker 6 | else 7 | CMD=podman 8 | fi 9 | 10 | : "${BITCOINVERSION:=0.20.0}" 11 | $CMD run --rm -d -p 18443:18443 --name bitcoin-node ruimarinho/bitcoin-core:$BITCOINVERSION \ 12 | -regtest=1 \ 13 | -rpcallowip=0.0.0/0 \ 14 | -rpcbind=0.0.0.0 \ 15 | -rpcauth='testuser:ea8070e0acccb49670309dd6c7812e16$2a3487173f9f6b603d43a70e6ccb0aa671a16dbee1cf86b098e77532d2515370' \ 16 | -addresstype=bech32 17 | -------------------------------------------------------------------------------- /docs/Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are very welcome. 4 | As much as possible try to follow these simple rules: 5 | 6 | - Separate PRs in logical commits, this makes it easier to bisect in case of bugs, and also helps with code review. 7 | - Avoid long PRs, better to make multiple small ones. 8 | - Open an issue before starting to work on big changes, so you can get feedbacks before doing too much work. 9 | 10 | Read the [development document](./Development.md) for some help in getting started. 11 | -------------------------------------------------------------------------------- /dlc-manager/benches/Readme.md: -------------------------------------------------------------------------------- 1 | # Dlc-manager Benchmarks 2 | 3 | This folder contains benchmarks to measure the signing and verification of adaptor signatures for numerical outcome contracts. 4 | The `const` parameters at the beginning of the file can be changed to try out different settings. 5 | See code comments for details on the parameters. 6 | 7 | ## Running 8 | 9 | To run the benchmarks: `cargo bench`. 10 | To run the benchmarks using parallelization of anticipation points computation: `cargo bench --features=parallel`. 11 | -------------------------------------------------------------------------------- /fuzz/src/bin/msg_offer.rs: -------------------------------------------------------------------------------- 1 | use dlc_messages::*; 2 | use honggfuzz::fuzz; 3 | 4 | fn main() { 5 | fuzz!(|data| { 6 | use lightning::util::ser::{Readable, Writeable}; 7 | let mut buf = ::std::io::Cursor::new(data); 8 | if let Ok(msg) = ::read(&mut buf) { 9 | let p = buf.position() as usize; 10 | let mut writer = Vec::new(); 11 | msg.write(&mut writer).unwrap(); 12 | assert_eq!(&buf.into_inner()[..p], &writer[..p]); 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /fuzz/src/bin/msg_template.txt: -------------------------------------------------------------------------------- 1 | use dlc_messages::*; 2 | use honggfuzz::fuzz; 3 | 4 | fn main() { 5 | fuzz!(|data| { 6 | use lightning::util::ser::{Readable, Writeable}; 7 | let mut buf = ::std::io::Cursor::new(data); 8 | if let Ok(msg) = ::read(&mut buf) { 9 | let p = buf.position() as usize; 10 | let mut writer = Vec::new(); 11 | msg.write(&mut writer).unwrap(); 12 | assert_eq!(&buf.into_inner()[..p], &writer[..p]); 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /fuzz/src/bin/offerdlc_fuzz.rs: -------------------------------------------------------------------------------- 1 | use dlc_messages::*; 2 | use honggfuzz::fuzz; 3 | 4 | fn main() { 5 | fuzz!(|data| { 6 | use lightning::util::ser::{Readable, Writeable}; 7 | let mut buf = ::std::io::Cursor::new(data); 8 | if let Ok(msg) = ::read(&mut buf) { 9 | let p = buf.position() as usize; 10 | let mut writer = Vec::new(); 11 | msg.write(&mut writer).unwrap(); 12 | assert_eq!(&buf.into_inner()[..p], &writer[..p]); 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /fuzz/src/bin/signdlc_fuzz.rs: -------------------------------------------------------------------------------- 1 | use dlc_messages::*; 2 | use honggfuzz::fuzz; 3 | 4 | fn main() { 5 | fuzz!(|data| { 6 | use lightning::util::ser::{Readable, Writeable}; 7 | let mut buf = ::std::io::Cursor::new(data); 8 | if let Ok(msg) = ::read(&mut buf) { 9 | let p = buf.position() as usize; 10 | let mut writer = Vec::new(); 11 | msg.write(&mut writer).unwrap(); 12 | assert_eq!(&buf.into_inner()[..p], &writer[..p]); 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /scripts/run_integration_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if command -v docker &> /dev/null 6 | then 7 | CMD=docker 8 | else 9 | CMD=podman 10 | fi 11 | 12 | export TEST_BIN=$1 13 | LIST=$(bash ${PWD}/scripts/generate_test_list.sh $TEST_BIN) 14 | 15 | for TEST_NAME in $LIST 16 | do 17 | if [ ! -z $TEST_NAME ] 18 | then 19 | docker-compose up -d 20 | ./scripts/wait_for_electrs.sh 21 | cargo test -- $TEST_NAME --ignored --exact --nocapture 22 | docker-compose down -v 23 | fi 24 | done 25 | -------------------------------------------------------------------------------- /fuzz/src/bin/acceptdlc_fuzz.rs: -------------------------------------------------------------------------------- 1 | use dlc_messages::*; 2 | use honggfuzz::fuzz; 3 | 4 | fn main() { 5 | fuzz!(|data| { 6 | use lightning::util::ser::{Readable, Writeable}; 7 | let mut buf = ::std::io::Cursor::new(data); 8 | if let Ok(msg) = ::read(&mut buf) { 9 | let p = buf.position() as usize; 10 | let mut writer = Vec::new(); 11 | msg.write(&mut writer).unwrap(); 12 | assert_eq!(&buf.into_inner()[..p], &writer[..p]); 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /dlc/benches/Readme.md: -------------------------------------------------------------------------------- 1 | # DLC Benchmarks 2 | 3 | This folder contains benchmarks to evaluate the impact of optimizations on computation of adaptor signatures and aggregated anticipation points. 4 | The `const` parameters at the beginning of the file can be changed to try out different settings (note that the computation of all aggregated points without optimization can take a very long time for large number of digits and/or oracles). 5 | See code comments for details on each benchmark. 6 | 7 | ## Running 8 | 9 | To run the benchmarks: `cargo +nightly bench --features=unstable` 10 | -------------------------------------------------------------------------------- /scripts/check_balances.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Locate bitcoin-cli 4 | bitcoincli=$(command -v bitcoin-cli) 5 | 6 | # Define RPC options 7 | opts=( -rpcuser="testuser" -rpcpassword="lq6zequb-gYTdF2_ZEUtr8ywTXzLYtknzWU4nV8uVoo=" -regtest -named) 8 | 9 | # Check and print balance of alice wallet 10 | aliceBalance=$($bitcoincli "${opts[@]}" -rpcwallet=alice getbalance) 11 | echo "Alice's balance: ${aliceBalance} BTC" 12 | 13 | # Check and print balance of bob wallet 14 | bobBalance=$($bitcoincli "${opts[@]}" -rpcwallet=bob getbalance) 15 | echo "Bob's balance: ${bobBalance} BTC" -------------------------------------------------------------------------------- /simple-wallet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2018" 3 | name = "simple-wallet" 4 | version = "0.1.0" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | bdk_wallet = "=1.0.0-beta.5" 10 | bitcoin = "0.32.2" 11 | dlc = {path = "../dlc"} 12 | dlc-manager = {path = "../dlc-manager"} 13 | lightning = {version = "0.0.125"} 14 | secp256k1-zkp = {version = "0.11.0"} 15 | 16 | [dev-dependencies] 17 | mocks = {path = "../mocks"} 18 | secp256k1-zkp = {version = "0.11.0", features = ["global-context"]} 19 | -------------------------------------------------------------------------------- /scripts/load_wallets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | bitcoincli=$(command -v bitcoin-cli) 4 | opts=( -rpcuser="testuser" -rpcpassword="lq6zequb-gYTdF2_ZEUtr8ywTXzLYtknzWU4nV8uVoo=" -regtest ) 5 | 6 | $bitcoincli "${opts[@]}" loadwallet "alice" 7 | $bitcoincli "${opts[@]}" loadwallet "bob" 8 | 9 | aliceAddress=$($bitcoincli "${opts[@]}" -rpcwallet=alice getnewaddress bec32) 10 | $bitcoincli "${opts[@]}" generatetoaddress 101 ${aliceAddress} &> /dev/null 11 | bobAddress=$($bitcoincli "${opts[@]}" -rpcwallet=bob getnewaddress bec32) 12 | $bitcoincli "${opts[@]}" generatetoaddress 201 ${bobAddress} &> /dev/null 13 | -------------------------------------------------------------------------------- /scripts/generate_test_vectors.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SCRIPT_PATH=$(cd "$(dirname ${BASH_SOURCE[0]})"; pwd -P) 6 | echo $SCRIPT_PATH 7 | LIST=$(${SCRIPT_PATH}/generate_test_list.sh manager) 8 | echo $LIST 9 | OUTPUT="$(pwd)/test_vectors" 10 | mkdir -p $OUTPUT 11 | 12 | for TEST_NAME in $LIST 13 | do 14 | if [[ ! -z ${TEST_NAME} && ! ${TEST_NAME} =~ bad|refund ]] 15 | then 16 | ./scripts/start_node.sh 17 | GENERATE_TEST_VECTOR=1 TEST_VECTOR_OUTPUT_NAME="$OUTPUT/${TEST_NAME}.json" cargo test -- --ignored ${TEST_NAME} 18 | ./scripts/stop_node.sh 19 | fi 20 | done -------------------------------------------------------------------------------- /dlc-trie/src/test_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::OracleNumericInfo; 2 | 3 | pub(crate) fn same_num_digits_oracle_numeric_infos( 4 | nb_oracles: usize, 5 | nb_digits: usize, 6 | base: usize, 7 | ) -> OracleNumericInfo { 8 | OracleNumericInfo { 9 | nb_digits: std::iter::repeat(nb_digits).take(nb_oracles).collect(), 10 | base, 11 | } 12 | } 13 | 14 | pub(crate) fn get_variable_oracle_numeric_infos( 15 | nb_digits: &[usize], 16 | base: usize, 17 | ) -> OracleNumericInfo { 18 | OracleNumericInfo { 19 | base, 20 | nb_digits: nb_digits.to_vec(), 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /electrs-blockchain-provider/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "electrs-blockchain-provider" 4 | version = "0.1.0" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | bitcoin = {version = "0.32.2"} 10 | bitcoin-test-utils = {path = "../bitcoin-test-utils"} 11 | dlc-manager = {path = "../dlc-manager"} 12 | lightning = {version = "0.0.125"} 13 | lightning-block-sync = {version = "0.0.125"} 14 | reqwest = {version = "0.11", features = ["blocking", "json"]} 15 | serde = {version = "*", features = ["derive"]} 16 | simple-wallet = {path = "../simple-wallet"} 17 | tokio = "1" 18 | -------------------------------------------------------------------------------- /scripts/create_wallets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | bitcoincli=$(command -v bitcoin-cli) 4 | opts=( -rpcuser="testuser" -rpcpassword="lq6zequb-gYTdF2_ZEUtr8ywTXzLYtknzWU4nV8uVoo=" -regtest -named) 5 | 6 | $bitcoincli "${opts[@]}" createwallet wallet_name="alice" descriptors="false" 7 | $bitcoincli "${opts[@]}" createwallet wallet_name="bob" descriptors="false" 8 | 9 | aliceAddress=$($bitcoincli "${opts[@]}" -rpcwallet=alice getnewaddress bec32) 10 | $bitcoincli "${opts[@]}" generatetoaddress 101 ${aliceAddress} &> /dev/null 11 | bobAddress=$($bitcoincli "${opts[@]}" -rpcwallet=bob getnewaddress bec32) 12 | $bitcoincli "${opts[@]}" generatetoaddress 201 ${bobAddress} &> /dev/null 13 | -------------------------------------------------------------------------------- /scripts/gen-sample-offer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Calculate the Unix timestamp one hour from now 4 | DIR=$(git rev-parse --show-toplevel) 5 | 6 | current_timestamp=$(date +%s) 7 | future_timestamp=$((current_timestamp + 3600)) 8 | # Read the JSON file and update the eventId field 9 | updated_json=$(jq --arg new_timestamp "$future_timestamp" '.contractInfos[0].oracles.eventId |= sub("\\d+$"; $new_timestamp)' ${DIR}/sample/examples/contracts/numerical_contract_input.json) 10 | 11 | # Output the updated JSON 12 | rm -f ${DIR}/sample/examples/contracts/sample_contract.json 13 | echo "$updated_json" > ${DIR}/sample/examples/contracts/sample_contract.json 14 | 15 | echo "Updated numerical contract has been saved" -------------------------------------------------------------------------------- /bitcoin-rpc-provider/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Crypto Garage"] 3 | edition = '2018' 4 | name = "bitcoin-rpc-provider" 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | bitcoin = {version = "0.32.2"} 9 | bitcoincore-rpc = {version = "0.19.0"} 10 | bitcoincore-rpc-json = {version = "0.19.0"} 11 | dlc-manager = {path = "../dlc-manager"} 12 | hex = { package = "hex-conservative", version = "0.1" } 13 | lightning = { version = "0.0.125" } 14 | log = "0.4.14" 15 | rust-bitcoin-coin-selection = { version = "0.1.0", git = "https://github.com/p2pderivatives/rust-bitcoin-coin-selection", rev = "405451929568422f7df809e35d6ad8f36fccce90", features = ["rand"] } 16 | simple-wallet = {path = "../simple-wallet"} 17 | -------------------------------------------------------------------------------- /dlc-sled-storage-provider/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Crypto Garage"] 3 | description = "Sled backend for persisting Discreet Log Contracts (DLC)." 4 | edition = "2018" 5 | homepage = "https://github.com/p2pderivatives/rust-dlc" 6 | license-file = "../LICENSE" 7 | name = "dlc-sled-storage-provider" 8 | repository = "https://github.com/p2pderivatives/rust-dlc/tree/master/dlc-sled-storage-provider" 9 | version = "0.1.0" 10 | 11 | [features] 12 | wallet = ["bitcoin", "secp256k1-zkp", "simple-wallet"] 13 | 14 | [dependencies] 15 | bitcoin = {version = "0.32.2", optional = true} 16 | dlc-manager = {path = "../dlc-manager"} 17 | lightning = "0.0.125" 18 | secp256k1-zkp = {version = "0.11.0", optional = true} 19 | simple-wallet = {path = "../simple-wallet", optional = true} 20 | sled = "0.34" 21 | -------------------------------------------------------------------------------- /p2pd-oracle-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Crypto Garage"] 3 | description = "Oracle interface implementation for the p2pderivatives oracle." 4 | homepage = "https://github.com/p2pderivatives/rust-dlc" 5 | license-file = "../LICENSE" 6 | name = "p2pd-oracle-client" 7 | repository = "https://github.com/p2pderivatives/rust-dlc/tree/master/p2pd-oracle-client" 8 | version = "0.1.0" 9 | 10 | [dependencies] 11 | chrono = {version = "0.4.19", features = ["serde"]} 12 | dlc-manager = {path = "../dlc-manager"} 13 | dlc-messages = {path = "../dlc-messages", features = ["use-serde"]} 14 | reqwest = {version = "0.11", features = ["blocking", "json"]} 15 | secp256k1-zkp = {version = "0.11.0" } 16 | serde = {version = "*", features = ["derive"]} 17 | 18 | [dev-dependencies] 19 | mockito = "0.31.0" 20 | -------------------------------------------------------------------------------- /scripts/generate_integration_test_coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export RUSTFLAGS="-Cinstrument-coverage" 6 | export LLVM_PROFILE_FILE="rust-dlc-%p-%m.profraw" 7 | 8 | if command -v docker &> /dev/null 9 | then 10 | CMD=docker 11 | else 12 | CMD=podman 13 | fi 14 | 15 | export TEST_BIN=$1 16 | LIST=$(bash ${PWD}/scripts/generate_test_list.sh $TEST_BIN) 17 | 18 | echo $LIST 19 | 20 | for TEST_NAME in $LIST 21 | do 22 | if [ ! -z $TEST_NAME ] 23 | then 24 | export TEST_NAME=${TEST_NAME} 25 | bash ${PWD}/scripts/start_node.sh 26 | cargo test -- $TEST_NAME --ignored --exact --nocapture 27 | bash ${PWD}/scripts/stop_node.sh 28 | fi 29 | done 30 | 31 | grcov . --binary-path ./target/debug/deps/ -s . -t html --branch --ignore-not-existing -o ./integration_coverage/ 32 | rm **/rust-dlc-* -------------------------------------------------------------------------------- /dlc-trie/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Crypto Garage"] 3 | description = "Data structures for storage and retrival of numerical Discreet Log Contracts (DLC)." 4 | homepage = "https://github.com/p2pderivatives/rust-dlc" 5 | license-file = "../LICENSE" 6 | name = "dlc-trie" 7 | repository = "https://github.com/p2pderivatives/rust-dlc/tree/master/dlc-trie" 8 | version = "0.8.0" 9 | 10 | [features] 11 | default = ["std"] 12 | std = ["dlc/std", "bitcoin/std"] 13 | no-std = ["dlc/no-std"] 14 | parallel = ["rayon"] 15 | use-serde = ["serde", "dlc/use-serde"] 16 | 17 | [dependencies] 18 | bitcoin = { version = "0.32.2", default-features = false } 19 | dlc = {version = "0.8.0", default-features = false, path = "../dlc"} 20 | rayon = {version = "1.5", optional = true} 21 | secp256k1-zkp = {version = "0.11.0" } 22 | serde = {version = "1.0", optional = true, default-features = false, features = ["derive"]} 23 | -------------------------------------------------------------------------------- /sample/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Crypto Garage"] 3 | edition = "2018" 4 | name = "sample" 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | bitcoin = {version = "0.32.2"} 9 | bitcoin-rpc-provider = {path = "../bitcoin-rpc-provider"} 10 | dlc = {path = "../dlc", features = ["use-serde"]} 11 | dlc-manager = {path = "../dlc-manager", features = ["use-serde", "parallel"]} 12 | dlc-messages = {path = "../dlc-messages"} 13 | dlc-sled-storage-provider = {path = "../dlc-sled-storage-provider"} 14 | futures = "0.3" 15 | lightning = {version = "0.0.125"} 16 | lightning-net-tokio = {version = "0.0.125" } 17 | p2pd-oracle-client = {path = "../p2pd-oracle-client"} 18 | serde = "1.0" 19 | serde_json = "1.0" 20 | serde_yaml = "0.9.14" 21 | time = "0.3.16" 22 | tokio = {version = "1.5", features = ["io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time"]} 23 | 24 | [dev-dependencies] 25 | assert_cmd = "2.0.12" 26 | bitcoin-test-utils = { path = "../bitcoin-test-utils" } 27 | predicates = "3.0.3" 28 | rexpect = "0.5.0" 29 | -------------------------------------------------------------------------------- /testconfig/oracle/integration.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | address: "0.0.0.0:8080" 3 | oracle: 4 | keyFile: ../../certs/oracle/key.pem 5 | keyPass: 6 | file: ../../certs/oracle/pass.txt 7 | log: 8 | dir: _log 9 | output_stdout: true 10 | basename: unittest.log.%Y-%m-%d 11 | rotation_interval: PT24H 12 | rotation_counts: 7 13 | format: json 14 | level: info 15 | database: 16 | log: false 17 | host: localhost 18 | port: 5432 19 | dbuser: postgres 20 | dbpassword: 1234 21 | dbname: db 22 | api: 23 | assets: 24 | btcusd: 25 | startDate: 2020-01-01T00:00:00Z 26 | frequency: PT1H 27 | range: P10DT 28 | unit: usd/btc 29 | precision: 0 30 | signconfig: 31 | base: 2 32 | nbDigits: 20 33 | btcjpy: 34 | startDate: 2020-01-01T00:00:00Z 35 | frequency: PT1H 36 | range: P2MT 37 | unit: jpy/btc 38 | precision: 0 39 | signconfig: 40 | base: 2 41 | nbDigits: 20 42 | datafeed: 43 | dummy: 44 | returnValue: 9000 45 | -------------------------------------------------------------------------------- /scripts/generate_serialized_contract_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | CONTRACT_TEST_FILES=("Offered" "Accepted" "Confirmed" "Confirmed1" "Signed" "Signed1" "PreClosed" "Closed") 6 | 7 | DEST=${PWD}/dlc-sled-storage-provider/test_files/ 8 | 9 | docker-compose up -d 10 | ./scripts/wait_for_electrs.sh 11 | 12 | for FILE in ${CONTRACT_TEST_FILES[@]} 13 | do 14 | GENERATE_SERIALIZED_CONTRACT=1 cargo test -- single_oracle_numerical_test --ignored --exact 15 | cp ${PWD}/dlc-manager/${FILE//1/} ${DEST}${FILE} 16 | done 17 | 18 | CHANNEL_TEST_FILES=("OfferedChannel" "AcceptedChannel" "SignedChannelEstablished" "SignedChannelSettled") 19 | 20 | for FILE in ${CHANNEL_TEST_FILES[@]} 21 | do 22 | GENERATE_SERIALIZED_CHANNEL=1 cargo test -- channel_settled_close_test --ignored --exact 23 | cp ${PWD}/dlc-manager/${FILE//1/} ${DEST}${FILE} 24 | done 25 | 26 | TEST_FILES=( "${CONTRACT_TEST_FILES[@]}" "${CHANNEL_TEST_FILES[@]}" ) 27 | 28 | for FILE in ${TEST_FILES[@]} 29 | do 30 | rm -f ${PWD}/dlc-manager/${FILE} 31 | done 32 | 33 | rm ${PWD}/dlc-manager/Signed* 34 | 35 | docker-compose down -v 36 | -------------------------------------------------------------------------------- /fuzz/Readme.md: -------------------------------------------------------------------------------- 1 | # Fuzz testing 2 | 3 | This folder contains utilities to carry out fuzz testing. 4 | Currently only Honggfuzz is supported. 5 | Heavily inspired by [the fuzz testing setup of rust-lightning](https://github.com/lightningdevkit/rust-lightning/tree/main/fuzz). 6 | 7 | ## Running 8 | 9 | Generate the fuzzing code: 10 | ```bash 11 | pushd src/bin 12 | ./gen_msgs_fuzz.sh 13 | popd 14 | HFUZZ_RUN_ARGS="--exit_upon_crash" cargo hfuzz run offerdlc_fuzz 15 | ``` 16 | (replace with whichever target you want to fuzz) 17 | 18 | ## Running through docker 19 | 20 | A docker image is provided to run honggfuzz on it. 21 | To build it, from the repository root directory run: 22 | ```bash 23 | docker build . -f fuzz/Dockerfile -t dlcfuzz 24 | ``` 25 | 26 | You can then use it to fuzz as follow: 27 | ```bash 28 | docker run --rm -it dlcfuzz offerdlc_fuzz 29 | ``` 30 | (replacing with whichever target you want to fuzz) 31 | 32 | You can alter the hongfuzz arguments using the `HFUZZ_RUN_ARGS` environment variable, e.g.: 33 | ```bash 34 | docker run --rm -it -e HFUZZ_RUN_ARGS='--exit_upon_crash -t 10' dlcfuzz offerdlc_fuzz 35 | ``` -------------------------------------------------------------------------------- /dlc-trie/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [0.8.0] - 2025-12-13 8 | 9 | ### Changed 10 | - use `bitcoin::Amount` instead of `u64` for amounts (@bennyhodl) 11 | - validate that `RangePayout` do not contain a `count` of zero 12 | - `group_by_ignoring_digits` panics when `start` is greater than `end` 13 | 14 | ## [0.5.0] - 2024-07-11 15 | 16 | ### Changed 17 | - update bitcoin and secp256k1_zkp dependencies 18 | 19 | ### Added 20 | - support for `no-std` 21 | 22 | ## [0.4.0] - 2022-10-28 23 | 24 | ### Changed 25 | - `dlc` crate version update 26 | 27 | ## [0.3.0] - 2022-10-28 28 | 29 | ### Changed 30 | - `dlc` crate version update 31 | 32 | ## [0.2.0] 33 | 34 | ### Added 35 | - `parallel` feature for computing anticipation points in parallel. 36 | - support for multi oracles with varying number of digits. 37 | 38 | ### Fixed 39 | - iteration of DigitTrie sometimes omitting values. 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 p2pderivatives 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dlc-messages/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [0.8.0] - 2025-12-13 8 | 9 | ### Changed 10 | - use `bitcoin::Amount` instead of `u64` for amounts (@bennyhodl) 11 | 12 | ## [0.5.0] - 2024-07-11 13 | 14 | ### Fixed 15 | - serialization of `f64` 16 | - `use-serde` feature 17 | - `Reject` message 18 | 19 | ### Changed 20 | - updated bitcoin, lightning and secp256k1_zkp dependencies 21 | - `read_dlc_message` is no public 22 | - implement `std::error::Error` for `Error` struct 23 | 24 | ### Added 25 | - support for `no-std` 26 | - `nonces` method for `OracleAttestation` 27 | 28 | ## [0.3.0] - 2022-10-28 29 | 30 | ### Changed 31 | - `dlc` crate version update 32 | 33 | ## [0.2.0] - 2022-06-06 34 | 35 | ### Changed 36 | - contract id computation function removed from crate. 37 | - serialization format according to the DLC specifications. 38 | 39 | ### Fixed 40 | - vulnerability on deserialization of vectors. 41 | -------------------------------------------------------------------------------- /dlc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Crypto Garage"] 3 | description = "Creation, signing and verification of Discreet Log Contracts (DLC) transactions." 4 | homepage = "https://github.com/p2pderivatives/rust-dlc" 5 | license-file = "../LICENSE" 6 | name = "dlc" 7 | repository = "https://github.com/p2pderivatives/rust-dlc/tree/master/dlc" 8 | version = "0.8.0" 9 | 10 | [dependencies] 11 | bitcoin = { version = "0.32.2", default-features = false } 12 | miniscript = { version = "12.2", default-features = false } 13 | secp256k1-sys = "0.10.0" 14 | secp256k1-zkp = "0.11.0" 15 | serde = { version = "1.0", default-features = false, optional = true } 16 | 17 | [features] 18 | # for benchmarks 19 | unstable = [] 20 | default = ["std"] 21 | std = ["bitcoin/std", "miniscript/std", "secp256k1-zkp/rand-std"] 22 | no-std = ["miniscript/no-std"] 23 | use-serde = ["serde", "secp256k1-zkp/serde", "bitcoin/serde"] 24 | 25 | [dev-dependencies] 26 | bitcoin-test-utils = { path = "../bitcoin-test-utils" } 27 | bitcoincore-rpc = { version = "0.19.0" } 28 | bitcoincore-rpc-json = { version = "0.19.0" } 29 | rayon = "1.5" 30 | secp256k1-zkp = { version = "0.11.0", features = ["hashes", "rand","serde", "global-context"] } 31 | -------------------------------------------------------------------------------- /sample/src/disk.rs: -------------------------------------------------------------------------------- 1 | use lightning::util::logger::{Logger, Record}; 2 | use std::fs; 3 | use std::io::Write; 4 | use time::OffsetDateTime; 5 | 6 | pub(crate) struct FilesystemLogger { 7 | data_dir: String, 8 | } 9 | impl FilesystemLogger { 10 | pub(crate) fn new(data_dir: String) -> Self { 11 | let logs_path = format!("{}/logs", data_dir); 12 | fs::create_dir_all(logs_path.clone()).unwrap(); 13 | Self { 14 | data_dir: logs_path, 15 | } 16 | } 17 | } 18 | impl Logger for FilesystemLogger { 19 | fn log(&self, record: Record) { 20 | let raw_log = record.args.to_string(); 21 | let log = format!( 22 | "{} {:<5} [{}:{}] {}\n", 23 | OffsetDateTime::now_utc().unix_timestamp(), 24 | record.level, 25 | record.module_path, 26 | record.line, 27 | raw_log 28 | ); 29 | 30 | let logs_file_path = format!("{}/logs.txt", self.data_dir.clone()); 31 | fs::OpenOptions::new() 32 | .create(true) 33 | .append(true) 34 | .open(logs_file_path) 35 | .unwrap() 36 | .write_all(log.as_bytes()) 37 | .unwrap(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dlc-messages/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Crypto Garage"] 3 | description = "Structs and serialization for the Discreet Log Contract (DLC) protocol." 4 | homepage = "https://github.com/p2pderivatives/rust-dlc" 5 | license-file = "../LICENSE" 6 | name = "dlc-messages" 7 | repository = "https://github.com/p2pderivatives/rust-dlc/tree/master/dlc-messages" 8 | version = "0.8.0" 9 | 10 | [features] 11 | default = ["std"] 12 | std = ["dlc/std", "bitcoin/std", "lightning/std"] 13 | no-std = ["dlc/no-std", "lightning/no-std"] 14 | use-serde = ["serde", "secp256k1-zkp/serde", "bitcoin/serde"] 15 | 16 | [dependencies] 17 | bitcoin = { version = "0.32.2", default-features = false } 18 | dlc = { version = "0.8.0", path = "../dlc", default-features = false } 19 | lightning = { version = "0.0.125", default-features = false } 20 | secp256k1-zkp = {version = "0.11.0"} 21 | serde = {version = "1.0", features = ["derive"], optional = true} 22 | 23 | [dev-dependencies] 24 | bitcoin = { version = "0.32.2", default-features = false, features = ["serde"] } 25 | dlc-messages = {path = "./", default-features = false, features = ["use-serde"]} 26 | secp256k1-zkp = {version = "0.11.0", features = ["serde", "global-context"]} 27 | serde = {version = "1.0", features = ["derive"]} 28 | serde_json = "1.0" 29 | -------------------------------------------------------------------------------- /sample/src/hex_utils.rs: -------------------------------------------------------------------------------- 1 | use bitcoin::secp256k1::PublicKey; 2 | 3 | use std::fmt::Write as _; 4 | 5 | pub fn to_vec(hex: &str) -> Option> { 6 | let len = hex.len() / 2; 7 | let mut out = Vec::with_capacity(hex.len() / 2); 8 | out.resize(len, 0); 9 | 10 | match to_slice(hex, &mut out) { 11 | Err(_) => None, 12 | Ok(_) => Some(out), 13 | } 14 | } 15 | 16 | pub fn to_slice(hex: &str, arr: &mut [u8]) -> Result<(), ()> { 17 | let mut b = 0; 18 | for (idx, c) in hex.as_bytes().iter().enumerate() { 19 | b <<= 4; 20 | match *c { 21 | b'A'..=b'F' => b |= c - b'A' + 10, 22 | b'a'..=b'f' => b |= c - b'a' + 10, 23 | b'0'..=b'9' => b |= c - b'0', 24 | _ => return Err(()), 25 | } 26 | if (idx & 1) == 1 { 27 | arr[idx / 2] = b; 28 | b = 0; 29 | } 30 | } 31 | 32 | Ok(()) 33 | } 34 | 35 | #[inline] 36 | pub fn hex_str(value: &[u8]) -> String { 37 | let mut res = String::with_capacity(64); 38 | for v in value { 39 | write!(res, "{:02x}", v).unwrap(); 40 | } 41 | res 42 | } 43 | 44 | pub fn to_compressed_pubkey(hex: &str) -> Option { 45 | let data = to_vec(&hex[0..33 * 2])?; 46 | PublicKey::from_slice(&data).ok() 47 | } 48 | -------------------------------------------------------------------------------- /dlc/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [0.8.0] - 2025-12-13 8 | 9 | ### Changed 10 | - use `bitcoin::Amount` instead of `u64` for amounts (@bennyhodl) 11 | - pre-allocate vector when creating CETs (@jharveyb) 12 | 13 | ## [0.5.0] - 2024-07-11 14 | 15 | ### Added 16 | - Implement `std::error::Error` for `dlc::Error` 17 | - Support for `no-std` 18 | - Re-export of `secp256k1_zkp` 19 | - `get_fund_outpoint` for `DlcTransactions` 20 | 21 | ### Changed 22 | - Updated bitcoin and secp256k1 dependencies 23 | 24 | ### Fixed 25 | - Issue with fee computation 26 | - Issue with the `use-serde` feature 27 | 28 | ## [0.4.0] - 2023-02-06 29 | 30 | ### Added 31 | - Utility functions for DLC channels. 32 | 33 | ### Fixed 34 | - Use checked additions and multiplications to avoid panicking on bad use inputs. 35 | 36 | ## [0.3.0] - 2022-10-28 37 | 38 | ### Changed 39 | - Rename `local_payout` to `offer_payout` in `Payout` structure. 40 | 41 | ## [0.2.0] - 2022-06-06 42 | 43 | ### Fixed 44 | - Fix build issue caused by breaking minor update of `rust-secp256k1-sys` crate. 45 | 46 | ### Changed 47 | - Discard dust outputs on the refund transaction. 48 | -------------------------------------------------------------------------------- /testconfig/oracle/certs/db/db.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAuLMMRBiR8aF/vzrJ8mk9Tg3raVyptCBKCiQL/qpGxl9LadH7 3 | k3Y2v23ZvedMyzEN9T0EEnxq4GtZzVE6UHVUTBkWB0doABUnsSKLVnFftsDnVQsL 4 | omilXbNvkaC74qTllmZRHepTawFdhKgWsj2kad9RDmEY5LUHQSdTqMdvCEWu9g7w 5 | IPgWLL6xL9yUsdp2Av99T8vGxIFS3Cx3AhiY75rlcsTedCcnivPhUnmREN92aqoL 6 | 9JrJw2ZwBhA9ntsHK/Yy3/fshha1A4VB0ugqlP1GSsiMIRtAy0dSz1TPLALMoBf4 7 | lDNf6OtapzfHKbYl7E1R5SaqC0C5oKwSx94axQIDAQABAoIBACJ9XNcCd45ViBNf 8 | mQJpYIdI1iLwqMf4F5tZK9w8U8cLWHvEOGRK+OI6UyTBInf3CxI2eKIzFYoewcAz 9 | YN5RapJjRNfLH8KUMLtFJhvYQ/VOhGQ/EcccH3Ul4rmg+DTUcgLpzE/3x+f9c0co 10 | VESoeiFDEFHNE+bxXw9tlpO32iYC++DWB4xGHicZ1U7EonbD1GGTcZFVACMtnrml 11 | WotqKe1jcxMU7ozPxvfddKyaJggYqyFLNS6q7rCMTYyWSqg6BXJBxqj6IzeS4ItZ 12 | YhavmDV9Exb4PdL8dotywDYsbVWN7Z8SH3JKJhaS2brs5xmjytpvmoRM4x/KzgDI 13 | tWHUB4ECgYEA5DIcbqqJuZUs0eDBnuL1TQcKoyLEt1thZyYjmA0zCvV3BVEtmKVs 14 | lOk7Q4FhxcyKOKnfGuzW7XEclHRx4KvK4z/Esro6FdExdNWzSKWgjLxf7IwV9Gee 15 | /idC4keUdGG9fiVyW8WST6DjfB8Dr2EKeixyzNh7O8kFtSlyv3bLsrUCgYEAzzQy 16 | l4EbQ8HeAcUg4BDIOhDrn8wUYBw7yUC46qempUolNGEu0EsE9qEgZw0R4bpshMr+ 17 | bstvEhe86DoOq0yUb3lov3kFqykTGfonNPXaPHgR8BsWZh9EwKfBbIlT7Hdy98Wq 18 | dG0IOboEQ9LpNE4nSeNdEqCow+J3pLWr6cr0gdECgYAkxAoerm1YMDezbPHlJo39 19 | JhhJpm1pWVi9JMDxW5cQufG+MpEVGfn/mABLZQoas1TFwmDG1sfeI65GIOjEGQms 20 | SXbokOaQ406Dk3a6Sq0uX59Y3k0fPp64Nh0plfzOL303WNMvBAsJt1NPiTOvywPE 21 | IWsxo+NfA4le4dmyDXLOTQKBgQDIGLdWufZTe8/iU7VIzMwfzyFMgy+WFQ3jb15k 22 | NZzn+G9vYv5rZlcXuUhqXCPNolOT9di5tDnB9iyW8yIhaOXbtRpj9gJ0ZUkuB/Z+ 23 | 3YFwbd+cyPvbiQzDI/3Vy9TBAiWDg1716ilMXggqW26b9XFZmHjUOVRhPr2d0VeA 24 | gl+XUQKBgQDHq2n4NrF1uV4+ab/7PApLoPG/h2U3ocusqYvfUJZboNM8G1bGvBf6 25 | ArRIl0JqnQ7BSMEuJh9I8yG15BWfkuHdN841G222cEYBuxP6x8E4sFwutNoSB0vc 26 | rykrADchduIUKgXFap+1MaInsuKUUIptFz06y37wjt8kUWHVL4ShRg== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /dlc-manager/src/contract/signed_contract.rs: -------------------------------------------------------------------------------- 1 | //! #SignedContract 2 | 3 | use crate::conversion_utils::PROTOCOL_VERSION; 4 | use crate::ChannelId; 5 | 6 | use super::accepted_contract::AcceptedContract; 7 | use dlc_messages::CetAdaptorSignature; 8 | use dlc_messages::CetAdaptorSignatures; 9 | use dlc_messages::FundingSignatures; 10 | use dlc_messages::SignDlc; 11 | use secp256k1_zkp::ecdsa::Signature; 12 | use secp256k1_zkp::EcdsaAdaptorSignature; 13 | 14 | /// Contain information about a contract that was fully signed. 15 | #[derive(Clone)] 16 | pub struct SignedContract { 17 | /// The accepted contract that was signed. 18 | pub accepted_contract: AcceptedContract, 19 | /// The adaptor signatures of the offering party (None if offering party). 20 | pub adaptor_signatures: Option>, 21 | /// The refund signature of the offering party. 22 | pub offer_refund_signature: Signature, 23 | /// The signatures for the funding inputs of the offering party. 24 | pub funding_signatures: FundingSignatures, 25 | /// The [`ChannelId`] to which the contract was associated if any. 26 | pub channel_id: Option, 27 | } 28 | 29 | impl SignedContract { 30 | pub(crate) fn get_sign_dlc( 31 | &self, 32 | cet_adaptor_signatures: Vec, 33 | ) -> SignDlc { 34 | let contract_id = self.accepted_contract.get_contract_id(); 35 | 36 | SignDlc { 37 | protocol_version: PROTOCOL_VERSION, 38 | contract_id, 39 | cet_adaptor_signatures: CetAdaptorSignatures { 40 | ecdsa_adaptor_signatures: cet_adaptor_signatures 41 | .into_iter() 42 | .map(|x| CetAdaptorSignature { signature: x }) 43 | .collect(), 44 | }, 45 | refund_signature: self.offer_refund_signature, 46 | funding_signatures: self.funding_signatures.clone(), 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/Development.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | Developing the library requires only a working rust environment. 4 | Integration testing and code coverage report generation require `docker` and `docker-compose`. 5 | 6 | ## Running tests 7 | 8 | In the main directory, you can run `cargo test --all-features` to run all the unit tests. 9 | 10 | ## Running integration tests (requires docker-compose) 11 | 12 | In the root directory, run `docker-compose up -d` to run a bitcoin node and electrs instance. 13 | 14 | Then in the `dlc` or `dlc-manager` directory run 15 | ``` 16 | cargo test -- --ignored test_name 17 | ``` 18 | replacing `test_name` with the test you want to run. 19 | For example within the `dlc-manager` folder: 20 | ``` 21 | cargo test -- --ignored two_of_five_oracle_numerical_test 22 | ``` 23 | 24 | ## Running fuzz tests 25 | 26 | Some fuzz testing are implemented, check [the documentation](../fuzz/Readme.md) for details. 27 | 28 | ## Generating code coverage report 29 | 30 | ### Code coverage for unit tests 31 | 32 | From the main folder run: 33 | ``` 34 | ./scripts/generate_test_coverage.sh 35 | ``` 36 | 37 | This will generate a coverage report in `coverage/index.html`. 38 | 39 | ### Code coverage for dlc-manager integration tests 40 | 41 | From the main folder run: 42 | ``` 43 | ./scripts/generate_integration_test_coverage.sh 44 | ``` 45 | The coverage report will be available at `integration_coverage/index.html`. 46 | 47 | ## Benchmarking and profiling 48 | 49 | At the moment the bottlenecks are mainly the adaptor signature verification and signing for numerical outcome DLC with a relatively large number of CETs. 50 | However it can sometimes be useful to have a look at benchmarks and do some profiling. 51 | 52 | ### Benchmarking (requires rust nightly) 53 | 54 | Some benchmarks are [available for the dlc manager](../dlc-manager/benches/benchmarks.rs). 55 | To run them: 56 | ``` 57 | cargo +nightly bench --features=unstable 58 | ``` 59 | 60 | ### Profiling (requires docker-compose) 61 | 62 | Profiling is currently not working. 63 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.4" 2 | services: 3 | ### BITCOIND 4 | bitcoind: 5 | image: polarlightning/bitcoind:27.0 6 | container_name: bitcoin-node 7 | command: | 8 | -conf=/config/default.conf 9 | -printtoconsole 10 | -regtest 11 | ports: 12 | # regtest ports 13 | - 18443:18443 14 | - 18444:18444 15 | volumes: 16 | - bitcoind-data:/home/bitcoin/.bitcoin 17 | - ./testconfig/config:/config 18 | - ./scripts:/scripts 19 | electrs: 20 | image: ghcr.io/cryptogarageinc/electrs:v0.4.12-bitcoin 21 | command: | 22 | sh -c "electrs -vv --daemon-rpc-addr bitcoind:18443 --daemon-dir /home/bitcoin/.bitcoin --network regtest --http-addr 0.0.0.0:3004" 23 | volumes: 24 | - bitcoind-data:/home/bitcoin/.bitcoin 25 | ports: 26 | - 3004:3004 27 | oracle-server: 28 | image: ghcr.io/p2pderivatives/oracle:v0.2.3 29 | container_name: oracle-server 30 | profiles: [oracle] 31 | command: | 32 | -config /config 33 | -appname p2pdoracle 34 | -e integration 35 | -migrate 36 | environment: 37 | - P2PDORACLE_DATABASE_HOST=oracle-db 38 | - P2PDORACLE_ORACLE_KEYFILE=/key/key.pem 39 | - P2PDORACLE_ORACLE_KEYPASS_FILE=/key/pass.txt 40 | restart: always 41 | depends_on: 42 | - oracle-db 43 | ports: 44 | - 8080:8080 45 | volumes: 46 | - ./testconfig/oracle:/config 47 | - ./testconfig/oracle/certs/oracle:/key 48 | 49 | oracle-db: 50 | image: oracledb:latest 51 | build: 52 | context: . 53 | dockerfile: ./testconfig/oracle/oracledb.dockerfile 54 | profiles: [oracle] 55 | command: | 56 | -c log_statement=all 57 | -c ssl=on 58 | -c ssl_cert_file=/certs/db.crt 59 | -c ssl_key_file=/certs/db.key 60 | restart: always 61 | ports: 62 | - 5432:5432 63 | environment: 64 | - POSTGRES_USER=postgres 65 | - POSTGRES_PASSWORD=1234 66 | - POSTGRES_DB=db 67 | volumes: 68 | - oracle-db-data:/var/lib/postgresql/data/ # persist data even if container shuts down 69 | 70 | 71 | volumes: 72 | bitcoind-data: 73 | oracle-db-data: 74 | -------------------------------------------------------------------------------- /dlc-manager/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Crypto Garage"] 3 | description = "Creation and handling of Discrete Log Contracts (DLC)." 4 | edition = "2018" 5 | homepage = "https://github.com/p2pderivatives/rust-dlc" 6 | license-file = "../LICENSE" 7 | name = "dlc-manager" 8 | repository = "https://github.com/p2pderivatives/rust-dlc/tree/master/dlc-manager" 9 | version = "0.8.0" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["dlc/std", "dlc-messages/std", "dlc-trie/std", "bitcoin/std", "lightning/std"] 14 | fuzztarget = ["rand_chacha"] 15 | parallel = ["dlc-trie/parallel"] 16 | use-serde = ["serde", "dlc/use-serde", "dlc-messages/use-serde", "dlc-trie/use-serde"] 17 | 18 | [dependencies] 19 | async-trait = "0.1.50" 20 | bitcoin = { version = "0.32.2", default-features = false } 21 | dlc = { version = "0.8.0", default-features = false, path = "../dlc" } 22 | dlc-messages = { version = "0.8.0", default-features = false, path = "../dlc-messages" } 23 | dlc-trie = { version = "0.8.0", default-features = false, path = "../dlc-trie" } 24 | hex = { package = "hex-conservative", version = "0.1" } 25 | lightning = { version = "0.0.125", default-features = false, features = ["grind_signatures"] } 26 | log = "0.4.14" 27 | rand_chacha = {version = "0.3.1", optional = true} 28 | secp256k1-zkp = {version = "0.11.0"} 29 | serde = {version = "1.0", optional = true} 30 | 31 | [dev-dependencies] 32 | bitcoin-rpc-provider = {path = "../bitcoin-rpc-provider"} 33 | bitcoin-test-utils = {path = "../bitcoin-test-utils"} 34 | bitcoincore-rpc = {version = "0.19"} 35 | bitcoincore-rpc-json = {version = "0.19"} 36 | criterion = "0.4.0" 37 | dlc-manager = { path = ".", default-features = false, features = ["use-serde"] } 38 | dlc-messages = { path = "../dlc-messages", default-features = false, features = ["serde"] } 39 | electrs-blockchain-provider = {path = "../electrs-blockchain-provider"} 40 | env_logger = "0.9.1" 41 | mocks = {path = "../mocks"} 42 | secp256k1-zkp = {version = "0.11.0", features = ["hashes", "rand", "rand-std", "global-context", "serde"]} 43 | serde = "1.0" 44 | serde_json = "1.0" 45 | simple-wallet = {path = "../simple-wallet"} 46 | 47 | [[bench]] 48 | harness = false 49 | name = "benchmarks" 50 | -------------------------------------------------------------------------------- /bitcoin-test-utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions to be used only in tests. 2 | 3 | extern crate bitcoin; 4 | extern crate bitcoincore_rpc; 5 | extern crate bitcoincore_rpc_json; 6 | 7 | use bitcoin::consensus::Decodable; 8 | use bitcoin::consensus::Encodable; 9 | use bitcoin::Transaction; 10 | use std::fmt::Write; 11 | 12 | pub mod rpc_helpers; 13 | 14 | /// Utility function used to parse hex into a target u8 buffer. Returns 15 | /// the number of bytes converted or an error if it encounters an invalid 16 | /// character or unexpected end of string. 17 | #[allow(clippy::result_unit_err)] // This is just a test util 18 | pub fn from_hex(hex: &str, target: &mut [u8]) -> Result { 19 | if hex.len() % 2 == 1 || hex.len() > target.len() * 2 { 20 | return Err(()); 21 | } 22 | 23 | let mut b = 0; 24 | let mut idx = 0; 25 | for c in hex.bytes() { 26 | b <<= 4; 27 | match c { 28 | b'A'..=b'F' => b |= c - b'A' + 10, 29 | b'a'..=b'f' => b |= c - b'a' + 10, 30 | b'0'..=b'9' => b |= c - b'0', 31 | _ => return Err(()), 32 | } 33 | if (idx & 1) == 1 { 34 | target[idx / 2] = b; 35 | b = 0; 36 | } 37 | idx += 1; 38 | } 39 | Ok(idx / 2) 40 | } 41 | 42 | /// Transforms an hex string to a Vec. 43 | /// Panics if the string is not valid hex. 44 | pub fn str_to_hex(hex_str: &str) -> Vec { 45 | let mut hex = vec![0; hex_str.len() / 2]; 46 | from_hex(hex_str, &mut hex).unwrap(); 47 | hex 48 | } 49 | 50 | /// Serialize a transaction to an lower hex string. 51 | pub fn tx_to_string(tx: &Transaction) -> String { 52 | let mut writer = Vec::new(); 53 | tx.consensus_encode(&mut writer).unwrap(); 54 | let mut serialized = String::new(); 55 | for x in writer { 56 | write!(&mut serialized, "{:02x}", x).unwrap(); 57 | } 58 | serialized 59 | } 60 | 61 | /// Deserialize an hex string to a bitcoin transaction. 62 | /// Panics if given invalid hex or data. 63 | pub fn tx_from_string(tx_str: &str) -> Transaction { 64 | let tx_hex = str_to_hex(tx_str); 65 | Transaction::consensus_decode(&mut tx_hex.as_slice()).unwrap() 66 | } 67 | -------------------------------------------------------------------------------- /mocks/src/mock_blockchain.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Mutex; 2 | 3 | use bitcoin::{Block, Transaction, Txid}; 4 | use dlc_manager::{error::Error, Blockchain, Utxo}; 5 | use lightning::chain::chaininterface::FeeEstimator; 6 | use simple_wallet::WalletBlockchainProvider; 7 | 8 | pub struct MockBlockchain { 9 | transactions: Mutex>, 10 | } 11 | 12 | impl MockBlockchain { 13 | pub fn new() -> Self { 14 | Self { 15 | transactions: Mutex::new(Vec::new()), 16 | } 17 | } 18 | } 19 | 20 | impl Default for MockBlockchain { 21 | fn default() -> Self { 22 | Self::new() 23 | } 24 | } 25 | 26 | impl Blockchain for MockBlockchain { 27 | fn send_transaction(&self, transaction: &Transaction) -> Result<(), Error> { 28 | self.transactions.lock().unwrap().push(transaction.clone()); 29 | Ok(()) 30 | } 31 | fn get_network(&self) -> Result { 32 | Ok(bitcoin::Network::Regtest) 33 | } 34 | fn get_blockchain_height(&self) -> Result { 35 | Ok(10) 36 | } 37 | fn get_block_at_height(&self, _height: u64) -> Result { 38 | unimplemented!(); 39 | } 40 | fn get_transaction(&self, tx_id: &Txid) -> Result { 41 | Ok(self 42 | .transactions 43 | .lock() 44 | .unwrap() 45 | .iter() 46 | .find(|x| &x.compute_txid() == tx_id) 47 | .unwrap() 48 | .clone()) 49 | } 50 | fn get_transaction_confirmations(&self, _tx_id: &Txid) -> Result { 51 | Ok(6) 52 | } 53 | } 54 | 55 | impl WalletBlockchainProvider for MockBlockchain { 56 | fn get_utxos_for_address(&self, _address: &bitcoin::Address) -> Result, Error> { 57 | unimplemented!() 58 | } 59 | 60 | fn is_output_spent(&self, _txid: &Txid, _vout: u32) -> Result { 61 | unimplemented!() 62 | } 63 | } 64 | 65 | impl FeeEstimator for MockBlockchain { 66 | fn get_est_sat_per_1000_weight( 67 | &self, 68 | _confirmation_target: lightning::chain::chaininterface::ConfirmationTarget, 69 | ) -> u32 { 70 | unimplemented!() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_channel.json: -------------------------------------------------------------------------------- 1 | {"protocolVersion":1,"contractFlags":0,"chainHash":"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f","temporaryContractId":[70,18,232,131,5,250,209,3,102,240,132,230,226,21,17,88,145,129,122,9,186,170,224,167,97,113,188,5,21,236,86,189],"temporaryChannelId":[123,203,234,245,246,100,117,252,84,18,27,224,26,223,88,238,60,239,247,99,155,54,131,7,197,176,9,229,185,198,71,151],"contractInfo":{"singleContractInfo":{"totalCollateral":101000000,"contractInfo":{"contractDescriptor":{"enumeratedContractDescriptor":{"payouts":[{"outcome":"a","offerPayout":101000000},{"outcome":"b","offerPayout":0},{"outcome":"c","offerPayout":101000000},{"outcome":"d","offerPayout":0}]}},"oracleInfo":{"single":{"oracleAnnouncement":{"announcementSignature":"58e271eb4ffb6084182fbc8097afe86d8aea17e096c107f831abc405030dc69d70611369918f2879176f53195d9bfc807ef50f2e1152d535174a5808ee87569a","oraclePublicKey":"de2caf701a3935c0e7d63ba43f1c65d3bfbde7117f24871714c8a82d688a5d0e","oracleEvent":{"oracleNonces":["f1f7444917012f2b7966b9de97de118bf5a39c564ed5c18188b942a006bcb696"],"eventMaturityEpoch":1623133104,"eventDescriptor":{"enumEvent":{"outcomes":["a","b","c","d"]}},"eventId":"Test"}}}}}}},"fundingPubkey":"03897aad0fe458b9800f001ae95f4d7fb8dea6cf267494ad8d63ceb7ccecc751e0","revocationBasepoint":"03a2536f9b457b10ae22da642a9cc894514ba9192deee3605085adcab86f790248","publishBasepoint":"03c165e866284766b031066cb8945a8ceacd13c524235245c5fa9bde2b521f5e2a","ownBasepoint":"037f63d5bb4df2da97c63ef02afb2e114d64a8d17d7e7e31cd52696b23423a7e89","firstPerUpdatePoint":"0223e2f88b20dbd7ecf0d2b1800e01a6baa08cb3f7f4e11524f518fe845c1786cb","payoutSpk":"0014ff7e53c1edaa115e2004d213bc82d5b79cbd2973","payoutSerialId":1085418968450537681,"offerCollateral":90000000,"fundingInputs":[{"inputSerialId":6696372491465549741,"prevTx":"0200000000010137c00b6d6f70d6a04ded5ff4b235499b9aa03f55201287190fdc86502fc891560100000000feffffff0200c2eb0b000000001600148ac1bfaf83647dba23115cc257de034f6de3336ef8572e1201000000160014dc4574b41f25cb3576ab938a444501b75edc032c0247304402205d3136d26e41df04ee02a52f2c64614e93b0704fee3bdf2d5cf6368256dff4c402207772db0e66f3771bcd2d56d1907f86ebe0621bff75d149c744fcea6819ad28dc012103d8b6143cb3a01028094ccf4a1de13f411876a8e2c856e4e6ecda48318bd17ce668000000","prevTxVout":0,"sequence":4294967295,"maxWitnessLen":107,"redeemScript":""}],"changeSpk":"00148591eaef0a9c8b2759d2c967cf60f64dec6505a5","changeSerialId":11608938898304204128,"fundOutputSerialId":11319861280411552317,"feeRatePerVb":2,"cetLocktime":1623133103,"refundLocktime":1623737904,"cetNsequence":288} -------------------------------------------------------------------------------- /dlc-messages/src/serde_utils.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions to serialize hexadecimal values as strings in json. 2 | 3 | use std::fmt::Write; 4 | 5 | /// Serialize an hexadecimal value. 6 | pub fn serialize_hex(hex: &[u8], s: S) -> Result 7 | where 8 | S: serde::Serializer, 9 | { 10 | if s.is_human_readable() { 11 | let string = hex.iter().fold(String::new(), |mut s, e| { 12 | write!(s, "{:02x}", e).unwrap(); 13 | s 14 | }); 15 | assert!(string.len() % 2 == 0); 16 | s.serialize_str(&string) 17 | } else { 18 | s.serialize_bytes(hex) 19 | } 20 | } 21 | 22 | /// Deserialize an hexadecimal value represented as an array. 23 | pub fn deserialize_hex_array<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error> 24 | where 25 | D: serde::de::Deserializer<'de>, 26 | { 27 | if deserializer.is_human_readable() { 28 | let string: String = serde::de::Deserialize::deserialize(deserializer)?; 29 | let mut hex = [0u8; 32]; 30 | from_hex(&string, &mut hex).map_err(serde::de::Error::custom)?; 31 | Ok(hex) 32 | } else { 33 | serde::de::Deserialize::deserialize(deserializer) 34 | } 35 | } 36 | 37 | /// Deserialize an hexadecimal value represented as a string. 38 | pub fn deserialize_hex_string<'de, D>(deserializer: D) -> Result, D::Error> 39 | where 40 | D: serde::de::Deserializer<'de>, 41 | { 42 | if deserializer.is_human_readable() { 43 | let string: String = serde::de::Deserialize::deserialize(deserializer)?; 44 | let mut hex = vec![0; string.len() / 2]; 45 | from_hex(&string, &mut hex).map_err(serde::de::Error::custom)?; 46 | Ok(hex) 47 | } else { 48 | serde::de::Deserialize::deserialize(deserializer) 49 | } 50 | } 51 | 52 | fn from_hex(hex: &str, target: &mut [u8]) -> Result { 53 | if hex.len() % 2 == 1 || hex.len() > target.len() * 2 { 54 | return Err("Invalid hex length".to_string()); 55 | } 56 | 57 | let mut b = 0; 58 | let mut idx = 0; 59 | for c in hex.bytes() { 60 | b <<= 4; 61 | match c { 62 | b'A'..=b'F' => b |= c - b'A' + 10, 63 | b'a'..=b'f' => b |= c - b'a' + 10, 64 | b'0'..=b'9' => b |= c - b'0', 65 | _ => return Err("Invalid hex character".to_string()), 66 | } 67 | if (idx & 1) == 1 { 68 | target[idx / 2] = b; 69 | b = 0; 70 | } 71 | idx += 1; 72 | } 73 | Ok(idx / 2) 74 | } 75 | -------------------------------------------------------------------------------- /dlc-manager/src/contract/utils.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::hash::Hash; 3 | 4 | pub(crate) fn get_majority_combination( 5 | outcomes: &[(usize, &Vec)], 6 | ) -> Option<(Vec, Vec)> { 7 | let mut hash_set: HashMap<&Vec, Vec> = HashMap::new(); 8 | 9 | for outcome in outcomes { 10 | let index = outcome.0; 11 | let outcome_value = outcome.1; 12 | 13 | let index_set = hash_set.entry(outcome_value).or_default(); 14 | index_set.push(index); 15 | } 16 | 17 | if hash_set.is_empty() { 18 | return None; 19 | } 20 | 21 | let mut values: Vec<_> = hash_set.into_iter().collect(); 22 | values.sort_by(|x, y| x.1.len().partial_cmp(&y.1.len()).unwrap()); 23 | let (last_outcomes, last_indexes) = values.pop().expect("to have at least one element."); 24 | Some((last_outcomes.to_vec(), last_indexes)) 25 | } 26 | 27 | pub(super) fn unordered_equal(a: &[T], b: &[T]) -> bool { 28 | fn count(items: &[T]) -> HashMap<&T, usize> 29 | where 30 | T: Eq + Hash, 31 | { 32 | let mut cnt = HashMap::new(); 33 | for i in items { 34 | *cnt.entry(i).or_insert(0) += 1 35 | } 36 | cnt 37 | } 38 | 39 | count(a) == count(b) 40 | } 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use super::*; 45 | 46 | #[test] 47 | fn get_majority_combination_test() { 48 | let outcomes_a = vec!["a".to_string(), "b".to_string(), "c".to_string()]; 49 | let outcomes_b = vec!["d".to_string(), "e".to_string(), "f".to_string()]; 50 | 51 | let input = vec![ 52 | (0_usize, &outcomes_a), 53 | (1, &outcomes_a), 54 | (2, &outcomes_b), 55 | (3, &outcomes_a), 56 | (5, &outcomes_a), 57 | (10, &outcomes_b), 58 | (14, &outcomes_b), 59 | (17, &outcomes_a), 60 | ]; 61 | 62 | let actual_combination = get_majority_combination(&input); 63 | 64 | let expected_majority = Some((outcomes_a, vec![0, 1, 3, 5, 17])); 65 | 66 | assert_eq!(expected_majority, actual_combination); 67 | } 68 | 69 | #[test] 70 | fn unordered_equal_test() { 71 | let a = vec![4, 7, 2, 9, 12]; 72 | let b = vec![7, 12, 9, 2, 4]; 73 | let c = vec![12, 2, 9, 4, 7]; 74 | let d = vec![4, 7, 3, 9, 12]; 75 | 76 | assert!(unordered_equal(&a, &b)); 77 | assert!(unordered_equal(&a, &c)); 78 | assert!(unordered_equal(&b, &c)); 79 | assert!(!unordered_equal(&a, &d)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust-Dlc 2 | 3 | A Rust library for working with [Discreet Log Contracts](https://adiabat.github.io/dlc.pdf). 4 | 5 | A [sample](./sample) is provided as an example usage of this library. 6 | 7 | Contributions are welcome. 8 | Check the [contributing](./docs/Contributing.md) and [development](./docs/Development.md) documents for more information. 9 | 10 | ## Warning 11 | 12 | This library is still in early stage and while test coverage is decent it has not been thoroughly tested in production yet. 13 | We thus recommend avoiding using it on main-net at the moment. 14 | 15 | ## Status 16 | 17 | The implementation is mainly based on the [DLC specifications](https://github.com/discreetlogcontracts/dlcspecs) but is not yet fully compliant with it. 18 | 19 | 20 | ## Organization 21 | 22 | The library provides several crates to let users chose which functionality they want to utilize within their applications. 23 | 24 | ### dlc 25 | 26 | The [dlc](./dlc) crate provides basic functionalities for creating, signing and verifying DLC transactions. 27 | 28 | ### dlc-trie 29 | 30 | The [dlc-trie](./dlc-trie) crate provides data structures for facilitating the storage and retrieval of information related to DLC based on numerical events. 31 | 32 | ### dlc-manager 33 | 34 | The [dlc-manager](./dlc-manager) crate provides functionalities for handling the creation and processing of DLC, as well as the generation of messages to be exchanged between two parties of a DLC. 35 | 36 | ### dlc-messages 37 | 38 | The [dlc-messages](./dlc-messages) crate provides data structures and serialization functionalities for messages to be exchanged between DLC peers. 39 | 40 | ### bitcoin-rpc-provider 41 | 42 | The [bitcoin-rpc-provider](./bitcoin-rpc-provider) crate implements interfaces required by the [dlc-manager](#dlc-manager) for interacting with the Bitcoin blockchain and proving wallet functionalities through the bitcoin-core RPC. 43 | 44 | ### p2pd-oracle-client 45 | 46 | The [p2pd-oracle-client](./p2pd-oracle-client) crate implements the oracle interface required by the [dlc-manager](#dlc-manager) to interact with an instance of the [P2PDerivatives oracle](https://github.com/p2pderivatives/p2pderivatives-oracle). 47 | 48 | ### sled-storage-provider 49 | 50 | The [sled-storage-provider](./sled-storage-provider) crate implements the storage interface required by the [dlc-manager](#dlc-manager) to provide persistent storage of data. 51 | 52 | ### Testing related crates 53 | 54 | The [bitcoin-test-utils](./bitcoin-test-utils), [fuzz](./fuzz) and [mocks](./mocks) crates are used for testing purpose and are not intended to be used externally. 55 | -------------------------------------------------------------------------------- /sample/examples/contracts/numerical_contract_input.json: -------------------------------------------------------------------------------- 1 | { 2 | "offerCollateral": 100000000, 3 | "acceptCollateral": 100000000, 4 | "feeRate": 2, 5 | "contractInfos": [ 6 | { 7 | "contractDescriptor": { 8 | "numerical": { 9 | "payoutFunction": { 10 | "payoutFunctionPieces": [ 11 | { 12 | "polynomialPayoutCurvePiece": { 13 | "payoutPoints": [ 14 | { 15 | "eventOutcome": 0, 16 | "outcomePayout": 0, 17 | "extraPrecision": 0 18 | }, 19 | { 20 | "eventOutcome": 50000, 21 | "outcomePayout": 0, 22 | "extraPrecision": 0 23 | } 24 | ] 25 | } 26 | }, 27 | { 28 | "polynomialPayoutCurvePiece": { 29 | "payoutPoints": [ 30 | { 31 | "eventOutcome": 50000, 32 | "outcomePayout": 0, 33 | "extraPrecision": 0 34 | }, 35 | { 36 | "eventOutcome": 60000, 37 | "outcomePayout": 200000000, 38 | "extraPrecision": 0 39 | } 40 | ] 41 | } 42 | }, 43 | { 44 | "polynomialPayoutCurvePiece": { 45 | "payoutPoints": [ 46 | { 47 | "eventOutcome": 60000, 48 | "outcomePayout": 200000000, 49 | "extraPrecision": 0 50 | }, 51 | { 52 | "eventOutcome": 1048575, 53 | "outcomePayout": 200000000, 54 | "extraPrecision": 0 55 | } 56 | ] 57 | } 58 | } 59 | ] 60 | }, 61 | "roundingIntervals": { 62 | "intervals": [ 63 | { 64 | "beginInterval": 0, 65 | "roundingMod": 1 66 | } 67 | ] 68 | }, 69 | "differenceParams": null, 70 | "oracleNumericInfos": { 71 | "base": 2, 72 | "nbDigits": [20] 73 | } 74 | } 75 | }, 76 | "oracles": { 77 | "publicKeys": [ 78 | "0d829c1cc556aa59060df5a9543c5357199ace5db9bcd5a8ddd6ee2fc7b6d174" 79 | ], 80 | "eventId": "btcusd1707120297", 81 | "threshold": 1 82 | } 83 | } 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /dlc-manager/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [0.8.0] - 2025-12-13 8 | 9 | ### Changed 10 | - use `bitcoin::Amount` instead of `u64` for amounts (@bennyhodl) 11 | 12 | ## [0.5.0] - 2024-07-11 13 | 14 | ### Added 15 | - support for `no-std` 16 | - option to skip channel checks 17 | - possibility to pass oracle announcements when offering a contract 18 | - possibility to manually close contract 19 | - possibility to manally handle counter party closing of contract 20 | - `get_new_change_address` for `Wallet` trait 21 | - ability to reject channel offers 22 | 23 | ### Changed 24 | - reject contract and channel offers when the id already exists 25 | - implement `std::error::Error` for `Error` structs 26 | - update bitcoin, lightning and secp256k1_zkp dependencies 27 | - `fee_rate` parameter is not optional anymore in `get_utxo_for_amount` of `Wallet` trait 28 | - `Signer::sign_tx_input` changed to `Signer::sign_psbt_input` 29 | - Use a `ContractSignerProvider` for generating signers for contracts 30 | - Remove usage of `global-context` feature of secp256k1_zkp 31 | 32 | ### Fixed 33 | - validation of hyperbola parameters 34 | - ensure that payouts are not rounded up above the collateral 35 | - load channel monitor from storage when it exists 36 | - floating point arithmetic triggering payout computation errors 37 | - overflow bug in payout curve 38 | - added timeout on `RenewOffer` state 39 | - issue with channel protocol 40 | 41 | 42 | ## [0.4.0] - 2023-02-06 43 | 44 | ### Added 45 | - support for DLC channels 46 | 47 | ### Changed 48 | - remove event maturity from `ContractInput`. The maturity is now computed from the oracle event information. 49 | - closed contract data is now pruned geatly reducing storage usage. 50 | 51 | ### Fixed 52 | - better validation of rounding intervals 53 | - better validation of `ContractInput` and contract offers. 54 | 55 | ## [0.3.0] - 2022-10-28 56 | 57 | ### Changed 58 | - `Contract` enum now has a `Pre-Closed` state. 59 | - `dlc` crate version update. 60 | 61 | ## [0.2.0] - 2022-06-06 62 | 63 | ### Added 64 | - `parallel` feature for computing anticipation points in parallel. 65 | - signing of transaction inputs was added to the `Wallet` trait. 66 | - support for contracts with multi oracle with varying number of digits. 67 | 68 | ### Changed 69 | - serialization format was changed according to update of the DLC specifications. 70 | - improved validation of payout curves. 71 | - validation of node id on manager callbacks. 72 | - validation of received contract offers. 73 | 74 | ### Fixed 75 | - computation of contract id according to the DLC specifications. 76 | - conversion of hyperbola curves. 77 | -------------------------------------------------------------------------------- /dlc-messages/src/test_inputs/dlc_fee_test_scripts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "byteLen": 0, 4 | "script": "", 5 | "description": "empty" 6 | }, 7 | { 8 | "byteLen": 22, 9 | "script": "00146f47307cd1d7e61b89a6fe24e660714d31e4aca6", 10 | "description": "p2wpkh spk" 11 | }, 12 | { 13 | "byteLen": 25, 14 | "script": "76a914a5745456754abf104d8d1a8852f9ef4cbb0af3ee88ac", 15 | "description": "p2pkh spk" 16 | }, 17 | { 18 | "byteLen": 34, 19 | "script": "0020e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 20 | "description": "p2wsh spk" 21 | }, 22 | { 23 | "byteLen": 35, 24 | "script": "21022a5c418a642d70ab3f21029f510931a5af44617d58fcc50195acc136ec90b75cac", 25 | "description": "p2pk spk" 26 | }, 27 | { 28 | "byteLen": 71, 29 | "script": "522102a48558c126c5e3eda2188e743ab31cf9b3e956bb5cc6b9a9eeceb17c69ccffc42103f9be793aa26af2acb555ced3702e360e3c65b8c040c0fc0cf85844bce904cf7452ae", 30 | "description": "2-of-2 multisig spk" 31 | }, 32 | { 33 | "byteLen": 173, 34 | "script": "532103828103a5eb0a8663fffd122ccc52ab7705197686cb09abc8a3d25bdae7426a7a2102fdfc0fbf0148587cb076bbeea390fad9f110ef4b5f5c916b68f0b846ccd8892d2102ab454385715ca02c7cc3aad97b202fd84375906612798b88d386e1c9a7e5bb5421020af3417e4c0a9ddd5a0adbbf460ca05301a9a13d61dcd7ac8bf43b49f06d4d2c21034f5385fdeaaaa725231983765e7fe4c1dbab5c54c051b02b1d4b68c57b8eb67555ae", 35 | "description": "3-of-5 multisig spk" 36 | }, 37 | { 38 | "byteLen": 107, 39 | "script": "02473045022100dc049370a3e1e2f1c30976f97b503ce00e840b10603cf7da86cbbb7a3362501302206104618664922452bfbf40951deb1cccba49f659e3a082761e360d1e1f6abff921029a5276b84f01538373a67033942bb9c4078d8ee2cb72b6dd148fc25e075109cf", 40 | "description": "p2wpkh (low R) witness" 41 | }, 42 | { 43 | "byteLen": 108, 44 | "script": "024830460221009efb9e799b6ddbad4040bf6ae214e3320064e68ccba81dc5a19c516eb0cb8148022100dc049370a3e1e2f1c30976f97b503ce00e840b10603cf7da86cbbb7a336250132103b000bc3cae94b64f26375a8f5d5700f320beeeeb19f82cbb1494cd22f9bf6325", 45 | "description": "p2wpkh (high R) witness" 46 | }, 47 | { 48 | "byteLen": 133, 49 | "script": "03473045022100dc049370a3e1e2f1c30976f97b503ce00e840b10603cf7da86cbbb7a3362501302206104618664922452bfbf40951deb1cccba49f659e3a082761e360d1e1f6abff92102d96d94954612b0ca242247baa8f09ebc4a98f9d4fe335e1b2d1efcecc4ec2f351976a9141969d7b6c106a848d1330652290cd128705d28da88ac", 50 | "description": "p2wsh(p2pkh) witness" 51 | }, 52 | { 53 | "byteLen": 218, 54 | "script": "0400473045022100dc049370a3e1e2f1c30976f97b503ce00e840b10603cf7da86cbbb7a3362501302206104618664922452bfbf40951deb1cccba49f659e3a082761e360d1e1f6abff94730450221009efb9e799b6ddbad4040bf6ae214e3320064e68ccba81dc5a19c516eb0cb8148022023fb6c8f5c1e1d0e3cf6890684afc31eac2ad1d64f0ba8613906a3129cd3f12e47522102a48558c126c5e3eda2188e743ab31cf9b3e956bb5cc6b9a9eeceb17c69ccffc42103f9be793aa26af2acb555ced3702e360e3c65b8c040c0fc0cf85844bce904cf7452ae", 55 | "description": "p2wsh(2-of-2 multsig) witness" 56 | } 57 | ] 58 | -------------------------------------------------------------------------------- /bitcoin-test-utils/src/rpc_helpers.rs: -------------------------------------------------------------------------------- 1 | use bitcoincore_rpc::{Auth, Client, RpcApi}; 2 | use bitcoincore_rpc_json::AddressType; 3 | use std::env; 4 | 5 | pub const OFFER_PARTY: &str = "alice"; 6 | pub const ACCEPT_PARTY: &str = "bob"; 7 | pub const SINK: &str = "sink"; 8 | 9 | fn rpc_base() -> String { 10 | let host = env::var("BITCOIND_HOST").unwrap_or_else(|_| "localhost".to_owned()); 11 | format!("http://{}:18443", host) 12 | } 13 | 14 | pub fn get_new_wallet_rpc( 15 | default_rpc: &Client, 16 | wallet_name: &str, 17 | auth: Auth, 18 | ) -> Result { 19 | let wallet_list = { 20 | let mut retry_count = 20; 21 | loop { 22 | if let Ok(wallets) = default_rpc.list_wallets() { 23 | break wallets; 24 | } 25 | std::thread::sleep(std::time::Duration::from_millis(200)); 26 | if retry_count == 0 { 27 | panic!("Could not get wallet list."); 28 | } 29 | retry_count -= 1; 30 | } 31 | }; 32 | if !wallet_list.contains(&wallet_name.to_owned()) { 33 | default_rpc.create_wallet(wallet_name, Some(false), None, None, None)?; 34 | } 35 | let rpc_url = format!("{}/wallet/{}", rpc_base(), wallet_name); 36 | Client::new(&rpc_url, auth) 37 | } 38 | 39 | pub fn init_clients() -> (Client, Client, Client) { 40 | let auth = Auth::UserPass( 41 | "testuser".to_string(), 42 | "lq6zequb-gYTdF2_ZEUtr8ywTXzLYtknzWU4nV8uVoo=".to_string(), 43 | ); 44 | let rpc = Client::new(&rpc_base(), auth.clone()).unwrap(); 45 | 46 | // Deals with wallet loading error. 47 | let mut retry_count = 0; 48 | std::thread::sleep(std::time::Duration::from_millis(100)); 49 | let offer_rpc = loop { 50 | match get_new_wallet_rpc(&rpc, OFFER_PARTY, auth.clone()) { 51 | Ok(rpc) => break rpc, 52 | Err(e) => { 53 | if retry_count < 20 { 54 | retry_count += 1; 55 | std::thread::sleep(std::time::Duration::from_millis(100)); 56 | } else { 57 | panic!("{}", e); 58 | } 59 | } 60 | }; 61 | }; 62 | 63 | let accept_rpc = get_new_wallet_rpc(&rpc, ACCEPT_PARTY, auth.clone()).unwrap(); 64 | let sink_rpc = get_new_wallet_rpc(&rpc, SINK, auth).unwrap(); 65 | 66 | let offer_address = offer_rpc 67 | .get_new_address(None, Some(AddressType::Bech32)) 68 | .unwrap() 69 | .assume_checked(); 70 | let accept_address = accept_rpc 71 | .get_new_address(None, Some(AddressType::Bech32)) 72 | .unwrap() 73 | .assume_checked(); 74 | let sink_address = sink_rpc 75 | .get_new_address(None, Some(AddressType::Bech32)) 76 | .unwrap() 77 | .assume_checked(); 78 | 79 | sink_rpc.generate_to_address(1, &offer_address).unwrap(); 80 | sink_rpc.generate_to_address(1, &accept_address).unwrap(); 81 | sink_rpc.generate_to_address(101, &sink_address).unwrap(); 82 | 83 | (offer_rpc, accept_rpc, sink_rpc) 84 | } 85 | -------------------------------------------------------------------------------- /sample/tests/cli_tests.rs: -------------------------------------------------------------------------------- 1 | use std::{process::Command, thread::sleep, time::Duration}; 2 | 3 | use assert_cmd::cargo::cargo_bin; 4 | use dlc_manager::contract::contract_input::ContractInput; 5 | use rexpect::session::{spawn_command, PtySession}; 6 | 7 | #[test] 8 | #[ignore] 9 | fn sample_cli_test() { 10 | let contract_str = include_str!("../examples/contracts/numerical_contract_input.json"); 11 | let mut contract: ContractInput = serde_json::from_str(&contract_str).unwrap(); 12 | let time_now = std::time::SystemTime::now(); 13 | let unix_time = (time_now 14 | .duration_since(std::time::SystemTime::UNIX_EPOCH) 15 | .unwrap() 16 | + Duration::new(300, 0)) 17 | .as_secs(); 18 | contract.contract_infos[0].oracles.event_id = format!("btcusd{}", unix_time); 19 | 20 | let alice_config_str = include_str!("../examples/configurations/alice.yml"); 21 | let bob_config_str = include_str!("../examples/configurations/bob.yml"); 22 | std::fs::write( 23 | "./numerical_contract_input.json", 24 | serde_json::to_string(&contract).unwrap(), 25 | ) 26 | .unwrap(); 27 | std::fs::write("./alice.yml", alice_config_str).unwrap(); 28 | std::fs::write("./bob.yml", bob_config_str).unwrap(); 29 | 30 | let bin_path = cargo_bin("sample"); 31 | let mut command = Command::new(bin_path.to_str().unwrap()); 32 | command.arg("./alice.yml"); 33 | let mut alice_cli = spawn_command(command, Some(5000)).unwrap(); 34 | 35 | alice_cli.exp_regex("[a-f0-9]{66}").unwrap(); 36 | alice_cli.exp_regex("> $").unwrap(); 37 | 38 | let mut command = Command::new(bin_path.to_str().unwrap()); 39 | command.arg("./bob.yml"); 40 | let mut bob_cli = spawn_command(command, Some(5000)).unwrap(); 41 | 42 | let (_, bob_ip) = bob_cli.exp_regex("[a-f0-9]{66}").unwrap(); 43 | bob_cli.exp_regex("> $").unwrap(); 44 | 45 | alice_cli 46 | .send_line(&format!( 47 | "offercontract {}@127.0.0.1:9001 ./numerical_contract_input.json", 48 | bob_ip 49 | )) 50 | .unwrap(); 51 | 52 | alice_cli.exp_char('>').unwrap(); 53 | 54 | std::thread::sleep(std::time::Duration::from_secs(5)); 55 | 56 | try_send_until(&mut bob_cli, "listoffers", "Offer"); 57 | 58 | let (_, offer_id) = bob_cli.exp_regex("[a-f0-9]{64}").unwrap(); 59 | 60 | bob_cli 61 | .send_line(&format!("acceptoffer {}", offer_id)) 62 | .unwrap(); 63 | bob_cli.exp_char('>').unwrap(); 64 | 65 | try_send_until(&mut alice_cli, "listcontracts", "Signed contract"); 66 | alice_cli.exp_char('>').unwrap(); 67 | 68 | try_send_until(&mut bob_cli, "listcontracts", "Signed contract"); 69 | bob_cli.exp_char('>').unwrap(); 70 | } 71 | 72 | fn try_send_until(session: &mut PtySession, to_send: &str, expected: &str) { 73 | const RETRY: u8 = 5; 74 | 75 | for _ in 0..RETRY { 76 | session.send_line(to_send).unwrap(); 77 | if let Ok(_) = session.exp_string(expected) { 78 | return; 79 | } 80 | sleep(Duration::from_secs(1)); 81 | } 82 | 83 | panic!("Did not receive expected output after {} tries", RETRY); 84 | } 85 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_contract.json: -------------------------------------------------------------------------------- 1 | {"protocolVersion":1,"contractFlags":0,"chainHash":"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f","temporaryContractId":"f6a1b2841c93db06e94200b227bb4bdea83068efa557d68e14775237cbaab56a","contractInfo":{"singleContractInfo":{"totalCollateral":101000000,"contractInfo":{"contractDescriptor":{"numericOutcomeContractDescriptor":{"numDigits":10,"payoutFunction":{"payoutFunctionPieces":[{"endPoint":{"eventOutcome":0,"outcomePayout":0,"extraPrecision":0},"payoutCurvePiece":{"polynomialPayoutCurvePiece":{"payoutPoints":[{"eventOutcome":3,"outcomePayout":90000000,"extraPrecision":0}]}}},{"endPoint":{"eventOutcome":5,"outcomePayout":101000000,"extraPrecision":0},"payoutCurvePiece":{"polynomialPayoutCurvePiece":{"payoutPoints":[]}}}],"lastEndpoint":{"eventOutcome":1023,"outcomePayout":101000000,"extraPrecision":0}},"roundingIntervals":{"intervals":[{"beginInterval":0,"roundingMod":1}]}}},"oracleInfo":{"single":{"oracleAnnouncement":{"announcementSignature":"18e18de8b3547e210addd32589db9520286f55c0c18510c67bb6f8ea66b05154b84c6ec0075e3623f886b7e2bc623b7df25e1bc25d1cc87c622b28f0ae526664","oraclePublicKey":"1d524d2753a36ebe340af67370f78219b4dbb6f56d2f96b3b21eaabec6f4a114","oracleEvent":{"oracleNonces":["bc927a2c8bf43c9d208e679848ffaf95d178fdbd2e29d1c66668f21dd75149e8","9ed74e19c1d532f5127829b7d9f183e0738ad084485428b53a7fe0c50f2efe5e","f44733d1129d0cd9253124749f8cff2c7e7eecd79888a4a015d3e3ad153ef282","f4f39e5733bfc5ca18530eb444419b31d9dc0ec938502615c33f2b0b7c05ac71","930991374fbf6b9a49e5e16fa3c5c39638af58f5a4c55682a93b2b940502e7bf","e3af3b59907c349d627e3f4f20125bdc1e979cac41ee82ef0a184000c79e904b","0b95d4335713752329a1791b963d526c0a49873bbbfcad9e1c03881508b2a801","48776cc1e3b8f3ff7fd6226ea2df5607787913468a1c0faad4ff315b7cf3b41d","0b39b0e1a14f5f50cb05f0a6d8e7c082f75e9fe386006727af933ce4d273a76f","479a38e13c1622bfd53299ee67680d7a0edd3fed92223e3a878c8d010fcc1a2d"],"eventMaturityEpoch":1623133104,"eventDescriptor":{"digitDecompositionEvent":{"base":2,"isSigned":false,"unit":"sats/sec","precision":0,"nbDigits":10}},"eventId":"Test"}}}}}}},"fundingPubkey":"02556021f6abda2ae7a74d38a4e4a3b00c1dd648db96397dcd8642c3d0b0b139d1","payoutSpk":"0014430af74f2f9dc88729fd02eaeb946fc161e2be1e","payoutSerialId":8165863461276958928,"offerCollateral":90000000,"fundingInputs":[{"inputSerialId":11632658032743242199,"prevTx":"02000000000101e79f7a30bb35206060eb09a99b6956bcdc7a1767b310c8dfde3595c69246a60e0000000000feffffff0200c2eb0b000000001600142a416c1e5f5e78bc6c518294fd1dd86b40eed2d77caf953e000000001600148e56705661334df89b2c1c7c4e41da9cef9eb38e0247304402201491f05ebe196b333420cbab3e7e7f3e431bfe91a42730cef9c6e64b0e8ff62302202c5fc79abbdb0a1c8ad422dbb97a54693feedc580f0cb7a62bdadaecbfc4f9430121035f57172a38f35f29f4357dcc2d24ea8e72638cf43190e4fdcb3f0ace215cfd5602020000","prevTxVout":0,"sequence":4294967295,"maxWitnessLen":107,"redeemScript":""}],"changeSpk":"001441ca183be469eab996f34ed31197a96b57f6050e","changeSerialId":16919534260907952016,"fundOutputSerialId":5054305248376932341,"feeRatePerVb":2,"cetLocktime":1623133103,"refundLocktime":1623737904, "keysId":[16, 175, 248, 204, 229, 173, 124, 107, 34, 204, 75, 138, 154, 151, 193, 8, 142, 116, 180, 156, 90, 229, 80, 251, 77, 5, 215, 234, 70, 117, 68, 208]} 2 | -------------------------------------------------------------------------------- /dlc-trie/src/combination_iterator.rs: -------------------------------------------------------------------------------- 1 | //! # Combination Iterator 2 | //! Utility struct and functions too support iterating though all possible 3 | //! t combinations of n elements (where t >= n). 4 | 5 | /// Structure to use to support iteration through all possible t combinations 6 | /// of n elements. 7 | pub struct CombinationIterator { 8 | selector: Vec, 9 | pub(crate) nb_selected: usize, 10 | pub(crate) nb_elements: usize, 11 | is_init: bool, 12 | } 13 | 14 | impl CombinationIterator { 15 | /// Creates a new combination iterator for a collection of `nb_elements` 16 | /// where each combination includes `nb_selected` elements. Panics if 17 | /// `nb_elements < nb_selected`. 18 | pub fn new(nb_elements: usize, nb_selected: usize) -> CombinationIterator { 19 | assert!(nb_elements >= nb_selected); 20 | 21 | let selector = (0..nb_selected).collect(); 22 | CombinationIterator { 23 | selector, 24 | nb_elements, 25 | nb_selected, 26 | is_init: false, 27 | } 28 | } 29 | 30 | /// Returns the index of the provided combination if part of the set of 31 | /// combinations produced by the iterator, None otherwise. 32 | pub fn get_index_for_combination(self, combination: &[usize]) -> Option { 33 | for (i, cur) in self.enumerate() { 34 | if combination == cur { 35 | return Some(i); 36 | } 37 | } 38 | 39 | None 40 | } 41 | } 42 | 43 | impl Iterator for CombinationIterator { 44 | type Item = Vec; 45 | fn next(&mut self) -> Option { 46 | if !self.is_init { 47 | self.is_init = true; 48 | return Some(self.selector.clone()); 49 | } 50 | 51 | let last_index = self.nb_selected - 1; 52 | let mut cur_index = last_index; 53 | while cur_index > 0 54 | && self.selector[cur_index] == self.nb_elements - 1 - (last_index - cur_index) 55 | { 56 | cur_index -= 1; 57 | } 58 | 59 | self.selector[cur_index] += 1; 60 | cur_index += 1; 61 | 62 | while cur_index <= last_index { 63 | self.selector[cur_index] = self.selector[cur_index - 1] + 1; 64 | cur_index += 1; 65 | } 66 | 67 | if self.selector[0] == self.nb_elements - self.nb_selected + 1 { 68 | return None; 69 | } 70 | 71 | Some(self.selector.clone()) 72 | } 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | use super::*; 78 | 79 | #[test] 80 | fn generate_combinations_test() { 81 | let combination_iterator = CombinationIterator::new(4, 3); 82 | let expected = vec![vec![0, 1, 2], vec![0, 1, 3], vec![0, 2, 3], vec![1, 2, 3]]; 83 | 84 | for (i, cur) in combination_iterator.enumerate() { 85 | assert_eq!(cur, expected[i]); 86 | } 87 | } 88 | 89 | #[test] 90 | fn get_combination_index_test() { 91 | let combination_iterator = CombinationIterator::new(4, 3); 92 | 93 | assert_eq!( 94 | 2, 95 | combination_iterator 96 | .get_index_for_combination(&[0, 2, 3]) 97 | .expect("Could not find combination") 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /dlc-manager/src/channel/accepted_channel.rs: -------------------------------------------------------------------------------- 1 | //! # Structure and methods for channels that have been accepted. 2 | 3 | use bitcoin::{ScriptBuf, Transaction}; 4 | use dlc_messages::channel::AcceptChannel; 5 | use secp256k1_zkp::{EcdsaAdaptorSignature, PublicKey}; 6 | 7 | use crate::{contract::accepted_contract::AcceptedContract, ChannelId, ContractId}; 8 | 9 | use super::party_points::PartyBasePoints; 10 | 11 | /// A [`super::Channel`] is in `Accepted` state when the accept party 12 | /// accepts the [`super::offered_channel::OfferedChannel`]. 13 | #[derive(Clone, Debug)] 14 | pub struct AcceptedChannel { 15 | /// The [`secp256k1_zkp::PublicKey`] of the node of the offer party. 16 | pub counter_party: PublicKey, 17 | /// The id of the initial contract in the channel. 18 | pub accepted_contract_id: ContractId, 19 | /// The set of [`super::party_points::PartyBasePoints`] that will be used by 20 | /// the offer party throughout the lifetime of the channel. 21 | pub offer_base_points: PartyBasePoints, 22 | /// The set of [`super::party_points::PartyBasePoints`] that will be used by 23 | /// the accept party throughout the lifetime of the channel. 24 | pub accept_base_points: PartyBasePoints, 25 | /// The initial per update point of the offer party. 26 | pub offer_per_update_point: PublicKey, 27 | /// The initial per update point of the accept party. 28 | pub accept_per_update_point: PublicKey, 29 | /// The buffer transaction for the initial contract in the channel. 30 | pub buffer_transaction: Transaction, 31 | /// The script pubkey of the buffer transaction output. 32 | pub buffer_script_pubkey: ScriptBuf, 33 | /// The temporary id of the channel. 34 | pub temporary_channel_id: ChannelId, 35 | /// The actual id of the channel. 36 | pub channel_id: ChannelId, 37 | /// The image of the per update seed used by the accept party. 38 | pub accept_per_update_seed: PublicKey, 39 | /// The accept party adaptor signature for the buffer transaction. 40 | pub accept_buffer_adaptor_signature: EcdsaAdaptorSignature, 41 | } 42 | 43 | impl AcceptedChannel { 44 | pub(crate) fn get_accept_channel_msg( 45 | &self, 46 | contract: &AcceptedContract, 47 | buffer_adaptor_signature: &EcdsaAdaptorSignature, 48 | cet_adaptor_signatures: &[EcdsaAdaptorSignature], 49 | ) -> AcceptChannel { 50 | AcceptChannel { 51 | temporary_channel_id: self.temporary_channel_id, 52 | accept_collateral: contract.accept_params.collateral, 53 | funding_pubkey: contract.accept_params.fund_pubkey, 54 | payout_spk: contract.accept_params.payout_script_pubkey.clone(), 55 | payout_serial_id: contract.accept_params.payout_serial_id, 56 | funding_inputs: contract.funding_inputs.clone(), 57 | change_spk: contract.accept_params.change_script_pubkey.clone(), 58 | change_serial_id: contract.accept_params.change_serial_id, 59 | cet_adaptor_signatures: cet_adaptor_signatures.into(), 60 | refund_signature: contract.accept_refund_signature, 61 | negotiation_fields: None, 62 | revocation_basepoint: self.accept_base_points.revocation_basepoint, 63 | publish_basepoint: self.accept_base_points.publish_basepoint, 64 | own_basepoint: self.accept_base_points.own_basepoint, 65 | first_per_update_point: self.accept_per_update_point, 66 | buffer_adaptor_signature: *buffer_adaptor_signature, 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /testconfig/oracle/certs/db/db.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 5 | b4:4b:9e:02:a0:9c:f2:eb 6 | Signature Algorithm: sha256WithRSAEncryption 7 | Issuer: CN=localhost 8 | Validity 9 | Not Before: Feb 3 06:22:22 2023 GMT 10 | Not After : Mar 5 06:22:22 2023 GMT 11 | Subject: CN=localhost 12 | Subject Public Key Info: 13 | Public Key Algorithm: rsaEncryption 14 | RSA Public-Key: (2048 bit) 15 | Modulus: 16 | 00:b8:b3:0c:44:18:91:f1:a1:7f:bf:3a:c9:f2:69: 17 | 3d:4e:0d:eb:69:5c:a9:b4:20:4a:0a:24:0b:fe:aa: 18 | 46:c6:5f:4b:69:d1:fb:93:76:36:bf:6d:d9:bd:e7: 19 | 4c:cb:31:0d:f5:3d:04:12:7c:6a:e0:6b:59:cd:51: 20 | 3a:50:75:54:4c:19:16:07:47:68:00:15:27:b1:22: 21 | 8b:56:71:5f:b6:c0:e7:55:0b:0b:a2:68:a5:5d:b3: 22 | 6f:91:a0:bb:e2:a4:e5:96:66:51:1d:ea:53:6b:01: 23 | 5d:84:a8:16:b2:3d:a4:69:df:51:0e:61:18:e4:b5: 24 | 07:41:27:53:a8:c7:6f:08:45:ae:f6:0e:f0:20:f8: 25 | 16:2c:be:b1:2f:dc:94:b1:da:76:02:ff:7d:4f:cb: 26 | c6:c4:81:52:dc:2c:77:02:18:98:ef:9a:e5:72:c4: 27 | de:74:27:27:8a:f3:e1:52:79:91:10:df:76:6a:aa: 28 | 0b:f4:9a:c9:c3:66:70:06:10:3d:9e:db:07:2b:f6: 29 | 32:df:f7:ec:86:16:b5:03:85:41:d2:e8:2a:94:fd: 30 | 46:4a:c8:8c:21:1b:40:cb:47:52:cf:54:cf:2c:02: 31 | cc:a0:17:f8:94:33:5f:e8:eb:5a:a7:37:c7:29:b6: 32 | 25:ec:4d:51:e5:26:aa:0b:40:b9:a0:ac:12:c7:de: 33 | 1a:c5 34 | Exponent: 65537 (0x10001) 35 | Signature Algorithm: sha256WithRSAEncryption 36 | 8d:8f:96:9d:43:03:64:54:df:1b:cd:cd:3d:8a:cd:a0:bc:ce: 37 | ee:72:57:81:77:8b:ff:5e:a6:db:38:a5:b3:a2:e8:dd:60:e9: 38 | 09:33:40:50:6b:09:54:78:34:ce:d9:63:06:8c:5b:d8:2d:34: 39 | 77:d3:c0:b0:44:83:45:75:9b:37:c9:93:5b:61:0e:c3:c8:e6: 40 | 94:ca:c7:81:58:fa:fe:db:a1:94:61:b5:cd:86:6e:81:72:36: 41 | b1:43:54:c9:73:05:48:b3:91:e0:88:ee:1c:cb:3b:5f:68:de: 42 | 25:06:79:d6:3a:75:8d:4d:c4:e7:7b:f8:71:f0:b4:02:d9:e5: 43 | 3f:e3:7d:fe:f7:4f:b0:3a:2f:03:14:92:db:b1:19:ed:5e:ca: 44 | 79:fe:68:e8:07:60:28:00:0f:83:f4:f6:21:91:88:e0:2c:46: 45 | 96:7a:ef:88:9d:05:67:b1:ce:a1:df:9d:f8:09:df:c7:71:71: 46 | 4b:d7:aa:f0:69:a9:de:13:3b:f8:f0:c8:f4:c3:33:c7:3e:60: 47 | e3:6f:86:ee:49:5f:bb:80:81:bb:73:a5:50:da:56:b2:47:c6: 48 | d2:f8:80:12:09:96:0a:ee:7a:65:ae:b9:9d:83:55:da:a6:f4: 49 | 7d:4f:27:f2:eb:9b:1a:10:38:1c:6c:40:be:e9:64:55:d9:9e: 50 | ec:36:0d:33 51 | -----BEGIN CERTIFICATE----- 52 | MIICpDCCAYwCCQC0S54CoJzy6zANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 53 | b2NhbGhvc3QwHhcNMjMwMjAzMDYyMjIyWhcNMjMwMzA1MDYyMjIyWjAUMRIwEAYD 54 | VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4 55 | swxEGJHxoX+/OsnyaT1ODetpXKm0IEoKJAv+qkbGX0tp0fuTdja/bdm950zLMQ31 56 | PQQSfGrga1nNUTpQdVRMGRYHR2gAFSexIotWcV+2wOdVCwuiaKVds2+RoLvipOWW 57 | ZlEd6lNrAV2EqBayPaRp31EOYRjktQdBJ1Oox28IRa72DvAg+BYsvrEv3JSx2nYC 58 | /31Py8bEgVLcLHcCGJjvmuVyxN50JyeK8+FSeZEQ33Zqqgv0msnDZnAGED2e2wcr 59 | 9jLf9+yGFrUDhUHS6CqU/UZKyIwhG0DLR1LPVM8sAsygF/iUM1/o61qnN8cptiXs 60 | TVHlJqoLQLmgrBLH3hrFAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAI2Plp1DA2RU 61 | 3xvNzT2KzaC8zu5yV4F3i/9epts4pbOi6N1g6QkzQFBrCVR4NM7ZYwaMW9gtNHfT 62 | wLBEg0V1mzfJk1thDsPI5pTKx4FY+v7boZRhtc2GboFyNrFDVMlzBUizkeCI7hzL 63 | O19o3iUGedY6dY1NxOd7+HHwtALZ5T/jff73T7A6LwMUktuxGe1eynn+aOgHYCgA 64 | D4P09iGRiOAsRpZ674idBWexzqHfnfgJ38dxcUvXqvBpqd4TO/jwyPTDM8c+YONv 65 | hu5JX7uAgbtzpVDaVrJHxtL4gBIJlgruemWuuZ2DVdqm9H1PJ/LrmxoQOBxsQL7p 66 | ZFXZnuw2DTM= 67 | -----END CERTIFICATE----- 68 | -------------------------------------------------------------------------------- /dlc-manager/src/channel/utils.rs: -------------------------------------------------------------------------------- 1 | //! # 2 | 3 | use bitcoin::hashes::HashEngine; 4 | use bitcoin::hashes::{sha256::Hash as Sha256, Hash}; 5 | use secp256k1_zkp::{PublicKey, Scalar, Secp256k1, SecretKey}; 6 | 7 | /// Derives a public key from a `base_point` and a `per_commitment_point` as described in BOLT-3 8 | /// (https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation). 9 | /// 10 | /// Taken from a previous version of ldk as it was refactored into something less practical to use 11 | /// externally. 12 | pub(crate) fn derive_public_key( 13 | secp_ctx: &Secp256k1, 14 | per_commitment_point: &PublicKey, 15 | base_point: &PublicKey, 16 | ) -> PublicKey { 17 | let mut sha = Sha256::engine(); 18 | sha.input(&per_commitment_point.serialize()); 19 | sha.input(&base_point.serialize()); 20 | let res = Sha256::from_engine(sha).to_byte_array(); 21 | 22 | let hashkey = PublicKey::from_secret_key( 23 | secp_ctx, 24 | &SecretKey::from_slice(&res) 25 | .expect("Hashes should always be valid keys unless SHA-256 is broken"), 26 | ); 27 | base_point.combine(&hashkey) 28 | .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.") 29 | } 30 | 31 | /// Derives a per-commitment-transaction revocation public key from its constituent parts. This is 32 | /// the public equivalent of derive_private_revocation_key - using only public keys to derive a 33 | /// public key instead of private keys. 34 | /// 35 | /// Only the cheating participant owns a valid witness to propagate a revoked 36 | /// commitment transaction, thus per_commitment_point always come from cheater 37 | /// and revocation_base_point always come from punisher, which is the broadcaster 38 | /// of the transaction spending with this key knowledge. 39 | /// 40 | /// Note that this is infallible iff we trust that at least one of the two input keys are randomly 41 | /// generated (ie our own). 42 | /// 43 | /// Taken from a previous version of ldk as it was refactored into something less practical to use 44 | /// externally. 45 | pub fn derive_public_revocation_key( 46 | secp_ctx: &Secp256k1, 47 | per_commitment_point: &PublicKey, 48 | countersignatory_revocation_base_point: &PublicKey, 49 | ) -> PublicKey { 50 | let rev_append_commit_hash_key = { 51 | let mut sha = Sha256::engine(); 52 | sha.input(&countersignatory_revocation_base_point.serialize()); 53 | sha.input(&per_commitment_point.serialize()); 54 | 55 | Sha256::from_engine(sha).to_byte_array() 56 | }; 57 | let commit_append_rev_hash_key = { 58 | let mut sha = Sha256::engine(); 59 | sha.input(&per_commitment_point.serialize()); 60 | sha.input(&countersignatory_revocation_base_point.serialize()); 61 | 62 | Sha256::from_engine(sha).to_byte_array() 63 | }; 64 | 65 | let countersignatory_contrib = countersignatory_revocation_base_point 66 | .mul_tweak( 67 | secp_ctx, 68 | &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap(), 69 | ) 70 | .expect( 71 | "Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs", 72 | ); 73 | let broadcaster_contrib = per_commitment_point 74 | .mul_tweak( 75 | secp_ctx, 76 | &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap(), 77 | ) 78 | .expect( 79 | "Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs", 80 | ); 81 | countersignatory_contrib.combine(&broadcaster_contrib) 82 | .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.") 83 | } 84 | -------------------------------------------------------------------------------- /dlc/src/secp_utils.rs: -------------------------------------------------------------------------------- 1 | //! Crypto utilities providing necessary DLC specific functions not available in 2 | //! rust-secp256k1 or rust-secp256k1-zkp. 3 | 4 | use crate::Error; 5 | use bitcoin::hashes::{sha256t_hash_newtype, Hash}; 6 | use core::ptr; 7 | use secp256k1_sys::{ 8 | types::{c_int, c_uchar, c_void, size_t}, 9 | CPtr, SchnorrSigExtraParams, 10 | }; 11 | use secp256k1_zkp::{ 12 | schnorr::Signature as SchnorrSignature, Keypair, Message, PublicKey, Scalar, Secp256k1, 13 | Signing, Verification, XOnlyPublicKey, 14 | }; 15 | 16 | const BIP340_MIDSTATE: [u8; 32] = [ 17 | 0x9c, 0xec, 0xba, 0x11, 0x23, 0x92, 0x53, 0x81, 0x11, 0x67, 0x91, 0x12, 0xd1, 0x62, 0x7e, 0x0f, 18 | 0x97, 0xc8, 0x75, 0x50, 0x00, 0x3c, 0xc7, 0x65, 0x90, 0xf6, 0x11, 0x64, 0x33, 0xe9, 0xb6, 0x6a, 19 | ]; 20 | 21 | sha256t_hash_newtype! { 22 | /// BIP340 Hash Tag 23 | pub struct BIP340HashTag = raw(BIP340_MIDSTATE, 64); 24 | 25 | /// BIP340 Hash 26 | #[hash_newtype(backward)] 27 | pub struct BIP340Hash(_); 28 | } 29 | 30 | /// Create a Schnorr signature using the provided nonce instead of generating one. 31 | pub fn schnorrsig_sign_with_nonce( 32 | secp: &Secp256k1, 33 | msg: &Message, 34 | keypair: &Keypair, 35 | nonce: &[u8; 32], 36 | ) -> SchnorrSignature { 37 | unsafe { 38 | let mut sig = [0u8; secp256k1_zkp::constants::SCHNORR_SIGNATURE_SIZE]; 39 | let extra_params = 40 | SchnorrSigExtraParams::new(Some(constant_nonce_fn), nonce.as_c_ptr() as *const c_void); 41 | assert_eq!( 42 | 1, 43 | secp256k1_sys::secp256k1_schnorrsig_sign_custom( 44 | secp.ctx().as_ref(), 45 | sig.as_mut_c_ptr(), 46 | msg.as_c_ptr(), 47 | 32_usize, 48 | keypair.as_c_ptr(), 49 | &extra_params, 50 | ) 51 | ); 52 | 53 | SchnorrSignature::from_slice(&sig).unwrap() 54 | } 55 | } 56 | 57 | /// Compute a signature point for the given public key, nonce and message. 58 | pub fn schnorrsig_compute_sig_point( 59 | secp: &Secp256k1, 60 | pubkey: &XOnlyPublicKey, 61 | nonce: &XOnlyPublicKey, 62 | message: &Message, 63 | ) -> Result { 64 | let hash = create_schnorr_hash(message, nonce, pubkey); 65 | let pk = schnorr_pubkey_to_pubkey(pubkey)?; 66 | let scalar = Scalar::from_be_bytes(hash).unwrap(); 67 | let tweaked = pk.mul_tweak(secp, &scalar)?; 68 | let npk = schnorr_pubkey_to_pubkey(nonce)?; 69 | Ok(npk.combine(&tweaked)?) 70 | } 71 | 72 | /// Decompose a bip340 signature into a nonce and a secret key (as byte array) 73 | pub fn schnorrsig_decompose( 74 | signature: &SchnorrSignature, 75 | ) -> Result<(XOnlyPublicKey, &[u8]), Error> { 76 | let bytes = signature.as_ref(); 77 | Ok((XOnlyPublicKey::from_slice(&bytes[0..32])?, &bytes[32..64])) 78 | } 79 | 80 | extern "C" fn constant_nonce_fn( 81 | nonce32: *mut c_uchar, 82 | _msg32: *const c_uchar, 83 | _msg_len: size_t, 84 | _key32: *const c_uchar, 85 | _xonly_pk32: *const c_uchar, 86 | _algo16: *const c_uchar, 87 | _algo_len: size_t, 88 | data: *mut c_void, 89 | ) -> c_int { 90 | unsafe { 91 | ptr::copy_nonoverlapping(data as *const c_uchar, nonce32, 32); 92 | } 93 | 1 94 | } 95 | 96 | fn create_schnorr_hash(msg: &Message, nonce: &XOnlyPublicKey, pubkey: &XOnlyPublicKey) -> [u8; 32] { 97 | let mut buf = Vec::::new(); 98 | buf.extend(nonce.serialize()); 99 | buf.extend(pubkey.serialize()); 100 | buf.extend(msg.as_ref().to_vec()); 101 | BIP340Hash::hash(&buf).to_byte_array() 102 | } 103 | 104 | fn schnorr_pubkey_to_pubkey(schnorr_pubkey: &XOnlyPublicKey) -> Result { 105 | let mut buf = Vec::::with_capacity(33); 106 | buf.push(0x02); 107 | buf.extend(schnorr_pubkey.serialize()); 108 | Ok(PublicKey::from_slice(&buf)?) 109 | } 110 | -------------------------------------------------------------------------------- /dlc-manager/src/error.rs: -------------------------------------------------------------------------------- 1 | //! #Error 2 | use std::fmt; 3 | 4 | /// An error code. 5 | #[derive(Debug)] 6 | pub enum Error { 7 | /// Error that occured while converting from DLC message to internal 8 | /// representation. 9 | Conversion(crate::conversion_utils::Error), 10 | /// An IO error. 11 | IOError(lightning::io::Error), 12 | /// Deserialize error 13 | Deserialize(bitcoin::consensus::encode::Error), 14 | /// Some invalid parameters were provided. 15 | InvalidParameters(String), 16 | /// An invalid state was encounter, likely to indicate a bug. 17 | InvalidState(String), 18 | /// An error occurred in the wallet component. 19 | WalletError(Box), 20 | /// An error occurred in the blockchain component. 21 | BlockchainError(String), 22 | /// The storage component encountered an error. 23 | StorageError(String), 24 | /// The oracle component encountered an error. 25 | OracleError(String), 26 | /// An error occurred in the DLC library. 27 | DlcError(dlc::Error), 28 | /// An error occurred in the Secp library. 29 | SecpError(secp256k1_zkp::Error), 30 | /// A computation was out of range. 31 | OutOfRange, 32 | } 33 | 34 | impl fmt::Display for Error { 35 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 36 | match *self { 37 | Error::Conversion(_) => write!(f, "Conversion error"), 38 | Error::IOError(_) => write!(f, "IO error"), 39 | Error::Deserialize(ref s) => write!(f, "Deserialize error: {}", s), 40 | Error::InvalidState(ref s) => write!(f, "Invalid state: {}", s), 41 | Error::InvalidParameters(ref s) => write!(f, "Invalid parameters were provided: {}", s), 42 | Error::WalletError(ref e) => write!(f, "Wallet error {}", e), 43 | Error::BlockchainError(ref s) => write!(f, "Blockchain error {}", s), 44 | Error::StorageError(ref s) => write!(f, "Storage error {}", s), 45 | Error::DlcError(ref e) => write!(f, "Dlc error {}", e), 46 | Error::OracleError(ref s) => write!(f, "Oracle error {}", s), 47 | Error::SecpError(_) => write!(f, "Secp error"), 48 | Error::OutOfRange => write!(f, "Out of range error"), 49 | } 50 | } 51 | } 52 | 53 | impl From for Error { 54 | fn from(e: lightning::io::Error) -> Error { 55 | Error::IOError(e) 56 | } 57 | } 58 | 59 | impl From for Error { 60 | fn from(e: dlc::Error) -> Error { 61 | Error::DlcError(e) 62 | } 63 | } 64 | 65 | impl From for Error { 66 | fn from(e: crate::conversion_utils::Error) -> Error { 67 | Error::Conversion(e) 68 | } 69 | } 70 | 71 | impl From for Error { 72 | fn from(e: secp256k1_zkp::Error) -> Error { 73 | Error::SecpError(e) 74 | } 75 | } 76 | 77 | impl From for Error { 78 | fn from(e: secp256k1_zkp::UpstreamError) -> Error { 79 | Error::SecpError(secp256k1_zkp::Error::Upstream(e)) 80 | } 81 | } 82 | 83 | impl From for Error { 84 | fn from(e: bitcoin::consensus::encode::Error) -> Self { 85 | Error::Deserialize(e) 86 | } 87 | } 88 | 89 | #[cfg(feature = "std")] 90 | impl std::error::Error for Error { 91 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 92 | match self { 93 | Error::Conversion(e) => Some(e), 94 | Error::IOError(e) => Some(e), 95 | Error::Deserialize(e) => Some(e), 96 | Error::InvalidParameters(_) => None, 97 | Error::InvalidState(_) => None, 98 | Error::WalletError(_) => None, 99 | Error::BlockchainError(_) => None, 100 | Error::StorageError(_) => None, 101 | Error::OracleError(_) => None, 102 | Error::DlcError(e) => Some(e), 103 | Error::SecpError(e) => Some(e), 104 | Error::OutOfRange => None, 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | - 'testci/**' 6 | pull_request: 7 | 8 | name: Continuous integration 9 | 10 | jobs: 11 | lint: 12 | name: lint 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 30 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: install clippy 18 | run: rustup component add clippy 19 | - name: Run clippy 20 | run: cargo clippy -- -D warnings 21 | fmt: 22 | name: rustfmt-check 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v4 26 | - name: Install Rust toolchain 27 | uses: dtolnay/rust-toolchain@stable 28 | with: 29 | components: rustfmt 30 | - name: Check format 31 | run: cargo fmt --all --check 32 | no-std: 33 | name: no-std lint 34 | runs-on: ubuntu-latest 35 | timeout-minutes: 30 36 | steps: 37 | - uses: actions/checkout@v4 38 | - name: install clippy 39 | run: rustup component add clippy 40 | - name: Run clippy dlc 41 | run: cargo clippy --no-default-features --features no-std -p dlc -- -D warnings 42 | - name: Run clippy dlc-messages 43 | run: cargo clippy --no-default-features --features no-std -p dlc-messages -- -D warnings 44 | - name: Run clippy dlc-trie 45 | run: cargo clippy --no-default-features --features no-std -p dlc-trie -- -D warnings 46 | unit-tests: 47 | name: unit-tests 48 | runs-on: ubuntu-latest 49 | timeout-minutes: 30 50 | steps: 51 | - uses: actions/checkout@v4 52 | - name: Build 53 | run: cargo build --verbose 54 | - name: Test 55 | run: cargo test --verbose --all-features 56 | 57 | integration_tests_prepare: 58 | runs-on: ubuntu-latest 59 | timeout-minutes: 30 60 | outputs: 61 | matrix: ${{ steps.set-matrix.outputs.matrix }} 62 | steps: 63 | - uses: actions/cache@v3 64 | env: 65 | cache-name: test-cache 66 | with: 67 | path: target/debug/deps 68 | key: test-cache-${{ github.run_id }}-${{ github.run_number }} 69 | - uses: actions/checkout@v4 70 | - id: set-matrix 71 | run: cargo test --no-run && echo "::set-output name=matrix::$(scripts/get_test_list.sh execution manager channel_execution)" 72 | integration_tests: 73 | name: integration-tests 74 | needs: integration_tests_prepare 75 | runs-on: ubuntu-latest 76 | timeout-minutes: 30 77 | strategy: 78 | matrix: 79 | tests: ${{ fromJson(needs.integration_tests_prepare.outputs.matrix) }} 80 | steps: 81 | - uses: actions/checkout@v4 82 | - uses: actions/cache@v3 83 | env: 84 | cache-name: test-cache 85 | with: 86 | path: target/debug/deps 87 | key: test-cache-${{ github.run_id }}-${{ github.run_number }} 88 | - name: Start bitcoin node 89 | run: docker compose up -d 90 | - name: Wait for container to run 91 | run: ./scripts/wait_for_container.sh bitcoin-node 92 | - name: Wait for electrs to be ready 93 | run: ./scripts/wait_for_electrs.sh 94 | - name: Run test 95 | run: RUST_BACKTRACE=1 ${{ matrix.tests }} --ignored 96 | - name: Stop bitcoin node 97 | run: ./scripts/stop_node.sh 98 | sample_test: 99 | name: sample-test 100 | runs-on: ubuntu-latest 101 | timeout-minutes: 30 102 | steps: 103 | - uses: actions/checkout@v4 104 | - name: Start environment 105 | run: docker compose --profile oracle up -d 106 | - name: Wait for container to run 107 | run: ./scripts/wait_for_container.sh oracle-server 108 | - name: Wait for electrs to be ready 109 | run: ./scripts/wait_for_electrs.sh 110 | - name: Create wallets 111 | run: docker exec bitcoin-node /scripts/create_wallets.sh 112 | - name: Run test 113 | run: | 114 | if ! cargo test -- --ignored sample; then 115 | cat sample/dlc_sample_alice/.dlc/logs/logs.txt 116 | cat sample/dlc_sample_bob/.dlc/logs/logs.txt 117 | exit 1 118 | fi 119 | -------------------------------------------------------------------------------- /dlc-messages/src/segmentation/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module used when working with message segmentation. 2 | 3 | use lightning::ln::msgs::DecodeError; 4 | use lightning::ln::wire::Type; 5 | use lightning::util::ser::{Readable, Writeable, Writer}; 6 | 7 | /// The type of the [`SegmentStart`] message. 8 | pub const SEGMENT_START_TYPE: u16 = 42900; 9 | 10 | /// The type of the [`SegmentChunk`] message. 11 | pub const SEGMENT_CHUNK_TYPE: u16 = 42902; 12 | 13 | /// Maximum allowed size by noise protocol: 14 | pub const MAX_DATA_SIZE: usize = 65535; 15 | 16 | // Max data size - 2 for wrapper type - 5 for bigsize length prefix - 2 for nb segments 17 | const MAX_START_DATA_SIZE: usize = 65526; 18 | 19 | // Max data size - 2 for wrapper type - 5 for bigsize length prefix 20 | const MAX_CHUNK_SIZE: usize = 65528; 21 | 22 | const MAX_SEGMENTS: usize = 1000; 23 | 24 | pub mod segment_reader; 25 | 26 | #[cfg_attr( 27 | feature = "use-serde", 28 | derive(serde::Serialize, serde::Deserialize), 29 | serde(rename_all = "camelCase") 30 | )] 31 | #[derive(Clone, Debug, PartialEq, Eq)] 32 | /// Message indicating that an incoming message has been split and needs to be 33 | /// reconstructed. 34 | pub struct SegmentStart { 35 | /// The number of segments into which the large message has been split. 36 | pub nb_segments: u16, 37 | /// The data for the first segment. 38 | pub data: Vec, 39 | } 40 | 41 | impl_dlc_writeable!(SegmentStart, { 42 | (nb_segments, writeable), 43 | (data, writeable) 44 | }); 45 | 46 | impl Type for SegmentStart { 47 | fn type_id(&self) -> u16 { 48 | SEGMENT_START_TYPE 49 | } 50 | } 51 | 52 | #[cfg_attr( 53 | feature = "use-serde", 54 | derive(serde::Serialize, serde::Deserialize), 55 | serde(rename_all = "camelCase") 56 | )] 57 | #[derive(Clone, Debug, PartialEq, Eq)] 58 | /// Message providing a chunk of a split message. 59 | pub struct SegmentChunk { 60 | /// The data to be appended to previously received chunks. 61 | pub data: Vec, 62 | } 63 | 64 | impl_dlc_writeable!(SegmentChunk, { (data, writeable) }); 65 | 66 | impl Type for SegmentChunk { 67 | fn type_id(&self) -> u16 { 68 | SEGMENT_CHUNK_TYPE 69 | } 70 | } 71 | 72 | /// Split the given data into multiple segments, pre-pending the message type 73 | /// to enable decoding on the receiving side. 74 | pub fn get_segments(mut data: Vec, msg_type: u16) -> (SegmentStart, Vec) { 75 | debug_assert!(data.len() > MAX_DATA_SIZE); 76 | 77 | let len_minus_start = data.len() - MAX_START_DATA_SIZE + 2; 78 | let mut nb_segments = (len_minus_start / MAX_CHUNK_SIZE + 1) as u16; 79 | 80 | if len_minus_start % MAX_CHUNK_SIZE != 0 { 81 | nb_segments += 1; 82 | } 83 | 84 | debug_assert!(nb_segments > 1); 85 | 86 | let mut start_data = Vec::with_capacity(MAX_START_DATA_SIZE); 87 | msg_type 88 | .write(&mut start_data) 89 | .expect("to be able to write the type prefix"); 90 | start_data.append(&mut data.drain(..MAX_START_DATA_SIZE - 2).collect()); 91 | 92 | debug_assert_eq!(MAX_START_DATA_SIZE, start_data.len()); 93 | 94 | let segment_start = SegmentStart { 95 | nb_segments, 96 | data: start_data, 97 | }; 98 | 99 | let mut segments = Vec::with_capacity((nb_segments as usize) - 1); 100 | 101 | for _ in 1..(nb_segments as usize) { 102 | let to_take = usize::min(data.len(), MAX_CHUNK_SIZE); 103 | segments.push(SegmentChunk { 104 | data: data.drain(..to_take).collect(), 105 | }); 106 | } 107 | 108 | (segment_start, segments) 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use super::*; 114 | 115 | #[test] 116 | fn test_get_segments() { 117 | let data_size = MAX_START_DATA_SIZE + 2 * MAX_CHUNK_SIZE + 1234; 118 | let mut data = Vec::new(); 119 | data.resize(data_size, 1); 120 | 121 | let (segment_start, segment_chunks) = get_segments(data, 2); 122 | 123 | assert_eq!(4, segment_start.nb_segments); 124 | assert_eq!(MAX_START_DATA_SIZE, segment_start.data.len()); 125 | assert_eq!(3, segment_chunks.len()); 126 | assert_eq!(MAX_CHUNK_SIZE, segment_chunks[0].data.len()); 127 | assert_eq!(MAX_CHUNK_SIZE, segment_chunks[1].data.len()); 128 | assert_eq!(1236, segment_chunks[2].data.len()); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /dlc-manager/src/channel/party_points.rs: -------------------------------------------------------------------------------- 1 | //! # DLC channels use base points used to derive points used to make revocation 2 | //! of states possible. This module contain a structure containing them and methods 3 | //! useful for derivation. 4 | 5 | use super::utils::{derive_public_key, derive_public_revocation_key}; 6 | use bitcoin::PublicKey as BitcoinPublicKey; 7 | use dlc::channel::RevokeParams; 8 | use secp256k1_zkp::{All, PublicKey, Secp256k1, Signing, Verification}; 9 | 10 | /// Base points used by a party of a DLC channel to derive public and private 11 | /// values necessary for state update throughout the lifetime of the channel. 12 | #[derive(Clone, Debug)] 13 | #[cfg_attr( 14 | feature = "use-serde", 15 | derive(serde::Serialize, serde::Deserialize), 16 | serde(rename_all = "camelCase") 17 | )] 18 | pub struct PartyBasePoints { 19 | /// Base point used to derive "own" secrets and points. 20 | pub own_basepoint: PublicKey, 21 | /// Base point used to derive revocation secrets and points, revealed when 22 | /// the state of a channel is revoked. 23 | pub revocation_basepoint: PublicKey, 24 | /// Base point used to derive publish secrets and points, used as adaptor 25 | /// secrets, that get revealed when using the adaptor signature. 26 | pub publish_basepoint: PublicKey, 27 | } 28 | 29 | impl PartyBasePoints { 30 | /// Creates a new [`PartyBasePoints`] structure filled with the given values. 31 | pub fn new( 32 | own_basepoint: PublicKey, 33 | revocation_basepoint: PublicKey, 34 | publish_basepoint: PublicKey, 35 | ) -> Self { 36 | Self { 37 | own_basepoint, 38 | revocation_basepoint, 39 | publish_basepoint, 40 | } 41 | } 42 | 43 | /// Get [`RevokeParams`] from the base points, counter party revocation base 44 | /// point and the current per update point. 45 | pub fn get_revokable_params( 46 | &self, 47 | secp: &Secp256k1, 48 | countersignatory_revocation_basepoint: &PublicKey, 49 | per_update_point: &PublicKey, 50 | ) -> RevokeParams { 51 | RevokeParams { 52 | own_pk: derive_bitcoin_public_key(secp, per_update_point, &self.own_basepoint), 53 | publish_pk: derive_bitcoin_public_key(secp, per_update_point, &self.publish_basepoint), 54 | revoke_pk: derive_bitcoin_public_revocation_key( 55 | secp, 56 | per_update_point, 57 | countersignatory_revocation_basepoint, 58 | ), 59 | } 60 | } 61 | 62 | /// Returns an "own" point using the own base point and given per update point. 63 | pub fn get_own_pk( 64 | &self, 65 | secp: &Secp256k1, 66 | per_update_point: &PublicKey, 67 | ) -> PublicKey { 68 | derive_public_key(secp, per_update_point, &self.own_basepoint) 69 | } 70 | 71 | /// Returns a publish point using the publish base point and given per update point. 72 | pub fn get_publish_pk( 73 | &self, 74 | secp: &Secp256k1, 75 | per_update_point: &PublicKey, 76 | ) -> PublicKey { 77 | derive_public_key(secp, per_update_point, &self.publish_basepoint) 78 | } 79 | 80 | /// Returns a publish point using the publish base point and given per update point. 81 | pub fn get_revocation_pk( 82 | &self, 83 | secp: &Secp256k1, 84 | per_update_point: &PublicKey, 85 | ) -> PublicKey { 86 | derive_public_key(secp, per_update_point, &self.revocation_basepoint) 87 | } 88 | } 89 | 90 | fn derive_bitcoin_public_key( 91 | secp: &Secp256k1, 92 | per_commitment_point: &PublicKey, 93 | base_point: &PublicKey, 94 | ) -> BitcoinPublicKey { 95 | let inner = derive_public_key(secp, per_commitment_point, base_point); 96 | BitcoinPublicKey { 97 | compressed: true, 98 | inner, 99 | } 100 | } 101 | 102 | fn derive_bitcoin_public_revocation_key( 103 | secp: &Secp256k1, 104 | per_commitment_point: &PublicKey, 105 | countersignatory_revocation_base_point: &PublicKey, 106 | ) -> BitcoinPublicKey { 107 | let inner = derive_public_revocation_key( 108 | secp, 109 | per_commitment_point, 110 | countersignatory_revocation_base_point, 111 | ); 112 | BitcoinPublicKey { 113 | compressed: true, 114 | inner, 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /mocks/src/mock_wallet.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use bitcoin::psbt::Psbt; 4 | use bitcoin::transaction::Version; 5 | use bitcoin::{absolute::LockTime, Address, OutPoint, ScriptBuf, Transaction, TxOut}; 6 | use bitcoin::{Amount, CompressedPublicKey}; 7 | use dlc_manager::{error::Error, Blockchain, ContractSignerProvider, SimpleSigner, Utxo, Wallet}; 8 | use secp256k1_zkp::{rand::seq::SliceRandom, PublicKey, SecretKey}; 9 | 10 | use crate::mock_blockchain::MockBlockchain; 11 | 12 | pub struct MockWallet { 13 | utxos: Vec, 14 | } 15 | 16 | impl MockWallet { 17 | pub fn new(blockchain: &Rc, utxo_values: &[Amount]) -> Self { 18 | let mut utxos = Vec::with_capacity(utxo_values.len()); 19 | 20 | for utxo_value in utxo_values { 21 | let tx_out = TxOut { 22 | value: *utxo_value, 23 | script_pubkey: ScriptBuf::default(), 24 | }; 25 | let tx = Transaction { 26 | version: Version::TWO, 27 | lock_time: LockTime::ZERO, 28 | input: vec![], 29 | output: vec![tx_out.clone()], 30 | }; 31 | blockchain.send_transaction(&tx).unwrap(); 32 | let utxo = Utxo { 33 | tx_out, 34 | outpoint: bitcoin::OutPoint { 35 | txid: tx.compute_txid(), 36 | vout: 0, 37 | }, 38 | address: get_address(), 39 | redeem_script: ScriptBuf::default(), 40 | reserved: false, 41 | }; 42 | 43 | utxos.push(utxo); 44 | } 45 | 46 | Self { utxos } 47 | } 48 | } 49 | 50 | impl ContractSignerProvider for MockWallet { 51 | type Signer = SimpleSigner; 52 | 53 | fn derive_signer_key_id(&self, _: bool, temp_id: [u8; 32]) -> [u8; 32] { 54 | temp_id 55 | } 56 | 57 | fn derive_contract_signer(&self, _: [u8; 32]) -> Result { 58 | Ok(SimpleSigner::new(get_secret_key())) 59 | } 60 | 61 | fn get_secret_key_for_pubkey(&self, _: &PublicKey) -> Result { 62 | Ok(get_secret_key()) 63 | } 64 | 65 | fn get_new_secret_key(&self) -> Result { 66 | Ok(get_secret_key()) 67 | } 68 | } 69 | 70 | impl Wallet for MockWallet { 71 | fn get_new_address(&self) -> Result { 72 | Ok(get_address()) 73 | } 74 | 75 | fn get_new_change_address(&self) -> Result { 76 | Ok(get_address()) 77 | } 78 | 79 | fn get_utxos_for_amount( 80 | &self, 81 | amount: Amount, 82 | _fee_rate: u64, 83 | _lock_utxos: bool, 84 | ) -> Result, Error> { 85 | let mut utxo_pool = self.utxos.clone(); 86 | let seed = 1; 87 | utxo_pool.shuffle(&mut secp256k1_zkp::rand::rngs::mock::StepRng::new( 88 | seed, seed, 89 | )); 90 | 91 | let mut sum = Amount::ZERO; 92 | 93 | let res = utxo_pool 94 | .iter() 95 | .take_while(|x| { 96 | if sum >= amount { 97 | return false; 98 | } 99 | sum += x.tx_out.value; 100 | true 101 | }) 102 | .cloned() 103 | .collect(); 104 | 105 | if sum >= amount { 106 | return Ok(res); 107 | } 108 | 109 | Err(Error::InvalidParameters("Not enought UTXOs".to_string())) 110 | } 111 | 112 | fn import_address(&self, _address: &Address) -> Result<(), dlc_manager::error::Error> { 113 | Ok(()) 114 | } 115 | 116 | fn sign_psbt_input(&self, _: &mut Psbt, _: usize) -> Result<(), Error> { 117 | Ok(()) 118 | } 119 | 120 | fn unreserve_utxos(&self, _outpoints: &[OutPoint]) -> Result<(), Error> { 121 | Ok(()) 122 | } 123 | } 124 | 125 | fn get_address() -> Address { 126 | let pk = bitcoin::PrivateKey::new(get_secret_key(), bitcoin::Network::Regtest); 127 | let pubkey = CompressedPublicKey::from_private_key(secp256k1_zkp::SECP256K1, &pk).unwrap(); 128 | Address::p2wpkh(&pubkey, bitcoin::Network::Regtest) 129 | } 130 | 131 | pub fn get_secret_key() -> SecretKey { 132 | SecretKey::from_slice(&[ 133 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134 | 0, 1, 135 | ]) 136 | .unwrap() 137 | } 138 | -------------------------------------------------------------------------------- /dlc-manager/src/contract/accepted_contract.rs: -------------------------------------------------------------------------------- 1 | //! # AcceptedContract 2 | use crate::Error; 3 | 4 | use super::offered_contract::OfferedContract; 5 | use super::AdaptorInfo; 6 | use bitcoin::{Amount, SignedAmount, Transaction}; 7 | use dlc::{DlcTransactions, PartyParams}; 8 | use dlc_messages::{AcceptDlc, FundingInput}; 9 | use secp256k1_zkp::ecdsa::Signature; 10 | use secp256k1_zkp::EcdsaAdaptorSignature; 11 | 12 | use std::fmt::Write as _; 13 | 14 | /// An AcceptedContract represents a contract in the accepted state. 15 | #[derive(Clone)] 16 | pub struct AcceptedContract { 17 | /// The offered contract that was accepted. 18 | pub offered_contract: OfferedContract, 19 | /// The parameters of the accepting party. 20 | pub accept_params: PartyParams, 21 | /// The funding inputs provided by the accepting party. 22 | pub funding_inputs: Vec, 23 | /// The adaptor information for the contract storing information about 24 | /// the relation between adaptor signatures and outcomes. 25 | pub adaptor_infos: Vec, 26 | /// The adaptor signatures of the accepting party. Note that the accepting 27 | /// party does not keep them thus an option is used. 28 | pub adaptor_signatures: Option>, 29 | /// The signature for the refund transaction from the accepting party. 30 | pub accept_refund_signature: Signature, 31 | /// The bitcoin set of bitcoin transactions for the contract. 32 | pub dlc_transactions: DlcTransactions, 33 | } 34 | 35 | impl AcceptedContract { 36 | /// Returns the contract id for the contract computed as specified here: 37 | /// 38 | pub fn get_contract_id(&self) -> [u8; 32] { 39 | crate::utils::compute_id( 40 | self.dlc_transactions.fund.compute_txid(), 41 | self.dlc_transactions.get_fund_output_index() as u16, 42 | &self.offered_contract.id, 43 | ) 44 | } 45 | 46 | /// Utility function to get the contract id as a string. 47 | pub fn get_contract_id_string(&self) -> String { 48 | let mut string_id = String::with_capacity(32 * 2 + 2); 49 | string_id.push_str("0x"); 50 | let id = self.get_contract_id(); 51 | for i in &id { 52 | write!(string_id, "{:02x}", i).unwrap(); 53 | } 54 | 55 | string_id 56 | } 57 | 58 | pub(crate) fn get_accept_contract_msg( 59 | &self, 60 | ecdsa_adaptor_signatures: &[EcdsaAdaptorSignature], 61 | ) -> AcceptDlc { 62 | AcceptDlc { 63 | protocol_version: crate::conversion_utils::PROTOCOL_VERSION, 64 | temporary_contract_id: self.offered_contract.id, 65 | accept_collateral: self.accept_params.collateral, 66 | funding_pubkey: self.accept_params.fund_pubkey, 67 | payout_spk: self.accept_params.payout_script_pubkey.clone(), 68 | payout_serial_id: self.accept_params.payout_serial_id, 69 | funding_inputs: self.funding_inputs.clone(), 70 | change_spk: self.accept_params.change_script_pubkey.clone(), 71 | change_serial_id: self.accept_params.change_serial_id, 72 | cet_adaptor_signatures: ecdsa_adaptor_signatures.into(), 73 | refund_signature: self.accept_refund_signature, 74 | negotiation_fields: None, 75 | } 76 | } 77 | 78 | /// Compute the profit and loss for this contract and an assciated cet index 79 | pub fn compute_pnl(&self, cet: &Transaction) -> Result { 80 | let offer = &self.offered_contract; 81 | let party_params = if offer.is_offer_party { 82 | &offer.offer_params 83 | } else { 84 | &self.accept_params 85 | }; 86 | let collateral = party_params.collateral; 87 | let v0_witness_payout_script = &party_params.payout_script_pubkey; 88 | let final_payout = cet 89 | .output 90 | .iter() 91 | .find_map(|x| { 92 | if &x.script_pubkey == v0_witness_payout_script { 93 | Some(x.value) 94 | } else { 95 | None 96 | } 97 | }) 98 | .unwrap_or(Amount::ZERO); 99 | Ok(final_payout.to_signed().map_err(|_| Error::OutOfRange)? 100 | - collateral.to_signed().map_err(|_| Error::OutOfRange)?) 101 | } 102 | } 103 | 104 | #[cfg(test)] 105 | mod tests { 106 | use lightning::io::Cursor; 107 | 108 | use lightning::util::ser::Readable; 109 | 110 | use super::*; 111 | 112 | #[test] 113 | fn pnl_compute_test() { 114 | let buf = include_bytes!("../../../dlc-sled-storage-provider/test_files/Accepted"); 115 | let accepted_contract: AcceptedContract = Readable::read(&mut Cursor::new(&buf)).unwrap(); 116 | let cets = &accepted_contract.dlc_transactions.cets; 117 | assert_eq!( 118 | accepted_contract.compute_pnl(&cets[0]).unwrap(), 119 | SignedAmount::from_sat(90000000) 120 | ); 121 | assert_eq!( 122 | accepted_contract 123 | .compute_pnl(&cets[cets.len() - 1]) 124 | .unwrap(), 125 | SignedAmount::from_sat(-11000000) 126 | ); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /mocks/src/mock_oracle_provider.rs: -------------------------------------------------------------------------------- 1 | use bitcoin::hashes::Hash; 2 | use dlc_manager::error::Error as DaemonError; 3 | use dlc_manager::Oracle; 4 | use dlc_messages::oracle_msgs::{ 5 | EventDescriptor, OracleAnnouncement, OracleAttestation, OracleEvent, 6 | }; 7 | use lightning::util::ser::Writeable; 8 | use secp256k1_zkp::rand::thread_rng; 9 | use secp256k1_zkp::SecretKey; 10 | use secp256k1_zkp::{All, Message, Secp256k1}; 11 | use secp256k1_zkp::{Keypair, XOnlyPublicKey}; 12 | 13 | use std::collections::HashMap; 14 | 15 | #[derive(Clone, Debug)] 16 | pub struct MockOracle { 17 | key_pair: Keypair, 18 | secp: Secp256k1, 19 | announcements: HashMap, 20 | attestations: HashMap, 21 | nonces: HashMap>, 22 | } 23 | 24 | impl MockOracle { 25 | pub fn new() -> Self { 26 | let secp = Secp256k1::new(); 27 | let key_pair = Keypair::new(&secp, &mut thread_rng()); 28 | 29 | MockOracle { 30 | secp, 31 | key_pair, 32 | announcements: HashMap::new(), 33 | attestations: HashMap::new(), 34 | nonces: HashMap::new(), 35 | } 36 | } 37 | 38 | pub fn from_secret_key(sk: &SecretKey) -> Self { 39 | let secp = Secp256k1::new(); 40 | let key_pair = Keypair::from_secret_key(&secp, sk); 41 | 42 | MockOracle { 43 | secp, 44 | key_pair, 45 | announcements: HashMap::new(), 46 | attestations: HashMap::new(), 47 | nonces: HashMap::new(), 48 | } 49 | } 50 | } 51 | 52 | impl Default for MockOracle { 53 | fn default() -> Self { 54 | Self::new() 55 | } 56 | } 57 | 58 | impl Oracle for MockOracle { 59 | fn get_public_key(&self) -> XOnlyPublicKey { 60 | XOnlyPublicKey::from_keypair(&self.key_pair).0 61 | } 62 | 63 | fn get_announcement(&self, event_id: &str) -> Result { 64 | let res = self 65 | .announcements 66 | .get(event_id) 67 | .ok_or_else(|| DaemonError::OracleError("Announcement not found".to_string()))?; 68 | Ok(res.clone()) 69 | } 70 | 71 | fn get_attestation(&self, event_id: &str) -> Result { 72 | let res = self 73 | .attestations 74 | .get(event_id) 75 | .ok_or_else(|| DaemonError::OracleError("Attestation not found".to_string()))?; 76 | Ok(res.clone()) 77 | } 78 | } 79 | 80 | impl MockOracle { 81 | fn generate_nonces_for_event( 82 | &mut self, 83 | event_id: &str, 84 | event_descriptor: &EventDescriptor, 85 | ) -> Vec { 86 | let nb_nonces = match event_descriptor { 87 | EventDescriptor::EnumEvent(_) => 1, 88 | EventDescriptor::DigitDecompositionEvent(d) => d.nb_digits, 89 | }; 90 | 91 | let priv_nonces: Vec<_> = (0..nb_nonces) 92 | .map(|_| SecretKey::new(&mut thread_rng())) 93 | .collect(); 94 | let key_pairs: Vec<_> = priv_nonces 95 | .iter() 96 | .map(|x| Keypair::from_seckey_slice(&self.secp, x.as_ref()).unwrap()) 97 | .collect(); 98 | 99 | let nonces = key_pairs 100 | .iter() 101 | .map(|k| XOnlyPublicKey::from_keypair(k).0) 102 | .collect(); 103 | 104 | self.nonces.insert(event_id.to_string(), priv_nonces); 105 | 106 | nonces 107 | } 108 | 109 | pub fn add_event(&mut self, event_id: &str, event_descriptor: &EventDescriptor, maturity: u32) { 110 | let oracle_nonces = self.generate_nonces_for_event(event_id, event_descriptor); 111 | let oracle_event = OracleEvent { 112 | oracle_nonces, 113 | event_maturity_epoch: maturity, 114 | event_descriptor: event_descriptor.clone(), 115 | event_id: event_id.to_string(), 116 | }; 117 | let mut event_hex = Vec::new(); 118 | oracle_event 119 | .write(&mut event_hex) 120 | .expect("Error writing oracle event"); 121 | let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex).to_byte_array(); 122 | let msg = Message::from_digest(hash); 123 | let sig = self.secp.sign_schnorr(&msg, &self.key_pair); 124 | let announcement = OracleAnnouncement { 125 | oracle_event, 126 | oracle_public_key: self.get_public_key(), 127 | announcement_signature: sig, 128 | }; 129 | self.announcements 130 | .insert(event_id.to_string(), announcement); 131 | } 132 | 133 | pub fn add_attestation(&mut self, event_id: &str, outcomes: &[String]) { 134 | let nonces = self.nonces.get(event_id).unwrap(); 135 | let signatures = outcomes 136 | .iter() 137 | .zip(nonces.iter()) 138 | .map(|(x, nonce)| { 139 | let hash = bitcoin::hashes::sha256::Hash::hash(x.as_bytes()).to_byte_array(); 140 | let msg = Message::from_digest(hash); 141 | dlc::secp_utils::schnorrsig_sign_with_nonce( 142 | &self.secp, 143 | &msg, 144 | &self.key_pair, 145 | nonce.as_ref(), 146 | ) 147 | }) 148 | .collect(); 149 | let attestation = OracleAttestation { 150 | event_id: event_id.to_string(), 151 | oracle_public_key: self.get_public_key(), 152 | signatures, 153 | outcomes: outcomes.to_vec(), 154 | }; 155 | self.attestations.insert(event_id.to_string(), attestation); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_enum_missing_payout.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": [ 3 | 66, 4 | 45, 5 | 75, 6 | 127, 7 | 245, 8 | 123, 9 | 32, 10 | 231, 11 | 135, 12 | 70, 13 | 226, 14 | 195, 15 | 249, 16 | 219, 17 | 192, 18 | 57, 19 | 194, 20 | 24, 21 | 51, 22 | 2, 23 | 115, 24 | 136, 25 | 214, 26 | 219, 27 | 13, 28 | 8, 29 | 11, 30 | 30, 31 | 7, 32 | 172, 33 | 177, 34 | 97 35 | ], 36 | "isOfferParty": false, 37 | "contractInfo": [ 38 | { 39 | "contractDescriptor": { 40 | "enum": { 41 | "outcomePayouts": [ 42 | { 43 | "outcome": "a", 44 | "payout": { 45 | "offer": 200000000, 46 | "accept": 0 47 | } 48 | }, 49 | { 50 | "outcome": "c", 51 | "payout": { 52 | "offer": 200000000, 53 | "accept": 0 54 | } 55 | }, 56 | { 57 | "outcome": "d", 58 | "payout": { 59 | "offer": 0, 60 | "accept": 200000000 61 | } 62 | } 63 | ] 64 | } 65 | }, 66 | "oracleAnnouncements": [ 67 | { 68 | "announcementSignature": "803cd857c979850c1f6d89747a02ce78803a3bef6ff5f9427eb193cc5f1ef79c3f2ababd63f31c84f88be5625e6853beeba08eb853eb523f99c768c782bee373", 69 | "oraclePublicKey": "5fd013bdf8448a1816d9e0f5a5bb31acab07ea52762a8240c2619bab1038f6da", 70 | "oracleEvent": { 71 | "oracleNonces": [ 72 | "aa7d5e8df82bf7116088b94e5e11828b9bc5b1670603ebc2406596f6ffa7509a" 73 | ], 74 | "eventMaturityEpoch": 1623133104, 75 | "eventDescriptor": { 76 | "enumEvent": { 77 | "outcomes": [ 78 | "a", 79 | "b", 80 | "c", 81 | "d" 82 | ] 83 | } 84 | }, 85 | "eventId": "Test" 86 | } 87 | }, 88 | { 89 | "announcementSignature": "f7a827db9aec750f0c79367378c87026ae1f626f2f751d6546901557b05dd77cfb65c5d669bd3d3b55db81732daf5f62e94211b43678f102ac1d5f4cb6b600e8", 90 | "oraclePublicKey": "552bc07fc8f39f750172e30e2eb22cccd761d9f058070ac36dbca8477c139223", 91 | "oracleEvent": { 92 | "oracleNonces": [ 93 | "8bc7fd68fe397a40ec6657d53a1eab8eb86e33c04fc410bb1b1fa4f071c58381" 94 | ], 95 | "eventMaturityEpoch": 1623133104, 96 | "eventDescriptor": { 97 | "enumEvent": { 98 | "outcomes": [ 99 | "a", 100 | "b", 101 | "c", 102 | "d" 103 | ] 104 | } 105 | }, 106 | "eventId": "Test" 107 | } 108 | }, 109 | { 110 | "announcementSignature": "e92d6b1b63ccd561d2af11dfef7783f0edf189ea5e3fd260a052ce71be11e640112cdab9aeec184bce3b88d898011122228ee0e301f73ae886b9bb36ad159256", 111 | "oraclePublicKey": "0739a8cf8dc8755a9593b81c766338176399ebd9281b0b73aec9a5c050ec92f9", 112 | "oracleEvent": { 113 | "oracleNonces": [ 114 | "af475a2c103d4fcb3085c099a95d886bcbf20e01a6cf36c4851c1b0b92173263" 115 | ], 116 | "eventMaturityEpoch": 1623133104, 117 | "eventDescriptor": { 118 | "enumEvent": { 119 | "outcomes": [ 120 | "a", 121 | "b", 122 | "c", 123 | "d" 124 | ] 125 | } 126 | }, 127 | "eventId": "Test" 128 | } 129 | } 130 | ], 131 | "threshold": 3 132 | } 133 | ], 134 | "counterParty": "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", 135 | "offerParams": { 136 | "fundPubkey": "022dec6d86aed605b2dc603e2f011b49bb434d877924bd32f1b07d41e4eea29e48", 137 | "changeScriptPubkey": "001495d8645b0176d8eb47b5638e048e71ff82d18128", 138 | "changeSerialId": 10279035205932415657, 139 | "payoutScriptPubkey": "001443f48906a3c171778f3c8b6a370e08a213de0f62", 140 | "payoutSerialId": 2203335585862709839, 141 | "inputs": [ 142 | { 143 | "outpoint": "7bd23207cc0f880db4f5b16dbbb62226f504c57529b1bc419802283e3793d5ab:0", 144 | "maxWitnessLen": 107, 145 | "redeemScript": "", 146 | "serialId": 8898618283234174875 147 | } 148 | ], 149 | "inputAmount": 5000000000, 150 | "collateral": 100000000 151 | }, 152 | "totalCollateral": 200000000, 153 | "fundingInputs": [ 154 | { 155 | "inputSerialId": 2904786313666239109, 156 | "prevTx": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000160014ce51088548a1bc02dc108da8ce5f5383c9309e630000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", 157 | "prevTxVout": 0, 158 | "sequence": 4294967295, 159 | "maxWitnessLen": 107, 160 | "redeemScript": "" 161 | } 162 | ], 163 | "fundOutputSerialId": 17285351301962852321, 164 | "feeRatePerVb": 2, 165 | "cetLocktime": 1623133104, 166 | "refundLocktime": 1623737904, 167 | "keysId": [ 168 | 16, 169 | 175, 170 | 248, 171 | 204, 172 | 229, 173 | 173, 174 | 124, 175 | 107, 176 | 34, 177 | 204, 178 | 75, 179 | 138, 180 | 154, 181 | 151, 182 | 193, 183 | 8, 184 | 142, 185 | 116, 186 | 180, 187 | 156, 188 | 90, 189 | 229, 190 | 80, 191 | 251, 192 | 77, 193 | 5, 194 | 215, 195 | 234, 196 | 70, 197 | 117, 198 | 68, 199 | 208 200 | ] 201 | } 202 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_numerical_empty_rounding_interval.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": [ 3 | 9, 4 | 164, 5 | 248, 6 | 98, 7 | 221, 8 | 224, 9 | 186, 10 | 237, 11 | 180, 12 | 155, 13 | 198, 14 | 201, 15 | 213, 16 | 182, 17 | 7, 18 | 99, 19 | 192, 20 | 179, 21 | 34, 22 | 214, 23 | 206, 24 | 92, 25 | 177, 26 | 1, 27 | 209, 28 | 63, 29 | 226, 30 | 247, 31 | 31, 32 | 128, 33 | 33, 34 | 115 35 | ], 36 | "isOfferParty": false, 37 | "contractInfo": [ 38 | { 39 | "contractDescriptor": { 40 | "numerical": { 41 | "payoutFunction": { 42 | "payoutFunctionPieces": [ 43 | { 44 | "polynomialPayoutCurvePiece": { 45 | "payoutPoints": [ 46 | { 47 | "eventOutcome": 0, 48 | "outcomePayout": 0, 49 | "extraPrecision": 0 50 | }, 51 | { 52 | "eventOutcome": 5, 53 | "outcomePayout": 200000000, 54 | "extraPrecision": 0 55 | } 56 | ] 57 | } 58 | }, 59 | { 60 | "polynomialPayoutCurvePiece": { 61 | "payoutPoints": [ 62 | { 63 | "eventOutcome": 5, 64 | "outcomePayout": 200000000, 65 | "extraPrecision": 0 66 | }, 67 | { 68 | "eventOutcome": 1023, 69 | "outcomePayout": 200000000, 70 | "extraPrecision": 0 71 | } 72 | ] 73 | } 74 | } 75 | ] 76 | }, 77 | "roundingIntervals": { 78 | "intervals": [] 79 | }, 80 | "differenceParams": null, 81 | "oracleNumericInfos": { 82 | "base": 2, 83 | "nbDigits": [ 84 | 10 85 | ] 86 | } 87 | } 88 | }, 89 | "oracleAnnouncements": [ 90 | { 91 | "announcementSignature": "706e97a76e5e4c25e2f1c180f7f6b5596304ae1c84c602cb3cb97c5b878ede942e6e4e9cabadb9f3b7ec5eb0370d92b2e8f0b3df05530b111cc69a633bf16908", 92 | "oraclePublicKey": "8a629938a0b7700ae7357c5d4447453aa502d4b644f8c62baad5d406d58b7f6f", 93 | "oracleEvent": { 94 | "oracleNonces": [ 95 | "7bc4eae76b8fa69d241b812e681f535dc93ba171c6d752813ac7710cb401b81b", 96 | "d01ea767509b40360e7a2b3ac1e4caf7c114760ab5e2091de426a6942a55fffc", 97 | "021d1d3b4e33876fc37fe106354a4109dcc064f24aae36d752e303f54ee0436d", 98 | "f7cedc1b24d697098484210110e07ee891569ee678c6616eb70990807e8a38ba", 99 | "b2d587c50e7dbcb0c975595835d966141f396ae849f8745f19cc877099842899", 100 | "9973eafc9436c8fe772e6ba86d35a189a0da03d0d05ae6299d1322ff755932b7", 101 | "d6d2333c3be484672b0ac3c8b4b407db08aa6a2c634240d77fbb39191e7f4658", 102 | "bd65cb669dd9eb467fd748a84f097165210e587a97677fb925be0f45d8d9e142", 103 | "b04bb0e368aaae1657f8ec7b50a9411dd96fdd763fb6732a83df52a95848b2cc", 104 | "51ada014e7194a596efab67568da0d0f622950517f3807a6ec555fcbebfceecd" 105 | ], 106 | "eventMaturityEpoch": 1623133104, 107 | "eventDescriptor": { 108 | "digitDecompositionEvent": { 109 | "base": 2, 110 | "isSigned": false, 111 | "unit": "sats/sec", 112 | "precision": 0, 113 | "nbDigits": 10 114 | } 115 | }, 116 | "eventId": "Test" 117 | } 118 | } 119 | ], 120 | "threshold": 1 121 | } 122 | ], 123 | "counterParty": "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", 124 | "offerParams": { 125 | "fundPubkey": "02d962a5e200e3c4cd9d425212d87cf92924d875126f9f6168b3757c6cb2ec419b", 126 | "changeScriptPubkey": "001479ee50e61e88a21baa2b5124b881c188bdf63c37", 127 | "changeSerialId": 13956474821580554639, 128 | "payoutScriptPubkey": "0014ed9c3ca30e6ff4b93c86ab4c7ea5a8efde31b874", 129 | "payoutSerialId": 14595850945083503669, 130 | "inputs": [ 131 | { 132 | "outpoint": "cac38ae578ed4ce3f32f60b25fa44768bb29c200beb2be80854b56802fbdc10f:0", 133 | "maxWitnessLen": 107, 134 | "redeemScript": "", 135 | "serialId": 4408916189615191417 136 | } 137 | ], 138 | "inputAmount": 5000000000, 139 | "collateral": 100000000 140 | }, 141 | "totalCollateral": 200000000, 142 | "fundingInputs": [ 143 | { 144 | "inputSerialId": 2904786313666239109, 145 | "prevTx": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000160014ce51088548a1bc02dc108da8ce5f5383c9309e630000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", 146 | "prevTxVout": 0, 147 | "sequence": 4294967295, 148 | "maxWitnessLen": 107, 149 | "redeemScript": "" 150 | } 151 | ], 152 | "fundOutputSerialId": 16475280753107887427, 153 | "feeRatePerVb": 2, 154 | "cetLocktime": 1623133104, 155 | "refundLocktime": 1623737904, 156 | "keysId": [ 157 | 16, 158 | 175, 159 | 248, 160 | 204, 161 | 229, 162 | 173, 163 | 124, 164 | 107, 165 | 34, 166 | 204, 167 | 75, 168 | 138, 169 | 154, 170 | 151, 171 | 193, 172 | 8, 173 | 142, 174 | 116, 175 | 180, 176 | 156, 177 | 90, 178 | 229, 179 | 80, 180 | 251, 181 | 77, 182 | 5, 183 | 215, 184 | 234, 185 | 70, 186 | 117, 187 | 68, 188 | 208 189 | ] 190 | } 191 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_numerical_non_continuous.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": [ 3 | 9, 4 | 164, 5 | 248, 6 | 98, 7 | 221, 8 | 224, 9 | 186, 10 | 237, 11 | 180, 12 | 155, 13 | 198, 14 | 201, 15 | 213, 16 | 182, 17 | 7, 18 | 99, 19 | 192, 20 | 179, 21 | 34, 22 | 214, 23 | 206, 24 | 92, 25 | 177, 26 | 1, 27 | 209, 28 | 63, 29 | 226, 30 | 247, 31 | 31, 32 | 128, 33 | 33, 34 | 115 35 | ], 36 | "isOfferParty": false, 37 | "contractInfo": [ 38 | { 39 | "contractDescriptor": { 40 | "numerical": { 41 | "payoutFunction": { 42 | "payoutFunctionPieces": [ 43 | { 44 | "polynomialPayoutCurvePiece": { 45 | "payoutPoints": [ 46 | { 47 | "eventOutcome": 0, 48 | "outcomePayout": 0, 49 | "extraPrecision": 0 50 | }, 51 | { 52 | "eventOutcome": 4, 53 | "outcomePayout": 200000000, 54 | "extraPrecision": 0 55 | } 56 | ] 57 | } 58 | }, 59 | { 60 | "polynomialPayoutCurvePiece": { 61 | "payoutPoints": [ 62 | { 63 | "eventOutcome": 5, 64 | "outcomePayout": 200000000, 65 | "extraPrecision": 0 66 | }, 67 | { 68 | "eventOutcome": 1023, 69 | "outcomePayout": 200000000, 70 | "extraPrecision": 0 71 | } 72 | ] 73 | } 74 | } 75 | ] 76 | }, 77 | "roundingIntervals": { 78 | "intervals": [ 79 | { 80 | "beginInterval": 0, 81 | "roundingMod": 1 82 | } 83 | ] 84 | }, 85 | "differenceParams": null, 86 | "oracleNumericInfos": { 87 | "base": 2, 88 | "nbDigits": [ 89 | 10 90 | ] 91 | } 92 | } 93 | }, 94 | "oracleAnnouncements": [ 95 | { 96 | "announcementSignature": "706e97a76e5e4c25e2f1c180f7f6b5596304ae1c84c602cb3cb97c5b878ede942e6e4e9cabadb9f3b7ec5eb0370d92b2e8f0b3df05530b111cc69a633bf16908", 97 | "oraclePublicKey": "8a629938a0b7700ae7357c5d4447453aa502d4b644f8c62baad5d406d58b7f6f", 98 | "oracleEvent": { 99 | "oracleNonces": [ 100 | "7bc4eae76b8fa69d241b812e681f535dc93ba171c6d752813ac7710cb401b81b", 101 | "d01ea767509b40360e7a2b3ac1e4caf7c114760ab5e2091de426a6942a55fffc", 102 | "021d1d3b4e33876fc37fe106354a4109dcc064f24aae36d752e303f54ee0436d", 103 | "f7cedc1b24d697098484210110e07ee891569ee678c6616eb70990807e8a38ba", 104 | "b2d587c50e7dbcb0c975595835d966141f396ae849f8745f19cc877099842899", 105 | "9973eafc9436c8fe772e6ba86d35a189a0da03d0d05ae6299d1322ff755932b7", 106 | "d6d2333c3be484672b0ac3c8b4b407db08aa6a2c634240d77fbb39191e7f4658", 107 | "bd65cb669dd9eb467fd748a84f097165210e587a97677fb925be0f45d8d9e142", 108 | "b04bb0e368aaae1657f8ec7b50a9411dd96fdd763fb6732a83df52a95848b2cc", 109 | "51ada014e7194a596efab67568da0d0f622950517f3807a6ec555fcbebfceecd" 110 | ], 111 | "eventMaturityEpoch": 1623133104, 112 | "eventDescriptor": { 113 | "digitDecompositionEvent": { 114 | "base": 2, 115 | "isSigned": false, 116 | "unit": "sats/sec", 117 | "precision": 0, 118 | "nbDigits": 10 119 | } 120 | }, 121 | "eventId": "Test" 122 | } 123 | } 124 | ], 125 | "threshold": 1 126 | } 127 | ], 128 | "counterParty": "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", 129 | "offerParams": { 130 | "fundPubkey": "02d962a5e200e3c4cd9d425212d87cf92924d875126f9f6168b3757c6cb2ec419b", 131 | "changeScriptPubkey": "001479ee50e61e88a21baa2b5124b881c188bdf63c37", 132 | "changeSerialId": 13956474821580554639, 133 | "payoutScriptPubkey": "0014ed9c3ca30e6ff4b93c86ab4c7ea5a8efde31b874", 134 | "payoutSerialId": 14595850945083503669, 135 | "inputs": [ 136 | { 137 | "outpoint": "cac38ae578ed4ce3f32f60b25fa44768bb29c200beb2be80854b56802fbdc10f:0", 138 | "maxWitnessLen": 107, 139 | "redeemScript": "", 140 | "serialId": 4408916189615191417 141 | } 142 | ], 143 | "inputAmount": 5000000000, 144 | "collateral": 100000000 145 | }, 146 | "totalCollateral": 200000000, 147 | "fundingInputs": [ 148 | { 149 | "inputSerialId": 2904786313666239109, 150 | "prevTx": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000160014ce51088548a1bc02dc108da8ce5f5383c9309e630000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", 151 | "prevTxVout": 0, 152 | "sequence": 4294967295, 153 | "maxWitnessLen": 107, 154 | "redeemScript": "" 155 | } 156 | ], 157 | "fundOutputSerialId": 16475280753107887427, 158 | "feeRatePerVb": 2, 159 | "cetLocktime": 1623133104, 160 | "refundLocktime": 1623737904, 161 | "keysId": [ 162 | 16, 163 | 175, 164 | 248, 165 | 204, 166 | 229, 167 | 173, 168 | 124, 169 | 107, 170 | 34, 171 | 204, 172 | 75, 173 | 138, 174 | 154, 175 | 151, 176 | 193, 177 | 8, 178 | 142, 179 | 116, 180 | 180, 181 | 156, 182 | 90, 183 | 229, 184 | 80, 185 | 251, 186 | 77, 187 | 5, 188 | 215, 189 | 234, 190 | 70, 191 | 117, 192 | 68, 193 | 208 194 | ] 195 | } 196 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_numerical_bad_first_payout.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": [ 3 | 9, 4 | 164, 5 | 248, 6 | 98, 7 | 221, 8 | 224, 9 | 186, 10 | 237, 11 | 180, 12 | 155, 13 | 198, 14 | 201, 15 | 213, 16 | 182, 17 | 7, 18 | 99, 19 | 192, 20 | 179, 21 | 34, 22 | 214, 23 | 206, 24 | 92, 25 | 177, 26 | 1, 27 | 209, 28 | 63, 29 | 226, 30 | 247, 31 | 31, 32 | 128, 33 | 33, 34 | 115 35 | ], 36 | "isOfferParty": false, 37 | "contractInfo": [ 38 | { 39 | "contractDescriptor": { 40 | "numerical": { 41 | "payoutFunction": { 42 | "payoutFunctionPieces": [ 43 | { 44 | "polynomialPayoutCurvePiece": { 45 | "payoutPoints": [ 46 | { 47 | "eventOutcome": 1, 48 | "outcomePayout": 0, 49 | "extraPrecision": 0 50 | }, 51 | { 52 | "eventOutcome": 5, 53 | "outcomePayout": 200000000, 54 | "extraPrecision": 0 55 | } 56 | ] 57 | } 58 | }, 59 | { 60 | "polynomialPayoutCurvePiece": { 61 | "payoutPoints": [ 62 | { 63 | "eventOutcome": 5, 64 | "outcomePayout": 200000000, 65 | "extraPrecision": 0 66 | }, 67 | { 68 | "eventOutcome": 1023, 69 | "outcomePayout": 200000000, 70 | "extraPrecision": 0 71 | } 72 | ] 73 | } 74 | } 75 | ] 76 | }, 77 | "roundingIntervals": { 78 | "intervals": [ 79 | { 80 | "beginInterval": 0, 81 | "roundingMod": 1 82 | } 83 | ] 84 | }, 85 | "differenceParams": null, 86 | "oracleNumericInfos": { 87 | "base": 2, 88 | "nbDigits": [ 89 | 10 90 | ] 91 | } 92 | } 93 | }, 94 | "oracleAnnouncements": [ 95 | { 96 | "announcementSignature": "706e97a76e5e4c25e2f1c180f7f6b5596304ae1c84c602cb3cb97c5b878ede942e6e4e9cabadb9f3b7ec5eb0370d92b2e8f0b3df05530b111cc69a633bf16908", 97 | "oraclePublicKey": "8a629938a0b7700ae7357c5d4447453aa502d4b644f8c62baad5d406d58b7f6f", 98 | "oracleEvent": { 99 | "oracleNonces": [ 100 | "7bc4eae76b8fa69d241b812e681f535dc93ba171c6d752813ac7710cb401b81b", 101 | "d01ea767509b40360e7a2b3ac1e4caf7c114760ab5e2091de426a6942a55fffc", 102 | "021d1d3b4e33876fc37fe106354a4109dcc064f24aae36d752e303f54ee0436d", 103 | "f7cedc1b24d697098484210110e07ee891569ee678c6616eb70990807e8a38ba", 104 | "b2d587c50e7dbcb0c975595835d966141f396ae849f8745f19cc877099842899", 105 | "9973eafc9436c8fe772e6ba86d35a189a0da03d0d05ae6299d1322ff755932b7", 106 | "d6d2333c3be484672b0ac3c8b4b407db08aa6a2c634240d77fbb39191e7f4658", 107 | "bd65cb669dd9eb467fd748a84f097165210e587a97677fb925be0f45d8d9e142", 108 | "b04bb0e368aaae1657f8ec7b50a9411dd96fdd763fb6732a83df52a95848b2cc", 109 | "51ada014e7194a596efab67568da0d0f622950517f3807a6ec555fcbebfceecd" 110 | ], 111 | "eventMaturityEpoch": 1623133104, 112 | "eventDescriptor": { 113 | "digitDecompositionEvent": { 114 | "base": 2, 115 | "isSigned": false, 116 | "unit": "sats/sec", 117 | "precision": 0, 118 | "nbDigits": 10 119 | } 120 | }, 121 | "eventId": "Test" 122 | } 123 | } 124 | ], 125 | "threshold": 1 126 | } 127 | ], 128 | "counterParty": "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", 129 | "offerParams": { 130 | "fundPubkey": "02d962a5e200e3c4cd9d425212d87cf92924d875126f9f6168b3757c6cb2ec419b", 131 | "changeScriptPubkey": "001479ee50e61e88a21baa2b5124b881c188bdf63c37", 132 | "changeSerialId": 13956474821580554639, 133 | "payoutScriptPubkey": "0014ed9c3ca30e6ff4b93c86ab4c7ea5a8efde31b874", 134 | "payoutSerialId": 14595850945083503669, 135 | "inputs": [ 136 | { 137 | "outpoint": "cac38ae578ed4ce3f32f60b25fa44768bb29c200beb2be80854b56802fbdc10f:0", 138 | "maxWitnessLen": 107, 139 | "redeemScript": "", 140 | "serialId": 4408916189615191417 141 | } 142 | ], 143 | "inputAmount": 5000000000, 144 | "collateral": 100000000 145 | }, 146 | "totalCollateral": 200000000, 147 | "fundingInputs": [ 148 | { 149 | "inputSerialId": 2904786313666239109, 150 | "prevTx": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000160014ce51088548a1bc02dc108da8ce5f5383c9309e630000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", 151 | "prevTxVout": 0, 152 | "sequence": 4294967295, 153 | "maxWitnessLen": 107, 154 | "redeemScript": "" 155 | } 156 | ], 157 | "fundOutputSerialId": 16475280753107887427, 158 | "feeRatePerVb": 2, 159 | "cetLocktime": 1623133104, 160 | "refundLocktime": 1623737904, 161 | "keysId": [ 162 | 16, 163 | 175, 164 | 248, 165 | 204, 166 | 229, 167 | 173, 168 | 124, 169 | 107, 170 | 34, 171 | 204, 172 | 75, 173 | 138, 174 | 154, 175 | 151, 176 | 193, 177 | 8, 178 | 142, 179 | 116, 180 | 180, 181 | 156, 182 | 90, 183 | 229, 184 | 80, 185 | 251, 186 | 77, 187 | 5, 188 | 215, 189 | 234, 190 | 70, 191 | 117, 192 | 68, 193 | 208 194 | ] 195 | } 196 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_numerical_bad_last_payout.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": [ 3 | 9, 4 | 164, 5 | 248, 6 | 98, 7 | 221, 8 | 224, 9 | 186, 10 | 237, 11 | 180, 12 | 155, 13 | 198, 14 | 201, 15 | 213, 16 | 182, 17 | 7, 18 | 99, 19 | 192, 20 | 179, 21 | 34, 22 | 214, 23 | 206, 24 | 92, 25 | 177, 26 | 1, 27 | 209, 28 | 63, 29 | 226, 30 | 247, 31 | 31, 32 | 128, 33 | 33, 34 | 115 35 | ], 36 | "isOfferParty": false, 37 | "contractInfo": [ 38 | { 39 | "contractDescriptor": { 40 | "numerical": { 41 | "payoutFunction": { 42 | "payoutFunctionPieces": [ 43 | { 44 | "polynomialPayoutCurvePiece": { 45 | "payoutPoints": [ 46 | { 47 | "eventOutcome": 0, 48 | "outcomePayout": 0, 49 | "extraPrecision": 0 50 | }, 51 | { 52 | "eventOutcome": 5, 53 | "outcomePayout": 200000000, 54 | "extraPrecision": 0 55 | } 56 | ] 57 | } 58 | }, 59 | { 60 | "polynomialPayoutCurvePiece": { 61 | "payoutPoints": [ 62 | { 63 | "eventOutcome": 5, 64 | "outcomePayout": 200000000, 65 | "extraPrecision": 0 66 | }, 67 | { 68 | "eventOutcome": 1018, 69 | "outcomePayout": 200000000, 70 | "extraPrecision": 0 71 | } 72 | ] 73 | } 74 | } 75 | ] 76 | }, 77 | "roundingIntervals": { 78 | "intervals": [ 79 | { 80 | "beginInterval": 0, 81 | "roundingMod": 1 82 | } 83 | ] 84 | }, 85 | "differenceParams": null, 86 | "oracleNumericInfos": { 87 | "base": 2, 88 | "nbDigits": [ 89 | 10 90 | ] 91 | } 92 | } 93 | }, 94 | "oracleAnnouncements": [ 95 | { 96 | "announcementSignature": "706e97a76e5e4c25e2f1c180f7f6b5596304ae1c84c602cb3cb97c5b878ede942e6e4e9cabadb9f3b7ec5eb0370d92b2e8f0b3df05530b111cc69a633bf16908", 97 | "oraclePublicKey": "8a629938a0b7700ae7357c5d4447453aa502d4b644f8c62baad5d406d58b7f6f", 98 | "oracleEvent": { 99 | "oracleNonces": [ 100 | "7bc4eae76b8fa69d241b812e681f535dc93ba171c6d752813ac7710cb401b81b", 101 | "d01ea767509b40360e7a2b3ac1e4caf7c114760ab5e2091de426a6942a55fffc", 102 | "021d1d3b4e33876fc37fe106354a4109dcc064f24aae36d752e303f54ee0436d", 103 | "f7cedc1b24d697098484210110e07ee891569ee678c6616eb70990807e8a38ba", 104 | "b2d587c50e7dbcb0c975595835d966141f396ae849f8745f19cc877099842899", 105 | "9973eafc9436c8fe772e6ba86d35a189a0da03d0d05ae6299d1322ff755932b7", 106 | "d6d2333c3be484672b0ac3c8b4b407db08aa6a2c634240d77fbb39191e7f4658", 107 | "bd65cb669dd9eb467fd748a84f097165210e587a97677fb925be0f45d8d9e142", 108 | "b04bb0e368aaae1657f8ec7b50a9411dd96fdd763fb6732a83df52a95848b2cc", 109 | "51ada014e7194a596efab67568da0d0f622950517f3807a6ec555fcbebfceecd" 110 | ], 111 | "eventMaturityEpoch": 1623133104, 112 | "eventDescriptor": { 113 | "digitDecompositionEvent": { 114 | "base": 2, 115 | "isSigned": false, 116 | "unit": "sats/sec", 117 | "precision": 0, 118 | "nbDigits": 10 119 | } 120 | }, 121 | "eventId": "Test" 122 | } 123 | } 124 | ], 125 | "threshold": 1 126 | } 127 | ], 128 | "counterParty": "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", 129 | "offerParams": { 130 | "fundPubkey": "02d962a5e200e3c4cd9d425212d87cf92924d875126f9f6168b3757c6cb2ec419b", 131 | "changeScriptPubkey": "001479ee50e61e88a21baa2b5124b881c188bdf63c37", 132 | "changeSerialId": 13956474821580554639, 133 | "payoutScriptPubkey": "0014ed9c3ca30e6ff4b93c86ab4c7ea5a8efde31b874", 134 | "payoutSerialId": 14595850945083503669, 135 | "inputs": [ 136 | { 137 | "outpoint": "cac38ae578ed4ce3f32f60b25fa44768bb29c200beb2be80854b56802fbdc10f:0", 138 | "maxWitnessLen": 107, 139 | "redeemScript": "", 140 | "serialId": 4408916189615191417 141 | } 142 | ], 143 | "inputAmount": 5000000000, 144 | "collateral": 100000000 145 | }, 146 | "totalCollateral": 200000000, 147 | "fundingInputs": [ 148 | { 149 | "inputSerialId": 2904786313666239109, 150 | "prevTx": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000160014ce51088548a1bc02dc108da8ce5f5383c9309e630000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", 151 | "prevTxVout": 0, 152 | "sequence": 4294967295, 153 | "maxWitnessLen": 107, 154 | "redeemScript": "" 155 | } 156 | ], 157 | "fundOutputSerialId": 16475280753107887427, 158 | "feeRatePerVb": 2, 159 | "cetLocktime": 1623133104, 160 | "refundLocktime": 1623737904, 161 | "keysId": [ 162 | 16, 163 | 175, 164 | 248, 165 | 204, 166 | 229, 167 | 173, 168 | 124, 169 | 107, 170 | 34, 171 | 204, 172 | 75, 173 | 138, 174 | 154, 175 | 151, 176 | 193, 177 | 8, 178 | 142, 179 | 116, 180 | 180, 181 | 156, 182 | 90, 183 | 229, 184 | 80, 185 | 251, 186 | 77, 187 | 5, 188 | 215, 189 | 234, 190 | 70, 191 | 117, 192 | 68, 193 | 208 194 | ] 195 | } 196 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_numerical_collateral_less_than_payout.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": [ 3 | 9, 4 | 164, 5 | 248, 6 | 98, 7 | 221, 8 | 224, 9 | 186, 10 | 237, 11 | 180, 12 | 155, 13 | 198, 14 | 201, 15 | 213, 16 | 182, 17 | 7, 18 | 99, 19 | 192, 20 | 179, 21 | 34, 22 | 214, 23 | 206, 24 | 92, 25 | 177, 26 | 1, 27 | 209, 28 | 63, 29 | 226, 30 | 247, 31 | 31, 32 | 128, 33 | 33, 34 | 115 35 | ], 36 | "isOfferParty": false, 37 | "contractInfo": [ 38 | { 39 | "contractDescriptor": { 40 | "numerical": { 41 | "payoutFunction": { 42 | "payoutFunctionPieces": [ 43 | { 44 | "polynomialPayoutCurvePiece": { 45 | "payoutPoints": [ 46 | { 47 | "eventOutcome": 0, 48 | "outcomePayout": 0, 49 | "extraPrecision": 0 50 | }, 51 | { 52 | "eventOutcome": 5, 53 | "outcomePayout": 200000000, 54 | "extraPrecision": 0 55 | } 56 | ] 57 | } 58 | }, 59 | { 60 | "polynomialPayoutCurvePiece": { 61 | "payoutPoints": [ 62 | { 63 | "eventOutcome": 5, 64 | "outcomePayout": 200000000, 65 | "extraPrecision": 0 66 | }, 67 | { 68 | "eventOutcome": 1023, 69 | "outcomePayout": 200000000, 70 | "extraPrecision": 0 71 | } 72 | ] 73 | } 74 | } 75 | ] 76 | }, 77 | "roundingIntervals": { 78 | "intervals": [ 79 | { 80 | "beginInterval": 0, 81 | "roundingMod": 1 82 | } 83 | ] 84 | }, 85 | "differenceParams": null, 86 | "oracleNumericInfos": { 87 | "base": 2, 88 | "nbDigits": [ 89 | 10 90 | ] 91 | } 92 | } 93 | }, 94 | "oracleAnnouncements": [ 95 | { 96 | "announcementSignature": "706e97a76e5e4c25e2f1c180f7f6b5596304ae1c84c602cb3cb97c5b878ede942e6e4e9cabadb9f3b7ec5eb0370d92b2e8f0b3df05530b111cc69a633bf16908", 97 | "oraclePublicKey": "8a629938a0b7700ae7357c5d4447453aa502d4b644f8c62baad5d406d58b7f6f", 98 | "oracleEvent": { 99 | "oracleNonces": [ 100 | "7bc4eae76b8fa69d241b812e681f535dc93ba171c6d752813ac7710cb401b81b", 101 | "d01ea767509b40360e7a2b3ac1e4caf7c114760ab5e2091de426a6942a55fffc", 102 | "021d1d3b4e33876fc37fe106354a4109dcc064f24aae36d752e303f54ee0436d", 103 | "f7cedc1b24d697098484210110e07ee891569ee678c6616eb70990807e8a38ba", 104 | "b2d587c50e7dbcb0c975595835d966141f396ae849f8745f19cc877099842899", 105 | "9973eafc9436c8fe772e6ba86d35a189a0da03d0d05ae6299d1322ff755932b7", 106 | "d6d2333c3be484672b0ac3c8b4b407db08aa6a2c634240d77fbb39191e7f4658", 107 | "bd65cb669dd9eb467fd748a84f097165210e587a97677fb925be0f45d8d9e142", 108 | "b04bb0e368aaae1657f8ec7b50a9411dd96fdd763fb6732a83df52a95848b2cc", 109 | "51ada014e7194a596efab67568da0d0f622950517f3807a6ec555fcbebfceecd" 110 | ], 111 | "eventMaturityEpoch": 1623133104, 112 | "eventDescriptor": { 113 | "digitDecompositionEvent": { 114 | "base": 2, 115 | "isSigned": false, 116 | "unit": "sats/sec", 117 | "precision": 0, 118 | "nbDigits": 10 119 | } 120 | }, 121 | "eventId": "Test" 122 | } 123 | } 124 | ], 125 | "threshold": 1 126 | } 127 | ], 128 | "counterParty": "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", 129 | "offerParams": { 130 | "fundPubkey": "02d962a5e200e3c4cd9d425212d87cf92924d875126f9f6168b3757c6cb2ec419b", 131 | "changeScriptPubkey": "001479ee50e61e88a21baa2b5124b881c188bdf63c37", 132 | "changeSerialId": 13956474821580554639, 133 | "payoutScriptPubkey": "0014ed9c3ca30e6ff4b93c86ab4c7ea5a8efde31b874", 134 | "payoutSerialId": 14595850945083503669, 135 | "inputs": [ 136 | { 137 | "outpoint": "cac38ae578ed4ce3f32f60b25fa44768bb29c200beb2be80854b56802fbdc10f:0", 138 | "maxWitnessLen": 107, 139 | "redeemScript": "", 140 | "serialId": 4408916189615191417 141 | } 142 | ], 143 | "inputAmount": 5000000000, 144 | "collateral": 100000000 145 | }, 146 | "totalCollateral": 150000000, 147 | "fundingInputs": [ 148 | { 149 | "inputSerialId": 2904786313666239109, 150 | "prevTx": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000160014ce51088548a1bc02dc108da8ce5f5383c9309e630000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", 151 | "prevTxVout": 0, 152 | "sequence": 4294967295, 153 | "maxWitnessLen": 107, 154 | "redeemScript": "" 155 | } 156 | ], 157 | "fundOutputSerialId": 16475280753107887427, 158 | "feeRatePerVb": 2, 159 | "cetLocktime": 1623133104, 160 | "refundLocktime": 1623737904, 161 | "keysId": [ 162 | 16, 163 | 175, 164 | 248, 165 | 204, 166 | 229, 167 | 173, 168 | 124, 169 | 107, 170 | 34, 171 | 204, 172 | 75, 173 | 138, 174 | 154, 175 | 151, 176 | 193, 177 | 8, 178 | 142, 179 | 116, 180 | 180, 181 | 156, 182 | 90, 183 | 229, 184 | 80, 185 | 251, 186 | 77, 187 | 5, 188 | 215, 189 | 234, 190 | 70, 191 | 117, 192 | 68, 193 | 208 194 | ] 195 | } 196 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_numerical_invalid_rounding_interval.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": [ 3 | 9, 4 | 164, 5 | 248, 6 | 98, 7 | 221, 8 | 224, 9 | 186, 10 | 237, 11 | 180, 12 | 155, 13 | 198, 14 | 201, 15 | 213, 16 | 182, 17 | 7, 18 | 99, 19 | 192, 20 | 179, 21 | 34, 22 | 214, 23 | 206, 24 | 92, 25 | 177, 26 | 1, 27 | 209, 28 | 63, 29 | 226, 30 | 247, 31 | 31, 32 | 128, 33 | 33, 34 | 115 35 | ], 36 | "isOfferParty": false, 37 | "contractInfo": [ 38 | { 39 | "contractDescriptor": { 40 | "numerical": { 41 | "payoutFunction": { 42 | "payoutFunctionPieces": [ 43 | { 44 | "polynomialPayoutCurvePiece": { 45 | "payoutPoints": [ 46 | { 47 | "eventOutcome": 0, 48 | "outcomePayout": 0, 49 | "extraPrecision": 0 50 | }, 51 | { 52 | "eventOutcome": 5, 53 | "outcomePayout": 200000000, 54 | "extraPrecision": 0 55 | } 56 | ] 57 | } 58 | }, 59 | { 60 | "polynomialPayoutCurvePiece": { 61 | "payoutPoints": [ 62 | { 63 | "eventOutcome": 5, 64 | "outcomePayout": 200000000, 65 | "extraPrecision": 0 66 | }, 67 | { 68 | "eventOutcome": 1023, 69 | "outcomePayout": 200000000, 70 | "extraPrecision": 0 71 | } 72 | ] 73 | } 74 | } 75 | ] 76 | }, 77 | "roundingIntervals": { 78 | "intervals": [ 79 | { 80 | "beginInterval": 1, 81 | "roundingMod": 1 82 | } 83 | ] 84 | }, 85 | "differenceParams": null, 86 | "oracleNumericInfos": { 87 | "base": 2, 88 | "nbDigits": [ 89 | 10 90 | ] 91 | } 92 | } 93 | }, 94 | "oracleAnnouncements": [ 95 | { 96 | "announcementSignature": "706e97a76e5e4c25e2f1c180f7f6b5596304ae1c84c602cb3cb97c5b878ede942e6e4e9cabadb9f3b7ec5eb0370d92b2e8f0b3df05530b111cc69a633bf16908", 97 | "oraclePublicKey": "8a629938a0b7700ae7357c5d4447453aa502d4b644f8c62baad5d406d58b7f6f", 98 | "oracleEvent": { 99 | "oracleNonces": [ 100 | "7bc4eae76b8fa69d241b812e681f535dc93ba171c6d752813ac7710cb401b81b", 101 | "d01ea767509b40360e7a2b3ac1e4caf7c114760ab5e2091de426a6942a55fffc", 102 | "021d1d3b4e33876fc37fe106354a4109dcc064f24aae36d752e303f54ee0436d", 103 | "f7cedc1b24d697098484210110e07ee891569ee678c6616eb70990807e8a38ba", 104 | "b2d587c50e7dbcb0c975595835d966141f396ae849f8745f19cc877099842899", 105 | "9973eafc9436c8fe772e6ba86d35a189a0da03d0d05ae6299d1322ff755932b7", 106 | "d6d2333c3be484672b0ac3c8b4b407db08aa6a2c634240d77fbb39191e7f4658", 107 | "bd65cb669dd9eb467fd748a84f097165210e587a97677fb925be0f45d8d9e142", 108 | "b04bb0e368aaae1657f8ec7b50a9411dd96fdd763fb6732a83df52a95848b2cc", 109 | "51ada014e7194a596efab67568da0d0f622950517f3807a6ec555fcbebfceecd" 110 | ], 111 | "eventMaturityEpoch": 1623133104, 112 | "eventDescriptor": { 113 | "digitDecompositionEvent": { 114 | "base": 2, 115 | "isSigned": false, 116 | "unit": "sats/sec", 117 | "precision": 0, 118 | "nbDigits": 10 119 | } 120 | }, 121 | "eventId": "Test" 122 | } 123 | } 124 | ], 125 | "threshold": 1 126 | } 127 | ], 128 | "counterParty": "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", 129 | "offerParams": { 130 | "fundPubkey": "02d962a5e200e3c4cd9d425212d87cf92924d875126f9f6168b3757c6cb2ec419b", 131 | "changeScriptPubkey": "001479ee50e61e88a21baa2b5124b881c188bdf63c37", 132 | "changeSerialId": 13956474821580554639, 133 | "payoutScriptPubkey": "0014ed9c3ca30e6ff4b93c86ab4c7ea5a8efde31b874", 134 | "payoutSerialId": 14595850945083503669, 135 | "inputs": [ 136 | { 137 | "outpoint": "cac38ae578ed4ce3f32f60b25fa44768bb29c200beb2be80854b56802fbdc10f:0", 138 | "maxWitnessLen": 107, 139 | "redeemScript": "", 140 | "serialId": 4408916189615191417 141 | } 142 | ], 143 | "inputAmount": 5000000000, 144 | "collateral": 100000000 145 | }, 146 | "totalCollateral": 200000000, 147 | "fundingInputs": [ 148 | { 149 | "inputSerialId": 2904786313666239109, 150 | "prevTx": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000160014ce51088548a1bc02dc108da8ce5f5383c9309e630000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", 151 | "prevTxVout": 0, 152 | "sequence": 4294967295, 153 | "maxWitnessLen": 107, 154 | "redeemScript": "" 155 | } 156 | ], 157 | "fundOutputSerialId": 16475280753107887427, 158 | "feeRatePerVb": 2, 159 | "cetLocktime": 1623133104, 160 | "refundLocktime": 1623737904, 161 | "keysId": [ 162 | 16, 163 | 175, 164 | 248, 165 | 204, 166 | 229, 167 | 173, 168 | 124, 169 | 107, 170 | 34, 171 | 204, 172 | 75, 173 | 138, 174 | 154, 175 | 151, 176 | 193, 177 | 8, 178 | 142, 179 | 116, 180 | 180, 181 | 156, 182 | 90, 183 | 229, 184 | 80, 185 | 251, 186 | 77, 187 | 5, 188 | 215, 189 | 234, 190 | 70, 191 | 117, 192 | 68, 193 | 208 194 | ] 195 | } 196 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_enum_oracle_with_diff_payout.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": [ 3 | 155, 4 | 212, 5 | 62, 6 | 219, 7 | 175, 8 | 95, 9 | 42, 10 | 204, 11 | 223, 12 | 54, 13 | 197, 14 | 38, 15 | 211, 16 | 63, 17 | 152, 18 | 35, 19 | 68, 20 | 201, 21 | 208, 22 | 36, 23 | 124, 24 | 66, 25 | 82, 26 | 30, 27 | 41, 28 | 1, 29 | 4, 30 | 234, 31 | 154, 32 | 144, 33 | 105, 34 | 176 35 | ], 36 | "isOfferParty": false, 37 | "contractInfo": [ 38 | { 39 | "contractDescriptor": { 40 | "enum": { 41 | "outcomePayouts": [ 42 | { 43 | "outcome": "a", 44 | "payout": { 45 | "offer": 200000000, 46 | "accept": 0 47 | } 48 | }, 49 | { 50 | "outcome": "b", 51 | "payout": { 52 | "offer": 0, 53 | "accept": 200000000 54 | } 55 | }, 56 | { 57 | "outcome": "c", 58 | "payout": { 59 | "offer": 200000000, 60 | "accept": 0 61 | } 62 | }, 63 | { 64 | "outcome": "d", 65 | "payout": { 66 | "offer": 0, 67 | "accept": 200000000 68 | } 69 | } 70 | ] 71 | } 72 | }, 73 | "oracleAnnouncements": [ 74 | { 75 | "announcementSignature": "afc2705c66bb8402c615ba757a02e11acd844a2275cbfb59dbe3b8046832c44ba7f4bd5c2147f6798cf01d1828d633ff8652d0a32a7b9fe37fd599b51a2757af", 76 | "oraclePublicKey": "db0b565c927bd86c5877cb6f56fb3281100eaf88b99a8d5f0717565429e4774d", 77 | "oracleEvent": { 78 | "oracleNonces": [ 79 | "2bd5c8e9703d13d094131acd9af1f062d6aab5ac9de4550dcaecf2a1de8d12aa" 80 | ], 81 | "eventMaturityEpoch": 1623133104, 82 | "eventDescriptor": { 83 | "enumEvent": { 84 | "outcomes": [ 85 | "a", 86 | "b", 87 | "c", 88 | "d" 89 | ] 90 | } 91 | }, 92 | "eventId": "Test" 93 | } 94 | }, 95 | { 96 | "announcementSignature": "b399ebc42a8ccea43058615aeed21ed6edddb35ec6de92628286532d9fb8b44924771e6c4e228aa3d0928df640b695670d2a0fdda86dc8fe8e3fe1f0638e3c81", 97 | "oraclePublicKey": "9776b9399283a4f12fa61cb65a5e43bbf483f0d3d6b3c689d02648af503003be", 98 | "oracleEvent": { 99 | "oracleNonces": [ 100 | "ca65995f15e6cbf3cf6c64576f037f3939b981538020c868a06b869c9e2399d3" 101 | ], 102 | "eventMaturityEpoch": 1623133104, 103 | "eventDescriptor": { 104 | "enumEvent": { 105 | "outcomes": [ 106 | "a", 107 | "b", 108 | "c", 109 | "d" 110 | ] 111 | } 112 | }, 113 | "eventId": "Test" 114 | } 115 | }, 116 | { 117 | "announcementSignature": "7fd48768a78ffaaf2d16cc5a47c7224c745f2560c63af0d1e67d0ccecaeb07e80d906fa9df389ec4782b52c4c9410745eaf01fa5a143146bf3855e3695d35516", 118 | "oraclePublicKey": "3de8d1ec423c641d2aecf3f39808a942bb2eb371a04a00323a927ec607b015ff", 119 | "oracleEvent": { 120 | "oracleNonces": [ 121 | "a49b73a5260e01713f14474ac140476ae30fbbfd5134341db6e6654eb845eb71" 122 | ], 123 | "eventMaturityEpoch": 1623133104, 124 | "eventDescriptor": { 125 | "enumEvent": { 126 | "outcomes": [ 127 | "a", 128 | "b", 129 | "d" 130 | ] 131 | } 132 | }, 133 | "eventId": "Test" 134 | } 135 | } 136 | ], 137 | "threshold": 3 138 | } 139 | ], 140 | "counterParty": "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", 141 | "offerParams": { 142 | "fundPubkey": "0315b3963f5f1043fd9945e3eeb729ecb379f07892a64a9c450c966db6853714b1", 143 | "changeScriptPubkey": "00148332f8eea19820571c912f425531a238cb8fc526", 144 | "changeSerialId": 15247286615727438658, 145 | "payoutScriptPubkey": "0014ed53d0938791fc51cdb61330c417e7205c9437d5", 146 | "payoutSerialId": 9358551406094833867, 147 | "inputs": [ 148 | { 149 | "outpoint": "120103080fcd221520a537cca2764064387f39939ebb692c748c870c0b17a61d:0", 150 | "maxWitnessLen": 107, 151 | "redeemScript": "", 152 | "serialId": 2904786313666239109 153 | } 154 | ], 155 | "inputAmount": 5000000000, 156 | "collateral": 100000000 157 | }, 158 | "totalCollateral": 200000000, 159 | "fundingInputs": [ 160 | { 161 | "inputSerialId": 2904786313666239109, 162 | "prevTx": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000160014ce51088548a1bc02dc108da8ce5f5383c9309e630000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", 163 | "prevTxVout": 0, 164 | "sequence": 4294967295, 165 | "maxWitnessLen": 107, 166 | "redeemScript": "" 167 | } 168 | ], 169 | "fundOutputSerialId": 12412566359681147037, 170 | "feeRatePerVb": 2, 171 | "cetLocktime": 1623133104, 172 | "refundLocktime": 1623737904, 173 | "keysId": [ 174 | 16, 175 | 175, 176 | 248, 177 | 204, 178 | 229, 179 | 173, 180 | 124, 181 | 107, 182 | 34, 183 | 204, 184 | 75, 185 | 138, 186 | 154, 187 | 151, 188 | 193, 189 | 8, 190 | 142, 191 | 116, 192 | 180, 193 | 156, 194 | 90, 195 | 229, 196 | 80, 197 | 251, 198 | 77, 199 | 5, 200 | 215, 201 | 234, 202 | 70, 203 | 117, 204 | 68, 205 | 208 206 | ] 207 | } 208 | -------------------------------------------------------------------------------- /dlc-manager/test_inputs/offer_enum_collateral_not_equal_payout.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": [ 3 | 155, 4 | 212, 5 | 62, 6 | 219, 7 | 175, 8 | 95, 9 | 42, 10 | 204, 11 | 223, 12 | 54, 13 | 197, 14 | 38, 15 | 211, 16 | 63, 17 | 152, 18 | 35, 19 | 68, 20 | 201, 21 | 208, 22 | 36, 23 | 124, 24 | 66, 25 | 82, 26 | 30, 27 | 41, 28 | 1, 29 | 4, 30 | 234, 31 | 154, 32 | 144, 33 | 105, 34 | 176 35 | ], 36 | "isOfferParty": false, 37 | "contractInfo": [ 38 | { 39 | "contractDescriptor": { 40 | "enum": { 41 | "outcomePayouts": [ 42 | { 43 | "outcome": "a", 44 | "payout": { 45 | "offer": 200000000, 46 | "accept": 0 47 | } 48 | }, 49 | { 50 | "outcome": "b", 51 | "payout": { 52 | "offer": 0, 53 | "accept": 200000000 54 | } 55 | }, 56 | { 57 | "outcome": "c", 58 | "payout": { 59 | "offer": 200000000, 60 | "accept": 0 61 | } 62 | }, 63 | { 64 | "outcome": "d", 65 | "payout": { 66 | "offer": 0, 67 | "accept": 200000000 68 | } 69 | } 70 | ] 71 | } 72 | }, 73 | "oracleAnnouncements": [ 74 | { 75 | "announcementSignature": "afc2705c66bb8402c615ba757a02e11acd844a2275cbfb59dbe3b8046832c44ba7f4bd5c2147f6798cf01d1828d633ff8652d0a32a7b9fe37fd599b51a2757af", 76 | "oraclePublicKey": "db0b565c927bd86c5877cb6f56fb3281100eaf88b99a8d5f0717565429e4774d", 77 | "oracleEvent": { 78 | "oracleNonces": [ 79 | "2bd5c8e9703d13d094131acd9af1f062d6aab5ac9de4550dcaecf2a1de8d12aa" 80 | ], 81 | "eventMaturityEpoch": 1623133104, 82 | "eventDescriptor": { 83 | "enumEvent": { 84 | "outcomes": [ 85 | "a", 86 | "b", 87 | "c", 88 | "d" 89 | ] 90 | } 91 | }, 92 | "eventId": "Test" 93 | } 94 | }, 95 | { 96 | "announcementSignature": "b399ebc42a8ccea43058615aeed21ed6edddb35ec6de92628286532d9fb8b44924771e6c4e228aa3d0928df640b695670d2a0fdda86dc8fe8e3fe1f0638e3c81", 97 | "oraclePublicKey": "9776b9399283a4f12fa61cb65a5e43bbf483f0d3d6b3c689d02648af503003be", 98 | "oracleEvent": { 99 | "oracleNonces": [ 100 | "ca65995f15e6cbf3cf6c64576f037f3939b981538020c868a06b869c9e2399d3" 101 | ], 102 | "eventMaturityEpoch": 1623133104, 103 | "eventDescriptor": { 104 | "enumEvent": { 105 | "outcomes": [ 106 | "a", 107 | "b", 108 | "c", 109 | "d" 110 | ] 111 | } 112 | }, 113 | "eventId": "Test" 114 | } 115 | }, 116 | { 117 | "announcementSignature": "7fd48768a78ffaaf2d16cc5a47c7224c745f2560c63af0d1e67d0ccecaeb07e80d906fa9df389ec4782b52c4c9410745eaf01fa5a143146bf3855e3695d35516", 118 | "oraclePublicKey": "3de8d1ec423c641d2aecf3f39808a942bb2eb371a04a00323a927ec607b015ff", 119 | "oracleEvent": { 120 | "oracleNonces": [ 121 | "a49b73a5260e01713f14474ac140476ae30fbbfd5134341db6e6654eb845eb71" 122 | ], 123 | "eventMaturityEpoch": 1623133104, 124 | "eventDescriptor": { 125 | "enumEvent": { 126 | "outcomes": [ 127 | "a", 128 | "b", 129 | "c", 130 | "d" 131 | ] 132 | } 133 | }, 134 | "eventId": "Test" 135 | } 136 | } 137 | ], 138 | "threshold": 3 139 | } 140 | ], 141 | "counterParty": "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", 142 | "offerParams": { 143 | "fundPubkey": "0315b3963f5f1043fd9945e3eeb729ecb379f07892a64a9c450c966db6853714b1", 144 | "changeScriptPubkey": "00148332f8eea19820571c912f425531a238cb8fc526", 145 | "changeSerialId": 15247286615727438658, 146 | "payoutScriptPubkey": "0014ed53d0938791fc51cdb61330c417e7205c9437d5", 147 | "payoutSerialId": 9358551406094833867, 148 | "inputs": [ 149 | { 150 | "outpoint": "120103080fcd221520a537cca2764064387f39939ebb692c748c870c0b17a61d:0", 151 | "maxWitnessLen": 107, 152 | "redeemScript": "", 153 | "serialId": 2904786313666239109 154 | } 155 | ], 156 | "inputAmount": 5000000000, 157 | "collateral": 100000000 158 | }, 159 | "totalCollateral": 150000000, 160 | "fundingInputs": [ 161 | { 162 | "inputSerialId": 2904786313666239109, 163 | "prevTx": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000160014ce51088548a1bc02dc108da8ce5f5383c9309e630000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", 164 | "prevTxVout": 0, 165 | "sequence": 4294967295, 166 | "maxWitnessLen": 107, 167 | "redeemScript": "" 168 | } 169 | ], 170 | "fundOutputSerialId": 12412566359681147037, 171 | "feeRatePerVb": 2, 172 | "cetLocktime": 1623133104, 173 | "refundLocktime": 1623737904, 174 | "keysId": [ 175 | 16, 176 | 175, 177 | 248, 178 | 204, 179 | 229, 180 | 173, 181 | 124, 182 | 107, 183 | 34, 184 | 204, 185 | 75, 186 | 138, 187 | 154, 188 | 151, 189 | 193, 190 | 8, 191 | 142, 192 | 116, 193 | 180, 194 | 156, 195 | 90, 196 | 229, 197 | 80, 198 | 251, 199 | 77, 200 | 5, 201 | 215, 202 | 234, 203 | 70, 204 | 117, 205 | 68, 206 | 208 207 | ] 208 | } 209 | -------------------------------------------------------------------------------- /dlc-manager/src/channel/offered_channel.rs: -------------------------------------------------------------------------------- 1 | //! # A channel is offered when an offer was made or received. This module contains 2 | //! the model for it and method for working with it. 3 | 4 | use dlc::PartyParams; 5 | use dlc_messages::channel::OfferChannel; 6 | // use dlc_messages::channel::OfferChannel; 7 | use secp256k1_zkp::PublicKey; 8 | 9 | use crate::{ 10 | contract::offered_contract::OfferedContract, conversion_utils::get_tx_input_infos, 11 | error::Error, ChannelId, ContractId, KeysId, 12 | }; 13 | 14 | use super::party_points::PartyBasePoints; 15 | 16 | #[derive(Clone, Debug)] 17 | #[cfg_attr( 18 | feature = "use-serde", 19 | derive(serde::Serialize, serde::Deserialize), 20 | serde(rename_all = "camelCase") 21 | )] 22 | /// A DLC channel for which an [`dlc_messages::channel::OfferChannel`] message 23 | /// was sent or received. 24 | pub struct OfferedChannel { 25 | /// The temporary [`crate::ContractId`] of the contract that was offered for 26 | /// channel setup. 27 | pub offered_contract_id: ContractId, 28 | /// The temporary [`crate::ChannelId`] of the channel. 29 | pub temporary_channel_id: ChannelId, 30 | /// The set of base points that the offer party will use during the lifetime 31 | /// of the channel. 32 | pub party_points: PartyBasePoints, 33 | /// The per update point for the initial establishment of the channel. 34 | pub per_update_point: PublicKey, 35 | /// The image of the seed used by the offer party to derive all per update 36 | /// points (Will be `None` on the accept party side.) 37 | pub offer_per_update_seed: Option, 38 | /// Whether the local party is the offer party or not. 39 | pub is_offer_party: bool, 40 | /// The [`secp256k1_zkp::PublicKey`] of the counter party's node. 41 | pub counter_party: PublicKey, 42 | /// The nSequence value to use for the CETs. 43 | pub cet_nsequence: u32, 44 | } 45 | 46 | impl OfferedChannel { 47 | pub(crate) fn get_offer_channel_msg(&self, offered_contract: &OfferedContract) -> OfferChannel { 48 | let party_points = &self.party_points; 49 | OfferChannel { 50 | protocol_version: crate::conversion_utils::PROTOCOL_VERSION, 51 | contract_flags: 0, 52 | chain_hash: crate::conversion_utils::BITCOIN_CHAINHASH, 53 | temporary_contract_id: offered_contract.id, 54 | temporary_channel_id: self.temporary_channel_id, 55 | contract_info: offered_contract.into(), 56 | funding_pubkey: offered_contract.offer_params.fund_pubkey, 57 | revocation_basepoint: party_points.revocation_basepoint, 58 | publish_basepoint: party_points.publish_basepoint, 59 | own_basepoint: party_points.own_basepoint, 60 | first_per_update_point: self.per_update_point, 61 | payout_spk: offered_contract.offer_params.payout_script_pubkey.clone(), 62 | payout_serial_id: offered_contract.offer_params.payout_serial_id, 63 | offer_collateral: offered_contract.offer_params.collateral, 64 | funding_inputs: offered_contract.funding_inputs.clone(), 65 | change_spk: offered_contract.offer_params.change_script_pubkey.clone(), 66 | change_serial_id: offered_contract.offer_params.change_serial_id, 67 | cet_locktime: offered_contract.cet_locktime, 68 | refund_locktime: offered_contract.refund_locktime, 69 | fee_rate_per_vb: offered_contract.fee_rate_per_vb, 70 | fund_output_serial_id: offered_contract.fund_output_serial_id, 71 | cet_nsequence: crate::manager::CET_NSEQUENCE, 72 | } 73 | } 74 | 75 | /// Creates an [`OfferedChannel`] and [`crate::contract::offered_contract::OfferedContract`] 76 | /// from an [`dlc_messages::channel::OfferChannel`] message. Fails if the 77 | /// transactions provided for funding cannot be decoded or the UTXO information 78 | /// are invalid, or if the contract information is invalid. 79 | pub fn from_offer_channel( 80 | offer_channel: &OfferChannel, 81 | counter_party: PublicKey, 82 | keys_id: KeysId, 83 | ) -> Result<(OfferedChannel, OfferedContract), Error> { 84 | let channel = OfferedChannel { 85 | offered_contract_id: offer_channel.temporary_contract_id, 86 | temporary_channel_id: offer_channel.temporary_channel_id, 87 | party_points: PartyBasePoints { 88 | own_basepoint: offer_channel.own_basepoint, 89 | revocation_basepoint: offer_channel.revocation_basepoint, 90 | publish_basepoint: offer_channel.publish_basepoint, 91 | }, 92 | per_update_point: offer_channel.first_per_update_point, 93 | offer_per_update_seed: None, 94 | is_offer_party: false, 95 | counter_party, 96 | cet_nsequence: offer_channel.cet_nsequence, 97 | }; 98 | 99 | let (inputs, input_amount) = get_tx_input_infos(&offer_channel.funding_inputs)?; 100 | 101 | let contract = OfferedContract { 102 | id: offer_channel.temporary_contract_id, 103 | is_offer_party: false, 104 | contract_info: crate::conversion_utils::get_contract_info_and_announcements( 105 | &offer_channel.contract_info, 106 | )?, 107 | counter_party, 108 | offer_params: PartyParams { 109 | fund_pubkey: offer_channel.funding_pubkey, 110 | change_script_pubkey: offer_channel.change_spk.clone(), 111 | change_serial_id: offer_channel.change_serial_id, 112 | payout_script_pubkey: offer_channel.payout_spk.clone(), 113 | payout_serial_id: offer_channel.payout_serial_id, 114 | collateral: offer_channel.offer_collateral, 115 | inputs, 116 | input_amount, 117 | }, 118 | cet_locktime: offer_channel.cet_locktime, 119 | refund_locktime: offer_channel.refund_locktime, 120 | fee_rate_per_vb: offer_channel.fee_rate_per_vb, 121 | fund_output_serial_id: offer_channel.fund_output_serial_id, 122 | funding_inputs: offer_channel.funding_inputs.clone(), 123 | total_collateral: offer_channel.contract_info.get_total_collateral(), 124 | keys_id, 125 | }; 126 | 127 | Ok((channel, contract)) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /sample/Readme.md: -------------------------------------------------------------------------------- 1 | # rust-dlc sample 2 | 3 | Example of combining the various components of the rust-dlc library together with the custom message handler of [rust-lightning](https://github.com/rust-bitcoin/rust-lightning) to enable networked communication. 4 | 5 | Originally based on the code from the [ldk sample](https://github.com/lightningdevkit/ldk-sample). 6 | 7 | Example configurations and contract input are available in the [examples](./examples) folder. 8 | In order to use the [example contract](./examples/contracts/numerical_contract_input.json) you will need to update the `event_id`. 9 | Replace the part after `btcusd` with a UNIX timestamp in the future. 10 | 11 | Use the [helper script](../scripts/gen-sample-offer.sh) to create the example contract 1 hour in the future. This will duplicate the `numerical_contract_input.json` sample under the name `sample_contract.json`, and modify the `event_id` with a UNIX timestamp 1 hour in the future. Manually update the value for a longer time period. 12 | 13 | 14 | ## Quick run 15 | 16 | To give a quick try to this sample, run the following set of commands (assuming that the working directory is the one in which this readme is located and that `docker` (and `docker-compose`, for V1; V2 is part of `docker`) is available on your machine): 17 | 18 | ```bash 19 | docker compose --profile oracle up -d 20 | docker compose exec bitcoind /scripts/create_wallets.sh 21 | cargo run ./examples/configurations/alice.yml 22 | ``` 23 | 24 | In a different terminal: 25 | ```bash 26 | cargo run ./examples/configurations/bob.yml 27 | ``` 28 | 29 | ### On chain DLC 30 | 31 | From the second instance (Bob), type: 32 | ``` 33 | offercontract 02c84f8e151590e718d22e528c55f14c0042c66e68c3f793d7b3b8bf5ae630c648@127.0.0.1:9000 ./examples/contracts/sample_contract.json 34 | ``` 35 | Replacing the public key by the one that was displayed when starting the first instance. 36 | 37 | From the first instance (Alice), type: 38 | ``` 39 | listoffers 40 | ``` 41 | You should see the id of the contract that was offered by Bob. 42 | A JSON file with contract information should also have been saved in Alice's data folder (`./dlc_sample_alice/offers/xxx.json`). 43 | Note: Just pressing `Enter` will also trigger checking and handling of pending messages. 44 | 45 | Alice can now accept the offer by typing: 46 | ``` 47 | acceptoffer xxxxx 48 | ``` 49 | replacing `xxxxx` with the contract id that was previously displayed. 50 | 51 | This will make Alice's instance send an accept message to Bob. 52 | In Bob's instance, typing `listcontracts` will trigger the processing of the message, and you should see that the contract will be in `Signed`, as Bob will automatically reply to Alice with a `Sign message`. 53 | 54 | Typing the same command in Alice's instance will make Alice broadcast the fund transaction. 55 | 56 | Now in yet another terminal (still from the same location) run: 57 | ```bash 58 | docker compose exec bitcoind /scripts/generate_blocks.sh 59 | ``` 60 | 61 | This will generate some blocks so that the fund transaction is confirmed. 62 | 63 | Typing `listcontracts` in either instance should now show the contract as `Confirmed`. 64 | 65 | Once the maturity of the contract is reached, typing `listcontracts` once more will retrieve the attestation from the oracle and close the contract, displaying the event outcome (in decomposed binary format) and the profit and loss for the given instance. 66 | 67 | ### DLC channels 68 | 69 | From the second instance (Bob), type: 70 | ``` 71 | offerchannel 02c84f8e151590e718d22e528c55f14c0042c66e68c3f793d7b3b8bf5ae630c648@127.0.0.1:9000 ./examples/contracts/sample_contract.json 72 | ``` 73 | Replacing the public key by the one that was displayed when starting the first instance. 74 | 75 | From the first instance (Alice), type: 76 | ``` 77 | listchanneloffers 78 | ``` 79 | You should see the id of the contract that was offered by Bob. 80 | A JSON file with contract information should also have been saved in Alice's data folder (`./dlc_sample_alice/offers/xxx.json`). 81 | 82 | Alice can now accept the offer by typing: 83 | ``` 84 | acceptchannel xxxxx 85 | ``` 86 | replacing `xxxxx` with the channel id that was previously displayed. 87 | 88 | This will make Alice's instance send an accept message to Bob. 89 | In Bob's instance, typing `listsignedchannels` will trigger the processing of the message, and you should see that the channel id being displayed (different than the one during the offer as it was a temporary id previously). 90 | 91 | Typing the same command in Alice's instance will make Alice broadcast the fund transaction. 92 | 93 | Now in yet another terminal (still from the same location) run: 94 | ```bash 95 | docker compose exec bitcoind /scripts/generate_blocks.sh 96 | ``` 97 | 98 | This will generate some blocks so that the fund transaction is confirmed. 99 | The channel is now setup. 100 | 101 | #### Settle the contract in the channel 102 | 103 | One of the party can offer a settlement of the contract within the channel. 104 | To do so, use the `offersettlechannel` command, passing in the channel id (that you can get using the `listsignedchannels` command), as well as the proposed payout for the counter party. 105 | 106 | In the other terminal, use the `listsettlechanneloffers` to display the received settle offer. 107 | 108 | To accept the offer, use the `acceptsettlechanneloffer` passing the channel id as a parameter. 109 | Three messages need to be exchanged between the peers to properly settle the channel, press `Enter` once in the terminal where the settle offer was made, once where the settle offer was received and once more where the settle offer was made for the settlement to be finalized. 110 | 111 | To reject the offer, use the `rejectsettlechanneloffer` command, passing the channel id as a parameter. 112 | 113 | #### Renew the contract in the channel 114 | 115 | One of the party can offer to renew the contract in the channel. 116 | To do so, use the `offerchannelrenew` command, passing in the channel id (that you can get using the `listsignedchannels` command), the proposed payout for the counter party, and the path to the json file containing the information about the contract to offer. 117 | 118 | In the other terminal, use the `listrenewchanneloffers` to display the received settle offer. 119 | 120 | To accept the offer, use the `acceptrenewchanneloffer` passing the channel id as a parameter. 121 | Three messages need to be exchanged between the peers to properly settle the channel, press `Enter` once in the terminal where the settle offer was made, once where the settle offer was received and once more where the settle offer was made for the settlement to be finalized. 122 | 123 | To reject the offer, use the `rejectrenewchanneloffer` command, passing the channel id as a parameter. 124 | -------------------------------------------------------------------------------- /dlc-manager/src/channel/ser.rs: -------------------------------------------------------------------------------- 1 | //! # Serialization implementation for DLC channel related structures. 2 | use super::accepted_channel::AcceptedChannel; 3 | use super::offered_channel::OfferedChannel; 4 | use super::party_points::PartyBasePoints; 5 | use super::signed_channel::{SignedChannel, SignedChannelState}; 6 | use super::{ClosedChannel, ClosedPunishedChannel, ClosingChannel, FailedAccept, FailedSign}; 7 | 8 | use dlc_messages::ser_impls::{ 9 | read_ecdsa_adaptor_signature, read_string, write_ecdsa_adaptor_signature, write_string, 10 | }; 11 | use lightning::ln::msgs::DecodeError; 12 | use lightning::util::ser::{Readable, Writeable, Writer}; 13 | 14 | impl_dlc_writeable!(PartyBasePoints, { (own_basepoint, writeable), (publish_basepoint, writeable), (revocation_basepoint, writeable) }); 15 | impl_dlc_writeable!(OfferedChannel, { (offered_contract_id, writeable), (temporary_channel_id, writeable), (party_points, writeable), (per_update_point, writeable), (offer_per_update_seed, writeable), (is_offer_party, writeable), (counter_party, writeable), (cet_nsequence, writeable) }); 16 | impl_dlc_writeable!(AcceptedChannel, { 17 | (accepted_contract_id, writeable), 18 | (offer_base_points, writeable), 19 | (accept_base_points, writeable), 20 | (offer_per_update_point, writeable), 21 | (accept_per_update_point, writeable), 22 | (buffer_transaction, writeable), 23 | (buffer_script_pubkey, writeable), 24 | (temporary_channel_id, writeable), 25 | (channel_id, writeable), 26 | (accept_per_update_seed, writeable), 27 | (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), 28 | (counter_party, writeable) 29 | }); 30 | impl_dlc_writeable!(SignedChannel, { 31 | (channel_id, writeable), 32 | (counter_party, writeable), 33 | (temporary_channel_id, writeable), 34 | (fund_output_index, usize), 35 | (own_points, writeable), 36 | (own_params, { cb_writeable, dlc_messages::ser_impls::party_params::write, dlc_messages::ser_impls::party_params::read }), 37 | (own_per_update_point, writeable), 38 | (counter_points, writeable), 39 | (counter_per_update_point, writeable), 40 | (counter_params, { cb_writeable, dlc_messages::ser_impls::party_params::write, dlc_messages::ser_impls::party_params::read }), 41 | (state, writeable), 42 | (update_idx, writeable), 43 | (fund_tx, writeable), 44 | (fund_script_pubkey, writeable), 45 | (roll_back_state, option), 46 | (own_per_update_seed, writeable), 47 | (counter_party_commitment_secrets, writeable), 48 | (fee_rate_per_vb, writeable) 49 | }); 50 | 51 | impl_dlc_writeable_enum!( 52 | SignedChannelState,; 53 | (0, Established, {(signed_contract_id, writeable), (own_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (counter_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (buffer_transaction, writeable), (is_offer, writeable), (total_collateral, writeable), (keys_id, writeable)}), 54 | (1, SettledOffered, {(counter_payout, writeable), (next_per_update_point, writeable), (timeout, writeable), (keys_id, writeable)}), 55 | (2, SettledReceived, {(own_payout, writeable), (counter_payout, writeable), (counter_next_per_update_point, writeable), (keys_id, writeable)}), 56 | (3, SettledAccepted, {(counter_next_per_update_point, writeable), (own_next_per_update_point, writeable), (settle_tx, writeable), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (counter_payout, writeable), (keys_id, writeable)}), 57 | (4, SettledConfirmed, {(settle_tx, writeable), (counter_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (counter_next_per_update_point, writeable), (own_next_per_update_point, writeable), (timeout, writeable), (own_payout, writeable), (counter_payout, writeable), (keys_id, writeable) }), 58 | (5, Settled, {(settle_tx, writeable), (counter_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (own_settle_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (own_payout, writeable), (counter_payout, writeable), (keys_id, writeable)}), 59 | (6, RenewOffered, {(offered_contract_id, writeable), (counter_payout, writeable), (is_offer, writeable), (offer_next_per_update_point, writeable), (timeout, writeable), (keys_id, writeable)}), 60 | (7, RenewAccepted, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (timeout, writeable), (own_payout, writeable), (keys_id, writeable)}), 61 | (8, RenewConfirmed, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (offer_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (total_collateral, writeable), (keys_id, writeable)}), 62 | (10, RenewFinalized, {(contract_id, writeable), (prev_offer_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (offer_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (total_collateral, writeable), (keys_id, writeable)}), 63 | (9, Closing, {(buffer_transaction, writeable), (contract_id, writeable), (keys_id, writeable), (is_initiator, writeable)}), 64 | (11, CollaborativeCloseOffered, { (counter_payout, writeable), (offer_signature, writeable), (close_tx, writeable), (timeout, writeable), (keys_id, writeable) }) 65 | ;; 66 | ); 67 | 68 | impl_dlc_writeable!(FailedAccept, {(temporary_channel_id, writeable), (error_message, {cb_writeable, write_string, read_string}), (accept_message, writeable), (counter_party, writeable)}); 69 | impl_dlc_writeable!(FailedSign, {(channel_id, writeable), (error_message, {cb_writeable, write_string, read_string}), (sign_message, writeable), (counter_party, writeable)}); 70 | 71 | impl_dlc_writeable!(ClosingChannel, { 72 | (channel_id, writeable), 73 | (counter_party, writeable), 74 | (temporary_channel_id, writeable), 75 | (rollback_state, option), 76 | (buffer_transaction, writeable), 77 | (contract_id, writeable), 78 | (is_closer, writeable) 79 | 80 | }); 81 | impl_dlc_writeable!(ClosedChannel, {(channel_id, writeable), (counter_party, writeable), (temporary_channel_id, writeable)}); 82 | impl_dlc_writeable!(ClosedPunishedChannel, {(channel_id, writeable), (counter_party, writeable), (temporary_channel_id, writeable), (punish_txid, writeable)}); 83 | --------------------------------------------------------------------------------