From: Mingjing <2595400537@qq.com> Date: Mon, 19 Apr 2021 10:25:33 +0000 (+0800) Subject: feat(proposal):move vapor proposal (#1903) X-Git-Tag: 2.0.0-alpha~24 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=27f54dbe602690f545ce9ad7de3007439040de73;p=bytom%2Fbytom.git feat(proposal):move vapor proposal (#1903) * 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 --- diff --git a/proposal/proposal.go b/proposal/proposal.go new file mode 100644 index 00000000..cfe58289 --- /dev/null +++ b/proposal/proposal.go @@ -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 index 00000000..0ce5b18c --- /dev/null +++ b/proposal/sort.go @@ -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) } diff --git a/protocol/bc/types/block_header.go b/protocol/bc/types/block_header.go index c3c8cedc..cf1e270e 100644 --- a/protocol/bc/types/block_header.go +++ b/protocol/bc/types/block_header.go @@ -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 index 00000000..75a64d25 --- /dev/null +++ b/protocol/bc/types/block_witness.go @@ -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) +} diff --git a/protocol/consensus.go b/protocol/consensus.go index 2cd26b06..8f091c75 100644 --- a/protocol/consensus.go +++ b/protocol/consensus.go @@ -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) +}