Skip to content

Commit

Permalink
Merge pull request #42 from bob-collective/feat/proof-checking
Browse files Browse the repository at this point in the history
feat: proof checking and relay init
  • Loading branch information
gregdhill authored Nov 1, 2023
2 parents d97ae4e + 458926c commit 9b62efe
Show file tree
Hide file tree
Showing 17 changed files with 204 additions and 152 deletions.
22 changes: 21 additions & 1 deletion script/Bridge.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,31 @@
pragma solidity ^0.8.13;

import {Script, console2} from "forge-std/Script.sol";
import "../src/relay/LightRelay.sol";

contract BridgeScript is Script {
function setUp() public {}

function run() public {
vm.broadcast();
bytes memory data =
hex"04000000473ed7b7ef2fce828c318fd5e5868344a5356c9e93b6040400000000000000004409cae5b7b2f8f18ea55f558c9bfa7c5f4778a1a53172a48fc57e172d0ed3d264c5eb56c3a40618af9bc1c7";
uint256 height = 403200;

uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

bytes memory genesisHeader = vm.envBytes("GENESIS_HEADER");
uint256 genesisHeight = vm.envUint("GENESIS_HEIGHT");
bytes memory retargetHeaders = vm.envBytes("RETARGET_HEADERS");

vm.startBroadcast(deployerPrivateKey);
LightRelay relay = new LightRelay();

// Initialize relay at the given block
relay.genesis(genesisHeader, genesisHeight, 1);

// submit retarget
relay.retarget(retargetHeaders);

vm.stopBroadcast();
}
}
4 changes: 3 additions & 1 deletion sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"types": "dist/index.d.ts",
"scripts": {
"test": "ts-mocha test/*.ts",
"deploy-relay": "ts-node scripts/init-bridge.ts",
"build": "tsc -p tsconfig.json"
},
"files": [
Expand All @@ -25,7 +26,8 @@
"tiny-secp256k1": "^2.2.3",
"ts-mocha": "^10.0.0",
"ts-node-dev": "^2.0.0",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"yargs": "^17.5.1"
},
"dependencies": {
"bitcoinjs-lib": "^6.1.5"
Expand Down
54 changes: 54 additions & 0 deletions sdk/scripts/init-bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* eslint @typescript-eslint/no-var-requires: "off" */

import { DefaultElectrsClient } from "../src/electrs";
const yargs = require("yargs/yargs");
const { hideBin } = require("yargs/helpers");
const { exec } = require('child_process');

const args = yargs(hideBin(process.argv))
.option("init-height", {
description: "Height of the bitcoin chain to initialize the relay at",
type: "number",
demandOption: true,
})
.option("private-key", {
description: "Private key to submit with",
type: "string",
demandOption: true,
}).argv;
main().catch((err) => {
console.log("Error thrown by script:");
console.log(err);
});


async function main(): Promise<void> {
const electrs = new DefaultElectrsClient("testnet");
// args["parachain-endpoint"]

const initHeight = args["init-height"];
const privateKey = args["private-key"];
if ((initHeight % 2016) != 0) {
throw new Error("Invalid genesis height: must be multiple of 2016");
}

const genesis = await electrs.getBlockHeaderAt(initHeight);

const beforeRetarget = await electrs.getBlockHeaderAt(initHeight + 2015);
const afterRetarget = await electrs.getBlockHeaderAt(initHeight + 2016);

console.log(`Genesis: ${genesis}`);
console.log(`beforeRetarget: ${beforeRetarget}`);
console.log(`afterRetarget: ${afterRetarget}`);


exec(`GENESIS_HEIGHT=${initHeight} GENESIS_HEADER=${genesis} RETARGET_HEADERS=${beforeRetarget}${afterRetarget} PRIVATE_KEY=${privateKey} forge script script/Bridge.s.sol:BridgeScript --rpc-url 'https://l2-fluffy-bob-7mjgi9pmtg.t.conduit.xyz' --chain 901 --verify --verifier blockscout --verifier-url 'https://explorerl2-fluffy-bob-7mjgi9pmtg.t.conduit.xyz/api?' --broadcast`,
(err: any, stdout: any, stderr: any) => {
if (err) {
throw new Error(`Failed to run command: ${err}`);
}

// the *entire* stdout and stderr (buffered)
console.log(`stdout: ${stdout}`);
});
}
8 changes: 8 additions & 0 deletions sdk/src/electrs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ export class DefaultElectrsClient implements ElectrsClient {
return this.getText(`${this.basePath}/block/${hash}/header`);
}

/**
* @ignore
*/
async getBlockHeaderAt(height: number): Promise<string> {
const blockHash = await this.getBlockHash(height);
return await this.getBlockHeader(blockHash);
}

/**
* @ignore
*/
Expand Down
6 changes: 6 additions & 0 deletions src/bridge/BitcoinTx.sol
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@ library BitcoinTx {
bytes memory bitcoinHeaders
) internal view {
IRelay relay = self.relay;

// for testing
if (!relay.difficultyCheckEnabled()) {
return;
}

uint256 currentEpochDifficulty = relay.getCurrentEpochDifficulty();
uint256 previousEpochDifficulty = relay.getPrevEpochDifficulty();

Expand Down
3 changes: 3 additions & 0 deletions src/bridge/IRelay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ interface IRelay {

/// @notice Returns the difficulty of the previous epoch.
function getPrevEpochDifficulty() external view returns (uint256);

/// @notice Returns true iff difficulty check should be performed.
function difficultyCheckEnabled() external view returns (bool);
}
28 changes: 28 additions & 0 deletions src/relay/DummyRelay.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-3.0-only

// Forked from https://github.com/keep-network/tbtc-v2

pragma solidity 0.8.17;

import "@openzeppelin/contracts/access/Ownable.sol";

import {BytesLib} from "@bob-collective/bitcoin-spv/BytesLib.sol";
import {BTCUtils} from "@bob-collective/bitcoin-spv/BTCUtils.sol";
import {ValidateSPV} from "@bob-collective/bitcoin-spv/ValidateSPV.sol";

import "../bridge/IRelay.sol";

/// @dev DEV-only!
contract DummyRelay is IRelay {
function getCurrentEpochDifficulty() external view returns (uint256) {
return 0;
}

function getPrevEpochDifficulty() external view returns (uint256) {
return 0;
}

function difficultyCheckEnabled() external view returns (bool) {
return false;
}
}
5 changes: 5 additions & 0 deletions src/relay/LightRelay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,11 @@ contract LightRelay is Ownable, ILightRelay {
return (currentEpochDifficulty, prevEpochDifficulty);
}

function difficultyCheckEnabled() external view returns (bool) {
return true;
}


/// @notice Get the difficulty of the specified epoch.
/// @param epochNumber The number of the epoch (the height of the first
/// block of the epoch, divided by 2016). Must fall within the relay range.
Expand Down
28 changes: 9 additions & 19 deletions src/swap/Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ contract Bridge {
address accepterAddress;
BitcoinAddress bitcoinAddress;
}

struct BitcoinAddress {
uint256 bitcoinAddress; // todo: use the right type
}

struct TransactionProof {
// todo: fields here
uint256 dummy;
Expand All @@ -43,11 +45,7 @@ contract Bridge {
}

/// request zBTC to be redeemed for given amount of BTC.
function requestSwap(
uint256 amountZbtc,
uint256 amountBtc,
BitcoinAddress calldata bitcoinAddress
) public {
function requestSwap(uint256 amountZbtc, uint256 amountBtc, BitcoinAddress calldata bitcoinAddress) public {
// lock Zbtc by transfering it to the contract address
wrapped.sudoTransferFrom(msg.sender, address(this), amountZbtc);
require(amountZbtc != 0);
Expand Down Expand Up @@ -79,14 +77,11 @@ contract Bridge {
require(order.requesterAddress == msg.sender);
// ensure the request was not accepted yet
require(order.accepterAddress == address(0));

delete orders[id];
}

function executeSwap(
uint256 id,
TransactionProof calldata transactionProof
) public {
function executeSwap(uint256 id, TransactionProof calldata transactionProof) public {
// todo: check proof

// move the zbtc thta was locked to whoever accepted the order
Expand All @@ -96,10 +91,7 @@ contract Bridge {
require(!order.open);
require(order.amountZbtc != 0);

wrapped.transfer(
order.accepterAddress,
order.amountZbtc
);
wrapped.transfer(order.accepterAddress, order.amountZbtc);

// clean up storage
delete orders[id];
Expand All @@ -119,18 +111,16 @@ contract Bridge {
uint256 totalZbtc = wrapped.totalSupply();
uint256 requiredCol = btcToCol(totalZbtc * collateralThreshold);
uint256 colFree = totalCollateral - requiredCol;
uint256 withdrawal = colFree < suppliedCollateral[msg.sender]
? colFree
: suppliedCollateral[msg.sender];
uint256 withdrawal = colFree < suppliedCollateral[msg.sender] ? colFree : suppliedCollateral[msg.sender];
suppliedCollateral[msg.sender] -= withdrawal;
totalCollateral -= withdrawal;
}

function colToBtc(uint256 collateral) internal pure returns (uint) {
function colToBtc(uint256 collateral) internal pure returns (uint256) {
return collateral / 5; // todo
}

function btcToCol(uint256 collateral) internal pure returns (uint) {
function btcToCol(uint256 collateral) internal pure returns (uint256) {
return collateral * 5; // todo
}
}
16 changes: 14 additions & 2 deletions src/swap/Btc_Marketplace.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {BitcoinTx} from "../bridge/BitcoinTx.sol";
// import {LightRelay} from "../relay/LightRelay.sol";
import {IRelay} from "../bridge/IRelay.sol";
import {BridgeState} from "../bridge/BridgeState.sol";

using SafeERC20 for IERC20;

contract BtcMarketPlace {
using BitcoinTx for BridgeState.Storage;

mapping(uint256 => BtcBuyOrder) public btcBuyOrders;
mapping(uint256 => AcceptedBtcBuyOrder) public acceptedBtcBuyOrders;
mapping(uint256 => BtcSellOrder) public btcSellOrders;
Expand All @@ -15,6 +20,13 @@ contract BtcMarketPlace {
uint256 nextOrderId;
uint256 public constant REQUEST_EXPIRATION_SECONDS = 6 hours;

BridgeState.Storage internal relay;

constructor(IRelay _relay) {
relay.relay = _relay;
relay.txProofDifficultyFactor = 1; // will make this an arg later on
}

// todo: should we merge buy&sell structs? They're structurally identical except for the
// bitcoinaddress location.

Expand Down Expand Up @@ -172,7 +184,7 @@ contract BtcMarketPlace {

require(accept.requester == msg.sender);

// todo: check proof
relay.validateProof(transaction, proof);

IERC20(accept.ercToken).safeTransfer(
accept.requester,
Expand Down Expand Up @@ -293,7 +305,7 @@ contract BtcMarketPlace {

require(accept.accepter == msg.sender);

// todo: check proof
relay.validateProof(transaction, proof);

IERC20(accept.ercToken).safeTransfer(accept.accepter, accept.ercAmount);

Expand Down
Loading

0 comments on commit 9b62efe

Please sign in to comment.