OSDN Git Service

add_signature_for_block_header (#1914)
authorPoseidon <shenao.78@163.com>
Thu, 22 Apr 2021 08:18:25 +0000 (16:18 +0800)
committerGitHub <noreply@github.com>
Thu, 22 Apr 2021 08:18:25 +0000 (16:18 +0800)
* add_signature_for_block_header

* add test for sup_link

* format code

* update test

* format hex string

consensus/general.go
netsync/consensusmgr/consensus_msg_test.go
netsync/messages/chain_msg_test.go
protocol/bc/types/block_header.go
protocol/bc/types/block_header_test.go
protocol/bc/types/block_test.go
protocol/bc/types/sup_link.go [new file with mode: 0644]
protocol/bc/types/sup_link_test.go [new file with mode: 0644]

index c1c1f4c..7e52890 100644 (file)
@@ -15,6 +15,7 @@ const (
        StorageGasRate   = int64(1)
        MaxGasAmount     = int64(200000)
        DefaultGasCredit = int64(30000)
+       NumOfValidators  = int(10)
 
        //config parameter for coinbase reward
        CoinbasePendingBlockNumber = uint64(100)
index 8bbea6d..862837c 100644 (file)
@@ -99,6 +99,7 @@ var testBlock = &types.Block{
                BlockCommitment: types.BlockCommitment{
                        TransactionsMerkleRoot: bc.Hash{V0: uint64(0x11)},
                },
+               SupLinks: types.SupLinks{},
        },
 }
 
index 5dd019a..44ef1d2 100644 (file)
@@ -1,7 +1,6 @@
 package messages
 
 import (
-       "github.com/bytom/bytom/testcontrol"
        "reflect"
        "testing"
 
@@ -10,6 +9,7 @@ import (
        "github.com/bytom/bytom/consensus"
        "github.com/bytom/bytom/protocol/bc"
        "github.com/bytom/bytom/protocol/bc/types"
+       "github.com/bytom/bytom/testcontrol"
 )
 
 var txs = []*types.Tx{
@@ -95,6 +95,7 @@ var testBlock = &types.Block{
                BlockCommitment: types.BlockCommitment{
                        TransactionsMerkleRoot: bc.Hash{V0: uint64(0x11)},
                },
+               SupLinks: types.SupLinks{},
        },
 }
 
@@ -128,6 +129,7 @@ var testHeaders = []*types.BlockHeader{
                BlockCommitment: types.BlockCommitment{
                        TransactionsMerkleRoot: bc.Hash{V0: uint64(0x11)},
                },
+               SupLinks: types.SupLinks{},
        },
        {
                Version:   1,
@@ -136,6 +138,7 @@ var testHeaders = []*types.BlockHeader{
                BlockCommitment: types.BlockCommitment{
                        TransactionsMerkleRoot: bc.Hash{V0: uint64(0x11)},
                },
+               SupLinks: types.SupLinks{},
        },
        {
                Version:   1,
@@ -144,6 +147,7 @@ var testHeaders = []*types.BlockHeader{
                BlockCommitment: types.BlockCommitment{
                        TransactionsMerkleRoot: bc.Hash{V0: uint64(0x11)},
                },
+               SupLinks: types.SupLinks{},
        },
 }
 
index 295030a..37d2990 100644 (file)
@@ -19,6 +19,7 @@ type BlockHeader struct {
        PreviousBlockHash bc.Hash // The hash of the previous block.
        Timestamp         uint64  // The time of the block in seconds.
        BlockWitness
+       SupLinks
        BlockCommitment
 }
 
@@ -94,6 +95,10 @@ func (bh *BlockHeader) readFrom(r *blockchain.Reader) (serflag uint8, err error)
                return 0, err
        }
 
+       if _, err = blockchain.ReadExtensibleString(r, bh.SupLinks.readFrom); err != nil {
+               return 0, err
+       }
+
        return
 }
 
@@ -132,5 +137,9 @@ func (bh *BlockHeader) writeTo(w io.Writer, serflags uint8) (err error) {
                return err
        }
 
+       if _, err = blockchain.WriteExtensibleString(w, nil, bh.SupLinks.writeTo); err != nil {
+               return err
+       }
+
        return
 }
index 29dec58..75fddde 100644 (file)
@@ -35,6 +35,7 @@ func TestBlockHeader(t *testing.T) {
                "20",         // commitment extensible field length
                "ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03",                                                                     // transactions merkle root
                "41403b71527c26bff856a0f247df4fce1b48780f1bd9fd55ba79fb637b0f2e897bb019c5449febf593032dd25b9027cea712c752104700e67d8813326b06d052bf00", // block witness
+               "0100", // supLinks
        }, "")
 
        gotHex := testutil.Serialize(t, blockHeader)
@@ -83,6 +84,7 @@ func TestMarshalBlockHeader(t *testing.T) {
                                "20",         // commitment extensible field length
                                "ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03",                                                                     // transactions merkle root
                                "41409dc8892df991e1d1110a5cb1bbfd57f2f5e3aa89464de50f9555c7575d9c2b21cf8f05b77b880d8ae4dd218efb15b775c32c9d77f9a2955d69dca9020c127709", // block witness
+                               "0100", // supLinks
                        }, ""),
                },
                {
@@ -128,6 +130,7 @@ func TestMarshalBlockHeader(t *testing.T) {
                                "20",         // commitment extensible field length
                                "0000000000000000000000000000000000000000000000000000000000000000",                                                                     // transactions merkle root
                                "4140ac6380a17685c48af4b0a0877d9d61e83b50bd95daa61083dd90031ae66d12d7a371c41cce24887d4d422202b747494bb0e7ca78567d6663be82b27714357407", // block witness
+                               "0100", // supLinks
                        }, ""),
                },
                {
@@ -147,6 +150,7 @@ func TestMarshalBlockHeader(t *testing.T) {
                                "20",         // commitment extensible field length
                                "0000000000000000000000000000000000000000000000000000000000000000", // transactions merkle root
                                "0100", // block witness
+                               "0100", // supLinks
                        }, ""),
                },
        }
@@ -190,6 +194,7 @@ func TestUnmarshalBlockHeader(t *testing.T) {
                                "20",         // commitment extensible field length
                                "ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03",                                                                     // transactions merkle root
                                "41409dc8892df991e1d1110a5cb1bbfd57f2f5e3aa89464de50f9555c7575d9c2b21cf8f05b77b880d8ae4dd218efb15b775c32c9d77f9a2955d69dca9020c127709", // block witness
+                               "0100", // supLinks
                        }, ""),
                        wantBlockHeader: &BlockHeader{
                                Version:           1,
@@ -212,6 +217,7 @@ func TestUnmarshalBlockHeader(t *testing.T) {
                                "20",         // commitment extensible field length
                                "ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03",                                                                     // transactions merkle root
                                "41409dc8892df991e1d1110a5cb1bbfd57f2f5e3aa89464de50f9555c7575d9c2b21cf8f05b77b880d8ae4dd218efb15b775c32c9d77f9a2955d69dca9020c127709", // block witness
+                               "0100", // supLinks
                        }, ""),
                        wantBlockHeader: &BlockHeader{
                                Version:           1,
@@ -258,6 +264,7 @@ func TestUnmarshalBlockHeader(t *testing.T) {
                                "20",         // commitment extensible field length
                                "ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03", // transactions merkle root
                                "0100", // block witness
+                               "0100", // supLinks
                        }, ""),
                        wantError: errors.New("binary: varint overflows a 64-bit integer"),
                },
@@ -271,6 +278,7 @@ func TestUnmarshalBlockHeader(t *testing.T) {
                                "20",         // commitment extensible field length
                                "ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03",                                                                     // transactions merkle root
                                "4140e0776a3cf17b3e0f8340caeee32a75d02ecc25cf20bee9e5c7503bca3b2703c3c61fdcb4211ed59b58eb025ac81e06b138d54b5d01ea4614dd0f65e641836900", // block witness
+                               "0100", // supLinks
                        }, ""),
                        wantBlockHeader: &BlockHeader{
                                Version:           1,
index 58a4030..50a4ed4 100644 (file)
@@ -38,6 +38,7 @@ func TestBlock(t *testing.T) {
                                "20", // commitment extensible field length
                                "0000000000000000000000000000000000000000000000000000000000000000", // transactions merkle root
                                "0100", // block witness
+                               "0100", // sup links
                                "00",   // num transactions
                        }, ""),
                        hash: testutil.MustDecodeHash("42e74d130e5ab27e8a71b90e7de8c8e00ecfa77456070202ab8509f7b0ab49ae"),
@@ -88,6 +89,7 @@ func TestBlock(t *testing.T) {
                                "20",         // commitment extensible field length
                                "ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03", // transactions merkle root
                                "0100", // block witness
+                               "0100", // sup links
                                "02",   // num transactions
                                "07018e0502012a00056e6f6e6365a69849e11add96ac7053aad22ba2349a4abf5feb0475a0afcadff4e128be76cf92c30f380f6173736574446566696e6974696f6e010f69737375616e636550726f6772616d020a617267756d656e7473310a617267756d656e74733201540152fad5195a0c8e3b590b86a3c0a95e7529565888508aecca96e9aeda633002f409ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92c30f03010c7370656e6450726f6772616d17020a617267756d656e7473330a617267756d656e74733401010029a69849e11add96ac7053aad22ba2349a4abf5feb0475a0afcadff4e128be76cf92c30f01047472756500",
                                "07010001010b02096172626974726172790002010029ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92c30f0104747275650001002affffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92c30f010566616c736500",
@@ -135,7 +137,7 @@ func TestReadFrom(t *testing.T) {
                wantBlock Block
        }{
                {
-                       rawBlock: "03018b5f3077f24528e94ecfc4491bb2e9ed6264a632a9a4b86b00c88093ca545d14a137d4f5e1e4052035a2d11158f47a5c5267630b2b6cf9e9a5f79a598085a2572a68defeb8013ad20100020701000101080206003132313731000101003effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809df3b49a010116001437e1aec83a4e6587ca9609e4e5aa728db700744900070100020160015e4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c480c1240201160014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e6302405760b15cc09e543437c4e3aad05bf073e82ebdb214beccb5f4473653dfc0a9d5ae59fb149de19eb71c1c1399594757aeea4dd6327ca2790ef919bd20caa86104201381d35e235813ad1e62f9a602c82abee90565639cc4573568206b55bcd2aed90130000840142084606f20ca7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad1480d0dbc3f402b001467b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d0125ae2054a71277cc162eb3eb21b5bd9fe54402829a53b294deaed91692a2cd8a081f9c5151ad0140621c2c3554da50d2a492d9d78be7c6159359d8f5f0b93a054ce0133617a61d85c532aff449b97a3ec2804ca5fe12b4d54aa6e8c3215c33d04abee9c9abdfdb030201003dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c0d1e123011600144b61da45324299e40dacc255e2ea07dfce3a56d20001003e7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad1480d0dbc3f4020116001437e1aec83a4e6587ca9609e4e5aa728db700744900",
+                       rawBlock: "03018b5f3077f24528e94ecfc4491bb2e9ed6264a632a9a4b86b00c88093ca545d14a137d4f5e1e4052035a2d11158f47a5c5267630b2b6cf9e9a5f79a598085a2572a68defeb8013ad201000100020701000101080206003132313731000101003effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809df3b49a010116001437e1aec83a4e6587ca9609e4e5aa728db700744900070100020160015e4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c480c1240201160014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e6302405760b15cc09e543437c4e3aad05bf073e82ebdb214beccb5f4473653dfc0a9d5ae59fb149de19eb71c1c1399594757aeea4dd6327ca2790ef919bd20caa86104201381d35e235813ad1e62f9a602c82abee90565639cc4573568206b55bcd2aed90130000840142084606f20ca7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad1480d0dbc3f402b001467b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d0125ae2054a71277cc162eb3eb21b5bd9fe54402829a53b294deaed91692a2cd8a081f9c5151ad0140621c2c3554da50d2a492d9d78be7c6159359d8f5f0b93a054ce0133617a61d85c532aff449b97a3ec2804ca5fe12b4d54aa6e8c3215c33d04abee9c9abdfdb030201003dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c0d1e123011600144b61da45324299e40dacc255e2ea07dfce3a56d20001003e7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad1480d0dbc3f4020116001437e1aec83a4e6587ca9609e4e5aa728db700744900",
                        wantBlock: Block{
                                BlockHeader: BlockHeader{
                                        Version:           1,
diff --git a/protocol/bc/types/sup_link.go b/protocol/bc/types/sup_link.go
new file mode 100644 (file)
index 0000000..551f491
--- /dev/null
@@ -0,0 +1,77 @@
+package types
+
+import (
+       "io"
+
+       "github.com/bytom/bytom/consensus"
+       "github.com/bytom/bytom/encoding/blockchain"
+       "github.com/bytom/bytom/protocol/bc"
+)
+
+// SupLinks is alias of SupLink slice
+type SupLinks []*SupLink
+
+func (s *SupLinks) readFrom(r *blockchain.Reader) (err error) {
+       size, err := blockchain.ReadVarint31(r)
+       if err != nil {
+               return err
+       }
+
+       supLinks := make([]*SupLink, size)
+       for i := 0; i < int(size); i++ {
+               supLink := &SupLink{}
+               if err := supLink.readFrom(r); err != nil {
+                       return err
+               }
+
+               supLinks[i] = supLink
+       }
+       *s = supLinks
+       return nil
+}
+
+func (s SupLinks) writeTo(w io.Writer) error {
+       if _, err := blockchain.WriteVarint31(w, uint64(len(s))); err != nil {
+               return err
+       }
+
+       for _, supLink := range s {
+               if err := supLink.writeTo(w); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+// SupLink is an ordered pair of checkpoints (a, b), also written a → b
+// the validators will sign it once considered as legal
+type SupLink struct {
+       SourceHash bc.Hash
+       Signatures [consensus.NumOfValidators][]byte
+}
+
+func (s *SupLink) readFrom(r *blockchain.Reader) (err error) {
+       if _, err := s.SourceHash.ReadFrom(r); err != nil {
+               return err
+       }
+
+       for i := 0; i < consensus.NumOfValidators; i++ {
+               if s.Signatures[i], err = blockchain.ReadVarstr31(r); err != nil {
+                       return err
+               }
+       }
+       return
+}
+
+func (s *SupLink) writeTo(w io.Writer) error {
+       if _, err := s.SourceHash.WriteTo(w); err != nil {
+               return err
+       }
+
+       for _, signature := range s.Signatures {
+               if _, err := blockchain.WriteVarstr31(w, signature); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
diff --git a/protocol/bc/types/sup_link_test.go b/protocol/bc/types/sup_link_test.go
new file mode 100644 (file)
index 0000000..7a6e00f
--- /dev/null
@@ -0,0 +1,153 @@
+package types
+
+import (
+       "bytes"
+       "encoding/hex"
+       "strings"
+       "testing"
+
+       "github.com/bytom/bytom/consensus"
+       "github.com/bytom/bytom/encoding/blockchain"
+       "github.com/bytom/bytom/testutil"
+)
+
+func TestReadWriteSupLink(t *testing.T) {
+       cases := []struct {
+               desc      string
+               supLinks  SupLinks
+               hexString string
+       }{
+               {
+                       desc: "normal sup links",
+                       supLinks: []*SupLink{
+                               {
+                                       SourceHash: testutil.MustDecodeHash("0a3cd1175e295a35c2b63054969c3fe54eeaa3eb68258227b28d8daa6cf4c50c"),
+                                       Signatures: [consensus.NumOfValidators][]byte{
+                                               testutil.MustDecodeHexString("750318156e8c913c378a8d31294fca1084df3be3967035425f470f81e00cd824d1f12bf8e1c3b308f4b1a916438b9ce630722bc8d92ef0feebbbaf987dd7a60e"),
+                                               testutil.MustDecodeHexString("be7c7e0ba54109c8c457cdbba4691d7aaae32eb4b8ac63755f2494be406027ce66c7b4730bfd2506fa2caaba12a7bbbea2faca5f07bb64fe06a568b6415e7506"),
+                                       },
+                               },
+                               {
+                                       SourceHash: testutil.MustDecodeHash("546c91cefc6a06f9b7a0aaa4d69db9a7f229af27928304a44ecd48e33ba2ba91"),
+                                       Signatures: [consensus.NumOfValidators][]byte{
+                                               testutil.MustDecodeHexString("38c9a6a48eeea993b2d4137e73b17e4743ce3935636fcce957ae2291c691491525f39509a1c21fec3c7f78403ae88e375b796fa9dcc4cac0af8a987994f62c07"),
+                                               testutil.MustDecodeHexString("4fe5646b2b669aaef0dd74a090e150de676218d0a6e693bb2d1cc791282517669d7903c60a909a5d9c5a996e5797ea9dded20b52dc4b8ec272e86e5fc4e8a008"),
+                                               nil,
+                                               nil,
+                                               testutil.MustDecodeHexString("4dd9508652a686b37247d2fa969ca92997095cec44aa2ceb223daf29c1c426f5e06d3e522e85161386ad70af2c04e703179e749870f6e669b0038067338fe709"),
+                                               testutil.MustDecodeHexString("3ab09481823ee2caff6939ea0e70693d63173c4295975be6bbf030554941de2babfb66fc3c795f026785fdf2f5090617f05292816d0ccb83f8d2dc487e3ad404"),
+                                               nil,
+                                               testutil.MustDecodeHexString("9e48c3852c16189dd82b48c43de6460771802caab373dc8e572c0e510edcc6341e7b070dec6a2068d2519e044eaadc609ae6c3233cdcbb713ef0546edfa2f803"),
+                                               testutil.MustDecodeHexString("b26b8f5fb33b800b8d06768304864138b0ece5ce7e57fcc339f714911d279d103b08a5f8a85c1723dfe0299690ad776fb8b11e003ddfc33749b5000d0a78350f"),
+                                       },
+                               },
+                       },
+                       hexString: strings.Join([]string{
+                               "02", // len of sup links,
+                               "0a3cd1175e295a35c2b63054969c3fe54eeaa3eb68258227b28d8daa6cf4c50c", // source hash 1
+                               "40", // len of signature 1
+                               "750318156e8c913c378a8d31294fca1084df3be3967035425f470f81e00cd824d1f12bf8e1c3b308f4b1a916438b9ce630722bc8d92ef0feebbbaf987dd7a60e", // signature 1
+                               "40", // len of signature 2
+                               "be7c7e0ba54109c8c457cdbba4691d7aaae32eb4b8ac63755f2494be406027ce66c7b4730bfd2506fa2caaba12a7bbbea2faca5f07bb64fe06a568b6415e7506", // signature 2
+                               "00", // len of signature 3
+                               "00", // len of signature 4
+                               "00", // len of signature 5
+                               "00", // len of signature 6
+                               "00", // len of signature 7
+                               "00", // len of signature 8
+                               "00", // len of signature 9
+                               "00", // len of signature 10
+                               "546c91cefc6a06f9b7a0aaa4d69db9a7f229af27928304a44ecd48e33ba2ba91", // source hash 2
+                               "40", // len of signature 1
+                               "38c9a6a48eeea993b2d4137e73b17e4743ce3935636fcce957ae2291c691491525f39509a1c21fec3c7f78403ae88e375b796fa9dcc4cac0af8a987994f62c07", // signature 1
+                               "40",  // len of signature 2
+                               "4fe5646b2b669aaef0dd74a090e150de676218d0a6e693bb2d1cc791282517669d7903c60a909a5d9c5a996e5797ea9dded20b52dc4b8ec272e86e5fc4e8a008", // signature 2
+                               "00", // len of signature 3
+                               "00", // len of signature 4
+                               "40", // len of signature 5
+                               "4dd9508652a686b37247d2fa969ca92997095cec44aa2ceb223daf29c1c426f5e06d3e522e85161386ad70af2c04e703179e749870f6e669b0038067338fe709", // signature 1
+                               "40",  // len of signature 6
+                               "3ab09481823ee2caff6939ea0e70693d63173c4295975be6bbf030554941de2babfb66fc3c795f026785fdf2f5090617f05292816d0ccb83f8d2dc487e3ad404", // signature 2
+                               "00", // len of signature 7
+                               "40", // len of signature 8
+                               "9e48c3852c16189dd82b48c43de6460771802caab373dc8e572c0e510edcc6341e7b070dec6a2068d2519e044eaadc609ae6c3233cdcbb713ef0546edfa2f803", // signature 1
+                               "40",  // len of signature 9
+                               "b26b8f5fb33b800b8d06768304864138b0ece5ce7e57fcc339f714911d279d103b08a5f8a85c1723dfe0299690ad776fb8b11e003ddfc33749b5000d0a78350f", // signature 2
+                               "00", // len of signature 10
+                       }, ""),
+               },
+               {
+                       desc: "sup links with full signature",
+                       supLinks: []*SupLink{
+                               {
+                                       SourceHash: testutil.MustDecodeHash("0a3cd1175e295a35c2b63054969c3fe54eeaa3eb68258227b28d8daa6cf4c50c"),
+                                       Signatures: [consensus.NumOfValidators][]byte{
+                                               testutil.MustDecodeHexString("750318156e8c913c378a8d31294fca1084df3be3967035425f470f81e00cd824d1f12bf8e1c3b308f4b1a916438b9ce630722bc8d92ef0feebbbaf987dd7a60e"),
+                                               testutil.MustDecodeHexString("be7c7e0ba54109c8c457cdbba4691d7aaae32eb4b8ac63755f2494be406027ce66c7b4730bfd2506fa2caaba12a7bbbea2faca5f07bb64fe06a568b6415e7506"),
+                                               testutil.MustDecodeHexString("9938ea16d6caae68b7e9318f1aed387ef9767dc0d80db807e0d0a77065229ceffef7a8b6407882f5d6e29b2edf1c6373bb1c47188138068e2baa4851c04c6f0e"),
+                                               testutil.MustDecodeHexString("4dd9508652a686b37247d2fa969ca92997095cec44aa2ceb223daf29c1c426f5e06d3e522e85161386ad70af2c04e703179e749870f6e669b0038067338fe709"),
+                                               testutil.MustDecodeHexString("3ab09481823ee2caff6939ea0e70693d63173c4295975be6bbf030554941de2babfb66fc3c795f026785fdf2f5090617f05292816d0ccb83f8d2dc487e3ad404"),
+                                               testutil.MustDecodeHexString("52a13c4502265fb456f8ecd051de7b6059b5ad59a741ed561efc06489f161b0d471d86f3bf62ef0083e603a26b98abc945018b8f94f591782d43deb5df1dec08"),
+                                               testutil.MustDecodeHexString("9e48c3852c16189dd82b48c43de6460771802caab373dc8e572c0e510edcc6341e7b070dec6a2068d2519e044eaadc609ae6c3233cdcbb713ef0546edfa2f803"),
+                                               testutil.MustDecodeHexString("4103f5e7939f1e83241580251a56d85f31cedbca0be7ea819e352ab61aebdb047419e2775704539af4897bdd65e0cf69dc7e82b9e338efe88b5e7eb911dd8303"),
+                                               testutil.MustDecodeHexString("b26b8f5fb33b800b8d06768304864138b0ece5ce7e57fcc339f714911d279d103b08a5f8a85c1723dfe0299690ad776fb8b11e003ddfc33749b5000d0a78350f"),
+                                               testutil.MustDecodeHexString("30a9b6922a04ad7e72310842d589da14edfc3a81d60e3d6d934bd4adff4c3bb78a8506fcbe1323a21d2058a294c4af7a5a961e4e033380e2ed150ef0dcfbcb00"),
+                                       },
+                               },
+                       },
+                       hexString: strings.Join([]string{
+                               "01", // len of sup links,
+                               "0a3cd1175e295a35c2b63054969c3fe54eeaa3eb68258227b28d8daa6cf4c50c", // source hash
+                               "40", // len of signature 1
+                               "750318156e8c913c378a8d31294fca1084df3be3967035425f470f81e00cd824d1f12bf8e1c3b308f4b1a916438b9ce630722bc8d92ef0feebbbaf987dd7a60e", // signature 1
+                               "40", // len of signature 2
+                               "be7c7e0ba54109c8c457cdbba4691d7aaae32eb4b8ac63755f2494be406027ce66c7b4730bfd2506fa2caaba12a7bbbea2faca5f07bb64fe06a568b6415e7506", // signature 2
+                               "40", // len of signature 3
+                               "9938ea16d6caae68b7e9318f1aed387ef9767dc0d80db807e0d0a77065229ceffef7a8b6407882f5d6e29b2edf1c6373bb1c47188138068e2baa4851c04c6f0e", // signature 1
+                               "40", // len of signature 4
+                               "4dd9508652a686b37247d2fa969ca92997095cec44aa2ceb223daf29c1c426f5e06d3e522e85161386ad70af2c04e703179e749870f6e669b0038067338fe709", // signature 2
+                               "40", // len of signature 5
+                               "3ab09481823ee2caff6939ea0e70693d63173c4295975be6bbf030554941de2babfb66fc3c795f026785fdf2f5090617f05292816d0ccb83f8d2dc487e3ad404", // signature 1
+                               "40", // len of signature 6
+                               "52a13c4502265fb456f8ecd051de7b6059b5ad59a741ed561efc06489f161b0d471d86f3bf62ef0083e603a26b98abc945018b8f94f591782d43deb5df1dec08", // signature 2
+                               "40", // len of signature 7
+                               "9e48c3852c16189dd82b48c43de6460771802caab373dc8e572c0e510edcc6341e7b070dec6a2068d2519e044eaadc609ae6c3233cdcbb713ef0546edfa2f803", // signature 1
+                               "40", // len of signature 8
+                               "4103f5e7939f1e83241580251a56d85f31cedbca0be7ea819e352ab61aebdb047419e2775704539af4897bdd65e0cf69dc7e82b9e338efe88b5e7eb911dd8303", // signature 2
+                               "40", // len of signature 9
+                               "b26b8f5fb33b800b8d06768304864138b0ece5ce7e57fcc339f714911d279d103b08a5f8a85c1723dfe0299690ad776fb8b11e003ddfc33749b5000d0a78350f", // signature 1
+                               "40", // len of signature 10
+                               "30a9b6922a04ad7e72310842d589da14edfc3a81d60e3d6d934bd4adff4c3bb78a8506fcbe1323a21d2058a294c4af7a5a961e4e033380e2ed150ef0dcfbcb00", // signature 2
+                       }, ""),
+               },
+               {
+                       desc:      "empty sup links",
+                       supLinks:  []*SupLink{},
+                       hexString: "00",
+               },
+       }
+
+       for _, c := range cases {
+               t.Run(c.desc, func(t *testing.T) {
+                       buff := []byte{}
+                       buffer := bytes.NewBuffer(buff)
+                       if err := c.supLinks.writeTo(buffer); err != nil {
+                               t.Fatal(err)
+                       }
+
+                       hexString := hex.EncodeToString(buffer.Bytes())
+                       if hexString != c.hexString {
+                               t.Errorf("test write suplinks fail, got:%s, want:%s", hexString, c.hexString)
+                       }
+
+                       supLinks := SupLinks{}
+                       if err := supLinks.readFrom(blockchain.NewReader(buffer.Bytes())); err != nil {
+                               t.Fatal(err)
+                       }
+
+                       if !testutil.DeepEqual(supLinks, c.supLinks) {
+                               t.Errorf("test read suplinks fail, got:%v, want:%v", supLinks, c.supLinks)
+                       }
+               })
+       }
+}