Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

fix: off by one error during log topic decoding #296

Merged
merged 1 commit into from
May 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ethers-contract/ethers-contract-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ fn derive_decode_from_log_impl(
let flat_topics = topics.iter().skip(1).flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
},
quote! {
if topic_tokens.is_empty() || topic_tokens.len() != topics.len() - 1 {
if topic_tokens.len() != topics.len() - 1 {
Copy link
Collaborator

@prestwich prestwich May 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't this subtraction underflow if the LOG0 opcode is used? I'm not super familiar with these codepaths 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro generates different code for anonymous events:

let (signature_check, flat_topics_init, topic_tokens_len_check) = if event.anonymous {
(
quote! {},
quote! {
let flat_topics = topics.iter().flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
},
quote! {
if topic_tokens.len() != topics.len() {
return Err(ethers_core::abi::Error::InvalidData);
}
},
)
} else {
(
quote! {
let event_signature = topics.get(0).ok_or(ethers_core::abi::Error::InvalidData)?;
if event_signature != &Self::signature() {
return Err(ethers_core::abi::Error::InvalidData);
}
},
quote! {
let flat_topics = topics.iter().skip(1).flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
},
quote! {
if topic_tokens.len() != topics.len() - 1 {
return Err(ethers_core::abi::Error::InvalidData);
}
},
)
};

only for non anonymous it makes sure that the signature is the first entry in topics

let event_signature = topics.get(0).ok_or(ethers_core::abi::Error::InvalidData)?;

so topics.len() - 1 can't underflow.

Most of this is adapted from:
https://github.com/rust-ethereum/ethabi/blob/970d47018e0f457440851752a7b46a4dbf793a0b/ethabi/src/event.rs#L122-L173

return Err(ethers_core::abi::Error::InvalidData);
}
},
Expand Down
36 changes: 34 additions & 2 deletions ethers-contract/tests/common/derive.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ethers::core::types::{H160, H256, I256, U128, U256};
use ethers_contract::{abigen, EthAbiType, EthEvent};
use ethers_core::abi::Tokenizable;
use ethers_contract::{abigen, EthAbiType, EthEvent, EthLogDecode};
use ethers_core::abi::{RawLog, Tokenizable};
use ethers_core::types::Address;

#[derive(Debug, Clone, PartialEq, EthAbiType)]
Expand Down Expand Up @@ -236,3 +236,35 @@ fn can_generate_ethevent_from_json() {
CreatedFilter::signature()
);
}

#[test]
fn can_decode_event_with_no_topics() {
#[derive(Debug, PartialEq, EthEvent)]
pub struct LiquidateBorrow {
liquidator: Address,
borrower: Address,
repay_amount: U256,
c_token_collateral: Address,
seize_tokens: U256,
}
// https://etherscan.io/tx/0xb7ba825294f757f8b8b6303b2aef542bcaebc9cc0217ddfaf822200a00594ed9#eventlog index 141
let log = RawLog {
topics: vec![
"298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52"
.parse()
.unwrap(),
],
data: vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 205, 0, 29, 173, 151, 238, 5, 127, 91, 31,
197, 154, 221, 40, 175, 143, 32, 26, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 129,
195, 136, 163, 5, 24, 136, 69, 34, 251, 23, 122, 146, 252, 33, 147, 81, 8, 20, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 18, 195, 162, 210,
38, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 220, 45, 25, 57, 72, 146, 109, 2,
249, 177, 254, 158, 29, 170, 7, 24, 39, 14, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 30, 88,
],
};
let event = <LiquidateBorrow as EthLogDecode>::decode_log(&log).unwrap();
assert_eq!(event.seize_tokens, 5250648u64.into());
assert_eq!(event.repay_amount, 653800000000000000u64.into());
}