#![cfg(not(feature = "client-wasm"))]
use fabric_sdk::{
error::LifecycleError,
fabric::lifecycle::{
chaincode_source, ApproveChaincodeDefinitionForMyOrgArgs, ChaincodeSource,
CheckCommitReadinessArgs, CommitChaincodeDefinitionArgs, InstallChaincodeResult,
},
gateway::{client, lifecycle::LifecycleClient},
identity,
};
use std::{env, fs};
async fn build_admin_client(
msp_id: &str,
cert_path_var: &str,
key_path_var: &str,
tls_cert_path_var: &str,
authority: &str,
) -> client::Client {
let key = fs::read_to_string(
env::var(key_path_var).unwrap_or_else(|_| panic!("{key_path_var} environment variable not set")),
)
.unwrap_or_else(|_| panic!("Couldn't read file at {key_path_var}"));
let identity = identity::IdentityBuilder::from_pem(
fs::read(
env::var(cert_path_var)
.unwrap_or_else(|_| panic!("{cert_path_var} environment variable not set")),
)
.unwrap_or_else(|_| panic!("Couldn't read file at {cert_path_var}"))
.as_slice(),
)
.unwrap()
.with_msp(msp_id)
.unwrap()
.with_private_key(key)
.unwrap()
.build()
.unwrap();
let tls_cert = fs::read(
env::var(tls_cert_path_var)
.unwrap_or_else(|_| panic!("{tls_cert_path_var} environment variable not set")),
)
.unwrap_or_else(|_| panic!("Couldn't read file at {tls_cert_path_var}"));
let mut client = client::ClientBuilder::new()
.with_identity(identity)
.unwrap()
.with_tls(tls_cert)
.unwrap()
.with_scheme("https")
.unwrap()
.with_authority(authority)
.unwrap()
.build()
.unwrap();
client.connect().await.unwrap();
client
}
async fn install_or_reuse(
lifecycle: &LifecycleClient<'_>,
package: Vec<u8>,
) -> InstallChaincodeResult {
match lifecycle.install_chaincode(package).await {
Ok(result) => result,
Err(LifecycleError::NodeError(ref msg)) if msg.contains("already successfully installed") => {
let package_id = msg
.split("package ID '")
.nth(1)
.and_then(|s| s.split('\'').next())
.unwrap_or_else(|| panic!("Failed to parse package_id from: {msg}"))
.to_string();
let queried = lifecycle
.query_installed_chaincode(&package_id)
.await
.unwrap_or_else(|e| panic!("Failed to query already-installed chaincode: {e:?}"));
InstallChaincodeResult {
package_id: queried.package_id,
label: queried.label,
}
}
Err(e) => panic!("install_chaincode failed: {e:?}"),
}
}
pub async fn run() {
let channel_name =
env::var("CHANNEL_NAME").expect("CHANNEL_NAME environment variable not set");
let chaincode_name =
env::var("CHAINCODE_NAME").expect("CHAINCODE_NAME environment variable not set");
let chaincode_version =
env::var("CHAINCODE_VERSION").expect("CHAINCODE_VERSION environment variable not set");
let msp_id_org1 = env::var("MSP_ID").expect("MSP_ID environment variable not set");
let msp_id_org2 = env::var("MSP_ID_ORG2").expect("MSP_ID_ORG2 environment variable not set");
let org1_client = build_admin_client(
&msp_id_org1,
"PEER1_ADMIN_CERT_PATH",
"PEER1_ADMIN_KEY_PATH",
"PEER1_TLS_CERT_PATH",
"localhost:7051",
)
.await;
let org2_client = build_admin_client(
&msp_id_org2,
"PEER2_ADMIN_CERT_PATH",
"PEER2_ADMIN_KEY_PATH",
"PEER2_TLS_CERT_PATH",
"localhost:9051",
)
.await;
let org1_lifecycle = org1_client.get_lifecycle_client();
let org2_lifecycle = org2_client.get_lifecycle_client();
let package = fs::read("tests/resources/basic.tar.gz")
.expect("tests/resources/basic.tar.gz not found");
let install_result = install_or_reuse(&org1_lifecycle, package.clone()).await;
println!(
"Org1 installed: package_id={}, label={}",
install_result.package_id, install_result.label
);
assert!(!install_result.package_id.is_empty());
assert_eq!(install_result.label, "basic");
let org2_install_result = install_or_reuse(&org2_lifecycle, package).await;
println!(
"Org2 installed: package_id={}, label={}",
org2_install_result.package_id, org2_install_result.label
);
assert_eq!(org2_install_result.package_id, install_result.package_id);
let installed = org1_lifecycle.query_installed_chaincodes().await.unwrap();
assert!(
installed
.installed_chaincodes
.iter()
.any(|cc| cc.package_id == install_result.package_id),
"Package not found in query_installed_chaincodes result"
);
let queried = org1_lifecycle
.query_installed_chaincode(&install_result.package_id)
.await
.unwrap();
assert_eq!(queried.package_id, install_result.package_id);
assert_eq!(queried.label, "basic");
let pkg_bytes = org1_lifecycle
.get_installed_chaincode_package(&install_result.package_id)
.await
.unwrap();
assert!(!pkg_bytes.is_empty(), "Downloaded package must not be empty");
let sequence = match org1_lifecycle
.query_chaincode_definition(&channel_name, &chaincode_name)
.await
{
Ok(def) => def.sequence + 1,
Err(_) => 1,
};
println!("Using sequence={sequence}");
org1_lifecycle
.approve_chaincode_definition(
&channel_name,
ApproveChaincodeDefinitionForMyOrgArgs {
name: chaincode_name.clone(),
version: chaincode_version.clone(),
sequence,
source: Some(ChaincodeSource {
r#type: Some(chaincode_source::Type::LocalPackage(
chaincode_source::Local {
package_id: install_result.package_id.clone(),
},
)),
}),
..Default::default()
},
)
.await
.unwrap();
println!("Org1 approved chaincode definition (sequence={sequence})");
org2_lifecycle
.approve_chaincode_definition(
&channel_name,
ApproveChaincodeDefinitionForMyOrgArgs {
name: chaincode_name.clone(),
version: chaincode_version.clone(),
sequence,
source: Some(ChaincodeSource {
r#type: Some(chaincode_source::Type::LocalPackage(
chaincode_source::Local {
package_id: org2_install_result.package_id.clone(),
},
)),
}),
..Default::default()
},
)
.await
.unwrap();
println!("Org2 approved chaincode definition (sequence={sequence})");
let readiness = org1_lifecycle
.check_commit_readiness(
&channel_name,
CheckCommitReadinessArgs {
name: chaincode_name.clone(),
version: chaincode_version.clone(),
sequence,
..Default::default()
},
)
.await
.unwrap();
assert!(
readiness.approvals.get(&msp_id_org1).copied().unwrap_or(false),
"{msp_id_org1} should have approved the definition"
);
assert!(
readiness.approvals.get(&msp_id_org2).copied().unwrap_or(false),
"{msp_id_org2} should have approved the definition"
);
org1_lifecycle
.commit_chaincode_definition(
&channel_name,
CommitChaincodeDefinitionArgs {
name: chaincode_name.clone(),
version: chaincode_version.clone(),
sequence,
..Default::default()
},
&[&org2_client],
)
.await
.unwrap();
println!("Committed chaincode definition (sequence={sequence})");
let committed = org1_lifecycle
.query_chaincode_definition(&channel_name, &chaincode_name)
.await
.unwrap();
assert_eq!(committed.sequence, sequence);
assert_eq!(committed.version, chaincode_version);
println!(
"Verified: {} v{} seq={}",
chaincode_name, committed.version, committed.sequence
);
let approved = org1_lifecycle
.query_approved_chaincode_definition(&channel_name, &chaincode_name, sequence)
.await
.unwrap();
assert_eq!(approved.sequence, sequence);
assert_eq!(approved.version, chaincode_version);
let all_committed = org1_lifecycle
.query_chaincode_definitions(&channel_name)
.await
.unwrap();
assert!(
all_committed
.chaincode_definitions
.iter()
.any(|def| def.name == chaincode_name),
"Chaincode not found in query_chaincode_definitions result"
);
}