Skip to content

Commit ec38c35

Browse files
author
Jason Yellick
committed
Update to new proposed block format
This changeset updates ab.proto to use the block format discussed in JIRA issue https://jira.hyperledger.org/browse/FAB-384 It more closely mirrors the batch format proposed by Simon in the SBFT work as well as the time tested bitcoin block structure. Note that the block is split into three pieces, Header, Data, and Metadata. This makes the clear distinction that the Headers chain together, and the Headers have a reference to the hash of the Data. Therefore the Metadata section can be used for storage of information which should not be hashed, such as signature collections. See the JIRA issue for more details. Assuming this block format is accepted, it should be removed from ab.proto and pushed into the fabric.proto / fabric_next.proto. Change-Id: I9e9f5afa10e29258ba5ad2a8c536a781e765664f Signed-off-by: Jason Yellick <[email protected]>
1 parent a293bc9 commit ec38c35

File tree

25 files changed

+1064
-252
lines changed

25 files changed

+1064
-252
lines changed

bddtests/ab_pb2.py

+724-24
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bddtests/steps/orderer_util.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def readDeliveredMessages(self, expectedCount):
122122
numToRead = self.getWindowSize() if self.getWindowSize() < expectedCount else expectedCount
123123
msgsRead.extend(self.readMessages(numToRead))
124124
# send the ack
125-
self.sendAcknowledgment(msgsRead[-1].Block.Number)
125+
self.sendAcknowledgment(msgsRead[-1].Block.Header.Number)
126126
print('SentACK!!')
127127
print('')
128128
return msgsRead

core/committer/noopssinglechain/committer.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ func (r *deliverClient) readUntilClose() {
133133
fmt.Println("Got error ", t)
134134
case *ab.DeliverResponse_Block:
135135
txs := []*pb.Transaction2{}
136-
for _, d := range t.Block.Messages {
137-
if d != nil && d.Data != nil {
136+
for _, d := range t.Block.Data.Data {
137+
if d != nil {
138138
tx := &pb.Transaction2{}
139-
if err = proto.Unmarshal(d.Data, tx); err != nil {
139+
if err = proto.Unmarshal(d, tx); err != nil {
140140
fmt.Printf("Error getting tx(%s)...dropping block\n", err)
141141
continue
142142
}
@@ -152,7 +152,7 @@ func (r *deliverClient) readUntilClose() {
152152
r.unAcknowledged++
153153
if r.unAcknowledged >= r.windowSize/2 {
154154
fmt.Println("Sending acknowledgement")
155-
err = r.client.Send(&ab.DeliverUpdate{Type: &ab.DeliverUpdate_Acknowledgement{Acknowledgement: &ab.Acknowledgement{Number: t.Block.Number}}})
155+
err = r.client.Send(&ab.DeliverUpdate{Type: &ab.DeliverUpdate_Acknowledgement{Acknowledgement: &ab.Acknowledgement{Number: t.Block.Header.Number}}})
156156
if err != nil {
157157
return
158158
}

orderer/atomicbroadcast/ab.pb.go

+167-112
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

orderer/atomicbroadcast/ab.proto

+21-8
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,28 @@ message DeliverUpdate {
168168
}
169169
}
170170

171-
// This is a temporary data structure, meant to hold the place of the finalized block structure
172-
// This must be a 'block' structure and not a 'batch' structure, although the terminology is slightly confusing
173-
// The requirement is to allow for a consumer of the orderer to declare the unvalidated blockchain as the definitive
174-
// blockchain, without breaking the hash chain or existing proof
171+
// This is a temporary data structure, but is hopefully very close to the finalized block structure
172+
// Note that the BlockHeader chains to the previous BlockHeader, and the BlockData hash is embedded
173+
// in the BlockHeader. This makes it natural and obvious that the Data is included in the hash, but
174+
// the Metadata is not.
175175
message Block {
176-
uint64 Number = 2;
177-
bytes PrevHash = 3;
178-
bytes Proof = 4;
179-
repeated BroadcastMessage Messages = 5;
176+
BlockHeader Header = 1;
177+
BlockData Data = 2;
178+
BlockMetadata Metadata = 3;
179+
}
180+
181+
message BlockHeader {
182+
uint64 Number = 1; // The position in the blockchain
183+
bytes PreviousHash = 2; // The hash of the previous block header
184+
bytes DataHash = 3; // The hash of the BlockData, by MerkleTree
185+
}
186+
187+
message BlockData {
188+
repeated bytes Data = 1;
189+
}
190+
191+
message BlockMetadata {
192+
repeated bytes Metadata = 1;
180193
}
181194

182195
message DeliverResponse {

orderer/atomicbroadcast/block.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,20 @@ import (
2121
"github.com/hyperledger/fabric/core/util"
2222
)
2323

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

3030
return util.ComputeCryptoHash(data)
3131
}
32+
33+
func (b *BlockData) Hash() []byte {
34+
data, err := proto.Marshal(b) // XXX this is wrong, protobuf is not the right mechanism to serialize for a hash, AND, it is not a MerkleTree hash
35+
if err != nil {
36+
panic("This should never fail and is generally irrecoverable")
37+
}
38+
39+
return util.ComputeCryptoHash(data)
40+
}

orderer/common/bootstrap/static/static.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,17 @@ func (b *bootstrapper) GenesisBlock() (*ab.Block, error) {
8787
},
8888
})
8989

90+
data := &ab.BlockData{
91+
Data: [][]byte{initialConfigTX},
92+
}
93+
9094
return &ab.Block{
91-
Number: 0,
92-
PrevHash: []byte("GENESIS"),
93-
Messages: []*ab.BroadcastMessage{
94-
&ab.BroadcastMessage{Data: initialConfigTX},
95+
Header: &ab.BlockHeader{
96+
Number: 0,
97+
PreviousHash: []byte("GENESIS"),
98+
DataHash: data.Hash(),
9599
},
100+
Data: data,
96101
}, nil
97102

98103
}

orderer/kafka/broadcast.go

+24-11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323

2424
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
2525
"github.com/hyperledger/fabric/orderer/config"
26+
27+
"github.com/golang/protobuf/proto"
2628
)
2729

2830
// Broadcaster allows the caller to submit messages to the orderer
@@ -37,7 +39,7 @@ type broadcasterImpl struct {
3739
once sync.Once
3840

3941
batchChan chan *ab.BroadcastMessage
40-
messages []*ab.BroadcastMessage
42+
messages [][]byte
4143
nextNumber uint64
4244
prevHash []byte
4345
}
@@ -47,7 +49,7 @@ func newBroadcaster(conf *config.TopLevel) Broadcaster {
4749
producer: newProducer(conf),
4850
config: conf,
4951
batchChan: make(chan *ab.BroadcastMessage, conf.General.BatchSize),
50-
messages: []*ab.BroadcastMessage{&ab.BroadcastMessage{Data: []byte("genesis")}},
52+
messages: [][]byte{[]byte("genesis")},
5153
nextNumber: 0,
5254
}
5355
}
@@ -75,19 +77,30 @@ func (b *broadcasterImpl) Close() error {
7577
}
7678

7779
func (b *broadcasterImpl) sendBlock() error {
80+
data := &ab.BlockData{
81+
Data: b.messages,
82+
}
7883
block := &ab.Block{
79-
Messages: b.messages,
80-
Number: b.nextNumber,
81-
PrevHash: b.prevHash,
84+
Header: &ab.BlockHeader{
85+
Number: b.nextNumber,
86+
PreviousHash: b.prevHash,
87+
DataHash: data.Hash(),
88+
},
89+
Data: data,
8290
}
83-
logger.Debugf("Prepared block %d with %d messages (%+v)", block.Number, len(block.Messages), block)
91+
logger.Debugf("Prepared block %d with %d messages (%+v)", block.Header.Number, len(block.Data.Data), block)
8492

85-
b.messages = []*ab.BroadcastMessage{}
93+
b.messages = [][]byte{}
8694
b.nextNumber++
87-
hash, data := hashBlock(block)
88-
b.prevHash = hash
95+
b.prevHash = block.Header.Hash()
96+
97+
blockBytes, err := proto.Marshal(block)
98+
99+
if err != nil {
100+
logger.Fatalf("Error marshaling block: %s", err)
101+
}
89102

90-
return b.producer.Send(data)
103+
return b.producer.Send(blockBytes)
91104
}
92105

93106
func (b *broadcasterImpl) cutBlock(period time.Duration, maxSize uint) {
@@ -96,7 +109,7 @@ func (b *broadcasterImpl) cutBlock(period time.Duration, maxSize uint) {
96109
for {
97110
select {
98111
case msg := <-b.batchChan:
99-
b.messages = append(b.messages, msg)
112+
b.messages = append(b.messages, msg.Data)
100113
if len(b.messages) >= int(maxSize) {
101114
if !timer.Stop() {
102115
<-timer.C

orderer/kafka/broadcast_mock_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func mockNewBroadcaster(t *testing.T, conf *config.TopLevel, seek int64, disk ch
2828
producer: mockNewProducer(t, conf, seek, disk),
2929
config: conf,
3030
batchChan: make(chan *ab.BroadcastMessage, conf.General.BatchSize),
31-
messages: []*ab.BroadcastMessage{&ab.BroadcastMessage{Data: []byte("checkpoint")}},
31+
messages: [][]byte{[]byte("checkpoint")},
3232
nextNumber: uint64(seek),
3333
}
3434
return mb

orderer/kafka/broadcast_test.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestBroadcastInit(t *testing.T) {
4747
if err != nil {
4848
t.Fatal("Expected a block on the broker's disk")
4949
}
50-
if !(bytes.Equal(block.GetMessages()[0].Data, []byte("checkpoint"))) {
50+
if !(bytes.Equal(block.Data.Data[0], []byte("checkpoint"))) {
5151
t.Fatal("Expected first block to be a checkpoint")
5252
}
5353
return
@@ -125,8 +125,8 @@ func TestBroadcastBatch(t *testing.T) {
125125
if err != nil {
126126
t.Fatal("Expected a block on the broker's disk")
127127
}
128-
if len(block.Messages) != int(testConf.General.BatchSize) {
129-
t.Fatalf("Expected block to have %d messages instead of %d", testConf.General.BatchSize, len(block.Messages))
128+
if len(block.Data.Data) != int(testConf.General.BatchSize) {
129+
t.Fatalf("Expected block to have %d messages instead of %d", testConf.General.BatchSize, len(block.Data.Data))
130130
}
131131
return
132132
case <-time.After(500 * time.Millisecond):
@@ -176,8 +176,8 @@ func TestBroadcastIncompleteBatch(t *testing.T) {
176176
if err != nil {
177177
t.Fatal("Expected a block on the broker's disk")
178178
}
179-
if len(block.Messages) != messageCount {
180-
t.Fatalf("Expected block to have %d messages instead of %d", messageCount, len(block.Messages))
179+
if len(block.Data.Data) != messageCount {
180+
t.Fatalf("Expected block to have %d messages instead of %d", messageCount, len(block.Data.Data))
181181
}
182182
return
183183
case <-time.After(testConf.General.BatchTimeout + timePadding):
@@ -229,8 +229,8 @@ func TestBroadcastConsecutiveIncompleteBatches(t *testing.T) {
229229
if err != nil {
230230
t.Fatal("Expected a block on the broker's disk")
231231
}
232-
if len(block.Messages) != messageCount {
233-
t.Fatalf("Expected block to have %d messages instead of %d", messageCount, len(block.Messages))
232+
if len(block.Data.Data) != messageCount {
233+
t.Fatalf("Expected block to have %d messages instead of %d", messageCount, len(block.Data.Data))
234234
}
235235
return
236236
case <-time.After(testConf.General.BatchTimeout + timePadding):
@@ -276,8 +276,8 @@ func TestBroadcastBatchAndQuitEarly(t *testing.T) {
276276
if err != nil {
277277
t.Fatal("Expected a block on the broker's disk")
278278
}
279-
if len(block.Messages) != int(testConf.General.BatchSize) {
280-
t.Fatalf("Expected block to have %d messages instead of %d", testConf.General.BatchSize, len(block.Messages))
279+
if len(block.Data.Data) != int(testConf.General.BatchSize) {
280+
t.Fatalf("Expected block to have %d messages instead of %d", testConf.General.BatchSize, len(block.Data.Data))
281281
}
282282
return
283283
case <-time.After(500 * time.Millisecond):

orderer/kafka/client_deliver.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func (cd *clientDelivererImpl) sendBlocks(stream ab.AtomicBroadcast_DeliverServe
136136
return fmt.Errorf("Failed to send block to the client: %s", err)
137137
}
138138
logger.Debugf("Sent block %v to client (prevHash: %v, messages: %v)\n",
139-
block.Number, block.PrevHash, block.Messages)
139+
block.Header.Number, block.Header.PreviousHash, block.Data.Data)
140140
default:
141141
// Return the push token if there are no messages
142142
// available from the ordering service.

orderer/kafka/client_deliver_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func testClientDeliverAckFunc(label string, seek, window uint64, threshold, expe
171171
case msg := <-mds.outgoing:
172172
count++
173173
if count == threshold {
174-
mds.incoming <- testNewAckMessage(msg.GetBlock().Number)
174+
mds.incoming <- testNewAckMessage(msg.GetBlock().Header.Number)
175175
}
176176
if count > expected {
177177
t.Fatalf("Delivered %d blocks to the client w/o ACK, expected %d", count, expected)

orderer/kafka/consumer_mock_test.go

+15-8
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ import (
2121
"strconv"
2222
"testing"
2323

24-
"github.com/Shopify/sarama"
25-
"github.com/Shopify/sarama/mocks"
2624
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
2725
"github.com/hyperledger/fabric/orderer/config"
26+
27+
"github.com/Shopify/sarama"
28+
"github.com/Shopify/sarama/mocks"
29+
"github.com/golang/protobuf/proto"
2830
)
2931

3032
type mockConsumerImpl struct {
@@ -93,15 +95,20 @@ func (mc *mockConsumerImpl) testFillWithBlocks(seek int64) {
9395
}
9496

9597
func testNewConsumerMessage(offset int64, topic string) *sarama.ConsumerMessage {
98+
blockData := &ab.BlockData{
99+
Data: [][]byte{[]byte(strconv.FormatInt(offset, 10))},
100+
}
96101
block := &ab.Block{
97-
Messages: []*ab.BroadcastMessage{
98-
&ab.BroadcastMessage{
99-
Data: []byte(strconv.FormatInt(offset, 10)),
100-
},
102+
Header: &ab.BlockHeader{
103+
Number: uint64(offset),
101104
},
102-
Number: uint64(offset),
105+
Data: blockData,
106+
}
107+
108+
data, err := proto.Marshal(block)
109+
if err != nil {
110+
panic("Error marshaling block")
103111
}
104-
_, data := hashBlock(block)
105112

106113
return &sarama.ConsumerMessage{
107114
Value: sarama.ByteEncoder(data),

orderer/kafka/util.go

-16
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,8 @@ limitations under the License.
1717
package kafka
1818

1919
import (
20-
"fmt"
21-
2220
"github.com/Shopify/sarama"
23-
"github.com/golang/protobuf/proto"
24-
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
2521
"github.com/hyperledger/fabric/orderer/config"
26-
"golang.org/x/crypto/sha3"
2722
)
2823

2924
const (
@@ -32,17 +27,6 @@ const (
3227
windowOutOfRangeError = "Window out of range"
3328
)
3429

35-
func hashBlock(block *ab.Block) (hash, data []byte) {
36-
data, err := proto.Marshal(block)
37-
if err != nil {
38-
panic(fmt.Errorf("Failed to marshal block: %v", err))
39-
}
40-
41-
hash = make([]byte, 64)
42-
sha3.ShakeSum256(hash, data)
43-
return
44-
}
45-
4630
func newBrokerConfig(conf *config.TopLevel) *sarama.Config {
4731
brokerConfig := sarama.NewConfig()
4832
brokerConfig.Version = conf.Kafka.Version

orderer/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,13 @@ func retrieveConfiguration(rl rawledger.Reader) *ab.ConfigurationEnvelope {
8484
panic(fmt.Errorf("Error parsing blockchain at startup: %v", status))
8585
}
8686
// ConfigTxs should always be by themselves
87-
if len(block.Messages) != 1 {
87+
if len(block.Data.Data) != 1 {
8888
continue
8989
}
9090

9191
maybeConfigTx := &ab.ConfigurationEnvelope{}
9292

93-
err := proto.Unmarshal(block.Messages[0].Data, maybeConfigTx)
93+
err := proto.Unmarshal(block.Data.Data[0], maybeConfigTx)
9494

9595
if err == nil {
9696
lastConfigTx = maybeConfigTx

0 commit comments

Comments
 (0)