diff --git a/Cargo.lock b/Cargo.lock index 0ee9bc3ac..e567bab35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -847,19 +847,28 @@ dependencies = [ name = "darwinia-eth-backing" version = "0.2.0" dependencies = [ + "darwinia-balances 0.2.0", "darwinia-eth-relay 0.2.0", + "darwinia-kton 0.2.0", + "darwinia-staking 0.3.0", "darwinia-support 0.2.0", "ethabi 9.0.1 (git+https://github.com/darwinia-network/ethabi.git?branch=with_no_std)", - "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.4.4 (git+https://github.com/darwinia-network/parity-common.git)", + "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sr-eth-primitives 0.2.0", + "sr-io 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "sr-primitives 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", + "sr-staking-primitives 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "sr-std 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", + "srml-authorship 0.1.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", + "srml-session 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "srml-support 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "srml-system 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "srml-timestamp 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", + "substrate-phragmen 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "substrate-primitives 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", ] diff --git a/srml/eth-backing/Cargo.toml b/srml/eth-backing/Cargo.toml index f21775067..c43227e14 100644 --- a/srml/eth-backing/Cargo.toml +++ b/srml/eth-backing/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" [dependencies] # crates.io codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } -hex = { version = "0.4", default-features = false } +#hex = { version = "0.4", default-features = false } serde = { version = "1.0.101", optional = true } # github.com @@ -28,12 +28,25 @@ sr-eth-primitives = { path = "../../core/sr-eth-primitives", default-features = [dev-dependencies] hex-literal = "0.2.1" +rustc-hex = "2.0" + +balances = { package = "darwinia-balances", path = '../balances' } +kton = { package = "darwinia-kton", path = "../kton" } +staking = { package = "darwinia-staking", path = "../staking" } + +authorship = { package = "srml-authorship", git = "https://github.com/darwinia-network/substrate.git", branch = "darwinia-develop"} +session = { package = "srml-session",git = "https://github.com/darwinia-network/substrate.git", branch = "darwinia-develop"} +sr-staking-primitives = { git = "https://github.com/darwinia-network/substrate.git", branch = "darwinia-develop"} +phragmen = { package = "substrate-phragmen", git = "https://github.com/darwinia-network/substrate.git", branch = "darwinia-develop"} +runtime_io = { package = "sr-io", git = "https://github.com/darwinia-network/substrate.git", branch = "darwinia-develop" } +rlp = { package = "rlp", git = "https://github.com/darwinia-network/parity-common.git"} + [features] default = ["std"] std = [ "codec/std", - "hex/std", +# "hex/std", "serde/std", "ethabi/std", diff --git a/srml/eth-backing/src/lib.rs b/srml/eth-backing/src/lib.rs index 392156370..b37d1d85d 100644 --- a/srml/eth-backing/src/lib.rs +++ b/srml/eth-backing/src/lib.rs @@ -7,32 +7,32 @@ //use codec::{Decode, Encode}; use ethabi::{Event as EthEvent, EventParam as EthEventParam, ParamType, RawLog}; use rstd::{borrow::ToOwned, convert::TryFrom, marker::PhantomData, result, vec}; // fmt::Debug -use sr_primitives::traits::{SaturatedConversion, Saturating}; +use sr_primitives::traits::{CheckedSub, SaturatedConversion}; use support::{decl_event, decl_module, decl_storage, ensure, traits::Currency, traits::OnUnbalanced}; // dispatch::Result, use system::ensure_signed; // Convert, -//use sr_primitives::RuntimeDebug; -//use primitives::crypto::UncheckedFrom; -//use core::convert::TryFrom; - use darwinia_eth_relay::{EthReceiptProof, VerifyEthReceipts}; use darwinia_support::{LockableCurrency, OnDepositRedeem}; -use sr_eth_primitives::{EthAddress, H256, U256}; // receipt::LogEntry, receipt::Receipt, - -//#[cfg(feature = "std")] -//use sr_primitives::{Deserialize, Serialize}; +use sr_eth_primitives::{EthAddress, H256, U256}; pub type Balance = u128; pub type Moment = u64; -type RingBalanceOf = <::Ring as Currency<::AccountId>>::Balance; +type Ring = <::Ring as Currency<::AccountId>>::Balance; type PositiveImbalanceRing = <::Ring as Currency<::AccountId>>::PositiveImbalance; //type NegativeImbalanceRing = <::Ring as Currency<::AccountId>>::NegativeImbalance; -type KtonBalanceOf = <::Kton as Currency<::AccountId>>::Balance; +type Kton = <::Kton as Currency<::AccountId>>::Balance; type PositiveImbalanceKton = <::Kton as Currency<::AccountId>>::PositiveImbalance; //type NegativeImbalanceKton = <::Kton as Currency<::AccountId>>::NegativeImbalance; +type EthTransactionIndex = (H256, u64); + +#[cfg(all(feature = "std", test))] +mod mock; +#[cfg(all(feature = "std", test))] +mod tests; + pub trait Trait: timestamp::Trait { type Event: From> + Into<::Event>; type EthRelay: VerifyEthReceipts; @@ -50,12 +50,12 @@ decl_storage! { pub KtonRedeemAddress get(kton_redeem_address) config(): EthAddress; pub DepositRedeemAddress get(deposit_redeem_address) config(): EthAddress; - pub RingLocked get(fn ring_locked) config(): RingBalanceOf; - pub KtonLocked get(fn kton_locked) config(): KtonBalanceOf; + pub RingLocked get(fn ring_locked) config(): Ring; + pub KtonLocked get(fn kton_locked) config(): Kton; - pub RingProofVerified get(ring_proof_verfied): map (H256, u64) => Option; - pub KtonProofVerified get(kton_proof_verfied): map (H256, u64) => Option; - pub DepositProofVerified get(deposit_proof_verfied): map (H256, u64) => Option; + pub RingProofVerified get(ring_proof_verfied): map EthTransactionIndex => Option; + pub KtonProofVerified get(kton_proof_verfied): map EthTransactionIndex => Option; + pub DepositProofVerified get(deposit_proof_verfied): map EthTransactionIndex => Option; } } @@ -64,18 +64,10 @@ decl_event! { where ::AccountId { - TODO(AccountId), - } -} - -impl Module { - pub fn adjust_deposit_value() { - unimplemented!() + RedeemRing(AccountId, Balance, EthTransactionIndex), + RedeemKton(AccountId, Balance, EthTransactionIndex), + RedeemDeposit(AccountId, Balance, EthTransactionIndex), } - - // fn _release(_dest: &T::AccountId, _value: RingBalanceOf) -> Result { - // unimplemented!() - // } } decl_module! { @@ -83,6 +75,8 @@ decl_module! { where origin: T::Origin { + fn deposit_event() = default; + // event RingBurndropTokens(address indexed token, address indexed owner, uint amount, bytes data) // https://ropsten.etherscan.io/tx/0x81f699c93b00ab0b7db701f87b6f6045c1e0692862fcaaf8f06755abb0536800 pub fn redeem_ring(origin, proof_record: EthReceiptProof) { @@ -94,15 +88,21 @@ decl_module! { ); let (darwinia_account, redeemed_amount) = Self::parse_token_redeem_proof(&proof_record, "RingBurndropTokens")?; - let redeemed_ring = >::saturated_from(redeemed_amount); + + let redeemed_ring = >::saturated_from(redeemed_amount); + + let new_ring_locked = Self::ring_locked().checked_sub(&redeemed_ring).ok_or("RING Locked - NO SUFFICIENT BACKING ASSETS")?; let redeemed_positive_imbalance_ring = T::Ring::deposit_into_existing(&darwinia_account, redeemed_ring)?; T::RingReward::on_unbalanced(redeemed_positive_imbalance_ring); - RingProofVerified::insert((proof_record.header_hash, proof_record.index), proof_record); + RingProofVerified::insert((proof_record.header_hash, proof_record.index), &proof_record); + >::mutate(|l| { - *l = l.saturating_sub(redeemed_ring); + *l = new_ring_locked; }); + + >::deposit_event(RawEvent::RedeemRing(darwinia_account, redeemed_amount, (proof_record.header_hash, proof_record.index))); } // event KtonBurndropTokens(address indexed token, address indexed owner, uint amount, bytes data) @@ -115,15 +115,20 @@ decl_module! { ); let (darwinia_account, redeemed_amount) = Self::parse_token_redeem_proof(&proof_record, "KtonBurndropTokens")?; - let redeemed_kton = >::saturated_from(redeemed_amount); - let redeemed_positive_imbalance_kton = T::Kton::deposit_into_existing(&darwinia_account, redeemed_kton)?; + let redeemed_kton = >::saturated_from(redeemed_amount); + let new_kton_locked = Self::kton_locked().checked_sub(&redeemed_kton).ok_or("KTON Locked - NO SUFFICIENT BACKING ASSETS")?; + + let redeemed_positive_imbalance_kton = T::Kton::deposit_into_existing(&darwinia_account, redeemed_kton)?; T::KtonReward::on_unbalanced(redeemed_positive_imbalance_kton); - KtonProofVerified::insert((proof_record.header_hash, proof_record.index), proof_record); + KtonProofVerified::insert((proof_record.header_hash, proof_record.index), &proof_record); + >::mutate(|l| { - *l = l.saturating_sub(redeemed_kton); + *l = new_kton_locked; }); + + >::deposit_event(RawEvent::RedeemKton(darwinia_account, redeemed_amount, (proof_record.header_hash, proof_record.index))); } // https://github.com/evolutionlandorg/bank @@ -185,7 +190,7 @@ decl_module! { .ok_or("Convert to Int - FAILED")?; let redeemed_amount = { // TODO: div 10**18 and mul 10**9 - let amount = result.params[2] + let amount = result.params[5] .value .clone() .to_uint() @@ -195,35 +200,35 @@ decl_module! { Balance::try_from(amount)? }; let darwinia_account = { - let raw_sub_key = result.params[3] + let raw_sub_key = result.params[6] .value .clone() .to_bytes() .ok_or("Convert to Bytes - FAILED")?; - let decoded_sub_key = hex::decode(&raw_sub_key).map_err(|_| "Decode Address - FAILED")?; +// let decoded_sub_key = hex::decode(&raw_sub_key).map_err(|_| "Decode Address - FAILED")?; - T::DetermineAccountId::account_id_for(&decoded_sub_key)? + T::DetermineAccountId::account_id_for(&raw_sub_key)? }; - let redeemed_ring = >::saturated_from(redeemed_amount); - let redeemed_positive_imbalance_ring = T::Ring::deposit_into_existing(&darwinia_account, redeemed_ring)?; - - T::RingReward::on_unbalanced(redeemed_positive_imbalance_ring); + let redeemed_ring = >::saturated_from(redeemed_amount); + let new_ring_locked = Self::ring_locked().checked_sub(&redeemed_ring).ok_or("RING Locked - NO SUFFICIENT BACKING ASSETS")?; + T::OnDepositRedeem::on_deposit_redeem( + month.saturated_into(), + start_at.saturated_into(), + redeemed_amount, + &darwinia_account, + )?; // TODO: check deposit_id duplication // TODO: Ignore Unit Interest for now - T::OnDepositRedeem::on_deposit_redeem( - month.saturated_into(), - start_at.saturated_into(), - redeemed_amount, - &darwinia_account, - )?; + DepositProofVerified::insert((proof_record.header_hash, proof_record.index), &proof_record); - DepositProofVerified::insert((proof_record.header_hash, proof_record.index), proof_record); >::mutate(|l| { - *l = l.saturating_sub(redeemed_ring); + *l = new_ring_locked; }); + + >::deposit_event(RawEvent::RedeemDeposit(darwinia_account, redeemed_amount, (proof_record.header_hash, proof_record.index))); } } } @@ -290,9 +295,10 @@ impl Module { .clone() .to_bytes() .ok_or("Convert to Bytes - FAILED")?; - let decoded_sub_key = hex::decode(&raw_sub_key).map_err(|_| "Decode Address - FAILED")?; - T::DetermineAccountId::account_id_for(&decoded_sub_key)? + // let decoded_sub_key = hex::decode(&raw_sub_key).map_err(|_| "Decode Address - FAILED")?; + + T::DetermineAccountId::account_id_for(&raw_sub_key)? }; Ok((darwinia_account, redeemed_amount)) @@ -300,7 +306,6 @@ impl Module { } pub trait AccountIdFor { - // fn contract_address_for(code_hash: &CodeHash, data: &[u8], origin: &AccountId) -> AccountId; fn account_id_for(decoded_sub_key: &[u8]) -> result::Result; } @@ -311,12 +316,12 @@ where T::AccountId: rstd::convert::From<[u8; 32]> + AsRef<[u8]>, { fn account_id_for(decoded_sub_key: &[u8]) -> result::Result { - if decoded_sub_key.len() != 32 { - return Err("Address Length - MISMATCHED"); - } + ensure!(decoded_sub_key.len() == 33, "Address Length - MISMATCHED"); + + ensure!(decoded_sub_key[0] == 42, "Pubkey Prefix - MISMATCHED"); let mut r = [0u8; 32]; - r.copy_from_slice(&decoded_sub_key[..]); + r.copy_from_slice(&decoded_sub_key[1..]); let darwinia_account = r.into(); @@ -325,3 +330,9 @@ where Ok(darwinia_account) } } + +impl Module { + pub fn adjust_deposit_value() { + unimplemented!() + } +} diff --git a/srml/eth-backing/src/mock.rs b/srml/eth-backing/src/mock.rs new file mode 100644 index 000000000..0871075ce --- /dev/null +++ b/srml/eth-backing/src/mock.rs @@ -0,0 +1,274 @@ +//! Test utilities + +use crate::*; +use phragmen::{build_support_map, elect, equalize, ExtendedBalance as Power, PhragmenStakedAssignment}; +use sr_primitives::{ + impl_opaque_keys, + testing::{Header, UintAuthorityId}, + traits::{Convert, IdentityLookup, OpaqueKeys}, + weights::Weight, + AccountId32, KeyTypeId, Perbill, +}; +use std::{cell::RefCell, collections::HashSet}; + +use sr_staking_primitives::SessionIndex; +use support::{ + impl_outer_origin, parameter_types, + traits::{Currency, FindAuthor, Get}, + ConsensusEngineId, StorageLinkedMap, +}; + +use primitives::{crypto::key_types, H256}; + +use hex_literal::hex; +use rustc_hex::FromHex; + +/// The AccountId alias in this test module. +pub type AccountId = AccountId32; +pub type BlockNumber = u64; + +pub type System = system::Module; + +pub type EthRelay = darwinia_eth_relay::Module; + +pub type EthBacking = Module; + +pub type RingModule = balances::Module; +pub type KtonModule = kton::Module; + +pub type Timestamp = timestamp::Module; + +pub type Staking = staking::Module; + +pub type Balance = u128; +pub type Moment = u64; + +/// Counter for the number of eras that have passed. +pub type EraIndex = u32; + +pub type Points = u32; + +pub const NANO: Balance = 1; +pub const MICRO: Balance = 1_000 * NANO; +pub const MILLI: Balance = 1_000 * MICRO; +pub const COIN: Balance = 1_000 * MILLI; + +/// Simple structure that exposes how u64 currency can be represented as... u64. +pub struct CurrencyToVoteHandler; +impl Convert for CurrencyToVoteHandler { + fn convert(x: u64) -> u64 { + x + } +} +impl Convert for CurrencyToVoteHandler { + fn convert(x: u128) -> u128 { + x + } +} +impl Convert for CurrencyToVoteHandler { + fn convert(x: u128) -> u64 { + x as u64 + } +} + +thread_local! { +// static SESSION: RefCell<(Vec, HashSet)> = RefCell::new(Default::default()); + static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); +} + +pub struct TestSessionHandler; +impl session::SessionHandler for TestSessionHandler { + const KEY_TYPE_IDS: &'static [KeyTypeId] = &[key_types::DUMMY]; + + fn on_genesis_session(_validators: &[(AccountId, Ks)]) {} + + fn on_new_session( + _changed: bool, + validators: &[(AccountId, Ks)], + _queued_validators: &[(AccountId, Ks)], + ) { + // SESSION.with(|x| *x.borrow_mut() = (validators.iter().map(|x| x.0.clone()).collect(), HashSet::new())); + } + + fn on_disabled(validator_index: usize) { + // SESSION.with(|d| { + // let mut d = d.borrow_mut(); + // let value = d.0[validator_index]; + // d.1.insert(value); + // }) + } +} + +pub fn is_disabled(controller: AccountId) -> bool { + // let stash = Staking::ledger(&controller).unwrap().stash; + // SESSION.with(|d| d.borrow().1.contains(&stash)) + false +} + +pub struct ExistentialDeposit; +impl Get for ExistentialDeposit { + fn get() -> Balance { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) + } +} + +impl_outer_origin! { + pub enum Origin for Test {} +} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} +impl system::Trait for Test { + type Origin = Origin; + type Call = (); + type Index = u64; + type BlockNumber = BlockNumber; + type Hash = H256; + type Hashing = ::sr_primitives::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); +} + +parameter_types! { + pub const Period: BlockNumber = 1; + pub const Offset: BlockNumber = 0; + pub const UncleGenerations: u64 = 0; + pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(25); +} +impl session::Trait for Test { + type Event = (); + type ValidatorId = AccountId; + type ValidatorIdOf = staking::StashOf; + type ShouldEndSession = session::PeriodicSessions; + type OnSessionEnding = session::historical::NoteHistoricalRoot; + type SessionHandler = TestSessionHandler; // ::KeyTypeIdProviders; + type Keys = UintAuthorityId; + type DisabledValidatorsThreshold = DisabledValidatorsThreshold; + type SelectInitialValidators = Staking; +} + +impl session::historical::Trait for Test { + type FullIdentification = staking::Exposure; + type FullIdentificationOf = staking::ExposureOf; +} + +impl authorship::Trait for Test { + type FindAuthor = (); + type UncleGenerations = UncleGenerations; + type FilterUncle = (); + type EventHandler = Staking; +} + +parameter_types! { + pub const MinimumPeriod: Moment = 5; +} +impl timestamp::Trait for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; +} + +parameter_types! { + pub const TransferFee: Balance = 0; + pub const CreationFee: Balance = 0; +} +impl balances::Trait for Test { + type Balance = Balance; + type OnFreeBalanceZero = Staking; + type OnNewAccount = (); + type TransferPayment = (); + type DustRemoval = (); + type Event = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; +} +impl kton::Trait for Test { + type Event = (); +} + +parameter_types! { + pub const SessionsPerEra: SessionIndex = 3; + pub const BondingDuration: Moment = 60; + pub const BondingDurationInEra: EraIndex = 60; + pub const CAP: Balance = 10_000_000_000 * COIN; + pub const GenesisTime: Moment = 0; +} +impl staking::Trait for Test { + type Time = Timestamp; + type CurrencyToVote = (); + type Event = (); + type SessionsPerEra = (); + type BondingDuration = (); + type BondingDurationInEra = (); + type SessionInterface = Self; + type Ring = RingModule; + type RingRewardRemainder = (); + type RingSlash = (); + type RingReward = (); + type Kton = KtonModule; + type KtonSlash = (); + type KtonReward = (); + + type Cap = CAP; + type GenesisTime = GenesisTime; +} + +parameter_types! { +// pub const EthMainet: u64 = 0; + pub const EthRopsten: u64 = 1; +} + +impl darwinia_eth_relay::Trait for Test { + type Event = (); + type EthNetwork = EthRopsten; +} + +impl Trait for Test { + type Event = (); + type EthRelay = EthRelay; + type Ring = RingModule; + type Kton = KtonModule; + type OnDepositRedeem = Staking; + type DetermineAccountId = AccountIdDeterminator; + type RingReward = (); + type KtonReward = (); +} + +pub struct ExtBuilder; +impl Default for ExtBuilder { + fn default() -> Self { + Self + } +} +impl ExtBuilder { + pub fn build(self) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::default().build_storage::().unwrap(); + + let _ = GenesisConfig:: { + ring_redeem_address: hex!["dbc888d701167cbfb86486c516aafbefc3a4de6e"].into(), + kton_redeem_address: hex!["dbc888d701167cbfb86486c516aafbefc3a4de6e"].into(), + deposit_redeem_address: hex!["6ef538314829efa8386fc43386cb13b4e0a67d1e"].into(), + ring_locked: 20000000000000, + kton_locked: 5000000000000, + } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() + } +} diff --git a/srml/eth-backing/src/tests.rs b/srml/eth-backing/src/tests.rs new file mode 100644 index 000000000..a5d96eb9f --- /dev/null +++ b/srml/eth-backing/src/tests.rs @@ -0,0 +1,287 @@ +//! Tests for the module. +use hex_literal::hex; +use rustc_hex::FromHex; + +use sr_eth_primitives::{ + header::EthHeader, + receipt::{LogEntry, TransactionOutcome}, + Bloom, EthAddress, H64, U128, +}; +use std::str::FromStr; +use support::{assert_ok, assert_err}; + +use sr_primitives::{AccountId32, traits::Dispatchable}; + +use staking::{ + RewardDestination, StakingBalances, StakingLedger, TimeDepositItem +}; + +use darwinia_support::StakingLock; + +use crate::{mock::*, *}; + +#[test] +fn verify_parse_token_redeem_proof() { + ExtBuilder::default() + .build() + .execute_with(|| { +// System::inc_account_nonce(&2); + + // https://ropsten.etherscan.io/tx/0x59c6758bd2b93b2f060e471df8d6f4d901c453d2c2c012ba28088acfb94f8216 + let proof_record = EthReceiptProof { + index: 0x3a, + proof: "f9085df9085ab90134f90131a05025d4155f73dc935fad82cc20a3fed5f6b940410da6ba1b730adfbd37d7e85ba09798863f04d85164553dec68189123664236df2a85429c69c8a98354db7fc70da0d4b1679cc2d369b9a3962ef22a7afb1dc8e1a5661429932256859e8e15109748a004f24c135084c8a77ce3ad483660bd99f86360003918ce4b5b491ab4869f8a00a035a5a21a02ae973b4006546f5f34cd491071f9a18f21d9c460ab9a352b5c9733a0997761170ed2834dd6424bf1b98e2189ba85d34d294b5c23e958e4550c4f034fa0a646256c9e897a3a4661de2012e89be0770617a30c761ac754c12ea0eee94d14a0f52a9a98c2b63a4ac0252dd7717a9f4165c7e1bd1a89b43356fe04319d917242a049b57bb5f70e9e5704746e07a2110902997a05d4d1c4d1f39191ca0e922f0fff8080808080808080b90214f90211a032cb337a5224bccf679c4bdb238b2a7adee325e97c84353c9694a8ddc93055b1a0b2f970e1b411cd7f96a1b0680b2cba0d005769df0cf40101eb99da894b738d0fa0bd9d15e4fa218ea894a0c8dc662e65f425dd3167d82422dbce97ddb309f1d6b8a0c5d36981f04881b885760a5454f26f12d01c4b3639e625cbcde97d21ce5f004fa0ccc76ec80500cc1eeb0dec7a5447db075f1c1ae6b4a40e697b2027b5c7fdd196a09862edb220bb1d6a80e4160907713b75b5d488394e91ddedc178b581bc420db9a007be230afa07ab6aa6163cb77d638ddb164a83c7334401a1debbbb0d237293a9a051eaeb16c0c107598c61f879a6e76eefd3e50684738fe93c7ab8f8755a3deb3ba0530d6340c3a3ef031bfc2c44f0ef99301638205554eedfc20a4c88e50b57063fa05162fab7a5598c4ef0c513c343e7861ddbf5351893e5f30903427c21799d63bea0fb84c07954cea6a8c379ceda59dc23506046d0ca1fdd80bbea27dbae65a86e2ca0595b2ab4a2845ebd85ede7fb0f13067800f44b88a4f5de5a15f384e2c6672cd1a07e1b535113d54afb651f5ebef05d09cb8877f7a53749430f28330afd12020bb3a0a9a46015245a41686190c0344235f5bd934057cb59d1f4af51c855a7e06c45e9a0d500f6ef7c8bd97685aa3f767549d29a30c1e94b9cb5518a65f44736d95a742aa0a9288d76cf48f260ba8a779a9779359598a82925dcf6df126f06b82dacbcbeb380b90509f9050620b90502f904ff0183404e24b9010000000000000000000000002000000000400000000000001008000020000000000040000000000002000000000000000000000000000080400000080000000000000000000000000000000008000000000000000000000000000000000000000000000000020000000000000000000804080000000000000000000010000000000000000000000001000000000000000000000000004000000000000020200000000000000000000000000000000000000000000000000200000000000000000000000002000000000000000000140000000001000000000800200000000060000000000000000000000000000000000000000000000000020000000000000000f903f4f89b94b52fbe2b925ab79a821b261c82c5ba0814aaa5e0f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000735182c782cb8e7806f8903de7913e6880cbf82ea0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6ea0000000000000000000000000000000000000000000000000112210f4c023b6d3f87a94b52fbe2b925ab79a821b261c82c5ba0814aaa5e0f842a0cc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5a0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6ea0000000000000000000000000000000000000000000000000112210f4c023b6d3f89b94b52fbe2b925ab79a821b261c82c5ba0814aaa5e0f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6ea00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000112210f4c023b6d3f9011c94dbc888d701167cbfb86486c516aafbefc3a4de6ef863a038045eaef0a21b74ff176350f18df02d9041a25d6694b5f63e9474b7b6cd6b94a0000000000000000000000000b52fbe2b925ab79a821b261c82c5ba0814aaa5e0a0000000000000000000000000735182c782cb8e7806f8903de7913e6880cbf82eb8a0000000000000000000000000000000000000000000000000112210f4c023b6d3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000212a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb4454600000000000000000000000000000000000000000000000000000000000000f9011c94b52fbe2b925ab79a821b261c82c5ba0814aaa5e0f863a09bfafdc2ae8835972d7b64ef3f8f307165ac22ceffde4a742c52da5487f45fd1a0000000000000000000000000735182c782cb8e7806f8903de7913e6880cbf82ea0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6eb8a0000000000000000000000000000000000000000000000000112210f4c023b6d3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000212a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb4454600000000000000000000000000000000000000000000000000000000000000" + .from_hex().unwrap(), + header_hash: H256::from(hex!("f3cc3fab1b6cae48660a36839630c350bace54156d57ee3c62c6113d4b7d82b1")) + }; + + let mixh = H256::from(hex!("7afe3c56ba983149cc5690df75110c5b8bd108d99ffb3203ea94d1bb0811389f")); + let nonce = H64::from(hex!("7775c8bc9f155252")); + + let header = EthHeader { + parent_hash: H256::from(hex!("e81c2b775e2fe499fc108626ac8fdb427eca0ef4073c4737ab85e4ad77245d2f")), + timestamp: 0x5df8dc97, + number: 6983947, + author: EthAddress::from(hex!("d34912efb0e7fedaedb9390990d7ef623e01f4fa")), + transactions_root: H256::from(hex!("2c1a476b3bb42bccd51f3df35c25cb1167de017f13c086b9d58dc56f2366614f")), + uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + extra_data: "706f6f6c696e2e636f6d".from_hex().unwrap(), + state_root: H256::from(hex!("aa6b1f9de1b3acf0939928b09aed7177bb81ea3bc102d05ce6fdada0fb8ca11c")), + receipts_root: H256::from(hex!("162102c848b94ffb7e3768f9df5df461da28f63d8f9240484246c037bb8f7460")), + log_bloom: Bloom::from_str("242800210084001820600020041104044004018450430c10081080a01224000920e434b1082288020100080042400028802504000208884d041009203036121019c0004117072068c1020088006a004401420421091400088120375400642008218030c8228102041410100000308a9c090804292800880049008111010280250900180869208025160238400000a04040630730c0d184004042440120ac0000220a000000c811810202010440040211d020c60140a8021a040824040110416000a0682300800010001980000094081846d221130428800803a3830c4420603206d0000c040014920402080008009020840f6e4c608d41420420080000000142").unwrap(), + gas_used: 0x78bd9a.into(), + gas_limit: 0x79d4fe.into(), + difficulty: 0x75e5a3ef_u64.into(), + seal: vec![rlp::encode(&mixh), rlp::encode(&nonce)], + hash: Some(H256::from(hex!("f3cc3fab1b6cae48660a36839630c350bace54156d57ee3c62c6113d4b7d82b1"))), + }; + + assert_ok!(EthRelay::init_genesis_header(&header, 0x68de130d2c02a8_u64)); + + let expect_account_id = ::DetermineAccountId::account_id_for(&hex!("2a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb44546")).unwrap(); + + assert_eq!(EthBacking::parse_token_redeem_proof(&proof_record, "RingBurndropTokens"), Ok((expect_account_id, 1234567891))); + }); +} + +#[test] +fn verify_redeem_ring() { + ExtBuilder::default() + .build() + .execute_with(|| { +// System::inc_account_nonce(&2); + + // https://ropsten.etherscan.io/tx/0x59c6758bd2b93b2f060e471df8d6f4d901c453d2c2c012ba28088acfb94f8216 + let proof_record = EthReceiptProof { + index: 0x3a, + proof: "f9085df9085ab90134f90131a05025d4155f73dc935fad82cc20a3fed5f6b940410da6ba1b730adfbd37d7e85ba09798863f04d85164553dec68189123664236df2a85429c69c8a98354db7fc70da0d4b1679cc2d369b9a3962ef22a7afb1dc8e1a5661429932256859e8e15109748a004f24c135084c8a77ce3ad483660bd99f86360003918ce4b5b491ab4869f8a00a035a5a21a02ae973b4006546f5f34cd491071f9a18f21d9c460ab9a352b5c9733a0997761170ed2834dd6424bf1b98e2189ba85d34d294b5c23e958e4550c4f034fa0a646256c9e897a3a4661de2012e89be0770617a30c761ac754c12ea0eee94d14a0f52a9a98c2b63a4ac0252dd7717a9f4165c7e1bd1a89b43356fe04319d917242a049b57bb5f70e9e5704746e07a2110902997a05d4d1c4d1f39191ca0e922f0fff8080808080808080b90214f90211a032cb337a5224bccf679c4bdb238b2a7adee325e97c84353c9694a8ddc93055b1a0b2f970e1b411cd7f96a1b0680b2cba0d005769df0cf40101eb99da894b738d0fa0bd9d15e4fa218ea894a0c8dc662e65f425dd3167d82422dbce97ddb309f1d6b8a0c5d36981f04881b885760a5454f26f12d01c4b3639e625cbcde97d21ce5f004fa0ccc76ec80500cc1eeb0dec7a5447db075f1c1ae6b4a40e697b2027b5c7fdd196a09862edb220bb1d6a80e4160907713b75b5d488394e91ddedc178b581bc420db9a007be230afa07ab6aa6163cb77d638ddb164a83c7334401a1debbbb0d237293a9a051eaeb16c0c107598c61f879a6e76eefd3e50684738fe93c7ab8f8755a3deb3ba0530d6340c3a3ef031bfc2c44f0ef99301638205554eedfc20a4c88e50b57063fa05162fab7a5598c4ef0c513c343e7861ddbf5351893e5f30903427c21799d63bea0fb84c07954cea6a8c379ceda59dc23506046d0ca1fdd80bbea27dbae65a86e2ca0595b2ab4a2845ebd85ede7fb0f13067800f44b88a4f5de5a15f384e2c6672cd1a07e1b535113d54afb651f5ebef05d09cb8877f7a53749430f28330afd12020bb3a0a9a46015245a41686190c0344235f5bd934057cb59d1f4af51c855a7e06c45e9a0d500f6ef7c8bd97685aa3f767549d29a30c1e94b9cb5518a65f44736d95a742aa0a9288d76cf48f260ba8a779a9779359598a82925dcf6df126f06b82dacbcbeb380b90509f9050620b90502f904ff0183404e24b9010000000000000000000000002000000000400000000000001008000020000000000040000000000002000000000000000000000000000080400000080000000000000000000000000000000008000000000000000000000000000000000000000000000000020000000000000000000804080000000000000000000010000000000000000000000001000000000000000000000000004000000000000020200000000000000000000000000000000000000000000000000200000000000000000000000002000000000000000000140000000001000000000800200000000060000000000000000000000000000000000000000000000000020000000000000000f903f4f89b94b52fbe2b925ab79a821b261c82c5ba0814aaa5e0f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000735182c782cb8e7806f8903de7913e6880cbf82ea0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6ea0000000000000000000000000000000000000000000000000112210f4c023b6d3f87a94b52fbe2b925ab79a821b261c82c5ba0814aaa5e0f842a0cc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5a0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6ea0000000000000000000000000000000000000000000000000112210f4c023b6d3f89b94b52fbe2b925ab79a821b261c82c5ba0814aaa5e0f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6ea00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000112210f4c023b6d3f9011c94dbc888d701167cbfb86486c516aafbefc3a4de6ef863a038045eaef0a21b74ff176350f18df02d9041a25d6694b5f63e9474b7b6cd6b94a0000000000000000000000000b52fbe2b925ab79a821b261c82c5ba0814aaa5e0a0000000000000000000000000735182c782cb8e7806f8903de7913e6880cbf82eb8a0000000000000000000000000000000000000000000000000112210f4c023b6d3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000212a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb4454600000000000000000000000000000000000000000000000000000000000000f9011c94b52fbe2b925ab79a821b261c82c5ba0814aaa5e0f863a09bfafdc2ae8835972d7b64ef3f8f307165ac22ceffde4a742c52da5487f45fd1a0000000000000000000000000735182c782cb8e7806f8903de7913e6880cbf82ea0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6eb8a0000000000000000000000000000000000000000000000000112210f4c023b6d3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000212a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb4454600000000000000000000000000000000000000000000000000000000000000" + .from_hex().unwrap(), + header_hash: H256::from(hex!("f3cc3fab1b6cae48660a36839630c350bace54156d57ee3c62c6113d4b7d82b1")) + }; + + let mixh = H256::from(hex!("7afe3c56ba983149cc5690df75110c5b8bd108d99ffb3203ea94d1bb0811389f")); + let nonce = H64::from(hex!("7775c8bc9f155252")); + + let header = EthHeader { + parent_hash: H256::from(hex!("e81c2b775e2fe499fc108626ac8fdb427eca0ef4073c4737ab85e4ad77245d2f")), + timestamp: 0x5df8dc97, + number: 6983947, + author: EthAddress::from(hex!("d34912efb0e7fedaedb9390990d7ef623e01f4fa")), + transactions_root: H256::from(hex!("2c1a476b3bb42bccd51f3df35c25cb1167de017f13c086b9d58dc56f2366614f")), + uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + extra_data: "706f6f6c696e2e636f6d".from_hex().unwrap(), + state_root: H256::from(hex!("aa6b1f9de1b3acf0939928b09aed7177bb81ea3bc102d05ce6fdada0fb8ca11c")), + receipts_root: H256::from(hex!("162102c848b94ffb7e3768f9df5df461da28f63d8f9240484246c037bb8f7460")), + log_bloom: Bloom::from_str("242800210084001820600020041104044004018450430c10081080a01224000920e434b1082288020100080042400028802504000208884d041009203036121019c0004117072068c1020088006a004401420421091400088120375400642008218030c8228102041410100000308a9c090804292800880049008111010280250900180869208025160238400000a04040630730c0d184004042440120ac0000220a000000c811810202010440040211d020c60140a8021a040824040110416000a0682300800010001980000094081846d221130428800803a3830c4420603206d0000c040014920402080008009020840f6e4c608d41420420080000000142").unwrap(), + gas_used: 0x78bd9a.into(), + gas_limit: 0x79d4fe.into(), + difficulty: 0x75e5a3ef_u64.into(), + seal: vec![rlp::encode(&mixh), rlp::encode(&nonce)], + hash: Some(H256::from(hex!("f3cc3fab1b6cae48660a36839630c350bace54156d57ee3c62c6113d4b7d82b1"))), + }; + + assert_ok!(EthRelay::init_genesis_header(&header, 0x68de130d2c02a8_u64)); + + let expect_account_id = ::DetermineAccountId::account_id_for(&hex!("2a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb44546")).unwrap(); + + let id1 = AccountId32::from([0; 32]); + // If expect_account_id doesn't exist, redeem should fail + assert_err!(EthBacking::redeem_ring(Origin::signed(id1.clone()), proof_record.clone()), "beneficiary account must pre-exist"); + + let ring_locked_before = EthBacking::ring_locked(); + + let _ = RingModule::deposit_creating(&expect_account_id, 1); + assert_ok!(EthBacking::redeem_ring(Origin::signed(id1.clone()), proof_record.clone())); + + assert_eq!(RingModule::free_balance(&expect_account_id), 1234567891 + 1); + + let ring_locked_after = EthBacking::ring_locked(); + + assert_eq!(ring_locked_after + 1234567891, ring_locked_before); + + // shouldn't redeem twice + assert_err!(EthBacking::redeem_ring(Origin::signed(id1.clone()), proof_record.clone()), "Ring For This Proof - ALREADY BEEN REDEEMED"); + }); +} + +#[test] +fn verify_redeem_kton() { + ExtBuilder::default() + .build() + .execute_with(|| { +// System::inc_account_nonce(&2); + + // https://ropsten.etherscan.io/tx/0xc878562085dd8b68ad81adf0820aa0380f1f81b0ea7c012be122937b74020f96 + // darwinia: 5FP2eFNSVxJzSrE3N2NEVFPhUU34VzYFD6DDtRXbYzTdwPn8 + // hex: 0x92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb44546 + // amount: 0.123456789123456789 KTON + let proof_record = EthReceiptProof { + index: 0xe, + proof: "f907bbf907b8b8b3f8b1a0adc9c2f1773854b67d199fe4ab9cf09a5acd076dc67dd90d2467bdc057109892a074db6124fd385d9fdd64a8911d65149935456f06208e6544512a15767b85dc47a085c757ed14e68ebbb710356211d00673922763d9c58726662fd25be97f132302a051caa42d7eda931122489032d3a88de12ecb7ebfd5788440a6a7eb7cd8b9498d80808080a069b207da947563a4195edc459548caf5646c4d814f84b4c516dae98490436b228080808080808080b901f4f901f180a0e437cbd8baff37825bac07ad32e0852b9c52b07c6de1fcca79e203f10d19c421a084f99876f06059390e9feccc9a18447dc64e0f3e45ab427784d4b75e367f5043a01b7421ceff091a6c1127f47f8921ea2abdf836e5115cf426183804fa7af5ceeea0fa81586ff394840f08796afa04efe89183542fc27396466e1a6900aa8cf61c8da080667dd8f2715f1abfa0dee483fc02eea840ac912628bc649161a0faaddb7d78a0ef064944ebcc178acfdfe6f255a21e0988f1c87da330a7bb59a5b7c2299c33f4a0e002f10d1424019ef731f1cfadcc6d8d037c5788437b9db7ab61c42c9bbb30a9a077794de8aa4dc428511ee88025b58c273a83f362efa37c771961afc82236be06a0610d647dd29aeb3a9703f9b29f3774aa3bb5453814350576482c7ec434c4f39da0f0f19cc201f05c9b5bc7c39efe9fa931c7c314b008d8bfbcd0aa38ad43f4d91ca010005704956fb1735705accb3a3936c9253a9f4ff243a3b9003854f2085b6206a017c40900dcaed002dc6e87aa8e6a73efd671ef3663dcce4609f468aefcf81171a079715ab8793135007503ee61146392fff91fd78ba90b788391b4f8034da5dd23a09157fc0b584fedb79d9cd0e535bb2face3b8a52455ef4282ad529ff74244b1fca014c862f02f3d871220c706c7a6fa2756086fe5e0d7a289858f717e38b546c72a80b90509f9050620b90502f904ff01830dbb87b9010000000000000000000000000000000000400000000000001008000020000000000040000000000002000000000000000000000000000080400000000000000800000000000000400000200008000000000000000000000000000000040000000000000000020000000000000000000804080000000000000000000010000000000000000000000000000000000000004000000000004000000020000020200000000000000000000000000000000000000000000000000002000000000000000000000002000000000000000000100000000000000000000000000000000020000000000000000000000040010000000000000000000000020000000000000000f903f4f89b941994100c58753793d52c6f457f189aa3ce9cee94f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000735182c782cb8e7806f8903de7913e6880cbf82ea0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6ea000000000000000000000000000000000000000000000000001b69b4bacd05f15f87a941994100c58753793d52c6f457f189aa3ce9cee94f842a0cc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5a0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6ea000000000000000000000000000000000000000000000000001b69b4bacd05f15f89b941994100c58753793d52c6f457f189aa3ce9cee94f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6ea00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000001b69b4bacd05f15f9011c94dbc888d701167cbfb86486c516aafbefc3a4de6ef863a07c6ab7280253e73a918d8297bd1601093f0e50b0e0af1ad4e40a73179d621a74a00000000000000000000000001994100c58753793d52c6f457f189aa3ce9cee94a0000000000000000000000000735182c782cb8e7806f8903de7913e6880cbf82eb8a000000000000000000000000000000000000000000000000001b69b4bacd05f15000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000212a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb4454600000000000000000000000000000000000000000000000000000000000000f9011c941994100c58753793d52c6f457f189aa3ce9cee94f863a09bfafdc2ae8835972d7b64ef3f8f307165ac22ceffde4a742c52da5487f45fd1a0000000000000000000000000735182c782cb8e7806f8903de7913e6880cbf82ea0000000000000000000000000dbc888d701167cbfb86486c516aafbefc3a4de6eb8a000000000000000000000000000000000000000000000000001b69b4bacd05f15000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000212a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb4454600000000000000000000000000000000000000000000000000000000000000" + .from_hex().unwrap(), + header_hash: H256::from(hex!("32abfdd1cd066853540af65bd7cc2246e38f134608b3998d32d05a4330bc183c")) + }; + + let mixh = H256::from(hex!("f1208c3da083aee3c37dd9510de03bcbe86a5ee0d5db1b8e75b4767de3b25473")); + let nonce = H64::from(hex!("44f14ec003488a81")); + + let header = EthHeader { + parent_hash: H256::from(hex!("312f10d1fc890bf1cde54b76791fd327a1ddcd20d9dea5e667389a4b7d75547b")), + timestamp: 0x5df9f1c0, + number: 6988603, + author: EthAddress::from(hex!("4ccfb3039b78d3938588157564c9ad559bafab94")), + transactions_root: H256::from(hex!("329f6a0e711a5227039edf8210a7fd82bc69eb2943b1b6b11ff959d729766d43")), + // sha3Uncles + uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + extra_data: "d983010906846765746889676f312e31312e3133856c696e7578".from_hex().unwrap(), + state_root: H256::from(hex!("f9be8e50805b32ba79e172d046e2ebb1baa5f207805fa0060007f5637b29abb9")), + receipts_root: H256::from(hex!("1c433422c5c4b5567820ba6c9c37b3a93d11213bdf211f00ad251944d3365801")), + log_bloom: Bloom::from_str("0400000040008000000010000080140048000100808000100800002080040002006010a100000002000000005240008000000000120280c00020080000360c00000010000400600001200009000202402000100000040000400000040020210100004100120a2100260010000200080408000001000080880401001000008000010048000000802004001a000000a04400020c80214000080020000030240000020400040040000000022100001208008080020400c00002000000404001514122090002000000000000001000100808002020000000000006000000100060001010000000000000000040858100000004008040009400020000000000200002").unwrap(), + gas_used: 0x325fb8.into(), + gas_limit: 0x79f34d.into(), + difficulty: 0x66196b6a_u64.into(), + seal: vec![rlp::encode(&mixh), rlp::encode(&nonce)], + hash: Some(H256::from(hex!("32abfdd1cd066853540af65bd7cc2246e38f134608b3998d32d05a4330bc183c"))), + }; + + // totalDifficulty + assert_ok!(EthRelay::init_genesis_header(&header, 0x68e4ea361f7a78_u64)); + + let expect_account_id = ::DetermineAccountId::account_id_for(&hex!("2a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb44546")).unwrap(); + + // 0.123456789123456789 KTON + assert_eq!(EthBacking::parse_token_redeem_proof(&proof_record, "KtonBurndropTokens"), Ok((expect_account_id.clone(), 123456789))); + + let id1 = AccountId32::from([0; 32]); + // If expect_account_id doesn't exist, redeem should fail + assert_err!(EthBacking::redeem_kton(Origin::signed(id1.clone()), proof_record.clone()), "beneficiary account must pre-exist"); + + let kton_locked_before = EthBacking::kton_locked(); + + let _ = KtonModule::deposit_creating(&expect_account_id, 1); + assert_ok!(EthBacking::redeem_kton(Origin::signed(id1.clone()), proof_record.clone())); + + assert_eq!(KtonModule::free_balance(&expect_account_id), 123456789 + 1); + + let kton_locked_after = EthBacking::kton_locked(); + assert_eq!(kton_locked_after + 123456789, kton_locked_before); + + // shouldn't redeem twice + assert_err!(EthBacking::redeem_kton(Origin::signed(id1.clone()), proof_record.clone()), "Kton For This Proof - ALREADY BEEN REDEEMED"); + }); +} + +#[test] +fn verify_redeem_deposit() { + ExtBuilder::default() + .build() + .execute_with(|| { +// System::inc_account_nonce(&2); + + // 1234ring -> 0.1234kton + + // _depositID 2 + // 0: address: 0x735182c782CB8e7806F8903dE7913e6880CbF82E _depositor + // 1: uint128: 1234000000000000000000 _value + // 2: uint128: 12 _months + // 3: uint256: 1576664555 _startAt + // 4: uint256: 1000 _unitInterest + // 5: bool: false + // _data 0x2a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb44546 + + // transfer:https://ropsten.etherscan.io/tx/0x4343443642cafe19e06d61047286c5ec5964b1483d5e8cf61e89892c09dc5209 + let proof_record = EthReceiptProof { + index: 0xe, + proof: "f9061ff9061cb873f871a00a6e86ba1ddb6ae5288f534e4d017c15d5d36e00de0f86962b1d22bb0ffe32cfa057cf58af3ecf32d2d81c2ef3417bfe98f38ff72edda617501416689d398ae5d1808080808080a0ace1570bcb9c5273ce1b6e176ff808d88092472b5e436d2c388494ca6f87e27c8080808080808080b901f4f901f180a01d97bd87c78056c3a86ebb1ba172f5f2e84d7ba632d33166cb09e7fdf44e7d19a045b6d434614db44568dc155b87d39de5241f59bed0886d8edfdf672952ae3a74a07375aa73af1f3c2f50707886678c020f128b015bfbf4bef2c9be4cabb084585ea06c3fe02b9db7ace4662886534ee3f4ff0946582d29883822a9f1c5f306a08232a064efdaae98eb58798d04513ff0e48cacaa6e0700cfa5a0bb47fdbc6d4dc453c5a03bc7225b5f7d9e8d47623355f8a76358f5eb7ec3435bed8129563418a11abc7ea0591e855c8785ed8f3de76ed2cf09094d19d5a8dea34c481282f34509e1d6bbdda0a0d4e9d7ebcb32c492fbb3b950c0a27ffe324c46f2b4fa80b50fda12e406295ea0ae735a45af8e3fffba292ab13a9d8e5271dcb1eff61a1fba1d2bcc5da8376efca00457b813f021a853505f32f5d3aa0f1a9fdb08edb7569a7753506045049aa50ca0f836c2b241f5c96094295ed8a013b68abdc78a1dab8fc35504d8d59388f1e177a08c96ece23b48480e91cf66241fabfbe28825c1a7a547ab4df651ad6134e74d97a07dbbb649a1a42ae6d34a3a1e8232d400523aec47be9540aec14cca50bd3673a1a0430672447c3458ccab73889beeb310357728351ac5516068e6c398aa345b838da005f4bca21b205248daefe170261ba8fc3f616d3d6c08140e6ca6eaee4463142580b903adf903aa20b903a6f903a301830d0c10b9010004000000000000400000000000000000000000000000001000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000080000000000000020200000000000000000800080000000000000000000010000000001000000000000000000000000000000000000000000400000000000000200000000000000000000000000100000000000800008000000200000000000000000000000002000000000000000000040000000001000000000000000000020020000000000000000000000000000000000000000000008000000000000000000000f90298f87a94b52fbe2b925ab79a821b261c82c5ba0814aaa5e0f842a0cc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5a00000000000000000000000006ef538314829efa8386fc43386cb13b4e0a67d1ea0000000000000000000000000000000000000000000000042e530adfce0080000f89b94b52fbe2b925ab79a821b261c82c5ba0814aaa5e0f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa00000000000000000000000006ef538314829efa8386fc43386cb13b4e0a67d1ea00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000042e530adfce0080000f9017c946ef538314829efa8386fc43386cb13b4e0a67d1ef842a0455d5fda67197daa1239477da37301be9abb7771027186e589d8c341c609d285a00000000000000000000000000000000000000000000000000000000000000002b90120000000000000000000000000735182c782cb8e7806f8903de7913e6880cbf82e000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000005df9fdeb00000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000042e530adfce008000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000212a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb4454600000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(), + header_hash: H256::from(hex!("14bf4b76a25a23ca1d625ff89673813548e138f84b511059695d801c0a7be578")) + }; + + let mixh = H256::from(hex!("3f3b1e56a051f395e9ee297a8bcd307ed7e328891d61eeb46b224dbdf710634a")); + let nonce = H64::from(hex!("87cce326ad070e33")); + + let header = EthHeader { + parent_hash: H256::from(hex!("6ff4be9ac4f39a5e3886874bb939437b752e0c6f27803f9050b32c27f925a214")), + timestamp: 0x5df9feec, + number: 6988980, + author: EthAddress::from(hex!("635b4764d1939dfacd3a8014726159abc277becc")), + transactions_root: H256::from(hex!("3126f8c7133dff518b1d7dee4885a7179c9e68b37f792b2192663cd033961385")), + // sha3Uncles + uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + extra_data: "de8302050a8f5061726974792d457468657265756d86312e33382e30826c69".from_hex().unwrap(), + state_root: H256::from(hex!("d90785981dfb161936c9d6cd5388da2ad98cf31717a0dfc7fce8b7dace0c8d07")), + receipts_root: H256::from(hex!("73f38c8ee57395ba8c46d856e33e3042738143d7db02f61881b714069b58982a")), + log_bloom: Bloom::from_str("04000008000000401000000080002000000011000001001000000000025000000000000100000000000000000000000000000000000000000800000000002000000000000000000010000048000000a40000000020400000000020000080800000000000420200020000000000000800080000000000080001008010000000001000000400000000000000000000001000000420000400000041000000300000008000001000000000000100000000000801008000000200000000000000000012000002000000000000800000848000000481000100000000000100420020001210004000040080004000000000100000400000018000004000040000000000").unwrap(), + gas_used: 0x141bd0.into(), + gas_limit: 0x7a121d.into(), + difficulty: 0x745523c7_u64.into(), + seal: vec![rlp::encode(&mixh), rlp::encode(&nonce)], + hash: Some(H256::from(hex!("14bf4b76a25a23ca1d625ff89673813548e138f84b511059695d801c0a7be578"))), + }; + + // totalDifficulty + assert_ok!(EthRelay::init_genesis_header(&header, 0x68e58ae1c31caf_u64)); + + let ring_locked_before = EthBacking::ring_locked(); + + let expect_account_id = ::DetermineAccountId::account_id_for(&hex!("2a92ae5b41feba5ee68a61449c557efa9e3b894a6461c058ec2de45429adb44546")).unwrap(); + + let id1 = AccountId32::from([0; 32]); + + let controller = AccountId32::from([1; 32]); + + let _ = RingModule::deposit_creating(&expect_account_id, 1); + assert_ok!( + staking::Call::::bond( + controller.clone(), + StakingBalances::Ring(1), + RewardDestination::Controller, + 0).dispatch(Origin::signed(expect_account_id.clone())) + ); + assert_ok!(EthBacking::redeem_deposit(Origin::signed(id1.clone()), proof_record.clone())); + + assert_eq!(RingModule::free_balance(&expect_account_id), 1234000000000 + 1); + + let ring_locked_after = EthBacking::ring_locked(); + assert_eq!(ring_locked_after + 1234000000000, ring_locked_before); + + let staking_ledger = Staking::ledger(&controller); + + assert_eq!(staking_ledger, Some(StakingLedger { + stash: expect_account_id, + active_ring: 1234000000001, + active_deposit_ring: 1234000000000, + deposit_items: vec![TimeDepositItem { value: 1234000000000, start_time: 1576664555000, expire_time: 1607768555000 }], + ring_staking_lock: StakingLock { staking_amount: 1234000000001, unbondings: vec![] }, + ..Default::default() + })); + + // shouldn't redeem twice + assert_err!(EthBacking::redeem_deposit(Origin::signed(id1.clone()), proof_record.clone()), "Deposit For This Proof - ALREADY BEEN REDEEMED"); + }); +} + +#[test] +fn verify_insurficient_backing_assets() { + // TODO +} + diff --git a/srml/eth-relay/src/lib.rs b/srml/eth-relay/src/lib.rs index b87fd45b4..2994f820a 100644 --- a/srml/eth-relay/src/lib.rs +++ b/srml/eth-relay/src/lib.rs @@ -97,17 +97,17 @@ decl_module! { fn deposit_event() = default; pub fn reset_genesis_header(origin, header: EthHeader, genesis_difficulty: u64) { - let _relayer = ensure_signed(origin)?; + let relayer = ensure_signed(origin)?; // TODO: Check authority // TODO: Just for easy testing. Self::init_genesis_header(&header, genesis_difficulty)?; - >::deposit_event(RawEvent::SetGenesisHeader(header, genesis_difficulty)); + >::deposit_event(RawEvent::SetGenesisHeader(relayer, header, genesis_difficulty)); } pub fn relay_header(origin, header: EthHeader) { - let _relayer = ensure_signed(origin)?; + let relayer = ensure_signed(origin)?; // 1. There must be a corresponding parent hash // 2. Update best hash if the current block number is larger than current best block's number (Chain reorg) @@ -115,15 +115,15 @@ decl_module! { Self::store_header(&header)?; - >::deposit_event(RawEvent::RelayHeader(header)); + >::deposit_event(RawEvent::RelayHeader(relayer, header)); } pub fn check_receipt(origin, proof_record: EthReceiptProof) { - let _relayer = ensure_signed(origin)?; + let relayer = ensure_signed(origin)?; let verified_receipt = Self::verify_receipt(&proof_record)?; - >::deposit_event(RawEvent::VerifyProof(verified_receipt, proof_record)); + >::deposit_event(RawEvent::VerifyProof(relayer, verified_receipt, proof_record)); } // Assuming that there are at least one honest worker submiting headers @@ -141,10 +141,9 @@ decl_event! { where ::AccountId { - SetGenesisHeader(EthHeader, u64), - RelayHeader(EthHeader), - VerifyProof(Receipt, EthReceiptProof), - TODO(AccountId), + SetGenesisHeader(AccountId, EthHeader, u64), + RelayHeader(AccountId, EthHeader), + VerifyProof(AccountId, Receipt, EthReceiptProof), // Develop // Print(u64), @@ -244,9 +243,12 @@ impl Module { let header_hash = header.hash(); let block_number = header.number(); - HeaderOf::insert(header_hash, header); + let prev_total_difficulty = Self::header_details_of(header.parent_hash()).ok_or("Previous Header Detail - NOT EXISTED")?.total_difficulty; + let best_header_hash = Self::best_header_hash(); + // let best_header = Self::header_of(best_header_hash).ok_or("Can not find best header."); + let best_header_details = Self::header_details_of(best_header_hash).ok_or("Best Header Detail - NOT EXISTED")?; - let prev_total_difficulty = Self::header_details_of(header.parent_hash()).unwrap().total_difficulty; + HeaderOf::insert(header_hash, header); HeaderDetailsOf::insert( header_hash, @@ -257,10 +259,6 @@ impl Module { }, ); - let best_header_hash = Self::best_header_hash(); - // let best_header = Self::header_of(best_header_hash).ok_or("Can not find best header."); - let best_header_details = Self::header_details_of(best_header_hash).unwrap(); - // TODO: Check total difficulty and reorg if necessary. if prev_total_difficulty + header.difficulty() > best_header_details.total_difficulty { BestHeaderHash::mutate(|hash| { diff --git a/srml/staking/src/inflation.rs b/srml/staking/src/inflation.rs index cf29b6a19..63391d2e8 100644 --- a/srml/staking/src/inflation.rs +++ b/srml/staking/src/inflation.rs @@ -8,7 +8,7 @@ use substrate_primitives::U256; use crate::{Kton, Moment, Ring, Trait}; // 1 - (99 / 100) ^ sqrt(year) -// () -> RingBalanceOf +// () -> Ring pub fn compute_total_payout( era_duration: Moment, living_time: Moment, diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 7113e659d..459e5772f 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -253,11 +253,11 @@ where #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct TimeDepositItem { #[codec(compact)] - value: Ring, + pub value: Ring, #[codec(compact)] - start_time: Moment, + pub start_time: Moment, #[codec(compact)] - expire_time: Moment, + pub expire_time: Moment, } /// The ledger of a (bonded) stash. @@ -639,11 +639,6 @@ decl_module! { let controller = T::Lookup::lookup(controller)?; ensure!(!>::exists(&controller), err::CONTROLLER_ALREADY_PAIRED); - // You're auto-bonded forever, here. We might improve this by only bonding when - // you actually validate/nominate and remove once you unbond __everything__. - >::insert(&stash, &controller); - >::insert(&stash, payee); - let ledger = StakingLedger { stash: stash.clone(), ..Default::default() @@ -658,6 +653,11 @@ decl_module! { Self::bond_helper_in_ring(&stash, &controller, value, promise_month, ledger); + // You're auto-bonded forever, here. We might improve this by only bonding when + // you actually validate/nominate and remove once you unbond __everything__. + >::insert(&stash, &controller); + >::insert(&stash, payee); + >::mutate(|r| *r += value); >::deposit_event(RawEvent::Bond( StakingBalances::Ring(value.saturated_into()), @@ -1999,23 +1999,25 @@ impl OnDepositRedeem for Module { let start = start_at * 1000; let promise_month = months.min(36); - let stash_balance = T::Ring::free_balance(&stash); - let r = amount.saturated_into(); + // let stash_balance = T::Ring::free_balance(&stash); + let value = amount.saturated_into(); // TODO: Lock but no kton reward because this is a deposit redeem - if let Some(extra) = stash_balance.checked_sub(&ledger.active_ring) { - let extra = extra.min(r); + // let extra = extra.min(r); - Self::bond_helper_in_ring_for_deposit_redeem(&stash, &controller, extra, start, promise_month, ledger); + let redeemed_positive_imbalance_ring = T::Ring::deposit_into_existing(&stash, value)?; - >::mutate(|r| *r += extra); - // TODO: Should we deposit an different event? - >::deposit_event(RawEvent::Bond( - StakingBalances::Ring(extra.saturated_into()), - start, - promise_month, - )); - } + T::RingReward::on_unbalanced(redeemed_positive_imbalance_ring); + + Self::bond_helper_in_ring_for_deposit_redeem(&stash, &controller, value, start, promise_month, ledger); + + >::mutate(|r| *r += value); + // TODO: Should we deposit an different event? + >::deposit_event(RawEvent::Bond( + StakingBalances::Ring(value.saturated_into()), + start, + promise_month, + )); Ok(()) } diff --git a/types.json b/types.json index 40352fb92..a6b49a433 100644 --- a/types.json +++ b/types.json @@ -56,6 +56,7 @@ "seal": "Vec", "hash": "Option" }, + "EthTransactionIndex": "(H256, u64)", "H64": { "_struct": "[u8; 8]" },