Skip to content

Commit

Permalink
[FAB-1528] Add block signature
Browse files Browse the repository at this point in the history
https://jira.hyperledger.org/browse/FAB-1528

This changeset adds a signing interface which the MSP team has agreed to
implement.  It also adds a single signature for the block creation
(which will be adequate for Solo/Kafka but will need to be augmented for
SBFT).

It also adds the signature to the last configuration item.

Change-Id: I87f37b3e4ff523f7a32ccdf2e87a80cc9ca7f87a
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Jan 11, 2017
1 parent 141ab4c commit 65e0675
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 14 deletions.
10 changes: 10 additions & 0 deletions orderer/mocks/multichain/multichain.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,13 @@ func (mcs *ConsenterSupport) WriteBlock(block *cb.Block, _committers []filter.Co
func (mcs *ConsenterSupport) ChainID() string {
return mcs.ChainIDVal
}

// Sign returns the bytes passed in
func (mcs *ConsenterSupport) Sign(message []byte) []byte {
return message
}

// NewSignatureHeader returns an empty signature header
func (mcs *ConsenterSupport) NewSignatureHeader() *cb.SignatureHeader {
return &cb.SignatureHeader{}
}
71 changes: 66 additions & 5 deletions orderer/multichain/chainsupport.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type Chain interface {

// ConsenterSupport provides the resources available to a Consenter implementation
type ConsenterSupport interface {
Signer
BlockCutter() blockcutter.Receiver
SharedConfig() sharedconfig.Manager
CreateNextBlock(messages []*cb.Envelope) *cb.Block
Expand All @@ -83,6 +84,7 @@ type chainSupport struct {
sharedConfigManager sharedconfig.Manager
ledger rawledger.ReadWriter
filters *filter.RuleSet
signer Signer
lastConfiguration uint64
lastConfigSeq uint64
}
Expand All @@ -94,6 +96,7 @@ func newChainSupport(
backing rawledger.ReadWriter,
sharedConfigManager sharedconfig.Manager,
consenters map[string]Consenter,
signer Signer,
) *chainSupport {

cutter := blockcutter.NewReceiverImpl(sharedConfigManager, filters)
Expand All @@ -110,6 +113,7 @@ func newChainSupport(
cutter: cutter,
filters: filters,
ledger: backing,
signer: signer,
}

var err error
Expand Down Expand Up @@ -145,6 +149,14 @@ func (cs *chainSupport) start() {
cs.chain.Start()
}

func (cs *chainSupport) NewSignatureHeader() *cb.SignatureHeader {
return cs.signer.NewSignatureHeader()
}

func (cs *chainSupport) Sign(message []byte) []byte {
return cs.signer.Sign(message)
}

func (cs *chainSupport) SharedConfig() sharedconfig.Manager {
return cs.sharedConfigManager
}
Expand Down Expand Up @@ -181,21 +193,70 @@ func (cs *chainSupport) CreateNextBlock(messages []*cb.Envelope) *cb.Block {
return rawledger.CreateNextBlock(cs.ledger, messages)
}

func (cs *chainSupport) WriteBlock(block *cb.Block, committers []filter.Committer) *cb.Block {
for _, committer := range committers {
committer.Commit()
// TODO, factor this out into common util code
func metadataSignatureBytes(value []byte, sigHeader []byte, blockHeader []byte) []byte {
result := make([]byte, len(value)+len(sigHeader)+len(blockHeader))
last := 0
for _, slice := range [][]byte{value, sigHeader, blockHeader} {
for i := range slice {
result[i+last] = slice[i]
}
last += len(slice)
}
return result
}

func (cs *chainSupport) addBlockSignature(block *cb.Block) {
logger.Debugf("%+v", cs)
logger.Debugf("%+v", cs.signer)
blockSignature := &cb.MetadataSignature{
SignatureHeader: utils.MarshalOrPanic(cs.signer.NewSignatureHeader()),
}

// Note, this value is intentionally nil, as this metadata is only about the signature, there is no additional metadata
// information required beyond the fact that the metadata item is signed.
blockSignatureValue := []byte(nil)

blockSignature.Signature = cs.signer.Sign(metadataSignatureBytes(blockSignatureValue, blockSignature.SignatureHeader, block.Header.Bytes()))

block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = utils.MarshalOrPanic(&cb.Metadata{
Value: blockSignatureValue,
Signatures: []*cb.MetadataSignature{
blockSignature,
},
})
}

func (cs *chainSupport) addLastConfigSignature(block *cb.Block) {
configSeq := cs.configManager.Sequence()
if configSeq > cs.lastConfigSeq {
cs.lastConfiguration = block.Header.Number
cs.lastConfigSeq = configSeq
}

lastConfigSignature := &cb.MetadataSignature{
SignatureHeader: utils.MarshalOrPanic(cs.signer.NewSignatureHeader()),
}

lastConfigValue := utils.MarshalOrPanic(&cb.LastConfiguration{Index: cs.lastConfiguration})

lastConfigSignature.Signature = cs.signer.Sign(metadataSignatureBytes(lastConfigValue, lastConfigSignature.SignatureHeader, block.Header.Bytes()))

block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIGURATION] = utils.MarshalOrPanic(&cb.Metadata{
Value: utils.MarshalOrPanic(&cb.LastConfiguration{Index: cs.lastConfiguration}),
// XXX Add signature once signing is available
Value: lastConfigValue,
Signatures: []*cb.MetadataSignature{
lastConfigSignature,
},
})
}

func (cs *chainSupport) WriteBlock(block *cb.Block, committers []filter.Committer) *cb.Block {
for _, committer := range committers {
committer.Commit()
}

cs.addBlockSignature(block)
cs.addLastConfigSignature(block)

err := cs.ledger.Append(block)
if err != nil {
Expand Down
35 changes: 33 additions & 2 deletions orderer/multichain/chainsupport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package multichain

import (
"bytes"
"reflect"
"testing"

Expand Down Expand Up @@ -64,7 +65,7 @@ func (mc *mockCommitter) Commit() {
func TestCommitConfig(t *testing.T) {
ml := &mockLedgerReadWriter{}
cm := &mockconfigtx.Manager{}
cs := &chainSupport{ledger: ml, configManager: cm}
cs := &chainSupport{ledger: ml, configManager: cm, signer: &xxxCryptoHelper{}}
txs := []*cb.Envelope{makeNormalTx("foo", 0), makeNormalTx("bar", 1)}
committers := []filter.Committer{&mockCommitter{}, &mockCommitter{}}
block := cs.CreateNextBlock(txs)
Expand All @@ -86,10 +87,40 @@ func TestCommitConfig(t *testing.T) {
}
}

func TestMetadataSignatureBytes(t *testing.T) {
value := []byte("Value")
signatureHeader := []byte("SignatureHeader")
blockHeader := []byte("BlockHeader")

sigBytes := metadataSignatureBytes(value, signatureHeader, blockHeader)
expected := []byte("ValueSignatureHeaderBlockHeader")
if !bytes.Equal(sigBytes, expected) {
t.Errorf("Did not compute first signature bytes correctly, expected %s, got %s", expected, sigBytes)
}
}

func TestWriteBlockSignatures(t *testing.T) {
ml := &mockLedgerReadWriter{}
cm := &mockconfigtx.Manager{}
cs := &chainSupport{ledger: ml, configManager: cm, signer: &xxxCryptoHelper{}}

blockMetadata := func(block *cb.Block) *cb.Metadata {
metadata, err := utils.GetMetadataFromBlock(block, cb.BlockMetadataIndex_SIGNATURES)
if err != nil {
panic(err)
}
return metadata
}

if blockMetadata(cs.WriteBlock(cb.NewBlock(0, nil), nil)) == nil {
t.Fatalf("Block should have block signature")
}
}

func TestWriteLastConfiguration(t *testing.T) {
ml := &mockLedgerReadWriter{}
cm := &mockconfigtx.Manager{}
cs := &chainSupport{ledger: ml, configManager: cm}
cs := &chainSupport{ledger: ml, configManager: cm, signer: &xxxCryptoHelper{}}

lastConfig := func(block *cb.Block) uint64 {
index, err := utils.GetLastConfigurationIndexFromBlock(block)
Expand Down
23 changes: 20 additions & 3 deletions orderer/multichain/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,23 @@ func (xxx xxxCryptoHelper) VerifySignature(sd *cb.SignedData) error {
return nil
}

func (xxx xxxCryptoHelper) NewSignatureHeader() *cb.SignatureHeader {
return &cb.SignatureHeader{}
}

func (xxx xxxCryptoHelper) Sign(message []byte) []byte {
return message
}

// Signer is a temporary stub interface which will be implemented by the local MSP
type Signer interface {
// NewSignatureHeader creates a SignatureHeader with the correct signing identity and a valid nonce
NewSignatureHeader() *cb.SignatureHeader

// Sign a message which should embed a signature header created by NewSignatureHeader
Sign(message []byte) []byte
}

// Manager coordinates the creation and access of chains
type Manager interface {
// GetChain retrieves the chain support for a chain (and whether it exists)
Expand Down Expand Up @@ -99,14 +116,14 @@ func NewManagerImpl(ledgerFactory rawledger.Factory, consenters map[string]Conse
logger.Fatalf("There appear to be two system chains %s and %s", ml.sysChain.support.ChainID(), chainID)
}
logger.Debugf("Starting with system chain: %s", chainID)
chain := newChainSupport(createSystemChainFilters(ml, configManager), configManager, policyManager, backingLedger, sharedConfigManager, consenters)
chain := newChainSupport(createSystemChainFilters(ml, configManager), configManager, policyManager, backingLedger, sharedConfigManager, consenters, &xxxCryptoHelper{})
ml.chains[string(chainID)] = chain
ml.sysChain = newSystemChain(chain)
// We delay starting this chain, as it might try to copy and replace the chains map via newChain before the map is fully built
defer chain.start()
} else {
logger.Debugf("Starting chain: %s", chainID)
chain := newChainSupport(createStandardFilters(configManager), configManager, policyManager, backingLedger, sharedConfigManager, consenters)
chain := newChainSupport(createStandardFilters(configManager), configManager, policyManager, backingLedger, sharedConfigManager, consenters, &xxxCryptoHelper{})
ml.chains[string(chainID)] = chain
chain.start()
}
Expand Down Expand Up @@ -208,7 +225,7 @@ func (ml *multiLedger) newChain(configtx *cb.Envelope) {
newChains[key] = value
}

cs := newChainSupport(createStandardFilters(configManager), configManager, policyManager, backingLedger, sharedConfig, ml.consenters)
cs := newChainSupport(createStandardFilters(configManager), configManager, policyManager, backingLedger, sharedConfig, ml.consenters, &xxxCryptoHelper{})
chainID := configManager.ChainID()

logger.Debugf("Created and starting new chain %s", chainID)
Expand Down
7 changes: 5 additions & 2 deletions protos/common/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ func NewBlock(seqNum uint64, previousHash []byte) *Block {
return block
}

func (b *BlockHeader) Hash() []byte {
func (b *BlockHeader) Bytes() []byte {
data, err := proto.Marshal(b) // XXX this is wrong, protobuf is not the right mechanism to serialize for a hash
if err != nil {
panic("This should never fail and is generally irrecoverable")
}
return data
}

return util.ComputeCryptoHash(data)
func (b *BlockHeader) Hash() []byte {
return util.ComputeCryptoHash(b.Bytes())
}

func (b *BlockData) Hash() []byte {
Expand Down
13 changes: 11 additions & 2 deletions protos/utils/blockutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,19 @@ func GetChainIDFromBlock(block *cb.Block) (string, error) {
return payload.Header.ChainHeader.ChainID, nil
}

// GetMetadataFromBlock retrieves metadata at the specified index
func GetMetadataFromBlock(block *cb.Block, index cb.BlockMetadataIndex) (*cb.Metadata, error) {
md := &cb.Metadata{}
err := proto.Unmarshal(block.Metadata.Metadata[index], md)
if err != nil {
return nil, err
}
return md, nil
}

// GetLastConfigurationIndexFromBlock retrieves the index of the last configuration block as encoded in the block metadata
func GetLastConfigurationIndexFromBlock(block *cb.Block) (uint64, error) {
md := &cb.Metadata{}
err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIGURATION], md)
md, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIGURATION)
if err != nil {
return 0, err
}
Expand Down

0 comments on commit 65e0675

Please sign in to comment.