GithubHelp home page GithubHelp logo

acalanetwork / acala Goto Github PK

View Code? Open in Web Editor NEW
725.0 50.0 248.0 101.36 MB

Acala - cross-chain DeFi hub and stablecoin based on Substrate for Polkadot and Kusama.

Home Page: https://acala.network

License: GNU General Public License v3.0

Makefile 0.27% Rust 96.63% Shell 0.14% Handlebars 0.31% Dockerfile 0.07% Solidity 0.19% TypeScript 2.38%
substrate rust stablecoin polkadot acala defi kusama

acala's Introduction

GitHub Workflow Status GitHub tag (latest by date) Substrate version codecov License
Twitter URL Discord Telegram Discourse Medium

1. Introduction

This project is initiated and facilitated by the Acala Foundation. Acala Foundation nurtures applications in the fields of decentralized finance protocols, particularly those that serve as open finance infrastructures such as stable currency and staking liquidity. The Acala Foundation is founded by Laminar and Polkawallet, participants and contributors to the Polkadot ecosystem. The Acala Foundation welcomes more industry participants as it progresses.

2. Overview

The significance of cross-chain communication to the blockchain is like that of the internet to the intranet. Polkadot empowers a network of public, consortium and private blockchains, and enables true interoperability, economic and transactional scalability. A cross-chain stablecoin system will:

  1. Create a sound, stable currency for low cost, borderless value transfer for all chains in the network
  2. Enable commercial lending with predictable risk
  3. Serve as a building block for more open finance services

The Acala Dollar stablecoin (ticker: aUSD) is a multi-collateral-backed cryptocurrency, with value stable against US Dollar (aka. 1:1 aUSD to USD soft peg). It is completely decentralized, that it can be created using assets from blockchains connected to the Polkadot network including Ethereum and Bitcoin as collaterals, and can be used by any chains (or digital jurisdictions) within the Polkadot network and applications on those chains.

By this nature, it is essential that the Acala Network eventually become community-owned with an economic model that can sustain its development and participation in the Polkadot network, as well as ensure its stability and security. The following section will provide a high-level overview of the following topics:

  • aUSD and the Honzon stablecoin protocol
  • the economic model and initial parachain offering

2.1. aUSD and the Honzon stablecoin protocol

Every aUSD is backed in excess by a crypto asset, the mechanism of which is known as an over-collateralized debt position (or CDP). Together with a set of incentives, supply & demand balancing, and risk management mechanisms, as the core components of the Honzon stablecoin protocol on the Acala Network, the stability of the aUSD is ensured. The CDP mechanism design is inspired by the first decentralized stablecoin project MakerDAO, which has become the DeFi building block in the Ethereum ecosystem. Besides, the Honzon protocol enables many unique features - native multi-asset support, cross-chain stablecoin capability, automatic liquidation to increase responsiveness to risk, and pluggable oracle and auction house to improve modularity, just to name a few.

The Honzon protocol contains the following components

  • Multi Collateral Type
  • Collateral Adapter
  • Oracle and Prices
  • Auction and Auction Manager
  • CDP and CDP Engine
  • Emergency shutdown
  • Governance
  • Honzon as an interface to other components

Note: This section is still work in progress, we will update more information as we progress. Refer to the Github Wiki for more details.

2.2. Acala Network Economic Model

The Acala Network Token (ACA) features the following utilities, and the value of ACA token will accrue with the increased usage of the network and revenue from stability fees and liquidation penalties

  1. As Network Utility Token: to pay for network fees and stability fees
  2. As Governance Token: to vote for/against risk parameters and network change proposals
  3. As Economic Capital: in case of liquidation without sufficient collaterals

To enable cross-chain functionality, the Acala Network will connect to the Polkadot in one of the three ways:

  1. as parathread - pay-as-you-go connection to Polkadot
  2. as parachain - permanent connection for a given period
  3. as an independent chain with a bridge back to Polkadot

Becoming a parachain would be an ideal option to bootstrap the Acala Network, and maximize its benefits and reach to other chains and applications on the Polkadot network. To secure a parachain slot, the Acala Network will require supportive DOT holders to lock their DOTs to bid for a slot collectively - a process known as the Initial Parachain Offering (IPO). ACA tokens will be offered as a reward for those who participated in the IPO, as compensation for their opportunity cost of staking the DOTs.

Note: This section is still work in progress, we will update more information as we progress. Refer to the token economy working paper for more details.

3. Building

NOTE

To connect on the "Mandala TC6" network, you will want the version ~0.7.10 code which is in this repo.

Install Rust:

curl https://sh.rustup.rs -sSf | sh

You may need additional dependencies, checkout substrate.io for more info

sudo apt-get install -y git clang curl make libssl-dev llvm libudev-dev protobuf-compiler

Make sure you have submodule.recurse set to true to make life with submodule easier.

git config --global submodule.recurse true

Install required tools and install git hooks:

make init

Build Mandala TC native code:

make build-full

4. Run

You can start a development chain with:

make run

5. Development

To type check:

make check-all

To purge old chain data:

make purge

To purge old chain data and run

make restart

Update ORML

make update

Note: All build command from Makefile are designed for local development purposes and hence have SKIP_WASM_BUILD enabled to speed up build time and use --execution native to only run use native execution mode.

6. Bug Bounty 🐛

The Bug Bounty Program includes only on-chain vulnerabilities that can lead to significant economic loss or instability of the network. You check details of the Bug Bounty or Submit a vulnerability here: https://immunefi.com/bounty/acala/

7. Bench Bot

Bench bot can take care of syncing branch with master and generating WeightInfos for module or runtime.

Generate module weights

Comment on a PR /bench module <module_name> i.e.: module_currencies

Bench bot will do the benchmarking, generate weights.rs file and push changes into your branch.

Generate runtime weights

Comment on a PR /bench runtime <runtime> <module_name> i.e.: /bench runtime mandala module_currencies.

To generate weights for all modules just pass * as module_name i.e: /bench runtime mandala *

Bench bot will do the benchmarking, generate weights file and push changes into your branch.

Bench Acala EVM+

Comment on a PR /bench evm to benchmark Acala EVM+ and bench bot will generate precompile weights and GasToWeight ratio.

8. Migration testing runtime

If modifying the storage, you should test the data migration before upgrading the runtime.

Try testing runtime

try-runtime on karura

# Use a live chain to run the migration test.
# Add `-p module_name` can specify the module.
make try-runtime-karura

# Create a state snapshot to run the migration test.
# Add `--pallet module_name` can specify the module.
cargo run --features with-karura-runtime --features try-runtime -- try-runtime --runtime existing create-snapshot --uri wss://karura.api.onfinality.io:443/public-ws karura-latest.snap

# Use a state snapshot to run the migration test.
./target/release/acala try-runtime --runtime ./target/release/wbuild/karura-runtime/karura_runtime.compact.compressed.wasm --chain=karura-dev on-runtime-upgrade snap -s karura-latest.snap

try-runtime on acala

# Use a live chain to run the migration test.
# Add `--pallet module_name` can specify the module.
make try-runtime-acala

# Create a state snapshot to run the migration test.
# Add `-palet module_name` can specify the module.
cargo run --features with-acala-runtime --features try-runtime -- try-runtime --runtime existing create-snapshot --uri wss://acala.api.onfinality.io:443/public-ws acala-latest.snap

# Use a state snapshot to run the migration test.
./target/release/acala try-runtime --runtime ./target/release/wbuild/acala-runtime/acala_runtime.compact.compressed.wasm --chain=acala-dev on-runtime-upgrade snap -s acala-latest.snap

9. Run local testnet with parachain-launch

Build RelayChain and Parachain local testnet to develop.

cd launch

# install dependencies
yarn

# generate docker-compose.yml and genesis
# NOTE: If the docker image is not the latest, need to download it manually.
# e.g.: docker pull acala/karura-node:latest
# karura testnet:
yarn start generate
# karura-bifrost testnet:
yarn start generate --config=karura-bifrost.yml

# start relaychain and parachain
cd output
# NOTE: If regenerate the output directory, need to rebuild the images.
docker-compose up -d --build

# list all of the containers.
docker ps -a

# track container logs
docker logs -f [container_id/container_name]

# stop all of the containers.
docker-compose stop

# remove all of the containers.
docker-compose rm

# NOTE: If you want to clear the data and restart, you need to clear the volumes.
# remove volume
docker volume ls
docker volume rm [volume_name]
# prune all volumes
docker volume prune

10. Build For Release

For release artifacts, a more optimized build config is used. This config takes around 2x to 3x longer to build, but produces a more optimized binary to run.

make build-release

11. Setup Local EVM+ Test Environment

To set up a basic local network you need two things running locally, a node and the eth-rpc-adapter. Setup each service in their respective terminals and then you are free to use your favorite EVM tools locally! (ex: hardhat)

Setting up local node

Compile the node from source code:

make run

Note: You may need normal block production for certain workflow, use command below to run node without instant-sealing flag

cargo run --features with-mandala-runtime -- --dev -lruntime=debug

Run node using docker:

docker run -it --rm -p 9944:9944 -p 9933:9933 ghcr.io/acalanetwork/mandala-node:master --dev --ws-external --rpc-port=9933 --rpc-external --rpc-cors=all --rpc-methods=unsafe --tmp -levm=debug --instant-sealing

Setting up eth-rpc-adapter

npx @acala-network/eth-rpc-adapter -l 1

Note: If your use case needs eth_getLogs rpc call, then you need to have a subquery instance to index the local chain. For this case, follow the tutorial found here: Local Network Tutorial

acala's People

Contributors

0xthreebody avatar alfellati avatar bette7 avatar brettkolodny avatar cuardaigh avatar dependabot[bot] avatar ermalkaleci avatar ferrell-code avatar herryho avatar jasl avatar justinphamnz avatar omahs avatar qwer951123 avatar roynalnaruto avatar sander2 avatar sasha-nechaiev avatar shaunxw avatar shengda avatar shunjizhan avatar strawberryflavor avatar syan095 avatar tolak avatar ukby1234 avatar wangjj9219 avatar warfollowsme avatar xiaolou86 avatar xiaoxianboy avatar xlc avatar zjb0807 avatar zqhxuyuan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

acala's Issues

airdrop module

  • module-airdrop

    • Trait
      • CurrencyId
    • Stroage
      • Airdrops: double_map AccountId, CurrencyId => Balance
    • Call
      • airdrop(origin, to: AccountId, currency_id: CurrencyId, amount: Balance)
        • require root
      • update_airdrop(origin, to: AccountId, currency_id: CurrencyId, amount: Balance)
        • require root
  • module-primitives

    • Type
      • enum AirDropCurrencyId
        • KAR
        • ACA

This is used to record airdropped token in Mandala testnet.
The values from this module will be used to generate genesis of Karura / Acala Mainnet.

Accounts module update

  • Trait
    • type FreeTransferDeposit: Get<Balance>
    • type DepositCurrency: LockableCurrency
  • Storages
    • FreeTransferEnabledAccounts: map AccountId => Option<()>
  • Calls
    • fn enable_free_transfer(origin)
      • set lock with FreeTransferDeposit amount, max expire date, all reasons
      • FreeTransferEnabledAccounts::put(origin, ())
    • fn disable_free_transfers(origin)
      • remove lock
      • FreeTransferEnabledAccounts::kill(origin)
  • Module
    • update try_free_transfer
      • Check FreeTransferEnabledAccounts::exists(who) and return false if did not exist

Also

if <Module<T>>::try_free_transfer(who) {
true
} else {
false
}

is same as <Module<T>>::try_free_transfer(who)

Validator node panicked

Running the docker container for validators, I had the following panic in my logs:

====================

Version: 0.2.8-36e791e-x86_64-linux-gnu

   0: sp_panic_handler::set::{{closure}}
   1: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:475
   2: rust_begin_unwind
             at src/libstd/panicking.rs:375
   3: core::panicking::panic_fmt
             at src/libcore/panicking.rs:84
   4: core::option::expect_failed
             at src/libcore/option.rs:1188
   5: core::option::Option<T>::expect
             at rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libcore/option.rs:348
      std::time::Instant::duration_since
             at src/libstd/time.rs:261
      <std::time::Instant as core::ops::arith::Sub>::sub
             at src/libstd/time.rs:388
   6: <sc_network::protocol::legacy_proto::behaviour::LegacyProto<TSubstream> as libp2p_swarm::behaviour::NetworkBehaviour>::poll
   7: <sc_network::protocol::Protocol<B,S,H> as libp2p_swarm::behaviour::NetworkBehaviour>::poll
   8: <sc_network::behaviour::Behaviour<B,S,H> as libp2p_swarm::behaviour::NetworkBehaviour>::poll
   9: libp2p_swarm::ExpandedSwarm<TTransport,TBehaviour,TInEvent,TOutEvent,THandler,THandlerErr,TConnInfo>::poll_next_event
  10: futures_util::future::future::FutureExt::poll_unpin
  11: <sc_network::service::NetworkWorker<B,S,H> as core::future::future::Future>::poll
  12: futures_util::future::future::FutureExt::poll_unpin
  13: <futures_util::future::select::Select<A,B> as core::future::future::Future>::poll
  14: <futures_util::future::future::map::Map<Fut,F> as core::future::future::Future>::poll
  15: tokio::task::core::Core<T>::poll
  16: std::panicking::try::do_call
  17: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:78
  18: tokio::task::harness::Harness<T,S>::poll
  19: tokio::runtime::thread_pool::worker::GenerationGuard::run_task
  20: tokio::runtime::thread_pool::worker::GenerationGuard::run
  21: std::thread::local::LocalKey<T>::with
  22: tokio::runtime::thread_pool::worker::Worker::run
  23: tokio::task::core::Core<T>::poll
  24: std::panicking::try::do_call
  25: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:78
  26: tokio::task::harness::Harness<T,S>::poll
  27: tokio::runtime::blocking::pool::Inner::run
  28: tokio::runtime::context::enter
  29: std::sys_common::backtrace::__rust_begin_short_backtrace
  30: std::panicking::try::do_call
  31: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:78
  32: core::ops::function::FnOnce::call_once{{vtable.shim}}
  33: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
             at rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/liballoc/boxed.rs:1022
  34: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
             at rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/liballoc/boxed.rs:1022
      std::sys_common::thread::start_thread
             at src/libstd/sys_common/thread.rs:13
      std::sys::unix::thread::Thread::new::thread_start
             at src/libstd/sys/unix/thread.rs:80
  35: start_thread
  36: clone


Thread 'main-tokio-' panicked at 'supplied instant is later than self', src/libcore/option.rs:1188

Validation for unsinged Tx doesn't work.

I did some tests, I dont know why the SignedExtension doesn't work to validate unsigned tx,
In fact, the offchain worker successfully submit the unsigned tx, no errors are thrown during the submission process. But tx pool report errors:

WARN txpool  (offchain call) Error submitting a transaction to the pool: Pool(UnknownTransaction(UnknownTransaction::NoUnsignedValidator))

Then I switch deprecated ValidateUnsigned just like im-online module in polkadot, It works.

There is no example that use SignedExtension to validate unsigned tx submitted by offchain worker in srml at the moment , I will switch to ValidateUnsigned first.

AuctionManager: change withdraw / deposit

let _ = T::Currency::withdraw(T::GetNativeCurrencyId::get(), &(new_bid.0), payment);

let _ = T::Currency::deposit(T::GetNativeCurrencyId::get(), &(previous_bidder), refund);

let _ = T::Currency::deposit(auction_item.currency_id, &(auction_item.owner), deduct_amount);

The return values cannot be ignored because they may fail (shouldn't fail for deposit, but will fail on withdraw on malicious transaction).

Also better to use transfer over withdraw / deposit to avoid modify token total issuance.
The money should be transfer into / from the module's account, something like

https://github.com/paritytech/substrate/blob/73f4c118c07ff4a45c3acbbf62e8be96af96c28a/paint/treasury/src/lib.rs#L262-L264

Should have tests to ensure making bids without enough money won't be accepted.

ACA Token Allocation

  • Foundation
    • Multi-sig account owned by Acala Foundation members
  • Ecosystem
    • Treasury
    • General Council can spend this
  • Reserved
    • Multi-sig account owned by Acala Foundation members
  • IEO
    • Multi-sig account owned by Acala Foundation members until IEO finishes
  • Reward
    • Collator Reward
    • Oracle Reward
    • IPO Reward

FinancialCouncil

Depends on #60

Update all the CDP related modules to add type UpdateOrigin: EnsureOrigin and update all the set_xxx method to use this to enforce origin:

T:: UpdateOrigin::try_origin(origin).map(|_| ()).or_else(ensure_root)?;

And in runtime set UpdateOrigin to FinancialCouncil with EnsureProportionAtLeast 1/2

The result of `dex::get_supply_amount` is not correct

Because the div will discard the remainder, the result of the dex::get_supply_amount is not correct. use the incorrect result as supply amount to exchange with dex, the actual maxmum avalible target amount are most likely less than the specified target amount, lead to trading failed.

For each div in the calculation process, if there is a remainder, the quotient needs to be increased by 1 to ensure that the final result is sufficient for trading.

Accounts module

Accounts module

  • Trait
    • FreeTransferCount: Get<u8>
    • FreeTransferPeriod: Get<Moment>
    • Time: Time
    • Call: Parameter + Dispatchable<Origin=::Origin> + IsSubType<orml_currencies::Module<Self>, Self>;
    • Currency: Currency
  • Storages
    • LastFreeTransfers: map AccountId => Vec<Moment>
      • Store last FreeTransferCount free transfer time for each account
  • Module
    • impl OnReapAccount
      • clear LastFreeTransfers for reaped account
    • try_free_transfer(who: AccountId) -> bool
      • Read LastFreeTransfers, remove all the expired entries (value < now - FreeTransferPeriod), return true and append now to the list if entries count less than FreeTransferCount otherwise return false
  • ChargeTransactionPayment: SignedExtension
    • Similar to ChargeTransactionPayment in pallet-transaction-payment module
    • it additionaly call try_free_transfer and skip the withdraw fee part if the call is currencies.transfer and user still have free transfer
    • check pallet-contracts CheckBlockGasLimit for how to check call type
    • Use transaction_payment::ChargeTransactionPayment::from(tip)::compute_fee to reduce copy & paste

Update runtime to use the new ChargeTransactionPayment instead of the one in transaction payment module

We will still need additional requirements for an account to be able to make free transfer. This is still TBD.

Meta: Tx Fee & DEX integration

Depends on #58

First stage:

  • Use ACA as tx fee unit.
  • Use ACA to pay tx fee.
  • For transfer, fallback to transferred currency to pay fee using DEX for conversion.

Second stage:

  • New Signed Extension with a byte to indicate which currency to pay for fees and use DEX for conversion.

Final stage:

  • Use AUSD as tx fee unit.
    • Still use ACA for payment.

Acala roadmaps

  • Testnet

    • Lock ACA to enable free transfer
      • Implementation
      • Integration tests
      • Docs
    • Operate oracle to earn rewards
    • Validator Staking
      • Implementation #65
    • Faucet
      • Implementation
      • Docs
    • DEX
      • Implementation
      • Integration tests
      • Docs
    • Honzon
      • Implementation
      • Integration tests
      • Docs
  • Kusama Parachain

    • IPO reward
      • Implementation
      • Integration tests
      • Docs
    • Collector reward
      • Implementation
      • Integration tests
      • Docs
    • Governance
      • Implementation
      • Docs
    • Collector Staking
      • Implementation
      • Integration tests
      • Docs
    • Oracle Operator Staking
      • Implementation
      • Integration tests
      • Docs
    • Offline detection for oracle
      • Implementation
      • Integration tests
      • Docs
    • aUSD saving rates
      • Specification
      • Implementation
      • Integration tests
      • Docs
    • ACA DEX liquidity incentive
      • Specification
      • Implementation
      • Integration tests
      • Docs
    • Open CDP incentive (should be inversely proportional to total issued aUSD)
      • Specification
      • Implementation
      • Docs

Homa Protocol Design

homa

  • module-homa

    • Type
      • enum RedeemStrategy
        • Immedately
        • Target(EraIndex)
        • WaitForUnbonding
    • Trait: module_staking_pool::Trait
    • Storage
    • Constant
    • Call
      • mint(origin, amount: StakingBalance)
        • Lock DOT and mint LDOT
      • redeem(origin, amount: LiquidBalance, strategy: RedeemStrategy)
    • Module

module-homa-council

  • Type

    • struct UnlockChunk
      • value: Balance
      • era: EraIndex
    • struct BondingLedger
      • total: Balance
        • total amount of locked LDOT
      • active: Balance
        • total amount of bonded LDOT considered for nomination
      • unlocking: Vec<UnlockChunk>
        • unlocking chunks, ordered by unlocking block
        • i.e. new unlocking chunk always added to end, unlocked chunk removed from front
  • Trait

    • type Currency: BasicLockableCurrency
    • type PolkadotAccountId
    • type Bridge: PolkadotBridgeState
  • Storage

    • Nominations: map AccountId => Vec<PolkadotAccountId>
    • Ledger: map AccountId => BondingLedger
    • Votes: map PolkadotAccountId => Balance
    • Nominees: Vec<PolkadotAccountId>
  • Constant

    • NominateesCount: Get<u32>
    • MinBondThreshold: Get<Balance>
  • Call

    • bond(origin, amount: Balance)
      • lock LDOT
      • total bonded amount must be more than MinBondThreshold
    • unbond(origin, amount: Balance)
      • move LDOT to unlocking
      • total bonded amount after unbond must be more than MinBondThreshold or zero
      • if all fund are unbonded, remove nomination
    • rebond(origin, amount: Balance)
    • withdraw_unbond(origin)
      • withdraw unlocked LDOT
    • nominate(origin, targets: Vec<PolkadotAccountId>)
      • ensure_signed
      • ensure have bonded LDOT
    • chill(origin)
      • remove nominations
  • Module

    • rebalance()
    • impl NomineesProvider
      • Read all Votes and find top NominateesCount
    • impl OnNewEra
      • rebalance()
  • module-staking-pool

    • Type
      • trait NomineesProvider
        • nominees(): Vec<AccountId>
      • struct UnlockChunk
        • value: StakingBalance
        • era: EraIndex
        • claimed: StakingBalance
      • struct StakingLedger
        • total_bonded: StakingBalance
        • active: StakingBalance
        • unlocking: Vec<UnlockChunk>
        • free: StakingBalance
      • trait OnCommission
        • on_commission(amount: Balance)
    • Trait
      • type StakingCurrency: BasicCurrency
      • type LiquidCurrencyId: BasicCurrency
      • type Bridge: PolkadotBridge
      • type Nominees: NomineesProvider
      • type OnCommission: OnCommission
    • Storage
      • ClaimedUnlockChunk: double_map EraIndex, AccountId => Amount
      • Ledger: StakingLedger
    • Constant
      • MaxBondRatio: Ratio
      • MinBondRatio: Ratio
      • MaxClaimFee: Rate
      • Commission: Rate
    • Call
      • claim_payout(origin, amount: StakingBalance, proof: Vec<u8>)
        • claim a payout by submiting payout amount and a proof
        • during testnet phase, ensure_root, our bot will submit this every era
        • with real Polkadot bridge, verify proof matches to amount
        • comission is taken from amount
    • Module
      • rebalance()
        • bonded_percent = Ledger.total_bonded / (Ledger.total_bonded + Ledger.free)
        • if bonded_percent < MinBondPercent
          • bond more
        • if bonded_percent > MaxBondPercent
          • unbond some
      • bond(amount: StakingBalance)
      • unbond(amount: StakingBalance)
      • claim(amount: LiquidBalance, era: EraIndex)
      • claim_amount_percent(amount: StakingBalance, era: EraIndex) -> Ratio
        • free = Bridge::balance() - Bridge::ledger().total
        • available = Bridge::ledger().unlocking.map(|x| x.era <= era).sum()
        • amount / (free + available)
      • claim_period_percent(era: EraIndex) -> Ratio
        • (era - Bridge::current_era()) / BondingDuration
      • claim_fee(amount: StakingBalance, era: EraIndex)
        • TODO
      • impl OnNewEra
        • on_new_era
          • rebalance()
          • Bridge::nominate()
  • module-polkadot-bridge

    • Type
      • trait PolkadotBridgeType
        • type BondingDuration: Get<EraIndex>
        • type EraLength: Get<BlockNumber>
        • type PolkadotAccountId
      • trait PolkadotBridgeCall: PolkadotBridgeType
        • bond_extra(amount: Balance)
        • unbond(amount: Balance)
        • rebond(amount: Balance)
        • withdraw_unbonded()
        • nominate(targets: Vec<PolkadotAccountId>)
        • transfer(to: PolkadotAccountId, amount: Balance)
        • payout_nominator()
      • trait PolkadotBridgeState
        • ledger(): StakingLedger
        • balance(): Balance
        • current_era(): EraIndex
      • trait PolkadotBridge: PolkadotBridgeCall + PolkadotBridgeState
      • trait OnNewEra
        • on_new_era(era: EraIndex)
    • Trait
      • type DotCurrency: BasicCurrency
      • type OnNewEra: OnNewEra
      • type MockRewardPercent: Get<Permill>
      • type BondingDuration: Get<EraIndex>
      • type EraLength: Get<BlockNumber>
    • Constants
      • MockRewardPercent: Permill
      • BondingDuration: EraIndex
      • EraLength: BlockNumber
    • Storage
      • Bonded: Balance
      • Available: Balance
      • Unbonding: Vec<(Balance, BlockNumber)>
      • CurrentEra: EraIndex
      • ForcedEra: Option<BlockNumber>
    • Call
      • simulate_slash(origin, amount: Balance)
        • ensure_root
        • Bonded -= amount
        • for testing only
      • simualte_receive(origin, to: AccountId, balance: Amount)
        • ensure_root
        • DotCurrency::deposit(to, balance)
      • simulate_redeem(origin, to: PolkadotAccountId, balance: Amount)
        • ensure_signed
        • DotCurrency::withdraw(origin, balance)
      • force_era(origin, at: BlockNumber)
        • ensure_root
        • ForcedEra = at
    • Module
      • impl PolkadotBridge
      • on_finalize
        • if now % EraLength == 0 || if now === ForcedEra
          • CurrentEra++
          • OnNewEra::on_new_era(CurrenrEra)

Code cleanup

  • cdp_engine
    • Add DefaultLiquidationPenalty
    • Implement custom getter that returning non-option type
      • e.g. fn liquidation_ratio(currency: CurrencyId) -> Ratio
  • cdp_treasury
    • add fn set_collateral_auction_maximum_size
    • and remove collateral auction maximum size from set_debit_and_surplus_handle_params

Impl RPC api for dex module

The calculation related to exchange in dex is complicated, impl rpc api for dex module so that front-end developers don't need to know the specific calculation rules

Add accounts module to runtime

FreeTransferCount: 3
FreeTransferPerioid: 24 hours
FreeTransferDeposit: 1 DOLLAR

Make sure replace ChargeTransactionPayment as well

Emergency shutdown

这里只考虑关停流程,如何启动关停流程属于治理设计的一部分,另行考虑

关停单一资产:

当某单个资产因为某些风险太高,可以启动关停单一资产流程

流程:

  • 停止接受资产
    • 通过调整资产债务硬顶,不再接受更多该资产的抵押
    • 用户可以在这个阶段赎回资产
  • 提升liquidation ratio
    • 每个block都提升liquidation ratio
    • 如果用户不主动关闭仓位的话,渐渐的所有仓位都会根据抵押率先后被平仓
  • 强制清仓
    • 一定时间后,自动拍卖掉所有剩下的仓位

全局关停:

因为某种恶性原因,比如严重资金安全漏洞,恶意治理提案被通过等,可以启用全局关停流程避免情况恶化

流程:

  • 快照最新Oracle价格
  • 停止接受资产
    • 不再接受更多该资产的抵押
    • 仓位持有者可以将超额抵押的资产取回,不可直接赎回剩余部分
  • 清算系统盈余 / 亏损
  • aUSD持有者根据持有比率,按比例兑换全局抵押资产
  • (以后)过渡为平行线程,只保留赎回资产功能

Acala Runtime Design

Acala Runtime Design

注:

  • 很多类型的泛型参数我跳过了,实现的时候按需求添加
  • 完全没有考虑事件,这个可以最后根据前端和看护机的需求添加
  • 我们会使用 offchain_worker 来实现看护机,这部分设计会陆续添加
  • 第二版会加入 uniswap 类型的 DEX,作为价钱来源之一,参与竞拍,无缝转换各种手续费
  • 各种需要 root 权限来修改链上变量参数的方法我就不细写了,每个变量提供一个 set_xxx 就好

模块结构:

每个模块都分为这些部分:

  • Types
    • 所有模块定义的 Rust 类型,包括了 trait, struct, enum, type
  • Trait
    • Substrate Runtime Module Trait 部分,所有可以传入的类型参数
  • Storage
    • decl_storage 部分,链上存储
  • Call
    • decl_module 中的 dispatchable call 部分,用户可以调用发起的交易
  • Module
    • impl Module 部分,对其他模块的 Rust 接口,还有 module hooks (on_initialize / on_finalize / offchain_worker)
    • Module: TraitName, TraitNameTwo 的意思是实现了 TraitName 和 TraitNameTwo 这两个接口
  • Event
    • decl_event 部分,定义链上可以发生的事件

Common Modules (ORML)

  • utilities
  • traits
    • 包含了所有通用接口定义
    • Types
      • trait BasicCurrency
        • 简化版单资产管理接口
        • type Balance
        • fn transfer
        • fn deposit
        • fn withdraw
      • trait BasicCurrencyExtended: MultiCurrency
        • 增加了 Amount,支持负数
        • type Amount: TryInto<Balance> + TryFrom<Balance>
        • fn update_balance(who: AccountId, amount: Amount)
      • trait MultiCurrency
        • 多资产管理接口
        • type CurrencyId
        • type Balance
        • fn transfer
        • fn deposit
        • fn withdraw
      • trait MultiCurrencyExtended: MultiCurrency
        • 增加了 Amount,支持负数
        • type Amount: TryInto<Balance>, TryFrom<Balance>
        • fn update_balance(who: AccountId, currency_id: CurrencyId, amount: Amount)
      • trait OnNewData<Key, Value>
        • 通用新数据钩子
        • fn on_new_data(key: Key, value: Value)
      • trait DataProvider<Key, Value>
        • 通用数据读取接口
        • fn get(key: Key) -> Option<Value>
      • trait PriceProvider<CurrencyId, Price>
        • 价钱数据接口
        • fn get_price(base: CurrencyId, quote: CurrencyId): Option<Price>
      • struct AuctionInfo
        • 拍卖信息
        • bid: Option<(AccountId, Balance)>
        • end: Option<BlockNumber>
      • trait Auction
        • 通用拍卖接口
        • type AuctionId
        • type Balance
        • fn auction_info(id: AuctionId): AuctionInfo
        • fn update_auction(id: AuctionId, info: AuctionInfo)
        • fn new_auction(start: BlockNumber, end: Option<BlockNumber>): AuctionId
      • struct OnNewBidResult
        • accept_bid: bool
          • 是否接受出价,比如如果增加金额低于要求
        • auction_end: Option<Option<BlockNumber>>
          • 是否修改拍卖结束时间
      • trait AuctionHandler
        • 拍卖接口钩子
        • fn on_new_bid(now: BlockNumber, id: AuctionId, new_bid: (AccountId, Balance), last_bid: Option<(AccountId, Balance)>): OnNewBidResult
          • 接收到新竞价,通过返回值决定是否接受竞价和修改拍卖截止时间
          • 这里要预先提取当前赢家资金,返回上个赢家资金
        • fn on_auction_ended(id: AuctionId, winner: Option<(AccountId, Balance)>)
          • 拍卖结束
  • tokens
    • 多资产接口实现
    • Trait
      • CurrencyId
    • Call
      • transfer(origin, currency_id: CurrencyId, to: AccountId, value: Balance)
    • Module: MultiCurrencyExtended
  • currencies
    • 用来聚合 srml-balances 和 orml-tokens
    • Trait
      • NativeCurrency: Currency
      • NativeCurrencyId: Get<CurrencyId>
      • MultiCurrency: MultiCurrencyExtended<CurrencyId = Self::MultiCurrencyId>
    • Call
      • transfer(origin, currency_id: CurrencyId, to: AccountId, value: Balance)
    • Module: MultiCurrencyExtended
  • oracle
    • 预言机模块,实现通用数据接口
    • Trait
      • Key
      • Value
      • OperatorOrigin: EnsureOrigin
      • OnNewData: OnNewData<Key, Value>
    • Call
      • feed_data(origin, key: Key, value: Value)
    • Module: DataProvider<Key, Value>
  • prices
    • 价钱模块,价钱数据接口实现
    • Trait
      • CurrencyId
      • Source: DataProvider<CurrencyId, Price>
    • Module: PriceProvider<CurrencyId, Price>
  • auction
    • 通用拍卖接口实现。具体的货品交割由 AuctionHandler 的实现来执行
    • Trait
      • Balance
      • Handler: AuctionHandler
    • Storage
      • Actions: map AuctionId => Option<AuctionInfo>
      • AuctionEndTime: map (BlockNumber, Option<AuctionId>) => Option<LinkedItem<AuctionId>>
        • 每个区块中会截止的拍卖,用链表存储
      • AuctionCounts: AuctionId
    • Call
      • bid(origin, id: AuctionId, value: Balance)
    • Module: Auction
      • on_finalize
        • 遍历 AuctionEndTime ,结束拍卖

Acala Runtime Modules

  • support
    • 辅助模块,包含同样帮助方法和常用类型
    • Types
      • trait RiskManager<CurrencyId, Balance, DebitBalance>
        • 风险评估接口
        • fn requiredCollateralRatio(currency_id: CurrencyId): Ratio
          • 要求抵押率,用户开仓,调整仓位,不能让抵押率低于这个值
        • fn check_position_adjuestment(currency_id: CurrencyId, collaterals: SignedBalance<Balance>, debits: SignedBalance<DebitBalance>): Result
          • 检验这个操作后抵押率是否足够,足够的话 Ok, 不够的话 Err
      • type Price = Fixed128
      • type ExchangeRate = Fixed128
      • type Ratio = Fixed128
  • debits
    • 债务管理模块,负责处理债务单位的增减
    • Trait
      • Currency: BasicCurrencyExtended
        • 稳定币资产
      • DebitBalance
        • 债务资产单位
      • Convert: Convert<(CurrencyId, Balance), DebitBalance> + Convert<(CurrencyId, DebitBalance), Balance>
        • 负责不同资产的债务单位和稳定币直接的转换
    • Module: MultiCurrencyExtended
      • 实现了多资产接口,但所有交易都会根据债务单位和稳定币的汇率转换成稳定币资产,然后交易稳定币资产
  • vaults
    • 负载与资产管理模块
    • Trait
      • Currency: MultiCurrencyExtended
        • 多资产接口
      • DebitCurrency: MultiCurrencyExtended
        • 债务资产接口
      • PriceSource: PriceProvider<CurrencyId, Price>
        • 价钱来源
      • RickManager: RickManager<CurrencyId>
        • 风控接口
    • Storage
      • Debits: double_map AccountId, CurrencyId => DebitBalance
        • 用户对映资产 CDP 的债务金额
      • Collaterals: double_map AccountId, CurrencyId => Balance
        • 用户对映资产的 CDP 的抵押物金额
      • TotalDebits: map CurrencyId => DebitBalance
      • TotalCollaterals: map CurrencyId => Balance
    • Module
      • collateral_ratio(who: AccountId, currency_id: CurrencyId): Option<Fixed128>
        • 用户对映资产的 CDP 的抵押率
      • update_position(who: AccountId, currency_id: CurrencyId, collaterals: SignedBalance<Balance>, debits: SignedBalance<DebitBalance>): Result
        • 修改 CDP。对债务的变动会直接修改用户 aUSD 余额,对抵押物的变动也会直接修改用户抵押物的余额
  • auction_manager
    • 负责管理不同类型的拍卖
    • 需要讨论的几个点
      • 拍卖时长硬顶软顶
      • 大量抵押物同时拍卖的时候是否需要把时间稍微分离,防止对市场过大的冲击
    • 需要添加的功能
      • 盈余拍卖
      • 负债拍卖
    • Types
      • struct AuctionItem
        • 不会变的信息放在这个结构里面,会变的单独放,比如截止时间
        • owenr: AccountId
        • currency_id: CurrencyId
        • amount: Balance
        • target: Balance
        • start_time: BlockNumber
      • trait AuctionManagerHandler
        • on_auction_end(currency_id: CurrencyId, target: Balance, bid: Balance)
          • 反馈拍卖结果,这个主要是用来报告债务的。如果达标了,auction_manager 自己处理相关的所有资产转移
    • Trait
      • Currency: MultiCurrencyExtended
      • Auction: Auction
      • Handler: AuctionManagerHandler
    • Storage
      • MaximumAuctionSize: map CurrencyId => Balance
        • 一次拍卖最大数额,超过这个的会被拆分为多次拍卖
      • Auctions: map AuctionId => Option<AuctionItem>
        • 所有当前拍卖,结束的清空
      • AuctionWinner: map AuctionId => Option<AccountId, Balance>
        • 当前竞拍赢家,拍卖结束后清空
    • Constants
      • MinimumIncrementSize: Permill
        • 每次拍卖最低增加幅度百分比,两次竞拍价格至少要增加 max(target, last_bid) * MinimumIncrementSize
      • AuctionTimeToClose: BlockNumber
        • 每次成功竞拍后,拍卖截止时间调整为当前时间加上这个值
      • AuctionDurationSoftCap: BlockNumber
        • 如果拍卖持续了这么久还未结束,竞价最低增加金额翻倍,截止延长时间减半
        • 这个机制实现前可以详细讨论下,看看需不需要调整
    • Module: AuctionHandler
      • new_collateral_auction(who: AccountId, currency_id: CurrencyId, amount: Balance, target: Balance)
        • 转移用户CDP中抵押品
        • 转移债务
        • 如果数额太大,拆分成多个拍卖
  • cdp_engine
    • CDP 引擎
    • Types
      • struct DebitExchangeRateConvertor: Convert<(CurrencyId, Balance), DebitBalance> + Convert<(CurrencyId, DebitBalance), Balance>
        • 进行不同资产的债务单位和稳定币直接的转换
    • Trait: auction_manager::Trait + vaults::Trait
      • Currency: MultiCurrencyExtended
      • PriceSource: PriceProvider<CurrencyId, Price>
        • 价钱来源
      • CollateralCurrencyIds: Get<static '&[CurrencyId]>
        • 可抵押资产编号
      • StableCurrencyId: Get<CurrencyId>
        • 稳定币编号
      • Auction: Auction
        • 拍卖模块接口
    • Constants
      • GlobalStabilityFee: Permill
        • 全局稳定费率
      • DefaultLiquidationRatio: Ratio
        • 默认触发清算抵押率
    • DefaultLiquidationPenalty: Permill
      • 默认清算惩罚费率
      • DefaultDebitExchangeRate: ExchangeRate
        • 默认起始负债汇率
      • MinimumDebitValue: Balance
        • 债务下限,单位为 aUSD。CDP 通过操作后,要么债务为0,要么 aUSD 价值必须大于这个数值
    • Storage
      • StabilityFee: map CurrencyId => Option<Permill>
        • 对映资产的稳定费率,加上全局稳定费率得到真正费率
      • LiquidationRatio: map CurrencyId => Option<Ratio>
        • 对映资产的触发清算抵押率
      • LiquidationPenalty: map CurrencyId => Option<Permill>
        • 对映资产的清算惩罚费率
      • RequiredCollateralRatio: map CurrencyId => Option<Ratio>
        • 对映资产的要求抵押率,用户无法主动把抵押率降低与这个值
      • DebitExchangeRate: map CurrencyId => Option<ExchangeRate>
        • 对映资产的负债汇率
      • MaximumTotalDebitValue: map CurrencyId => Balance
        • 对映资产的总共债务上限
    • Module: RiskManager, AuctionManagerHandler
      • 实现了风控接口,验证CDP抵押率,债务上限,债务下限
      • 实现了拍卖钩子,结算处理拍卖结果
      • on_finalize
        • 根据当前 StabilityFee 更新每个 DebitExchangeRate
  • honzon
    • Trait: cdp_engine::Trait
    • Storage
      • Authorizations: double_map AccountId, (CurrencyId, AccountId) => bool
        • 仓位权限授权
    • Call
      • update_vault(origin, currency_id: CurrencyId, collateral: Amount, debits: Amount)
        • 修改仓位
        • 添加 / 减少 抵押品会从用户账上 转入 / 转出 抵押品
        • 添加 / 减少 债务会从用户稳定币账户执行对映的 铸币 / 烧币
      • transfer_vault(origin, currency_id: CurrencyId, to: AccountId, collateral: Amount, debits: Amount)
        • 转移仓位,必须有对方授权
      • authorize(origin, currency_id: CurrencyId, to: AccountId)
        • 授权给对方
      • unauthorize(origin, currency_id: CurrencyId, to: AccountId)
        • 取消授权
      • unauthorize_all(origin)
        • 取消全部授权
  • primitives
    • Types
      • enum TokensCurrencyId
        • AUSD = 1
        • DOT
        • XBTC
      • enum CurrencyId
        • ACA = 0
        • AUSD
        • DOT
        • XBTC
  • Runtime
    • Balances
    • Tokens
      • CurrencyId = CurrencyId
    • Currencies
      • NativeCurrency = Balances
      • NativeCurrencyId = Get<CurrencyId::ACA>
      • MultiCurrency = Tokens
    • Oracle
    • Auction
      • Handler = AuctionManager
    • Prices
      • CurrencyId = CurrencyId
      • Source = Oracle
    • Debits
      • Currency = tokens::NativeCurrency
      • DebitBalance = Balance
      • Convert = cdp_engine::DebitExchangeRateConvertor
    • Vaults
      • Currency = Tokens
      • DebitCurrency = Debits
      • PriceSource = Prices
      • RickManager = CDPEngine
    • AuctionManager
      • Auction = Auction
      • Handler = CDPEngine
    • CDPEngine
      • Currency = Currencies
      • PriceSource = Prices
      • CollateralCurrencyIds = [CurrencyId::DOT, CurrencyId::XBTC]
    • StableCurrencyId: Currency::AUSD
      • Auction: Auction
    • Honzon

Panic with "Transaction has a bad signature"

sentry_1       | ====================
sentry_1       |
sentry_1       | Version: 0.3.1-83c13dc-x86_64-linux-gnu
sentry_1       |
sentry_1       |    0: sp_panic_handler::set::{{closure}}
sentry_1       |    1: std::panicking::rust_panic_with_hook
sentry_1       |              at src/libstd/panicking.rs:475
sentry_1       |    2: std::panicking::begin_panic
sentry_1       |    3: frame_executive::Executive<System,Block,Context,UnsignedValidator,AllModules>::execute_block
sentry_1       |    4: <acala_runtime::Runtime as sp_api::runtime_decl_for_Core::Core<sp_runtime::generic::block::Block<sp_runtime::generic::header::Header<u32,sp_runtime::traits::BlakeTwo256>,sp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic<<pallet_indices::Module<acala_runtime::Runtime> as sp_runtime::traits::StaticLookup>::Source,acala_runtime::Call,sp_runtime::MultiSignature,(frame_system::CheckVersion<acala_runtime::Runtime>, frame_system::CheckGenesis<acala_runtime::Runtime>, frame_system::CheckEra<acala_runtime::Runtime>, frame_system::CheckNonce<acala_runtime::Runtime>, frame_system::CheckWeight<acala_runtime::Runtime>, orml_oracle::CheckOperator<acala_runtime::Runtime>, module_accounts::ChargeTransactionPayment<acala_runtime::Runtime>)>>>>::execute_block
sentry_1       |    5: sp_api::runtime_decl_for_Core::execute_block_native_call_generator::{{closure}}
sentry_1       |    6: std::panicking::try::do_call
sentry_1       |    7: __rust_maybe_catch_panic
sentry_1       |              at src/libpanic_unwind/lib.rs:78
sentry_1       |    8: std::thread::local::LocalKey<T>::with
sentry_1       |    9: std::thread::local::LocalKey<T>::with
sentry_1       |   10: sp_state_machine::StateMachine<B,H,N,Exec>::execute_aux
sentry_1       |   11: sp_state_machine::StateMachine<B,H,N,Exec>::execute_using_consensus_failure_handler
sentry_1       |   12: <sc_client::call_executor::LocalCallExecutor<B,E> as sc_client_api::call_executor::CallExecutor<Block>>::contextual_call
sentry_1       |   13: <sc_client::client::Client<B,E,Block,RA> as sp_api::CallApiAt<Block>>::call_api_at
sentry_1       |   14: sp_api::runtime_decl_for_Core::execute_block_call_api_at
sentry_1       |   15: sp_api::Core::execute_block
sentry_1       |   16: <&sc_client::client::Client<B,E,Block,RA> as sp_consensus::block_import::BlockImport<Block>>::import_block
sentry_1       |   17: <sc_finality_grandpa::import::GrandpaBlockImport<BE,Block,Client,SC> as sp_consensus::block_import::BlockImport<Block>>::import_block
sentry_1       |   18: <sc_consensus_babe::BabeBlockImport<Block,Client,Inner> as sp_consensus::block_import::BlockImport<Block>>::import_block
sentry_1       |   19: sp_consensus::import_queue::import_single_block
sentry_1       |   20: <futures_util::future::poll_fn::PollFn<F> as core::future::future::Future>::poll
sentry_1       |   21: futures_util::future::future::chain::Chain<Fut1,Fut2,Data>::poll
sentry_1       |   22: <futures_util::future::poll_fn::PollFn<F> as core::future::future::Future>::poll
sentry_1       |   23: futures_executor::thread_pool::PoolState::work
sentry_1       |   24: std::sys_common::backtrace::__rust_begin_short_backtrace
sentry_1       |   25: std::panicking::try::do_call
sentry_1       |   26: __rust_maybe_catch_panic
sentry_1       |              at src/libpanic_unwind/lib.rs:78
sentry_1       |   27: core::ops::function::FnOnce::call_once{{vtable.shim}}
sentry_1       |   28: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
sentry_1       |              at rustc/f3e1a954d2ead4e2fc197c7da7d71e6c61bad196/src/liballoc/boxed.rs:1022
sentry_1       |   29: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
sentry_1       |              at rustc/f3e1a954d2ead4e2fc197c7da7d71e6c61bad196/src/liballoc/boxed.rs:1022
sentry_1       |       std::sys_common::thread::start_thread
sentry_1       |              at src/libstd/sys_common/thread.rs:13
sentry_1       |       std::sys::unix::thread::Thread::new::thread_start
sentry_1       |              at src/libstd/sys/unix/thread.rs:80
sentry_1       |   30: start_thread
sentry_1       |   31: clone
sentry_1       |
sentry_1       |
sentry_1       | Thread 'import-queue-worker-0' panicked at 'Transaction has a bad signature', /root/.cargo/git/checkouts/substrate-7e08433d4c370a21/29cee59/frame/executive/src/lib.rs:272
sentry_1       |
sentry_1       | This is a bug. Please report it at:
sentry_1       |
sentry_1       | 	https://github.com/AcalaNetwork/Acala/issues
sentry_1       |
sentry_1       | 2020-03-01 09:49:49.712 import-queue-worker-0 WARN sc_client::client  Block prepare storage changes error:
sentry_1       | Execution(RuntimePanicked("Transaction has a bad signature"))

I believe this is the reason of stopped finality

Chain stop finalizing blocks

Have a problem in acala testnet now that chain stoped to finalize block, The node log is:

2020-02-28 10:34:56 Handler initialization process is too long with PeerId("QmSveKjM2cY2VNoZ6Lq7w71XmDzPSUFpZsoXrgtP3myn49")
2020-02-28 10:34:59 Idle (12 peers), best: #21962 (0xeec4…cf2c), finalized #15553 (0x8275…58ff), ⬇ 4.4kiB/s ⬆ 5.6kiB/s

====================

Version: 0.3.0-f887701-x86_64-linux-gnu

   0: sp_panic_handler::set::{{closure}}
   1: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:475
   2: std::panicking::begin_panic
   3: frame_executive::Executive<System,Block,Context,UnsignedValidator,AllModules>::execute_block
   4: <acala_runtime::Runtime as sp_api::runtime_decl_for_Core::Core<sp_runtime::generic::block::Block<sp_runtime::generic::header::Header<u32,sp_runtime::traits::BlakeTwo256>,sp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic<<pallet_indices::Module<acala_runtime::Runtime> as sp_runtime::traits::StaticLookup>::Source,acala_runtime::Call,sp_runtime::MultiSignature,(frame_system::CheckVersion<acala_runtime::Runtime>, frame_system::CheckGenesis<acala_runtime::Runtime>, frame_system::CheckEra<acala_runtime::Runtime>, frame_system::CheckNonce<acala_runtime::Runtime>, frame_system::CheckWeight<acala_runtime::Runtime>, orml_oracle::CheckOperator<acala_runtime::Runtime>, module_accounts::ChargeTransactionPayment<acala_runtime::Runtime>, module_cdp_engine::AutomaticLiquidationValidation<acala_runtime::Runtime>)>>>>::execute_block
   5: sp_api::runtime_decl_for_Core::execute_block_native_call_generator::{{closure}}
   6: std::panicking::try::do_call
   7: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:78
   8: std::thread::local::LocalKey<T>::with
   9: std::thread::local::LocalKey<T>::with
  10: sp_state_machine::StateMachine<B,H,N,Exec>::execute_aux
  11: sp_state_machine::StateMachine<B,H,N,Exec>::execute_using_consensus_failure_handler
  12: <sc_client::call_executor::LocalCallExecutor<B,E> as sc_client_api::call_executor::CallExecutor<Block>>::contextual_call
  13: <sc_client::client::Client<B,E,Block,RA> as sp_api::CallApiAt<Block>>::call_api_at
  14: sp_api::Core::execute_block
  15: <&sc_client::client::Client<B,E,Block,RA> as sp_consensus::block_import::BlockImport<Block>>::import_block
  16: <sc_finality_grandpa::import::GrandpaBlockImport<B,E,Block,RA,SC> as sp_consensus::block_import::BlockImport<Block>>::import_block
  17: <sc_consensus_babe::BabeBlockImport<Block,Client,Inner> as sp_consensus::block_import::BlockImport<Block>>::import_block
  18: sp_consensus::import_queue::import_single_block
  19: <futures_util::future::poll_fn::PollFn<F> as core::future::future::Future>::poll
  20: futures_util::future::future::chain::Chain<Fut1,Fut2,Data>::poll
  21: <futures_util::future::poll_fn::PollFn<F> as core::future::future::Future>::poll
  22: futures_executor::thread_pool::PoolState::work
  23: std::sys_common::backtrace::__rust_begin_short_backtrace
  24: std::panicking::try::do_call
  25: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:78
  26: core::ops::function::FnOnce::call_once{{vtable.shim}}
  27: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/liballoc/boxed.rs:1022
  28: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/liballoc/boxed.rs:1022
      std::sys_common::thread::start_thread
             at src/libstd/sys_common/thread.rs:13
      std::sys::unix::thread::Thread::new::thread_start
             at src/libstd/sys/unix/thread.rs:80
  29: start_thread
  30: __clone


Thread 'import-queue-worker-0' panicked at 'Transaction has a bad signature', /root/.cargo/git/checkouts/substrate-7e08433d4c370a21/41bb219/frame/executive/src/lib.rs:272

This is a bug. Please report it at:

	https://github.com/AcalaNetwork/Acala/issues

2020-02-28 10:34:59 Block prepare storage changes error:
Execution(RuntimePanicked("Transaction has a bad signature"))
2020-02-28 10:35:04 Idle (12 peers), best: #21962 (0xeec4…cf2c), finalized #15553 (0x8275…58ff), ⬇ 4.9kiB/s ⬆ 4.9kiB/s
2020-02-28 10:35:08 offchain worker start at block: 21962 execute automatic liquidation for collateral: CurrencyId::DOT

We think this problem may be caused by an unsigned transaction submitted by offchain worker.

As we have not yet designed a reward mechanism of running offchain worker, there's no Authority,and no signature verification by Authority for unsigned tx :

fn submit_unsigned_liquidation_tx(currency_id: CurrencyIdOf<T>, who: T::AccountId) -> Result<(), OffchainErr> {
let call = Call::<T>::liquidate(currency_id, who);
T::SubmitTransaction::submit_unsigned(call).map_err(|_| OffchainErr::SubmitTransaction)?;
Ok(())
}

And the validation for submitted tx we use SingedExtension instead of deprecated unsigned::ValidateUnsigned :

impl<T: Trait + Send + Sync> SignedExtension for AutomaticLiquidationValidation<T> {
const IDENTIFIER: &'static str = "AutomaticLiquidationValidation";
type AccountId = T::AccountId;
type Call = <T as Trait>::Call;
type AdditionalSigned = ();
type DispatchInfo = DispatchInfo;
type Pre = ();
fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> {
Ok(())
}
fn validate(
&self,
_who: &Self::AccountId,
call: &Self::Call,
_info: Self::DispatchInfo,
_len: usize,
) -> TransactionValidity {
let call = match call.is_sub_type() {
Some(call) => call,
None => return Ok(ValidTransaction::default()),
};
match call {
Call::<T>::liquidate(currency_id, account_id) => {
// check cdp is unsafe
if !<Module<T>>::is_unsafe_cdp(*currency_id, account_id) {
InvalidTransaction::Stale.into()
} else {
let mut valid_tx = ValidTransaction::default();
valid_tx.priority = TransactionPriority::max_value();
Ok(valid_tx)
}
}
_ => Ok(ValidTransaction::default()),
}
}
}

There's no InvalidTransaction::BadProof will be throughed by above code.

Is the lack of sign verification by Authority or the incorrect validation for unsigned tx cause this problem? or something else

Use Saturating / UniqueSaturatedFrom / UniqueSaturatedInto to simplify code

Review the code and do following changes:

  • Change x.unwrap_or(0.into)
    • To x.unwrap_or_default()
  • Change let x = TryInto::<u128>::try_into(x).unwrap_or(Bounded::max_value());
    • To let x: u128 = x.unique_saturated_into();
  • Change x.checked_mul(&y).unwrap_or(Bounded::max_value())
    • To x. saturating_mul(y)
  • Same for checked_div / checked_add / checked_sub / checked_mul_int

Relates: #41

Dex Module Design Draft

dex module

参考uniswap的交易模式, 以aUSD为base token进行资产的去中心化交易

Trait

  • Currency: MultiCurrency
  • Share
  • GetBaseCurrencyId: Get
  • GetExchangeFee: Get

Storage

  • LiquidityPool: double_map CurrencyId, CurrencyId => (Balance, Balance) 流动性池子
  • TotalShares: map CurrencyId => Share
  • Shares: double_map AccountId, CurrencyId => Share

Call

  • swap_tokens(origin, supply: (CurrencyId, Balance), target: (CurrencyId, Balance))
    • 用supply资产交换 target 资产, target.1 为能接受的换回的最低数量。
  • inject_liquidity(origin, currency_id: CurrencyId, amount: Balance, base_currency_amount: Balance)
    • 向流动性池子注入资金,获得股权,amount 与 base_currency_amount分别是能够支付的other token 和 base token的上限
  • extract_liquidity(origin, currency_id: CurrencyId, extract_amount: T::Share)
    • 用股权赎回流动池资金

Module: DexManager

实现 DexManager 以供拍卖模块调用

Update CurrencyId

Change existing ACA to MAC, and add ACA and KAR

ACA and KAR will be used to record airdropped tokens in the followup candy events

ACA Treasury

We cannot mint ACA directly other than the case of debit auction.

So we need an ACA Treasury holding all the not yet released tokens, and rewards should be transferred from treasury.

Details TBD. Needs to match white paper.

Claim module

Hold funds for IPO and allow IPO participants to submit their proof of participation to claim their ACA.

Proof verification needs to wait for child storage proof generation.

Details TBD.

Make all financial parameters configurable in chain spec.

This is required to ensure a newly launched network is in a working state from block one.

Like those, and maybe few more

pub SurplusAuctionFixedSize get(fn surplus_auction_fixed_size): BalanceOf<T>;
pub SurplusBufferSize get(fn surplus_buffer_size): BalanceOf<T>;
pub InitialAmountPerDebitAuction get(fn initial_amount_per_debit_auction): BalanceOf<T>;
pub DebitAuctionFixedSize get(fn debit_auction_fixed_size): BalanceOf<T>;
pub CollateralAuctionMaximumSize get(fn collateral_auction_maximum_size): map hasher(blake2_256) CurrencyIdOf<T> => BalanceOf<T>;

CDP Treasury V1

  • primitive
    • Trait
      • CDPTreasury
        • type Balance
        • fn on_debit(amount: Balance)
        • fn on_surplus(amount: Balance)
  • auction_manager
    • Trait
      • Treasury: CDPTreasury
    • storage
      • BadDebtPool
        • 删除,由 Treasury::on_debit 来代替
      • SurplusPool
        • 删除,由 Treasury::on_surplus 来代替
  • cdp_treasury
    • Trait
      • Currency: MultiCurrency
    • Storage
      • DebitPool: Balance
      • SurplusPool: Balance
    • Module: CDPTreasury
      • on_finalize
        • 抵消负债和盈余(之前在AuctionManager中实现)

初版,没有新功能,就是AuctionManager的重构
下个版本应该会添加两个配置 MAX_DEBIT MAX_SURPLUS,当负债 / 盈余太多的时候,铸造ACA并拍卖取得aUSD抵债 / 拍卖aUSD购买ACA并销毁,具体细节还需要考虑

Basic governance setup

Add and configure following modules:

  • pallet-democracy
  • pallet-collective
    • Instance1: Board
    • Instance2: FinancialCouncil
    • Instance3: OperatorCollective
  • pallet-membership
    • Instance1: BoardMembership
      • Managed by Board, EnsureProportionAtLeast 3/4
    • Instance2: FinancialCouncilMembership
      • Managed by Board, EnsureProportionAtLeast 1/2
    • Instance3: OperatorMembership
      • Managed by Board, EnsureProportionAtLeast 1/3

债务管理

债务管理

债务分为三种:

  • 全局债务
  • CDP债务
  • 系统债务 / 盈余

其中全局债务和系统债务都由 CDP Treasury 来管理,用户债务由 Vaults 管理

  • 全局债务
    • 由 CDP Treasury 模块管理
    • 增发 aUSD 增加全局债务
    • 销毁 aUSD 减少全局债务
    • 所有 aUSD 的增发烧毁都必须由国库来完成
  • 用户债务
    • 由 Vaults 模块管理
    • Valuts 管理用户债务和抵押品资产
    • 用户抵押资产铸造aUSD的流程
      • Vaults 把用户资产转移给模块账号,记录添加抵押品
      • Vaults 向 Treasury 申请资产
        • 添加全局债务
        • 铸造 aUSD
      • Vaults 添加CDP债务
    • 清偿债务赎回抵押资产流程
      • Vaults 通过 Treasury 销毁用户 aUSD
        • 减少全局债务
        • 销毁 aUSD
      • Vaults 转回抵押品给用户
  • 系统债务 / 盈余
    • 每区块收取稳定费率利息
      • 通过增加债务单位汇率来增加所有CDP债务
      • 增加的部分计入系统盈余
    • 拍卖危险资产流程
      • 没收用户CDP资产
      • 转移CDP债务进入系统债务
      • 拍卖用户资产
      • 还回多余资产
      • 拍卖所得金额作为系统盈余
    • 每周期系统盈余和债务互相对抵

全局债务金额应等于:所有CDP债务金额 + 系统债务 - 系统盈余

回购ACA流程:

  • 当系统盈余大于一定值的时候,发起回购ACA流程
  • 系统盈余(一部分?)转换成 aUSD,同时添加全局债务
  • 卖出aUSD,购买ACA
    • 具体方法待定,可以是拍卖,或者通过DEX
  • 销毁回购的ACA

增发ACA流程:

  • 当系统债务大于一定值的时候,发起增发ACA流程
  • 增发卖出ACA,购买相当于系统债务的 aUSD
    • 具体方法待定,可以是拍卖,或者通过DEX
  • 销毁 aUSD,减少相对应的全局债务
  • 减少相对应的系统债务

purge-chain panic

Version: 0.2.3-488aeec-x86_64-macos

stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /Users/xiliangchen/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.43/src/backtrace/libunwind.rs:86
      backtrace::backtrace::trace_unsynchronized
             at /Users/xiliangchen/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.43/src/backtrace/mod.rs:66
   1: backtrace::backtrace::trace
             at /Users/xiliangchen/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.43/src/backtrace/mod.rs:53
   2: backtrace::capture::Backtrace::create
             at /Users/xiliangchen/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.43/src/capture.rs:164
   3: backtrace::capture::Backtrace::new
             at /Users/xiliangchen/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.43/src/capture.rs:128
   4: sp_panic_handler::panic_hook
             at /Users/xiliangchen/.cargo/git/checkouts/substrate-7e08433d4c370a21/f3742e7/primitives/panic-handler/src/lib.rs:148
   5: sp_panic_handler::set::{{closure}}
             at /Users/xiliangchen/.cargo/git/checkouts/substrate-7e08433d4c370a21/f3742e7/primitives/panic-handler/src/lib.rs:58
   6: <std::panicking::begin_panic::PanicPayload<A> as core::panic::BoxMeUp>::get
   7: std::panicking::continue_panic_fmt
   8: std::panicking::try::do_call
   9: std::panicking::begin_panic
  10: std::panicking::begin_panic
  11: core::option::Option<T>::expect
             at /rustc/73528e339aae0f17a15ffa49a8ac608f50c6cf14/src/libcore/option.rs:345
  12: sc_service::config::Configuration<G,E>::expect_database
             at /Users/xiliangchen/.cargo/git/checkouts/substrate-7e08433d4c370a21/f3742e7/client/service/src/config.rs:255
  13: sc_cli::params::PurgeChainCmd::run
             at /Users/xiliangchen/.cargo/git/checkouts/substrate-7e08433d4c370a21/f3742e7/client/cli/src/params.rs:1128
  14: sc_cli::params::Subcommand::run
             at /Users/xiliangchen/.cargo/git/checkouts/substrate-7e08433d4c370a21/f3742e7/client/cli/src/params.rs:911
  15: sc_cli::run_subcommand
             at /Users/xiliangchen/.cargo/git/checkouts/substrate-7e08433d4c370a21/f3742e7/client/cli/src/lib.rs:263
  16: acala::command::run
             at src/command.rs:14
  17: acala::main
             at src/main.rs:22
  18: std::rt::lang_start::{{closure}}
             at /rustc/73528e339aae0f17a15ffa49a8ac608f50c6cf14/src/libstd/rt.rs:61
  19: std::panicking::try::do_call
  20: panic_unwind::dwarf::eh::read_encoded_pointer
  21: std::panicking::update_count_then_panic
  22: std::rt::lang_start
             at /rustc/73528e339aae0f17a15ffa49a8ac608f50c6cf14/src/libstd/rt.rs:61
  23: acala::main


Thread 'main' panicked at 'database must be specified', src/libcore/option.rs:1185

Must be relates to paritytech/substrate#4849

Upgrade Substrate

paritytech/substrate#4820 is a major breaking change that is going to break all SDK, frontend and bots.

Upgrade procedure:

Preparation:

  • ORML still tracking latest Substrate
  • Acala freeze ORML version, unless urgent bug fix is required
  • Mandala freeze runtime upgrade, unless urgent bug fix is required

Update code:

  • Acala upgrade ORML & Substrate version to latest that including the breaking change
  • Deployed to a private testnet
  • Upgrade SDK & console to latest version
  • Upgrade frontend & bots to use latest SDK
  • Perform testing against private testent to ensure it works

Deploy:

  • 🙏
  • Runtime upgrade Mandala
  • Deploy new frontend & console & bots
  • 🎉

Meta: 账户系统

  • 为了减少复杂度,不针对 aUSD 做特别处理
  • 交易费单位为 aUSD,保证交易费用的稳定性
  • 转账交易费收取货币为交易货币,系统自动从DEX中购买 aUSD 支付手续费 #59
  • 手续费 5:3:2 (?) 比率分给国库,IPO参与者,收集人
  • 免费转账每24小时每个账号3次 #58
  • 所有币种账户都有尘埃额度,低于该额度的金额进入国库 open-web3-stack/open-runtime-module-library#70
  • 每个账号都有开户费用,当为开户的账号收取金额时,自动扣除开户费用进行开户
    • 开户费用为 ACA
    • 自动使用DEX购买相应ACA来开户
  • 所有账号都必须开户后才可以发送交易
  • 提供销户功能
    • 转出所有剩余ACA加上返回额度到指定账号
    • 清除Nonce
    • 清除短地址
    • 用户需要自己保证没有其他资产,不然都会被冻结无法使用,需要重新开户才可以转账

CurrencyId type convert

pub trait Trait: system::Trait {
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
type CurrencyId: From<u8> + Into<CurrencyIdOf<Self>> + Into<DebitCurrencyIdOf<Self>> + Parameter + Copy;
type Price: FullCodec + SimpleArithmetic + Member + Into<u64> + From<Self::Balance>;
type Balance: From<BalanceOf<Self>> + From<DebitBalanceOf<Self>> + Into<Self::Price> + Zero;
type Convert: Convert<(Self::CurrencyId, DebitBalanceOf<Self>), BalanceOf<Self>>;
type Currency: MultiCurrencyExtended<Self::AccountId>;
type DebitCurrency: MultiCurrencyExtended<Self::AccountId>;
type PriceSource: PriceProvider<Self::CurrencyId, Self::Price>;
type RiskManager: RiskManager<Self::AccountId, Self::CurrencyId, AmountOf<Self>, DebitAmountOf<Self>>;
}
type CurrencyIdOf<T> = <<T as Trait>::Currency as MultiCurrency<<T as system::Trait>::AccountId>>::CurrencyId;
type DebitCurrencyIdOf<T> = <<T as Trait>::DebitCurrency as MultiCurrency<<T as system::Trait>::AccountId>>::CurrencyId;
type BalanceOf<T> = <<T as Trait>::Currency as MultiCurrency<<T as system::Trait>::AccountId>>::Balance;
type DebitBalanceOf<T> = <<T as Trait>::DebitCurrency as MultiCurrency<<T as system::Trait>::AccountId>>::Balance;
type AmountOf<T> = <<T as Trait>::Currency as MultiCurrencyExtended<<T as system::Trait>::AccountId>>::Amount;
type DebitAmountOf<T> = <<T as Trait>::DebitCurrency as MultiCurrencyExtended<<T as system::Trait>::AccountId>>::Amount;

vaults的trait的中有两个handler都有各自的CurrencyId,vaults在调用DebitCurrency的函数时,要做两个CurrencyId的类型转化,避免传参类型错误 。vaults实现中是新增了一个CurrencyId 关联类型 , trait bond 中加上了 Into From 方便操作。

cdp_engine 要继承 vaults,auction_manager,三个模块里定义CurrencyIdOf 的来源handler都不一样

pub type CurrencyIdOf<T> = <<T as Trait>::Currency as MultiCurrency<<T as system::Trait>::AccountId>>::CurrencyId;
pub type AuctionIdOf<T> =
<<T as Trait>::Auction as Auction<<T as system::Trait>::AccountId, <T as system::Trait>::BlockNumber>>::AuctionId;
pub trait Trait: system::Trait {
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
type Currency: MultiCurrencyExtended<Self::AccountId>;

pub type CurrencyIdOf<T> = <<T as Trait>::Currency as MultiCurrency<<T as system::Trait>::AccountId>>::CurrencyId;
pub type DebitBalanceOf<T> =
<<T as vaults::Trait>::DebitCurrency as MultiCurrency<<T as system::Trait>::AccountId>>::Balance;
pub type AmountOf<T> = <<T as vaults::Trait>::Currency as MultiCurrencyExtended<<T as system::Trait>::AccountId>>::Amount;
pub type DebitAmountOf<T> =
<<T as vaults::Trait>::DebitCurrency as MultiCurrencyExtended<<T as system::Trait>::AccountId>>::Amount;
pub trait Trait: system::Trait + auction_manager::Trait + vaults::Trait {
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
type Currency: MultiCurrencyExtended<Self::AccountId>;

如果按照vaults的实现来做,那也是挺麻烦的。 可以在通用trait MultiCurrency 的 CurrencyId 的trait bonds 中,加上From 吗, 同一个参数在不同handler之间传递时都先转成u8, 再into():

https://github.com/laminar-protocol/open-runtime-module-library/blob/feb24f52cb5522329b0cbb6203591c398650a6a8/traits/src/lib.rs#L18-L20

或者有什么更好的办法?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.