Skip to content

Commit e72a671

Browse files
author
Jason Yellick
committed
[FAb-1699] Deterministic hashing for block header
https://jira.hyperledger.org/browse/FAB-1699 Currently, we compute the hash of the block header by hashing over the protobuf marshaling of the BlockHeader object. This is not guaranteed to be deterministic, so a reliable marshaling scheme is required. This changeset causes block headers to be marshaled to bytes via ASN.1. Note: Due to an idiosyncracy in the golang ASN.1 implementation, block numbers of MaxInt64 cannot be encoded, and this will result in a panic. This resulting byte slice is used as the parameter to the hashing algorithm. Change-Id: I3b0582e89c17c0d120f3e2d888a2205022f2de18 Signed-off-by: Jason Yellick <[email protected]>
1 parent 051229a commit e72a671

File tree

4 files changed

+81
-4
lines changed

4 files changed

+81
-4
lines changed

protos/common/block.go

+27-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ limitations under the License.
1717
package common
1818

1919
import (
20+
"encoding/asn1"
21+
"fmt"
22+
"math"
23+
2024
"github.com/golang/protobuf/proto"
2125
"github.com/hyperledger/fabric/common/util"
2226
)
@@ -38,16 +42,35 @@ func NewBlock(seqNum uint64, previousHash []byte) *Block {
3842
return block
3943
}
4044

41-
// Bytes returns the marshaled representation of the block header.
45+
type asn1Header struct {
46+
Number int64
47+
PreviousHash []byte
48+
DataHash []byte
49+
}
50+
51+
// Bytes returns the ASN.1 marshaled representation of the block header.
4252
func (b *BlockHeader) Bytes() []byte {
43-
data, err := proto.Marshal(b) // XXX this is wrong, protobuf is not the right mechanism to serialize for a hash
53+
asn1Header := asn1Header{
54+
PreviousHash: b.PreviousHash,
55+
DataHash: b.DataHash,
56+
}
57+
if b.Number > uint64(math.MaxInt64) {
58+
panic(fmt.Errorf("Golang does not currently support encoding uint64 to asn1"))
59+
} else {
60+
asn1Header.Number = int64(b.Number)
61+
}
62+
result, err := asn1.Marshal(asn1Header)
4463
if err != nil {
45-
panic("This should never fail and is generally irrecoverable")
64+
// Errors should only arise for types which cannot be encoded, since the
65+
// BlockHeader type is known a-priori to contain only encodable types, an
66+
// error here is fatal and should not be propogated
67+
panic(err)
4668
}
47-
return data
69+
return result
4870
}
4971

5072
// Hash returns the hash of the block header.
73+
// XXX This method will be removed shortly to allow for confgurable hashing algorithms
5174
func (b *BlockHeader) Hash() []byte {
5275
return util.ComputeCryptoHash(b.Bytes())
5376
}

protos/common/block_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package common
18+
19+
import (
20+
"math"
21+
"testing"
22+
)
23+
24+
func TestGoodBlockHeaderBytes(t *testing.T) {
25+
goodBlockHeader := &BlockHeader{
26+
Number: 1,
27+
PreviousHash: []byte("foo"),
28+
DataHash: []byte("bar"),
29+
}
30+
31+
_ = goodBlockHeader.Bytes() // Should not panic
32+
}
33+
34+
func TestBadBlockHeaderBytes(t *testing.T) {
35+
defer func() {
36+
if recover() == nil {
37+
t.Fatalf("Should have panicked on block number too high to encode as int64")
38+
}
39+
}()
40+
41+
badBlockHeader := &BlockHeader{
42+
Number: math.MaxUint64,
43+
PreviousHash: []byte("foo"),
44+
DataHash: []byte("bar"),
45+
}
46+
47+
_ = badBlockHeader.Bytes() // Should panic
48+
}

protos/common/common.pb.go

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

protos/common/common.proto

+3
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ message Block {
145145
BlockMetadata Metadata = 3;
146146
}
147147

148+
// BlockHeader is the element of the block which forms the block chain
149+
// The block header is hashed using the configured chain hashing algorithm
150+
// over the ASN.1 encoding of the BlockHeader
148151
message BlockHeader {
149152
uint64 Number = 1; // The position in the blockchain
150153
bytes PreviousHash = 2; // The hash of the previous block header

0 commit comments

Comments
 (0)