Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Makefile, wemix: rewards distribution according to stakes #41

Merged
merged 3 commits into from
May 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ BEGIN { print "package wemix\n"; } \
n = "Registry"; \
print "var " n "Abi = `{ \"contractName\": \"" n "\", \"abi\": " $$0 "}`"; \
} \
/^var Staking_contract/ { \
/^var StakingImp_contract/ { \
sub("^var[^(]*\\(","",$$0); sub("\\);$$","",$$0); \
n = "Staking"; \
print "var " n "Abi = `{ \"contractName\": \"" n "\", \"abi\": " $$0 "}`"; \
Expand Down
152 changes: 114 additions & 38 deletions wemix/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ type wemixNode struct {
}

type wemixMember struct {
Addr common.Address `json:"address"`
Stake *big.Int `json:"stake"`
Staker common.Address `json:"address"`
Reward common.Address `json:"reward"`
Stake *big.Int `json:"stake"`
}

type wemixAdmin struct {
Expand Down Expand Up @@ -113,11 +114,11 @@ type blockBuildParameters struct {

// reward related parameters
type rewardParameters struct {
rewardAmount *big.Int
staker, ecoSystem, maintenance *common.Address
members []*wemixMember
distributionMethod []*big.Int
blocksPer int64
rewardAmount *big.Int
staker, ecoSystem, maintenance, feeCollector *common.Address
members []*wemixMember
distributionMethod []*big.Int
blocksPer int64
}

var (
Expand Down Expand Up @@ -280,7 +281,7 @@ func (ma *wemixAdmin) getInt(ctx context.Context, contract *metclient.RemoteCont
}

// TODO: error handling
func (ma *wemixAdmin) getRegGovEnvContracts(ctx context.Context, height *big.Int) (reg, gov, env *metclient.RemoteContract, err error) {
func (ma *wemixAdmin) getRegGovEnvContracts(ctx context.Context, height *big.Int) (reg, gov, env, staking *metclient.RemoteContract, err error) {
if ma.registry == nil {
err = wemixminer.ErrNotInitialized
return
Expand All @@ -297,6 +298,10 @@ func (ma *wemixAdmin) getRegGovEnvContracts(ctx context.Context, height *big.Int
Cli: ma.cli,
Abi: ma.gov.Abi,
}
staking = &metclient.RemoteContract{
Cli: ma.cli,
Abi: ma.staking.Abi,
}
if ma.registry.To != nil {
reg.To = ma.registry.To
} else {
Expand Down Expand Up @@ -325,6 +330,14 @@ func (ma *wemixAdmin) getRegGovEnvContracts(ctx context.Context, height *big.Int
env.To = &common.Address{}
env.To.SetBytes(addr.Bytes())

input = []interface{}{metclient.ToBytes32("Staking")}
if err = metclient.CallContract(ctx, reg, "getContractAddress", input, &addr, height); err != nil {
err = wemixminer.ErrNotInitialized
return
}
staking.To = &common.Address{}
staking.To.SetBytes(addr.Bytes())

return
}

Expand Down Expand Up @@ -419,7 +432,7 @@ func (ma *wemixAdmin) getWemixNodes(ctx context.Context, block *big.Int) ([]*wem
return nil, err
}

if err = metclient.CallContract(ctx, ma.gov, "getReward", input, &addr, block); err != nil {
if err = metclient.CallContract(ctx, ma.gov, "getMember", input, &addr, block); err != nil {
return nil, err
}

Expand All @@ -445,7 +458,7 @@ func (ma *wemixAdmin) getWemixNodes(ctx context.Context, block *big.Int) ([]*wem

func (ma *wemixAdmin) getRewardParams(ctx context.Context, height *big.Int) (*rewardParameters, error) {
rp := &rewardParameters{}
reg, gov, env, err := ma.getRegGovEnvContracts(ctx, height)
reg, gov, env, staking, err := ma.getRegGovEnvContracts(ctx, height)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -481,6 +494,15 @@ func (ma *wemixAdmin) getRewardParams(ctx context.Context, height *big.Int) (*re
rp.maintenance = &common.Address{}
rp.maintenance.SetBytes(addr.Bytes())

input = []interface{}{metclient.ToBytes32("FeeCollector")}
if err = metclient.CallContract(ctx, reg, "getContractAddress", input, &addr, height); err != nil {
// ignore error
rp.feeCollector = nil
} else {
rp.feeCollector = &common.Address{}
rp.feeCollector.SetBytes(addr.Bytes())
}

rp.blocksPer, err = ma.getInt(ctx, env, height, "getBlocksPer")
if err != nil {
return nil, err
Expand All @@ -490,16 +512,25 @@ func (ma *wemixAdmin) getRewardParams(ctx context.Context, height *big.Int) (*re
return nil, err
} else {
for i := int64(1); i <= count; i++ {
var rewardAddress common.Address
var stake *big.Int

input = []interface{}{big.NewInt(i)}
if err = metclient.CallContract(ctx, gov, "getReward", input, &addr, height); err != nil {
if err = metclient.CallContract(ctx, gov, "getMember", input, &addr, height); err != nil {
return nil, err
}
input = []interface{}{big.NewInt(i)}
if err = metclient.CallContract(ctx, gov, "getReward", input, &rewardAddress, height); err != nil {
return nil, err
}
input = []interface{}{addr}
if err = metclient.CallContract(ctx, staking, "lockedBalanceOf", input, &stake, height); err != nil {
return nil, err
}
// NB. no staking consideration
// if err = metclient.CallContract(ctx, staking, "lockedBalanceOf", input, &stake, height); err != nil {
// return nil, err
// }
rp.members = append(rp.members, &wemixMember{
Addr: addr,
Staker: addr,
Reward: rewardAddress,
Stake: stake,
})
}
}
Expand Down Expand Up @@ -540,12 +571,19 @@ func (ma *wemixAdmin) getRewardAccounts(ctx context.Context, block *big.Int) (re
}

for i := int64(1); i <= count; i++ {
var rewardAddress common.Address

input = []interface{}{big.NewInt(i)}
err = metclient.CallContract(ctx, ma.gov, "getReward", input,
err = metclient.CallContract(ctx, ma.gov, "getMember", input,
&addr, block)
if err != nil {
return
}
err = metclient.CallContract(ctx, ma.gov, "getReward", input,
&rewardAddress, block)
if err != nil {
return
}
input = []interface{}{addr}
err = metclient.CallContract(ctx, ma.staking, "lockedBalanceOf", input,
&stake, block)
Expand All @@ -554,8 +592,9 @@ func (ma *wemixAdmin) getRewardAccounts(ctx context.Context, block *big.Int) (re
}

members = append(members, &wemixMember{
Addr: addr,
Stake: stake,
Staker: addr,
Reward: rewardAddress,
Stake: stake,
})
}

Expand Down Expand Up @@ -1010,6 +1049,8 @@ func handleBlock94Rewards(height *big.Int, rp *rewardParameters, fees *big.Int)
return testnetBlock94Rewards
}

// distributeRewards divides the rewardAmount among members according to their
// stakes, and allocates rewards to staker, ecoSystem, and maintenance accounts.
func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([]reward, error) {
dm := new(big.Int)
for i := 0; i < len(rp.distributionMethod); i++ {
Expand All @@ -1032,27 +1073,56 @@ func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([]
maintenanceAmount.Sub(maintenanceAmount, stakerAmount)
maintenanceAmount.Sub(maintenanceAmount, ecoSystemAmount)

// fees go to maintenance
maintenanceAmount.Add(maintenanceAmount, fees)
// if feeCollector is not specified, i.e. nil, fees go to maintenance
if rp.feeCollector == nil {
maintenanceAmount.Add(maintenanceAmount, fees)
}

var rewards []reward
if n := len(rp.members); n > 0 {
v0, v1 := big.NewInt(0), big.NewInt(1)
vn := big.NewInt(int64(n))
b := new(big.Int).Set(minerAmount)
d := new(big.Int)
d.Div(b, vn)
stakeTotal, equalStakes := big.NewInt(0), true
for i := 0; i < n; i++ {
rewards = append(rewards, reward{
Addr: rp.members[i].Addr,
Reward: new(big.Int).Set(d),
})
if equalStakes && i < n-1 && rp.members[i].Stake.Cmp(rp.members[i+1].Stake) != 0 {
equalStakes = false
}
stakeTotal.Add(stakeTotal, rp.members[i].Stake)
}
d.Mul(d, vn)
b.Sub(b, d)
for i, ix := 0, height.Int64()%int64(n); b.Cmp(v0) > 0; i, ix = i+1, (ix+1)%int64(n) {
rewards[ix].Reward.Add(rewards[ix].Reward, v1)
b.Sub(b, v1)

if equalStakes {
v0, v1 := big.NewInt(0), big.NewInt(1)
vn := big.NewInt(int64(n))
b := new(big.Int).Set(minerAmount)
d := new(big.Int)
d.Div(b, vn)
for i := 0; i < n; i++ {
rewards = append(rewards, reward{
Addr: rp.members[i].Reward,
Reward: new(big.Int).Set(d),
})
}
d.Mul(d, vn)
b.Sub(b, d)
for i, ix := 0, height.Int64()%int64(n); b.Cmp(v0) > 0; i, ix = i+1, (ix+1)%int64(n) {
rewards[ix].Reward.Add(rewards[ix].Reward, v1)
b.Sub(b, v1)
}
} else {
// rewards distributed according to stakes
v0, v1 := big.NewInt(0), big.NewInt(1)
remainder := new(big.Int).Set(minerAmount)
for i := 0; i < n; i++ {
memberReward := new(big.Int).Mul(minerAmount, rp.members[i].Stake)
memberReward.Div(memberReward, stakeTotal)
remainder.Sub(remainder, memberReward)
rewards = append(rewards, reward{
Addr: rp.members[i].Reward,
Reward: memberReward,
})
}
for ix := height.Int64() % int64(n); remainder.Cmp(v0) > 0; ix = (ix + 1) % int64(n) {
rewards[ix].Reward.Add(rewards[ix].Reward, v1)
remainder.Sub(remainder, v1)
}
}
}
rewards = append(rewards, reward{
Expand All @@ -1067,6 +1137,12 @@ func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([]
Addr: *rp.maintenance,
Reward: maintenanceAmount,
})
if rp.feeCollector != nil {
rewards = append(rewards, reward{
Addr: *rp.feeCollector,
Reward: fees,
})
}
return rewards, nil
}

Expand Down Expand Up @@ -1100,7 +1176,7 @@ func (ma *wemixAdmin) calculateRewards(num, blockReward, fees *big.Int, addBalan
if len(rp.members) > 0 {
mix := int(num.Int64()/ma.blocksPer) % len(rp.members)
coinbase = &common.Address{}
coinbase.SetBytes(rp.members[mix].Addr.Bytes())
coinbase.SetBytes(rp.members[mix].Reward.Bytes())
}

rr, errr := distributeRewards(num, rp, fees)
Expand Down Expand Up @@ -1151,7 +1227,7 @@ func verifyBlockSig(height *big.Int, coinbase common.Address, nodeId []byte, has

// get nodeid from the coinbase
num := new(big.Int).Sub(height, common.Big1)
_, gov, _, err := admin.getRegGovEnvContracts(ctx, num)
_, gov, _, _, err := admin.getRegGovEnvContracts(ctx, num)
if err != nil {
return err == wemixminer.ErrNotInitialized
} else if count, err := admin.getInt(ctx, gov, num, "getMemberLength"); err != nil || count == 0 {
Expand Down Expand Up @@ -1339,7 +1415,7 @@ func getBlockBuildParameters(height *big.Int) (blockInterval int64, maxBaseFee,
defer cancel()

var env, gov *metclient.RemoteContract
if _, gov, env, err = admin.getRegGovEnvContracts(ctx, height); err != nil {
if _, gov, env, _, err = admin.getRegGovEnvContracts(ctx, height); err != nil {
err = wemixminer.ErrNotInitialized
return
} else if count, err2 := admin.getInt(ctx, gov, height, "getMemberLength"); err2 != nil || count == 0 {
Expand Down
8 changes: 4 additions & 4 deletions wemix/miner_limit.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (ma *wemixAdmin) collectMinerStates(height *big.Int) []*wemixapi.WemixMiner
err error
)
ctx = context.Background()
if _, gov, _, err = ma.getRegGovEnvContracts(ctx, height); err != nil {
if _, gov, _, _, err = ma.getRegGovEnvContracts(ctx, height); err != nil {
return nil
}
e, err := getCoinbaseEnodeCache(ctx, height, gov)
Expand Down Expand Up @@ -123,7 +123,7 @@ func enodeExists(ctx context.Context, height *big.Int, gov *metclient.RemoteCont
// returns wemix nodes at given height
func getNodesAt(height *big.Int) ([]*wemixNode, error) {
ctx := context.Background()
if _, gov, _, err := admin.getRegGovEnvContracts(ctx, height); err != nil {
if _, gov, _, _, err := admin.getRegGovEnvContracts(ctx, height); err != nil {
return nil, wemixminer.ErrNotInitialized
} else if e, err := getCoinbaseEnodeCache(ctx, height, gov); err != nil {
return nil, err
Expand Down Expand Up @@ -214,7 +214,7 @@ func (ma *wemixAdmin) isEligibleMiner(height *big.Int) (bool, error) {
if err != nil {
return false, wemixminer.ErrNotInitialized
}
if _, gov, _, err = ma.getRegGovEnvContracts(ctx, prev); err != nil {
if _, gov, _, _, err = ma.getRegGovEnvContracts(ctx, prev); err != nil {
return false, wemixminer.ErrNotInitialized
}
e, err := getCoinbaseEnodeCache(ctx, prev, gov)
Expand Down Expand Up @@ -252,7 +252,7 @@ func (ma *wemixAdmin) nextMinerCandidates(height *big.Int) ([]*wemixNode, error)
err error
)
ctx = context.Background()
if _, gov, _, err = ma.getRegGovEnvContracts(ctx, height); err != nil {
if _, gov, _, _, err = ma.getRegGovEnvContracts(ctx, height); err != nil {
return nil, wemixminer.ErrNotInitialized
}
e, err := getCoinbaseEnodeCache(ctx, height, gov)
Expand Down
Loading