diff --git a/orderer/common/bootstrap/static/static.go b/orderer/common/bootstrap/static/static.go index b133e9de2d5..5d7c046d95f 100644 --- a/orderer/common/bootstrap/static/static.go +++ b/orderer/common/bootstrap/static/static.go @@ -18,18 +18,17 @@ package static import ( "fmt" - "time" "github.com/hyperledger/fabric/core/crypto/primitives" "github.com/hyperledger/fabric/orderer/common/bootstrap" "github.com/hyperledger/fabric/orderer/common/cauthdsl" "github.com/hyperledger/fabric/orderer/common/configtx" + "github.com/hyperledger/fabric/orderer/common/util" cb "github.com/hyperledger/fabric/protos/common" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes/timestamp" ) +const msgVersion = int32(1) + type bootstrapper struct { chainID []byte } @@ -43,93 +42,27 @@ func New() bootstrap.Helper { return &bootstrapper{chainID} } -// errorlessMarshal prevents poluting this code with panics -// If the genesis block cannot be created, the system cannot start so panic is correct -func errorlessMarshal(thing proto.Message) []byte { - data, err := proto.Marshal(thing) - if err != nil { - panic(err) - } - return data -} - -func makeChainHeader(headerType cb.HeaderType, version int32, chainID []byte, epoch uint64) *cb.ChainHeader { - return &cb.ChainHeader{ - Type: int32(headerType), - Version: version, - Timestamp: ×tamp.Timestamp{ - Seconds: time.Now().Unix(), - Nanos: 0, - }, - ChainID: chainID, - Epoch: epoch, - } -} - -func makeSignatureHeader(serializedCreatorCertChain []byte, nonce []byte) *cb.SignatureHeader { - return &cb.SignatureHeader{ - Creator: serializedCreatorCertChain, - Nonce: nonce, - } -} - -func (b *bootstrapper) makeSignedConfigurationItem(configurationItemType cb.ConfigurationItem_ConfigurationType, modificationPolicyID string, key string, value []byte) *cb.SignedConfigurationItem { - marshaledConfigurationItem := errorlessMarshal(&cb.ConfigurationItem{ - Header: makeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, 1, b.chainID, 0), - Type: configurationItemType, - LastModified: 0, - ModificationPolicy: modificationPolicyID, - Key: key, - Value: value, - }) - - return &cb.SignedConfigurationItem{ - ConfigurationItem: marshaledConfigurationItem, - Signatures: nil, - } -} - -func (b *bootstrapper) makeConfigurationEnvelope(items ...*cb.SignedConfigurationItem) *cb.ConfigurationEnvelope { - return &cb.ConfigurationEnvelope{ - Items: items, - } -} - -func (b *bootstrapper) makeEnvelope(configurationEnvelope *cb.ConfigurationEnvelope) *cb.Envelope { - nonce, err := primitives.GetRandomNonce() - if err != nil { - panic(fmt.Errorf("Cannot generate random nonce: %s", err)) - } - marshaledPayload := errorlessMarshal(&cb.Payload{ - Header: &cb.Header{ - ChainHeader: makeChainHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, 1, b.chainID, 0), - SignatureHeader: makeSignatureHeader(nil, nonce), - }, - Data: errorlessMarshal(configurationEnvelope), - }) - return &cb.Envelope{ - Payload: marshaledPayload, - Signature: nil, - } -} - -func sigPolicyToPolicy(sigPolicy *cb.SignaturePolicyEnvelope) []byte { - policy := &cb.Policy{ - Type: &cb.Policy_SignaturePolicy{ - SignaturePolicy: sigPolicy, - }, - } - return errorlessMarshal(policy) -} - // GenesisBlock returns the genesis block to be used for bootstrapping func (b *bootstrapper) GenesisBlock() (*cb.Block, error) { // Lock down the default modification policy to prevent any further policy modifications - lockdownDefaultModificationPolicy := b.makeSignedConfigurationItem(cb.ConfigurationItem_Policy, configtx.DefaultModificationPolicyID, configtx.DefaultModificationPolicyID, sigPolicyToPolicy(cauthdsl.RejectAllPolicy)) - - blockData := &cb.BlockData{ - Data: [][]byte{errorlessMarshal(b.makeEnvelope(b.makeConfigurationEnvelope(lockdownDefaultModificationPolicy)))}, - } + configItemKey := configtx.DefaultModificationPolicyID + configItemValue := util.MarshalOrPanic(util.MakePolicyOrPanic(cauthdsl.RejectAllPolicy)) + modPolicy := configtx.DefaultModificationPolicyID + + lastModified := uint64(0) + epoch := uint64(0) + configItemChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, msgVersion, b.chainID, epoch) + configItem := util.MakeConfigurationItem(configItemChainHeader, cb.ConfigurationItem_Policy, lastModified, modPolicy, configItemKey, configItemValue) + signedConfigItem := &cb.SignedConfigurationItem{ConfigurationItem: util.MarshalOrPanic(configItem), Signatures: nil} + + configEnvelope := util.MakeConfigurationEnvelope(signedConfigItem) + payloadChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, configItemChainHeader.Version, b.chainID, epoch) + payloadSignatureHeader := util.MakeSignatureHeader(nil, util.CreateNonceOrPanic()) + payloadHeader := util.MakePayloadHeader(payloadChainHeader, payloadSignatureHeader) + payload := &cb.Payload{Header: payloadHeader, Data: util.MarshalOrPanic(configEnvelope)} + envelope := &cb.Envelope{Payload: util.MarshalOrPanic(payload), Signature: nil} + + blockData := &cb.BlockData{Data: [][]byte{util.MarshalOrPanic(envelope)}} return &cb.Block{ Header: &cb.BlockHeader{ diff --git a/orderer/common/bootstrap/static/static_test.go b/orderer/common/bootstrap/static/static_test.go index 888ed85a205..cde51e1b127 100644 --- a/orderer/common/bootstrap/static/static_test.go +++ b/orderer/common/bootstrap/static/static_test.go @@ -20,11 +20,11 @@ import ( "bytes" "testing" + "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/orderer/common/cauthdsl" "github.com/hyperledger/fabric/orderer/common/configtx" + "github.com/hyperledger/fabric/orderer/common/util" cb "github.com/hyperledger/fabric/protos/common" - - "github.com/golang/protobuf/proto" ) func TestGenesisBlockCreation(t *testing.T) { @@ -49,13 +49,19 @@ func TestGenesisBlockHeader(t *testing.T) { } func TestGenesisBlockData(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("OrPanicked unexpectedly: %s", r) + } + }() + expectedBlockDataLength := 1 expectedPayloadChainHeaderType := int32(cb.HeaderType_CONFIGURATION_TRANSACTION) - expectedChainHeaderVersion := int32(1) + expectedChainHeaderVersion := msgVersion expectedChainHeaderEpoch := uint64(0) expectedConfigEnvelopeItemsLength := 1 expectedConfigurationItemChainHeaderType := int32(cb.HeaderType_CONFIGURATION_ITEM) - expectedConfigurationItemChainHeaderVersion := int32(1) + expectedConfigurationItemChainHeaderVersion := msgVersion expectedConfigurationItemType := cb.ConfigurationItem_Policy expectedConfigEnvelopeSequence := uint64(0) expectedConfigurationItemModificationPolicy := configtx.DefaultModificationPolicyID @@ -67,22 +73,14 @@ func TestGenesisBlockData(t *testing.T) { t.Fatalf("Expected genesis block data length %d, got %d", expectedBlockDataLength, len(genesisBlock.Data.Data)) } - marshaledEnvelope := genesisBlock.Data.Data[0] - envelope := &cb.Envelope{} - if err := proto.Unmarshal(marshaledEnvelope, envelope); err != nil { - t.Fatalf("Expected genesis block to carry an Envelope") - } + envelope := util.ExtractEnvelopeOrPanic(genesisBlock, 0) envelopeSignature := envelope.Signature if !bytes.Equal(envelopeSignature, nil) { t.Fatalf("Expected envelope signature to be nil, got %x", envelopeSignature) } - marshaledPayload := envelope.Payload - payload := &cb.Payload{} - if err := proto.Unmarshal(marshaledPayload, payload); err != nil { - t.Fatalf("Expected genesis block to carry a Payload") - } + payload := util.ExtractPayloadOrPanic(envelope) signatureHeader := payload.Header.SignatureHeader if !bytes.Equal(signatureHeader.Creator, nil) { @@ -100,7 +98,7 @@ func TestGenesisBlockData(t *testing.T) { t.Fatalf("Expected payload chain header version %d, got %d", expectedChainHeaderVersion, payloadChainHeader.Version) } if payloadChainHeader.Epoch != expectedChainHeaderEpoch { - t.Fatalf("Expected payload chain header epoch to be %d, got %d", expectedChainHeaderEpoch, payloadChainHeader.Epoch) + t.Fatalf("Expected payload chain header header epoch to be %d, got %d", expectedChainHeaderEpoch, payloadChainHeader.Epoch) } marshaledConfigurationEnvelope := payload.Data diff --git a/orderer/common/util/util.go b/orderer/common/util/util.go new file mode 100644 index 00000000000..cc390e35cab --- /dev/null +++ b/orderer/common/util/util.go @@ -0,0 +1,184 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "fmt" + "time" + + "github.com/hyperledger/fabric/core/crypto/primitives" + cb "github.com/hyperledger/fabric/protos/common" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes/timestamp" +) + +// MarshalOrPanic serializes a protobuf message and panics if this operation fails. +func MarshalOrPanic(pb proto.Message) []byte { + data, err := proto.Marshal(pb) + if err != nil { + panic(err) + } + return data +} + +// Marshal serializes a protobuf message. +func Marshal(pb proto.Message) ([]byte, error) { + return proto.Marshal(pb) +} + +// CreateNonceOrPanic generates a nonce using the crypto/primitives package +// and panics if this operation fails. +func CreateNonceOrPanic() []byte { + nonce, err := primitives.GetRandomNonce() + if err != nil { + panic(fmt.Errorf("Cannot generate random nonce: %s", err)) + } + return nonce +} + +// CreateNonce generates a nonce using the crypto/primitives package. +func CreateNonce() ([]byte, error) { + nonce, err := primitives.GetRandomNonce() + if err != nil { + return nil, fmt.Errorf("Cannot generate random nonce: %s", err) + } + return nonce, nil +} + +// ExtractEnvelopeOrPanic retrieves the requested envelope from a given block and unmarshals it -- it panics if either of these operation fail. +func ExtractEnvelopeOrPanic(block *cb.Block, index int) *cb.Envelope { + envelopeCount := len(block.Data.Data) + if index < 0 || index >= envelopeCount { + panic("Envelope index out of bounds") + } + marshaledEnvelope := block.Data.Data[index] + envelope := &cb.Envelope{} + if err := proto.Unmarshal(marshaledEnvelope, envelope); err != nil { + panic(fmt.Errorf("Block data does not carry an envelope at index %d: %s", index, err)) + } + return envelope +} + +// ExtractEnvelope retrieves the requested envelope from a given block and unmarshals it. +func ExtractEnvelope(block *cb.Block, index int) (*cb.Envelope, error) { + envelopeCount := len(block.Data.Data) + if index < 0 || index >= envelopeCount { + return nil, fmt.Errorf("Envelope index out of bounds") + } + marshaledEnvelope := block.Data.Data[index] + envelope := &cb.Envelope{} + if err := proto.Unmarshal(marshaledEnvelope, envelope); err != nil { + return nil, fmt.Errorf("Block data does not carry an envelope at index %d: %s", index, err) + } + return envelope, nil +} + +// ExtractPayloadOrPanic retrieves the payload of a given envelope and unmarshals it -- it panics if either of these operations fail. +func ExtractPayloadOrPanic(envelope *cb.Envelope) *cb.Payload { + payload := &cb.Payload{} + if err := proto.Unmarshal(envelope.Payload, payload); err != nil { + panic(fmt.Errorf("Envelope does not carry a Payload: %s", err)) + } + return payload +} + +// ExtractPayload retrieves the payload of a given envelope and unmarshals it. +func ExtractPayload(envelope *cb.Envelope) (*cb.Payload, error) { + payload := &cb.Payload{} + if err := proto.Unmarshal(envelope.Payload, payload); err != nil { + return nil, fmt.Errorf("Envelope does not carry a Payload: %s", err) + } + return payload, nil +} + +// MakeChainHeader creates a ChainHeader. +func MakeChainHeader(headerType cb.HeaderType, version int32, chainID []byte, epoch uint64) *cb.ChainHeader { + return &cb.ChainHeader{ + Type: int32(headerType), + Version: version, + Timestamp: ×tamp.Timestamp{ + Seconds: time.Now().Unix(), + Nanos: 0, + }, + ChainID: chainID, + Epoch: epoch, + } +} + +// MakeSignatureHeader creates a SignatureHeader. +func MakeSignatureHeader(serializedCreatorCertChain []byte, nonce []byte) *cb.SignatureHeader { + return &cb.SignatureHeader{ + Creator: serializedCreatorCertChain, + Nonce: nonce, + } +} + +// MakePayloadHeader creates a Payload Header. +func MakePayloadHeader(ch *cb.ChainHeader, sh *cb.SignatureHeader) *cb.Header { + return &cb.Header{ + ChainHeader: ch, + SignatureHeader: sh, + } +} + +// MakeConfigurationItem makes a ConfigurationItem. +func MakeConfigurationItem(ch *cb.ChainHeader, configItemType cb.ConfigurationItem_ConfigurationType, lastModified uint64, modPolicyID string, key string, value []byte) *cb.ConfigurationItem { + return &cb.ConfigurationItem{ + Header: ch, + Type: configItemType, + LastModified: lastModified, + ModificationPolicy: modPolicyID, + Key: key, + Value: value, + } +} + +// MakeConfigurationEnvelope makes a ConfigurationEnvelope. +func MakeConfigurationEnvelope(items ...*cb.SignedConfigurationItem) *cb.ConfigurationEnvelope { + return &cb.ConfigurationEnvelope{Items: items} +} + +// MakePolicyOrPanic creates a Policy proto message out of a SignaturePolicyEnvelope, and panics if this operation fails. +// NOTE Expand this as more policy types as supported. +func MakePolicyOrPanic(policyEnvelope interface{}) *cb.Policy { + switch pe := policyEnvelope.(type) { + case *cb.SignaturePolicyEnvelope: + return &cb.Policy{ + Type: &cb.Policy_SignaturePolicy{ + SignaturePolicy: pe, + }, + } + default: + panic("Unknown policy envelope type given") + } +} + +// MakePolicy creates a Policy proto message out of a SignaturePolicyEnvelope. +// NOTE Expand this as more policy types as supported. +func MakePolicy(policyEnvelope interface{}) (*cb.Policy, error) { + switch pe := policyEnvelope.(type) { + case *cb.SignaturePolicyEnvelope: + return &cb.Policy{ + Type: &cb.Policy_SignaturePolicy{ + SignaturePolicy: pe, + }, + }, nil + default: + return nil, fmt.Errorf("Unknown policy envelope type given") + } +} diff --git a/orderer/common/util/util_test.go b/orderer/common/util/util_test.go new file mode 100644 index 00000000000..6178f2a6a0f --- /dev/null +++ b/orderer/common/util/util_test.go @@ -0,0 +1,135 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "bytes" + "testing" + + "github.com/hyperledger/fabric/core/crypto/primitives" + + "github.com/golang/protobuf/proto" + cb "github.com/hyperledger/fabric/protos/common" +) + +func TestNonceRandomness(t *testing.T) { + n1, err := CreateNonce() + if err != nil { + t.Fatal(err) + } + n2, err := CreateNonce() + if err != nil { + t.Fatal(err) + } + if bytes.Equal(n1, n2) { + t.Fatalf("Expected nonces to be different, got %x and %x", n1, n2) + } +} + +func TestNonceLength(t *testing.T) { + n, err := CreateNonce() + if err != nil { + t.Fatal(err) + } + actual := len(n) + expected := primitives.NonceSize + if actual != expected { + t.Fatalf("Expected nonce to be of size %d, got %d instead", expected, actual) + } + +} + +func TestExtractEnvelopeWrongIndex(t *testing.T) { + block := testBlock() + if _, err := ExtractEnvelope(block, len(block.GetData().Data)); err == nil { + t.Fatal("Expected envelope extraction to fail (wrong index)") + } +} + +func TestExtractEnvelopeWrongIndexOrPanic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fatal("Expected envelope extraction to panic (wrong index)") + } + }() + + block := testBlock() + ExtractEnvelopeOrPanic(block, len(block.GetData().Data)) +} + +func TestExtractEnvelope(t *testing.T) { + if envelope, err := ExtractEnvelope(testBlock(), 0); err != nil { + t.Fatalf("Expected envelop extraction to succeed: %s", err) + } else if !proto.Equal(envelope, testEnvelope()) { + t.Fatal("Expected extracted envelope to match test envelope") + } +} + +func TestExtractEnvelopeOrPanic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatal("Expected envelope extraction to succeed") + } + }() + + if !proto.Equal(ExtractEnvelopeOrPanic(testBlock(), 0), testEnvelope()) { + t.Fatal("Expected extracted envelope to match test envelope") + } +} + +func TestExtractPayload(t *testing.T) { + if payload, err := ExtractPayload(testEnvelope()); err != nil { + t.Fatalf("Expected payload extraction to succeed: %s", err) + } else if !proto.Equal(payload, testPayload()) { + t.Fatal("Expected extracted payload to match test payload") + } +} + +func TestExtractPayloadOrPanic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatal("Expected payload extraction to succeed") + } + }() + + if !proto.Equal(ExtractPayloadOrPanic(testEnvelope()), testPayload()) { + t.Fatal("Expected extracted payload to match test payload") + } +} + +// Helper functions + +func testPayload() *cb.Payload { + return &cb.Payload{ + Header: MakePayloadHeader(MakeChainHeader(cb.HeaderType_MESSAGE, int32(1), []byte("test"), 0), nil), + Data: []byte("test"), + } +} + +func testEnvelope() *cb.Envelope { + // No need to set the signature + return &cb.Envelope{Payload: MarshalOrPanic(testPayload())} +} + +func testBlock() *cb.Block { + // No need to set the block's Header, or Metadata + return &cb.Block{ + Data: &cb.BlockData{ + Data: [][]byte{MarshalOrPanic(testEnvelope())}, + }, + } +} diff --git a/orderer/main.go b/orderer/main.go index 988604c0084..37bd53f9f87 100644 --- a/orderer/main.go +++ b/orderer/main.go @@ -33,6 +33,7 @@ import ( // "github.com/hyperledger/fabric/orderer/common/broadcastfilter/configfilter" "github.com/hyperledger/fabric/orderer/common/configtx" "github.com/hyperledger/fabric/orderer/common/policies" + "github.com/hyperledger/fabric/orderer/common/util" "github.com/hyperledger/fabric/orderer/config" "github.com/hyperledger/fabric/orderer/kafka" "github.com/hyperledger/fabric/orderer/rawledger" @@ -104,12 +105,8 @@ func retrieveConfiguration(rl rawledger.Reader) *cb.ConfigurationEnvelope { if len(block.Data.Data) != 1 { continue } - if err := proto.Unmarshal(block.Data.Data[0], envelope); err != nil { - panic(fmt.Errorf("Block doesn't carry a message envelope: %s", err)) - } - if err := proto.Unmarshal(envelope.Payload, payload); err != nil { - panic(fmt.Errorf("Message envelope doesn't carry a payload: %s", err)) - } + envelope = util.ExtractEnvelopeOrPanic(block, 0) + payload = util.ExtractPayloadOrPanic(envelope) if payload.Header.ChainHeader.Type != int32(cb.HeaderType_CONFIGURATION_TRANSACTION) { continue }