Skip to main content

Europa 调试示例

复制主题#

  1. 检出ink!的提交8e8fe09565ca6d2fad7701d68ff13f12deda7eed

    $ cd ink$ git checkout 8e8fe09565ca6d2fad7701d68ff13f12deda7eed -b tmp
  2. ink/examples/erc20/lib.rs:L90下的Transfer事件中把value改为0_u128

    #[ink(constructor)]pub fn new(initial_supply: Balance) -> Self {     //...     Self::env().emit_event(Transfer {        from: None,        to: Some(caller),        // change this from `initial_supply` to `0_u128`        value: 0_u128.into() // initial_supply,     });     instance}
  3. 执行cargo +nightly contract build --debug来编译合约。

  4. 使用RedspotPolkadot/Substrate Portal部署合约 。

注意 这里必须使用erc20.wasm,而不是erc20-opt.wasm,否则无法正常打印wasm backtrace。

在部署过程中,您会遇到DuplicateTopics,Europa日志如下。

```bash1: NestedRuntime {    #...    env_trace: [        seal_input(Some(0xd183512b0)),                #...                    seal_deposit_event((Some([0x45726332303a3a5472616e736....]), None)),    ],    trap_reason: TrapReason::SupervisorError(DispatchError::Module { index: 5, error: 23, message: Some("DuplicateTopics") }),    wasm_error: Error::WasmiExecution(Trap(Trap { kind: Host(DummyHostError) }))            wasm backtrace:             |  ink_env::engine::on_chain::ext::deposit_event[1623]            |  ink_env::engine::on_chain::impls::<impl ink_env::backend::TypedEnvBackend for ink_env::engine::on_chain::EnvInstance>::emit_event[1564]            |  ink_env::api::emit_event::{{closure}}[1563]            |  <ink_env::engine::on_chain::EnvInstance as ink_env::engine::OnInstance>::on_instance[1562]            |  ink_env::api::emit_event[1561]            |  erc20::erc20::_::<impl ink_lang::events::EmitEvent<erc20::erc20::Erc20> for ink_lang::env_access::EnvAccess<<erc20::erc20::Erc20 as ink_lang::env_access::ContractEnv>::Env>>::emit_event[1685]        # ...        # ...            |  deploy[1691]            ╰─><unknown>[2385]    ,    nest: [],}```
  • env_trace中最后一条记录是seal_deposit_event,而不是seal_return。如果合约执行正常,最后一条记录应当是seal_return
  • seal_deposit_event的第二个参数为None,这意味着这个host_function没有被正常执行。更多详情请参见相关实现
  • 结合wasm backtrace,可以看到栈顶为deposit_event

综上,可以断定错误出现在seal_deposit_event这个host_function中。

Balance类型不一致#

假设链上的Balance定义为u64,而ink!中Balance定义为u128,部署了一个普通的erc20合约。

读取total_supply时,Europa中日志内容如下。

1: NestedRuntime {    ext_result: [failed] ExecError { error: DispatchError::Module { index: 5, error: 17, message: Some("ContractTrapped") }, origin: ErrorOrigin::Caller },    caller: 0000000000000000000000000000000000000000000000000000000000000000 (5C4hrfjw...),    self_account: 2fe715301c9609c0c5ab75b24f2d8ad7dbe9671d7aebfeed80ed8963bc017955 (5D9Wkfa3...),    selector: 0xdb6375a8,    args: None,    value: 0,    gas_limit: 4999999999999,    gas_left: 4999865113466,    env_trace: [        seal_value_transferred(Some(0x0000000000000000)),    ],    wasm_error: Error::WasmiExecution(Trap(Trap { kind: Unreachable }))            wasm backtrace:            |  core::panicking::panic_fmt.48[1956]            |  core::result::unwrap_failed[1057]            |  core::result::Result<T,E>::expect[1060]            |  ink_lang::dispatcher::deny_payment[1878]            |  call[1906]            ╰─><unknown>[2614]    ,    nest: [],}

调用tranfer时,Europa中日志如下。

1: NestedRuntime {    ext_result: [failed] ExecError { error: DispatchError::Module { index: 5, error: 17, message: Some("ContractTrapped") }, origin: ErrorOrigin::Caller },    caller: 0000000000000000000000000000000000000000000000000000000000000000 (5C4hrfjw...),    self_account: 2fe715301c9609c0c5ab75b24f2d8ad7dbe9671d7aebfeed80ed8963bc017955 (5D9Wkfa3...),    selector: 0xdb6375a8,    args: None,    value: 0,    gas_limit: 4999999999999,    gas_left: 4999865113466,    env_trace: [        seal_value_transferred(Some(0x0000000000000000)),    ],    wasm_error: Error::WasmiExecution(Trap(Trap { kind: Unreachable }))            wasm backtrace:            |  core::panicking::panic_fmt.48[1956]            |  core::result::unwrap_failed[1057]            |  core::result::Result<T,E>::expect[1060]            |  ink_lang::dispatcher::deny_payment[1878]            |  call[1906]            ╰─><unknown>[2614]    ,    nest: [],}

可以发现,无论是读取操作还是写入操作,都会出现call -> deny_payment -> expect这样的调用过程。原因是在ink!中,有如下代码。

#[no_mangle]fn call() -> u32 {    if true {     ::ink_lang::deny_payment::<<Erc20 as ::ink_lang::ContractEnv>::Env>()                    .expect("caller transferred value even though all ink! message deny payments")    }    ::ink_lang::DispatchRetCode::from(        <Erc20 as ::ink_lang::DispatchUsingMode>::dispatch_using_mode(            ::ink_lang::DispatchMode::Call,        ),    )    .to_u32()}pub fn deny_payment<E>() -> Result<()>where    E: Environment,{    let transferred = ink_env::transferred_balance::<E>()        .expect("encountered error while querying transferred balance");    if transferred != <E as Environment>::Balance::from(0u32) {        return Err(DispatchError::PaidUnpayableMessage)    }    Ok(())}

ink!中,对于off_chainon_chain,出现expect的时机是不同的。在off_chain中,是由于ink_env::transferred_balance::<E>()不能正确解码。在on_chain中,由于transferred!=0导致deny_payment返回Error,在call中才出现expect。由此可见,当前ink!off_chainon_chain中对某些情况是不完全一致的,会造成调试的困扰。