diff --git a/core/types/block.go b/core/types/block.go
index 92b868d9da..fc3da50d5c 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -100,6 +100,14 @@ type headerMarshaling struct {
// Hash returns the block hash of the header, which is simply the keccak256 hash of its
// RLP encoding.
func (h *Header) Hash() common.Hash {
+ // If the mix digest is equivalent to the predefined Istanbul digest, use Istanbul
+ // specific hash calculation.
+ if h.MixDigest == IstanbulDigest {
+ // Seal is reserved in extra-data. To prove block is signed by the proposer.
+ if istanbulHeader := IstanbulFilteredHeader(h, true); istanbulHeader != nil {
+ return rlpHash(istanbulHeader)
+ }
+ }
return rlpHash(h)
}
diff --git a/core/types/istanbul.go b/core/types/istanbul.go
new file mode 100644
index 0000000000..3375dbbda3
--- /dev/null
+++ b/core/types/istanbul.go
@@ -0,0 +1,107 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "errors"
+ "io"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+var (
+ // IstanbulDigest represents a hash of "Istanbul practical byzantine fault tolerance"
+ // to identify whether the block is from Istanbul consensus engine
+ IstanbulDigest = common.HexToHash("0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365")
+
+ IstanbulExtraVanity = 32 // Fixed number of extra-data bytes reserved for validator vanity
+ IstanbulExtraSeal = 65 // Fixed number of extra-data bytes reserved for validator seal
+
+ // ErrInvalidIstanbulHeaderExtra is returned if the length of extra-data is less than 32 bytes
+ ErrInvalidIstanbulHeaderExtra = errors.New("invalid istanbul header extra-data")
+)
+
+type IstanbulExtra struct {
+ Validators []common.Address
+ Seal []byte
+ CommittedSeal [][]byte
+}
+
+// EncodeRLP serializes ist into the Ethereum RLP format.
+func (ist *IstanbulExtra) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, []interface{}{
+ ist.Validators,
+ ist.Seal,
+ ist.CommittedSeal,
+ })
+}
+
+// DecodeRLP implements rlp.Decoder, and load the istanbul fields from a RLP stream.
+func (ist *IstanbulExtra) DecodeRLP(s *rlp.Stream) error {
+ var istanbulExtra struct {
+ Validators []common.Address
+ Seal []byte
+ CommittedSeal [][]byte
+ }
+ if err := s.Decode(&istanbulExtra); err != nil {
+ return err
+ }
+ ist.Validators, ist.Seal, ist.CommittedSeal = istanbulExtra.Validators, istanbulExtra.Seal, istanbulExtra.CommittedSeal
+ return nil
+}
+
+// ExtractIstanbulExtra extracts all values of the IstanbulExtra from the header. It returns an
+// error if the length of the given extra-data is less than 32 bytes or the extra-data can not
+// be decoded.
+func ExtractIstanbulExtra(h *Header) (*IstanbulExtra, error) {
+ if len(h.Extra) < IstanbulExtraVanity {
+ return nil, ErrInvalidIstanbulHeaderExtra
+ }
+
+ var istanbulExtra *IstanbulExtra
+ err := rlp.DecodeBytes(h.Extra[IstanbulExtraVanity:], &istanbulExtra)
+ if err != nil {
+ return nil, err
+ }
+ return istanbulExtra, nil
+}
+
+// IstanbulFilteredHeader returns a filtered header which some information (like seal, committed seals)
+// are clean to fulfill the Istanbul hash rules. It returns nil if the extra-data cannot be
+// decoded/encoded by rlp.
+func IstanbulFilteredHeader(h *Header, keepSeal bool) *Header {
+ newHeader := CopyHeader(h)
+ istanbulExtra, err := ExtractIstanbulExtra(newHeader)
+ if err != nil {
+ return nil
+ }
+
+ if !keepSeal {
+ istanbulExtra.Seal = []byte{}
+ }
+ istanbulExtra.CommittedSeal = [][]byte{}
+
+ payload, err := rlp.EncodeToBytes(&istanbulExtra)
+ if err != nil {
+ return nil
+ }
+
+ newHeader.Extra = append(newHeader.Extra[:IstanbulExtraVanity], payload...)
+
+ return newHeader
+}
diff --git a/core/types/istanbul_test.go b/core/types/istanbul_test.go
new file mode 100644
index 0000000000..db5453b53a
--- /dev/null
+++ b/core/types/istanbul_test.go
@@ -0,0 +1,88 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+func TestHeaderHash(t *testing.T) {
+ // 0xcefefd3ade63a5955bca4562ed840b67f39e74df217f7e5f7241a6e9552cca70
+ expectedExtra := common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000000f89af8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b440b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0")
+ expectedHash := common.HexToHash("0xcefefd3ade63a5955bca4562ed840b67f39e74df217f7e5f7241a6e9552cca70")
+
+ // for istanbul consensus
+ header := &Header{MixDigest: IstanbulDigest, Extra: expectedExtra}
+ if !reflect.DeepEqual(header.Hash(), expectedHash) {
+ t.Errorf("expected: %v, but got: %v", expectedHash.Hex(), header.Hash().Hex())
+ }
+
+ // append useless information to extra-data
+ unexpectedExtra := append(expectedExtra, []byte{1, 2, 3}...)
+ header.Extra = unexpectedExtra
+ if !reflect.DeepEqual(header.Hash(), rlpHash(header)) {
+ t.Errorf("expected: %v, but got: %v", rlpHash(header).Hex(), header.Hash().Hex())
+ }
+}
+
+func TestExtractToIstanbul(t *testing.T) {
+ testCases := []struct {
+ vanity []byte
+ istRawData []byte
+ expectedResult *IstanbulExtra
+ expectedErr error
+ }{
+ {
+ // normal case
+ bytes.Repeat([]byte{0x00}, IstanbulExtraVanity),
+ hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0"),
+ &IstanbulExtra{
+ Validators: []common.Address{
+ common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")),
+ common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")),
+ common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6")),
+ common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440")),
+ },
+ Seal: []byte{},
+ CommittedSeal: [][]byte{},
+ },
+ nil,
+ },
+ {
+ // insufficient vanity
+ bytes.Repeat([]byte{0x00}, IstanbulExtraVanity-1),
+ nil,
+ nil,
+ ErrInvalidIstanbulHeaderExtra,
+ },
+ }
+ for _, test := range testCases {
+ h := &Header{Extra: append(test.vanity, test.istRawData...)}
+ istanbulExtra, err := ExtractIstanbulExtra(h)
+ if err != test.expectedErr {
+ t.Errorf("expected: %v, but got: %v", test.expectedErr, err)
+ }
+ if !reflect.DeepEqual(istanbulExtra, test.expectedResult) {
+ t.Errorf("expected: %v, but got: %v", test.expectedResult, istanbulExtra)
+ }
+ }
+}