Skip to content

Commit

Permalink
Merge pull request #14 from octopus-network/v1.3.0
Browse files Browse the repository at this point in the history
Upgrade to v1.3.0
  • Loading branch information
riversyang authored May 1, 2022
2 parents 3a5337d + af43999 commit e7077e2
Show file tree
Hide file tree
Showing 17 changed files with 547 additions and 91 deletions.
3 changes: 2 additions & 1 deletion appchain-anchor/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "appchain-anchor"
version = "1.2.1"
version = "1.3.0"
authors = ["Octopus Network"]
edition = "2018"

Expand All @@ -14,6 +14,7 @@ near-contract-standards = "3.1.0"
hex = "0.4.2"
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
getrandom = { version = "0.2", features = ["custom"] }
ed25519-dalek = { version = "1.0.1", features = ["alloc"] }

[dev-dependencies]
hex-literal = "0.3.1"
Expand Down
30 changes: 30 additions & 0 deletions appchain-anchor/src/anchor_viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ impl AnchorViewer for AppchainAnchor {
start_index: U64::from(u64::from(appchain_messages.min_nonce())),
end_index: U64::from(u64::from(appchain_messages.max_nonce())),
},
index_range_of_appchain_challenges: self
.appchain_challenges
.get()
.unwrap()
.index_range(),
permissionless_actions_status: self.permissionless_actions_status.get().unwrap(),
asset_transfer_is_paused: self.asset_transfer_is_paused,
rewards_withdrawal_is_paused: self.rewards_withdrawal_is_paused,
Expand Down Expand Up @@ -590,4 +595,29 @@ impl AnchorViewer for AppchainAnchor {
let appchain_messages = self.appchain_messages.get().unwrap();
appchain_messages.get_processing_results(&start_nonce, quantity)
}
//
fn get_appchain_challenge(&self, index: Option<U64>) -> Option<AppchainChallenge> {
let index = match index {
Some(index) => index,
None => {
self.appchain_challenges
.get()
.unwrap()
.index_range()
.end_index
}
};
self.appchain_challenges.get().unwrap().get(&index.0)
}
//
fn get_appchain_challenges(
&self,
start_index: U64,
quantity: Option<U64>,
) -> Vec<AppchainChallenge> {
self.appchain_challenges
.get()
.unwrap()
.get_slice_of(&start_index.0, quantity.map(|q| q.0))
}
}
162 changes: 162 additions & 0 deletions appchain-anchor/src/appchain_challenge/equivocation_challenge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use crate::*;
use codec::{Decode, Encode};
use core::convert::TryFrom;
use ed25519_dalek::Verifier;

pub type RoundNumber = u32;
pub type SetId = u32;
pub type BlockNumber = u32;

#[derive(
BorshDeserialize,
BorshSerialize,
Deserialize,
Serialize,
Clone,
Debug,
Decode,
Encode,
PartialEq,
)]
#[serde(crate = "near_sdk::serde")]
pub struct Hash(pub [u8; 32]);

#[derive(BorshDeserialize, BorshSerialize, Deserialize, Serialize, Clone, Debug)]
#[serde(crate = "near_sdk::serde")]
pub struct PublicKey(pub [u8; 32]);

#[derive(BorshDeserialize, BorshSerialize, Deserialize, Serialize, Clone, Debug)]
#[serde(crate = "near_sdk::serde")]
pub struct SignatureData(pub Vec<u8>);

#[derive(BorshDeserialize, BorshSerialize, Deserialize, Serialize, Clone, Debug)]
#[serde(crate = "near_sdk::serde")]
pub struct GrandpaEquivocation<V, S> {
pub round_number: RoundNumber,
pub identity: PublicKey,
pub first: (V, S),
pub second: (V, S),
}

#[derive(BorshDeserialize, BorshSerialize, Deserialize, Serialize, Clone, Debug)]
#[serde(crate = "near_sdk::serde")]
pub enum Equivocation {
Prevote(GrandpaEquivocation<GrandpaPrevote, SignatureData>),
Precommit(GrandpaEquivocation<GrandpaPrecommit, SignatureData>),
}

#[derive(BorshDeserialize, BorshSerialize, Deserialize, Serialize, Clone, Debug)]
#[serde(crate = "near_sdk::serde")]
pub struct EquivocationProof {
pub set_id: SetId,
pub equivocation: Equivocation,
}

#[derive(
BorshDeserialize, BorshSerialize, Deserialize, Serialize, Clone, Debug, Decode, Encode,
)]
#[serde(crate = "near_sdk::serde")]
pub struct GrandpaPrevote {
pub target_hash: Hash,
pub target_number: BlockNumber,
}

#[derive(
BorshDeserialize, BorshSerialize, Deserialize, Serialize, Clone, Debug, Decode, Encode,
)]
#[serde(crate = "near_sdk::serde")]
pub struct GrandpaPrecommit {
pub target_hash: Hash,
pub target_number: BlockNumber,
}

#[derive(Clone, Debug, Decode, Encode)]
pub enum GrandpaMessage {
Prevote(GrandpaPrevote),
Precommit(GrandpaPrecommit),
}

impl EquivocationProof {
///
pub fn is_valid(&self) -> bool {
// NOTE: the bare `Prevote` and `Precommit` types don't share any trait,
// this is implemented as a macro to avoid duplication.
macro_rules! check {
( $equivocation:expr, $message:expr ) => {
// if both votes have the same target the equivocation is invalid.
if $equivocation.first.0.target_hash == $equivocation.second.0.target_hash
&& $equivocation.first.0.target_number == $equivocation.second.0.target_number
{
log!("Votes in equivocation have same targets.");
return false;
}

// check signatures on both votes are valid
let valid_first = self.check_signature(
&$message($equivocation.first.0.clone()),
&$equivocation.round_number,
&$equivocation.first.1,
&$equivocation.identity,
);

let valid_second = self.check_signature(
&$message($equivocation.second.0.clone()),
&$equivocation.round_number,
&$equivocation.second.1,
&$equivocation.identity,
);

return valid_first && valid_second
};
}

match &self.equivocation {
Equivocation::Prevote(equivocation) => {
check!(equivocation, GrandpaMessage::Prevote);
}
Equivocation::Precommit(equivocation) => {
check!(equivocation, GrandpaMessage::Precommit);
}
}
}
//
fn check_signature(
&self,
message: &GrandpaMessage,
round: &RoundNumber,
signature: &SignatureData,
pubkey: &PublicKey,
) -> bool {
let mut buffer = Vec::<u8>::new();
// Notice:
// Need to convert `round` and `set_id` to u64 to match original
// signing data in appchain side
(message, u64::from(*round), u64::from(self.set_id)).encode_to(&mut buffer);
if signature.0.len() != 64 {
log!("Invalid signature data length.");
return false;
}
let mut sig_data: [u8; 64] = [0; 64];
for i in 0..64 {
sig_data[i] = signature.0.get(i).unwrap_or(&0).clone();
}
if let Ok(signature) = ed25519_dalek::Signature::try_from(sig_data) {
match ed25519_dalek::PublicKey::from_bytes(&pubkey.0) {
Ok(pubkey) => match pubkey.verify(&buffer, &signature) {
Ok(()) => true,
Err(err) => {
log!("Signature verification failed: {}", err);
false
}
},
Err(err) => {
log!("Invalid ed25519 pubkey: {}", err);
false
}
}
} else {
log!("Invalid ed25519 signature data.");
false
}
}
}
18 changes: 18 additions & 0 deletions appchain-anchor/src/appchain_challenge/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
mod equivocation_challenge;

use crate::*;

use self::equivocation_challenge::EquivocationProof;

#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone)]
#[serde(crate = "near_sdk::serde")]
pub enum AppchainChallenge {
EquivocationChallenge {
submitter_account: AccountId,
proof: EquivocationProof,
},
ConspiracyMmr {
submitter_account: AccountId,
block_number: u32,
},
}
33 changes: 18 additions & 15 deletions appchain-anchor/src/assets/near_fungible_tokens.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use near_contract_standards::fungible_token::metadata::FungibleTokenMetadata;
use near_sdk::serde_json;

use crate::{
interfaces::NearFungibleTokenManager,
Expand Down Expand Up @@ -102,12 +101,24 @@ impl NearFungibleTokenManager for AppchainAnchor {
price: U128,
) {
self.assert_owner();
assert!(
ValidAccountId::try_from(contract_account.clone()).is_ok(),
"Invalid account id: {}",
contract_account
);
let mut near_fungible_tokens = self.near_fungible_tokens.get().unwrap();
assert!(
!near_fungible_tokens.contains(&symbol),
"Token '{}' is already registered.",
&symbol
);
assert!(
near_fungible_tokens
.get_by_contract_account(&contract_account)
.is_none(),
"Token contract '{}' is already registered.",
contract_account
);
near_fungible_tokens.insert(&NearFungibleToken {
metadata: FungibleTokenMetadata {
spec: "ft-1.0.0".to_string(),
Expand All @@ -121,7 +132,7 @@ impl NearFungibleTokenManager for AppchainAnchor {
contract_account,
price_in_usd: price,
locked_balance: U128::from(0),
bridging_state: BridgingState::Active,
bridging_state: BridgingState::Closed,
});
self.near_fungible_tokens.set(&near_fungible_tokens);
}
Expand Down Expand Up @@ -206,25 +217,14 @@ impl AppchainAnchor {
predecessor_account_id: AccountId,
sender_id: AccountId,
amount: U128,
msg: String,
deposit_message: DepositMessage,
) -> PromiseOrValue<U128> {
let mut near_fungible_tokens = self.near_fungible_tokens.get().unwrap();
if let Some(mut near_fungible_token) =
near_fungible_tokens.get_by_contract_account(&predecessor_account_id)
{
let deposit_message: NearFungibleTokenDepositMessage =
match serde_json::from_str(msg.as_str()) {
Ok(msg) => msg,
Err(_) => {
log!(
"Invalid msg '{}' attached in `ft_transfer_call`. Return deposit.",
msg
);
return PromiseOrValue::Value(amount);
}
};
match deposit_message {
NearFungibleTokenDepositMessage::BridgeToAppchain {
DepositMessage::BridgeToAppchain {
receiver_id_in_appchain,
} => {
AccountIdInAppchain::new(Some(receiver_id_in_appchain.clone())).assert_valid();
Expand Down Expand Up @@ -273,6 +273,9 @@ impl AppchainAnchor {
);
return PromiseOrValue::Value(0.into());
}
_ => panic!(
"Internal error: misuse of internal function 'internal_process_near_fungible_token_deposit'."
),
}
}
panic!(
Expand Down
15 changes: 15 additions & 0 deletions appchain-anchor/src/interfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,19 @@ pub trait AnchorViewer {
start_nonce: u32,
quantity: Option<u32>,
) -> Vec<AppchainMessageProcessingResult>;
/// Get appchain challenge by index.
/// If the param `index `is omitted, the latest challenge data will be returned.
/// If the paran `index` is smaller than the start index, or bigger than the end index
/// stored in anchor, or there is no challenge data in anchor yet,
/// `Option::None` will be returned.
fn get_appchain_challenge(&self, index: Option<U64>) -> Option<AppchainChallenge>;
/// Get appchain challenge data by start index and quantity.
/// If the param `quantity` is omitted, up to 50 records will be returned.
fn get_appchain_challenges(
&self,
start_index: U64,
quantity: Option<U64>,
) -> Vec<AppchainChallenge>;
}

pub trait AppchainLifecycleManager {
Expand Down Expand Up @@ -216,6 +229,8 @@ pub trait PermissionlessActions {
);
///
fn process_appchain_messages(&mut self) -> MultiTxsOperationProcessingResult;
///
fn commit_appchain_challenge(&mut self, appchain_challenge: AppchainChallenge);
}

pub trait ProtocolSettingsManager {
Expand Down
Loading

0 comments on commit e7077e2

Please sign in to comment.