diff --git a/pkg/fab/resource/configtx.go b/pkg/fab/resource/configtx.go index d4a3f67e99..f205d27d1e 100644 --- a/pkg/fab/resource/configtx.go +++ b/pkg/fab/resource/configtx.go @@ -19,6 +19,7 @@ import ( "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/sdkinternal/configtxgen/localconfig" "github.com/hyperledger/fabric-sdk-go/pkg/fab/resource/genesisconfig" + cb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common" ) // See https://github.com/hyperledger/fabric/blob/be235fd3a236f792a525353d9f9586c8b0d4a61a/cmd/configtxgen/main.go @@ -33,7 +34,7 @@ func CreateGenesisBlock(config *genesisconfig.Profile, channelID string) ([]byte if err != nil { return nil, errors.WithMessage(err, "could not create bootstrapper") } - logger.Info("Generating genesis block") + logger.Debug("Generating genesis block") if config.Orderer == nil { return nil, errors.Errorf("refusing to generate block which is missing orderer section") } @@ -41,7 +42,7 @@ func CreateGenesisBlock(config *genesisconfig.Profile, channelID string) ([]byte logger.Warn("Genesis block does not contain a consortiums group definition. This block cannot be used for orderer bootstrap.") } genesisBlock := pgen.GenesisBlockForChannel(channelID) - logger.Info("Writing genesis block") + logger.Debug("Writing genesis block") return protoutil.Marshal(genesisBlock) } @@ -60,7 +61,7 @@ func genesisToLocalConfig(config *genesisconfig.Profile) (*localconfig.Profile, // InspectGenesisBlock inspects a block func InspectGenesisBlock(data []byte) (string, error) { - logger.Info("Parsing genesis block") + logger.Debug("Parsing genesis block") block, err := protoutil.UnmarshalBlock(data) if err != nil { return "", fmt.Errorf("error unmarshaling to block: %s", err) @@ -72,3 +73,45 @@ func InspectGenesisBlock(data []byte) (string, error) { } return buf.String(), nil } + +// CreateChannelCreateTx creates a Fabric transaction for creating a channel +func CreateChannelCreateTx(conf, baseProfile *genesisconfig.Profile, channelID string) ([]byte, error) { + logger.Debug("Generating new channel configtx") + + localConf, err := genesisToLocalConfig(conf) + if err != nil { + return nil, err + } + localBaseProfile, err := genesisToLocalConfig(baseProfile) + if err != nil { + return nil, err + } + + var configtx *cb.Envelope + if baseProfile == nil { + configtx, err = encoder.MakeChannelCreationTransaction(channelID, nil, localConf) + } else { + configtx, err = encoder.MakeChannelCreationTransactionWithSystemChannelContext(channelID, nil, localConf, localBaseProfile) + } + if err != nil { + return nil, err + } + + logger.Debug("Writing new channel tx") + return protoutil.Marshal(configtx) +} + +// InspectChannelCreateTx inspects a Fabric transaction for creating a channel +func InspectChannelCreateTx(data []byte) (string, error) { + logger.Debug("Parsing transaction") + env, err := protoutil.UnmarshalEnvelope(data) + if err != nil { + return "", fmt.Errorf("Error unmarshaling envelope: %s", err) + } + var buf bytes.Buffer + err = protolator.DeepMarshalJSON(&buf, env) + if err != nil { + return "", fmt.Errorf("malformed transaction contents: %s", err) + } + return buf.String(), nil +} diff --git a/pkg/fab/resource/configtx_test.go b/pkg/fab/resource/configtx_test.go index c07720ae1c..9a2ef41009 100644 --- a/pkg/fab/resource/configtx_test.go +++ b/pkg/fab/resource/configtx_test.go @@ -34,33 +34,36 @@ func getPolicies() map[string]*genesisconfig.Policy { } } -var ordererOrg = &genesisconfig.Organization{ - Name: "OrdererOrg", - SkipAsForeign: false, - ID: "OrdererOrg", - MSPDir: filepath.Join(metadata.GetProjectPath(), "test/fixtures/fabric/v1/crypto-config/ordererOrganizations/example.com/msp"), - MSPType: "bccsp", - Policies: map[string]*genesisconfig.Policy{ - "Readers": { - Type: "Signature", - Rule: "OR('OrdererOrg.admin')", - }, - "Writers": { - Type: "Signature", - Rule: "OR('OrdererOrg.admin')", - }, - "Admins": { - Type: "Signature", - Rule: "OR('OrdererOrg.admin')", - }, - "Endorsement": { - Type: "Signature", - Rule: "OR('OrdererOrg.admin')", +func getOrdererOrg() *genesisconfig.Organization { + + return &genesisconfig.Organization{ + Name: "OrdererOrg", + SkipAsForeign: false, + ID: "OrdererOrg", + MSPDir: filepath.Join(metadata.GetProjectPath(), "test/fixtures/fabric/v1/crypto-config/ordererOrganizations/example.com/msp"), + MSPType: "bccsp", + Policies: map[string]*genesisconfig.Policy{ + "Readers": { + Type: "Signature", + Rule: "OR('OrdererOrg.admin')", + }, + "Writers": { + Type: "Signature", + Rule: "OR('OrdererOrg.admin')", + }, + "Admins": { + Type: "Signature", + Rule: "OR('OrdererOrg.admin')", + }, + "Endorsement": { + Type: "Signature", + Rule: "OR('OrdererOrg.admin')", + }, }, - }, + } } -func mockProfile() *genesisconfig.Profile { +func mockSampleSingleMSPSoloProfile() *genesisconfig.Profile { return &genesisconfig.Profile{ Policies: getPolicies(), @@ -79,16 +82,80 @@ func mockProfile() *genesisconfig.Profile { Consortiums: map[string]*genesisconfig.Consortium{ "SampleConsortium": { Organizations: []*genesisconfig.Organization{ - ordererOrg, + getOrdererOrg(), }, }, }, } } +func getApplication() *genesisconfig.Application { + return &genesisconfig.Application{ + ACLs: map[string]string{ + "_lifecycle/CommitChaincodeDefinition": "/Channel/Application/Writers", + "_lifecycle/QueryChaincodeDefinition": "/Channel/Application/Readers", + "_lifecycle/QueryNamespaceDefinitions": "/Channel/Application/Readers", + "lscc/ChaincodeExists": "/Channel/Application/Readers", + "lscc/GetDeploymentSpec": "/Channel/Application/Readers", + "lscc/GetChaincodeData": "/Channel/Application/Readers", + "lscc/GetInstantiatedChaincodes": "/Channel/Application/Readers", + "qscc/GetChainInfo": "/Channel/Application/Readers", + "qscc/GetBlockByNumber": "/Channel/Application/Readers", + "qscc/GetBlockByHash": "/Channel/Application/Readers", + "qscc/GetTransactionByID": "/Channel/Application/Readers", + "qscc/GetBlockByTxID": "/Channel/Application/Readers", + "cscc/GetConfigBlock": "/Channel/Application/Readers", + "cscc/GetConfigTree": "/Channel/Application/Readers", + "cscc/SimulateConfigTreeUpdate": "/Channel/Application/Readers", + "peer/Propose": "/Channel/Application/Writers", + "peer/ChaincodeToChaincode": "/Channel/Application/Readers", + "event/Block": "/Channel/Application/Readers", + "event/FilteredBlock": "/Channel/Application/Readers", + }, + Organizations: []*genesisconfig.Organization{}, + Policies: map[string]*genesisconfig.Policy{ + "LifecycleEndorsement": { + Type: "ImplicitMeta", + Rule: "MAJORITY Endorsement", + }, + "Endorsement": { + Type: "ImplicitMeta", + Rule: "MAJORITY Endorsement", + }, + "Readers": { + Type: "ImplicitMeta", + Rule: "ANY Readers", + }, + "Writers": { + Type: "ImplicitMeta", + Rule: "ANY Writers", + }, + "Admins": { + Type: "ImplicitMeta", + Rule: "MAJORITY Admins", + }, + }, + Capabilities: map[string]bool{ + "V2_0": true, + "V1_3": false, + "V1_2": false, + "V1_1": false, + }, + } +} + +func mockSampleSingleMSPChannelProfile() *genesisconfig.Profile { + + return &genesisconfig.Profile{ + Policies: getPolicies(), + Application: getApplication(), + Consortium: "SampleConsortium", + } +} + func TestCreateAndInspectGenesiBlock(t *testing.T) { - b, err := CreateGenesisBlock(mockProfile(), "mychannel") + b, err := CreateGenesisBlock(mockSampleSingleMSPSoloProfile(), "mychannel") require.NoError(t, err, "Failed to create genesis block") require.NotNil(t, b, "Failed to create genesis block") @@ -96,3 +163,14 @@ func TestCreateAndInspectGenesiBlock(t *testing.T) { require.NoError(t, err, "Failed to inspect genesis block") require.False(t, s == "", "Failed to inspect genesis block") } + +func TestCreateAndInspectConfigTx(t *testing.T) { + + e, err := CreateChannelCreateTx(mockSampleSingleMSPChannelProfile(), nil, "foo") + require.NoError(t, err, "Failed to create channel create tx") + require.NotNil(t, e, "Failed to create channel create tx") + + s, err := InspectChannelCreateTx(e) + require.NoError(t, err, "Failed to inspect channel create tx") + require.False(t, s == "", "Failed to inspect channel create tx") +}