Signing transactions
Advanced
Tutorial
Overview
To use Threshold ECDSA, you will need a public key and a transaction to sign. Public keys can be obtained through the ecdsa_public_key
API method, and transactions can be signed using the sign_with_ecdsa
API method.
Cost
To send calls to the Threshold ECDSA API, cycles must be attached to the call. Learn more about Threshold ECDSA costs.
Obtaining public keys
To sign transactions, first you need to obtain a public key by making a call to the ecdsa_public_key
API method.
- Motoko
- rust
// Declare "ic" to be the management canister, which is evoked by `actor("aaaaa-aa")`.:
let ic : BIG = actor("aaaaa-aa");
public shared (msg) func public_key() : async { #Ok : { public_key: Blob }; #Err : Text } {
let caller = Principal.toBlob(msg.caller);
try {
// Make a call to the management canister to request an ECDSA public key:
let { public_key } = await ic.ecdsa_public_key({
canister_id = null;
derivation_path = [ caller ];
key_id = { curve = #secp256k1; name = "test_key_1" };
});
#Ok({ public_key })
} catch (err) {
#Err(Error.message(err))
}
};
#[update]
async fn public_key() -> Result<PublicKeyReply, String> {
let request = ECDSAPublicKey {
canister_id: None,
derivation_path: vec![],
key_id: EcdsaKeyIds::TestKeyLocalDevelopment.to_key_id(),
};
let (res,): (ECDSAPublicKeyReply,) =
ic_cdk::call(mgmt_canister_id(), "ecdsa_public_key", (request,))
.await
.map_err(|e| format!("ecdsa_public_key failed {}", e.1))?;
Ok(PublicKeyReply {
public_key_hex: hex::encode(&res.public_key),
})
}
Signing transactions
To sign a transaction, make a call to the sign_with_ecdsa
API method:
- Motoko
- rust
public shared (msg) func sign(message_hash: Blob) : async { #Ok : { signature: Blob }; #Err : Text } {
assert(message_hash.size() == 32);
let caller = Principal.toBlob(msg.caller);
try {
Cycles.add(10_000_000_000);
let { signature } = await ic.sign_with_ecdsa({
message_hash;
derivation_path = [ caller ];
key_id = { curve = #secp256k1; name = "dfx_test_key" };
});
#Ok({ signature })
} catch (err) {
#Err(Error.message(err))
}
};
#[update]
async fn sign(message: String) -> Result<SignatureReply, String> {
let request = SignWithECDSA {
message_hash: sha256(&message).to_vec(),
derivation_path: vec![],
key_id: EcdsaKeyIds::TestKeyLocalDevelopment.to_key_id(),
};
let (response,): (SignWithECDSAReply,) = ic_cdk::api::call::call_with_payment(
mgmt_canister_id(),
"sign_with_ecdsa",
(request,),
25_000_000_000,
)
.await
.map_err(|e| format!("sign_with_ecdsa failed {}", e.1))?;
Ok(SignatureReply {
signature_hex: hex::encode(&response.signature),
})
}