From: Paladz Date: Tue, 9 Jul 2019 02:20:55 +0000 (+0800) Subject: edit code while reviw (#253) X-Git-Tag: v1.0.5~168 X-Git-Url: http://git.osdn.net/view?p=bytom%2Fvapor.git;a=commitdiff_plain;h=2e4f5f11ae3bec54e4918a7aac5c8b3e2381de44 edit code while reviw (#253) * edit code while reviw * edit the code format --- diff --git a/protocol/state/consensus_result.go b/protocol/state/consensus_result.go index e1368485..16fcded0 100644 --- a/protocol/state/consensus_result.go +++ b/protocol/state/consensus_result.go @@ -29,30 +29,6 @@ func (c byVote) Less(i, j int) bool { } func (c byVote) Swap(i, j int) { c[i], c[j] = c[j], c[i] } -// CalcVoteSeq calculate the vote sequence -// seq 0 is the genesis block -// seq 1 is the the block height 1, to block height RoundVoteBlockNums -// seq 2 is the block height RoundVoteBlockNums + 1 to block height 2 * RoundVoteBlockNums -// consensus node of the current round is the final result of previous round -func CalcVoteSeq(blockHeight uint64) uint64 { - if blockHeight == 0 { - return 0 - } - return (blockHeight-1)/consensus.RoundVoteBlockNums + 1 -} - -// ConsensusResult represents a snapshot of each round of DPOS voting -// Seq indicates the sequence of current votes, which start from zero -// NumOfVote indicates the number of votes each consensus node receives, the key of map represent public key -// CoinbaseReward indicates the coinbase receiver and reward -type ConsensusResult struct { - Seq uint64 - NumOfVote map[string]uint64 - CoinbaseReward map[string]uint64 - BlockHash bc.Hash - BlockHeight uint64 -} - // CoinbaseReward contains receiver and reward type CoinbaseReward struct { Amount uint64 @@ -68,28 +44,47 @@ func (a SortByAmount) Less(i, j int) bool { return a[i].Amount < a[j].Amount } // CalCoinbaseReward calculate the coinbase reward for block func CalCoinbaseReward(block *types.Block) (*CoinbaseReward, error) { - var coinbaseReceiver []byte + result := &CoinbaseReward{} if len(block.Transactions) > 0 && len(block.Transactions[0].Outputs) > 0 { - coinbaseReceiver = block.Transactions[0].Outputs[0].ControlProgram() - } - - if coinbaseReceiver == nil { + result.ControlProgram = block.Transactions[0].Outputs[0].ControlProgram() + } else { return nil, errors.New("not found coinbase receiver") } - coinbaseAmount := consensus.BlockSubsidy(block.BlockHeader.Height) + result.Amount = consensus.BlockSubsidy(block.BlockHeader.Height) for _, tx := range block.Transactions { txFee, err := arithmetic.CalculateTxFee(tx) if err != nil { return nil, errors.Wrap(checked.ErrOverflow, "calculate transaction fee") } - coinbaseAmount += txFee + + result.Amount += txFee + } + return result, nil +} + +// CalcVoteSeq calculate the vote sequence +// seq 0 is the genesis block +// seq 1 is the the block height 1, to block height RoundVoteBlockNums +// seq 2 is the block height RoundVoteBlockNums + 1 to block height 2 * RoundVoteBlockNums +// consensus node of the current round is the final result of previous round +func CalcVoteSeq(blockHeight uint64) uint64 { + if blockHeight == 0 { + return 0 } + return (blockHeight-1)/consensus.RoundVoteBlockNums + 1 +} - return &CoinbaseReward{ - Amount: coinbaseAmount, - ControlProgram: coinbaseReceiver, - }, nil +// ConsensusResult represents a snapshot of each round of DPOS voting +// Seq indicates the sequence of current votes, which start from zero +// NumOfVote indicates the number of votes each consensus node receives, the key of map represent public key +// CoinbaseReward indicates the coinbase receiver and reward +type ConsensusResult struct { + Seq uint64 + NumOfVote map[string]uint64 + CoinbaseReward map[string]uint64 + BlockHash bc.Hash + BlockHeight uint64 } // ApplyBlock calculate the consensus result for new block @@ -139,6 +134,26 @@ func (c *ConsensusResult) ApplyBlock(block *types.Block) error { return nil } +// AttachCoinbaseReward attach coinbase reward +func (c *ConsensusResult) AttachCoinbaseReward(block *types.Block) error { + reward, err := CalCoinbaseReward(block) + if err != nil { + return err + } + + if block.Height%consensus.RoundVoteBlockNums == 1 { + c.CoinbaseReward = map[string]uint64{} + } + + var ok bool + program := hex.EncodeToString(reward.ControlProgram) + c.CoinbaseReward[program], ok = checked.AddUint64(c.CoinbaseReward[program], reward.Amount) + if !ok { + return checked.ErrOverflow + } + return nil +} + // ConsensusNodes returns all consensus nodes func (c *ConsensusResult) ConsensusNodes() (map[string]*ConsensusNode, error) { var nodes []*ConsensusNode @@ -167,14 +182,6 @@ func (c *ConsensusResult) ConsensusNodes() (map[string]*ConsensusNode, error) { return federationNodes(), nil } -func federationNodes() map[string]*ConsensusNode { - consensusResult := map[string]*ConsensusNode{} - for i, xpub := range config.CommonConfig.Federation.Xpubs { - consensusResult[xpub.String()] = &ConsensusNode{XPub: xpub, VoteNum: 0, Order: uint64(i)} - } - return consensusResult -} - // DetachBlock calculate the consensus result for detach block func (c *ConsensusResult) DetachBlock(block *types.Block) error { if c.BlockHash != block.Hash() { @@ -223,49 +230,6 @@ func (c *ConsensusResult) DetachBlock(block *types.Block) error { return nil } -func (c *ConsensusResult) Fork() *ConsensusResult { - f := &ConsensusResult{ - Seq: c.Seq, - NumOfVote: map[string]uint64{}, - CoinbaseReward: map[string]uint64{}, - BlockHash: c.BlockHash, - BlockHeight: c.BlockHeight, - } - - for key, value := range c.NumOfVote { - f.NumOfVote[key] = value - } - - for key, value := range c.CoinbaseReward { - f.CoinbaseReward[key] = value - } - return f -} - -func (c *ConsensusResult) IsFinalize() bool { - return c.BlockHeight%consensus.RoundVoteBlockNums == 0 -} - -// AttachCoinbaseReward attach coinbase reward -func (c *ConsensusResult) AttachCoinbaseReward(block *types.Block) error { - reward, err := CalCoinbaseReward(block) - if err != nil { - return err - } - - if block.Height%consensus.RoundVoteBlockNums == 1 { - c.CoinbaseReward = map[string]uint64{} - } - - var ok bool - program := hex.EncodeToString(reward.ControlProgram) - c.CoinbaseReward[program], ok = checked.AddUint64(c.CoinbaseReward[program], reward.Amount) - if !ok { - return checked.ErrOverflow - } - return nil -} - // DetachCoinbaseReward detach coinbase reward func (c *ConsensusResult) DetachCoinbaseReward(block *types.Block) error { if block.Height%consensus.RoundVoteBlockNums == 0 { @@ -295,6 +259,31 @@ func (c *ConsensusResult) DetachCoinbaseReward(block *types.Block) error { return nil } +// Fork copy the ConsensusResult struct +func (c *ConsensusResult) Fork() *ConsensusResult { + f := &ConsensusResult{ + Seq: c.Seq, + NumOfVote: map[string]uint64{}, + CoinbaseReward: map[string]uint64{}, + BlockHash: c.BlockHash, + BlockHeight: c.BlockHeight, + } + + for key, value := range c.NumOfVote { + f.NumOfVote[key] = value + } + + for key, value := range c.CoinbaseReward { + f.CoinbaseReward[key] = value + } + return f +} + +// IsFinalize check if the result is end of consensus round +func (c *ConsensusResult) IsFinalize() bool { + return c.BlockHeight%consensus.RoundVoteBlockNums == 0 +} + // GetCoinbaseRewards convert into CoinbaseReward array and sort it by amount func (c *ConsensusResult) GetCoinbaseRewards(blockHeight uint64) ([]CoinbaseReward, error) { rewards := []CoinbaseReward{} @@ -303,17 +292,24 @@ func (c *ConsensusResult) GetCoinbaseRewards(blockHeight uint64) ([]CoinbaseRewa } for p, amount := range c.CoinbaseReward { - coinbaseAmount := amount program, err := hex.DecodeString(p) if err != nil { return nil, err } rewards = append(rewards, CoinbaseReward{ - Amount: coinbaseAmount, + Amount: amount, ControlProgram: program, }) } sort.Sort(SortByAmount(rewards)) return rewards, nil } + +func federationNodes() map[string]*ConsensusNode { + consensusResult := map[string]*ConsensusNode{} + for i, xpub := range config.CommonConfig.Federation.Xpubs { + consensusResult[xpub.String()] = &ConsensusNode{XPub: xpub, VoteNum: 0, Order: uint64(i)} + } + return consensusResult +} diff --git a/protocol/state/utxo_view.go b/protocol/state/utxo_view.go index 90c71b17..4e0d56c4 100644 --- a/protocol/state/utxo_view.go +++ b/protocol/state/utxo_view.go @@ -20,80 +20,93 @@ func NewUtxoViewpoint() *UtxoViewpoint { } func (view *UtxoViewpoint) ApplyTransaction(block *bc.Block, tx *bc.Tx, statusFail bool) error { - for _, prevout := range tx.MainchainOutputIDs { - entry, ok := view.Entries[prevout] - if !ok { - return errors.New("fail to find mainchain output entry") - } + if err := view.applyCrossChainUtxo(block, tx); err != nil { + return err + } - if entry.Type != storage.CrosschainUTXOType { - return errors.New("look up mainchainOutputID but find utxo not from mainchain") + if err := view.applySpendUtxo(block, tx, statusFail); err != nil { + return err + } + + return view.applyOutputUtxo(block, tx, statusFail) +} + +func (view *UtxoViewpoint) ApplyBlock(block *bc.Block, txStatus *bc.TransactionStatus) error { + for i, tx := range block.Transactions { + statusFail, err := txStatus.GetStatus(i) + if err != nil { + return err } - if entry.Spent { - return errors.New("mainchain output has been spent") + if err := view.ApplyTransaction(block, tx, statusFail); err != nil { + return err } + } + return nil +} - entry.BlockHeight = block.Height - entry.SpendOutput() +func (view *UtxoViewpoint) CanSpend(hash *bc.Hash) bool { + entry := view.Entries[*hash] + return entry != nil && !entry.Spent +} + +func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error { + if err := view.detachCrossChainUtxo(tx); err != nil { + return err } - for _, prevout := range tx.SpentOutputIDs { - assetID := bc.AssetID{} - entryOutput, err := tx.Entry(prevout) + if err := view.detachSpendUtxo(tx, statusFail); err != nil { + return err + } + + return view.detachOutputUtxo(tx, statusFail) +} + +func (view *UtxoViewpoint) DetachBlock(block *bc.Block, txStatus *bc.TransactionStatus) error { + for i := len(block.Transactions) - 1; i >= 0; i-- { + statusFail, err := txStatus.GetStatus(i) if err != nil { return err } - switch output := entryOutput.(type) { - case *bc.IntraChainOutput: - assetID = *output.Source.Value.AssetId - case *bc.VoteOutput: - assetID = *output.Source.Value.AssetId - default: - return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput) + if err := view.DetachTransaction(block.Transactions[i], statusFail); err != nil { + return err } + } + return nil +} - if statusFail && assetID != *consensus.BTMAssetID { - continue - } +func (view *UtxoViewpoint) HasUtxo(hash *bc.Hash) bool { + _, ok := view.Entries[*hash] + return ok +} +func (view *UtxoViewpoint) applyCrossChainUtxo(block *bc.Block, tx *bc.Tx) error { + for _, prevout := range tx.MainchainOutputIDs { entry, ok := view.Entries[prevout] if !ok { - return errors.New("fail to find utxo entry") + return errors.New("fail to find mainchain output entry") } if entry.Spent { - return errors.New("utxo has been spent") - } - - switch entry.Type { - case storage.CrosschainUTXOType: - return errors.New("look up spentOutputID but find utxo from mainchain") - - case storage.CoinbaseUTXOType: - if (entry.BlockHeight + consensus.CoinbasePendingBlockNumber) > block.Height { - return errors.New("coinbase utxo is not ready for use") - } - - case storage.VoteUTXOType: - if (entry.BlockHeight + consensus.VotePendingBlockNumber) > block.Height { - return errors.New("Coin is within the voting lock time") - } + return errors.New("mainchain output has been spent") } + entry.BlockHeight = block.Height entry.SpendOutput() } + return nil +} +func (view *UtxoViewpoint) applyOutputUtxo(block *bc.Block, tx *bc.Tx, statusFail bool) error { for _, id := range tx.TxHeader.ResultIds { - assetID := bc.AssetID{} entryOutput, err := tx.Entry(*id) if err != nil { - continue + return err } + var assetID bc.AssetID utxoType := storage.NormalUTXOType - switch output := entryOutput.(type) { case *bc.IntraChainOutput: if output.Source.Value.Amount == uint64(0) { @@ -120,57 +133,19 @@ func (view *UtxoViewpoint) ApplyTransaction(block *bc.Block, tx *bc.Tx, statusFa return nil } -func (view *UtxoViewpoint) ApplyBlock(block *bc.Block, txStatus *bc.TransactionStatus) error { - for i, tx := range block.Transactions { - statusFail, err := txStatus.GetStatus(i) - if err != nil { - return err - } - if err := view.ApplyTransaction(block, tx, statusFail); err != nil { - return err - } - } - return nil -} - -func (view *UtxoViewpoint) CanSpend(hash *bc.Hash) bool { - entry := view.Entries[*hash] - return entry != nil && !entry.Spent -} - -func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error { - for _, prevout := range tx.MainchainOutputIDs { - // don't simply delete(view.Entries, prevout), because we need to delete from db in saveUtxoView() - entry, ok := view.Entries[prevout] - if ok && (entry.Type != storage.CrosschainUTXOType) { - return errors.New("look up mainchainOutputID but find utxo not from mainchain") - } - - if ok && !entry.Spent { - return errors.New("try to revert an unspent utxo") - } - - if !ok { - view.Entries[prevout] = storage.NewUtxoEntry(storage.CrosschainUTXOType, 0, false) - continue - } - entry.UnspendOutput() - } - +func (view *UtxoViewpoint) applySpendUtxo(block *bc.Block, tx *bc.Tx, statusFail bool) error { for _, prevout := range tx.SpentOutputIDs { - assetID := bc.AssetID{} entryOutput, err := tx.Entry(prevout) if err != nil { return err } - utxoType := storage.NormalUTXOType + var assetID bc.AssetID switch output := entryOutput.(type) { case *bc.IntraChainOutput: assetID = *output.Source.Value.AssetId case *bc.VoteOutput: assetID = *output.Source.Value.AssetId - utxoType = storage.VoteUTXOType default: return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput) } @@ -180,28 +155,55 @@ func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error { } entry, ok := view.Entries[prevout] - if ok && (entry.Type == storage.CrosschainUTXOType) { - return errors.New("look up SpentOutputIDs but find mainchain utxo") + if !ok { + return errors.New("fail to find utxo entry") } - if ok && !entry.Spent { - return errors.New("try to revert an unspent utxo") + if entry.Spent { + return errors.New("utxo has been spent") } + switch entry.Type { + case storage.CoinbaseUTXOType: + if (entry.BlockHeight + consensus.CoinbasePendingBlockNumber) > block.Height { + return errors.New("coinbase utxo is not ready for use") + } + + case storage.VoteUTXOType: + if (entry.BlockHeight + consensus.VotePendingBlockNumber) > block.Height { + return errors.New("Coin is within the voting lock time") + } + } + + entry.SpendOutput() + } + return nil +} + +func (view *UtxoViewpoint) detachCrossChainUtxo(tx *bc.Tx) error { + for _, prevout := range tx.MainchainOutputIDs { + entry, ok := view.Entries[prevout] if !ok { - view.Entries[prevout] = storage.NewUtxoEntry(utxoType, 0, false) - continue + return errors.New("fail to find mainchain output entry") } + + if !entry.Spent { + return errors.New("mainchain output is unspent") + } + entry.UnspendOutput() } + return nil +} +func (view *UtxoViewpoint) detachOutputUtxo(tx *bc.Tx, statusFail bool) error { for _, id := range tx.TxHeader.ResultIds { - assetID := bc.AssetID{} entryOutput, err := tx.Entry(*id) if err != nil { - continue + return err } + var assetID bc.AssetID utxoType := storage.NormalUTXOType switch output := entryOutput.(type) { case *bc.IntraChainOutput: @@ -226,20 +228,39 @@ func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error { return nil } -func (view *UtxoViewpoint) DetachBlock(block *bc.Block, txStatus *bc.TransactionStatus) error { - for i := len(block.Transactions) - 1; i >= 0; i-- { - statusFail, err := txStatus.GetStatus(i) +func (view *UtxoViewpoint) detachSpendUtxo(tx *bc.Tx, statusFail bool) error { + for _, prevout := range tx.SpentOutputIDs { + entryOutput, err := tx.Entry(prevout) if err != nil { return err } - if err := view.DetachTransaction(block.Transactions[i], statusFail); err != nil { - return err + + var assetID bc.AssetID + utxoType := storage.NormalUTXOType + switch output := entryOutput.(type) { + case *bc.IntraChainOutput: + assetID = *output.Source.Value.AssetId + case *bc.VoteOutput: + assetID = *output.Source.Value.AssetId + utxoType = storage.VoteUTXOType + default: + return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput) + } + + if statusFail && assetID != *consensus.BTMAssetID { + continue + } + + entry, ok := view.Entries[prevout] + if ok && !entry.Spent { + return errors.New("try to revert an unspent utxo") } + + if !ok { + view.Entries[prevout] = storage.NewUtxoEntry(utxoType, 0, false) + continue + } + entry.UnspendOutput() } return nil } - -func (view *UtxoViewpoint) HasUtxo(hash *bc.Hash) bool { - _, ok := view.Entries[*hash] - return ok -} diff --git a/protocol/txpool.go b/protocol/txpool.go index 32d3f6df..912091cd 100644 --- a/protocol/txpool.go +++ b/protocol/txpool.go @@ -208,8 +208,7 @@ func isTransactionNoBtmInput(tx *types.Tx) bool { func isTransactionZeroOutput(tx *types.Tx) bool { for _, output := range tx.TxData.Outputs { - value := output.AssetAmount() - if value.Amount == uint64(0) { + if value := output.AssetAmount(); value.Amount == uint64(0) { return true } } @@ -282,11 +281,12 @@ func (tp *TxPool) addTransaction(txD *TxDesc) error { txD.Added = time.Now() tp.pool[tx.ID] = txD for _, id := range tx.ResultIds { - var assetID bc.AssetID outputEntry, err := tx.Entry(*id) if err != nil { return err } + + var assetID bc.AssetID switch output := outputEntry.(type) { case *bc.IntraChainOutput: assetID = *output.Source.Value.AssetId diff --git a/protocol/validation/vmcontext.go b/protocol/validation/vmcontext.go index df676937..7ac617c9 100644 --- a/protocol/validation/vmcontext.go +++ b/protocol/validation/vmcontext.go @@ -51,7 +51,6 @@ func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, args destPos = &e.WitnessDestination.Position s := e.SpentOutputId.Bytes() spentOutputID = &s - } var txSigHash *[]byte