Skip to content

Commit

Permalink
Merge pull request #1531 from orbs-network/feature/election-staking
Browse files Browse the repository at this point in the history
add locked stake to the election contract (merge only before actual deploy)
  • Loading branch information
noambergIL authored Mar 31, 2020
2 parents 999cfac + ad96a16 commit 12e2ab6
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package elections_systemcontract
* Connections to Ethereum contracts
*/
var ETHEREUM_TOKEN_ADDR = "0xff56Cc6b1E6dEd347aA0B7676C85AB0B3D08B0FA"
var ETHEREUM_STAKING_ADDR = "0x0cD370eDcBbD783815a7f45beeA559b47D39de15"
var ETHEREUM_VOTING_ADDR = "0x30f855afb78758Aa4C2dc706fb0fA3A98c865d2d"
var ETHEREUM_VALIDATORS_ADDR = "0x240fAa45557c61B6959162660E324Bb90984F00f"
var ETHEREUM_VALIDATORS_REGISTRY_ADDR = "0x56A6895FD37f358c17cbb3F14A864ea5Fe871F0a"
Expand All @@ -23,6 +24,14 @@ func getTokenAbi() string {
return `[{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]`
}

func getStakingEthereumContractAddress() string {
return ETHEREUM_STAKING_ADDR
}

func getStakingAbi() string {
return `[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stakeOwner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalStakedAmount","type":"uint256"}],"name":"MigratedStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stakeOwner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalStakedAmount","type":"uint256"}],"name":"Restaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stakeOwner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalStakedAmount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stakeOwner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalStakedAmount","type":"uint256"}],"name":"Unstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stakeOwner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalStakedAmount","type":"uint256"}],"name":"Withdrew","type":"event"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"unstake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"restake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_totalAmount","type":"uint256"},{"internalType":"address[]","name":"_stakeOwners","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"distributeRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_stakeOwner","type":"address"}],"name":"getStakeBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTotalStakedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_stakeOwner","type":"address"}],"name":"getUnstakeStatus","outputs":[{"internalType":"uint256","name":"cooldownAmount","type":"uint256"},{"internalType":"uint256","name":"cooldownEndTime","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IMigratableStakingContract","name":"_newStakingContract","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"migrateStakedTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]`
}

func getGuardiansEthereumContractAddress() string {
return ETHEREUM_GUARDIANS_ADDR
}
Expand Down
12 changes: 5 additions & 7 deletions services/processor/native/repository/_Elections/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,22 @@ import (
"github.com/orbs-network/orbs-contract-sdk/go/sdk/v1"
)

var PUBLIC = sdk.Export(getTokenEthereumContractAddress, getGuardiansEthereumContractAddress, getVotingEthereumContractAddress, getValidatorsEthereumContractAddress, getValidatorsRegistryEthereumContractAddress,
var PUBLIC = sdk.Export(getTokenEthereumContractAddress, getStakingEthereumContractAddress, getGuardiansEthereumContractAddress, getVotingEthereumContractAddress, getValidatorsEthereumContractAddress, getValidatorsRegistryEthereumContractAddress,
mirrorDelegationByTransfer, mirrorDelegation,
processVoting, isProcessingPeriod, hasProcessingStarted, processTrigger,
getNumberOfElections, isElectionOverdue,
getElectionPeriodInNanos, getEffectiveElectionTimeInNanos, getCurrentElectionTimeInNanos, getNextElectionTimeInNanos,
getElectedValidatorsOrbsAddress, getElectedValidatorsEthereumAddress, getElectedValidatorsEthereumAddressByBlockNumber, getElectedValidatorsOrbsAddressByBlockHeight,
getElectedValidatorsOrbsAddressByIndex, getElectedValidatorsEthereumAddressByIndex, getElectedValidatorsBlockNumberByIndex, getElectedValidatorsBlockHeightByIndex,
getCumulativeParticipationReward, getCumulativeGuardianExcellenceReward, getCumulativeValidatorReward,
getGuardianStake, getGuardianVotingWeight, getTotalStake, getValidatorStake, getValidatorVote, getExcellenceProgramGuardians,
getCurrentEthereumBlockNumber,

_fixRewardsDrift9108900,

// feature flag
switchToTimeBasedElections,

// block based
getElectionPeriod, getCurrentElectionBlockNumber, getNextElectionBlockNumber, getEffectiveElectionBlockNumber,
getProcessingStartBlockNumber, getMirroringEndBlockNumber,

// time base
//switchToTimeBasedElections,
//getElectionPeriodInNanos, getEffectiveElectionTimeInNanos, getCurrentElectionTimeInNanos, getNextElectionTimeInNanos,
)
var SYSTEM = sdk.Export(_init)
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,22 @@ import (
"time"
)

var PUBLIC = sdk.Export(getTokenEthereumContractAddress, getGuardiansEthereumContractAddress, getVotingEthereumContractAddress, getValidatorsEthereumContractAddress, getValidatorsRegistryEthereumContractAddress,
unsafetests_setTokenEthereumContractAddress, unsafetests_setGuardiansEthereumContractAddress,
var PUBLIC = sdk.Export(getTokenEthereumContractAddress, getStakingEthereumContractAddress, getGuardiansEthereumContractAddress, getVotingEthereumContractAddress, getValidatorsEthereumContractAddress, getValidatorsRegistryEthereumContractAddress,
unsafetests_setTokenEthereumContractAddress, unsafetests_setStakingEthereumContractAddress, unsafetests_setGuardiansEthereumContractAddress,
unsafetests_setVotingEthereumContractAddress, unsafetests_setValidatorsEthereumContractAddress, unsafetests_setValidatorsRegistryEthereumContractAddress,
unsafetests_setVariables, unsafetests_setElectedValidators, unsafetests_setCurrentElectedBlockNumber,
unsafetests_setCurrentElectionTimeNanos, unsafetests_setElectionMirrorPeriodInSeconds, unsafetests_setElectionVotePeriodInSeconds, unsafetests_setElectionPeriodInSeconds,
mirrorDelegationByTransfer, mirrorDelegation,
processVoting, isProcessingPeriod, hasProcessingStarted, processTrigger,
getElectionPeriod, getCurrentElectionBlockNumber, getNextElectionBlockNumber, getEffectiveElectionBlockNumber, getNumberOfElections,
getElectionPeriodInNanos, getEffectiveElectionTimeInNanos, getCurrentElectionTimeInNanos, getNextElectionTimeInNanos,
getCurrentEthereumBlockNumber, getProcessingStartBlockNumber, isElectionOverdue, getMirroringEndBlockNumber,
getElectedValidatorsOrbsAddress, getElectedValidatorsEthereumAddress, getElectedValidatorsEthereumAddressByBlockNumber, getElectedValidatorsOrbsAddressByBlockHeight,
getElectedValidatorsOrbsAddressByIndex, getElectedValidatorsEthereumAddressByIndex, getElectedValidatorsBlockNumberByIndex, getElectedValidatorsBlockHeightByIndex,
getCumulativeParticipationReward, getCumulativeGuardianExcellenceReward, getCumulativeValidatorReward,
getGuardianStake, getGuardianVotingWeight, getTotalStake, getValidatorStake, getValidatorVote, getExcellenceProgramGuardians,
switchToTimeBasedElections,
// time based
// switchToTimeBasedElections,
//getElectionPeriodInNanos, getEffectiveElectionTimeInNanos, getCurrentElectionTimeInNanos, getNextElectionTimeInNanos,
)
var SYSTEM = sdk.Export(_init)

Expand Down Expand Up @@ -64,6 +65,10 @@ func unsafetests_setTokenEthereumContractAddress(addr string) {
ETHEREUM_TOKEN_ADDR = addr
}

func unsafetests_setStakingEthereumContractAddress(addr string) {
ETHEREUM_STAKING_ADDR = addr
}

func unsafetests_setVotingEthereumContractAddress(addr string) {
ETHEREUM_VOTING_ADDR = addr
}
Expand Down
53 changes: 39 additions & 14 deletions services/processor/native/repository/_Elections/harness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type harness struct {

type actor struct {
stake int
lockedStake int
address [20]byte
}

Expand All @@ -52,14 +53,9 @@ func (g *guardian) withIsGuardian(isGuardian bool) *guardian {
return g
}

type delegator struct {
actor
delegate [20]byte
}

type validator struct {
actor
orbsAddress [20]byte
func (g *guardian) vote(asOfBlock uint64, validators ...*validator) {
g.voteBlock = asOfBlock
g.votedValidators = getValidatorAddresses(validators)
}

func getValidatorAddresses(validatorObjs []*validator) [][20]byte {
Expand All @@ -69,9 +65,20 @@ func getValidatorAddresses(validatorObjs []*validator) [][20]byte {
}
return addresses
}
func (g *guardian) vote(asOfBlock uint64, validators ...*validator) {
g.voteBlock = asOfBlock
g.votedValidators = getValidatorAddresses(validators)

type delegator struct {
actor
delegate [20]byte
}

func (d *delegator) withLockedStake(lockedStake int) *delegator {
d.lockedStake = lockedStake
return d
}

type validator struct {
actor
orbsAddress [20]byte
}

func newHarness(isTime bool) *harness {
Expand Down Expand Up @@ -182,7 +189,7 @@ func (f *harness) setupEthereumStateBeforeProcess(m Mockery) {
f.setupEthereumGuardiansDataBeforeProcess(m)

for _, d := range f.delegators {
mockStakeInEthereum(m, f.electionBlock, d.address, d.stake)
mockStakedAndLockedInEthereum(m, f.electionBlock, d.address, d.stake, d.lockedStake)
}
}

Expand All @@ -191,7 +198,7 @@ func (f *harness) setupEthereumGuardiansDataBeforeProcess(m Mockery) {
if a.isGuardian {
mockGuardianVoteInEthereum(m, f.electionBlock, a.address, a.votedValidators, a.voteBlock)
if a.voteBlock >= _getProcessCurrentElectionEarliestValidVoteBlockNumber() {
mockStakeInEthereum(m, f.electionBlock, a.address, a.stake)
mockStakedAndLockedInEthereum(m, f.electionBlock, a.address, a.stake, a.lockedStake)
}
}
}
Expand All @@ -202,7 +209,7 @@ func (f *harness) setupEthereumValidatorsBeforeProcess(m Mockery) {
validatorAddresses := make([][20]byte, len(f.validators))
for i, a := range f.validators {
validatorAddresses[i] = a.address
mockStakeInEthereum(m, f.electionBlock, a.address, a.stake)
mockStakedAndLockedInEthereum(m, f.electionBlock, a.address, a.stake, a.lockedStake)
mockValidatorOrbsAddressInEthereum(m, f.electionBlock, a.address, a.orbsAddress)
}
mockValidatorsInEthereum(m, f.electionBlock, validatorAddresses)
Expand Down Expand Up @@ -289,6 +296,11 @@ func mockValidatorOrbsAddressInEthereum(m Mockery, blockNumber uint64, validator
}, validatorAddress)
}

func mockStakedAndLockedInEthereum(m Mockery, blockNumber uint64, address [20]byte, stake int, lockedStake int) {
mockStakeInEthereum(m, blockNumber, address, stake)
mockLockedStakeInEthereum(m, blockNumber, address, lockedStake)
}

func mockStakeInEthereum(m Mockery, blockNumber uint64, address [20]byte, stake int) {
stakeValue := big.NewInt(int64(stake))
stakeValue = stakeValue.Mul(stakeValue, ETHEREUM_STAKE_FACTOR)
Expand All @@ -302,6 +314,19 @@ func mockStakeInEthereum(m Mockery, blockNumber uint64, address [20]byte, stake
}, address)
}

func mockLockedStakeInEthereum(m Mockery, blockNumber uint64, address [20]byte, stake int) {
stakeValue := big.NewInt(int64(stake))
stakeValue = stakeValue.Mul(stakeValue, ETHEREUM_STAKE_FACTOR)
m.MockEthereumCallMethodAtBlock(blockNumber, getStakingEthereumContractAddress(), getStakingAbi(), "getStakeBalanceOf", func(out interface{}) {
i, ok := out.(**big.Int)
if ok {
*i = stakeValue
} else {
panic(fmt.Sprintf("wrong something %s", out))
}
}, address)
}

func startTimeBasedGetElectionTime() uint64 {
switchToTimeBasedElections()
electionDate := 2 * ELECTION_PERIOD_LENGTH_IN_NANOS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
)

func TestOrbsVotingContract_initCurrentElection(t *testing.T) {
t.Skip()
tests := []struct {
name string
expectCurrentTime uint64
Expand Down
25 changes: 18 additions & 7 deletions services/processor/native/repository/_Elections/processing_vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,11 @@ func _collectOneValidatorDataFromEthereum(i int) {
var orbsAddress [20]byte
ethereum.CallMethodAtBlock(_getProcessCurrentElectionBlockNumber(), getValidatorsRegistryEthereumContractAddress(), getValidatorsRegistryAbi(), "getOrbsAddress", &orbsAddress, validator)
stake := _getStakeAtElection(validator)
lockedStake := _getLockedStakeAtElection(validator)

_setValidatorStake(validator[:], stake)
_setValidatorStake(validator[:], safeuint64.Add(stake, lockedStake))
_setValidatorOrbsAddress(validator[:], orbsAddress[:])
fmt.Printf("elections %10d: from ethereum validator %x, stake %d orbsAddress %x\n", _getProcessCurrentElectionBlockNumber(), validator, stake, orbsAddress)
fmt.Printf("elections %10d: from ethereum validator %x, unlocked-stake %d, locked-stake %d, orbsAddress %x\n", _getProcessCurrentElectionBlockNumber(), validator, stake, lockedStake, orbsAddress)
}

func _collectNextGuardiansDataFromEthereum() bool {
Expand All @@ -139,22 +140,24 @@ type Vote struct {
func _collectOneGuardianDataFromEthereum(i int) {
guardian := _getGuardianAtIndex(i)
stake := uint64(0)
lockedStake := uint64(0)
candidates := [][20]byte{{}}

out := Vote{}
ethereum.CallMethodAtBlock(_getProcessCurrentElectionBlockNumber(), getVotingEthereumContractAddress(), getVotingAbi(), "getCurrentVoteBytes20", &out, guardian)
voteBlockNumber := out.BlockNumber.Uint64()
if voteBlockNumber != 0 && voteBlockNumber >= _getProcessCurrentElectionEarliestValidVoteBlockNumber() {
stake = _getStakeAtElection(guardian)
lockedStake = _getLockedStakeAtElection(guardian)
candidates = out.ValidatorsBytes20
voteBlockNumber = out.BlockNumber.Uint64()
fmt.Printf("elections %10d: from ethereum guardian %x voted at %d, stake %d\n", _getProcessCurrentElectionBlockNumber(), guardian, voteBlockNumber, stake)
fmt.Printf("elections %10d: from ethereum guardian %x voted at %d, unlocked-stake %d, locked-stake %d\n", _getProcessCurrentElectionBlockNumber(), guardian, voteBlockNumber, stake, lockedStake)
} else {
voteBlockNumber = uint64(0)
fmt.Printf("elections %10d: from ethereum guardian %x vote is too old, will ignore\n", _getProcessCurrentElectionBlockNumber(), guardian)
}

_setGuardianStake(guardian[:], stake)
_setGuardianStake(guardian[:], safeuint64.Add(stake, lockedStake))
_setGuardianVoteBlockNumber(guardian[:], voteBlockNumber)
_setCandidates(guardian[:], candidates)
}
Expand All @@ -170,13 +173,15 @@ func _collectNextDelegatorStakeFromEthereum() bool {
func _collectOneDelegatorStakeFromEthereum(i int) {
delegator := _getDelegatorAtIndex(i)
stake := uint64(0)
lockedStake := uint64(0)
if !_isGuardian(delegator) {
stake = _getStakeAtElection(delegator)
lockedStake = _getLockedStakeAtElection(delegator)
} else {
fmt.Printf("elections %10d: from ethereum delegator %x is actually a guardian, will ignore\n", _getProcessCurrentElectionBlockNumber(), delegator)
}
state.WriteUint64(_formatDelegatorStakeKey(delegator[:]), stake)
fmt.Printf("elections %10d: from ethereum delegator %x , stake %d\n", _getProcessCurrentElectionBlockNumber(), delegator, stake)
state.WriteUint64(_formatDelegatorStakeKey(delegator[:]), safeuint64.Add(stake, lockedStake))
fmt.Printf("elections %10d: from ethereum delegator %x , unlocked-stake %d, locked stake %d\n", _getProcessCurrentElectionBlockNumber(), delegator, stake, lockedStake)
}

func _getStakeAtElection(ethAddr [20]byte) uint64 {
Expand All @@ -185,6 +190,12 @@ func _getStakeAtElection(ethAddr [20]byte) uint64 {
return ((*stake).Div(*stake, ETHEREUM_STAKE_FACTOR)).Uint64()
}

func _getLockedStakeAtElection(ethAddr [20]byte) uint64 {
lockedStake := new(*big.Int)
ethereum.CallMethodAtBlock(_getProcessCurrentElectionBlockNumber(), getStakingEthereumContractAddress(), getStakingAbi(), "getStakeBalanceOf", lockedStake, ethAddr)
return ((*lockedStake).Div(*lockedStake, ETHEREUM_STAKE_FACTOR)).Uint64()
}

func _calculateVotes() (candidateVotes map[[20]byte]uint64, totalVotes uint64, participants [][20]byte, participantStakes map[[20]byte]uint64, guardianAccumulatedStakes map[[20]byte]uint64) {
guardians := _getGuardians()
guardianStakes := _collectGuardiansStake(guardians)
Expand Down Expand Up @@ -218,7 +229,7 @@ func _collectDelegatorsStake(guardians map[[20]byte]bool) (delegators [][20]byte
for i := 0; i < numOfDelegators; i++ {
delegator := _getDelegatorAtIndex(i)
if !guardians[delegator] {
if _, ok := delegatorStakes[delegator]; !ok { //
if _, ok := delegatorStakes[delegator]; !ok {
stake := state.ReadUint64(_formatDelegatorStakeKey(delegator[:]))
delegatorStakes[delegator] = stake
delegators = append(delegators, delegator)
Expand Down
Loading

0 comments on commit 12e2ab6

Please sign in to comment.