Skip to content

Commit

Permalink
feat(jstzd): insert jstz bootstrap contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
huancheng-trili committed Nov 29, 2024
1 parent 4a237bb commit 0857f6f
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 11 deletions.
166 changes: 165 additions & 1 deletion crates/jstzd/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#![allow(dead_code)]
use std::path::{Path, PathBuf};

use crate::task::jstzd::JstzdConfig;
use crate::{EXCHANGER_ADDRESS, JSTZ_NATIVE_BRIDGE_ADDRESS};
use anyhow::{Context, Result};
use octez::r#async::protocol::{BootstrapContract, ProtocolParameter};
use octez::{
r#async::{
baker::{BakerBinaryPath, OctezBakerConfig, OctezBakerConfigBuilder},
Expand All @@ -15,6 +19,10 @@ use tokio::io::AsyncReadExt;

const ACTIVATOR_PUBLIC_KEY: &str =
"edpkuSLWfVU1Vq7Jg9FucPyKmma6otcMHac9zG4oU1KMHSTBpJuGQ2";
pub const BOOTSTRAP_CONTRACT_NAMES: [(&str, &str); 2] = [
("exchanger", EXCHANGER_ADDRESS),
("jstz_native_bridge", JSTZ_NATIVE_BRIDGE_ADDRESS),
];

#[derive(Deserialize, Default)]
struct Config {
Expand Down Expand Up @@ -58,7 +66,7 @@ async fn build_config(
&octez_client_config,
)?;

let protocol_params = config.protocol.build()?;
let protocol_params = build_protocol_params(config.protocol).await?;
let server_port = config.server_port.unwrap_or(unused_port());
Ok((
server_port,
Expand Down Expand Up @@ -105,6 +113,51 @@ fn populate_baker_config(
config_builder.build()
}

async fn read_bootstrap_contracts() -> anyhow::Result<Vec<BootstrapContract>> {
let mut contracts = vec![];
for (contract_name, hash) in BOOTSTRAP_CONTRACT_NAMES {
let script = read_json_file(
Path::new(std::env!("CARGO_MANIFEST_DIR"))
.join(format!("resources/bootstrap_contract/{contract_name}.json")),
)
.await
.context(format!(
"error loading bootstrap contract '{contract_name}'"
))?;
contracts.push(BootstrapContract::new(script, 1_000_000, Some(hash)).unwrap());
}
Ok(contracts)
}

async fn read_json_file(path: PathBuf) -> anyhow::Result<serde_json::Value> {
let mut buf = String::new();
tokio::fs::File::open(&path)
.await?
.read_to_string(&mut buf)
.await?;
Ok(serde_json::from_str(&buf)?)
}

async fn build_protocol_params(
mut builder: ProtocolParameterBuilder,
) -> anyhow::Result<ProtocolParameter> {
// User contracts whose addresses collide with those reserved for jstz contracts
// will overwrite jstz contracts. This aligns with the current implementation
// where bootstrap contracts in the base parameter file take precedence, even
// if it means that jstz won't launch in such cases.
let mut contracts = builder
.bootstrap_contracts()
.iter()
.map(|v| (*v).to_owned())
.collect::<Vec<BootstrapContract>>();
for contract in read_bootstrap_contracts().await? {
contracts.push(contract);
}

// TODO: insert jstz rollup
builder.set_bootstrap_contracts(contracts).build()
}

#[cfg(test)]
mod tests {
use std::{io::Read, io::Write, path::PathBuf, str::FromStr};
Expand All @@ -123,9 +176,26 @@ mod tests {
},
};
use tempfile::{tempdir, NamedTempFile};
use tezos_crypto_rs::hash::ContractKt1Hash;

use super::Config;

async fn read_bootstrap_contracts_from_param_file(
path: PathBuf,
) -> Vec<BootstrapContract> {
let params_json = super::read_json_file(path).await.unwrap();
params_json
.as_object()
.unwrap()
.get("bootstrap_contracts")
.unwrap()
.as_array()
.unwrap()
.iter()
.map(|v| serde_json::from_value::<BootstrapContract>(v.to_owned()).unwrap())
.collect::<Vec<BootstrapContract>>()
}

#[tokio::test]
async fn parse_config() {
let mut tmp_file = NamedTempFile::new().unwrap();
Expand Down Expand Up @@ -332,6 +402,7 @@ mod tests {
}))
.unwrap();
tmp_file.write_all(content.as_bytes()).unwrap();

let (_, config) =
super::build_config(&Some(tmp_file.path().to_str().unwrap().to_owned()))
.await
Expand All @@ -340,6 +411,16 @@ mod tests {
config.octez_client_config().octez_node_endpoint(),
&Endpoint::localhost(9999)
);

let contracts = read_bootstrap_contracts_from_param_file(
config
.protocol_params()
.parameter_file()
.path()
.to_path_buf(),
)
.await;
assert_eq!(contracts.len(), 2);
}

#[tokio::test]
Expand Down Expand Up @@ -391,4 +472,87 @@ mod tests {
&Endpoint::localhost(8888)
);
}

#[tokio::test]
async fn read_bootstrap_contracts() {
let mut contracts = super::read_bootstrap_contracts()
.await
.unwrap()
.iter()
.map(|v| v.hash().to_owned())
.collect::<Vec<Option<ContractKt1Hash>>>();
contracts.sort();
assert_eq!(
contracts,
vec![
Some(
ContractKt1Hash::from_base58_check(super::EXCHANGER_ADDRESS).unwrap()
),
Some(
ContractKt1Hash::from_base58_check(super::JSTZ_NATIVE_BRIDGE_ADDRESS)
.unwrap()
)
]
)
}

#[tokio::test]
async fn build_protocol_params() {
let mut builder = ProtocolParameterBuilder::new();
builder.set_bootstrap_accounts([BootstrapAccount::new(
super::ACTIVATOR_PUBLIC_KEY,
40_000_000_000,
)
.unwrap()]);
let params = super::build_protocol_params(builder).await.unwrap();
let mut addresses = read_bootstrap_contracts_from_param_file(
params.parameter_file().path().to_path_buf(),
)
.await
.iter()
.map(|v| v.hash().as_ref().unwrap().clone().to_string())
.collect::<Vec<String>>();
addresses.sort();
assert_eq!(
addresses,
[super::EXCHANGER_ADDRESS, super::JSTZ_NATIVE_BRIDGE_ADDRESS]
);
}

#[tokio::test]
async fn build_protocol_params_contract_collision() {
let dummy_contract = BootstrapContract::new(
serde_json::json!("test-contract"),
1,
Some(super::EXCHANGER_ADDRESS),
)
.unwrap();
let mut builder = ProtocolParameterBuilder::new();
builder
.set_bootstrap_accounts([BootstrapAccount::new(
super::ACTIVATOR_PUBLIC_KEY,
40_000_000_000,
)
.unwrap()])
.set_bootstrap_contracts([dummy_contract.clone()]);
let params = super::build_protocol_params(builder).await.unwrap();
let mut contracts = read_bootstrap_contracts_from_param_file(
params.parameter_file().path().to_path_buf(),
)
.await;
assert_eq!(contracts.len(), 2);

contracts.sort_by_key(|v| v.hash().as_ref().unwrap().to_string());
let addresses = contracts
.iter()
.map(|v| v.hash().to_owned().unwrap().to_string())
.collect::<Vec<String>>();
assert_eq!(
addresses,
[super::EXCHANGER_ADDRESS, super::JSTZ_NATIVE_BRIDGE_ADDRESS]
);
// the first contract should be overwritten by the dummy contract
let exchanger_contract = contracts.first().unwrap();
assert_eq!(exchanger_contract, &dummy_contract);
}
}
2 changes: 1 addition & 1 deletion crates/jstzd/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod config;
pub mod docker;
pub mod task;

pub use config::BOOTSTRAP_CONTRACT_NAMES;
pub const EXCHANGER_ADDRESS: &str = "KT1F3MuqvT9Yz57TgCS3EkDcKNZe9HpiavUJ";
pub const JSTZ_ROLLUP_ADDRESS: &str = "sr1PuFMgaRUN12rKQ3J2ae5psNtwCxPNmGNK";
pub const JSTZ_NATIVE_BRIDGE_ADDRESS: &str = "KT1GFiPkkTjd14oHe6MrBPiRh5djzRkVWcni";
Expand Down
13 changes: 4 additions & 9 deletions crates/jstzd/tests/jstzd_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::path::PathBuf;

use jstzd::task::jstzd::{JstzdConfig, JstzdServer};
use jstzd::task::utils::retry;
use jstzd::{EXCHANGER_ADDRESS, JSTZ_NATIVE_BRIDGE_ADDRESS};
use jstzd::BOOTSTRAP_CONTRACT_NAMES;
use octez::r#async::baker::{BakerBinaryPath, OctezBakerConfigBuilder};
use octez::r#async::client::{OctezClient, OctezClientConfigBuilder};
use octez::r#async::endpoint::Endpoint;
Expand All @@ -14,11 +14,6 @@ use octez::r#async::protocol::{
use octez::unused_port;

const CONTRACT_INIT_BALANCE: f64 = 1.0;
const CONTRACT_NAMES: [(&str, &str); 2] = [
("exchanger", EXCHANGER_ADDRESS),
("jstz_native_bridge", JSTZ_NATIVE_BRIDGE_ADDRESS),
];

#[tokio::test(flavor = "multi_thread")]
async fn jstzd_test() {
let rpc_endpoint = Endpoint::localhost(unused_port());
Expand Down Expand Up @@ -214,7 +209,7 @@ async fn fetch_config_test(jstzd_config: JstzdConfig, jstzd_port: u16) {

async fn read_bootstrap_contracts() -> Vec<BootstrapContract> {
let mut contracts = vec![];
for (contract_name, hash) in CONTRACT_NAMES {
for (contract_name, hash) in BOOTSTRAP_CONTRACT_NAMES {
let script = utils::read_json_file(
PathBuf::from(std::env!("CARGO_MANIFEST_DIR"))
.join(format!("resources/bootstrap_contract/{contract_name}.json")),
Expand All @@ -233,10 +228,10 @@ async fn read_bootstrap_contracts() -> Vec<BootstrapContract> {
}

async fn check_bootstrap_contracts(octez_client: &OctezClient) {
for (contract_name, hash) in CONTRACT_NAMES {
for (contract_name, hash) in BOOTSTRAP_CONTRACT_NAMES {
assert_eq!(
octez_client
.get_balance(EXCHANGER_ADDRESS)
.get_balance(hash)
.await
.unwrap_or_else(|_| panic!(
"should be able to find contract '{contract_name}' at '{hash}'"
Expand Down
4 changes: 4 additions & 0 deletions crates/octez/src/async/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ impl BootstrapContract {
},
})
}

pub fn hash(&self) -> &Option<ContractKt1Hash> {
&self.hash
}
}

#[derive(Default, Debug, PartialEq)]
Expand Down
7 changes: 7 additions & 0 deletions crates/octez/src/async/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,13 @@ impl ProtocolParameterBuilder {
self
}

pub fn bootstrap_contracts(&self) -> Vec<&BootstrapContract> {
self.bootstrap_contracts
.contracts()
.iter()
.collect::<Vec<&BootstrapContract>>()
}

pub fn set_bootstrap_smart_rollups(
&mut self,
rollups: impl IntoIterator<Item = BootstrapSmartRollup>,
Expand Down

0 comments on commit 0857f6f

Please sign in to comment.