Skip to content

Commit d4a11db

Browse files
author
Jason Yellick
committed
[FAB-2584] configtxgen prints block config as json
https://jira.hyperledger.org/browse/FAB-2584 This CR leverages the work from FAB-2577 to allow the configtxgen tool to dump the configuration inside a configuration block to the screen as JSON. This allows for the inspection of the genesis material. Change-Id: I863286b88f456adf7a7958af86306de148991817 Signed-off-by: Jason Yellick <[email protected]>
1 parent 86f65d3 commit d4a11db

File tree

4 files changed

+261
-23
lines changed

4 files changed

+261
-23
lines changed

common/configtx/config.go

+8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ import (
2929
"github.com/golang/protobuf/proto"
3030
)
3131

32+
type ConfigResult interface {
33+
JSON() string
34+
}
35+
36+
func NewConfigResult(config *cb.ConfigGroup, proposer api.Proposer) (ConfigResult, error) {
37+
return processConfig(config, proposer)
38+
}
39+
3240
type configResult struct {
3341
tx interface{}
3442
groupName string

common/configtx/tool/configtxgen/main.go

+98-23
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,118 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"bytes"
21+
"encoding/json"
2022
"flag"
23+
"fmt"
2124
"io/ioutil"
2225

2326
"github.com/hyperledger/fabric/bccsp/factory"
2427
"github.com/hyperledger/fabric/common/configtx"
2528
genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig"
2629
"github.com/hyperledger/fabric/common/configtx/tool/provisional"
2730
"github.com/hyperledger/fabric/msp"
31+
cb "github.com/hyperledger/fabric/protos/common"
2832
"github.com/hyperledger/fabric/protos/utils"
2933

34+
"github.com/golang/protobuf/proto"
3035
logging "github.com/op/go-logging"
3136
)
3237

3338
var logger = logging.MustGetLogger("common/configtx/tool")
3439

40+
func doOutputBlock(pgen provisional.Generator, channelID string, outputBlock string) {
41+
logger.Info("Generating genesis block")
42+
genesisBlock := pgen.GenesisBlockForChannel(channelID)
43+
logger.Info("Writing genesis block")
44+
err := ioutil.WriteFile(outputBlock, utils.MarshalOrPanic(genesisBlock), 0644)
45+
if err != nil {
46+
logger.Errorf("Error writing genesis block: %s", err)
47+
}
48+
}
49+
50+
func doOutputChannelCreateTx(pgen provisional.Generator, channelID string, outputChannelCreateTx string) {
51+
logger.Info("Generating new channel configtx")
52+
// TODO, use actual MSP eventually
53+
signer, err := msp.NewNoopMsp().GetDefaultSigningIdentity()
54+
if err != nil {
55+
logger.Fatalf("Error getting signing identity: %s", err)
56+
}
57+
configtx, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, channelID, signer, pgen.ChannelTemplate())
58+
if err != nil {
59+
logger.Fatalf("Error generating configtx: %s", err)
60+
}
61+
logger.Info("Writing new channel tx")
62+
err = ioutil.WriteFile(outputChannelCreateTx, utils.MarshalOrPanic(configtx), 0644)
63+
if err != nil {
64+
logger.Errorf("Error writing channel create tx: %s", err)
65+
}
66+
}
67+
68+
func doInspectBlock(inspectBlock string) {
69+
logger.Info("Inspecting block")
70+
data, err := ioutil.ReadFile(inspectBlock)
71+
logger.Info("Parsing genesis block")
72+
block := &cb.Block{}
73+
err = proto.Unmarshal(data, block)
74+
if err != nil {
75+
logger.Fatalf("Error unmarshaling block: %s", err)
76+
}
77+
78+
ctx, err := utils.ExtractEnvelope(block, 0)
79+
if err != nil {
80+
logger.Fatalf("Error retrieving configtx from block: %s", err)
81+
}
82+
83+
payload, err := utils.UnmarshalPayload(ctx.Payload)
84+
if err != nil {
85+
logger.Fatalf("Error extracting configtx payload: %s", err)
86+
}
87+
88+
if payload.Header == nil {
89+
logger.Fatalf("Config block did not contain header")
90+
}
91+
92+
header, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
93+
if err != nil {
94+
logger.Fatalf("Error unmarshaling channel header: %s", err)
95+
}
96+
97+
if header.Type != int32(cb.HeaderType_CONFIG) {
98+
logger.Fatalf("Bad header type: %d", header.Type)
99+
}
100+
101+
configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data)
102+
if err != nil {
103+
logger.Fatalf("Bad configuration envelope")
104+
}
105+
106+
if configEnvelope.Config == nil {
107+
logger.Fatalf("ConfigEnvelope contained no config")
108+
}
109+
110+
configResult, err := configtx.NewConfigResult(configEnvelope.Config.ChannelGroup, configtx.NewInitializer())
111+
if err != nil {
112+
logger.Fatalf("Error parsing configuration: %s", err)
113+
}
114+
115+
buffer := &bytes.Buffer{}
116+
json.Indent(buffer, []byte(configResult.JSON()), "", " ")
117+
118+
fmt.Printf("Config for channel: %s at sequence %d\n", header.ChannelId, configEnvelope.Config.Sequence)
119+
120+
fmt.Println(buffer.String())
121+
}
122+
35123
func main() {
36-
var outputBlock, outputChannelCreateTx, profile, channelID string
124+
var outputBlock, outputChannelCreateTx, profile, channelID, inspectBlock string
37125

38126
flag.StringVar(&outputBlock, "outputBlock", "", "The path to write the genesis block to (if set)")
39127
flag.StringVar(&channelID, "channelID", provisional.TestChainID, "The channel ID to use in the configtx")
40128
flag.StringVar(&outputChannelCreateTx, "outputCreateChannelTx", "", "The path to write a channel creation configtx to (if set)")
41129
flag.StringVar(&profile, "profile", genesisconfig.SampleInsecureProfile, "The profile from configtx.yaml to use for generation.")
130+
flag.StringVar(&inspectBlock, "inspectBlock", "", "Prints the configuration contained in the block at the specified path")
131+
42132
flag.Parse()
43133

44134
logging.SetLevel(logging.INFO, "")
@@ -49,30 +139,15 @@ func main() {
49139
pgen := provisional.New(config)
50140

51141
if outputBlock != "" {
52-
logger.Info("Generating genesis block")
53-
genesisBlock := pgen.GenesisBlock()
54-
logger.Info("Writing genesis block")
55-
err := ioutil.WriteFile(outputBlock, utils.MarshalOrPanic(genesisBlock), 0644)
56-
if err != nil {
57-
logger.Errorf("Error writing genesis block: %s", err)
58-
}
142+
doOutputBlock(pgen, channelID, outputBlock)
59143
}
60144

61145
if outputChannelCreateTx != "" {
62-
logger.Info("Generating new channel configtx")
63-
// TODO, use actual MSP eventually
64-
signer, err := msp.NewNoopMsp().GetDefaultSigningIdentity()
65-
if err != nil {
66-
logger.Fatalf("Error getting signing identity: %s", err)
67-
}
68-
configtx, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, channelID, signer, pgen.ChannelTemplate())
69-
if err != nil {
70-
logger.Fatalf("Error generating configtx: %s", err)
71-
}
72-
logger.Info("Writing new channel tx")
73-
err = ioutil.WriteFile(outputChannelCreateTx, utils.MarshalOrPanic(configtx), 0644)
74-
if err != nil {
75-
logger.Errorf("Error writing channel create tx: %s", err)
76-
}
146+
doOutputChannelCreateTx(pgen, channelID, outputChannelCreateTx)
147+
}
148+
149+
if inspectBlock != "" {
150+
doInspectBlock(inspectBlock)
77151
}
152+
78153
}

common/configtx/tool/provisional/provisional.go

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ type Generator interface {
4343

4444
// ChannelTemplate returns a template which can be used to help initialize a channel
4545
ChannelTemplate() configtx.Template
46+
47+
GenesisBlockForChannel(channelID string) *cb.Block
4648
}
4749

4850
const (

docs/configtxgen.md

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Configuring using the configtxgen tool
2+
3+
This document describe the usage for the `configtxgen` utility for manipulating fabric channel configuration.
4+
5+
For now, the tool is primarily focused on generating the genesis block for bootstrapping the orderer, but it is intended to be enhanced in the future for generating new channel configurations as well as reconfiguring existing channels.
6+
7+
## Building the tool
8+
9+
Building the tool is as simple as `make configtxgen`. This will create a `configtxgen` binary at `build/bin/configtxgen` which is included in the Vagrant development environment path by default.
10+
11+
## Configuration Profiles
12+
13+
The configuration parameters supplied to the `configtxgen` tool are primarily provided by the `configtx.yaml` file. This file is located at `fabric/common/configtx/tool/configtx.yaml` in the fabric.git repository.
14+
15+
This configuration file is split primarily into three pieces.
16+
17+
1. The `Profiles` section. By default, this section includes some sample configurations which can be used for development or testing scenarios, and refer to crypto material present in the fabric.git tree. These profiles can make a good starting point for construction a real deployment profile. The `configtxgen` tool allows you to specify the profile it is operating under by passing the `-profile` flag. Profiles may explicitly declare all configuration, but usually inherit configuration from the defaults in (3) below.
18+
2. The `Organizations` section. By default, this section includes a single reference to the sampleconfig MSP definition. For production deployments, the sample organization should be removed, and the MSP definitions of the network members should be referenced and defined instead. Each element in the `Organizations` section should be tagged with an anchor label such as `&orgName` which will allow the definition to be referenced in the `Profiles` sections.
19+
3. The default sections. There are default sections for `Orderer` and `Application` configuration, these include attributes like `BatchTimeout` and are generally used as the base inherited values for the profiles.
20+
21+
This configuration file may be edited, or, individual properties may be overridden by setting environment variables, such as `CONFIGTX_ORDERER_ORDERERTYPE=kafka`. Note that the `Profiles` element and profile name do not need to be specified.
22+
23+
## Bootstrapping the orderer
24+
After creating a configuration profile as desired, simply invoke
25+
```
26+
configtxgen -profile <profile_name> -outputBlock <genesis.blockname>
27+
```
28+
This will produce a `genesis.block` file in the current directory. If you wish to skip writing the file simply do not pass `outputBlock`
29+
30+
Then, to utilize this genesis block, before starting the orderer, simply specify `ORDERER_GENERAL_GENESISMETHOD=file` and `ORDERER_GENERAL_GENESISFILE=$PWD/genesis.block` or modify the `orderer.yaml` file to encode these values.
31+
32+
## Creating a channel
33+
34+
The tool can also output a channel creation tx by executing
35+
36+
```
37+
configtxgen -profile <profile_name> -outputCreateChannelTx <output.txname>
38+
```
39+
40+
This will output a marshaled `Envelope` message which may be sent to broadcast to create a channel.
41+
42+
## Reviewing a configuration block
43+
44+
In addition to creating configuration, the `configtxgen` tool is also capable of inspecting configuration. You may even wish to combine the inspection with generation. For example:
45+
46+
```
47+
$ build/bin/configtxgen -channelID foo -outputBlock foo.block -inspectBlock foo.block
48+
2017/03/01 21:24:24 Loading configuration
49+
2017/03/01 21:24:24 Checking for configtx.yaml at:
50+
2017/03/01 21:24:24 Checking for configtx.yaml at:
51+
2017/03/01 21:24:24 Checking for configtx.yaml at: /home/yellickj/go/src/github.com/hyperledger/fabric/common/configtx/tool
52+
2017/03/01 21:24:24 map[orderer:map[BatchSize:map[MaxMessageCount:10 AbsoluteMaxBytes:99 MB PreferredMaxBytes:512 KB] Kafka:map[Brokers:[127.0.0.1:9092]] Organizations:<nil> OrdererType:solo Addresses:[127.0.0.1:7050] BatchTimeout:10s] application:map[Organizations:<nil>] profiles:map[SampleInsecureSolo:map[Orderer:map[BatchTimeout:10s BatchSize:map[MaxMessageCount:10 AbsoluteMaxBytes:99 MB PreferredMaxBytes:512 KB] Kafka:map[Brokers:[127.0.0.1:9092]] Organizations:<nil> OrdererType:solo Addresses:[127.0.0.1:7050]] Application:map[Organizations:<nil>]] SampleInsecureKafka:map[Orderer:map[Addresses:[127.0.0.1:7050] BatchTimeout:10s BatchSize:map[AbsoluteMaxBytes:99 MB PreferredMaxBytes:512 KB MaxMessageCount:10] Kafka:map[Brokers:[127.0.0.1:9092]] Organizations:<nil> OrdererType:kafka] Application:map[Organizations:<nil>]] SampleSingleMSPSolo:map[Orderer:map[OrdererType:solo Addresses:[127.0.0.1:7050] BatchTimeout:10s BatchSize:map[MaxMessageCount:10 AbsoluteMaxBytes:99 MB PreferredMaxBytes:512 KB] Kafka:map[Brokers:[127.0.0.1:9092]] Organizations:[map[Name:SampleOrg ID:DEFAULT MSPDir:msp/sampleconfig BCCSP:map[Default:SW SW:map[Hash:SHA3 Security:256 FileKeyStore:map[KeyStore:<nil>]]] AnchorPeers:[map[Host:127.0.0.1 Port:7051]]]]] Application:map[Organizations:[map[Name:SampleOrg ID:DEFAULT MSPDir:msp/sampleconfig BCCSP:map[Default:SW SW:map[Hash:SHA3 Security:256 FileKeyStore:map[KeyStore:<nil>]]] AnchorPeers:[map[Port:7051 Host:127.0.0.1]]]]]]] organizations:[map[Name:SampleOrg ID:DEFAULT MSPDir:msp/sampleconfig BCCSP:map[Default:SW SW:map[Hash:SHA3 Security:256 FileKeyStore:map[KeyStore:<nil>]]] AnchorPeers:[map[Host:127.0.0.1 Port:7051]]]]]
53+
2017/03/01 21:24:24 Generating genesis block
54+
2017/03/01 21:24:24 Writing genesis block
55+
2017/03/01 21:24:24 Inspecting block
56+
2017/03/01 21:24:24 Parsing genesis block
57+
Config for channel: foo
58+
{
59+
"": {
60+
"Values": {},
61+
"Groups": {
62+
"/Channel": {
63+
"Values": {
64+
"HashingAlgorithm": {
65+
"Version": "0",
66+
"ModPolicy": "",
67+
"Value": {
68+
"name": "SHA256"
69+
}
70+
},
71+
"BlockDataHashingStructure": {
72+
"Version": "0",
73+
"ModPolicy": "",
74+
"Value": {
75+
"width": 4294967295
76+
}
77+
},
78+
"OrdererAddresses": {
79+
"Version": "0",
80+
"ModPolicy": "",
81+
"Value": {
82+
"addresses": [
83+
"127.0.0.1:7050"
84+
]
85+
}
86+
}
87+
},
88+
"Groups": {
89+
"/Channel/Orderer": {
90+
"Values": {
91+
"ChainCreationPolicyNames": {
92+
"Version": "0",
93+
"ModPolicy": "",
94+
"Value": {
95+
"names": [
96+
"AcceptAllPolicy"
97+
]
98+
}
99+
},
100+
"ConsensusType": {
101+
"Version": "0",
102+
"ModPolicy": "",
103+
"Value": {
104+
"type": "solo"
105+
}
106+
},
107+
"BatchSize": {
108+
"Version": "0",
109+
"ModPolicy": "",
110+
"Value": {
111+
"maxMessageCount": 10,
112+
"absoluteMaxBytes": 103809024,
113+
"preferredMaxBytes": 524288
114+
}
115+
},
116+
"BatchTimeout": {
117+
"Version": "0",
118+
"ModPolicy": "",
119+
"Value": {
120+
"timeout": "10s"
121+
}
122+
},
123+
"IngressPolicyNames": {
124+
"Version": "0",
125+
"ModPolicy": "",
126+
"Value": {
127+
"names": [
128+
"AcceptAllPolicy"
129+
]
130+
}
131+
},
132+
"EgressPolicyNames": {
133+
"Version": "0",
134+
"ModPolicy": "",
135+
"Value": {
136+
"names": [
137+
"AcceptAllPolicy"
138+
]
139+
}
140+
}
141+
},
142+
"Groups": {}
143+
},
144+
"/Channel/Application": {
145+
"Values": {},
146+
"Groups": {}
147+
}
148+
}
149+
}
150+
}
151+
}
152+
}
153+
```

0 commit comments

Comments
 (0)