Signing Transactions
Before a transaction can be sent to the Dogecoin network, each input must be signed. Canisters can sign transactions with threshold ECDSA through the sign_with_ecdsa system API endpoint.
Threshold ECDSA
The following snippet shows a simplified example of how to sign a Dogecoin transaction where all UTXOs are owned by own_address and own_address is a P2PKH address.
pub async fn sign_transaction<SignFun, Fut>(
ctx: &DogecoinContext,
own_public_key: &PublicKey,
own_address: &Address,
mut transaction: Transaction,
derivation_path: Vec<Vec<u8>>,
signer: SignFun,
) -> Transaction
where
SignFun: Fn(String, Vec<Vec<u8>>, Vec<u8>) -> Fut,
Fut: std::future::Future<Output = SecpSignature>,
{
assert_eq!(
own_address.address_type(),
Some(AddressType::P2pkh),
"Only P2PKH addresses are supported"
);
let transaction_clone = transaction.clone();
let sighash_cache = SighashCache::new(&transaction_clone);
for (index, input) in transaction.input.iter_mut().enumerate() {
let sighash = sighash_cache
.legacy_signature_hash(
index,
&own_address.script_pubkey(),
EcdsaSighashType::All.to_u32(),
)
.unwrap();
let signature = signer(
ctx.key_name.to_string(),
derivation_path.clone(),
sighash.as_byte_array().to_vec(),
)
.await;
let mut signature = signature.serialize_der().to_vec();
signature.push(EcdsaSighashType::All.to_u32() as u8);
let sig_bytes = PushBytesBuf::try_from(signature).unwrap();
let pubkey_bytes = PushBytesBuf::try_from(own_public_key.to_bytes()).unwrap();
input.script_sig = Builder::new()
.push_slice(sig_bytes)
.push_slice(pubkey_bytes)
.into_script();
}
transaction
}
View the source on GitHub: p2pkh.rs
The signature function signer: SignFun passed as argument is shown below. This function makes a call to the sign_with_ecdsa system API endpoint to sign the provided transaction sighash.
/// Signs a 32-byte message hash using the ECDSA key derived from the given path.
///
/// This function uses the ICP ECDSA signing API to produce a compact, 64-byte signature.
pub async fn sign_with_ecdsa(
key_name: String,
derivation_path: Vec<Vec<u8>>,
message_hash: Vec<u8>,
) -> Signature {
let signature = management_canister::sign_with_ecdsa(&SignWithEcdsaArgs {
message_hash,
derivation_path,
key_id: EcdsaKeyId {
curve: EcdsaCurve::Secp256k1,
name: key_name,
},
})
.await
.unwrap()
.signature;
Signature::from_compact(&signature).unwrap()
}
View the source on GitHub: ecdsa.rs