Skip to content

Commit

Permalink
some improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
o-tsaruk committed Jun 16, 2023
1 parent 7ec26e0 commit 5bbe85a
Show file tree
Hide file tree
Showing 15 changed files with 242 additions and 72 deletions.
34 changes: 25 additions & 9 deletions crates/builder/src/process/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,31 @@ pub struct Container {
volume: Volume,
}

/// Container environment variables.
pub struct Environment<'a, U: fmt::Display> {
/// Build session file upload token.
pub build_session_token: &'a str,

/// Rust toolchain version used to build the contract.
pub rustc_version: &'a str,

/// `cargo-contract` version used to build the contract.
pub cargo_contract_version: &'a str,

/// S3 pre-signed URL to the source code archive.
pub source_code_url: U,

/// API server URL used to upload the source code archive contents.
pub api_server_url: &'a str,
}

impl Container {
/// Spawn new Docker container with the provided configuration.
pub async fn new<U: fmt::Display>(
config: &config::Builder,
client: &Docker,
volume: Volume,
build_session_token: &str,
rust_version: &str,
cargo_contract_version: &str,
source_code_url: U,
env: Environment<'_, U>,
) -> Result<Self, Error> {
// Attempt to isolate container as much as possible.
//
Expand Down Expand Up @@ -106,17 +121,18 @@ impl Container {
let container = client
.create_container(
Some(CreateContainerOptions {
name: build_session_token,
name: env.build_session_token,
..Default::default()
}),
Config {
image: Some("ink-builder"),
// Pass information about the current build session to container
env: Some(vec![
&format!("SOURCE_CODE_URL={source_code_url}"),
&format!("CARGO_CONTRACT_VERSION={cargo_contract_version}"),
&format!("RUST_VERSION={rust_version}"),
&format!("BUILD_SESSION_TOKEN={build_session_token}"),
&format!("SOURCE_CODE_URL={}", env.source_code_url),
&format!("CARGO_CONTRACT_VERSION={}", env.cargo_contract_version),
&format!("RUST_VERSION={}", env.rustc_version),
&format!("BUILD_SESSION_TOKEN={}", env.build_session_token),
&format!("API_SERVER_URL={}", env.api_server_url),
]),
host_config: Some(host_config),
attach_stdout: Some(true),
Expand Down
13 changes: 8 additions & 5 deletions crates/builder/src/process/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
};

use super::{
container::{ContainerRemoveError, DownloadFromContainerError},
container::{ContainerRemoveError, DownloadFromContainerError, Environment},
volume::VolumeError,
};

Expand Down Expand Up @@ -134,10 +134,13 @@ pub(crate) async fn spawn(
&builder_config,
&docker,
volume,
&token,
&build_session.rustc_version,
&build_session.cargo_contract_version,
source_code_url.uri(),
Environment {
build_session_token: &token,
rustc_version: &build_session.rustc_version,
cargo_contract_version: &build_session.cargo_contract_version,
source_code_url: source_code_url.uri(),
api_server_url: &builder_config.api_server_url,
},
)
.await?;

Expand Down
3 changes: 3 additions & 0 deletions crates/common/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ pub struct Builder {
/// Path in which contract builder will store all user artifacts.
pub images_path: PathBuf,

/// URL of an API server.
pub api_server_url: String,

/// Total count of workers started for build processing.
#[serde(default = "default_worker_count")]
pub worker_count: usize,
Expand Down
2 changes: 1 addition & 1 deletion crates/migration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ sea-orm-migration = { version = "0.11.3", features = ["runtime-tokio-native-tls"
tokio = { version = "1.28.1", features = ["macros", "rt-multi-thread"] }
tracing = "0.1.37"

common = { path = "../common", features = ["logging"] }
common = { path = "../common" }
db = { path = "../db" }
4 changes: 1 addition & 3 deletions crates/migration/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::error::Error;

use clap::Parser;
use cli::Cli;
use common::{config::Config, logging};
use common::config::Config;
use migration::{cli::run_migrate, sea_orm::Database};
use tracing::info;

Expand All @@ -14,8 +14,6 @@ async fn main() -> Result<(), Box<dyn Error>> {

let config = Config::new()?;

logging::init(&config);

info!("connecting to database");
let db = Database::connect(&config.database.url).await?;
info!("database connection established");
Expand Down
6 changes: 5 additions & 1 deletion crates/patron/src/commands/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,11 @@ pub(crate) fn deploy(
return Err(DeployError::InstantiationError);
}

pg.finish_with_message("Contract uploaded.");
pg.finish_with_message(format!(
"Contract uploaded: {}/codeHash/{}",
auth_config.web_path(),
code_hash
));

Ok(())
}
5 changes: 5 additions & 0 deletions crates/patron/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ impl AuthenticationConfig {
&self.token
}

/// Get web UI path from the current configuration.
pub fn web_path(&self) -> &str {
&self.web_path
}

/// Get API server path from the current configuration.
pub fn server_path(&self) -> &str {
&self.server_path
Expand Down
1 change: 1 addition & 0 deletions crates/server/src/handlers/build_sessions/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ pub(super) async fn logs(
.select_only()
.column(build_session::Column::Id)
.filter(build_session::Column::CodeHash.eq(&val.0[..]))
.order_by_desc(build_session::Column::Id)
.into_tuple::<i64>()
.one(txn)
.await?
Expand Down
83 changes: 57 additions & 26 deletions crates/server/src/handlers/contracts/details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use axum::{
Json,
};
use axum_derive_error::ErrorResponse;
use db::{contract, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QuerySelect};
use db::{
contract, node, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QuerySelect,
TransactionErrorExt, TransactionTrait,
};
use derive_more::{Display, Error, From};
use schemars::JsonSchema;
use serde::Serialize;
Expand Down Expand Up @@ -35,6 +38,10 @@ pub(super) enum ContractDetailsError {
#[display(fmt = "incorrect address size of an owner account")]
IncorrectAddressSizeOfOwner,

/// Owner account attached to a contract is invalid.
#[display(fmt = "found a contract without related node")]
ContractWithoutRelatedNode,

/// The requested contract was not found.
#[status(StatusCode::NOT_FOUND)]
#[display(fmt = "contract not found")]
Expand All @@ -44,6 +51,10 @@ pub(super) enum ContractDetailsError {
/// Contract details response.
#[derive(Serialize, JsonSchema)]
pub struct ContractData {
/// Related node name.
#[schemars(example = "crate::schema::example_node")]
pub node: String,

/// Related code hash.
#[schemars(example = "crate::schema::example_hex_hash")]
pub code_hash: HexHash,
Expand Down Expand Up @@ -71,32 +82,51 @@ pub(super) async fn details(
Path(account): Path<WrappedAccountId32>,
State(db): State<Arc<DatabaseConnection>>,
) -> Result<Json<ContractData>, ContractDetailsError> {
let (code_hash, owner) = contract::Entity::find()
.select_only()
.columns([contract::Column::CodeHash, contract::Column::Owner])
.filter(contract::Column::Address.eq(account.0.as_slice()))
.into_tuple::<(Vec<u8>, Option<Vec<u8>>)>()
.one(&*db)
.await?
.ok_or(ContractDetailsError::ContractNotFound)?;

let owner = owner
.map(|address| {
Result::<_, ContractDetailsError>::Ok(
AccountId32::new(
address
.try_into()
.map_err(|_| ContractDetailsError::IncorrectAddressSizeOfOwner)?,
)
.to_ss58check(),
)
db.transaction(|txn| {
Box::pin(async move {
let (node_id, code_hash, owner) = contract::Entity::find()
.select_only()
.columns([
contract::Column::NodeId,
contract::Column::CodeHash,
contract::Column::Owner,
])
.filter(contract::Column::Address.eq(account.0.as_slice()))
.into_tuple::<(i64, Vec<u8>, Option<Vec<u8>>)>()
.one(txn)
.await?
.ok_or(ContractDetailsError::ContractNotFound)?;

let node = node::Entity::find_by_id(node_id)
.select_only()
.column(node::Column::Name)
.into_tuple::<String>()
.one(txn)
.await?
.ok_or(ContractDetailsError::ContractWithoutRelatedNode)?;

let owner = owner
.map(|address| {
Result::<_, ContractDetailsError>::Ok(
AccountId32::new(
address
.try_into()
.map_err(|_| ContractDetailsError::IncorrectAddressSizeOfOwner)?,
)
.to_ss58check(),
)
})
.transpose()?;

Ok(Json(ContractData {
node,
code_hash: code_hash.as_slice().try_into()?,
owner,
}))
})
.transpose()?;

Ok(Json(ContractData {
code_hash: code_hash.as_slice().try_into()?,
owner,
}))
})
.await
.into_raw_result()
}

#[cfg(test)]
Expand Down Expand Up @@ -165,6 +195,7 @@ mod tests {
.unwrap();

assert_json!(response.json().await, {
"node": "test",
"code_hash": hex::encode([0; 32]),
"owner": AccountId32::from([2; 32]).to_string(),
})
Expand Down
19 changes: 19 additions & 0 deletions crates/server/src/handlers/docs/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use std::sync::Arc;

use aide::{
axum::{routing::get, ApiRouter},
openapi::OpenApi,
redoc::Redoc,
};
use axum::{Extension, Json};
use db::DatabaseConnection;

/// Create an [`ApiRouter`] that provides an API server with documentation routes.
pub(crate) fn routes() -> ApiRouter<Arc<DatabaseConnection>> {
ApiRouter::new()
.route("/", Redoc::new("/docs/api.json").axum_route())
.route(
"/api.json",
get(|Extension(oapi): Extension<Arc<OpenApi>>| async move { Json(oapi) }),
)
}
64 changes: 64 additions & 0 deletions crates/server/src/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::fmt::Display;

use axum::response::IntoResponse;
use db::{build_session, event::EventBody};
use serde_json::{json, Value};
use sp_core::{
crypto::{AccountId32, Ss58Codec},
sr25519::{Pair, Public, Signature},
Pair as _,
};

use crate::hex_hash::HexHash;

/// Generate example values for OAPI documentation.
macro_rules! generate_examples {
($name:ident, $type:ty, $expr:expr) => {
::paste::paste! {
#[doc = concat!("Generate example [`", stringify!($type), "`] value for OAPI documentation.")]
pub(crate) fn [<example_ $name>]() -> $type {
$expr
}
}
};

($name:ident, $type:ty, $expr:expr; $($name_repeat:ident, $type_repeat:ty, $expr_repeat:expr);+) => {
generate_examples!($name, $type, $expr);
generate_examples!($($name_repeat, $type_repeat, $expr_repeat);+);
}
}

/// Convert an error into a JSON value suitable for OAPI documentation.
pub(crate) fn example_error<E: Display + IntoResponse>(err: E) -> Value {
let error = err.to_string();

json! {{
"code": err.into_response().status().as_u16(),
"error": error,
}}
}

generate_examples!(
database_identifier, i64, 1;
hex_hash, HexHash, HexHash([200; 32]);
cargo_contract_version, String, String::from("3.0.1");
rustc_version, String, String::from("1.70.0");
build_session_status, build_session::Status, build_session::Status::Completed;
log_position, Option<i64>, Some(40);
log_entry, String, String::from("Compiling futures-util v0.3.28");
timestamp, i64, 1672531200;
account, AccountId32, AccountId32::from_ss58check("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY").unwrap();
public_key, Public, Public(example_account().into());
signature, Signature, Pair::from_seed(&[0; 32]).sign(b"test message");
token, String, String::from("UYEIngStyH6Bxu1hLFIIwBxLgyMBhMQv4SVR1KzzbvzIDCSMcwwF8ApXagqyuWbh");
event_body, EventBody, EventBody::CodeHashUpdate {
new_code_hash: hex::encode([200; 32]),
};
file, String, String::from("lib.rs");
files, Vec<String>, vec![
String::from("lib.rs"),
String::from("Cargo.toml"),
String::from("Cargo.lock"),
];
node, String, String::from("alephzero")
);
17 changes: 2 additions & 15 deletions docs/self-hosted.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ level = "info"
[builder]
# Path where to store temporary build images
images_path = "/tmp/images"
# URL of an API server
api_server_url = "https://api.example.com"
# The amount of build workers to start simultaneously.
worker_count = 1
# Build duration limit, after which the container if forcefully deleted (in seconds).
Expand Down Expand Up @@ -128,21 +130,6 @@ cargo build --release

## Smart contract builder image

Before building the smart contract builder image itself, we need to adjust the
API server URL in the `flake.nix` file.

Search for the following snippet in `flake.nix` file:

```
url = "https://api.patron.works";
```

and adjust it for your own API server endpoint:

```
url = "https://api.example.com";
```

To build the Docker image itself, you can utilize the next command:

```sh
Expand Down
Loading

0 comments on commit 5bbe85a

Please sign in to comment.