I have deployed 2 canisters on ICP,
x5kf3-raaaa-aaaah-aq2xq-cai, which provides a build_witness function
#[update]
fn build_witness(tx_id_hex: String, sign_hashes_hex: Vec<String>)->(bool, Vec<u8>){
let available_cycles = msg_cycles_available();
if available_cycles < BUILD_WITNESS_FEE {
ic_cdk::eprintln!("Not enough cycles provided for get build witness operation.");
return (false, "Not enough cycles provided for get build witness operation.".as_bytes().to_vec());
}
let _ = msg_cycles_accept(BUILD_WITNESS_FEE);
let tx_id_bytes = match hex::decode(tx_id_hex.clone()){
Ok(bytes) => bytes,
Err(e) => {
ic_cdk::eprintln!("hex::decode error {:?}", e);
return (false, format!("hex::decode error {:?}", e).as_bytes().to_vec());
}
};
let mut sign_hashes_bytes = Vec::with_capacity(sign_hashes_hex.len());
for hash in &sign_hashes_hex {
let hash_bytes = match hex::decode(hash.clone()){
Ok(bytes) => bytes,
Err(e) => {
ic_cdk::eprintln!("hex::decode error {:?}", e);
return (false, format!("hex::decode error {:?}", e).as_bytes().to_vec());
}
};
sign_hashes_bytes.push(hash_bytes);
}
let genesis_sc_root = GENESIS_SC_ROOT.with(|p| p.borrow().clone());
let witness = match witness::build_public_witness(genesis_sc_root.as_bytes(),&tx_id_bytes, &sign_hashes_bytes){
Ok(witness) => witness,
Err(e) => {
ic_cdk::eprintln!("witness::build_public_witness error {:?}", e);
return (false, format!("witness::build_public_witness error {:?}", e).as_bytes().to_vec());
}
};
ic_cdk::println!("witness:{}", hex::encode(witness.clone()));
return (true, witness)
}
xbsjj-qaaaa-aaaai-aqamq-cai, which provides 2 funcitons
#[update]
pub async fn verify_and_sign(tx_id_hex :String, sign_hashes_hex: Vec<String>, proof_hex:String) -> (bool, Vec<String>) {
let available_cycles = msg_cycles_available();
let needed_cycles = PLONK_VERIY_FEE + BUILD_WITNESS_FEE + SIGN_WITH_ECDSA_FEE;
ic_cdk::println!("available_cycles:{:?}, needed_cycles:{:?}", available_cycles, needed_cycles);
if available_cycles < needed_cycles {
ic_cdk::eprintln!("Not enough cycles provided for get sign hashes operation.");
return (false, vec![]);
}
let _ = msg_cycles_accept(needed_cycles);
let derivation_path = DERIVATION_PATH.with(|d| d.clone());
let key_name = KEY_NAME.with(|kn| kn.borrow().to_string());
ic_cdk::println!("tx_hex:{:?}, sign_hashes_hex:{:?}, proof_hex:{:?}", tx_id_hex, sign_hashes_hex, proof_hex);
let proof_bytes = match hex::decode(proof_hex){
Ok(bytes) => bytes,
Err(e) => {
ic_cdk::eprintln!("hex::decode error {:?}", e);
return (false, vec![]);
}
};
//build public witness
let witness_builder = WITNESS_BUILDER_CANISTER.with(|d| d.borrow().clone());
let (build_witness_success, witness_bytes): (bool,Vec<u8>)= match call_with_payment(witness_builder, "build_witness", (tx_id_hex.clone(), sign_hashes_hex.clone()), BUILD_WITNESS_FEE).await {
Ok((build_witness_success, witness_bytes)) => (build_witness_success, witness_bytes),
Err(err) => {
// Handle the error here
ic_cdk::eprintln!("build_public_witness error: {:?}", err);
// Return a default value or handle it appropriately
return (false, vec![])
}
};
if !build_witness_success{
ic_cdk::eprintln!("build_public_witness failed");
return (false, vec![]);
}
//verify
let plonk_verifier = PLONK_VERIFIER_CANISTER.with(|d: &RefCell<Principal>| d.borrow().clone());
let zk_valid: bool = match call_with_payment(plonk_verifier, "plonk_verify", (proof_bytes, witness_bytes,), PLONK_VERIY_FEE).await {
Ok((valid,)) => valid,
Err(err) => {
// Handle the error here
ic_cdk::eprintln!("plonk verify error: {:?}", err);
// Return a default value or handle it appropriately
return (false, vec![])
}
};
if !zk_valid {
ic_cdk::eprintln!("plonk verify failed");
return (false, vec![]);
}
let mut sigs = Vec::with_capacity(sign_hashes_hex.len());
for hash in &sign_hashes_hex {
ic_cdk::println!("{}", hash);
let hash_bytes = match hex::decode(hash) {
Ok(bytes) => bytes,
Err(e) => {
ic_cdk::eprintln!("hex::decode error {:?}", e);
return (false, vec![]);
}
};
let sig = ecdsa_api::sign(key_name.clone(), derivation_path.clone(), hash_bytes.clone()).await;
ic_cdk::println!("hash:{}, sig:{}", hash, hex::encode(sig.clone()));
sigs.push(hex::encode(sig));
}
(true, sigs)
}
and
#[update]
pub async fn verify_and_sign_free(tx_id_hex :String, sign_hashes_hex: Vec<String>, proof_hex:String) -> (bool, Vec<String>) {
let derivation_path = DERIVATION_PATH.with(|d| d.clone());
let key_name = KEY_NAME.with(|kn| kn.borrow().to_string());
ic_cdk::println!("tx_hex:{:?}, sign_hashes_hex:{:?}, proof_hex:{:?}", tx_id_hex, sign_hashes_hex, proof_hex);
let proof_bytes = match hex::decode(proof_hex){
Ok(bytes) => bytes,
Err(e) => {
ic_cdk::eprintln!("hex::decode error {:?}", e);
return (false, vec![]);
}
};
//build public witness
let witness_builder = WITNESS_BUILDER_CANISTER.with(|d| d.borrow().clone());
let (build_witness_success, witness_bytes): (bool,Vec<u8>)= match call_with_payment(witness_builder, "build_witness", (tx_id_hex.clone(), sign_hashes_hex.clone()), BUILD_WITNESS_FEE).await {
Ok((build_witness_success, witness_bytes)) => (build_witness_success, witness_bytes),
Err(err) => {
// Handle the error here
ic_cdk::eprintln!("build_public_witness error: {:?}", err);
// Return a default value or handle it appropriately
return (false, vec![])
}
};
if !build_witness_success{
ic_cdk::eprintln!("build_public_witness failed");
return (false, vec![]);
}
//verify
let plonk_verifier = PLONK_VERIFIER_CANISTER.with(|d: &RefCell<Principal>| d.borrow().clone());
let zk_valid: bool = match call_with_payment(plonk_verifier, "plonk_verify", (proof_bytes, witness_bytes,), PLONK_VERIY_FEE).await {
Ok((valid,)) => valid,
Err(err) => {
// Handle the error here
ic_cdk::eprintln!("plonk verify error: {:?}", err);
// Return a default value or handle it appropriately
return (false, vec![])
}
};
if !zk_valid {
ic_cdk::eprintln!("plonk verify failed");
return (false, vec![]);
}
let mut sigs = Vec::with_capacity(sign_hashes_hex.len());
for hash in &sign_hashes_hex {
ic_cdk::println!("{}", hash);
let hash_bytes = match hex::decode(hash) {
Ok(bytes) => bytes,
Err(e) => {
ic_cdk::eprintln!("hex::decode error {:?}", e);
return (false, vec![]);
}
};
let sig = ecdsa_api::sign(key_name.clone(), derivation_path.clone(), hash_bytes.clone()).await;
ic_cdk::println!("hash:{}, sig:{}", hash, hex::encode(sig.clone()));
sigs.push(hex::encode(sig));
}
(true, sigs)
}
verify_and_sign_free is a cycles free version of verify_and_sign.
Test Steps
1. call x5kf3-raaaa-aaaah-aq2xq-cai with user pay cycles, it works as expceted.
dfx canister call x5kf3-raaaa-aaaah-aq2xq-cai build_witness '("98da4b13e7508b84712850e723b0f2f339e93df6250a0e93af7f231e85fdac8b", vec{"2cb50594fb592826b00b7e83d70ad96feef2a55a183222409883e1bfc6022662"})' --network ic --with-cycles 100000000 --wallet 4ichq-aqaaa-aaaak-qcosa-cai
(
true,
blob "\00\00\00\14\00\00\00\00\00\00\00\14\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\98\da\4b\13\e7\50\8b\84\71\28\50\e7\23\b0\f2\f3\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\39\e9\3d\f6\25\0a\0e\93\af\7f\23\1e\85\fd\ac\8b\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\2c\b5\05\94\fb\59\28\26\b0\0b\7e\83\d7\0a\d9\6f\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\ee\f2\a5\5a\18\32\22\40\98\83\e1\bf\c6\02\26\62\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\5b\96\db\d1\59\14\12\77\77\ef\58\37\32\79\b5\7e\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\c9\ca\81\6e\1f\7b\34\c4\3e\ad\9c\03\bb\46\36\4d",
)
2. If I call xbsjj-qaaaa-aaaai-aqamq-cai verify_and_sign_free function, it works as expected.
dfx canister call xbsjj-qaaaa-aaaai-aqamq-cai verify_and_sign_free '("98da4b13e7508b84712850e723b0f2f339e93df6250a0e93af7f231e85fdac8b", vec {"2cb50594fb592826b00b7e83d70ad96feef2a55a183222409883e1bfc6022662"}, "229baaee55571d2454fa2ba9f32e2b07135f0e10972009fd5e8a07b7e4025fe300ae9f755de6d73c11a82eb1b2cbc4fd2ba28c20fc215e51b7201ba31e262eb819709d1d8cfe03e7699b894d71052e6ccb5324c89825c2ecdf46d784f4a2e91510ac3e74bd63e57cd443ab4e8df6f8076742bf9a9e17cb61df35d08241eca8222c02e4390122e9c4b7bebb4a704fd78d96dba4cde80500e7240e15713301a4f8065fe4df227810ad02291f579fab794d24cba12c75c864fd47fcaa96343d2cfe05080197de901ad51b89e1035c6157b658f7b730a952e742b16ab99bb324307e12b6c16a4b4d0edd3af9d1eed0cc0ea001a8f1f3499f770ec6dd943119428db5100ac6efb2d4474c879247fd8d7132e33332c544fced798f85e24230bfd402890144705cf054a8784adf374e32da9729e541ee52963533a4a84abb04b83926aa03d7f95792708d08ae265db38423f35d5cf7b4203ccb28f0d330a7e0de694b6d0f324015bf06d3f7c5e8b5bc9f05018d98d8744f4ae99b16992adcc2b567704c1d7a69ec4b67334842aa1f105c00492fd92898a12b623ae942b0d27257c825191d2391cdc920925c37eddc3955bac7c562891b4f312a77b6cfd14beb54313bab0f92f3aa7b28e0756e51d2b6acee7f91c06f599ef8ac0fc84f3e9ca1a29f7dba05b1fc6dbb885994c78effe92941b231195cf990f4684ce80c8561840418de87134c1940edb3d7bfdc49c7202e28e795bd0547daee1e788764205c5e9b0995552896972dc9f8c8dc460f1b5a16db62ccf95fd27d02ec8972e69ee3e513bc43f01f2202ae9a09043697d1c5d49ed51813d81f51efc2aade8e480d8fff7014b6f41d09419fc87a071f116d4a04ffe99e50016b5b73bdd357c7fd72f0ac4bae6dbe25178b4dd99fe080c219741ccec1e2d713a29f9f4dd97ca323fbb9b49f572672022552b85f7e78be4dae71d3fd2adeef4549c4c584bc9787c51599a6f73c84382768aa27767f314f07cd3b8bcc4bb842daf2b3f43c9918e3513652ad8c9205071403995b6014a4dd5ef2fa03f1b497d0ec387be2a5f4c044f0f33f222a6e8ed5065ae326e0a46db583e874ce5567f0a215ed80ab39fa49159579529b5808f891012accdbb8424f962502df0a3b88b1abeca31104ae5094e83f4121b4774bd94621811743f001c3cb970bfc1f7b0125429963df1c8e4164183b38ad15cfacce942bd22ebc1146292580cee8469b4d870f4e3093987d2efbdb9e3e2147fbbfe0332835a812b42955a77a17c659c8c7b582e05874833abef31cdeb2c2c5b718de13")' --network ic
(
true,
vec {
"7bc8a0bcec5d8e707eea512d819e8d1e5441cd0d328a2c9bd67146634c41a1af1df605305e077d8631310fa990fbd4690533931750f67535f785479d325d7109";
},
)
3. If I call xbsjj-qaaaa-aaaai-aqamq-cai verify_and_sign, it resports “Couldn’t send messge”
%dfx canister call xbsjj-qaaaa-aaaai-aqamq-cai verify_and_sign '("98da4b13e7508b84712850e723b0f2f339e93df6250a0e93af7f231e85fdac8b", vec {"2cb50594fb592826b00b7e83d70ad96feef2a55a183222409883e1bfc6022662"}, "229baaee55571d2454fa2ba9f32e2b07135f0e10972009fd5e8a07b7e4025fe300ae9f755de6d73c11a82eb1b2cbc4fd2ba28c20fc215e51b7201ba31e262eb819709d1d8cfe03e7699b894d71052e6ccb5324c89825c2ecdf46d784f4a2e91510ac3e74bd63e57cd443ab4e8df6f8076742bf9a9e17cb61df35d08241eca8222c02e4390122e9c4b7bebb4a704fd78d96dba4cde80500e7240e15713301a4f8065fe4df227810ad02291f579fab794d24cba12c75c864fd47fcaa96343d2cfe05080197de901ad51b89e1035c6157b658f7b730a952e742b16ab99bb324307e12b6c16a4b4d0edd3af9d1eed0cc0ea001a8f1f3499f770ec6dd943119428db5100ac6efb2d4474c879247fd8d7132e33332c544fced798f85e24230bfd402890144705cf054a8784adf374e32da9729e541ee52963533a4a84abb04b83926aa03d7f95792708d08ae265db38423f35d5cf7b4203ccb28f0d330a7e0de694b6d0f324015bf06d3f7c5e8b5bc9f05018d98d8744f4ae99b16992adcc2b567704c1d7a69ec4b67334842aa1f105c00492fd92898a12b623ae942b0d27257c825191d2391cdc920925c37eddc3955bac7c562891b4f312a77b6cfd14beb54313bab0f92f3aa7b28e0756e51d2b6acee7f91c06f599ef8ac0fc84f3e9ca1a29f7dba05b1fc6dbb885994c78effe92941b231195cf990f4684ce80c8561840418de87134c1940edb3d7bfdc49c7202e28e795bd0547daee1e788764205c5e9b0995552896972dc9f8c8dc460f1b5a16db62ccf95fd27d02ec8972e69ee3e513bc43f01f2202ae9a09043697d1c5d49ed51813d81f51efc2aade8e480d8fff7014b6f41d09419fc87a071f116d4a04ffe99e50016b5b73bdd357c7fd72f0ac4bae6dbe25178b4dd99fe080c219741ccec1e2d713a29f9f4dd97ca323fbb9b49f572672022552b85f7e78be4dae71d3fd2adeef4549c4c584bc9787c51599a6f73c84382768aa27767f314f07cd3b8bcc4bb842daf2b3f43c9918e3513652ad8c9205071403995b6014a4dd5ef2fa03f1b497d0ec387be2a5f4c044f0f33f222a6e8ed5065ae326e0a46db583e874ce5567f0a215ed80ab39fa49159579529b5808f891012accdbb8424f962502df0a3b88b1abeca31104ae5094e83f4121b4774bd94621811743f001c3cb970bfc1f7b0125429963df1c8e4164183b38ad15cfacce942bd22ebc1146292580cee8469b4d870f4e3093987d2efbdb9e3e2147fbbfe0332835a812b42955a77a17c659c8c7b582e05874833abef31cdeb2c2c5b718de13")' --network ic --with-cycles 28000000000 --wallet 4ichq-aqaaa-aaaak-qcosa-cai
Error: Failed to do wallet call.
Caused by: An error happened during the call: 2: Couldn't send message
4. I check my cycles, enough cycles in this wallet.
dfx cycles balance --network ic
6.999 TC (trillion cycles).
The following is the snapshot of execution records with dfx v0.22.0
Anybody have idea about this problem?