@@ -9,6 +9,8 @@ use nimiq_primitives::coin::Coin;
99use nimiq_primitives:: policy:: Policy ;
1010use serde:: { Deserialize , Serialize } ;
1111
12+ use super :: AddStakeReceipt ;
13+ use crate :: BalanceType ;
1214#[ cfg( feature = "interaction-traits" ) ]
1315use crate :: {
1416 account:: staking_contract:: {
@@ -146,6 +148,7 @@ impl Staker {
146148 /// Invariants:
147149 /// (1) active + inactive balances must be == 0 or >= minimum stake
148150 /// (2) active + inactive + retired balances must be == 0 or >= minimum stake
151+ /// IMPORTANT: This code is shared between new and legacy add stake versions.
149152 pub ( crate ) fn enforce_min_stake (
150153 active_balance : Coin ,
151154 inactive_balance : Coin ,
@@ -283,68 +286,118 @@ impl StakingContract {
283286 store : & mut StakingContractStoreWrite ,
284287 staker_address : & Address ,
285288 value : Coin ,
289+ protocol_version : u16 ,
286290 tx_logger : & mut TransactionLog ,
287- ) -> Result < ( ) , AccountError > {
291+ ) -> Result < AddStakeReceipt , AccountError > {
292+ if protocol_version < Policy :: ADD_STAKE_PROTOCOL_UPGRADE_VERSION {
293+ log:: warn!( %protocol_version, "Adding stake using old protocol version" ) ;
294+ return self . legacy_add_stake_0v ( store, staker_address, value, tx_logger) ;
295+ }
288296 // Get the staker.
289297 let mut staker = store. expect_staker ( staker_address) ?;
290298
291- // Fail if the minimum stake would be violated for the non-retired funds (invariant 1).
292- Staker :: enforce_min_stake (
293- staker. active_balance + value,
294- staker. inactive_balance ,
295- staker. retired_balance ,
296- ) ?;
297-
298- // All checks passed, not allowed to fail from here on!
299-
300- // If we are delegating to a validator, we need to update it.
299+ // Check that the delegation is still valid, i.e. the validator hasn't been deleted.
301300 if let Some ( validator_address) = & staker. delegation {
302- // Check that the delegation is still valid, i.e. the validator hasn't been deleted.
303301 store. expect_validator ( validator_address) ?;
304- self . increase_stake_to_validator ( store, validator_address, value) ;
305302 }
306303
304+ // Add stake txs never violate minimum stake for the non-retired funds (invariant 1),
305+ // because the intrinsic tx checks that value is >= min stake.
306+ assert ! (
307+ Staker :: enforce_min_stake(
308+ staker. active_balance + value,
309+ staker. inactive_balance,
310+ staker. retired_balance,
311+ )
312+ . is_ok( ) ,
313+ "Add stake should never violate the min stake invariants"
314+ ) ;
315+
316+ // All checks passed, not allowed to fail from here on!
317+
307318 // Update the staker's and staking contract's balances.
308- staker. active_balance += value;
319+ // We want to preferentially credit the active balance. Only if there is no active balance,
320+ // then we will choose the balance with the most funds between inactive and retired.
321+ let credited_balance = if !staker. active_balance . is_zero ( ) {
322+ staker. active_balance += value;
323+ BalanceType :: Active
324+ } else {
325+ staker. inactive_balance += value;
326+ if staker. inactive_from . is_none ( ) {
327+ staker. inactive_from = Some ( 0 ) ;
328+ }
329+ BalanceType :: Inactive
330+ } ;
309331 self . balance += value;
310332
333+ // Create the receipt.
334+ let receipt = AddStakeReceipt {
335+ credited_balance : credited_balance. clone ( ) ,
336+ } ;
337+
338+ // If we are actively delegating to a validator, we need to update it.
339+ if credited_balance == BalanceType :: Active {
340+ if let Some ( validator_address) = & staker. delegation {
341+ self . increase_stake_to_validator ( store, validator_address, value) ;
342+ }
343+ }
344+
311345 // Build the return logs
312346 tx_logger. push_log ( Log :: Stake {
313347 staker_address : staker_address. clone ( ) ,
314348 validator_address : staker. delegation . clone ( ) ,
315349 value,
350+ credited_balance,
316351 } ) ;
317352
318353 // Update the staker entry.
319354 store. put_staker ( staker_address, staker) ;
320355
321- Ok ( ( ) )
356+ Ok ( receipt )
322357 }
323358
324359 /// Reverts a stake transaction.
360+ /// IMPORTANT: This code is shared between new and legacy add stake versions.
325361 pub fn revert_add_stake (
326362 & mut self ,
327363 store : & mut StakingContractStoreWrite ,
328364 staker_address : & Address ,
329365 value : Coin ,
366+ receipt : AddStakeReceipt ,
330367 tx_logger : & mut TransactionLog ,
331368 ) -> Result < ( ) , AccountError > {
332369 // Get the staker.
333370 let mut staker = store. expect_staker ( staker_address) ?;
334371
335372 // If we are delegating to a validator, we need to update it too.
336- if let Some ( validator_address) = & staker. delegation {
337- self . decrease_stake_from_validator ( store, validator_address, value) ;
373+ if receipt. credited_balance == BalanceType :: Active {
374+ if let Some ( validator_address) = & staker. delegation {
375+ self . decrease_stake_from_validator ( store, validator_address, value) ;
376+ }
338377 }
339378
340379 // Update the staker's and staking contract's balances.
341- staker. active_balance -= value;
380+ match receipt. credited_balance {
381+ BalanceType :: Active => {
382+ staker. active_balance -= value;
383+ }
384+ BalanceType :: Inactive => {
385+ staker. inactive_balance -= value;
386+ if staker. inactive_balance . is_zero ( ) {
387+ staker. inactive_from = None ;
388+ }
389+ }
390+ BalanceType :: Retired => {
391+ staker. retired_balance -= value;
392+ }
393+ }
342394 self . balance -= value;
343395
344396 tx_logger. push_log ( Log :: Stake {
345397 staker_address : staker_address. clone ( ) ,
346398 validator_address : staker. delegation . clone ( ) ,
347399 value,
400+ credited_balance : receipt. credited_balance ,
348401 } ) ;
349402
350403 // Update the staker entry.
@@ -949,6 +1002,7 @@ impl StakingContract {
9491002 }
9501003
9511004 /// Adds `value` coins to a given validator's total stake.
1005+ /// IMPORTANT: This code is shared between new and legacy add stake versions.
9521006 fn increase_stake_to_validator (
9531007 & mut self ,
9541008 store : & mut StakingContractStoreWrite ,
@@ -982,6 +1036,7 @@ impl StakingContract {
9821036 }
9831037
9841038 /// Removes `value` coins from a given validator's inactive total stake.
1039+ /// IMPORTANT: This code is shared between new and legacy add stake versions.
9851040 fn decrease_stake_from_validator (
9861041 & mut self ,
9871042 store : & mut StakingContractStoreWrite ,
@@ -1015,4 +1070,57 @@ impl StakingContract {
10151070 // Neither validator nor tombstone exist, this is an error.
10161071 panic ! ( "inconsistent contract state" ) ;
10171072 }
1073+
1074+ // Pre protocol upgrade.
1075+ // IMPORTANT: DO NOT REMOVE THIS CODE!
1076+ // It is needed for history nodes to sync.
1077+
1078+ /// Legacy add stake logic, it adds more Coins to a staker's active balance.
1079+ /// It will be directly added to the staker's balance.
1080+ /// Anyone can add stake for a staker. The staker must already exist.
1081+ pub fn legacy_add_stake_0v (
1082+ & mut self ,
1083+ store : & mut StakingContractStoreWrite ,
1084+ staker_address : & Address ,
1085+ value : Coin ,
1086+ tx_logger : & mut TransactionLog ,
1087+ ) -> Result < AddStakeReceipt , AccountError > {
1088+ // Get the staker.
1089+ let mut staker = store. expect_staker ( staker_address) ?;
1090+
1091+ // Fail if the minimum stake would be violated for the non-retired funds (invariant 1).
1092+ Staker :: enforce_min_stake (
1093+ staker. active_balance + value,
1094+ staker. inactive_balance ,
1095+ staker. retired_balance ,
1096+ ) ?;
1097+
1098+ // All checks passed, not allowed to fail from here on!
1099+
1100+ // If we are delegating to a validator, we need to update it.
1101+ if let Some ( validator_address) = & staker. delegation {
1102+ // Check that the delegation is still valid, i.e. the validator hasn't been deleted.
1103+ store. expect_validator ( validator_address) ?;
1104+ self . increase_stake_to_validator ( store, validator_address, value) ;
1105+ }
1106+
1107+ // Update the staker's and staking contract's balances.
1108+ staker. active_balance += value;
1109+ self . balance += value;
1110+
1111+ // Build the return logs
1112+ tx_logger. push_log ( Log :: Stake {
1113+ staker_address : staker_address. clone ( ) ,
1114+ validator_address : staker. delegation . clone ( ) ,
1115+ value,
1116+ credited_balance : BalanceType :: Active ,
1117+ } ) ;
1118+
1119+ // Update the staker entry.
1120+ store. put_staker ( staker_address, staker) ;
1121+
1122+ Ok ( AddStakeReceipt {
1123+ credited_balance : BalanceType :: Active ,
1124+ } )
1125+ }
10181126}
0 commit comments