Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jstzd): insert jstz bootstrap contracts #684

Merged
merged 1 commit into from
Nov 29, 2024
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
172 changes: 167 additions & 5 deletions 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 All @@ -39,9 +47,7 @@ async fn parse_config(path: &str) -> Result<Config> {
Ok(serde_json::from_str::<Config>(&s)?)
}

async fn build_config(
config_path: &Option<String>,
) -> anyhow::Result<(u16, JstzdConfig)> {
async fn build_config(config_path: &Option<String>) -> Result<(u16, JstzdConfig)> {
let mut config = match config_path {
Some(p) => parse_config(p).await?,
None => default_config(),
Expand All @@ -58,7 +64,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 @@ -89,7 +95,7 @@ fn populate_baker_config(
mut config_builder: OctezBakerConfigBuilder,
octez_node_config: &OctezNodeConfig,
octez_client_config: &OctezClientConfig,
) -> anyhow::Result<OctezBakerConfig> {
) -> Result<OctezBakerConfig> {
if config_builder.binary_path().is_none() {
config_builder =
config_builder.set_binary_path(BakerBinaryPath::Env(Protocol::Alpha));
Expand All @@ -105,6 +111,51 @@ fn populate_baker_config(
config_builder.build()
}

async fn read_bootstrap_contracts() -> 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) -> 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,
) -> 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 +174,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 +400,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 +409,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 +470,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