OSDN Git Service

restruct
authoroys <oys@oysdeMBP.localdomain>
Thu, 4 Jul 2019 06:36:32 +0000 (14:36 +0800)
committeroys <oys@oysdeMBP.localdomain>
Thu, 4 Jul 2019 06:36:32 +0000 (14:36 +0800)
api/query.go
blockchain/txbuilder/builder.go
blockchain/txbuilder/finalize.go
proposal/proposal.go
proposal/proposal_test.go
protocol/block.go
protocol/state/consensus_result.go

index abbda52..4029d5c 100644 (file)
@@ -11,7 +11,6 @@ import (
        "github.com/vapor/asset"
        "github.com/vapor/blockchain/query"
        "github.com/vapor/blockchain/signers"
-       "github.com/vapor/blockchain/txbuilder"
        "github.com/vapor/consensus"
        "github.com/vapor/crypto/ed25519"
        "github.com/vapor/crypto/ed25519/chainkd"
@@ -19,6 +18,7 @@ import (
        "github.com/vapor/errors"
        "github.com/vapor/protocol/bc"
        "github.com/vapor/protocol/bc/types"
+       "github.com/vapor/protocol/state"
 )
 
 // POST /list-accounts
@@ -279,7 +279,7 @@ func (a *API) decodeRawTransaction(ctx context.Context, ins struct {
                tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(&ins.Tx, i))
        }
 
-       tx.Fee = txbuilder.CalculateTxFee(&ins.Tx)
+       tx.Fee = state.CalculateTxFee(&ins.Tx)
        return NewSuccessResponse(tx)
 }
 
index 52a8b39..c69f4a1 100644 (file)
@@ -6,6 +6,7 @@ import (
 
        "github.com/vapor/errors"
        "github.com/vapor/protocol/bc/types"
+       "github.com/vapor/protocol/state"
 )
 
 // NewBuilder return new TemplateBuilder instance
@@ -132,6 +133,6 @@ func (b *TemplateBuilder) Build() (*Template, *types.TxData, error) {
        }
 
        tpl.Transaction = types.NewTx(*tx)
-       tpl.Fee = CalculateTxFee(tpl.Transaction)
+       tpl.Fee = state.CalculateTxFee(tpl.Transaction)
        return tpl, tx, nil
 }
index f701548..6ccb79b 100644 (file)
@@ -5,10 +5,10 @@ import (
        "context"
 
        cfg "github.com/vapor/config"
-       "github.com/vapor/consensus"
        "github.com/vapor/errors"
        "github.com/vapor/protocol"
        "github.com/vapor/protocol/bc/types"
+       "github.com/vapor/protocol/state"
        "github.com/vapor/protocol/vm"
 )
 
@@ -31,7 +31,7 @@ var (
 // assembles a fully signed tx, and stores the effects of
 // its changes on the UTXO set.
 func FinalizeTx(ctx context.Context, c *protocol.Chain, tx *types.Tx) error {
-       if fee := CalculateTxFee(tx); fee > cfg.CommonConfig.Wallet.MaxTxFee {
+       if fee := state.CalculateTxFee(tx); fee > cfg.CommonConfig.Wallet.MaxTxFee {
                return ErrExtTxFee
        }
 
@@ -128,30 +128,6 @@ func checkTxSighashCommitment(tx *types.Tx) error {
        return lastError
 }
 
-// CalculateTxFee calculate transaction fee
-func CalculateTxFee(tx *types.Tx) (fee uint64) {
-       totalInputBTM := uint64(0)
-       totalOutputBTM := uint64(0)
-
-       for _, input := range tx.Inputs {
-               if input.InputType() == types.CoinbaseInputType {
-                       return 0
-               }
-               if input.AssetID() == *consensus.BTMAssetID {
-                       totalInputBTM += input.Amount()
-               }
-       }
-
-       for _, output := range tx.Outputs {
-               if *output.AssetAmount().AssetId == *consensus.BTMAssetID {
-                       totalOutputBTM += output.AssetAmount().Amount
-               }
-       }
-
-       fee = totalInputBTM - totalOutputBTM
-       return
-}
-
 func checkGasInputIDs(tx *types.Tx) error {
        for _, inp := range tx.Inputs {
                switch inp.InputType() {
index 0edd561..83507a8 100644 (file)
@@ -24,11 +24,8 @@ const logModule = "mining"
 // createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
 // based on the passed block height to the provided address.  When the address
 // is nil, the coinbase transaction will instead be redeemable by anyone.
-func createCoinbaseTx(consensusResult *state.ConsensusResult, accountManager *account.Manager, amount uint64) (tx *types.Tx, err error) {
-       blockHeight := consensusResult.BlockHeight + 1
-       amount += consensus.BlockSubsidy(blockHeight)
+func createCoinbaseTx(accountManager *account.Manager, blockHeight uint64) (tx *types.Tx, err error) {
        arbitrary := append([]byte{0x00}, []byte(strconv.FormatUint(blockHeight, 10))...)
-
        var script []byte
        if accountManager == nil {
                script, err = vmutil.DefaultCoinbaseProgram()
@@ -52,18 +49,46 @@ func createCoinbaseTx(consensusResult *state.ConsensusResult, accountManager *ac
                return nil, err
        }
 
-       coinbaseReward := &state.CoinbaseReward{
-               Amount:         amount,
-               ControlProgram: script,
+       _, txData, err := builder.Build()
+       if err != nil {
+               return nil, err
        }
-       rewards, err := state.AddCoinbaseRewards(consensusResult, coinbaseReward, blockHeight)
+
+       byteData, err := txData.MarshalText()
        if err != nil {
                return nil, err
        }
+       txData.SerializedSize = uint64(len(byteData))
+
+       tx = &types.Tx{
+               TxData: *txData,
+               Tx:     types.MapTx(txData),
+       }
+       return tx, nil
+}
+
+// restructCoinbaseTx build coinbase transaction with aggregate outputs when it achieved the specified block height
+func restructCoinbaseTx(tx *types.Tx, rewards []state.CoinbaseReward) (*types.Tx, error) {
+       var arbitrary []byte
+       if len(tx.InputIDs) > 0 {
+               e, ok := tx.Entries[tx.Tx.InputIDs[0]].(*bc.Coinbase)
+               if ok {
+                       arbitrary = e.Arbitrary
+               }
+       }
+
+       if arbitrary == nil {
+               return nil, errors.New("bad origin coinbase input")
+       }
+
+       builder := txbuilder.NewBuilder(time.Now())
+       if err := builder.AddInput(types.NewCoinbaseInput(arbitrary), &txbuilder.SigningInstruction{}); err != nil {
+               return nil, err
+       }
 
        // add the aggregate coinbase rewards
        for _, r := range rewards {
-               if err = builder.AddOutput(types.NewIntraChainOutput(*consensus.BTMAssetID, r.Amount, r.ControlProgram)); err != nil {
+               if err := builder.AddOutput(types.NewIntraChainOutput(*consensus.BTMAssetID, r.Amount, r.ControlProgram)); err != nil {
                        return nil, err
                }
        }
@@ -79,11 +104,10 @@ func createCoinbaseTx(consensusResult *state.ConsensusResult, accountManager *ac
        }
        txData.SerializedSize = uint64(len(byteData))
 
-       tx = &types.Tx{
+       return &types.Tx{
                TxData: *txData,
                Tx:     types.MapTx(txData),
-       }
-       return tx, nil
+       }, nil
 }
 
 // NewBlockTemplate returns a new block template that is ready to be solved
@@ -95,7 +119,6 @@ func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager
        }
        txEntries := []*bc.Tx{nil}
        gasUsed := uint64(0)
-       txFee := uint64(0)
 
        // get preblock info for generate next block
        preBlockHeader := c.BestBlockHeader()
@@ -159,26 +182,43 @@ func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager
                b.Transactions = append(b.Transactions, txDesc.Tx)
                txEntries = append(txEntries, tx)
                gasUsed += uint64(gasStatus.GasUsed)
-               txFee += txDesc.Fee
-
                if gasUsed == consensus.MaxBlockGas {
                        break
                }
 
        }
 
+       // create coinbase transaction
+       b.Transactions[0], err = createCoinbaseTx(accountManager, nextBlockHeight)
+       if err != nil {
+               return nil, errors.Wrap(err, "fail on createCoinbaseTx")
+       }
+
        consensusResult, err := c.GetConsensusResultByHash(&preBlockHash)
        if err != nil {
                return nil, err
        }
 
-       // creater coinbase transaction
-       b.Transactions[0], err = createCoinbaseTx(consensusResult, accountManager, txFee)
+       coinbaseReceiver, err := consensusResult.AttachCoinbaseReward(b)
        if err != nil {
-               return nil, errors.Wrap(err, "fail on createCoinbaseTx")
+               return nil, err
+       }
+
+       rewards, err := consensusResult.GetCoinbaseRewards(nextBlockHeight)
+       if err != nil {
+               return nil, err
        }
-       txEntries[0] = b.Transactions[0].Tx
 
+       // restruct coinbase transaction
+       if len(rewards) > 0 {
+               rewards = append([]state.CoinbaseReward{state.CoinbaseReward{ControlProgram: coinbaseReceiver}}, rewards...)
+               b.Transactions[0], err = restructCoinbaseTx(b.Transactions[0], rewards)
+               if err != nil {
+                       return nil, errors.Wrap(err, "fail on createCoinbaseTx")
+               }
+       }
+
+       txEntries[0] = b.Transactions[0].Tx
        b.BlockHeader.BlockCommitment.TransactionsMerkleRoot, err = types.TxMerkleRoot(txEntries)
        if err != nil {
                return nil, err
index 894e544..8b7d2c7 100644 (file)
@@ -8,7 +8,7 @@ import (
        "github.com/vapor/testutil"
 )
 
-func TestCreateCoinbaseTx(t *testing.T) {
+func TestRestructCoinbaseTx(t *testing.T) {
        reductionInterval := uint64(840000)
        cases := []struct {
                desc            string
index 4eaee08..34cfd9e 100644 (file)
@@ -228,16 +228,16 @@ func (c *Chain) saveBlock(block *types.Block) error {
                return err
        }
 
-       coinbaseReward, err := state.CalCoinbaseReward(block)
+       coinbaseReceiver, err := consensusResult.AttachCoinbaseReward(block)
        if err != nil {
                return err
        }
 
-       rewards, err := state.AddCoinbaseRewards(consensusResult, coinbaseReward, block.Height)
+       rewards, err := consensusResult.GetCoinbaseRewards(block.Height)
        if err != nil {
                return err
        }
-       rewards = append([]state.CoinbaseReward{state.CoinbaseReward{ControlProgram: coinbaseReward.ControlProgram}}, rewards...)
+       rewards = append([]state.CoinbaseReward{state.CoinbaseReward{ControlProgram: coinbaseReceiver}}, rewards...)
 
        bcBlock := types.MapBlock(block)
        if err := validation.ValidateBlock(bcBlock, parent, rewards); err != nil {
index 6746b30..8d6689c 100644 (file)
@@ -1,7 +1,6 @@
 package state
 
 import (
-       "bytes"
        "encoding/hex"
        "sort"
 
@@ -60,26 +59,14 @@ type ConsensusResult struct {
 
 // ApplyBlock calculate the consensus result for new block
 func (c *ConsensusResult) ApplyBlock(block *types.Block) error {
-       var ok bool
        if c.BlockHash != block.PreviousBlockHash {
                return errors.New("block parent hash is not equals last block hash of vote result")
        }
 
-       reward, err := CalCoinbaseReward(block)
-       if err != nil {
+       if _, err := c.AttachCoinbaseReward(block); err != nil {
                return err
        }
 
-       if c.IsFinalize() {
-               c.CoinbaseReward = map[string]uint64{}
-       }
-
-       program := hex.EncodeToString(reward.ControlProgram)
-       c.CoinbaseReward[program], ok = checked.AddUint64(c.CoinbaseReward[program], reward.Amount)
-       if !ok {
-               return errMathOperationOverFlow
-       }
-
        for _, tx := range block.Transactions {
                for _, input := range tx.Inputs {
                        vetoInput, ok := input.TypedInput.(*types.VetoInput)
@@ -155,40 +142,14 @@ func federationNodes() map[string]*ConsensusNode {
 
 // DetachBlock calculate the consensus result for detach block
 func (c *ConsensusResult) DetachBlock(block *types.Block) error {
-       var ok bool
        if c.BlockHash != block.Hash() {
                return errors.New("block hash is not equals last block hash of vote result")
        }
 
-       if block.Height%consensus.RoundVoteBlockNums == 0 {
-               if len(c.CoinbaseReward) != 0 {
-                       return errCoinbaseReward
-               }
-
-               c.CoinbaseReward = map[string]uint64{}
-               for i, output := range block.Transactions[0].Outputs {
-                       if i == 0 && output.AssetAmount().Amount == 0 {
-                               continue
-                       }
-                       program := output.ControlProgram()
-                       c.CoinbaseReward[hex.EncodeToString(program)] = output.AssetAmount().Amount
-               }
-       }
-
-       reward, err := CalCoinbaseReward(block)
-       if err != nil {
+       if err := c.DetachCoinbaseReward(block); err != nil {
                return err
        }
 
-       program := hex.EncodeToString(reward.ControlProgram)
-       if c.CoinbaseReward[program], ok = checked.SubUint64(c.CoinbaseReward[program], reward.Amount); !ok {
-               return errMathOperationOverFlow
-       }
-
-       if c.CoinbaseReward[program] == 0 {
-               delete(c.CoinbaseReward, program)
-       }
-
        for i := len(block.Transactions) - 1; i >= 0; i-- {
                tx := block.Transactions[i]
                for _, input := range tx.Inputs {
@@ -263,22 +224,79 @@ func (a SortByAmount) Len() int           { return len(a) }
 func (a SortByAmount) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 func (a SortByAmount) Less(i, j int) bool { return a[i].Amount < a[j].Amount }
 
+// AttachCoinbaseReward attach coinbase reward
+func (c *ConsensusResult) AttachCoinbaseReward(block *types.Block) ([]byte, error) {
+       reward, err := CalCoinbaseReward(block)
+       if err != nil {
+               return nil, err
+       }
+
+       if (block.Height-1)%consensus.RoundVoteBlockNums == 0 {
+               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 nil, errMathOperationOverFlow
+       }
+       return reward.ControlProgram, nil
+}
+
+// DetachCoinbaseReward detach coinbase reward
+func (c *ConsensusResult) DetachCoinbaseReward(block *types.Block) error {
+       if block.Height%consensus.RoundVoteBlockNums == 0 {
+               if len(c.CoinbaseReward) != 0 {
+                       return errCoinbaseReward
+               }
+
+               c.CoinbaseReward = map[string]uint64{}
+               for i, output := range block.Transactions[0].Outputs {
+                       if i == 0 && output.AssetAmount().Amount == 0 {
+                               continue
+                       }
+                       program := output.ControlProgram()
+                       c.CoinbaseReward[hex.EncodeToString(program)] = output.AssetAmount().Amount
+               }
+       }
+
+       reward, err := CalCoinbaseReward(block)
+       if err != nil {
+               return err
+       }
+
+       var ok bool
+       program := hex.EncodeToString(reward.ControlProgram)
+       if c.CoinbaseReward[program], ok = checked.SubUint64(c.CoinbaseReward[program], reward.Amount); !ok {
+               return errMathOperationOverFlow
+       }
+
+       if c.CoinbaseReward[program] == 0 {
+               delete(c.CoinbaseReward, program)
+       }
+       return nil
+}
+
 // CalCoinbaseReward calculate the coinbase reward for block
 func CalCoinbaseReward(block *types.Block) (*CoinbaseReward, error) {
        var coinbaseReceiver []byte
-       if len(block.Transactions) > 0 && len(block.Transactions[0].Outputs) > 0 {
-               coinbaseReceiver = block.Transactions[0].Outputs[0].ControlProgram()
+       coinbaseTx := block.Transactions[0]
+       if len(block.Transactions) > 0 && len(coinbaseTx.InputIDs) > 0 && len(coinbaseTx.Outputs) > 0 {
+               if _, ok := coinbaseTx.Entries[coinbaseTx.InputIDs[0]].(*bc.Coinbase); ok {
+                       coinbaseReceiver = coinbaseTx.Outputs[0].ControlProgram()
+               }
        }
 
        if coinbaseReceiver == nil {
-               return nil, errors.New("invalid block coinbase transaction with receiver address is empty")
+               return nil, errors.New("not found coinbase receiver")
        }
 
        coinbaseAmount := consensus.BlockSubsidy(block.BlockHeader.Height)
        for _, tx := range block.Transactions {
-               txFee, err := calTxFee(tx)
-               if err != nil {
-                       return nil, err
+               txFee := CalculateTxFee(tx)
+               if txFee < 0 {
+                       return nil, errors.Wrap(errMathOperationOverFlow, "calculate transaction fee")
                }
                coinbaseAmount += txFee
        }
@@ -289,11 +307,12 @@ func CalCoinbaseReward(block *types.Block) (*CoinbaseReward, error) {
        }, nil
 }
 
-func calTxFee(tx *types.Tx) (uint64, error) {
+// CalculateTxFee calculate transaction fee
+func CalculateTxFee(tx *types.Tx) uint64 {
        var totalInputBTM, totalOutputBTM uint64
        for _, input := range tx.Inputs {
                if input.InputType() == types.CoinbaseInputType {
-                       return 0, nil
+                       return 0
                }
                if input.AssetID() == *consensus.BTMAssetID {
                        totalInputBTM += input.Amount()
@@ -305,46 +324,28 @@ func calTxFee(tx *types.Tx) (uint64, error) {
                        totalOutputBTM += output.AssetAmount().Amount
                }
        }
-
-       txFee, ok := checked.SubUint64(totalInputBTM, totalOutputBTM)
-       if !ok {
-               return 0, errMathOperationOverFlow
-       }
-       return txFee, nil
+       return totalInputBTM - totalOutputBTM
 }
 
-// AddCoinbaseRewards add block coinbase reward and sort rewards by amount
-func AddCoinbaseRewards(consensusResult *ConsensusResult, reward *CoinbaseReward, blockHeight uint64) ([]CoinbaseReward, error) {
+// GetCoinbaseRewards convert into CoinbaseReward array and sort it by amount
+func (c *ConsensusResult) GetCoinbaseRewards(blockHeight uint64) ([]CoinbaseReward, error) {
        rewards := []CoinbaseReward{}
        if blockHeight%consensus.RoundVoteBlockNums != 0 {
                return []CoinbaseReward{}, nil
        }
 
-       aggregateFlag := false
-       for p, amount := range consensusResult.CoinbaseReward {
+       for p, amount := range c.CoinbaseReward {
                coinbaseAmount := amount
                program, err := hex.DecodeString(p)
                if err != nil {
                        return nil, err
                }
 
-               if res := bytes.Compare(program, reward.ControlProgram); res == 0 {
-                       var ok bool
-                       if coinbaseAmount, ok = checked.AddUint64(coinbaseAmount, reward.Amount); !ok {
-                               return nil, errMathOperationOverFlow
-                       }
-                       aggregateFlag = true
-               }
-
                rewards = append(rewards, CoinbaseReward{
                        Amount:         coinbaseAmount,
                        ControlProgram: program,
                })
        }
-
-       if !aggregateFlag {
-               rewards = append(rewards, *reward)
-       }
        sort.Sort(SortByAmount(rewards))
        return rewards, nil
 }