Skip to content

Commit

Permalink
Peer state validate and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
isoosiss7 committed Apr 25, 2021
1 parent 33f8af0 commit d4a8141
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 13 deletions.
25 changes: 21 additions & 4 deletions packages/native-utils/common/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::ops::Deref;

use ethabi::{encode, Token};
use ethereum_types::{Address, U256};
use secp256k1::{recover, sign, verify, Message, RecoveryId, SecretKey, Signature};
use secp256k1::{recover, sign, Message, RecoveryId, SecretKey, Signature};
use serde_derive::*;

use super::encode::*;
Expand Down Expand Up @@ -135,6 +135,7 @@ pub struct State {
pub app_data: Bytes,
}

#[derive(Serialize)]
pub enum Status {
True,
NeedToCheckApp,
Expand Down Expand Up @@ -183,7 +184,7 @@ impl State {
})
}

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 @@ -197,6 +198,23 @@ 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")
Expand All @@ -213,7 +231,7 @@ impl State {
}
}

pub fn require_valid_protocol_transition(self, to_state: State) -> Result<Status, &'static str> {
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 {
Expand Down Expand Up @@ -284,6 +302,5 @@ impl RecoverableSignature {
#[serde(rename_all = "camelCase")]
pub struct StateSignature {
hash: Bytes32,
//#[serde(serialize_with = "serialize_signature")]
signature: RecoverableSignature,
}
9 changes: 9 additions & 0 deletions packages/native-utils/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,12 @@ export function recoverAddress(state: State, signature: string): string
* @param signature A signature resulting from a previous call to `signState`.
*/
export function verifySignature(hash: Bytes32, address: string, signature: string): boolean

/**
* Validate peer update.
*
* @param state A Nitro state.
* @param peer_update Next state suggested by peer
* @param signature Peer's signature for next state.
*/
export function validatePeerUpdate(state, peer_update, signature): string
3 changes: 3 additions & 0 deletions packages/native-utils/lib/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const {
signState,
recoverAddress,
verifySignature,
validatePeerUpdate,
} = require('../native/index.node')

function unwrapResult({ Ok, Err }) {
Expand Down Expand Up @@ -43,4 +44,6 @@ module.exports = {
recoverAddress: (state, signature) => unwrapResult(recoverAddress(state, signature)),

verifySignature: (hash, address, signature) => unwrapResult(verifySignature(hash, address, signature)),

validatePeerUpdate: (state, peer_update, signature) => unwrapResult(validatePeerUpdate(state, peer_update, signature)),
}
4 changes: 4 additions & 0 deletions packages/native-utils/native/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@ export! {
fn verifySignature(hash: Bytes32, address: String, signature: Bytes) -> Result<bool, &'static str> {
verify_sig(hash, address, signature)
}

fn validatePeerUpdate(state: State, peer_update: State, peer_signature: Bytes) -> Result<Status, &'static str> {
state.validate_peer_update(peer_update, peer_signature)
}
}
64 changes: 55 additions & 9 deletions packages/native-utils/tests/sign.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ const DEFAULT_STATE: State = {
channel: {
chainId: '1',
channelNonce: 1,
participants: ['0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377'],
participants: ['0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377', '0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1'],
},
challengeDuration: 1,
outcome: [],
appDefinition: '0x0000000000000000000000000000000000000000',
appData: '0x0000000000000000000000000000000000000000000000000000000000000000',
}

const PRIVATE_KEY = '0x8da4ef21b864d2cc526dbdb2a120bd2874c36c9d0a1fb7f8c63d7f7a8b41de8f'
const PRIVATE_KEY1 = '0x8da4ef21b864d2cc526dbdb2a120bd2874c36c9d0a1fb7f8c63d7f7a8b41de8f'
const PRIVATE_KEY2 = '0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d'

describe('Hash message', () => {
test('Hash a message', async () => {
Expand Down Expand Up @@ -54,14 +55,14 @@ describe('Sign state', () => {
outcome,
}
const oldSignature = utils.joinSignature(
(await nitro.signState(state, PRIVATE_KEY)).signature,
(await nitro.signState(state, PRIVATE_KEY1)).signature,
)

// Native
const nativeSignature = native.signState(state, PRIVATE_KEY).signature
const nativeSignature = native.signState(state, PRIVATE_KEY1).signature

// WASM
const wasmSignature = wasm.signState(state, PRIVATE_KEY).signature
const wasmSignature = wasm.signState(state, PRIVATE_KEY1).signature

expect(nativeSignature).toStrictEqual(oldSignature)
expect(wasmSignature).toStrictEqual(oldSignature)
Expand All @@ -88,14 +89,14 @@ describe('Sign state', () => {
}

const oldSignature = utils.joinSignature(
(await nitro.signState(state, PRIVATE_KEY)).signature,
(await nitro.signState(state, PRIVATE_KEY1)).signature,
)

// Native
const nativeSigned = native.signState(state, PRIVATE_KEY)
const nativeSigned = native.signState(state, PRIVATE_KEY1)

// WASM
const wasmSigned = wasm.signState(state, PRIVATE_KEY)
const wasmSigned = wasm.signState(state, PRIVATE_KEY1)

expect(nativeSigned.signature).toStrictEqual(oldSignature)
expect(wasmSigned.signature).toStrictEqual(oldSignature)
Expand All @@ -108,12 +109,57 @@ describe('Sign state', () => {
// Old state cannot be verified by new signature
// New state cannto be vierified by old signature
state.turnNum += 1
const signedNewState = native.signState(state, PRIVATE_KEY)
const signedNewState = native.signState(state, PRIVATE_KEY1)
expect(native.verifySignature(nativeSigned.hash, state.channel.participants[0], signedNewState.signature)).toBe(false)
expect(native.verifySignature(signedNewState.hash, state.channel.participants[0], nativeSigned.signature)).toBe(false)
expect(native.verifySignature(signedNewState.hash, state.channel.participants[0], signedNewState.signature)).toBe(true)
})

test('Peer states validate as expected', async () => {
const outcome = [
{
assetHolderAddress: '0x0000000000000000000000000000000000000000',
guarantee: {
targetChannelId:
'0x0000000000000000000000000000000000000000000000000000000000000000',
destinations: [
'0x0000000000000000000000000000000000000000000000000000000000000000',
'0x1111111111111111111111111111111111111111111111111111111111111111',
],
},
},
]

const currentState = {
...DEFAULT_STATE,
outcome,
}

const peerState = {
...DEFAULT_STATE,
outcome,
}

// First 2 updates
currentState.turnNum = 1;
peerState.turnNum = 2;

const nativeSigned1 = native.signState(peerState, PRIVATE_KEY2);
expect(native.validatePeerUpdate(currentState, peerState, nativeSigned1.signature)).toEqual("True")

// Additional updates
currentState.turnNum = 3;
peerState.turnNum = 4;

const nativeSigned2 = native.signState(peerState, PRIVATE_KEY2);
expect(native.validatePeerUpdate(currentState, peerState, nativeSigned2.signature)).toEqual("NeedToCheckApp")

// Singed by wrong signature
const nativeSigned3 = native.signState(peerState, PRIVATE_KEY1);
expect(() => native.validatePeerUpdate(currentState, peerState, nativeSigned3.signature)).toThrow('Signature verification failed');
})


test('Catches invalid private key', async () => {
// Invalid signature length
expect(() => native.signState(DEFAULT_STATE, '0x00')).toThrow('invalid private key')
Expand Down

0 comments on commit d4a8141

Please sign in to comment.