go_study/fabric-main/orderer/consensus/smartbft/assembler.go

195 lines
5.7 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package smartbft
import (
"encoding/asn1"
"sync/atomic"
"github.com/SmartBFT-Go/consensus/pkg/types"
cb "github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/orderer/common/cluster"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
)
//go:generate mockery -dir . -name Ledger -case underscore -output mocks
// Ledger returns the height and a block with the given number
type Ledger interface {
// Height returns the number of blocks in the ledger this channel is associated with.
Height() uint64
// Block returns a block with the given number,
// or nil if such a block doesn't exist.
Block(number uint64) *cb.Block
}
// Assembler is the proposal assembler
type Assembler struct {
RuntimeConfig *atomic.Value
Logger *flogging.FabricLogger
VerificationSeq func() uint64
}
// AssembleProposal assembles a proposal from the metadata and the request
func (a *Assembler) AssembleProposal(metadata []byte, requests [][]byte) (nextProp types.Proposal) {
rtc := a.RuntimeConfig.Load().(RuntimeConfig)
lastConfigBlockNum := rtc.LastConfigBlock.Header.Number
lastBlock := rtc.LastBlock
if len(requests) == 0 {
a.Logger.Panicf("Programming error, no requests in proposal")
}
batchedRequests := singleConfigTxOrSeveralNonConfigTx(requests, a.Logger)
block := protoutil.NewBlock(lastBlock.Header.Number+1, protoutil.BlockHeaderHash(lastBlock.Header))
block.Data = &cb.BlockData{Data: batchedRequests}
block.Header.DataHash = protoutil.BlockDataHash(block.Data)
if protoutil.IsConfigBlock(block) {
lastConfigBlockNum = block.Header.Number
}
block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.LastConfig{Index: lastConfigBlockNum}),
})
block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
ConsenterMetadata: metadata,
LastConfig: &cb.LastConfig{
Index: lastConfigBlockNum,
},
}),
})
tuple := &ByteBufferTuple{
A: protoutil.MarshalOrPanic(block.Data),
B: protoutil.MarshalOrPanic(block.Metadata),
}
prop := types.Proposal{
Header: protoutil.BlockHeaderBytes(block.Header),
Payload: tuple.ToBytes(),
Metadata: metadata,
VerificationSequence: int64(a.VerificationSeq()),
}
return prop
}
func singleConfigTxOrSeveralNonConfigTx(requests [][]byte, logger Logger) [][]byte {
// Scan until a config transaction is found
var batchedRequests [][]byte
var i int
for i < len(requests) {
currentRequest := requests[i]
envelope, err := protoutil.UnmarshalEnvelope(currentRequest)
if err != nil {
logger.Panicf("Programming error, received bad envelope but should have validated it: %v", err)
continue
}
// If we saw a config transaction, we cannot add any more transactions to the batch.
if protoutil.IsConfigTransaction(envelope) {
break
}
// Else, it's not a config transaction, so add it to the batch.
batchedRequests = append(batchedRequests, currentRequest)
i++
}
// If we don't have any transaction in the batch, it is safe to assume we only
// saw a single transaction which is a config transaction.
if len(batchedRequests) == 0 {
batchedRequests = [][]byte{requests[0]}
}
// At this point, batchedRequests contains either a single config transaction, or a few non config transactions.
return batchedRequests
}
// LastConfigBlockFromLedgerOrPanic returns the last config block from the ledger
func LastConfigBlockFromLedgerOrPanic(ledger Ledger, logger Logger) *cb.Block {
block, err := lastConfigBlockFromLedger(ledger)
if err != nil {
logger.Panicf("Failed retrieving last config block: %v", err)
}
return block
}
func lastConfigBlockFromLedger(ledger Ledger) (*cb.Block, error) {
lastBlockSeq := ledger.Height() - 1
lastBlock := ledger.Block(lastBlockSeq)
if lastBlock == nil {
return nil, errors.Errorf("unable to retrieve block [%d]", lastBlockSeq)
}
lastConfigBlock, err := cluster.LastConfigBlock(lastBlock, ledger)
if err != nil {
return nil, err
}
return lastConfigBlock, nil
}
func PreviousConfigBlockFromLedgerOrPanic(ledger Ledger, logger Logger) *cb.Block {
block, err := previousConfigBlockFromLedger(ledger)
if err != nil {
logger.Panicf("Failed retrieving previous config block: %v", err)
}
return block
}
func previousConfigBlockFromLedger(ledger Ledger) (*cb.Block, error) {
previousBlockSeq := ledger.Height() - 2
if ledger.Height() == 1 {
previousBlockSeq = 0
}
previousBlock := ledger.Block(previousBlockSeq)
if previousBlock == nil {
return nil, errors.Errorf("unable to retrieve block [%d]", previousBlockSeq)
}
previousConfigBlock, err := cluster.LastConfigBlock(previousBlock, ledger)
if err != nil {
return nil, err
}
return previousConfigBlock, nil
}
// LastBlockFromLedgerOrPanic returns the last block from the ledger
func LastBlockFromLedgerOrPanic(ledger Ledger, logger Logger) *cb.Block {
lastBlockSeq := ledger.Height() - 1
lastBlock := ledger.Block(lastBlockSeq)
if lastBlock == nil {
logger.Panicf("Failed retrieving last block")
}
return lastBlock
}
// ByteBufferTuple is the byte slice tuple
type ByteBufferTuple struct {
A []byte
B []byte
}
// ToBytes marshals the buffer tuple to bytes
func (bbt *ByteBufferTuple) ToBytes() []byte {
bytes, err := asn1.Marshal(*bbt)
if err != nil {
panic(err)
}
return bytes
}
// FromBytes unmarshals bytes to a buffer tuple
func (bbt *ByteBufferTuple) FromBytes(bytes []byte) error {
_, err := asn1.Unmarshal(bytes, bbt)
return err
}