}
type Chain struct {
+ ChainID string `json:"chain_id"`
Name string `json:"name"`
Upstream string `json:"upstream"`
SyncSeconds uint64 `json:"sync_seconds"`
-- Table structure for block_state
-- ----------------------------
DROP TABLE IF EXISTS `block_state`;
-CREATE TABLE `block_state` (
+CREATE TABLE `block_states` (
`height` int(11) NOT NULL,
`block_hash` varchar(64) NOT NULL
) ENGINE = InnoDB DEFAULT CHARSET=utf8;
-- Table structure for vote
-- ----------------------------
DROP TABLE IF EXISTS `vote`;
-CREATE TABLE `vote` (
+CREATE TABLE `utxos` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`xpub` varchar(128) NOT NULL,
`voter_address` varchar(62) NOT NULL,
`vote_height` int(11) NOT NULL,
- `vote_num` int(11) NOT NULL,
+ `vote_num` bigint(21) NOT NULL,
`veto_height` int(11) NOT NULL,
`output_id` varchar(64) NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
package orm
type BlockState struct {
- Height uint64
- BlockHash string
+ Height uint64 `json:"height"`
+ BlockHash string `json:"block_hash"`
}
package reward
import (
+ "time"
+
"github.com/jinzhu/gorm"
+ "github.com/vapor/errors"
"github.com/vapor/toolbar/reward/config"
"github.com/vapor/toolbar/reward/database/orm"
instance "github.com/vapor/toolbar/reward/reward"
period uint64
}
-func NewReward(db *gorm.DB, cfg *config.Config, period uint64) *Reward {
+func NewReward(db *gorm.DB, cfg *config.Config, period uint64, quit chan struct{}) *Reward {
voteInfoCh := make(chan instance.VoteInfo)
overReadCh := make(chan struct{})
var countReward CountReward
if len(cfg.VoteConf) != 0 {
- countReward = instance.NewVote(cfg.VoteConf, voteInfoCh, overReadCh, period)
+ countReward = instance.NewVote(cfg.VoteConf, voteInfoCh, overReadCh, quit, period)
} else if cfg.OptionalNodeConf != nil {
// OptionalNode reward instance
}
minHeight := (1 + 1200*(r.period-1))
maxHeight := 1200 * r.period
- rows, err := r.db.Model(&orm.Utxo{}).Select("xpub, voter_address, vote_num, vote_height, veto_height").Where("vote_height BETWEEN ? and ? and xpub in (?)", minHeight, maxHeight, xpubs).Rows()
+
+ ticker := time.NewTicker(time.Duration(r.cfg.Chain.SyncSeconds) * time.Second)
+ for ; true; <-ticker.C {
+ blockState := &orm.BlockState{}
+ if err := r.db.First(blockState).Error; err != nil {
+ return errors.Wrap(err, "query blockState")
+ }
+ if blockState.Height >= maxHeight {
+ break
+ }
+
+ }
+
+ //rows, err := r.db.Model(&orm.Utxo{}).Select("xpub, voter_address, vote_num, vote_height, veto_height").Where("vote_height BETWEEN ? and ? and xpub in (?)", minHeight, maxHeight, xpubs).Rows()
+ rows, err := r.db.Model(&orm.Utxo{}).Select("xpub, voter_address, vote_num, vote_height, veto_height").Where("veto_height >= ? and vote_height <= ? and xpub in (?)", minHeight, maxHeight, xpubs).Rows()
+
if err != nil {
return err
}
return err
}
- if vetoHeight > 0 && vetoHeight < 1200*r.period {
+ if vetoHeight < 1200*r.period {
voteBlockNum = vetoHeight - voteHeight
} else {
voteBlockNum = 1200*r.period - voteHeight
func (r *Reward) Start() {
go r.readVoteInfo()
- go r.countReward.Start()
+ r.countReward.Start()
}
return nil, errors.New("no coinbase")
}
+
+type getBlockCountResp struct {
+ BlockCount uint64 `json:"block_count"`
+}
+
+func (t *Transaction) GetCurrentHeight() (uint64, error) {
+ url := "/get-block-count"
+ res := &getBlockCountResp{}
+ return res.BlockCount, t.request(url, nil, res)
+}
nodes []config.VoteRewardConfig
ch chan VoteInfo
overReadCH chan struct{}
+ quit chan struct{}
voteResults map[string]*voteResult
voterRewards map[string]*voterReward
coinBaseReward map[string]*coinBaseReward
period uint64
}
-func NewVote(nodes []config.VoteRewardConfig, ch chan VoteInfo, overReadCH chan struct{}, period uint64) *Vote {
+func NewVote(nodes []config.VoteRewardConfig, ch chan VoteInfo, overReadCH, quit chan struct{}, period uint64) *Vote {
return &Vote{
nodes: nodes,
ch: ch,
overReadCH: overReadCH,
+ quit: quit,
voteResults: make(map[string]*voteResult),
voterRewards: make(map[string]*voterReward),
coinBaseReward: make(map[string]*coinBaseReward),
v.countReward()
// send transactions
- v.sendRewardTransaction()
+ if err := v.sendRewardTransaction(); err != nil {
+ panic(err)
+ }
}
func (v *Vote) getCoinbaseReward() error {
tx := Transaction{
ip: fmt.Sprintf("http://%s:%d", v.nodes[0].Host, v.nodes[0].Port),
}
+ for {
+ h, err := tx.GetCurrentHeight()
+ if err != nil {
+ close(v.quit)
+ return errors.Wrap(err, "get block height")
+ }
+ if h >= 1200*v.period {
+ break
+ }
+ }
+
coinbaseTx, err := tx.GetCoinbaseTx(1200 * v.period)
if err != nil {
+ close(v.quit)
return err
}
for _, output := range coinbaseTx.Outputs {
- voteOutput, ok := output.TypedOutput.(*types.IntraChainOutput)
+ output, ok := output.TypedOutput.(*types.IntraChainOutput)
if !ok {
+ close(v.quit)
return errors.New("Output type error")
}
- address := common.GetAddressFromControlProgram(voteOutput.ControlProgram)
+ address := common.GetAddressFromControlProgram(output.ControlProgram)
for _, node := range v.nodes {
if address == node.MiningAddress {
reward := &coinBaseReward{
- totalReward: voteOutput.Amount,
+ totalReward: output.Amount,
}
ratioNumerator := big.NewInt(int64(node.RewardRatio))
ratioDenominator := big.NewInt(100)
- coinBaseReward := big.NewInt(0).SetUint64(voteOutput.Amount)
+ coinBaseReward := big.NewInt(0).SetUint64(output.Amount)
reward.voteTotalReward = coinBaseReward.Mul(coinBaseReward, ratioNumerator).Div(coinBaseReward, ratioDenominator)
v.coinBaseReward[node.XPub] = reward
}
case voteInfo := <-v.ch:
bigBlockNum := big.NewInt(0).SetUint64(voteInfo.VoteBlockNum)
bigVoteNum := big.NewInt(0).SetUint64(voteInfo.VoteNum)
- bigVoteNum = bigBlockNum.Mul(bigBlockNum, bigVoteNum)
+ bigVoteNum.Mul(bigVoteNum, bigBlockNum)
if value, ok := v.voteResults[voteInfo.XPub]; ok {
- value.Votes[voteInfo.Address] = bigVoteNum.Add(bigVoteNum, value.Votes[voteInfo.Address])
+ if vote, ok := value.Votes[voteInfo.Address]; ok {
+ vote.Add(vote, bigVoteNum)
+ } else {
+ value.Votes[voteInfo.Address] = bigVoteNum
+ }
} else {
voteResult := &voteResult{
Votes: make(map[string]*big.Int),
voteResult.Votes[voteInfo.Address] = bigVoteNum
v.voteResults[voteInfo.XPub] = voteResult
}
-
- v.voteResults[voteInfo.XPub].VoteTotal = bigVoteNum.Add(bigVoteNum, v.voteResults[voteInfo.XPub].VoteTotal)
+ voteTotal := v.voteResults[voteInfo.XPub].VoteTotal
+ voteTotal.Add(voteTotal, bigVoteNum)
+ v.voteResults[voteInfo.XPub].VoteTotal = voteTotal
case <-v.overReadCH:
break out
}
for address, vote := range votes.Votes {
if value, ok := v.voterRewards[xpub]; ok {
- value.rewards[address] = vote.Mul(vote, coinBaseReward.voteTotalReward).Div(vote, votes.VoteTotal)
+ mul := vote.Mul(vote, coinBaseReward.voteTotalReward)
+ amount := big.NewInt(0)
+ amount.Div(mul, votes.VoteTotal)
+
+ value.rewards[address] = amount
} else {
reward := &voterReward{
rewards: make(map[string]*big.Int),
}
- reward.rewards[address] = vote.Mul(vote, coinBaseReward.voteTotalReward).Div(vote, votes.VoteTotal)
+
+ mul := vote.Mul(vote, coinBaseReward.voteTotalReward)
+ amount := big.NewInt(0)
+ amount.Div(mul, votes.VoteTotal)
+
+ reward.rewards[address] = amount
v.voterRewards[xpub] = reward
}
}
log.Info("tx_id: ", txID)
}
}
-
+ close(v.quit)
return nil
}
}
func NewChainKeeper(db *gorm.DB, cfg *config.Config) *ChainKeeper {
- return &ChainKeeper{
+
+ keeper := &ChainKeeper{
cfg: &cfg.Chain,
db: db,
node: service.NewNode(cfg.Chain.Upstream),
}
+
+ blockState := &orm.BlockState{}
+ if err := db.First(blockState).Error; err != nil {
+ if err == gorm.ErrRecordNotFound {
+ blockStr, _, err := keeper.node.GetBlockByHeight(0)
+ if err != nil {
+ panic(err)
+ }
+ block := &types.Block{}
+ if err := block.UnmarshalText([]byte(blockStr)); err != nil {
+ panic(err)
+ }
+ if err := keeper.insertBlockState(db, block); err != nil {
+ panic(err)
+ }
+ } else {
+ panic(err)
+ }
+ }
+
+ return keeper
}
func (c *ChainKeeper) Run() {
OutputID: outputID.String(),
}
// update data
- db := ormDB.Where(utxo).Update("veto_height", block.Height)
+ db := ormDB.Model(&orm.Utxo{}).Where(utxo).Update("veto_height", block.Height)
if err := db.Error; err != nil {
ormDB.Rollback()
return err
return ormDB.Commit().Error
}
+func (c *ChainKeeper) insertBlockState(db *gorm.DB, block *types.Block) error {
+ blockHash := block.Hash()
+ blockState := &orm.BlockState{
+ Height: block.Height,
+ BlockHash: blockHash.String(),
+ }
+ if err := db.Save(blockState).Error; err != nil {
+ return err
+ }
+ return nil
+}
+
func (c *ChainKeeper) updateBlockState(db *gorm.DB, block *types.Block) error {
// update blockState
blockHash := block.Hash()
BlockHash: blockHash.String(),
}
- u := db.Updates(blockState)
+ u := db.Model(&orm.BlockState{}).Updates(blockState)
if err := u.Error; err != nil {
db.Rollback()