Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions prdoc/pr_10233.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
title: not charging ED in ctor if account already has enough balance
doc:
- audience: Runtime Dev
description: fixes https://github.com/paritytech/contract-issues/issues/179
crates:
- name: pallet-revive
bump: patch
25 changes: 1 addition & 24 deletions substrate/frame/revive/dev-node/node/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@
// limitations under the License.

use crate::cli::Consensus;
use futures::FutureExt;
use polkadot_sdk::{
sc_client_api::{backend::Backend, StorageProvider},
sc_client_api::StorageProvider,
sc_executor::WasmExecutor,
sc_service::{error::Error as ServiceError, Configuration, TaskManager},
sc_telemetry::{Telemetry, TelemetryWorker},
sc_transaction_pool_api::OffchainTransactionPoolFactory,
sp_runtime::traits::Block as BlockT,
*,
};
Expand Down Expand Up @@ -142,27 +140,6 @@ pub fn new_full<Network: sc_network::NetworkBackend<Block, <Block as BlockT>::Ha
metrics,
})?;

if config.offchain_worker.enabled {
let offchain_workers =
sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions {
runtime_api_provider: client.clone(),
is_validator: config.role.is_authority(),
keystore: Some(keystore_container.keystore()),
offchain_db: backend.offchain_storage(),
transaction_pool: Some(OffchainTransactionPoolFactory::new(
transaction_pool.clone(),
)),
network_provider: Arc::new(network.clone()),
enable_http_requests: true,
custom_extensions: |_| vec![],
})?;
task_manager.spawn_handle().spawn(
"offchain-workers-runner",
"offchain-worker",
offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(),
);
}

let rpc_extensions_builder = {
let client = client.clone();
let pool = transaction_pool.clone();
Expand Down
12 changes: 8 additions & 4 deletions substrate/frame/revive/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1222,11 +1222,15 @@ where
// if we reached this point the origin has an associated account.
let origin = &self.origin.account_id()?;

// get balance of the account before charging ED, only charge ED if balance
// insufficient
let balance = T::Currency::total_balance(account_id);
let ed = <Contracts<T>>::min_balance();
frame.nested_storage.record_charge(&StorageDeposit::Charge(ed))?;
<Contracts<T>>::charge_deposit(None, origin, account_id, ed, self.exec_config)
.map_err(|_| <Error<T>>::StorageDepositNotEnoughFunds)?;

if balance < ed {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should just use
frame_system::Pallet::<T>::account_exists(&account_id), since an account exits only if it has e.d
we do that in a few other places in the pallet

frame.nested_storage.record_charge(&StorageDeposit::Charge(ed))?;
<Contracts<T>>::charge_deposit(None, origin, account_id, ed, self.exec_config)
.map_err(|_| <Error<T>>::StorageDepositNotEnoughFunds)?;
}
// A consumer is added at account creation and removed it on termination, otherwise
// the runtime could remove the account. As long as a contract exists its
// account must exist. With the consumer, a correct runtime cannot remove the
Expand Down
33 changes: 33 additions & 0 deletions substrate/frame/revive/src/tests/pvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5133,3 +5133,36 @@ fn consume_all_gas_works() {
);
});
}

#[test]
fn existential_deposit_shall_not_charged_twice() {
let (code, code_hash) = compile_module("dummy").unwrap();

let salt = [0u8; 32];

ExtBuilder::default().build().execute_with(|| {
let _ = <Test as Config>::Currency::set_balance(&ALICE, 100_000_000_000_000);
let callee_addr = create2(
&ALICE_ADDR,
&code,
&[0u8; 0], // empty input
&salt,
);
let callee_account = <Test as Config>::AddressMapper::to_account_id(&callee_addr);

// first send funds to callee_addr
let result = builder::bare_call(callee_addr).native_value(666_000_000_000).build();
assert_ok!(result.result);
assert_eq!(get_balance(&callee_account), 666_000_000_000 + Contracts::min_balance());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to keep it minimal you can just initialize with

let _ = <Test as Config>::Currency::set_balance(&callee_account, Contracts::min_balance());

and check that

assert_eq!(get_balance(&callee_account),  Contracts::min_balance());


// then deploy contract to callee_addr using create2
let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code.clone()))
.salt(Some(salt))
.build_and_unwrap_contract();

assert_eq!(callee_addr, addr);

// check we charged ed only 1 time
assert_eq!(get_balance(&callee_account), 666_000_000_000 + Contracts::min_balance());
});
}
Loading