Skip to content

Commit

Permalink
Merge pull request #718 from Emurgo/evgenii/malformed_address_fix
Browse files Browse the repository at this point in the history
Malformed address deserialization fix
  • Loading branch information
lisicky authored Feb 11, 2025
2 parents 813f580 + 6b9f0ea commit 1d82859
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 16 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cardano-serialization-lib",
"version": "14.1.0",
"version": "14.1.1",
"description": "(De)serialization functions for the Cardano blockchain along with related utility functions",
"scripts": {
"rust:build-nodejs": "(rimraf ./rust/pkg && cd rust; wasm-pack build --target=nodejs; cd ..; npm run js:ts-json-gen; cd rust; wasm-pack pack) && npm run js:flowgen",
Expand Down
2 changes: 1 addition & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cardano-serialization-lib"
version = "14.1.0"
version = "14.1.1"
edition = "2018"
authors = ["EMURGO"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion rust/json-gen/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 16 additions & 10 deletions rust/src/protocol_types/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,17 +393,17 @@ impl Address {
}

fn from_bytes_impl_safe(data: &[u8]) -> Result<Address, DeserializeError> {
Self::from_bytes_internal_impl(data)
Self::from_bytes_internal_impl(data, false)
}

fn from_bytes_impl_unsafe(data: &[u8]) -> Address {
match Self::from_bytes_internal_impl(data) {
match Self::from_bytes_internal_impl(data, true) {
Ok(addr) => addr,
Err(_) => Address(AddrType::Malformed(MalformedAddress(data.to_vec())))
}
}

fn from_bytes_internal_impl(data: &[u8]) -> Result<Address, DeserializeError> {
fn from_bytes_internal_impl(data: &[u8], ignore_leftover_bytes: bool) -> Result<Address, DeserializeError> {
use std::convert::TryInto;
// header has 4 bits addr type discrim then 4 bits network discrim.
// Copied from shelley.cddl:
Expand All @@ -426,8 +426,13 @@ impl Address {
let header = data[0];
let network = header & 0x0F;
const HASH_LEN: usize = Ed25519KeyHash::BYTE_COUNT;
// should be static assert but it's maybe not worth importing a whole external crate for it now
assert_eq!(ScriptHash::BYTE_COUNT, HASH_LEN);

if ScriptHash::BYTE_COUNT != HASH_LEN {
return Err(DeserializeFailure::CustomError(
"ScriptHash and Ed25519KeyHash must have the same length".to_string(),
).into());
}

// checks the /bit/ bit of the header for key vs scripthash then reads the credential starting at byte position /pos/
let read_addr_cred = |bit: u8, pos: usize| {
let hash_bytes: [u8; HASH_LEN] = data[pos..pos + HASH_LEN].try_into().unwrap();
Expand All @@ -444,7 +449,7 @@ impl Address {
const BASE_ADDR_SIZE: usize = 1 + HASH_LEN * 2;
if data.len() < BASE_ADDR_SIZE {
Err(cbor_event::Error::NotEnough(data.len(), BASE_ADDR_SIZE).into())
} else if data.len() > BASE_ADDR_SIZE {
} else if data.len() > BASE_ADDR_SIZE && !ignore_leftover_bytes {
Err(cbor_event::Error::TrailingData.into())
} else {
Ok(AddrType::Base(BaseAddress::new(
Expand All @@ -468,7 +473,7 @@ impl Address {
match Self::decode_pointer(&data[byte_index..]) {
Ok((pointer, offset)) => {
byte_index += offset;
if byte_index < data.len() {
if byte_index < data.len() && !ignore_leftover_bytes {
Err(cbor_event::Error::TrailingData.into())
} else {
Ok(AddrType::Ptr(PointerAddress::new(
Expand All @@ -488,7 +493,7 @@ impl Address {
if data.len() < ENTERPRISE_ADDR_SIZE {
Err(cbor_event::Error::NotEnough(data.len(), ENTERPRISE_ADDR_SIZE).into())
} else {
if data.len() > ENTERPRISE_ADDR_SIZE {
if data.len() > ENTERPRISE_ADDR_SIZE && !ignore_leftover_bytes {
Err(cbor_event::Error::TrailingData.into())
} else {
Ok(AddrType::Enterprise(EnterpriseAddress::new(
Expand All @@ -504,7 +509,7 @@ impl Address {
if data.len() < REWARD_ADDR_SIZE {
Err(cbor_event::Error::NotEnough(data.len(), REWARD_ADDR_SIZE).into())
} else {
if data.len() > REWARD_ADDR_SIZE {
if data.len() > REWARD_ADDR_SIZE && !ignore_leftover_bytes{
Err(cbor_event::Error::TrailingData.into())
} else {
Ok(AddrType::Reward(RewardAddress::new(
Expand Down Expand Up @@ -590,7 +595,8 @@ impl Address {
pub fn from_bech32(bech_str: &str) -> Result<Address, JsError> {
let (_hrp, u5data) =
bech32::decode(bech_str).map_err(|e| JsError::from_str(&e.to_string()))?;
let data: Vec<u8> = bech32::FromBase32::from_base32(&u5data).unwrap();
let data: Vec<u8> = bech32::FromBase32::from_base32(&u5data)
.map_err(|_| JsError::from_str("Can't decode data from base32"))?;
Ok(Self::from_bytes_impl_safe(data.as_ref())?)
}

Expand Down
20 changes: 19 additions & 1 deletion rust/src/tests/serialization/general.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Address, BigInt, BigNum, Block, BlockHash, CborContainerType, Coin, Credential, DataHash, ExUnits, HeaderBody, HeaderLeaderCertEnum, Int, KESVKey, MIRPot, MIRToStakeCredentials, MoveInstantaneousReward, NativeScript, OperationalCert, PlutusData, PlutusList, PlutusScript, PlutusScripts, ProtocolVersion, Redeemer, RedeemerTag, Redeemers, ScriptHash, ScriptRef, TimelockStart, TransactionBody, TransactionInputs, TransactionOutput, TransactionOutputs, TransactionWitnessSet, VRFCert, VRFVKey, Value, Vkeywitness, Vkeywitnesses, VersionedBlock, BlockEra, to_bytes, BootstrapWitnesses, Credentials, Ed25519KeyHashes, CborSetType, ScriptPubkey, NativeScripts, Language, PlutusDatumSchema};
use crate::{Address, BigInt, BigNum, Block, BlockHash, CborContainerType, Coin, Credential, DataHash, ExUnits, HeaderBody, HeaderLeaderCertEnum, Int, KESVKey, MIRPot, MIRToStakeCredentials, MoveInstantaneousReward, NativeScript, OperationalCert, PlutusData, PlutusList, PlutusScript, PlutusScripts, ProtocolVersion, Redeemer, RedeemerTag, Redeemers, ScriptHash, ScriptRef, TimelockStart, TransactionBody, TransactionInputs, TransactionOutput, TransactionOutputs, TransactionWitnessSet, VRFCert, VRFVKey, Value, Vkeywitness, Vkeywitnesses, VersionedBlock, BlockEra, to_bytes, BootstrapWitnesses, Credentials, Ed25519KeyHashes, CborSetType, ScriptPubkey, NativeScripts, Language, PlutusDatumSchema, AddressKind};
use crate::protocol_types::ScriptRefEnum;
use crate::tests::fakes::{fake_base_address, fake_bootsrap_witness, fake_bytes_32, fake_data_hash, fake_key_hash, fake_signature, fake_tx_input, fake_tx_output, fake_value, fake_value2, fake_vkey, fake_vkey_witness};

Expand Down Expand Up @@ -1133,3 +1133,21 @@ fn native_scripts_roundtrip() {
assert_eq!(bytes, new_native_scripts.to_bytes());
assert_eq!(json, new_native_scripts_json.to_json().unwrap());
}


#[test]
fn block_with_tailed_address() {
let block = VersionedBlock::from_hex("820484828f1a005ea87a1a0256e76458203652c26eb2b4f9d6e0f3153e5d50bc2134d2d0479e3b327131c30eb5a9f0161b5820a3f5766ec2361a7524bca87a127beee3841cdaa5565ab43aa534eb2a986bf7cd582038cadf5a45240c648ac17216f6c605509823459636e76d41f8fdb77a4e0e2795825840a18c71e75e907777f626ee64cbaf937f75c53bb5680a3ebb1ae60a7d08d43e969ea287815569623247d2667a65c429458ff5abe3af29cc6b6c20b93fc8c4c1e15850aeb94bf3ad6245ead2629f77aca72de3fec3c63f7dfeaa3ae93e57fba527091d27a81eab6edeefbd910a82010486aa20d25ecfbd444f8f0e473fe9c8873df8db4c54b97fb84e650c7ca2f2d99d0e420f8258400006fcd138764f970b96af60dc0d76e5ce8646d0a48a141dc7fe180c8245ff2433c308a44687f76e8b822f0c85fe84ed5512ae052d380d0ddb2a1109ef45afb85850e4be0efedb6048a2dd1dd3521072bbf06469f31ff0e04807b2fc3d8fe52162c71a6d757809f0d0c4b3360401690a6626909f8692a43fb927b3031858aac671082a404eb89a4b2c25e9bda05c9a81f20c190989582056b93d983f269f5d661050608852226f81ec9b7ed59c571df18ecf63c012a70f58200009f1a4c36b9e677cc6cf9381e7591fbda7628fa78f079e9d75e0cc30bbdfad0219010658406ef722bc8c6042e448db3602c06c7bf7cc8da8178884fb163d4c99a91435cb0c18a32f9c5088a6e7410c79117d41cae7efdd28c987c7d8865e5d9cffd5db7e0405005901c0a63d2e90c3eee6163eb1922b02ba5ad153cdf6971bb73008774dd85bfb31e38f0ac1de687e625f2457fc8065163508e56a2763cb7461d65e941dbae779ba660008b3270def81ccf9cd4757708d09b99ae46b7e08d9f568bd5edfe1360a42d853b5ac77adae1fa08e9c92ca5529cd35da266ba3b16ed491b9dd7c3b7c4913d8d2d349412bde96ff206dcc27350245d0d57245523cdaf28dfa6d6111203a992f07b02583996e7e456111e667d10c846cbbac055394e68fc0627b71d67fa5e8b268174929df51a34549b3f49eddcb6ac4828f0630f9892c041f2f0fd600cb7b7c2f4e5bebf43a6f1dd481496c11cbbdde5e51287466e400783e1bb45272d8bbf215bcf8f8b192f260259e5ee4f9bd10c11fae550f6e7f9536d2397e84fd2f8523e09f0211aac6748b0014e98f0698cf0201a4536596ad68bd0b80858fcc3d1498c431e58a6995681f94087856aeda6fbeb74af133a813d8e232511b3a6a0ac7aa6dacac90d647896193d03e33bcaac33d8c6a3f37f26d15a4946855395abca7dbf54774ceae4aff77b57cf01eebe1185a80bd2397b1cf36b74c2c13d80088fea6075b7bc57f9679f2a562f8c6f659b542ad7a53005c901fbfc6e8b05313c3fc49d586a40085825820a82d2a425e648e19a5b5b0ff140c96487224261134f8621642f8fa925fc15d50008258202efcf544877a9a8fdec4bda55d09da5c86ba5449dc93c3f9bf50bb3de2a0e2be00825820552461c8bbc83e923a51a22e51ce95b8f63b9ef236a505d4253c87882a48947401825820c0ec4bafea97374742245d8189ad5406215b1853feb6b36ccd60a9b710b05d2e008258202fa050591d0ebf2bd99120a87a5618017be34c4e072c78e299d1745c4cdd124800018182583901f041fb39e7861a3f8acadd002703a9c66333934552c43cc60791636b87e5fb16f24c4d44ca4af68ede0114506b50a4d5ed5ac7075fa65ff01b0000000141ea48c5021a00030655031a0256f4a7a40083825820bf4f8f6287993e17f785c949ef287416ba784198bb0ee12b2c7720cbffecd26f0082582052cf971bee4ba1b4e3b32b762e0bc3f7af83700bc8897eddbd77bd425dd28e8300825820bf4f8f6287993e17f785c949ef287416ba784198bb0ee12b2c7720cbffecd26f01018282584e015bad085057ac10ecc7060f7ac41edd6f63068d8963ef7d86ca58669e5ecf2d283418a60be5a848a2380eb721000da1e0bbf39733134beca4cb57afb0b35fc89c63061c9914e055001a518c75161a035377d082583901f4e9e89cc628b9204fd6e2f4ae3e875aa0591fc2eb45b721520d2d22f4e9e89cc628b9204fd6e2f4ae3e875aa0591fc2eb45b721520d2d221a2c651d70021a00031570031a05f5e100a30081825820a64d649dc94133d09592b181a24ec21da6d15f767e9be9928279140fd3f9490b01018282581d715bb7f3c67cb9bece3ae0f654ec5eed2f8c0b16ecd18e66157392eb3c821a004ac4a0a1581cc4c00fbd8fa227442a5e7cdecde33b24588494d05a2c50fda8938c6da1444b49445a1903e8825839019774bdabcb27968087f3cb1f585910a37a44f64454f405bf819ffb7c82caad1b8c97f4af160089e7aeca2609d14841fe6d16040903a2d80f821b000000010d8d5127a1581cc4c00fbd8fa227442a5e7cdecde33b24588494d05a2c50fda8938c6da1444b49445a1a0015d380021a0002ebe5a400818258200462df66b903760f0f80b9f7de5cf4548b8cd7095aff742a11ed9ef036068f1101018282583901dfd12dbfed73e63d9cb564a4781c639746b361940a297a81faba1cebc8ce72bd544c384036cda8506b13f325bc47042350d8c1db8ff38414821a00160a5ba1581c0c78f619e54a5d00e143f66181a2c500d0c394b38a10e86cd1a23c5fa144414441581903498258390195220a921ca980c2746ca7d77ac08a403ab55f6b58359df85f4a6c8e716f950c914afbd8b1a95e2ad35fba5b84e500d4eae8858152717ff5821b00000005cc0af534a1581c0c78f619e54a5d00e143f66181a2c500d0c394b38a10e86cd1a23c5fa144414441581a000386a4021a0002a389031a0257037aa4008182582010d5dd2a979f4df80bf54519b9b58799acd4284816f505b69af6360261e0a1c1010182825839017cfa936d1b5c11e82ae583bef5e4c42ace5eb5f7ec22b722b319f6932b7c6d18b5244f7f6b0cebdbfc5e2276d7face6b7d66866363809ccf821a00160a5ba1581c0c78f619e54a5d00e143f66181a2c500d0c394b38a10e86cd1a23c5fa1444144415818fd825839011d4cb69837e8f69a7f51e52cd75bbc1ff5e27b253c80ed813b5145a8716f950c914afbd8b1a95e2ad35fba5b84e500d4eae8858152717ff5821b00000005c2c91266a1581c0c78f619e54a5d00e143f66181a2c500d0c394b38a10e86cd1a23c5fa144414441581a0005fbf9021a0002a389031a0257037da40082825820346b1a30114897826965e63f25a47f5433554902d4db2d9da1474559a88176610082582007753d4a0e766a20b422c082ebf6401e3954710f8294150a7ce5211a1a028d95000182825839012aca698658d00c7977add88b5901038ba2a0fc46ff72a703b371cef6ad12fb6fe4753f1f0e36bd5bcf5f9fa07662e9d90fa22644040ee1f81a041e6a4882581d610237be10f5ec0ccb6cbd226b112f0940fed44ae0466d9b53962ba8b11a0aa48c09021a0002bf35031a0256f56486a10081825820f012931764da749606b8e2c121c5760d83e525760c094940e5a6bc9b0c425e0158403919ec9db365deea8eea6beeb3b935b6b0a92e977138a8195ef78a039b45d9fe5b05def9ac44c519ac5b9eee1cc36feef8d8d1329c422084dafea1507d99b302a10083825820ec791e29bdb3157589872d3678db93f661d72ac18204759fa5e8d630eee1e66a58408944366b1015ee38809356ec48a59277701d40772a232c7cbb1e9510f2c632537d16318ba30a06953401dc2bfd693a34dc73d482050e6ccdea5811d0e1e32507825820ec791e29bdb3157589872d3678db93f661d72ac18204759fa5e8d630eee1e66a58408944366b1015ee38809356ec48a59277701d40772a232c7cbb1e9510f2c632537d16318ba30a06953401dc2bfd693a34dc73d482050e6ccdea5811d0e1e32507825820ec791e29bdb3157589872d3678db93f661d72ac18204759fa5e8d630eee1e66a58408944366b1015ee38809356ec48a59277701d40772a232c7cbb1e9510f2c632537d16318ba30a06953401dc2bfd693a34dc73d482050e6ccdea5811d0e1e32507a1008182582050ab9c8ef8e58016a1e2bb82f4ece98ac0f32c68b2961d4d5200051637d457925840cdeaa9f41affa62ca83a6a0b4afe8054429571ea8df8faa7bbeb477cc86383ab850d9394068d597ad334adb860e5a6f3b6f518e0407881fb45dbe48161f8140fa100818258206078302fb4ef95ddfd2c6c3c656f025b28450a1ad6ac037b1b01de57db39ad24584083d7d22ea38e5fbf7a652fafac2aadc94a6701a9074466102bb8d729c4317426c0fdb21b455ed4edc16517aab04edf6bba4d453eabb58c92d26cfe819adf7007a100818258204754554aa8bb2ef5882e97d414f3c51fc171c176a59877d41870b618a148494b5840302cc0334840df0194982203065073235e087251c8a86f2f55b76b9f0200e46103ff10f2394dbb17dce48167a8a29d8d18ba2108c846141da10eca1c1e978e05a100828258207a5661cc58574f74d18f0a0c808a8d6e0220a2933bd564fc224b09bd68c76cd7584030b51308602fc3561595f972c292b2f65eabeaf2c7f9079522f507daeb1945eaed072177ca1c90daa92b6e6199d7076258e0379986efc14c10614c30394d460d825820cce7051c1837ac80500d2226fde41ea0c5c2ccc4a6aeb5511019912b25e8c0755840d7689178e5df3fc162a9803824fe03ed1833927332a63e4a9bffb445f0b2a9d001ca21fed9d961624261684b764b1283130036b4c9ff178a47edf98af9e5420fa0").unwrap();
let bodies = block.block().transaction_bodies;
let exact_tx_body = bodies.0.iter().find(
|tx_body| tx_body.fee.0 == 202096u64
).unwrap();
let exact_output = exact_tx_body.outputs.get(0);
let address = exact_output.address;
let address_kind = address.kind();
assert_eq!(address_kind, AddressKind::Base);

let bech32 = address.to_bech32(None).unwrap();
let expected_bech32 = "addr1q9d66zzs27kppmx8qc8h43q7m4hkxp5d39377lvxefvxd8j7eukjsdqc5c97t2zg5guqadepqqx6rc9m7wtnxy6tajjq6r54x9";
assert_eq!(bech32, expected_bech32);
}

0 comments on commit 1d82859

Please sign in to comment.