Golang implemented sidechain for Bytom
修订版 | b8c0ca35eceda6f29802712c97eae34ab35f3e55 (tree) |
---|---|
时间 | 2020-04-02 20:59:30 |
作者 | Paladz <yzhu101@uott...> |
Commiter | GitHub |
Merge branch 'master' into key_alias
@@ -12,8 +12,12 @@ import ( | ||
12 | 12 | "github.com/bytom/vapor/protocol/bc/types" |
13 | 13 | ) |
14 | 14 | |
15 | +// ErrNotInitDBState represent the database state of mov store is not initialized | |
16 | +var ErrNotInitDBState = errors.New("database state of mov store is not initialized") | |
17 | + | |
15 | 18 | // MovStore is the interface for mov's persistent storage |
16 | 19 | type MovStore interface { |
20 | + Clear() | |
17 | 21 | GetMovDatabaseState() (*common.MovDatabaseState, error) |
18 | 22 | InitDBState(height uint64, hash *bc.Hash) error |
19 | 23 | ListOrders(orderAfter *common.Order) ([]*common.Order, error) |
@@ -89,6 +93,19 @@ func NewLevelDBMovStore(db dbm.DB) *LevelDBMovStore { | ||
89 | 93 | return &LevelDBMovStore{db: db} |
90 | 94 | } |
91 | 95 | |
96 | +// Clear will clear all the data of store | |
97 | +func (m *LevelDBMovStore) Clear() { | |
98 | + batch := m.db.NewBatch() | |
99 | + | |
100 | + iter := m.db.Iterator() | |
101 | + defer iter.Release() | |
102 | + | |
103 | + for iter.Next() { | |
104 | + batch.Delete(iter.Key()) | |
105 | + } | |
106 | + batch.Write() | |
107 | +} | |
108 | + | |
92 | 109 | // GetMovDatabaseState return the current DB's image status |
93 | 110 | func (m *LevelDBMovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) { |
94 | 111 | if value := m.db.Get(bestMatchStore); value != nil { |
@@ -96,7 +113,7 @@ func (m *LevelDBMovStore) GetMovDatabaseState() (*common.MovDatabaseState, error | ||
96 | 113 | return state, json.Unmarshal(value, state) |
97 | 114 | } |
98 | 115 | |
99 | - return nil, errors.New("don't find state of mov-database") | |
116 | + return nil, ErrNotInitDBState | |
100 | 117 | } |
101 | 118 | |
102 | 119 | // InitDBState set the DB's image status |
@@ -11,11 +11,13 @@ import ( | ||
11 | 11 | "github.com/bytom/vapor/consensus/segwit" |
12 | 12 | dbm "github.com/bytom/vapor/database/leveldb" |
13 | 13 | "github.com/bytom/vapor/errors" |
14 | + "github.com/bytom/vapor/protocol" | |
14 | 15 | "github.com/bytom/vapor/protocol/bc" |
15 | 16 | "github.com/bytom/vapor/protocol/bc/types" |
16 | 17 | ) |
17 | 18 | |
18 | 19 | var ( |
20 | + errChainStatusHasAlreadyInit = errors.New("mov chain status has already initialized") | |
19 | 21 | errInvalidTradePairs = errors.New("The trade pairs in the tx input is invalid") |
20 | 22 | errStatusFailMustFalse = errors.New("status fail of transaction does not allow to be true") |
21 | 23 | errInputProgramMustP2WMCScript = errors.New("input program of trade tx must p2wmc script") |
@@ -57,7 +59,7 @@ func (m *Core) ApplyBlock(block *types.Block) error { | ||
57 | 59 | |
58 | 60 | if block.Height == m.startBlockHeight { |
59 | 61 | blockHash := block.Hash() |
60 | - return m.movStore.InitDBState(block.Height, &blockHash) | |
62 | + return m.InitChainStatus(&blockHash) | |
61 | 63 | } |
62 | 64 | |
63 | 65 | if err := m.validateMatchedTxSequence(block.Transactions); err != nil { |
@@ -98,6 +100,10 @@ func (m *Core) BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft | ||
98 | 100 | // ChainStatus return the current block height and block hash in dex core |
99 | 101 | func (m *Core) ChainStatus() (uint64, *bc.Hash, error) { |
100 | 102 | state, err := m.movStore.GetMovDatabaseState() |
103 | + if err == database.ErrNotInitDBState { | |
104 | + return 0, nil, protocol.ErrNotInitSubProtocolChainStatus | |
105 | + } | |
106 | + | |
101 | 107 | if err != nil { |
102 | 108 | return 0, nil, err |
103 | 109 | } |
@@ -112,6 +118,11 @@ func (m *Core) DetachBlock(block *types.Block) error { | ||
112 | 118 | return nil |
113 | 119 | } |
114 | 120 | |
121 | + if block.Height == m.startBlockHeight { | |
122 | + m.movStore.Clear() | |
123 | + return nil | |
124 | + } | |
125 | + | |
115 | 126 | deleteOrders, addOrders, err := decodeTxsOrders(block.Transactions) |
116 | 127 | if err != nil { |
117 | 128 | return err |
@@ -120,6 +131,15 @@ func (m *Core) DetachBlock(block *types.Block) error { | ||
120 | 131 | return m.movStore.ProcessOrders(addOrders, deleteOrders, &block.BlockHeader) |
121 | 132 | } |
122 | 133 | |
134 | +// InitChainStatus used to init the start block height and start block hash to store | |
135 | +func (m *Core) InitChainStatus(startHash *bc.Hash) error { | |
136 | + if _, err := m.movStore.GetMovDatabaseState(); err == nil { | |
137 | + return errChainStatusHasAlreadyInit | |
138 | + } | |
139 | + | |
140 | + return m.movStore.InitDBState(m.startBlockHeight, startHash) | |
141 | +} | |
142 | + | |
123 | 143 | // IsDust block the transaction that are not generated by the match engine |
124 | 144 | func (m *Core) IsDust(tx *types.Tx) bool { |
125 | 145 | for _, input := range tx.Inputs { |
@@ -88,7 +88,7 @@ func NewNode(config *cfg.Config) *Node { | ||
88 | 88 | movCore := mov.NewCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight) |
89 | 89 | assetFilter := protocol.NewAssetFilter(config.CrossChain.AssetWhitelist) |
90 | 90 | txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore, assetFilter}, dispatcher) |
91 | - chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher) | |
91 | + chain, err := protocol.NewChain(store, txPool, []protocol.SubProtocol{movCore}, dispatcher) | |
92 | 92 | if err != nil { |
93 | 93 | cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err)) |
94 | 94 | } |
@@ -180,7 +180,7 @@ func Rollback(config *cfg.Config, targetHeight uint64) error { | ||
180 | 180 | dispatcher := event.NewDispatcher() |
181 | 181 | movCore := mov.NewCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight) |
182 | 182 | txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore}, dispatcher) |
183 | - chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher) | |
183 | + chain, err := protocol.NewChain(store, txPool, []protocol.SubProtocol{movCore}, dispatcher) | |
184 | 184 | if err != nil { |
185 | 185 | return err |
186 | 186 | } |
@@ -334,7 +334,7 @@ func (b *blockBuilder) preValidateTxs(txs []*types.Tx, chain *protocol.Chain, vi | ||
334 | 334 | return results, gasLeft |
335 | 335 | } |
336 | 336 | |
337 | -func (b *blockBuilder) validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.Protocoler) error { | |
337 | +func (b *blockBuilder) validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.SubProtocol) error { | |
338 | 338 | for _, subProtocol := range subProtocols { |
339 | 339 | verifyResult := &bc.TxVerifyResult{StatusFail: statusFail} |
340 | 340 | if err := subProtocol.ValidateTx(tx, verifyResult, b.block.Height); err != nil { |
@@ -19,12 +19,19 @@ const ( | ||
19 | 19 | maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS) |
20 | 20 | ) |
21 | 21 | |
22 | -// Protocoler is interface for layer 2 consensus protocol | |
23 | -type Protocoler interface { | |
22 | +// ErrNotInitSubProtocolChainStatus represent the node state of sub protocol has not been initialized | |
23 | +var ErrNotInitSubProtocolChainStatus = errors.New("node state of sub protocol has not been initialized") | |
24 | + | |
25 | +// SubProtocol is interface for layer 2 consensus protocol | |
26 | +type SubProtocol interface { | |
24 | 27 | Name() string |
25 | 28 | StartHeight() uint64 |
26 | 29 | BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error) |
30 | + | |
31 | + // ChainStatus return the the current block height and block hash of sub protocol. | |
32 | + // it will return ErrNotInitSubProtocolChainStatus if not initialized. | |
27 | 33 | ChainStatus() (uint64, *bc.Hash, error) |
34 | + InitChainStatus(*bc.Hash) error | |
28 | 35 | ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error |
29 | 36 | ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error |
30 | 37 | ApplyBlock(block *types.Block) error |
@@ -37,7 +44,7 @@ type Chain struct { | ||
37 | 44 | txPool *TxPool |
38 | 45 | store Store |
39 | 46 | processBlockCh chan *processBlockMsg |
40 | - subProtocols []Protocoler | |
47 | + subProtocols []SubProtocol | |
41 | 48 | |
42 | 49 | signatureCache *common.Cache |
43 | 50 | eventDispatcher *event.Dispatcher |
@@ -50,7 +57,7 @@ type Chain struct { | ||
50 | 57 | } |
51 | 58 | |
52 | 59 | // NewChain returns a new Chain using store as the underlying storage. |
53 | -func NewChain(store Store, txPool *TxPool, subProtocols []Protocoler, eventDispatcher *event.Dispatcher) (*Chain, error) { | |
60 | +func NewChain(store Store, txPool *TxPool, subProtocols []SubProtocol, eventDispatcher *event.Dispatcher) (*Chain, error) { | |
54 | 61 | knownTxs, _ := common.NewOrderedSet(maxKnownTxs) |
55 | 62 | c := &Chain{ |
56 | 63 | orphanManage: NewOrphanManage(), |
@@ -175,7 +182,7 @@ func (c *Chain) InMainChain(hash bc.Hash) bool { | ||
175 | 182 | } |
176 | 183 | |
177 | 184 | // SubProtocols return list of layer 2 consensus protocol |
178 | -func (c *Chain) SubProtocols() []Protocoler { | |
185 | +func (c *Chain) SubProtocols() []SubProtocol { | |
179 | 186 | return c.subProtocols |
180 | 187 | } |
181 | 188 |
@@ -215,14 +222,25 @@ func (c *Chain) markTransactions(txs ...*types.Tx) { | ||
215 | 222 | } |
216 | 223 | } |
217 | 224 | |
218 | -func (c *Chain) syncProtocolStatus(subProtocol Protocoler) error { | |
225 | +func (c *Chain) syncProtocolStatus(subProtocol SubProtocol) error { | |
219 | 226 | if c.bestBlockHeader.Height < subProtocol.StartHeight() { |
220 | 227 | return nil |
221 | 228 | } |
222 | 229 | |
223 | 230 | protocolHeight, protocolHash, err := subProtocol.ChainStatus() |
224 | - if err != nil { | |
225 | - return errors.Wrap(err, "failed on get sub protocol status") | |
231 | + if err == ErrNotInitSubProtocolChainStatus { | |
232 | + startHash, err := c.store.GetMainChainHash(subProtocol.StartHeight()) | |
233 | + if err != nil { | |
234 | + return errors.Wrap(err, subProtocol.Name(), "can't get block hash by height") | |
235 | + } | |
236 | + | |
237 | + if err := subProtocol.InitChainStatus(startHash); err != nil { | |
238 | + return errors.Wrap(err, subProtocol.Name(), "fail init chain status") | |
239 | + } | |
240 | + | |
241 | + protocolHeight, protocolHash = subProtocol.StartHeight(), startHash | |
242 | + } else if err != nil { | |
243 | + return errors.Wrap(err, subProtocol.Name(), "can't get chain status") | |
226 | 244 | } |
227 | 245 | |
228 | 246 | if *protocolHash == c.bestBlockHeader.Hash() { |
@@ -1460,7 +1460,7 @@ func TestRollback(t *testing.T) { | ||
1460 | 1460 | t.Fatal(err) |
1461 | 1461 | } |
1462 | 1462 | |
1463 | - chain, err := protocol.NewChain(store, nil, []protocol.Protocoler{movCore}, nil) | |
1463 | + chain, err := protocol.NewChain(store, nil, []protocol.SubProtocol{movCore}, nil) | |
1464 | 1464 | if err != nil { |
1465 | 1465 | t.Fatal(err) |
1466 | 1466 | } |
@@ -0,0 +1,235 @@ | ||
1 | +package test | |
2 | + | |
3 | +import ( | |
4 | + "os" | |
5 | + "testing" | |
6 | + | |
7 | + "github.com/bytom/vapor/application/mov" | |
8 | + movDatabase "github.com/bytom/vapor/application/mov/database" | |
9 | + "github.com/bytom/vapor/database" | |
10 | + dbm "github.com/bytom/vapor/database/leveldb" | |
11 | + "github.com/bytom/vapor/protocol" | |
12 | + "github.com/bytom/vapor/protocol/bc" | |
13 | + "github.com/bytom/vapor/protocol/bc/types" | |
14 | + "github.com/bytom/vapor/protocol/state" | |
15 | + "github.com/bytom/vapor/testutil" | |
16 | +) | |
17 | + | |
18 | +type chainStatus struct { | |
19 | + blockHeight uint64 | |
20 | + blockHash bc.Hash | |
21 | +} | |
22 | + | |
23 | +type chainBlock struct { | |
24 | + block *types.Block | |
25 | + inMainChain bool | |
26 | +} | |
27 | + | |
28 | +var blocks = map[uint64][]*types.Block{ | |
29 | + 0: { | |
30 | + { | |
31 | + BlockHeader: types.BlockHeader{ | |
32 | + Height: 0, | |
33 | + Timestamp: 1585814309, | |
34 | + PreviousBlockHash: bc.Hash{}, | |
35 | + }, | |
36 | + }, | |
37 | + }, | |
38 | + 1: { | |
39 | + // prev block is [0][0] | |
40 | + { | |
41 | + BlockHeader: types.BlockHeader{ | |
42 | + Height: 1, | |
43 | + Timestamp: 1585814310, | |
44 | + PreviousBlockHash: testutil.MustDecodeHash("2e5406c82fe34f6ee44fe694b05ffc8fb5918a026415b086df03fb03760b42a9"), | |
45 | + }, | |
46 | + }, | |
47 | + // prev block is [0][0] | |
48 | + { | |
49 | + BlockHeader: types.BlockHeader{ | |
50 | + Height: 1, | |
51 | + Timestamp: 1585814311, | |
52 | + PreviousBlockHash: testutil.MustDecodeHash("2e5406c82fe34f6ee44fe694b05ffc8fb5918a026415b086df03fb03760b42a9"), | |
53 | + }, | |
54 | + }, | |
55 | + }, | |
56 | + 2: { | |
57 | + // prev block is [1][0] | |
58 | + { | |
59 | + BlockHeader: types.BlockHeader{ | |
60 | + Height: 2, | |
61 | + Timestamp: 1585814320, | |
62 | + PreviousBlockHash: testutil.MustDecodeHash("5bc198f4c0198e7e8b52173a82836cfd3f124d88bf052f53390948d845bf6fe0"), | |
63 | + }, | |
64 | + }, | |
65 | + }, | |
66 | +} | |
67 | + | |
68 | +func TestSyncProtocolStatus(t *testing.T) { | |
69 | + cases := []struct { | |
70 | + desc string | |
71 | + savedBlocks []*chainBlock | |
72 | + startHeight uint64 | |
73 | + startHash *bc.Hash | |
74 | + wantChainStatus *chainStatus | |
75 | + }{ | |
76 | + { | |
77 | + desc: "start height from 0, mov is not init", | |
78 | + savedBlocks: []*chainBlock{ | |
79 | + { | |
80 | + block: blocks[0][0], | |
81 | + inMainChain: true, | |
82 | + }, | |
83 | + { | |
84 | + block: blocks[1][0], | |
85 | + inMainChain: true, | |
86 | + }, | |
87 | + { | |
88 | + block: blocks[2][0], | |
89 | + inMainChain: true, | |
90 | + }, | |
91 | + }, | |
92 | + startHeight: 0, | |
93 | + wantChainStatus: &chainStatus{ | |
94 | + blockHeight: 2, | |
95 | + blockHash: blocks[2][0].Hash(), | |
96 | + }, | |
97 | + }, | |
98 | + { | |
99 | + desc: "start height from 1, mov is not init", | |
100 | + savedBlocks: []*chainBlock{ | |
101 | + { | |
102 | + block: blocks[0][0], | |
103 | + inMainChain: true, | |
104 | + }, | |
105 | + { | |
106 | + block: blocks[1][0], | |
107 | + inMainChain: true, | |
108 | + }, | |
109 | + { | |
110 | + block: blocks[2][0], | |
111 | + inMainChain: true, | |
112 | + }, | |
113 | + }, | |
114 | + startHeight: 1, | |
115 | + wantChainStatus: &chainStatus{ | |
116 | + blockHeight: 2, | |
117 | + blockHash: blocks[2][0].Hash(), | |
118 | + }, | |
119 | + }, | |
120 | + { | |
121 | + desc: "start height from 1, state of mov is not sync completed", | |
122 | + savedBlocks: []*chainBlock{ | |
123 | + { | |
124 | + block: blocks[0][0], | |
125 | + inMainChain: true, | |
126 | + }, | |
127 | + { | |
128 | + block: blocks[1][0], | |
129 | + inMainChain: true, | |
130 | + }, | |
131 | + { | |
132 | + block: blocks[2][0], | |
133 | + inMainChain: true, | |
134 | + }, | |
135 | + }, | |
136 | + startHeight: 1, | |
137 | + startHash: hashPtr(blocks[1][0].Hash()), | |
138 | + wantChainStatus: &chainStatus{ | |
139 | + blockHeight: 2, | |
140 | + blockHash: blocks[2][0].Hash(), | |
141 | + }, | |
142 | + }, | |
143 | + { | |
144 | + desc: "chain status of mov is forked", | |
145 | + savedBlocks: []*chainBlock{ | |
146 | + { | |
147 | + block: blocks[0][0], | |
148 | + inMainChain: true, | |
149 | + }, | |
150 | + { | |
151 | + block: blocks[1][0], | |
152 | + inMainChain: true, | |
153 | + }, | |
154 | + { | |
155 | + block: blocks[1][1], | |
156 | + inMainChain: false, | |
157 | + }, | |
158 | + { | |
159 | + block: blocks[2][0], | |
160 | + inMainChain: true, | |
161 | + }, | |
162 | + }, | |
163 | + startHeight: 1, | |
164 | + startHash: hashPtr(blocks[1][1].Hash()), | |
165 | + wantChainStatus: &chainStatus{ | |
166 | + blockHeight: 2, | |
167 | + blockHash: blocks[2][0].Hash(), | |
168 | + }, | |
169 | + }, | |
170 | + } | |
171 | + | |
172 | + defer os.RemoveAll("temp") | |
173 | + | |
174 | + for i, c := range cases { | |
175 | + chainDB := dbm.NewDB("core", "leveldb", "temp") | |
176 | + store := database.NewStore(chainDB) | |
177 | + if err := initStore(store, c.savedBlocks); err != nil { | |
178 | + t.Fatal(err) | |
179 | + } | |
180 | + | |
181 | + movDB := dbm.NewDB("mov", "leveldb", "temp") | |
182 | + movCore := mov.NewCoreWithDB(movDatabase.NewLevelDBMovStore(movDB), c.startHeight) | |
183 | + if c.startHash != nil { | |
184 | + if err := movCore.InitChainStatus(c.startHash); err != nil { | |
185 | + t.Fatal(err) | |
186 | + } | |
187 | + } | |
188 | + | |
189 | + _, err := protocol.NewChain(store, nil, []protocol.SubProtocol{movCore}, nil) | |
190 | + if err != nil { | |
191 | + t.Fatal(err) | |
192 | + } | |
193 | + | |
194 | + gotHeight, gotHash, err := movCore.ChainStatus() | |
195 | + if err != nil { | |
196 | + t.Fatal(err) | |
197 | + } | |
198 | + | |
199 | + if gotHeight != c.wantChainStatus.blockHeight || *gotHash != c.wantChainStatus.blockHash { | |
200 | + t.Logf("#%d(%s): got chain status of sub protocol is not equals want chain status", i, c.desc) | |
201 | + } | |
202 | + | |
203 | + movDB.Close() | |
204 | + chainDB.Close() | |
205 | + os.RemoveAll("temp") | |
206 | + } | |
207 | +} | |
208 | + | |
209 | +func initStore(store *database.Store, savedBlocks []*chainBlock) error { | |
210 | + var mainBlockHeaders []*types.BlockHeader | |
211 | + for _, block := range savedBlocks { | |
212 | + if err := store.SaveBlock(block.block, bc.NewTransactionStatus()); err != nil { | |
213 | + return err | |
214 | + } | |
215 | + | |
216 | + if block.inMainChain { | |
217 | + mainBlockHeaders = append(mainBlockHeaders, &block.block.BlockHeader) | |
218 | + } | |
219 | + | |
220 | + last := len(mainBlockHeaders) - 1 | |
221 | + if err := store.SaveChainStatus(mainBlockHeaders[last], mainBlockHeaders[last], mainBlockHeaders, state.NewUtxoViewpoint(), nil); err != nil { | |
222 | + return err | |
223 | + } | |
224 | + } | |
225 | + return nil | |
226 | +} | |
227 | + | |
228 | +func hashPtr(hash bc.Hash) *bc.Hash { | |
229 | + return &hash | |
230 | +} | |
231 | + | |
232 | +func TestBlockHash(t *testing.T) { | |
233 | + blockHash := blocks[1][0].Hash() | |
234 | + t.Log(blockHash.String()) | |
235 | +} |