Skip to content

Commit

Permalink
Merge pull request #22 from statechannels/jie.pushmessage
Browse files Browse the repository at this point in the history
Validate peer_update in rust code
  • Loading branch information
isoosiss7 authored May 8, 2021
2 parents 2b3e4dd + 5895ac5 commit f194526
Show file tree
Hide file tree
Showing 13 changed files with 410 additions and 69 deletions.
2 changes: 1 addition & 1 deletion packages/native-utils/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ authors = ["Jannis Pohlmann <[email protected]>"]
ethereum-types = "0.9"
ethabi = { git = "https://github.com/graphprotocol/ethabi", rev = "fe7cab5" }
hex = "0.4"
libsecp256k1 = "0.3"
libsecp256k1 = "0.3.5"
serde_derive = "1.0"
serde = "1.0"
tiny-keccak = "2.0"
47 changes: 47 additions & 0 deletions packages/native-utils/common/src/channel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use ethabi::{encode, Token};
use ethereum_types::{Address};
use serde_derive::*;

use super::tokenize::*;
use super::types::*;
use super::utils::*;
use super::state::*;

//#[derive(Deserialize)]
//#[serde(rename_all = "camelCase")]
pub struct SignedStateVarsWithHash {
pub turn_num: Uint48,
pub is_final: bool,
pub outcome: Outcome,
pub app_data: Bytes,
pub hash: Bytes32,
pub signature: RecoverableSignature
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Channel {
pub chain_id: Uint256,
pub channel_nonce: Uint256,
pub participants: Vec<Address>,
}

impl Channel {
pub fn id(&self) -> Bytes32 {
keccak256(
encode(&[
self.chain_id.tokenize(),
Token::Array(
self.participants
.iter()
.cloned()
.map(Token::Address)
.collect(),
),
self.channel_nonce.tokenize(),
])
.as_slice(),
)
.into()
}
}
2 changes: 2 additions & 0 deletions packages/native-utils/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod encode;
mod serde;
mod state;
mod channel;
mod tokenize;
mod types;
mod utils;
Expand All @@ -11,4 +12,5 @@ pub mod prelude {
pub use super::tokenize::Tokenize;
pub use super::types::*;
pub use super::utils::*;
pub use super::channel::*;
}
49 changes: 32 additions & 17 deletions packages/native-utils/common/src/serde.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
use hex;
use secp256k1::{RecoveryId, Signature};
use serde::ser::*;
use serde::ser::{Serialize, Serializer};
use serde::de::{Error, Deserialize, Deserializer};

pub fn serialize_signature<S>(
(signature, recovery_id): &(Signature, RecoveryId),
serializer: S,
) -> Result<S::Ok, S::Error>
where
use super::state::RecoverableSignature;
use super::types::*;

impl Serialize for RecoverableSignature {
fn serialize<S>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// A helper struct to go from `[u8; 64]` to `&[u8]` so that `hex::encode`
// accepts it.
struct RawBytes64([u8; 64]);
{
// A helper struct to go from `[u8; 64]` to `&[u8]` so that `hex::encode`
// accepts it.
struct RawBytes64([u8; 64]);

impl AsRef<[u8]> for RawBytes64 {
fn as_ref(&self) -> &[u8] {
&self.0
impl AsRef<[u8]> for RawBytes64 {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

let bytes = self.0.serialize();
let s = hex::encode(RawBytes64(bytes));
serializer.serialize_str(format!("0x{}{:x}", s, self.1.serialize() + 27).as_str())
}
}

let bytes = signature.serialize();
let s = hex::encode(RawBytes64(bytes));
serializer.serialize_str(format!("0x{}{:x}", s, recovery_id.serialize() + 27).as_str())
impl<'de> Deserialize<'de> for RecoverableSignature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let bytes: Bytes = Deserialize::deserialize(deserializer)?;
Ok(RecoverableSignature::from_bytes(bytes).map_err(D::Error::custom)?)
}
}
138 changes: 99 additions & 39 deletions packages/native-utils/common/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ use secp256k1::{recover, sign, Message, RecoveryId, SecretKey, Signature};
use serde_derive::*;

use super::encode::*;
use super::serde::*;
use super::tokenize::*;
use super::types::*;
use super::utils::*;
use super::channel::*;

#[derive(Deserialize)]
#[derive(Deserialize,PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AllocationItem {
pub destination: Bytes32,
Expand Down Expand Up @@ -39,7 +39,7 @@ impl Tokenize for AssetOutcomeType {
}
}

#[derive(Deserialize)]
#[derive(Deserialize,PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AllocationAssetOutcome {
pub asset_holder_address: Address,
Expand All @@ -55,7 +55,7 @@ impl Tokenize for AllocationAssetOutcome {
}
}

#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Guarantee {
pub target_channel_id: Bytes32,
Expand All @@ -71,7 +71,7 @@ impl Tokenize for Guarantee {
}
}

#[derive(Deserialize)]
#[derive(Deserialize,PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct GuaranteeAssetOutcome {
pub asset_holder_address: Address,
Expand All @@ -87,7 +87,7 @@ impl Tokenize for GuaranteeAssetOutcome {
}
}

#[derive(Deserialize)]
#[derive(Deserialize, PartialEq)]
#[serde(untagged)]
pub enum AssetOutcome {
AllocationAssetOutcome(AllocationAssetOutcome),
Expand All @@ -107,7 +107,7 @@ impl Tokenize for AssetOutcome {
}
}

#[derive(Deserialize)]
#[derive(Deserialize, PartialEq)]
#[serde(transparent)]
pub struct Outcome(Vec<AssetOutcome>);

Expand All @@ -123,34 +123,6 @@ impl Tokenize for Outcome {
}
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Channel {
pub chain_id: Uint256,
pub channel_nonce: Uint256,
pub participants: Vec<Address>,
}

impl Channel {
pub fn id(&self) -> Bytes32 {
keccak256(
encode(&[
self.chain_id.tokenize(),
Token::Array(
self.participants
.iter()
.cloned()
.map(Token::Address)
.collect(),
),
self.channel_nonce.tokenize(),
])
.as_slice(),
)
.into()
}
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct State {
Expand All @@ -163,6 +135,12 @@ pub struct State {
pub app_data: Bytes,
}

#[derive(Serialize)]
pub enum Status {
True,
NeedToCheckApp,
}

impl State {
pub fn hash_app_part(&self) -> Bytes32 {
keccak256(
Expand Down Expand Up @@ -202,11 +180,11 @@ impl State {

Ok(StateSignature {
hash,
signature: (signature, recovery_id),
signature: RecoverableSignature(signature, recovery_id),
})
}

pub fn recover_address(self, signature: Bytes) -> Result<String, &'static str> {
pub fn recover_address(&self, signature: Bytes) -> Result<String, &'static str> {
let hash = self.hash();
let hashed_message = hash_message(&hash);
let message = Message::parse(&hashed_message);
Expand All @@ -219,12 +197,94 @@ impl State {

Ok(checksum_address(public_key_to_address(public_key)))
}

pub fn validate_peer_update(&self, peer_update: State, peer_signature: Bytes) -> Result<Status, &'static str> {
peer_update.validate_signature(peer_signature)?;
self.require_valid_protocol_transition(peer_update)
}

fn validate_signature(&self, signature: Bytes) -> Result<(), &'static str> {
let signer_index = ((self.turn_num.0 -1) % self.channel.participants.len() as u64) as usize;
let signer_address = self.channel.participants[signer_index];
let recovered_address = self.recover_address(signature)?;

if recovered_address.eq(&checksum_address(signer_address.0.to_vec())) {
Ok(())
} else {
Err("Signature verification failed")
}
}

fn _require_extra_implicit_checks(&self, to_state: &State) -> Result<(), &'static str> {
if &self.turn_num.0 + 1 != to_state.turn_num.0 {
Err("turnNum must increment by one")
} else if self.channel.chain_id != to_state.channel.chain_id {
Err("chainId must not change")
} else if self.channel.channel_nonce != to_state.channel.channel_nonce {
Err("channelNonce must not change")
} else if self.app_definition != to_state.app_definition {
Err("appDefinition must not change")
} else if self.challenge_duration != to_state.challenge_duration {
Err("challengeDuration must not change")
} else {
Ok(())
}
}

pub fn require_valid_protocol_transition(&self, to_state: State) -> Result<Status, &'static str> {
self._require_extra_implicit_checks(&to_state)?;

if to_state.is_final {
if self.outcome != to_state.outcome {
Err("Outcome change forbidden")
} else {
Ok(Status::True)
}
} else {
if self.is_final {
Err("transition from a final state to a non-final state")
} else {
if to_state.turn_num < Uint48(2 * to_state.channel.participants.len() as u64) {
if self.outcome != to_state.outcome {
Err("Outcome change forbidden")
} else if self.app_data != to_state.app_data {
Err("appData change forbidden")
}
else {
Ok(Status::True)
}
}
else {
Ok(Status::NeedToCheckApp)
}
}
}
}
}

pub struct RecoverableSignature(pub Signature, pub RecoveryId);

impl RecoverableSignature {
pub fn as_bytes(self) -> Bytes {
let mut v = self.0.serialize().to_vec();
v.push(self.1.serialize());
Bytes(v)
}

pub fn from_bytes(bytes: Bytes) -> Result<RecoverableSignature, &'static str> {
assert_eq!(65,bytes.0.len());
let mut a: [u8; 64] = [0; 64];
a.copy_from_slice(&bytes.0[0..64]);
Ok(RecoverableSignature(
Signature::parse(&a),
RecoveryId::parse(bytes.0[64] - 27).map_err(|_| "Invalid recovery ID")?
))
}
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StateSignature {
hash: Bytes32,
#[serde(serialize_with = "serialize_signature")]
signature: (Signature, RecoveryId),
signature: RecoverableSignature,
}
4 changes: 4 additions & 0 deletions packages/native-utils/common/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ impl ToHexString for Vec<u8> {
}
}

#[derive(PartialEq)]
pub struct Bytes(pub Vec<u8>);

impl Deref for Bytes {
Expand Down Expand Up @@ -55,6 +56,7 @@ impl Tokenize for Bytes {
}
}

#[derive(PartialEq)]
pub struct Bytes32(Vec<u8>);

impl From<[u8; 32]> for Bytes32 {
Expand Down Expand Up @@ -107,6 +109,7 @@ impl Tokenize for Bytes32 {
}
}

#[derive(PartialEq, PartialOrd)]
pub struct Uint48(pub u64);

impl<'de> Deserialize<'de> for Uint48 {
Expand All @@ -124,6 +127,7 @@ impl Tokenize for Uint48 {
}
}

#[derive(PartialEq)]
pub struct Uint256(pub U256);

impl From<U256> for Uint256 {
Expand Down
Loading

0 comments on commit f194526

Please sign in to comment.