Project version: v0.1 (Experimental)
The Contracts module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts.
These maps leverage the DelegateInfo and StakeInfo structs from pallet_contracts::stake module to track stake-related data, enabling contract-based staking and delegation.
DelegateInfoMap– Stores delegate-related information for each contract.StakeInfoMap– Tracks stake scores of smart contracts.ValidatorInfoMap– Records the number of delegates associated with a validator.
These errors handle edge cases and failures during delegation, stake management, and validator nomination.
InvalidContractOwner– Origin is not the valid owner of the contract.NoStakeExists– No stake information available for the contract.NoValidatorFound– Validator for the contract address is not found.LowReputation– Contract does not meet minimum reputation for staking.AlreadyDelegated– Contract is already delegated to the specified address.AlreadyOwner– Contract is already owned by the given account.NewBondFailed– Failure in creating a new bond.BondExtraFailed– Failure in adding extra funds to an existing bond.BondRemoveFailed– Failure in removing an existing bond.NominationFailed– Validator nomination process failed.ValidationFailed– Validator validation criteria check failed.InsufficientDelegates– Minimum delegate count for validation is not met.
These events ensure transparent tracking of stake updates, delegation actions, and validator status, facilitating better monitoring and debugging.
Staked– Emitted when a contract’s stake score is updated.Delegated– Emitted when a contract is delegated to a validator.ReadyToStake– Signals that a contract meets the minimum reputation for staking.ValidateInfo– Provides details about a validator’s status and eligibility.StakeOwner– Emitted when a contract’s stake owner is updated.
These functions enable smart contracts to interact with the PoCS system by delegating validation, updating contract ownership, and participating as validators.
-
delegate()- Purpose: Allows a contract to delegate its validation rights to another account.
- Logic: Ensures the origin is signed and calls the
delegate()function frompallet_contracts::stake::DelegateRequest::delegate.
-
update_owner()- Purpose: Updates the stake owner for a contract.
- Logic: Ensures the origin is signed and invokes
update_stake_owner()frompallet_contracts::stake::DelegateRequest::update_stake_owner.
-
validate()- Purpose: Allows a contract to register as a validator and set preferences.
- Logic: Ensures the origin is signed and calls
validate()frompallet_contracts::stake::ValidateRequest::validate, which in turns call thevalidate()method in pallet_staking.
Benchmarks for the two functions delegate() and update_owner() is written in pallet_contracts::src::benchmarking::mod
Key Additions
-
Capture Stake Arguments:
let account_id = &frame.account_id.clone(); let gas = frame.nested_gas.gas_consumed().ref_time(); let caller = self.caller().account_id()?.clone();
account_id: Retrieves the contract account ID from the current frame.gas: Measures the gas consumed during the execution.caller: Retrieves the caller of the contract.
-
Stake Invocation:
StakeRequest::<T>::stake(&caller, &account_id, &gas)?;
- Calls the
stakefunction from theStakeRequestimplementation. - This ensures the contract's execution is recorded for Proof of Contract Stake (PoCS).
⚠️ Important Note:The expected arguments for the stake request in
fn runintroduce a breaking change due to sub-calls allowing the Root Account to call and test. Since we now extract the caller's account ID viaself.caller().account_id(), this results in aRootNotAllowederror, causing existing tests to fail.This behavior cannot be bypassed, as PoCs fundamentally rely on the frame's caller account ID to assign stakes. Therefore, restricting the Root Account from calling contracts across all areas is necessary. Tests that previously expected root calls to succeed should be modified accordingly in future development.
-
Stake (PoCS) Deletion on Termination:
To remove stake records when a contract is terminated the following call is made.
StakeRequest::<T>::delete(&frame.account_id);
Encapsulates PoCS-specific logic in a self-contained module, improving modularity, testability, and maintainability.
-
Structure:
mod.rs– Centralized all PoCS functionalities, providing a cohesive and loosely coupled design.chain_ext.rs– Implements two chain extensions for querying and updating stake and delegate information.
-
Key Features:
- All struct fields are accessed via dedicated methods.
- Comprehensive error handling for all operations.
-
Core Functionalities:
- Implements stake/delegate/validator requests and management based on PoCS specification.
- Provides methods for struct access, enhancing data encapsulation.
-
Error Handling:
- Explicit error management for all operations.
- Uses
DispatchErrorwhere appropriate, ensuring accurate error propagation to existing disptachable functions.
Facilitates contract-level read and update operations on delegate and stake data while ensuring security and maintaining consistency with PoCS mappings.
-
FetchStakeInfo (ID: 1200) - For Stake State Query
Ensures only known function IDs are handled, with error logging for unknown requests.
Function Name Function ID delegate_to1000 delegate_at1001 stake_score1002 reputation1003 owner1004 -
UpdateDelegateInfo (ID: 1300) - For Stake State Update
Validates the calling contract using the environment context to provide extrinsic calling behaviour to contracts which own other contracts.
Function Name Function ID DelegateInfo::delegate_to1005 DelegateInfo::owner1006
Comprehensive tests have been implemented to cover all functionalities within the pallet-contracts module. These tests include integrated checks for stake/mod.rs and exec.rs. All the test for PoCS will start with the prefix pocs_ While the chain extension (chain-ext.rs) is excluded from these tests, dedicated tests for it are provided within the contracts directory.
Test's that are Failing
benchmarking::bench_seal_caller_is_roottest exec::tests::root_caller_succeedsexec::tests::root_caller_succeeds_with_consecutive_callstests::root_can_call
These test are failing due to the Breaking changes made in the pallet-contracts.
⚠️ Important Note: The changes made insrc/pallet/mod.rsandsrc/tests.rsare Breaking changes. These changes are temporary and will be corrected or revised in further development. As this was the only method that may couple PoCS and NPoS logic.
-
Commented out checks related to minimum balance requirement:
-
if value < T::Currency::minimum_balance() -
ledger.active >= T::Currency::minimum_balance() -
ledger.active >= min_active_bond -
ledger.active >= MinValidatorBond::<T>::get() -
ledger.active >= MinNominatorBond::<T>::get()
-
To treat the stake score as a balance and allow the creation of an empty bond.
-
Commented out Tests:
bond_with_no_staked_value– Previously tested bonding behavior with zero stake, which is no longer required.cannot_bond_extra_to_lower_than_ed– Checked that bond extra values cannot fall below the existential deposit, which is no longer enforced.min_bond_checks_work– Validated minimum bond checks for nominators and validators, now obsolete due to the removal of these limits.
These tests were downgraded since the minimum bond check is removed in PoCS.
For each chain extension and its function id, corresponding Ink! contracts are provided. The flipper and simple_caller contracts are extras, with flipper serving as a dummy contract.
Added five key functions for validator and delegation management which are also given as each seperate contracts:
-
delegate_of: Retrieves the delegate of a given account (Func ID: 1000) -
delegate_at: Returns the block number when the delegation was last updated (Func ID: 1001) -
stake_score: Retrieves the stake score of a given contract (Func ID: 1002) -
reputation: Fetches the reputation score (Func ID: 1003) -
owner: Returns the owner of a given contract (Func ID: 1004)
Added functions for advanced delegation and validator updates:
update_delegate: Updates the delegate for a given account and ensures synchronization with stake data (Func ID: 1005)update_owner:
- Implements a reward distribution mechanism by tracking stake scores and delegation.
- Validates contract ownership using the
owner_checkmethod. - Ensures delegation consistency with the
delegate_checkmethod. - Supports contract registration, reward claiming, and cancellation.
- Uses
CustomEnvironmentto integrate with the chain extension.
Key Methods:
register: Registers a contract and updates the stake pool.claim: Claims rewards based on updated stake scores.cancel: Removes a contract from the registry after claiming.
- The E2E testing is automated using the
e2e_test.shscript. - This script spins up the PoCS node and manages test cases for all Ink! contracts and chain extensions in the project as a combined test.
- It ensures comprehensive validation across the contract lifecycle, including deployment, delegation updates, and ownership transfers.
- Challenges with NPoS: Since NPoS is inherently tied to balances and currency, maintaining or removing invariants related to staking primitives is complex. In contrast, the stake score consists of non-transferable, non-fungible computational units.
- Stake Module Extension: Future development will extend the stake module in
pallet_contractsto support a minimal implementation of NPoS, derived frompallet_staking, but without dependencies on balance or currency primitives. This will ensure full compatibility with PoCS without breaking changes. - Optimized Staking Logic: To enhance efficiency, the staking logic will be integrated directly into the
pallet_contractscrate, aligning with PoCS's one-to-one communication between contracts and staking.