Skip to content

Commit

Permalink
Merge pull request #29 from lana-shanghai/li/feat/submit_inclusion_proof
Browse files Browse the repository at this point in the history
test proof of inclusion of a Bitcoin transaction via utu relay
  • Loading branch information
lana-shanghai authored Dec 20, 2024
2 parents 0411b5c + 64e62e2 commit 1579b4a
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 41 deletions.
29 changes: 27 additions & 2 deletions packages/onchain/src/orderbook/interface.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use core::starknet::contract_address::ContractAddress;
use consensus::{types::transaction::{Transaction}};
use utils::hash::Digest;
use utu_relay::bitcoin::block::BlockHeader;

#[derive(Default, Drop, PartialEq, starknet::Store)]
pub enum Status {
Expand All @@ -21,10 +24,21 @@ pub trait IOrderbook<TContractState> {
) -> u32;
fn cancel_inscription(ref self: TContractState, inscription_id: u32, currency_fee: felt252);
fn lock_inscription(ref self: TContractState, inscription_id: u32, tx_hash: ByteArray);
fn submit_inscription(ref self: TContractState, inscription_id: u32, tx_hash: ByteArray);
fn submit_inscription(
ref self: TContractState,
inscription_id: u32,
tx_hash: ByteArray,
tx: Transaction,
block_height: u64,
block_header: BlockHeader,
inclusion_proof: Array<(Digest, bool)>,
);
fn query_inscription(
self: @TContractState, inscription_id: u32,
) -> (ContractAddress, ByteArray, u256);
fn query_inscription_lock(
self: @TContractState, inscription_id: u32,
) -> (ContractAddress, ByteArray, u64);
}

#[starknet::interface]
Expand All @@ -38,10 +52,21 @@ pub trait OrderbookABI<TContractState> {
) -> u32;
fn cancel_inscription(ref self: TContractState, inscription_id: u32, currency_fee: felt252);
fn lock_inscription(ref self: TContractState, inscription_id: u32, tx_hash: ByteArray);
fn submit_inscription(ref self: TContractState, inscription_id: u32, tx_hash: ByteArray);
fn submit_inscription(
ref self: TContractState,
inscription_id: u32,
tx_hash: ByteArray,
tx: Transaction,
block_height: u64,
block_header: BlockHeader,
inclusion_proof: Array<(Digest, bool)>,
);
fn query_inscription(
self: @TContractState, inscription_id: u32,
) -> (ContractAddress, ByteArray, u256);
fn query_inscription_lock(
self: @TContractState, inscription_id: u32,
) -> (ContractAddress, ByteArray, u64);

// ERC20 functions
fn balance_of(self: @TContractState, account: ContractAddress) -> felt252;
Expand Down
19 changes: 18 additions & 1 deletion packages/onchain/src/orderbook/mock.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ use onchain::orderbook::interface::IOrderbook;
#[starknet::contract]
mod OrderbookMock {
use core::byte_array::ByteArray;
use consensus::{types::transaction::{Transaction}};
use onchain::orderbook::interface::Status;
use openzeppelin_token::erc20::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait};
use starknet::storage::{
Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePathEntry,
StoragePointerReadAccess, StoragePointerWriteAccess,
};
use starknet::{ContractAddress, get_caller_address, get_contract_address, get_block_number};
use utils::hash::Digest;
use utu_relay::bitcoin::block::BlockHeader;

#[storage]
struct Storage {
Expand Down Expand Up @@ -172,7 +175,15 @@ mod OrderbookMock {
/// Inputs:
/// - `inscription_id: felt252`, the ID of the inscription being locked.
/// - `tx_hash: ByteArray`, the hash of the transaction submitted to Bitcoin.
fn submit_inscription(ref self: ContractState, inscription_id: u32, tx_hash: ByteArray) {
fn submit_inscription(
ref self: ContractState,
inscription_id: u32,
tx_hash: ByteArray,
tx: Transaction,
block_height: u64,
block_header: BlockHeader,
inclusion_proof: Array<(Digest, bool)>,
) {
// TODO: process the submitted transaction hash, verify that it is on Bitcoin

self.inscription_statuses.write(inscription_id, Status::Closed);
Expand All @@ -184,6 +195,12 @@ mod OrderbookMock {
) -> (ContractAddress, ByteArray, u256) {
return self.inscriptions.read(inscription_id);
}

fn query_inscription_lock(
self: @ContractState, inscription_id: u32,
) -> (ContractAddress, ByteArray, u64) {
self.inscription_locks.read(inscription_id)
}
}

#[generate_trait]
Expand Down
44 changes: 37 additions & 7 deletions packages/onchain/src/orderbook/orderbook.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ use onchain::orderbook::interface::IOrderbook;
#[starknet::contract]
mod Orderbook {
use core::byte_array::ByteArray;
use consensus::{types::transaction::{Transaction}};
use onchain::orderbook::interface::Status;
use openzeppelin::utils::serde::SerializedAppend;
use openzeppelin_token::erc20::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait};
use starknet::storage::{
Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePathEntry,
StoragePointerReadAccess, StoragePointerWriteAccess,
};
use starknet::{ContractAddress, get_caller_address, get_contract_address, get_block_number};
use starknet::{SyscallResultTrait, syscalls::call_contract_syscall};
use utils::hash::Digest;
use utu_relay::bitcoin::block::BlockHeader;

#[storage]
struct Storage {
Expand All @@ -20,7 +24,7 @@ mod Orderbook {
// inscribed data, and submitter fee.
inscriptions: Map<u32, (ContractAddress, ByteArray, u256)>,
// A map from the inscription ID to status. Possible values:
// 'Open', 'Locked', 'Canceled', 'Closed'.
// 'Open', 'Locked', 'Canceled', 'Closed', 'Undefined'.
inscription_statuses: Map<u32, Status>,
// A map from the inscription ID to the potential submitters.
submitters: Map<u32, Map<ContractAddress, ContractAddress>>,
Expand Down Expand Up @@ -132,13 +136,25 @@ mod Orderbook {
/// Inputs:
/// - `inscription_id: felt252`, the ID of the inscription.
/// Returns:
/// - `(ByteArray, felt252)`, the tuple with the inscribed data and the fee.
/// - `(ContractAddress, ByteArray, felt252)`, the tuple with the requestor,
/// inscribed data and the fee.
fn query_inscription(
self: @ContractState, inscription_id: u32,
) -> (ContractAddress, ByteArray, u256) {
self.inscriptions.read(inscription_id)
}

/// Inputs:
/// - `inscription_id: felt252`, the ID of the inscription.
/// Returns:
/// - `(ContractAddress, ByteArray, felt252)`, the tuple with submitter address,
/// the precomputed tx, and the block number.
fn query_inscription_lock(
self: @ContractState, inscription_id: u32,
) -> (ContractAddress, ByteArray, u64) {
self.inscription_locks.read(inscription_id)
}

/// Called by a user.
/// Inputs:
/// - `inscription_id: felt252`, the ID of the inscription the user wants to
Expand Down Expand Up @@ -185,15 +201,18 @@ mod Orderbook {

if (status == Status::Locked) {
let (_, _, blocknumber) = self.inscription_locks.read(inscription_id);
// TODO: replace block time delta
assert(get_block_number() - blocknumber < 100, 'Prior lock has not expired');
// TODO: replace hardcoded block time delta
assert(get_block_number() - blocknumber > 100, 'Prior lock has not expired');
}

let submitter = get_caller_address();
let mut submitters = self.submitters.entry(inscription_id);
submitters.write(submitter, submitter);

self.inscription_statuses.write(inscription_id, Status::Locked);
self
.inscription_locks
.write(inscription_id, (submitter, tx_hash.clone(), get_block_number()));
self.emit(RequestLocked { id: inscription_id, submitter: submitter, tx_hash: tx_hash });
}

Expand All @@ -204,7 +223,15 @@ mod Orderbook {
/// Inputs:
/// - `inscription_id: felt252`, the ID of the inscription being locked.
/// - `tx_hash: ByteArray`, the hash of the transaction submitted to Bitcoin.
fn submit_inscription(ref self: ContractState, inscription_id: u32, tx_hash: ByteArray) {
fn submit_inscription(
ref self: ContractState,
inscription_id: u32,
tx_hash: ByteArray,
tx: Transaction,
block_height: u64,
block_header: BlockHeader,
inclusion_proof: Array<(Digest, bool)>,
) {
let caller = get_caller_address();
let submitters = self.submitters.entry(inscription_id);
let submitter = submitters.read(caller);
Expand All @@ -215,9 +242,12 @@ mod Orderbook {

const selector: felt252 = selector!("prove_inclusion");
let to = self.relay_address.read();
let calldata: Array<felt252> = array![];
let mut calldata = array![];
calldata.append_serde(tx);
calldata.append_serde(block_height);
calldata.append_serde(block_header);
calldata.append_serde(inclusion_proof);

// TODO: assert successful inclusion call
call_contract_syscall(to, selector, calldata.span()).unwrap_syscall();

// TODO: assert that the witness data contains the requested inscription
Expand Down
Loading

0 comments on commit 1579b4a

Please sign in to comment.