Skip to content
This repository has been archived by the owner on Sep 18, 2020. It is now read-only.

fix #112 - users were able to burn value during withdraw #114

Merged
merged 70 commits into from
Feb 15, 2018
Merged

Conversation

snd
Copy link
Contributor

@snd snd commented Jan 31, 2018

fixes #112 through solution laid out in issue

fix broke a lot of existing tests.
tests used a lot of raw data hex strings (#79) which were hard to adapt to changes.
therefore includes a pretty big refactoring to make it easy to adapt tests to changes like this.

still todo after opening PR:

  • adapt to newest ethabi
  • refactor tests in tests so they can be modified easier
    • required to sensibly modify tests to make them pass
    • will speed up future modifications to the bridge
  • extract message related code into module and struct
  • test message module
  • extract signature related code into module and struct
  • test signature module
  • make all existing tests pass
    • truffle
    • integration
    • rust
  • get back to 100% test coverage
  • write new tests to cover new behaviour
  • thoroughly test new Helpers.verifySignatures
  • test for Message.getHomeGasPrice
  • move message size into constant so it can be changed in a single location
  • remove code for ignoring low value relays that is no longer needed (rust and contract)
  • prevent truffle tests for promises that should fail from passing on other errors further up in the promise chain
  • update readme
  • docstrings for all new functions and types

@snd snd changed the title fix #112 - users could burn value during withdraw fix #112 - users were able to burn value during withdraw Jan 31, 2018

/// an ECDSA signature consisting of `v`, `r` and `s`
#[derive(PartialEq, Debug)]
pub struct Signature {
Copy link
Contributor

Choose a reason for hiding this comment

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

hah... something like this should be migrated to ethereum-types at some point ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

definitely : )

let payload: Bytes = app.home_bridge.functions().withdraw().input(
signatures.iter().map(|x| Signature::v_from_bytes(x.0.as_slice())),
signatures.iter().map(|x| Signature::r_from_bytes(x.0.as_slice()).0),
signatures.iter().map(|x| Signature::s_from_bytes(x.0.as_slice()).0),
Copy link
Contributor

Choose a reason for hiding this comment

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

v_from_bytes, r_from_bytes and s_from_bytes panic if bytes is not long enough

Copy link
Contributor

Choose a reason for hiding this comment

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

also, you don't have to call as_slice. & is shorter and commonly used ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i'll take more care here to handle the possible failures and either use Result or a panic with a proof (q.e.d.) it can't happen!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

as_slice(x) == &x[..], as_slice(x) != &x, right?

https://doc.rust-lang.org/std/vec/struct.Vec.html#method.as_slice

usually i prefer &x[..] as well.
i prefer .as_slice(x) when it's part of a . chain since it's only requires a suffix and not also a prefix.

signatures.iter().map(|x| Signature::s_from_bytes(x.0.as_slice()).0),
message.clone().0).into();
let request = TransactionRequest {
from: app.config.home.account.clone(),
Copy link
Contributor

Choose a reason for hiding this comment

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

you don't need to clone() it. It implements Copy

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

pub fn from_bytes(bytes: &[u8]) -> Self {
assert_eq!(bytes.len(), MESSAGE_LENGTH);

Self {
Copy link
Contributor

Choose a reason for hiding this comment

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

lol, this is new. It wasn't available in old versions of rust :)

let hash = web3_log.transaction_hash
.ok_or(Error::from_kind(ErrorKind::Msg("`log` must be mined and contain `transaction_hash`".into())))?;
Ok(Self {
recipient: withdraw_log.recipient.0.into(),
Copy link
Contributor

Choose a reason for hiding this comment

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

this conversion is probably redundant since web3 and ethabi are using the same version of ethereum-types crate

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

recipient: withdraw_log.recipient.0.into(),
value: U256(withdraw_log.value.0),
sidenet_transaction_hash: hash.to_vec().as_slice().into(),
mainnet_gas_price: U256(withdraw_log.home_gas_price.0),
Copy link
Contributor

Choose a reason for hiding this comment

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

all other coversion here as well

mainnet_gas_price_raw: u64
) -> TestResult {
if recipient_raw.len() != 20 || sidenet_transaction_hash_raw.len() != 32 {
return TestResult::discard();
Copy link
Contributor

Choose a reason for hiding this comment

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

is it possible to specify the length of these vectors? If not, the chance that both of them will have desired len is pretty low...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's possible but more work. one would have to create a new type that implements quickchecks Arbitrary and only generates vectors of a specific size.

quickcheck by default will fail if it doesn't generate at least 100 tests.
so we're guaranteed that it generates at least 100 vectors of the desired lengths.

this is not perfect but good enough in my opinion.

N.B. Since discarding a test means it neither passes nor fails, quickcheck will try to replace the discarded test with a fresh one. However, if your condition is seldom met, it's possible that quickcheck will have to settle for running fewer tests than usual. By default, if quickcheck can't find 100 valid tests after trying 10,000 times, then it will give up.

https://github.com/BurntSushi/quickcheck#discarding-test-results-or-properties-are-polymorphic

Copy link
Contributor

Choose a reason for hiding this comment

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

interesting. let's leave it as it is for now :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

now i remember the full context. this is just a workaround. the best solution would be to implement quickcheck::Arbitrary for the types in ethereum-types! ethereum-types could benefit from some quickchecking itself as well. i'll give it a shot next week.

Copy link
Contributor

Choose a reason for hiding this comment

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

yes. and let's hide it behind feature gate :)

Copy link
Contributor

Choose a reason for hiding this comment

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


quickcheck! {
fn quickcheck_signature_roundtrips(v: u8, r_raw: Vec<u8>, s_raw: Vec<u8>) -> TestResult {
if r_raw.len() != 32 || s_raw.len() != 32 {
Copy link
Contributor

Choose a reason for hiding this comment

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

same question. How are these lengths defined?

"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 👍 👍

@@ -41,10 +41,10 @@ impl MessageToMainnet {
let hash = web3_log.transaction_hash
.ok_or(Error::from_kind(ErrorKind::Msg("`log` must be mined and contain `transaction_hash`".into())))?;
Copy link
Contributor

Choose a reason for hiding this comment

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

this could be also shortened to:

let hash = web3_log.transaction_hash.ok_or_else(|| "`log` must be mined and contain `transaction_hash`")?;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@snd
Copy link
Contributor Author

snd commented Feb 15, 2018

@debris all grumbles addressed

Cargo.lock Outdated
@@ -1026,6 +1051,7 @@ dependencies = [
"checksum docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d8acd393692c503b168471874953a2531df0e9ab77d0b6bbc582395743300a4a"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
Copy link
Contributor

Choose a reason for hiding this comment

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

one more thing. Can you bump env_logger in cli/Cargo.toml:17:env_logger = "0.3" to 0.4 ? :)

Copy link
Contributor

Choose a reason for hiding this comment

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

so the library is not duplicated :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@snd snd merged commit 3961987 into master Feb 15, 2018
@snd snd deleted the snd-issue-112 branch February 15, 2018 13:48
noot pushed a commit to noot/poa-bridge that referenced this pull request Jul 18, 2018
noot pushed a commit to noot/poa-bridge that referenced this pull request Jul 18, 2018
noot pushed a commit to noot/poa-bridge that referenced this pull request Jul 18, 2018
noot pushed a commit to noot/poa-bridge that referenced this pull request Jul 18, 2018
noot pushed a commit to noot/poa-bridge that referenced this pull request Jul 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

withdraws can be burned by relayers through setting very high gas price
2 participants