From afe26fd5af415c0fc339f222f2972c399942c481 Mon Sep 17 00:00:00 2001 From: RostarMarek Date: Fri, 27 Sep 2024 11:19:55 +0200 Subject: [PATCH] Cardano: Add vote delegation to node api Added vote delegation to node api and wasm generation. Signed-off-by: RostarMarek --- CHANGELOG-npm.md | 3 ++ NPM_VERSION | 2 +- sandbox/src/Cardano.tsx | 58 ++++++++++++++++++++++++++++++++-- scripts/build-protos.rs | 6 +++- src/cardano.rs | 10 ++++++ src/shiftcrypto.bitbox02.rs | 4 +++ src/wasm/types.rs | 8 +++++ tests/subtests/test_cardano.rs | 57 +++++++++++++++++++++++++++++++++ 8 files changed, 144 insertions(+), 4 deletions(-) diff --git a/CHANGELOG-npm.md b/CHANGELOG-npm.md index 37e0067..5f7e921 100644 --- a/CHANGELOG-npm.md +++ b/CHANGELOG-npm.md @@ -2,6 +2,9 @@ ## Unreleased +## 0.8.0 +- cardano: allow vote delegation + ## 0.7.0 - btc: handle error when an input's previous transaction is required but missing - btc: add support for regtest diff --git a/NPM_VERSION b/NPM_VERSION index bcaffe1..8adc70f 100644 --- a/NPM_VERSION +++ b/NPM_VERSION @@ -1 +1 @@ -0.7.0 \ No newline at end of file +0.8.0 \ No newline at end of file diff --git a/sandbox/src/Cardano.tsx b/sandbox/src/Cardano.tsx index 322af09..c15e082 100644 --- a/sandbox/src/Cardano.tsx +++ b/sandbox/src/Cardano.tsx @@ -133,12 +133,11 @@ function CardanoAddress({ bb02 }: Props) { - ); } function CardanoSignTransaction({ bb02 }: Props) { - type TxType = 'normal' | 'zero-ttl' | 'tokens' | 'delegate' | 'withdraw-staking-rewards'; + type TxType = 'normal' | 'zero-ttl' | 'tokens' | 'delegate' | 'vote-delegation' | 'vote-delegation-keyhash' | 'withdraw-staking-rewards'; const [txType, setTxType] = useState('normal'); const [running, setRunning] = useState(false); const [result, setResult] = useState(); @@ -151,6 +150,8 @@ function CardanoSignTransaction({ bb02 }: Props) { ['zero-ttl', 'Transaction with TTL=0'], ['tokens', 'Transaction sending tokens'], ['delegate', 'Delegate staking to a pool'], + ['vote-delegation', 'Delegate vote to a dRep'], + ['vote-delegation-keyhash', 'Delegate vote to a dRep with a keyhash'], ['withdraw-staking-rewards', 'Withdraw staking rewards'], ]; @@ -176,6 +177,8 @@ function CardanoSignTransaction({ bb02 }: Props) { }; const changeAddress = await bb02.cardanoAddress(network, changeConfig, false); + const drepType: bitbox.CardanoDrepType = 'alwaysAbstain'; + const drepKeyHashType: bitbox.CardanoDrepType = 'keyHash'; const transaction = () => { switch (txType) { case 'normal': @@ -290,6 +293,57 @@ function CardanoSignTransaction({ bb02 }: Props) { validityIntervalStart: BigInt(41110811), allowZeroTTL: false, }; + case 'vote-delegation': + return { + network, + inputs, + outputs: [ + { + encodedAddress: changeAddress, + value: BigInt(2741512), + scriptConfig: changeConfig, + }, + ], + fee: BigInt(191681), + ttl: BigInt(41539125), + certificates: [ + { + voteDelegation: { + keypath: "m/1852'/1815'/0'/2/0", + type: drepType, + }, + }, + ], + withdrawals: [], + validityIntervalStart: BigInt(41110811), + allowZeroTTL: false, + }; + case 'vote-delegation-keyhash': + return { + network, + inputs, + outputs: [ + { + encodedAddress: changeAddress, + value: BigInt(2741512), + scriptConfig: changeConfig, + }, + ], + fee: BigInt(191681), + ttl: BigInt(41539125), + certificates: [ + { + voteDelegation: { + keypath: "m/1852'/1815'/0'/2/0", + type: drepKeyHashType, + drepCredhash: new Uint8Array(hexToArrayBuffer("abababababababababababababababababababababababababababab")), + }, + }, + ], + withdrawals: [], + validityIntervalStart: BigInt(41110811), + allowZeroTTL: false, + }; case 'withdraw-staking-rewards': return { network, diff --git a/scripts/build-protos.rs b/scripts/build-protos.rs index 8547470..d5ef0e0 100644 --- a/scripts/build-protos.rs +++ b/scripts/build-protos.rs @@ -67,11 +67,11 @@ fn add_serde_attrs(c: &mut prost_build::Config) { "shiftcrypto.bitbox02.BTCPubRequest.XPubType.CAPITAL_YPUB", "serde(rename = \"Ypub\")", ), + // Cardano ( "shiftcrypto.bitbox02.CardanoNetwork.CardanoMainnet", "serde(rename = \"mainnet\")", ), - // Cardano ( "shiftcrypto.bitbox02.CardanoNetwork.CardanoTestnet", "serde(rename = \"testnet\")", @@ -92,6 +92,10 @@ fn add_serde_attrs(c: &mut prost_build::Config) { "keypath_stake", "serde(deserialize_with = \"crate::keypath::serde_deserialize\")", ), + ( + "shiftcrypto.bitbox02.CardanoSignTransactionRequest.Certificate.VoteDelegation.type", + "serde(deserialize_with = \"crate::cardano::serde_deserialize_drep_type\")", + ), ]; for (path, attr) in type_attrs { diff --git a/src/cardano.rs b/src/cardano.rs index 83412b7..ddb10e6 100644 --- a/src/cardano.rs +++ b/src/cardano.rs @@ -15,6 +15,16 @@ where Ok(network as i32) } +#[cfg(feature = "wasm")] +pub(crate) fn serde_deserialize_drep_type<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + use serde::Deserialize; + let drep_type = pb::cardano_sign_transaction_request::certificate::vote_delegation::CardanoDRepType::deserialize(deserializer)?; + Ok(drep_type as i32) +} + #[cfg(feature = "wasm")] #[derive(serde::Deserialize)] pub(crate) struct SerdeScriptConfig(pb::cardano_script_config::Config); diff --git a/src/shiftcrypto.bitbox02.rs b/src/shiftcrypto.bitbox02.rs index 0e6de11..fb00593 100644 --- a/src/shiftcrypto.bitbox02.rs +++ b/src/shiftcrypto.bitbox02.rs @@ -1248,6 +1248,10 @@ pub mod cardano_sign_transaction_request { )] pub keypath: ::prost::alloc::vec::Vec, #[prost(enumeration = "vote_delegation::CardanoDRepType", tag = "2")] + #[cfg_attr( + feature = "wasm", + serde(deserialize_with = "crate::cardano::serde_deserialize_drep_type") + )] pub r#type: i32, #[prost(bytes = "vec", optional, tag = "3")] pub drep_credhash: ::core::option::Option<::prost::alloc::vec::Vec>, diff --git a/src/wasm/types.rs b/src/wasm/types.rs index d8c5b5e..63352e9 100644 --- a/src/wasm/types.rs +++ b/src/wasm/types.rs @@ -104,6 +104,7 @@ type CardanoOutput = { scriptConfig?: CardanoScriptConfig; assetGroups?: CardanoAssetGroup[]; } +type CardanoDrepType = 'keyHash' | 'scriptHash' | 'alwaysAbstain' | 'alwaysNoConfidence' type CardanoCertificate = | { stakeRegistration: { @@ -120,6 +121,13 @@ type CardanoCertificate = keypath: Keypath poolKeyhash: Uint8Array } + } + | { + voteDelegation: { + keypath: Keypath + type: CardanoDrepType + drepCredhash?: Uint8Array + } }; type CardanoWithdrawal = { keypath: Keypath; diff --git a/tests/subtests/test_cardano.rs b/tests/subtests/test_cardano.rs index 93834d0..a317428 100644 --- a/tests/subtests/test_cardano.rs +++ b/tests/subtests/test_cardano.rs @@ -223,6 +223,63 @@ pub async fn test(bitbox: &PairedBitBox) { ); } } + // Delegating vote to a drep with a keyhash + { + let transaction = pb::CardanoSignTransactionRequest { + network: pb::CardanoNetwork::CardanoMainnet as i32, + inputs: vec![pb::cardano_sign_transaction_request::Input { + keypath: keypath_input.to_vec(), + prev_out_hash: hex::decode( + "59864ee73ca5d91098a32b3ce9811bac1996dcbaefa6b6247dcaafb5779c2538", + ) + .unwrap(), + prev_out_index: 0, + }], + outputs: vec![pb::cardano_sign_transaction_request::Output { + encoded_address: change_address.clone(), + value: 2741512, + script_config: Some(change_config.clone()), + ..Default::default() + }], + fee: 191681, + ttl: 41539125, + certificates: vec![ + pb::cardano_sign_transaction_request::Certificate { + cert: Some( + pb::cardano_sign_transaction_request::certificate::Cert::VoteDelegation( + pb::cardano_sign_transaction_request::certificate::VoteDelegation { + keypath: vec![2147485500, 2147485463, 2147483648, 2, 0], + r#type: pb::cardano_sign_transaction_request::certificate::vote_delegation::CardanoDRepType::KeyHash.into(), + drep_credhash: Some(hex::decode( + "abababababababababababababababababababababababababababab", + ) + .unwrap()), + }, + ), + ), + }, + ], + withdrawals: vec![], + validity_interval_start: 41110811, + allow_zero_ttl: false, + }; + + if semver::VersionReq::parse(">=9.21.0") + .unwrap() + .matches(bitbox.version()) + { + let witness = bitbox.cardano_sign_transaction(transaction).await.unwrap(); + assert_eq!(witness.shelley_witnesses.len(), 2); + assert_eq!( + hex::encode(&witness.shelley_witnesses[0].public_key), + "6b5d4134cfc66281827d51cb0196f1a951ce168c19ba1314233f43d39d91e2bc", + ); + assert_eq!( + hex::encode(&witness.shelley_witnesses[1].public_key), + "ed0d6426efcae3b02b963db0997845ba43ed53c131aa2f0faa01976ddcdb3751", + ); + } + } // Withdrawing staking rewards... { let transaction = pb::CardanoSignTransactionRequest {