Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ use bitcoin::hashes::Hash;
use bitcoin::{Address as BtcAddress, TxMerkleNode, Txid};
use bitcoin::{Block, Network};
use esplora_client::{AsyncClient, Builder, MerkleProof, Utxo};
use store::localdb::LocalDB;
use store::{ipfs::IPFS, localdb::LocalDB};
use uuid::Uuid;

pub struct BitVM2Client {
pub local_db: LocalDB,
pub esplora: AsyncClient,
pub btc_network: Network,
pub chain_service: Chain,
pub ipfs: IPFS,
}

impl BitVM2Client {
Expand All @@ -27,6 +28,7 @@ impl BitVM2Client {
btc_network: Network,
goat_network: GoatNetwork,
goat_config: GoatInitConfig,
ipfs_endpoint: &str,
) -> Self {
let local_db = LocalDB::new(&format!("sqlite:{db_path}"), true).await;
local_db.migrate().await;
Expand All @@ -37,6 +39,7 @@ impl BitVM2Client {
.expect("Could not build esplora client"),
btc_network,
chain_service: Chain::new(get_chain_adaptor(goat_network, goat_config, None)),
ipfs: IPFS::new(ipfs_endpoint),
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod tests {
Network::Testnet,
GoatNetwork::Test,
global_init_config,
"http://localhost:5001",
)
.await;
let tx_id =
Expand Down
27 changes: 14 additions & 13 deletions crates/store/src/ipfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,36 @@ pub struct IPFS {
#[derive(Deserialize, Debug, PartialEq, Hash)]
#[serde(rename_all = "PascalCase")]
pub struct Link {
hash: String,
mod_time: String,
mode: u32,
name: String,
size: u32,
target: String,
pub hash: String,
pub mod_time: String,
pub mode: u32,
pub name: String,
pub size: u32,
pub target: String,
#[serde(rename = "Type")]
type_: u32,
pub type_: u32,
}

#[derive(Deserialize, Debug, PartialEq, Hash)]
#[serde(rename_all = "PascalCase")]
pub struct Object {
hash: String,
links: Vec<Link>,
pub hash: String,
pub links: Vec<Link>,
}

#[derive(Deserialize, Debug, PartialEq, Hash)]
#[serde(rename_all = "PascalCase")]
pub struct Objects {
objects: Vec<Object>,
pub objects: Vec<Object>,
}

/// If the name is empty, it's the directory name
#[derive(Deserialize, Debug, PartialEq, Hash)]
#[serde(rename_all = "PascalCase")]
pub struct AddedFile {
name: String,
hash: String,
size: String,
pub name: String,
pub hash: String,
pub size: String,
}

// Collects all files and returns relative + absolute paths
Expand Down Expand Up @@ -154,6 +154,7 @@ pub mod tests {
}

// it works, but skip for avoiding creating too much garbage
// use std::io::Write;
// let base_dir = tempfile::tempdir().unwrap();
// vec!["1.txt", "2.txt"].iter().for_each(|name| {
// let mut file = std::fs::File::create(
Expand Down
2 changes: 1 addition & 1 deletion crates/store/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mod ipfs;
pub mod ipfs;
pub mod localdb;
mod schema;

Expand Down
1 change: 1 addition & 0 deletions node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,4 @@ ark-serialize = { workspace = true }

[dev-dependencies]
tempfile = "3.19.1"
serial_test = "3.2.0"
34 changes: 26 additions & 8 deletions node/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use bitvm2_lib::keys::*;
use bitvm2_lib::types::{Bitvm2Graph, Bitvm2Parameters, CustomInputs};
use bitvm2_lib::verifier::export_challenge_tx;
use bitvm2_lib::{committee::*, operator::*, verifier::*};
use client::client::BitVM2Client;
use goat::transactions::{assert::utils::COMMIT_TX_NUM, pre_signed::PreSignedTransaction};
use libp2p::gossipsub::MessageId;
use libp2p::{PeerId, Swarm, gossipsub};
Expand Down Expand Up @@ -95,6 +96,7 @@ pub struct GraphFinalize {
pub instance_id: Uuid,
pub graph_id: Uuid,
pub graph: Bitvm2Graph,
pub graph_ipfs_cid: String,
}

#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -179,6 +181,7 @@ impl GOATMessage {
/// * peers: send
pub async fn recv_and_dispatch(
swarm: &mut Swarm<AllBehaviours>,
client: &BitVM2Client,
actor: Actor,
peer_id: PeerId,
id: MessageId,
Expand All @@ -204,7 +207,6 @@ pub async fn recv_and_dispatch(
println!("Handle message: {:?}", message);
let content: GOATMessageContent = message.to_typed()?;
// TODO: validate message
let client = client()?;
match (content, actor) {
// pegin
// CreateInstance sent by bootnode
Expand Down Expand Up @@ -378,6 +380,12 @@ pub async fn recv_and_dispatch(
&receive_data.agg_nonces,
&mut graph,
)?;
let prekickoff_tx = graph.pre_kickoff.tx().clone();
let node_keypair =
OperatorMasterKey::new(env::get_bitvm_key()?).master_keypair();
sign_and_broadcast_prekickoff_tx(&client, node_keypair, prekickoff_tx).await?;
let graph_ipfs_cid =
publish_graph_to_ipfs(client, receive_data.graph_id, &graph).await?;
store_graph(
&client,
receive_data.instance_id,
Expand All @@ -386,23 +394,26 @@ pub async fn recv_and_dispatch(
Some(GraphStatus::CommitteePresigned.to_string()),
)
.await?;
let prekickoff_tx = graph.pre_kickoff.tx().clone();
let node_keypair =
OperatorMasterKey::new(env::get_bitvm_key()?).master_keypair();
sign_and_broadcast_prekickoff_tx(&client, node_keypair, prekickoff_tx).await?;
update_graph_status_or_ipfs_base(
client,
receive_data.graph_id,
None,
Some(graph_ipfs_cid.clone()),
)
.await?;
let message_content = GOATMessageContent::GraphFinalize(GraphFinalize {
instance_id: receive_data.instance_id,
graph_id: receive_data.graph_id,
graph,
graph_ipfs_cid,
});
// TODO: ipfs
send_to_peer(swarm, GOATMessage::from_typed(Actor::All, &message_content)?)?;
force_stop_current_graph();
}
};
}
(GOATMessageContent::GraphFinalize(receive_data), _) => {
// TODO: validate graph
// TODO: validate graph & ipfs
store_graph(
&client,
receive_data.instance_id,
Expand All @@ -411,6 +422,13 @@ pub async fn recv_and_dispatch(
Some(GraphStatus::CommitteePresigned.to_string()),
)
.await?;
update_graph_status_or_ipfs_base(
client,
receive_data.graph_id,
None,
Some(receive_data.graph_ipfs_cid.clone()),
)
.await?;
}

// peg-out
Expand Down Expand Up @@ -454,7 +472,7 @@ pub async fn recv_and_dispatch(
Amount::from_sat(graph.challenge.min_crowdfunding_amount()),
receive_data.instance_id,
receive_data.graph_id,
&graph,
&graph.kickoff.tx().compute_txid(),
)
.await?
{
Expand Down
53 changes: 49 additions & 4 deletions node/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use alloy::eips::BlockNumberOrTag;
use alloy::primitives::Address as EvmAddress;
use bitcoin::{Network, PublicKey, key::Keypair};
use bitvm2_lib::keys::NodeMasterKey;
use client::chain::goat_adaptor::GoatInitConfig;
use client::chain::{chain_adaptor::GoatNetwork, goat_adaptor::GoatInitConfig};
use reqwest::Url;
use std::str::FromStr;

Expand All @@ -13,8 +13,14 @@ pub const ENV_GOAT_GATEWAY_CONTRACT_CREATION: &str = "GOAT_GATEWAY_CONTRACT_CREA
pub const ENV_GOAT_GATEWAY_CONTRACT_TO_BLOCK: &str = "GOAT_GATEWAY_CONTRACT_TO_BLOCK";
pub const ENV_GOAT_PRIVATE_KEY: &str = "GOAT_PRIVATE_KEY";
pub const ENV_GOAT_CHAIN_ID: &str = "GOAT_CHAIN_ID";
pub const ENV_BITVM_SECRET: &str = "BITVM_SECRET";
pub const ENV_PEER_KEY: &str = "KEY";
pub const ENV_PERR_ID: &str = "PEER_ID";
pub const ENV_ACTOR: &str = "ACTOR";
pub const ENV_IPFS_ENDPOINT: &str = "IPFS_ENDPOINT";

pub const SCRIPT_CACHE_FILE_NAME: &str = "cache/partial_script.bin";
pub const IPFS_GRAPH_CACHE_DIR: &str = "cache/graph_cache/";
pub const DUST_AMOUNT: u64 = goat::transactions::base::DUST_AMOUNT;
pub const MAX_CUSTOM_INPUTS: usize = 100;

Expand All @@ -36,15 +42,20 @@ pub const CHALLENGE_RATE: u64 = 0; // 0%
pub const RATE_MULTIPLIER: u64 = 10000;

const COMMITTEE_MEMBER_NUMBER: usize = 3;
const NETWORK: Network = Network::Testnet;
const BTC_NETWORK: Network = Network::Testnet;
const GOAT_NETWORK: GoatNetwork = GoatNetwork::Test;

pub fn get_network() -> Network {
NETWORK
BTC_NETWORK
}

pub fn get_goat_network() -> GoatNetwork {
GOAT_NETWORK
}

pub fn get_bitvm_key() -> Result<Keypair, Box<dyn std::error::Error>> {
// TODO: what if node restart with different BITVM_SECRET ?
let bitvm_secret = std::env::var("BITVM_SECRET").expect("BITVM_SECRET is missing");
let bitvm_secret = std::env::var(ENV_BITVM_SECRET).expect("{ENV_BITVM_SECRET} is missing");
Ok(Keypair::from_seckey_str_global(&bitvm_secret)?)
}

Expand Down Expand Up @@ -85,3 +96,37 @@ pub fn get_bitvm2_client_config() -> GoatInitConfig {
chain_id: chain_id.parse().expect("fail to parse int"),
}
}

pub enum IpfsTxName {
AssertCommit0,
AssertCommit1,
AssertCommit2,
AssertCommit3,
AssertFinal,
AssertInit,
Challenge,
Disprove,
Kickoff,
Pegin,
Take1,
Take2,
}

impl IpfsTxName {
pub fn as_str(&self) -> &'static str {
match self {
IpfsTxName::AssertCommit0 => "assert-commit0.hex",
IpfsTxName::AssertCommit1 => "assert-commit1.hex",
IpfsTxName::AssertCommit2 => "assert-commit2.hex",
IpfsTxName::AssertCommit3 => "assert-commit3.hex",
IpfsTxName::AssertFinal => "assert-final.hex",
IpfsTxName::AssertInit => "assert-init.hex",
IpfsTxName::Challenge => "challenge.hex",
IpfsTxName::Disprove => "disprove.hex",
IpfsTxName::Kickoff => "kickoff.hex",
IpfsTxName::Pegin => "pegin.hex",
IpfsTxName::Take1 => "take1.hex",
IpfsTxName::Take2 => "take2.hex",
}
}
}
37 changes: 28 additions & 9 deletions node/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![feature(trivial_bounds)]
use base64::Engine;
use clap::{Parser, Subcommand, command};
use client::client::BitVM2Client;
use env::ENV_IPFS_ENDPOINT;
use libp2p::PeerId;
use libp2p::futures::StreamExt;
use libp2p::{gossipsub, kad, mdns, multiaddr::Protocol, noise, swarm::SwarmEvent, tcp, yamux};
Expand All @@ -25,6 +27,7 @@ mod rpc_service;
mod utils;

use crate::action::GOATMessage;
use crate::env::{ENV_ACTOR, ENV_PEER_KEY, ENV_PERR_ID};
use crate::middleware::behaviour::AllBehavioursEvent;
use anyhow::Result;
use middleware::AllBehaviours;
Expand Down Expand Up @@ -107,19 +110,19 @@ async fn main() -> Result<(), Box<dyn Error>> {
let local_key = identity::generate_local_key();
let base64_key = base64::engine::general_purpose::STANDARD
.encode(&local_key.to_protobuf_encoding()?);
tracing::info!("export KEY={}", base64_key);
tracing::info!("export PEER_ID={}", local_key.public().to_peer_id());
tracing::info!("export {}={}", ENV_PEER_KEY, base64_key);
tracing::info!("export {}={}", ENV_PERR_ID, local_key.public().to_peer_id());
}
}
return Ok(());
}
// load role
let actor =
Actor::from_str(std::env::var("ACTOR").unwrap_or("Challenger".to_string()).as_str())
Actor::from_str(std::env::var(ENV_ACTOR).unwrap_or("Challenger".to_string()).as_str())
.unwrap();

let local_key = std::env::var("KEY").expect("KEY is missing");
let arg_peer_id = std::env::var("PEER_ID").expect("Peer ID is missing");
let local_key = std::env::var(ENV_PEER_KEY).expect("KEY is missing");
let arg_peer_id = std::env::var(ENV_PERR_ID).expect("Peer ID is missing");

let _ = tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).try_init();
let mut metric_registry = Registry::default();
Expand Down Expand Up @@ -207,8 +210,24 @@ async fn main() -> Result<(), Box<dyn Error>> {
tracing::debug!("RPC service listening on {}", &opt.rpc_addr);
let rpc_addr = opt.rpc_addr.clone();
let db_path = opt.db_path.clone();

tokio::spawn(rpc_service::serve(rpc_addr, db_path, Arc::new(Mutex::new(metric_registry))));
let ipfs_url = std::env::var(ENV_IPFS_ENDPOINT).expect("IPFS_ENDPOINT is missing");

let client = BitVM2Client::new(
&db_path,
None,
env::get_network(),
env::get_goat_network(),
env::get_bitvm2_client_config(),
&ipfs_url,
)
.await;

tokio::spawn(rpc_service::serve(
rpc_addr,
db_path.clone(),
ipfs_url.clone(),
Arc::new(Mutex::new(metric_registry)),
));
// Read full lines from stdin
let mut interval = interval(Duration::from_secs(20));
let mut stdin = io::BufReader::new(io::stdin()).lines();
Expand Down Expand Up @@ -243,7 +262,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
actor: actor.clone(),
content: "tick".as_bytes().to_vec(),
})?;
match action::recv_and_dispatch(&mut swarm, actor.clone(), peer_id, GOATMessage::default_message_id(), &tick_data).await{
match action::recv_and_dispatch(&mut swarm, &client, actor.clone(), peer_id, GOATMessage::default_message_id(), &tick_data).await{
Ok(_) => {}
Err(e) => { tracing::error!(e) }
}
Expand All @@ -256,7 +275,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
message_id: id,
message,
})) => {
match action::recv_and_dispatch(&mut swarm, actor.clone(), peer_id, id, &message.data).await {
match action::recv_and_dispatch(&mut swarm, &client, actor.clone(), peer_id, id, &message.data).await {
Ok(_) => {},
Err(e) => { tracing::error!(e) }
}
Expand Down
Loading
Loading