OSDN Git Service

feat(proposal):move vapor proposal (#1903)
authorMingjing <2595400537@qq.com>
Mon, 19 Apr 2021 10:25:33 +0000 (18:25 +0800)
committerGitHub <noreply@github.com>
Mon, 19 Apr 2021 10:25:33 +0000 (18:25 +0800)
* feat(proposal):move vapor proposal

* feat(proposal):set slice zero

* fix(proposal):fix sign header and witness

* style(chain):remove space

* style(protocol):remove space

proposal/proposal.go [new file with mode: 0644]
proposal/sort.go [new file with mode: 0644]
protocol/bc/types/block_header.go
protocol/bc/types/block_witness.go [new file with mode: 0644]
protocol/consensus.go

diff --git a/proposal/proposal.go b/proposal/proposal.go
new file mode 100644 (file)
index 0000000..cfe5828
--- /dev/null
@@ -0,0 +1,266 @@
+package proposal
+
+import (
+       "sort"
+       "strconv"
+       "time"
+
+       log "github.com/sirupsen/logrus"
+
+       "github.com/bytom/bytom/account"
+       "github.com/bytom/bytom/blockchain/txbuilder"
+       "github.com/bytom/bytom/consensus"
+       "github.com/bytom/bytom/errors"
+       "github.com/bytom/bytom/protocol"
+       "github.com/bytom/bytom/protocol/bc"
+       "github.com/bytom/bytom/protocol/bc/types"
+       "github.com/bytom/bytom/protocol/state"
+       "github.com/bytom/bytom/protocol/validation"
+       "github.com/bytom/bytom/protocol/vm/vmutil"
+)
+
+const (
+       logModule     = "proposal"
+       batchApplyNum = 16
+       softMaxTxNum  = 128
+
+       timeoutOk = iota + 1
+       timeoutWarn
+       timeoutCritical
+)
+
+// NewBlockTemplate returns a new block template that is ready to be solved
+func NewBlockTemplate(chain *protocol.Chain, accountManager *account.Manager, timestamp uint64, warnDuration, criticalDuration time.Duration) (*types.Block, error) {
+       builder := newBlockBuilder(chain, accountManager, timestamp, warnDuration, criticalDuration)
+       return builder.build()
+}
+
+type blockBuilder struct {
+       chain          *protocol.Chain
+       accountManager *account.Manager
+
+       block    *types.Block
+       utxoView *state.UtxoViewpoint
+
+       warnTimeoutCh     <-chan time.Time
+       criticalTimeoutCh <-chan time.Time
+       timeoutStatus     uint8
+       gasLeft           int64
+}
+
+func newBlockBuilder(chain *protocol.Chain, accountManager *account.Manager, timestamp uint64, warnDuration, criticalDuration time.Duration) *blockBuilder {
+       preBlockHeader := chain.BestBlockHeader()
+       block := &types.Block{
+               BlockHeader: types.BlockHeader{
+                       Version:           1,
+                       Height:            preBlockHeader.Height + 1,
+                       PreviousBlockHash: preBlockHeader.Hash(),
+                       Timestamp:         timestamp,
+                       BlockCommitment:   types.BlockCommitment{},
+                       BlockWitness:      make([]byte, protocol.SignatureLength),
+               },
+       }
+
+       builder := &blockBuilder{
+               chain:             chain,
+               accountManager:    accountManager,
+               block:             block,
+               utxoView:          state.NewUtxoViewpoint(),
+               warnTimeoutCh:     time.After(warnDuration),
+               criticalTimeoutCh: time.After(criticalDuration),
+               gasLeft:           int64(consensus.MaxBlockGas),
+               timeoutStatus:     timeoutOk,
+       }
+       return builder
+}
+
+func (b *blockBuilder) build() (*types.Block, error) {
+       if err := b.applyCoinbaseTransaction(); err != nil {
+               return nil, err
+       }
+
+       if err := b.applyTransactionFromPool(); err != nil {
+               return nil, err
+       }
+
+       if err := b.calculateBlockCommitment(); err != nil {
+               return nil, err
+       }
+
+       b.chain.SignBlockHeader(&b.block.BlockHeader)
+       return b.block, nil
+}
+
+func (b *blockBuilder) applyCoinbaseTransaction() error {
+       coinbaseTx, err := b.createCoinbaseTx()
+       if err != nil {
+               return errors.Wrap(err, "fail on create coinbase tx")
+       }
+
+       gasState, err := validation.ValidateTx(coinbaseTx.Tx, &bc.Block{BlockHeader: &bc.BlockHeader{Height: b.block.Height}, Transactions: []*bc.Tx{coinbaseTx.Tx}}, b.chain.ProgramConverter)
+       if err != nil {
+               return err
+       }
+
+       b.block.Transactions = append(b.block.Transactions, coinbaseTx)
+       b.gasLeft -= gasState.GasUsed
+       return nil
+}
+
+func (b *blockBuilder) applyTransactionFromPool() error {
+       txDescList := b.chain.GetTxPool().GetTransactions()
+       sort.Sort(byTime(txDescList))
+
+       poolTxs := make([]*types.Tx, len(txDescList))
+       for i, txDesc := range txDescList {
+               poolTxs[i] = txDesc.Tx
+       }
+
+       return b.applyTransactions(poolTxs, timeoutWarn)
+}
+
+func (b *blockBuilder) calculateBlockCommitment() (err error) {
+       var txEntries []*bc.Tx
+       for _, tx := range b.block.Transactions {
+               txEntries = append(txEntries, tx.Tx)
+       }
+
+       b.block.BlockHeader.BlockCommitment.TransactionsMerkleRoot, err = types.TxMerkleRoot(txEntries)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
+// 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 (b *blockBuilder) createCoinbaseTx() (tx *types.Tx, err error) {
+       arbitrary := append([]byte{0x00}, []byte(strconv.FormatUint(b.block.Height, 10))...)
+       var script []byte
+       if b.accountManager == nil {
+               script, err = vmutil.DefaultCoinbaseProgram()
+       } else {
+               script, err = b.accountManager.GetCoinbaseControlProgram()
+               arbitrary = append(arbitrary, b.accountManager.GetCoinbaseArbitrary()...)
+       }
+       if err != nil {
+               return nil, err
+       }
+
+       if len(arbitrary) > consensus.CoinbaseArbitrarySizeLimit {
+               return nil, validation.ErrCoinbaseArbitraryOversize
+       }
+
+       builder := txbuilder.NewBuilder(time.Now())
+       if err = builder.AddInput(types.NewCoinbaseInput(arbitrary), &txbuilder.SigningInstruction{}); err != nil {
+               return nil, err
+       }
+
+       if err = builder.AddOutput(types.NewOriginalTxOutput(*consensus.BTMAssetID, 0, script)); err != nil {
+               return nil, err
+       }
+       //TODO: calculate reward to proposer
+
+       _, txData, err := builder.Build()
+       if err != nil {
+               return nil, err
+       }
+
+       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
+}
+
+func (b *blockBuilder) applyTransactions(txs []*types.Tx, timeoutStatus uint8) error {
+       batchTxs := []*types.Tx{}
+       for i := 0; i < len(txs); i++ {
+               if batchTxs = append(batchTxs, txs[i]); len(batchTxs) < batchApplyNum && i != len(txs)-1 {
+                       continue
+               }
+
+               results, gasLeft := b.preValidateTxs(batchTxs, b.chain, b.utxoView, b.gasLeft)
+               for _, result := range results {
+                       if result.err != nil {
+                               log.WithFields(log.Fields{"module": logModule, "error": result.err}).Error("propose block generation: skip tx due to")
+                               b.chain.GetTxPool().RemoveTransaction(&result.tx.ID)
+                               continue
+                       }
+
+                       b.block.Transactions = append(b.block.Transactions, result.tx)
+               }
+
+               b.gasLeft = gasLeft
+               batchTxs = batchTxs[:0]
+               if b.getTimeoutStatus() >= timeoutStatus || len(b.block.Transactions) > softMaxTxNum {
+                       break
+               }
+       }
+       return nil
+}
+
+type validateTxResult struct {
+       tx  *types.Tx
+       err error
+}
+
+func (b *blockBuilder) preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) {
+       var results []*validateTxResult
+       bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}}
+       bcTxs := make([]*bc.Tx, len(txs))
+       for i, tx := range txs {
+               bcTxs[i] = tx.Tx
+       }
+
+       validateResults := validation.ValidateTxs(bcTxs, bcBlock, b.chain.ProgramConverter)
+       for i := 0; i < len(validateResults) && gasLeft > 0; i++ {
+               gasStatus := validateResults[i].GetGasState()
+               if err := validateResults[i].GetError(); err != nil {
+                       results = append(results, &validateTxResult{tx: txs[i], err: err})
+                       continue
+               }
+
+               if err := chain.GetTransactionsUtxo(view, []*bc.Tx{bcTxs[i]}); err != nil {
+                       results = append(results, &validateTxResult{tx: txs[i], err: err})
+                       continue
+               }
+
+               if gasLeft-gasStatus.GasUsed < 0 {
+                       break
+               }
+
+               if err := view.ApplyTransaction(bcBlock, bcTxs[i]); err != nil {
+                       results = append(results, &validateTxResult{tx: txs[i], err: err})
+                       continue
+               }
+
+               results = append(results, &validateTxResult{tx: txs[i], err: validateResults[i].GetError()})
+               gasLeft -= gasStatus.GasUsed
+       }
+       return results, gasLeft
+}
+
+func (b *blockBuilder) getTimeoutStatus() uint8 {
+       if b.timeoutStatus == timeoutCritical {
+               return b.timeoutStatus
+       }
+
+       select {
+       case <-b.criticalTimeoutCh:
+               b.timeoutStatus = timeoutCritical
+       case <-b.warnTimeoutCh:
+               b.timeoutStatus = timeoutWarn
+       default:
+       }
+
+       return b.timeoutStatus
+}
diff --git a/proposal/sort.go b/proposal/sort.go
new file mode 100644 (file)
index 0000000..0ce5b18
--- /dev/null
@@ -0,0 +1,9 @@
+package proposal
+
+import "github.com/bytom/bytom/protocol"
+
+type byTime []*protocol.TxDesc
+
+func (a byTime) Len() int           { return len(a) }
+func (a byTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a byTime) Less(i, j int) bool { return a[i].Added.Before(a[j].Added) }
index c3c8ced..cf1e270 100644 (file)
@@ -18,6 +18,7 @@ type BlockHeader struct {
        Height            uint64  // The height of the block.
        PreviousBlockHash bc.Hash // The hash of the previous block.
        Timestamp         uint64  // The time of the block in seconds.
+       BlockWitness
        BlockCommitment
 }
 
diff --git a/protocol/bc/types/block_witness.go b/protocol/bc/types/block_witness.go
new file mode 100644 (file)
index 0000000..75a64d2
--- /dev/null
@@ -0,0 +1,24 @@
+package types
+
+import (
+       "io"
+
+       "github.com/bytom/bytom/encoding/blockchain"
+)
+
+// BlockWitness save the consensus node sign
+type BlockWitness []byte
+
+func (bw *BlockWitness) readFrom(r *blockchain.Reader) (err error) {
+       *bw, err = blockchain.ReadVarstr31(r)
+       return err
+}
+
+func (bw *BlockWitness) writeTo(w io.Writer) error {
+       _, err := blockchain.WriteVarstr31(w, *bw)
+       return err
+}
+
+func (bw *BlockWitness) Set(data []byte) {
+       copy(*bw, data)
+}
index 2cd26b0..8f091c7 100644 (file)
@@ -1,9 +1,27 @@
 package protocol
 
-import "github.com/bytom/bytom/errors"
+import (
+       "crypto/ed25519"
+
+       "github.com/bytom/bytom/config"
+       "github.com/bytom/bytom/errors"
+       "github.com/bytom/bytom/protocol/bc/types"
+)
 
 var (
        // ErrDoubleSignBlock represent the consensus is double sign in same height of different block
        ErrDoubleSignBlock  = errors.New("the consensus is double sign in same height of different block")
        errInvalidSignature = errors.New("the signature of block is invalid")
 )
+
+var (
+       SignatureLength = ed25519.SignatureSize
+)
+
+func (c *Chain) SignBlockHeader(blockHeader *types.BlockHeader) {
+       c.cond.L.Lock()
+       defer c.cond.L.Unlock()
+       xprv := config.CommonConfig.PrivateKey()
+       signature := xprv.Sign(blockHeader.Hash().Bytes())
+       blockHeader.Set(signature)
+}