AckiNackiBlockKeeperNodeWallet.sol
Overview
AckiNackiBlockKeeperNodeWallet.sol implements a smart contract that serves as the wallet for a BlockKeeper node within the Acki Nacki blockchain ecosystem. This contract manages stakes, licenses, and interactions with various BlockKeeper-related contracts such as pre-epoch, epoch, and cooler contracts. It handles the lifecycle of stakes, license management, slashing mechanisms, and communication with the root contract and other components managing epochs and consensus.
The contract stores and controls a collection of active stakes, license data, and code references for deployed contracts. It enforces permissions based on the owner's public key and contract addresses derived from known code cells, ensuring secure and verifiable management of node wallet state.
Contract: AckiNackiBlockKeeperNodeWallet
This contract inherits from Modifiers and manages:
Node wallet state
Active stakes with their lifecycle
License data associated with the node
Interaction with BlockKeeper root and epoch contracts
Slashing and stake locking/unlocking
Wallet token withdrawals and balance management
State Variables
version(string constant): Contract version"1.0.0"._code(mapping uint8 => TvmCell): Stores code cells of dependent contracts indexed by type._owner_pubkey(uint256 static): Public key of the wallet owner._root(address): Address of the root BlockKeeper contract._activeStakes(mapping uint256 => Stake): Active stakes indexed by stake hash._stakesCnt(uint8): Count of active stakes._isContinue(bool): Flag indicating if the wallet is continuing an epoch._licenses(mapping uint256 => LicenseData): License data indexed by license number._licenses_count(uint128): Number of licenses tracked._epochDuration(uint128): Duration of the epoch._freeLicense(uint32): Timestamp or counter related to free licenses._isWaitStake(bool): Flag indicating whether the wallet is waiting on a stake operation._balance(uint128): Aggregate balance of licenses._whiteListLicense(mapping uint256 => bool): Whitelisted licenses allowed._licenseRoot(address): Root contract address for licenses._isSlashing(bool): Flag indicating if slashing is active._walletLastTouch(uint64): Timestamp of last wallet operation to prevent rapid requests._signing_pubkey(uint256): Current signing public key._walletTouch(uint8): Minimum delay between wallet operations.
Constructor
constructor (
TvmCell BlockKeeperPreEpochCode,
TvmCell AckiNackiBlockKeeperNodeWalletCode,
TvmCell BlockKeeperEpochCode,
TvmCell BlockKeeperEpochCoolerCode,
TvmCell BlockKeeperEpochProxyListCode,
TvmCell BLSKeyCode,
TvmCell SignerIndexCode,
TvmCell LicenseCode,
uint128 epochDuration,
uint32 freeLicense,
mapping(uint256=>bool) whiteListLicense,
address licenseRoot,
uint8 stakesCnt,
uint8 walletTouch
)
Initializes the contract, storing code cells for dependent contracts.
Sets initial state including epoch duration, license whitelist, license root, stake count, and wallet touch delay.
Verifies the sender is the root contract and the code library version matches.
Sets
_signing_pubkeyto the owner’s public key.
Usage Example
new AckiNackiBlockKeeperNodeWallet(
preEpochCode,
nodeWalletCode,
epochCode,
coolerCode,
proxyListCode,
blsKeyCode,
signerIndexCode,
licenseCode,
3600,
100,
whiteList,
licenseRootAddr,
0,
10
);
Key Functions
1. setSigningPubkey
function setSigningPubkey(uint256 pubkey) public onlyOwnerPubkey(_owner_pubkey) accept
Updates the signing public key for the wallet.
Requires a cooldown delay since last wallet operation (
_walletTouch).Calls
ensureBalance()to maintain minimum balance.
2. checkCooler
function checkCooler() private view returns(bool result)
Checks if any active stake is in the cooler state.
If a cooler stake has finished its sequence number, triggers a
touchcall on the cooler contract.Returns
trueif any cooler is deployed, elsefalse.
3. License Management Functions
setLockToStake: Locks or unlocks stake on a license, callable by the license contract.setLockToStakeByWallet: Locks or unlocks stake on a license, callable by wallet owner.removeLicense: Removes a license when it is in rest state and not locked.addLicense: Adds a license if whitelist and max count conditions are met.addBalance: Adds balance to a license.deleteLicense: Deletes a license locked due to slashing, callable only by owner.
These functions ensure licenses are properly managed with respect to stake locking, balance, and privilege status.
4. Stake Lifecycle Management
setLockStake: Locks stakes for licenses at pre-epoch deployment.deleteLockStake: Unlocks stakes and destroys associated BLS and signer index contracts.updateLockStake: Updates licenses to epoch state after stake is accepted.deployPreEpochContract: Deploys the pre-epoch contract with stake and license data.deployBlockKeeperContractContinue: Continues an epoch by deploying the epoch contract with licenses.cancelContinueStake: Cancels an epoch continuation and unlocks licenses.continueStakeAccept&continueStakeNotAccept: Handles acceptance or rejection of stake continuation requests.
These functions orchestrate the transition of stakes and licenses through pre-epoch, epoch, and cooler phases.
5. Slashing Mechanisms
slash: Initiates slashing for a stake by its BLS key.slashStakeandslashCooler: Perform partial or full slashing on stakes and cooler stakes.Helper functions manage license balances, reputation, and contract destruction related to slashing.
Slashing is used to penalize misbehaving stakes, reducing their balances and removing privileges.
6. Token Withdrawals
withdrawToken: License contract can withdraw tokens from its license balance.withdrawWalletToken: Owner can withdraw tokens from the wallet, respecting locked balances.
Both methods ensure tokens are withdrawn only when not locked or during stake operations.
7. Utility and Helpers
ensureBalance: Mints tokens if balance is too low to cover deployment fees.sendBlockKeeperRequestWithStakeandsendBlockKeeperRequestWithStakeContinue: Initiate staking requests to the root contract.getDetails,getEpochAddress,getSumBalanceForStake,getProxyListAddr,getProxyListCodeHash, andgetVersion: Read-only getters for wallet state.
Important Implementation Details and Algorithms
Stake Hashing: Stakes are indexed by a hash of their start sequence number using
tvm.hashof a cell built withTvmBuilder. This ensures unique identification of stakes.License Locking: Licenses can be locked either by stake, wallet, or due to slashing; these states affect balance accessibility and stake eligibility.
Cooldown Enforcement: Wallet operations enforce a cooldown delay (
_walletTouch) between actions to prevent spam or rapid state changes.Slashing Calculation: Slashing is proportional; partial slashes calculate the reduced amount based on stake percentage constants like
FULL_STAKE_PERCENT.Balance and Reputation Management: License balances and reputation times are carefully updated during stake lifecycle transitions, slashing, and stake continuations.
Contract Address Derivation: Uses deterministic calculations (
BlockKeeperLib.calculate...Address) to verify sender contracts and interact with deployed contracts, ensuring security.
Interaction with Other Contracts
BlockKeeperContractRoot: Receives staking requests and manages the root logic.
BlockKeeperPreEpochContract: Manages stakes during the pre-epoch phase.
BlockKeeperEpochContract: Manages stakes during the active epoch phase.
BlockKeeperCoolerContract: Manages stakes during the cooling-off phase.
LicenseContract: Represents licenses associated with node stakes.
BLSKeyIndex and SignerIndex: Manage cryptographic keys and signer indices for stakes.
The wallet acts as a central state manager, coordinating between licenses, stakes, and different phases of staking through these contracts.
Data Structures
Stake: Represents an active stake with fields like stake amount, start and finish sequence numbers, BLS key, status, and signer index.LicenseData: Represents license details including reputation, balance, lock states, privilege flags, and controller addresses.LicenseStake: Represents a license and stake amount pair used in arrays for batch operations.
Error Handling
The contract uses require statements with error constants such as:
ERR_SENDER_NO_ALLOWEDERR_WAIT_STAKEERR_LICENSE_NOT_EXISTERR_LICENSE_BUSYERR_WALLET_BUSYERR_TOO_MANY_LICENSESERR_NOT_SUPPORTERR_LOW_VALUEERR_STAKE_EXISTERR_TOO_LOW_LICENSESERR_WRONG_BLS_PUBKEYERR_WRONG_SIGNER_INDEXERR_STAKE_DIDNT_EXISTERR_STAKE_STATUS_NOT_EPOCHERR_TOO_LATEERR_EPOCH_ALREADY_CONTINUE
These ensure operations are valid and preserve contract invariants.
Mermaid Class Diagram
classDiagram
class AckiNackiBlockKeeperNodeWallet {
-string version
-mapping(uint8 => TvmCell) _code
-uint256 static _owner_pubkey
-address _root
-mapping(uint256 => Stake) _activeStakes
-uint8 _stakesCnt
-bool _isContinue
-mapping(uint256 => LicenseData) _licenses
-uint128 _licenses_count
-uint128 _epochDuration
-uint32 _freeLicense
-bool _isWaitStake
-uint128 _balance
-mapping(uint256 => bool) _whiteListLicense
-address _licenseRoot
-bool _isSlashing
-uint64 _walletLastTouch
-uint256 _signing_pubkey
-uint8 _walletTouch
+constructor(...)
+setSigningPubkey(pubkey)
+checkCooler() bool
+setLockToStake(license_number, lock)
+setLockToStakeByWallet(license_number, lock)
+removeLicense(license_number)
+setLicenseWhiteList(whiteListLicense)
+addLicense(license_number, reputationTime, last_touch, isPrivileged)
+addBalance(license_number)
+deleteLicense(license_number)
+setLockStake(seqNoStart, stake, bls_key, signerIndex, licenses)
+deleteLockStake(seqNoStart, bls_key, signerIndex, licenses)
+slash(slash_type, bls_key)
+updateLockStake(seqNoStart, seqNoFinish, stake, bls_key, signerIndex, licenses)
+stakeNotAccepted(epochDuration)
+stakeNotAcceptedContinue(epochDuration)
+sendBlockKeeperRequestWithStake(bls_pubkey, stake, signerIndex, ProxyList, myIp, nodeVersion)
+sendBlockKeeperRequestWithStakeContinue(bls_pubkey, stake, seqNoStartOld, signerIndex, ProxyList, nodeVersion)
+deployPreEpochContract(epochDuration, walletTouch, epochCliff, waitStep, bls_pubkey, signerIndex, rep_coef, virtualStake, ProxyList, reward_sum, myIp, nodeVersion)
+deployBlockKeeperContractContinue(walletTouch, seqNoStartold, bls_pubkey, signerIndex, virtualStake, ProxyList, sumReputationCoef, nodeVersion)
+cancelContinueStake(seqNoStartOld, bls_key, signerIndex, licenses)
+continueStakeAccept(seqNoStartOld)
+continueStakeNotAccept(seqNoStartOld, bls_key, signerIndex, licenses)
+updateLockStakeCooler(seqNoStart, seqNoFinish, licenses, isContinue, addReputationTime)
+unlockStakeCooler(seqNoStart, licenses, stake)
+slashCooler(seqNoStart, slash_type, slash_stake, licenses, isFromEpoch)
+slashStake(seqNoStart, slash_type, slash_stake, licenses, isContinue, bls_key_continue, signerIndex_continue, licenses_continue)
+withdrawToken(license_number, value)
+withdrawWalletToken(to, value)
+resetWallet()
+getDetails() returns (...)
+getEpochAddress() returns (optional(address))
+getSumBalanceForStake() returns (uint128)
+getProxyListAddr() returns (address)
+getProxyListCodeHash() returns (uint256)
+getProxyListCode() returns (TvmCell)
+getVersion() returns (string, string)
}
Notes on Interactions and Security
The contract enforces that calls altering stake or license state originate from pre-determined contract addresses derived via
BlockKeeperLib.Public key ownership is verified with
onlyOwnerPubkeymodifier to restrict sensitive operations.The contract maintains a cooldown mechanism between wallet operations to avoid state inconsistencies or replay attacks.
Slashing logic includes careful balance and reputation adjustments to penalize misbehaving node operators.
License whitelist and maximum license count restrictions guard against unauthorized or excessive license additions.
Refer to Modifiers for permission enforcement details and BlockKeeperLib for address calculation and utility methods used in this contract. Stake and license data structures and related constants are defined in imported files such as BLSKeyIndex.sol, SignerIndex.sol, and License.sol.