12 "github.com/vapor/crypto/ed25519/chainkd"
14 "github.com/vapor/consensus"
16 "github.com/vapor/chain"
17 "github.com/vapor/common"
18 "github.com/vapor/config"
19 "github.com/vapor/protocol/bc"
20 "github.com/vapor/protocol/bc/types"
23 type Delegate struct {
24 DelegateAddress string `json:"delegate_address"`
25 Votes uint64 `json:"votes"`
28 type DelegateInfo struct {
29 Delegates []Delegate `json:"delegates"`
32 //OP_FAIL PUBKEY SIG(block.time) DELEGATE_IDS
33 func DelegateInfoToScript(delegateInfo DelegateInfo, xpub chainkd.XPub, h bc.Hash) {
37 //ScriptToDelegateInfo
39 const maxConfirmBlockCount = 2
41 type IrreversibleBlockInfo struct {
44 HeightHash map[int64]bc.Hash
47 func newIrreversibleBlockInfo() *IrreversibleBlockInfo {
48 o := &IrreversibleBlockInfo{}
49 for i := 0; i < maxConfirmBlockCount; i++ {
50 o.heights = append(o.heights, -1)
55 type DposType struct {
57 MaxDelegateNumber uint64
58 BlockIntervalTime uint64
59 DposStartHeight uint64
61 cSuperForgerAddress common.Address
62 irreversibleBlockFileName string
63 cIrreversibleBlockInfo IrreversibleBlockInfo
64 lockIrreversibleBlockInfo sync.Mutex
67 var GDpos = &DposType{}
70 GDpos.irreversibleBlockFileName = filepath.Join(config.DefaultDataDir(), "dpos", "irreversible_block.dat")
71 GDpos.ReadIrreversibleBlockInfo(&GDpos.cIrreversibleBlockInfo)
75 func (d *DposType) ReadIrreversibleBlockInfo(info *IrreversibleBlockInfo) error {
79 func (d *DposType) IsMining(cDelegateInfo *DelegateInfo, address common.Address, t uint64) error {
81 header := d.c.BestBlockHeader()
82 currentLoopIndex := d.GetLoopIndex(t)
83 currentDelegateIndex := d.GetDelegateIndex(t)
84 prevLoopIndex := d.GetLoopIndex(header.Timestamp)
85 prevDelegateIndex := d.GetDelegateIndex(header.Timestamp)
87 if currentLoopIndex > prevLoopIndex {
88 *cDelegateInfo = d.GetNextDelegates(t)
89 if cDelegateInfo.Delegates[currentLoopIndex].DelegateAddress == address.EncodeAddress() {
92 return errors.New("Is not the current mining node")
93 } else if currentLoopIndex == prevLoopIndex && currentDelegateIndex > prevDelegateIndex {
94 //currentDelegateInfo := DelegateInfo{}
95 currentDelegateInfo, err := d.GetBlockDelegates(header)
99 if currentDelegateIndex+1 > uint64(len(currentDelegateInfo.Delegates)) {
100 return errors.New("Out of the block node list")
101 } else if currentDelegateInfo.Delegates[currentDelegateIndex].DelegateAddress == address.EncodeAddress() {
104 return errors.New("Is not the current mining node")
107 return errors.New("Time anomaly")
111 func (d *DposType) GetLoopIndex(time uint64) uint64 {
112 if time < d.DposStartTime {
116 return (time - d.DposStartTime) / (d.MaxDelegateNumber * d.BlockIntervalTime)
119 func (d *DposType) GetDelegateIndex(time uint64) uint64 {
120 if time < d.DposStartTime {
124 return (time - d.DposStartTime) % (d.MaxDelegateNumber * d.BlockIntervalTime) / d.BlockIntervalTime
127 func (d *DposType) GetNextDelegates(t uint64) DelegateInfo {
128 delegates := DposVote.GetTopDelegateInfo(consensus.MinHoldBalance, d.MaxDelegateNumber-1)
129 delegate := Delegate{
130 DelegateAddress: d.cSuperForgerAddress.EncodeAddress(),
133 delegates = append(delegates, delegate)
134 delegateInfo := DelegateInfo{}
135 delegateInfo.Delegates = SortDelegate(delegates, t)
139 func (d *DposType) GetBlockDelegates(header *types.BlockHeader) (*DelegateInfo, error) {
140 loopIndex := d.GetLoopIndex(header.Timestamp)
142 preHeader, err := d.c.GetHeaderByHash(&header.PreviousBlockHash)
146 if header.Height == d.DposStartHeight || d.GetLoopIndex(preHeader.Timestamp) < loopIndex {
147 block, err := d.c.GetBlockByHeight(header.Height)
151 delegateInfo, err := d.GetBlockDelegate(block)
155 return delegateInfo, nil
161 func (d *DposType) GetBlockDelegate(block *types.Block) (*DelegateInfo, error) {
162 tx := block.Transactions[0]
163 var delegate TypedData
164 if len(tx.TxData.Inputs) == 1 && tx.TxData.Inputs[0].InputType() == types.CoinbaseInputType {
165 if err := json.Unmarshal(tx.TxData.ReferenceData, delegate); err != nil {
168 if delegateInfo, ok := delegate.(*DelegateInfoList); ok {
169 return &delegateInfo.Delegate, nil
172 return nil, errors.New("The first transaction is not a coinbase transaction")
175 func (d *DposType) CheckCoinbase(tx types.TxData, t uint64, Height uint64) error {
180 func (d *DposType) CheckBlockHeader(header types.BlockHeader) error {
181 blockT := time.Unix(int64(header.Timestamp), 0)
183 if blockT.Sub(time.Now()).Seconds() > 3 {
184 return errors.New("block time is error")
190 func (d *DposType) CheckBlock(block types.Block, fIsCheckDelegateInfo bool) error {
191 blockT := time.Unix(int64(block.Timestamp), 0)
193 if blockT.Sub(time.Now()).Seconds() > 3 {
194 return errors.New("block time is error")
197 if err := d.CheckCoinbase(block.Transactions[0].TxData, block.Timestamp, block.Height); err != nil {
201 preBlock, err := d.c.GetBlockByHash(&block.PreviousBlockHash)
206 currentLoopIndex := d.GetLoopIndex(block.Timestamp)
207 currentDelegateIndex := d.GetDelegateIndex(block.Timestamp)
208 prevLoopIndex := d.GetLoopIndex(preBlock.Timestamp)
209 prevDelegateIndex := d.GetDelegateIndex(preBlock.Timestamp)
211 delegateInfo := &DelegateInfo{}
213 if currentLoopIndex < prevLoopIndex {
214 return errors.New("Block time exception")
215 } else if currentLoopIndex > prevLoopIndex {
216 if fIsCheckDelegateInfo {
217 if err := d.CheckBlockDelegate(block); err != nil {
220 d.ProcessIrreversibleBlock(block.Height, block.Hash())
222 delegateInfo, err = d.GetBlockDelegate(&block)
227 if currentDelegateIndex < prevDelegateIndex {
228 return errors.New("Block time exception")
231 delegateInfo, err = d.GetBlockDelegates(&preBlock.BlockHeader)
237 delegateAddress := d.getBlockForgerAddress(block)
238 if currentDelegateIndex < uint64(len(delegateInfo.Delegates)) &&
239 delegateInfo.Delegates[currentDelegateIndex].DelegateAddress == delegateAddress.EncodeAddress() {
243 return fmt.Errorf("CheckBlock GetDelegateID blockhash:%s error", h.String())
246 func (d *DposType) CheckBlockDelegate(block types.Block) error {
250 func (d *DposType) ProcessIrreversibleBlock(height uint64, hash bc.Hash) {
254 func (d *DposType) getBlockForgerAddress(block types.Block) common.Address {
255 tx := block.Transactions[0].TxData
257 if len(tx.Inputs) == 1 && tx.Inputs[0].InputType() == types.CoinbaseInputType {
258 address, err := common.NewAddressWitnessPubKeyHash(tx.Outputs[0].ControlProgram[2:], &consensus.ActiveNetParams)
260 address, err := common.NewAddressWitnessScriptHash(tx.Outputs[0].ControlProgram[2:], &consensus.ActiveNetParams)
272 func (d *DposType) IsValidBlockCheckIrreversibleBlock(height uint64, hash bc.Hash) error {
273 d.lockIrreversibleBlockInfo.Lock()
274 defer d.lockIrreversibleBlockInfo.Unlock()
276 if h, ok := d.cIrreversibleBlockInfo.HeightHash[int64(height)]; ok {
278 return fmt.Errorf("invalid block[%d:%s]", height, hash.String())
285 func SortDelegate(delegates []Delegate, t uint64) []Delegate {
286 var result []Delegate
287 r := getRand(uint64(len(delegates)), int64(t))
288 for _, i := range r {
289 result = append(result, delegates[i])
294 func getRand(num uint64, seed int64) []uint64 {
297 s := make(map[uint64]bool)
301 if _, ok := s[v]; ok {
306 if uint64(len(r)) >= num {