Commit 12dccb29 authored by madengji's avatar madengji Committed by vipwzw

add miner custom

parent 2983a997
......@@ -94,8 +94,13 @@ coinDevFund=12
coinBaseReward=3
#委托账户最少解绑定时间(按小时)
unBindTime=24
#支持挖矿奖励的1e8小数模式,比如18coin 需要配置成1800000000 以支持小数位后的配置
#decimalMode=true
#支持挖矿奖励的1e8小数模式,比如18coin 需要配置成1800000000 以支持小数位后的配置,如果true,意味着已经打开即coinReward=1800000000
decimalMode=false
#挖矿模式, 0:普通挖矿,1:减半挖矿,2:自定义
minerMode=2
#挖矿减半周期,按高度减半
halvePeriod=1000
[consensus.sub.para]
#主链节点的grpc服务器ip,当前可以支持多ip负载均衡,如“118.31.177.1:8802,39.97.2.127:8802”
......@@ -261,6 +266,8 @@ ForkLoopCheckCommitTxDone=0
#仅平行链适用,自共识分阶段开启,缺省是0,若对应主链高度7200000之前开启过自共识,需要重新配置此分叉,并为之前自共识设置selfConsensEnablePreContract配置项
ForkParaSelfConsStages=0
ForkParaAssetTransferRbk=0
#仅平行链适用,开启挖矿交易的高度,已有代码版本可能未在0高度开启挖矿,需要设置这个高度,新版本默认从0开启挖矿,通过交易配置分阶段奖励
ForkParaFullMinerHeight=0
[fork.sub.evm]
Enable=0
......
......@@ -11,7 +11,7 @@ require (
github.com/NebulousLabs/merkletree v0.0.0-20181203152040-08d5d54b07f5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bitly/go-simplejson v0.5.0
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
github.com/btcsuite/btcd v0.20.1-beta
github.com/coreos/etcd v3.3.15+incompatible
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
......
This diff is collapsed.
......@@ -490,6 +490,10 @@ func (client *commitMsgClient) getTxsGroup(txsArr *types.Transactions) (*types.T
func (client *commitMsgClient) getExecName(commitHeight int64) string {
cfg := client.paraClient.GetAPI().GetConfig()
if cfg.IsDappFork(commitHeight, pt.ParaX, pt.ForkParaFullMinerHeight) {
return paracross.GetExecName(cfg)
}
if cfg.IsDappFork(commitHeight, pt.ParaX, pt.ForkParaSelfConsStages) {
return paracross.GetExecName(cfg)
}
......
......@@ -1254,71 +1254,6 @@ func (a *action) CrossAssetTransfer(transfer *pt.CrossAssetTransfer) (*types.Rec
return receipt, nil
}
//当前miner tx不需要校验上一个区块的衔接性,因为tx就是本节点发出,高度,preHash等都在本区块里面的blockchain做了校验
func (a *action) Miner(miner *pt.ParacrossMinerAction) (*types.Receipt, error) {
cfg := a.api.GetConfig()
if miner.Status.Title != cfg.GetTitle() || miner.Status.MainBlockHash == nil {
return nil, pt.ErrParaMinerExecErr
}
var logs []*types.ReceiptLog
var receipt = &pt.ReceiptParacrossMiner{}
log := &types.ReceiptLog{}
log.Ty = pt.TyLogParacrossMiner
receipt.Status = miner.Status
log.Log = types.Encode(receipt)
logs = append(logs, log)
minerReceipt := &types.Receipt{Ty: types.ExecOk, KV: nil, Logs: logs}
//自共识分阶段使能,综合考虑挖矿奖励和共识分配奖励,判断是否自共识使能需要采用共识的高度,而不能采用当前区块高度a.height
//考虑自共识使能区块高度100,如果采用区块高度判断,则在100高度可能收到80~99的20条共识交易,这20条交易在100高度参与共识,则无奖励可分配,而且共识高度将是80而不是100
//采用共识高度miner.Status.Height判断,则严格执行了产生奖励和分配奖励,且共识高度从100开始
isSelfConsensOn := miner.IsSelfConsensus
if cfg.IsDappFork(a.height, pt.ParaX, pt.ForkParaSelfConsStages) {
var err error
isSelfConsensOn, err = isSelfConsOn(a.db, miner.Status.Height)
if err != nil && errors.Cause(err) != pt.ErrKeyNotExist {
clog.Error("paracross miner getConsensus ", "height", miner.Status.Height, "err", err)
return nil, err
}
}
//自共识后才挖矿
if isSelfConsensOn {
//增发coins到paracross合约中,只处理发放,不做分配
totalReward := int64(0)
coinReward := cfg.MGInt("mver.consensus.paracross.coinReward", a.height)
fundReward := cfg.MGInt("mver.consensus.paracross.coinDevFund", a.height)
if coinReward > 0 {
totalReward += coinReward
}
if fundReward > 0 {
totalReward += fundReward
}
decimalMode := cfg.MIsEnable("mver.consensus.paracross.decimalMode", a.height)
if !decimalMode {
totalReward *= types.Coin
}
if totalReward > 0 {
issueReceipt, err := a.coinsAccount.ExecIssueCoins(a.execaddr, totalReward)
if err != nil {
clog.Error("paracross miner issue err", "height", miner.Status.Height,
"execAddr", a.execaddr, "amount", totalReward)
return nil, err
}
minerReceipt = mergeReceipt(minerReceipt, issueReceipt)
}
}
return minerReceipt, nil
}
func getTitleFrom(exec []byte) ([]byte, error) {
last := bytes.LastIndex(exec, []byte("."))
if last == -1 {
......
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
import (
"github.com/33cn/chain33/types"
pt "github.com/33cn/plugin/plugin/dapp/paracross/types"
"github.com/pkg/errors"
)
//当前miner tx不需要校验上一个区块的衔接性,因为tx就是本节点发出,高度,preHash等都在本区块里面的blockchain做了校验
//note: 平行链的Miner从Height=1开始, 创世区块不挖矿
func (a *action) Miner(miner *pt.ParacrossMinerAction) (*types.Receipt, error) {
cfg := a.api.GetConfig()
if miner.Status.Title != cfg.GetTitle() || miner.Status.MainBlockHash == nil {
return nil, pt.ErrParaMinerExecErr
}
var logs []*types.ReceiptLog
var receipt = &pt.ReceiptParacrossMiner{}
log := &types.ReceiptLog{}
log.Ty = pt.TyLogParacrossMiner
receipt.Status = miner.Status
log.Log = types.Encode(receipt)
logs = append(logs, log)
minerReceipt := &types.Receipt{Ty: types.ExecOk, KV: nil, Logs: logs}
on, err := a.isSelfConsensOn(miner)
if err != nil {
return nil, err
}
//自共识后才挖矿
if on {
r, err := a.issueCoins(miner)
if err != nil {
return nil, err
}
minerReceipt = mergeReceipt(minerReceipt, r)
}
return minerReceipt, nil
}
func (a *action) isSelfConsensOn(miner *pt.ParacrossMinerAction) (bool, error) {
cfg := a.api.GetConfig()
//ForkParaInitMinerHeight高度后,默认全部挖矿,产生在paracross执行器地址,如果自共识分阶段,也只是分阶段奖励,挖矿一直产生
if cfg.IsDappFork(a.height, pt.ParaX, pt.ForkParaFullMinerHeight) {
return true, nil
}
isSelfConsensOn := miner.IsSelfConsensus
//自共识分阶段使能,综合考虑挖矿奖励和共识分配奖励,判断是否自共识使能需要采用共识的高度,而不能采用当前区块高度a.height
//考虑自共识使能区块高度100,如果采用区块高度判断,则在100高度可能收到80~99的20条共识交易,这20条交易在100高度参与共识,则无奖励可分配,而且共识高度将是80而不是100
//采用共识高度miner.Status.Height判断,则严格执行了产生奖励和分配奖励,且共识高度从100开始
if cfg.IsDappFork(a.height, pt.ParaX, pt.ForkParaSelfConsStages) {
var err error
isSelfConsensOn, err = isSelfConsOn(a.db, miner.Status.Height)
if err != nil && errors.Cause(err) != pt.ErrKeyNotExist {
clog.Error("paracross miner getConsensus ", "height", miner.Status.Height, "err", err)
return false, err
}
}
return isSelfConsensOn, nil
}
const (
normalMiner = iota
halveMiner
customMiner
)
type rewardValFn func(cfg *types.Chain33Config, height int64) (int64, int64, int64)
var getConfigRewards = make(map[int]rewardValFn)
func init() {
getConfigRewards[normalMiner] = getNormalReward
}
func (a *action) issueCoins(miner *pt.ParacrossMinerAction) (*types.Receipt, error) {
cfg := a.api.GetConfig()
mode := int(cfg.MGInt("mver.consensus.paracross.minerMode", a.height))
if getConfigRewards[mode] == nil {
panic("getTotalReward not be set depend on consensus.paracross.minerMode")
}
coinReward, coinFundReward, _ := getConfigRewards[mode](cfg, a.height)
totalReward := coinReward + coinFundReward
if totalReward > 0 {
issueReceipt, err := a.coinsAccount.ExecIssueCoins(a.execaddr, totalReward)
if err != nil {
clog.Error("paracross miner issue err", "height", miner.Status.Height,
"execAddr", a.execaddr, "amount", totalReward)
return nil, err
}
return issueReceipt, nil
}
return nil, nil
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
import (
"fmt"
"github.com/33cn/chain33/types"
pt "github.com/33cn/plugin/plugin/dapp/paracross/types"
"math"
"strconv"
"strings"
)
const (
startN uint32 = 11
minerUnit int64 = 10000
//addr:quota的配置
// 18.75/100份额,由于浮点数的原因,都扩大100倍即1875/10000
addrStaffA = "1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4:1875" //18.75quota 1875/10000
addrStaffB = "1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR:1875" //18.75 quota
addrBoss = "1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k:6250" //62.50 quota
)
var addrs = []string{addrStaffA, addrStaffB, addrBoss}
var addrsMap = make(map[string]int64)
func checkQuota() {
var sum int64
for _, a := range addrs {
val := strings.Split(a, ":")
v, err := strconv.Atoi(val[1])
if err != nil || v == 0 {
panic(fmt.Sprintf("minerCustom checkQuota err=%s,addr=%s", err, a))
}
addrsMap[val[0]] = int64(v)
sum += int64(v)
}
if sum > minerUnit {
panic(fmt.Sprintf("minerCustom checkQuota sum=%d beyond %d", sum, minerUnit))
}
}
func init() {
getConfigRewards[customMiner] = getCustomReward
rewardMiner[customMiner] = customRewardMiner
checkQuota()
}
func getCustomReward(cfg *types.Chain33Config, height int64) (int64, int64, int64) {
n := getCurrentN(height)
return calcCoins(n), 0, 0
}
func getNHeight(n uint32) int64 {
v := 1 << n
return 4096 * (int64(v) - 1)
}
//高度 4096*(2^n -1)+1 开始减半, n=1:1~4096, n=2:4096+1~12288
func getCurrentN(height int64) uint32 {
if height <= 0 {
panic("height should bigger than 0")
}
var totalCycle uint32 = 1 << 30
offsetHeight := getNHeight(6)
for n := uint32(0); n < totalCycle; n++ {
leftVal := getNHeight(n)
rightVal := getNHeight(n + 1)
if offsetHeight+height > leftVal && offsetHeight+height <= rightVal {
return n + 1
}
}
panic("not enought total cycle")
}
//客户原有链大约50s出一个块,一个块是32/16/8..coins, 我们平均5s一个块,需要/10
func calcCoins(n uint32) int64 {
if n <= startN {
return 1e7 * (1 << (startN - n))
}
v := 1 << (n - startN)
vf := 1e7 / v
return int64(math.Trunc(float64(vf)))
}
func customRewardMiner(coinReward int64, miners []string, height int64) ([]*pt.ParaMinerReward, int64) {
//找零
var change int64
var rewards []*pt.ParaMinerReward
coins, _, _ := getCustomReward(nil, height)
var sum int64
//get quto to miner
for _, m := range miners {
if v, ok := addrsMap[m]; ok {
amount := (v * coins) / minerUnit
sum += amount
r := &pt.ParaMinerReward{Addr: m, Amount: amount}
rewards = append(rewards, r)
}
}
//所有找零给addrC
change = coins - sum
if change > 0 {
val := strings.Split(addrBoss, ":")
r := &pt.ParaMinerReward{Addr: val[0], Amount: change}
rewards = append(rewards, r)
}
return rewards, 0
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
import (
"fmt"
"github.com/bmizerany/assert"
"testing"
_ "github.com/33cn/plugin/plugin/crypto/bls"
)
func TestGetNHeight(t *testing.T) {
h := getNHeight(1)
assert.Equal(t, int64(4096), h)
h = getNHeight(2)
assert.Equal(t, int64(12288), h)
h = getNHeight(6)
assert.Equal(t, int64(258048), h)
h = getNHeight(7)
assert.Equal(t, int64(520192), h)
}
func TestGetN(t *testing.T) {
n := getCurrentN(1)
assert.Equal(t, uint32(7), n)
n = getCurrentN(262144)
assert.Equal(t, uint32(7), n)
n = getCurrentN(262145)
assert.Equal(t, uint32(8), n)
n = getCurrentN(3932160)
assert.Equal(t, uint32(10), n)
n = getCurrentN(3932161)
assert.Equal(t, uint32(11), n)
}
func TestGetBlockNum(t *testing.T) {
offset := getNHeight(6)
for n := uint32(7); n < 50; n++ {
blocks := getNHeight(n) - offset
secs := blocks * 5
secsOfYear := int64(60 * 60 * 24 * 365)
year := secs / secsOfYear
fmt.Println("n=", n, "coins=", calcCoins(n), "height=", blocks, "year=", year)
}
}
func TestGetCoins(t *testing.T) {
c := calcCoins(7)
assert.Equal(t, int64(1.6*1e8), c)
c = calcCoins(6)
assert.Equal(t, int64(3.2*1e8), c)
//for i:=uint32(0);i<50;i++{
// coin := calcCoins(i)
// fmt.Println("n",i,"coins",coin)
//}
}
func getCustomRewardMinerRst(miners []string, height int64) (map[string]int64, int64) {
res, change := customRewardMiner(0, miners, height)
check := make(map[string]int64)
for _, r := range res {
//fmt.Println("addr",r.Addr,"amount",r.Amount)
check[r.Addr] += r.Amount
}
return check, change
}
func TestCustomRewardMiner(t *testing.T) {
staffA := "1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4"
staffB := "1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR"
staffBoss := "1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k"
miners := []string{staffA, staffB, staffBoss}
offsetHeight := getNHeight(6)
//height=1
height := int64(1)
check, change := getCustomRewardMinerRst(miners, height)
assert.Equal(t, int64(3*1e7), check[staffA])
assert.Equal(t, int64(3*1e7), check[staffB])
assert.Equal(t, int64(10*1e7), check[staffBoss])
assert.Equal(t, int64(0), change)
//height=262144
var n uint32
n = 8
height = getNHeight(n)
coins := calcCoins(n)
fmt.Println("n=", n, "coins=", coins)
check, change = getCustomRewardMinerRst(miners, height-offsetHeight)
assert.Equal(t, int64(1.5*1e7), check[staffA])
assert.Equal(t, int64(1.5*1e7), check[staffB])
assert.Equal(t, int64(5*1e7), check[staffBoss])
assert.Equal(t, int64(0), change)
//
n = 17
height = getNHeight(n)
coins = calcCoins(n)
fmt.Println("n=", n, "coins=", coins)
check, change = getCustomRewardMinerRst(miners, height-offsetHeight)
assert.Equal(t, int64(29296), check[staffA])
assert.Equal(t, int64(29296), check[staffB])
assert.Equal(t, int64(97658), check[staffBoss])
assert.Equal(t, int64(0), change)
//
n = 18
height = getNHeight(n)
coins = calcCoins(n)
fmt.Println("n=", n, "coins=", coins)
check, change = getCustomRewardMinerRst(miners, height-offsetHeight)
assert.Equal(t, int64(14648), check[staffA])
assert.Equal(t, int64(14648), check[staffB])
assert.Equal(t, int64(48829), check[staffBoss])
assert.Equal(t, int64(0), change)
}
......@@ -40,28 +40,64 @@ func (a *action) getBindAddrs(nodes []string, statusHeight int64) (*pt.ParaNodeB
}
func (a *action) rewardSuperNode(coinReward int64, miners []string, statusHeight int64) (*types.Receipt, int64, error) {
//分配给矿工的单位奖励
minerUnit := coinReward / int64(len(miners))
type rewardFn func(coinReward int64, miners []string, height int64) ([]*pt.ParaMinerReward, int64)
var rewardMiner = make(map[int]rewardFn)
func init() {
rewardMiner[normalMiner] = rewardEven
}
func rewardEven(coinReward int64, miners []string, height int64) ([]*pt.ParaMinerReward, int64) {
//找零
var change int64
receipt := &types.Receipt{Ty: types.ExecOk}
var rewards []*pt.ParaMinerReward
//分配给矿工的平均奖励
minerUnit := coinReward / int64(len(miners))
if minerUnit > 0 {
for _, m := range miners {
r := &pt.ParaMinerReward{Addr: m, Amount: minerUnit}
rewards = append(rewards, r)
}
//如果不等分转到发展基金
change = coinReward % minerUnit
for _, addr := range miners {
rep, err := a.coinsAccount.ExecDeposit(addr, a.execaddr, minerUnit)
}
return rewards, change
}
if err != nil {
clog.Error("paracross super node reward deposit err", "height", statusHeight,
"execAddr", a.execaddr, "minerAddr", addr, "amount", minerUnit, "err", err)
return nil, 0, err
}
receipt = mergeReceipt(receipt, rep)
}
func (a *action) rewardSuperNode(coinReward int64, miners []string, statusHeight int64) (*types.Receipt, int64, error) {
cfg := a.api.GetConfig()
receipt := &types.Receipt{Ty: types.ExecOk}
mode := int(cfg.MGInt("mver.consensus.paracross.minerMode", a.height))
if rewardMiner[mode] == nil {
panic("getReward not be set depend on consensus.paracross.minerMode")
}
rewards, change := rewardMiner[mode](coinReward, miners, statusHeight)
resp, err := a.rewardDeposit(rewards, statusHeight)
if err != nil {
return nil, 0, err
}
receipt = mergeReceipt(receipt, resp)
return receipt, change, nil
}
func (a *action) rewardDeposit(rewards []*pt.ParaMinerReward, statusHeight int64) (*types.Receipt, error) {
receipt := &types.Receipt{}
for _, v := range rewards {
rep, err := a.coinsAccount.ExecDeposit(v.Addr, a.execaddr, v.Amount)
if err != nil {
clog.Error("paracross super node reward deposit err", "height", statusHeight,
"execAddr", a.execaddr, "minerAddr", v.Addr, "amount", v.Amount, "err", err)
return nil, err
}
receipt = mergeReceipt(receipt, rep)
}
return receipt, nil
}
//奖励委托挖矿账户
func (a *action) rewardBindAddr(coinReward int64, bindList *pt.ParaNodeBindList, statusHeight int64) (*types.Receipt, int64, error) {
if coinReward <= 0 {
......@@ -103,28 +139,42 @@ func (a *action) rewardBindAddr(coinReward int64, bindList *pt.ParaNodeBindList,
return receipt, change, nil
}
// reward 挖矿奖励, 主要处理挖矿分配逻辑,先实现基本策略,后面根据需求进行重构
func (a *action) reward(nodeStatus *pt.ParacrossNodeStatus, stat *pt.ParacrossHeightStatus) (*types.Receipt, error) {
//获取挖矿相关配置,这里需注意是共识的高度,而不是交易的高度
cfg := a.api.GetConfig()
coinReward := cfg.MGInt("mver.consensus.paracross.coinReward", nodeStatus.Height)
fundReward := cfg.MGInt("mver.consensus.paracross.coinDevFund", nodeStatus.Height)
coinBaseReward := cfg.MGInt("mver.consensus.paracross.coinBaseReward", nodeStatus.Height)
func getNormalReward(cfg *types.Chain33Config, height int64) (int64, int64, int64) {
coinReward := cfg.MGInt("mver.consensus.paracross.coinReward", height)
fundReward := cfg.MGInt("mver.consensus.paracross.coinDevFund", height)
coinBaseReward := cfg.MGInt("mver.consensus.paracross.coinBaseReward", height)
if coinReward < 0 || fundReward < 0 || coinBaseReward < 0 {
panic("para config consensus.paracross.coinReward should bigger than 0")
}
decimalMode := cfg.MIsEnable("mver.consensus.paracross.decimalMode", nodeStatus.Height)
//decimalMode=false,意味着精简模式,需要乘1e8
decimalMode := cfg.MIsEnable("mver.consensus.paracross.decimalMode", height)
if !decimalMode {
coinReward *= types.Coin
fundReward *= types.Coin
coinBaseReward *= types.Coin
}
fundAddr := cfg.MGStr("mver.consensus.fundKeyAddr", nodeStatus.Height)
//防止coinBaseReward 设置出错场景, coinBaseReward 一定要比coinReward小
if coinBaseReward >= coinReward {
coinBaseReward = coinReward / 10
}
return coinReward, fundReward, coinBaseReward
}
// reward 挖矿奖励, 主要处理挖矿分配逻辑,先实现基本策略,后面根据需求进行重构
func (a *action) reward(nodeStatus *pt.ParacrossNodeStatus, stat *pt.ParacrossHeightStatus) (*types.Receipt, error) {
//获取挖矿相关配置,这里需注意是共识的高度,而不是交易的高度
cfg := a.api.GetConfig()
//此分叉后 0高度不产生挖矿奖励,也就是以后的新版本默认0高度不产生挖矿奖励
if nodeStatus.Height == 0 && cfg.IsDappFork(nodeStatus.Height, pt.ParaX, pt.ForkParaFullMinerHeight) {
return nil, nil
}
mode := int(cfg.MGInt("mver.consensus.paracross.minerMode", a.height))
coinReward, fundReward, coinBaseReward := getConfigRewards[mode](cfg, nodeStatus.Height)
fundAddr := cfg.MGStr("mver.consensus.fundKeyAddr", nodeStatus.Height)
//超级节点地址
nodeAddrs := getSuperNodes(stat.Details, nodeStatus.BlockHash)
//委托地址
......
......@@ -323,6 +323,11 @@ message ParacrossMinerAction {
bool isSelfConsensus = 2;
}
message ParaMinerReward{
string addr = 1;
int64 amount = 2;
}
message CrossAssetTransfer {
string assetExec = 1;
string assetSymbol = 2;
......
This diff is collapsed.
......@@ -28,6 +28,8 @@ var (
ForkParaSelfConsStages = "ForkParaSelfConsStages"
// ForkParaAssetTransferRbk 平行链资产转移平行链失败主链回滚
ForkParaAssetTransferRbk = "ForkParaAssetTransferRbk"
// ForkParaFullMinerHeight 平行链全挖矿开启高度
ForkParaFullMinerHeight = "ForkParaFullMinerHeight"
// ParaConsSubConf sub
ParaConsSubConf = "consensus.sub.para"
......@@ -59,6 +61,7 @@ func InitFork(cfg *types.Chain33Config) {
//只在平行链启用
cfg.RegisterDappFork(ParaX, ForkParaSelfConsStages, types.MaxHeight)
cfg.RegisterDappFork(ParaX, ForkParaFullMinerHeight, types.MaxHeight)
}
//InitExecutor ...
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment