public void Stake(Address from, BigInteger stakeAmount) { Runtime.Expect(stakeAmount >= MinimumValidStake, "invalid amount"); Runtime.Expect(Runtime.IsWitness(from), "witness failed"); var balance = Runtime.GetBalance(DomainSettings.StakingTokenSymbol, from); if (stakeAmount > balance) { var diff = stakeAmount - balance; throw new BalanceException("SOUL", from, diff); balance = stakeAmount; // debug mode only, otherwise a exception will prevent it from reaching here } Runtime.Expect(balance >= stakeAmount, $"balance: {balance} stake: {stakeAmount} not enough balance to stake at " + from); Runtime.TransferTokens(DomainSettings.StakingTokenSymbol, from, this.Address, stakeAmount); EnergyStake stake; if (_stakeMap.ContainsKey <Address>(from)) { stake = _stakeMap.Get <Address, EnergyStake>(from); } else { stake = new EnergyStake() { stakeTime = new Timestamp(0), stakeAmount = 0, }; } stake.stakeTime = Runtime.Time; stake.stakeAmount += stakeAmount; _stakeMap.Set <Address, EnergyStake>(from, stake); Runtime.AddMember(DomainSettings.StakersOrganizationName, this.Address, from); var claimList = _claimMap.Get <Address, StorageList>(from); var claimEntry = new EnergyClaim() { stakeAmount = stakeAmount, claimDate = this.Runtime.Time, isNew = true, }; claimList.Add(claimEntry); var logEntry = new VotingLogEntry() { timestamp = this.Runtime.Time, amount = stakeAmount }; var votingLogbook = _voteHistory.Get <Address, StorageList>(from); votingLogbook.Add(logEntry); // masters membership var masterAccountThreshold = GetMasterThreshold(); if (stake.stakeAmount >= masterAccountThreshold && !IsMaster(from)) { var nextClaim = GetMasterClaimDate(2); Runtime.AddMember(DomainSettings.MastersOrganizationName, this.Address, from); _masterClaims.Set <Address, Timestamp>(from, nextClaim); _masterAgeMap.Set <Address, Timestamp>(from, Runtime.Time); } }
// NOTE - witness not required, as anyone should be able to call this, permission is granted based on consensus public void SetValidator(Address target, BigInteger index, ValidatorType type) { Runtime.Expect(target.IsUser, "must be user address"); Runtime.Expect(type == ValidatorType.Primary || type == ValidatorType.Secondary, "invalid validator type"); var primaryValidators = GetValidatorCount(ValidatorType.Primary); var secondaryValidators = GetValidatorCount(ValidatorType.Secondary); Runtime.Expect(index >= 0, "invalid index"); var totalValidators = GetMaxTotalValidators(); Runtime.Expect(index < totalValidators, "invalid index"); var expectedType = index < GetMaxPrimaryValidators() ? ValidatorType.Primary : ValidatorType.Secondary; Runtime.Expect(type == expectedType, "unexpected validator type"); var requiredStake = Runtime.CallContext(NativeContractKind.Stake, nameof(StakeContract.GetMasterThreshold), target).AsNumber(); var stakedAmount = Runtime.GetStake(target); Runtime.Expect(stakedAmount >= requiredStake, "not enough stake"); if (index > 0) { var isPreviousSet = _validators.ContainsKey <BigInteger>(index - 1); Runtime.Expect(isPreviousSet, "previous validator slot is not set"); var previousEntry = _validators.Get <BigInteger, ValidatorEntry>(index - 1); Runtime.Expect(previousEntry.type != ValidatorType.Invalid, " previous validator has unexpected status"); } if (primaryValidators > 0) { var isValidatorProposed = _validators.ContainsKey <BigInteger>(index); if (isValidatorProposed) { var currentEntry = _validators.Get <BigInteger, ValidatorEntry>(index); if (currentEntry.type != ValidatorType.Proposed) { Runtime.Expect(currentEntry.type == ValidatorType.Invalid, "invalid validator state"); isValidatorProposed = false; } } if (isValidatorProposed) { Runtime.Expect(Runtime.IsWitness(target), "invalid witness"); } else { if (primaryValidators > 1) { var pollName = ConsensusContract.SystemPoll + ValidatorPollTag; var obtainedRank = Runtime.CallContext("consensus", "GetRank", pollName, target).AsNumber(); Runtime.Expect(obtainedRank >= 0, "no consensus for electing this address"); Runtime.Expect(obtainedRank == index, "this address was elected at a different index"); } else { var firstValidator = GetValidatorByIndex(0).address; Runtime.Expect(Runtime.IsWitness(firstValidator), "invalid witness"); } type = ValidatorType.Proposed; } } else { Runtime.Expect(Runtime.IsWitness(Runtime.GenesisAddress), "invalid witness"); } var entry = new ValidatorEntry() { address = target, election = Runtime.Time, type = type, }; _validators.Set <BigInteger, ValidatorEntry>(index, entry); if (type == ValidatorType.Primary) { var newValidators = GetValidatorCount(ValidatorType.Primary); Runtime.Expect(newValidators > primaryValidators, "number of primary validators did not change"); } else if (type == ValidatorType.Secondary) { var newValidators = GetValidatorCount(ValidatorType.Secondary); Runtime.Expect(newValidators > secondaryValidators, "number of secondary validators did not change"); } if (type != ValidatorType.Proposed) { Runtime.AddMember(DomainSettings.ValidatorsOrganizationName, this.Address, target); } Runtime.Notify(type == ValidatorType.Proposed ? EventKind.ValidatorPropose : EventKind.ValidatorElect, Runtime.Chain.Address, target); }