Golang implemented sidechain for Bytom
修订版 | 01eeb5341fd4d099389ee6186f95069a81ccb6f6 (tree) |
---|---|
时间 | 2020-02-23 13:00:51 |
作者 | Paladz <yzhu101@uott...> |
Commiter | GitHub |
Merge branch 'mov' into fix_add_order_of_order_book
@@ -53,6 +53,9 @@ func (m *MovCore) ApplyBlock(block *types.Block) error { | ||
53 | 53 | if err := m.movStore.InitDBState(block.Height, &blockHash); err != nil { |
54 | 54 | return err |
55 | 55 | } |
56 | + | |
57 | + // the next block can send orders | |
58 | + return nil | |
56 | 59 | } |
57 | 60 | |
58 | 61 | if err := m.validateMatchedTxSequence(block.Transactions); err != nil { |
@@ -0,0 +1,38 @@ | ||
1 | +package commands | |
2 | + | |
3 | +import ( | |
4 | + "strconv" | |
5 | + | |
6 | + log "github.com/sirupsen/logrus" | |
7 | + "github.com/spf13/cobra" | |
8 | + | |
9 | + "github.com/bytom/vapor/node" | |
10 | +) | |
11 | + | |
12 | +var rollbackCmd = &cobra.Command{ | |
13 | + Use: "rollback", | |
14 | + Short: "Rollback chain to target height!", | |
15 | + Args: cobra.ExactArgs(1), | |
16 | + Run: func(cmd *cobra.Command, args []string) { | |
17 | + setLogLevel(config.LogLevel) | |
18 | + | |
19 | + height, err := strconv.ParseInt(args[0], 10, 64) | |
20 | + if err != nil { | |
21 | + log.WithFields(log.Fields{"module": logModule, "err": err}).Fatal("failed to parse int") | |
22 | + } | |
23 | + | |
24 | + if height < 0 { | |
25 | + log.WithFields(log.Fields{"module": logModule}).Fatal("height should >= 0") | |
26 | + } | |
27 | + | |
28 | + if err = node.Rollback(config, uint64(height)); err != nil { | |
29 | + log.WithFields(log.Fields{"module": logModule, "err": err}).Fatal("failed to rollback") | |
30 | + } | |
31 | + | |
32 | + log.WithFields(log.Fields{"module": logModule}).Infof("success to rollback height of %d", height) | |
33 | + }, | |
34 | +} | |
35 | + | |
36 | +func init() { | |
37 | + RootCmd.AddCommand(rollbackCmd) | |
38 | +} |
@@ -156,6 +156,18 @@ func GetConsensusResult(db dbm.DB, seq uint64) (*state.ConsensusResult, error) { | ||
156 | 156 | return consensusResult, nil |
157 | 157 | } |
158 | 158 | |
159 | +// DeleteConsensusResult delete a consensusResult from cache and database | |
160 | +func (s *Store) DeleteConsensusResult(seq uint64) error { | |
161 | + consensusResult, err := GetConsensusResult(s.db, seq) | |
162 | + if err != nil { | |
163 | + return err | |
164 | + } | |
165 | + | |
166 | + s.db.Delete(calcConsensusResultKey(seq)) | |
167 | + s.cache.removeConsensusResult(consensusResult) | |
168 | + return nil | |
169 | +} | |
170 | + | |
159 | 171 | // DeleteBlock delete a new block in the protocol. |
160 | 172 | func (s *Store) DeleteBlock(block *types.Block) error { |
161 | 173 | blockHash := block.Hash() |
@@ -59,13 +59,7 @@ type Node struct { | ||
59 | 59 | |
60 | 60 | // NewNode create bytom node |
61 | 61 | func NewNode(config *cfg.Config) *Node { |
62 | - if err := lockDataDirectory(config); err != nil { | |
63 | - cmn.Exit("Error: " + err.Error()) | |
64 | - } | |
65 | - | |
66 | - if err := cfg.LoadFederationFile(config.FederationFile(), config); err != nil { | |
67 | - cmn.Exit(cmn.Fmt("Failed to load federated information:[%s]", err.Error())) | |
68 | - } | |
62 | + initNodeConfig(config) | |
69 | 63 | |
70 | 64 | if err := vaporLog.InitLogFile(config); err != nil { |
71 | 65 | log.WithField("err", err).Fatalln("InitLogFile failed") |
@@ -79,12 +73,6 @@ func NewNode(config *cfg.Config) *Node { | ||
79 | 73 | "fed_controlprogram": hex.EncodeToString(cfg.FederationWScript(config)), |
80 | 74 | }).Info() |
81 | 75 | |
82 | - if err := consensus.InitActiveNetParams(config.ChainID); err != nil { | |
83 | - log.Fatalf("Failed to init ActiveNetParams:[%s]", err.Error()) | |
84 | - } | |
85 | - | |
86 | - initCommonConfig(config) | |
87 | - | |
88 | 76 | // Get store |
89 | 77 | if config.DBBackend != "memdb" && config.DBBackend != "leveldb" { |
90 | 78 | cmn.Exit(cmn.Fmt("Param db_backend [%v] is invalid, use leveldb or memdb", config.DBBackend)) |
@@ -128,6 +116,10 @@ func NewNode(config *cfg.Config) *Node { | ||
128 | 116 | log.WithFields(log.Fields{"module": logModule, "error": err}).Error("init NewWallet") |
129 | 117 | } |
130 | 118 | |
119 | + if err = wallet.Run(); err != nil { | |
120 | + log.WithFields(log.Fields{"module": logModule, "error": err}).Error("init NewWallet work running thread") | |
121 | + } | |
122 | + | |
131 | 123 | // trigger rescan wallet |
132 | 124 | if config.Wallet.Rescan { |
133 | 125 | wallet.RescanBlocks() |
@@ -170,6 +162,69 @@ func NewNode(config *cfg.Config) *Node { | ||
170 | 162 | return node |
171 | 163 | } |
172 | 164 | |
165 | +// Rollback rollback chain from one height to targetHeight | |
166 | +func Rollback(config *cfg.Config, targetHeight uint64) error { | |
167 | + if err := initNodeConfig(config); err != nil { | |
168 | + return err | |
169 | + } | |
170 | + | |
171 | + // Get store | |
172 | + if config.DBBackend != "leveldb" { | |
173 | + return errors.New("Param db_backend is invalid, use leveldb") | |
174 | + } | |
175 | + | |
176 | + coreDB := dbm.NewDB("core", config.DBBackend, config.DBDir()) | |
177 | + store := database.NewStore(coreDB) | |
178 | + | |
179 | + dispatcher := event.NewDispatcher() | |
180 | + movCore := mov.NewMovCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight) | |
181 | + txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore}, dispatcher) | |
182 | + chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher) | |
183 | + if err != nil { | |
184 | + return err | |
185 | + } | |
186 | + | |
187 | + hsm, err := pseudohsm.New(config.KeysDir()) | |
188 | + if err != nil { | |
189 | + return err | |
190 | + } | |
191 | + | |
192 | + walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir()) | |
193 | + walletStore := database.NewWalletStore(walletDB) | |
194 | + accountStore := database.NewAccountStore(walletDB) | |
195 | + accounts := account.NewManager(accountStore, chain) | |
196 | + assets := asset.NewRegistry(walletDB, chain) | |
197 | + wallet, err := w.NewWallet(walletStore, accounts, assets, hsm, chain, dispatcher, config.Wallet.TxIndex) | |
198 | + if err != nil { | |
199 | + return err | |
200 | + } | |
201 | + | |
202 | + if err := wallet.Rollback(targetHeight); err != nil { | |
203 | + return err | |
204 | + } | |
205 | + | |
206 | + return chain.Rollback(targetHeight) | |
207 | +} | |
208 | + | |
209 | +func initNodeConfig(config *cfg.Config) error { | |
210 | + if err := lockDataDirectory(config); err != nil { | |
211 | + log.WithField("err", err).Info("Error: " + err.Error()) | |
212 | + return err | |
213 | + } | |
214 | + | |
215 | + if err := cfg.LoadFederationFile(config.FederationFile(), config); err != nil { | |
216 | + log.WithField("err", err).Info("Failed to load federated information") | |
217 | + return err | |
218 | + } | |
219 | + | |
220 | + if err := consensus.InitActiveNetParams(config.ChainID); err != nil { | |
221 | + log.Fatalf("Failed to init ActiveNetParams:[%s]", err.Error()) | |
222 | + } | |
223 | + | |
224 | + cfg.CommonConfig = config | |
225 | + return nil | |
226 | +} | |
227 | + | |
173 | 228 | // find whether config xpubs equal genesis block xpubs |
174 | 229 | func checkConfig(chain *protocol.Chain, config *cfg.Config) error { |
175 | 230 | fedpegScript := cfg.FederationWScript(config) |
@@ -137,63 +137,140 @@ func (c *Chain) connectBlock(block *types.Block) (err error) { | ||
137 | 137 | return nil |
138 | 138 | } |
139 | 139 | |
140 | -func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error { | |
141 | - attachBlockHeaders, detachBlockHeaders, err := c.calcReorganizeChain(blockHeader, c.bestBlockHeader) | |
140 | +func (c *Chain) detachBlock(detachBlockHeader *types.BlockHeader, consensusResult *state.ConsensusResult, utxoView *state.UtxoViewpoint) (*types.Block, error) { | |
141 | + detachHash := detachBlockHeader.Hash() | |
142 | + block, err := c.store.GetBlock(&detachHash) | |
142 | 143 | if err != nil { |
143 | - return err | |
144 | + return block, err | |
144 | 145 | } |
145 | 146 | |
146 | - utxoView := state.NewUtxoViewpoint() | |
147 | - consensusResults := []*state.ConsensusResult{} | |
148 | - consensusResult, err := c.getBestConsensusResult() | |
147 | + detachBlock := types.MapBlock(block) | |
148 | + if err := consensusResult.DetachBlock(block); err != nil { | |
149 | + return block, err | |
150 | + } | |
151 | + | |
152 | + if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil { | |
153 | + return block, err | |
154 | + } | |
155 | + | |
156 | + txStatus, err := c.GetTransactionStatus(&detachBlock.ID) | |
149 | 157 | if err != nil { |
150 | - return err | |
158 | + return block, err | |
151 | 159 | } |
152 | 160 | |
161 | + if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil { | |
162 | + return block, err | |
163 | + } | |
164 | + | |
165 | + for _, p := range c.subProtocols { | |
166 | + if err := p.DetachBlock(block); err != nil { | |
167 | + return block, errors.Wrap(err, p.Name(), "sub protocol detach block") | |
168 | + } | |
169 | + } | |
170 | + | |
171 | + log.WithFields(log.Fields{"module": logModule, "height": detachBlockHeader.Height, "hash": detachHash.String()}).Debug("detach from mainchain") | |
172 | + return block, nil | |
173 | +} | |
174 | + | |
175 | +func (c *Chain) syncSubProtocols() error { | |
153 | 176 | for _, p := range c.subProtocols { |
154 | 177 | if err := c.syncProtocolStatus(p); err != nil { |
155 | 178 | return errors.Wrap(err, p.Name(), "sync sub protocol status") |
156 | 179 | } |
157 | 180 | } |
181 | + return nil | |
182 | +} | |
183 | + | |
184 | +// Rollback rollback the chain from one blockHeight to targetBlockHeight | |
185 | +// WARNING: we recommend to use this only in commond line | |
186 | +func (c *Chain) Rollback(targetHeight uint64) error { | |
187 | + c.cond.L.Lock() | |
188 | + defer c.cond.L.Unlock() | |
189 | + | |
190 | + utxoView := state.NewUtxoViewpoint() | |
191 | + consensusResult, err := c.getBestConsensusResult() | |
192 | + if err != nil { | |
193 | + return err | |
194 | + } | |
158 | 195 | |
159 | - txsToRestore := map[bc.Hash]*types.Tx{} | |
160 | - for _, detachBlockHeader := range detachBlockHeaders { | |
161 | - detachHash := detachBlockHeader.Hash() | |
162 | - b, err := c.store.GetBlock(&detachHash) | |
163 | - if err != nil { | |
164 | - return err | |
165 | - } | |
196 | + if err = c.syncSubProtocols(); err != nil { | |
197 | + return err | |
198 | + } | |
166 | 199 | |
167 | - detachBlock := types.MapBlock(b) | |
168 | - if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil { | |
169 | - return err | |
170 | - } | |
200 | + targetBlockHeader, err := c.GetHeaderByHeight(targetHeight) | |
201 | + if err != nil { | |
202 | + return err | |
203 | + } | |
171 | 204 | |
172 | - txStatus, err := c.GetTransactionStatus(&detachBlock.ID) | |
205 | + _, deletedBlockHeaders, err := c.calcReorganizeChain(targetBlockHeader, c.bestBlockHeader) | |
206 | + if err != nil { | |
207 | + return err | |
208 | + } | |
209 | + | |
210 | + deletedBlocks := []*types.Block{} | |
211 | + for _, deletedBlockHeader := range deletedBlockHeaders { | |
212 | + block, err := c.detachBlock(deletedBlockHeader, consensusResult, utxoView) | |
173 | 213 | if err != nil { |
174 | 214 | return err |
175 | 215 | } |
176 | 216 | |
177 | - if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil { | |
217 | + deletedBlocks = append(deletedBlocks, block) | |
218 | + } | |
219 | + | |
220 | + setIrrBlockHeader := c.lastIrrBlockHeader | |
221 | + if c.lastIrrBlockHeader.Height > targetBlockHeader.Height { | |
222 | + setIrrBlockHeader = targetBlockHeader | |
223 | + } | |
224 | + | |
225 | + startSeq := state.CalcVoteSeq(c.bestBlockHeader.Height) | |
226 | + | |
227 | + if err = c.setState(targetBlockHeader, setIrrBlockHeader, nil, utxoView, []*state.ConsensusResult{consensusResult.Fork()}); err != nil { | |
228 | + return err | |
229 | + } | |
230 | + | |
231 | + for _, block := range deletedBlocks { | |
232 | + if err := c.store.DeleteBlock(block); err != nil { | |
178 | 233 | return err |
179 | 234 | } |
235 | + } | |
180 | 236 | |
181 | - if err := consensusResult.DetachBlock(b); err != nil { | |
237 | + endSeq := state.CalcVoteSeq(targetHeight) | |
238 | + for nowSeq := startSeq; nowSeq > endSeq; nowSeq-- { | |
239 | + if err := c.store.DeleteConsensusResult(nowSeq); err != nil { | |
182 | 240 | return err |
183 | 241 | } |
242 | + } | |
184 | 243 | |
185 | - for _, p := range c.subProtocols { | |
186 | - if err := p.DetachBlock(b); err != nil { | |
187 | - return errors.Wrap(err, p.Name(), "sub protocol detach block") | |
188 | - } | |
244 | + return nil | |
245 | +} | |
246 | + | |
247 | +func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error { | |
248 | + attachBlockHeaders, detachBlockHeaders, err := c.calcReorganizeChain(blockHeader, c.bestBlockHeader) | |
249 | + if err != nil { | |
250 | + return err | |
251 | + } | |
252 | + | |
253 | + utxoView := state.NewUtxoViewpoint() | |
254 | + consensusResults := []*state.ConsensusResult{} | |
255 | + consensusResult, err := c.getBestConsensusResult() | |
256 | + if err != nil { | |
257 | + return err | |
258 | + } | |
259 | + | |
260 | + if err = c.syncSubProtocols(); err != nil { | |
261 | + return err | |
262 | + } | |
263 | + | |
264 | + txsToRestore := map[bc.Hash]*types.Tx{} | |
265 | + for _, detachBlockHeader := range detachBlockHeaders { | |
266 | + b, err := c.detachBlock(detachBlockHeader, consensusResult, utxoView) | |
267 | + if err != nil { | |
268 | + return err | |
189 | 269 | } |
190 | 270 | |
191 | 271 | for _, tx := range b.Transactions { |
192 | 272 | txsToRestore[tx.ID] = tx |
193 | 273 | } |
194 | - | |
195 | - blockHash := blockHeader.Hash() | |
196 | - log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("detach from mainchain") | |
197 | 274 | } |
198 | 275 | |
199 | 276 | txsToRemove := map[bc.Hash]*types.Tx{} |
@@ -26,6 +26,7 @@ func (s *mStore) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error) { | ||
26 | 26 | func (s *mStore) GetConsensusResult(uint64) (*state.ConsensusResult, error) { return nil, nil } |
27 | 27 | func (s *mStore) GetMainChainHash(uint64) (*bc.Hash, error) { return nil, nil } |
28 | 28 | func (s *mStore) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) { return nil, nil } |
29 | +func (s *mStore) DeleteConsensusResult(seq uint64) error { return nil } | |
29 | 30 | func (s *mStore) DeleteBlock(*types.Block) error { return nil } |
30 | 31 | func (s *mStore) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil } |
31 | 32 | func (s *mStore) SaveBlockHeader(blockHeader *types.BlockHeader) error { |
@@ -735,6 +735,10 @@ func (s *dummyStore) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) { | ||
735 | 735 | return nil, nil |
736 | 736 | } |
737 | 737 | |
738 | +func (s *dummyStore) DeleteConsensusResult(seq uint64) error { | |
739 | + return nil | |
740 | +} | |
741 | + | |
738 | 742 | func (s *dummyStore) SaveBlock(block *types.Block, _ *bc.TransactionStatus) error { |
739 | 743 | hash := block.Hash() |
740 | 744 | s.blocks[hash.String()] = block |
@@ -27,6 +27,7 @@ type Store interface { | ||
27 | 27 | GetMainChainHash(uint64) (*bc.Hash, error) |
28 | 28 | GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) |
29 | 29 | |
30 | + DeleteConsensusResult(uint64) error | |
30 | 31 | DeleteBlock(*types.Block) error |
31 | 32 | SaveBlock(*types.Block, *bc.TransactionStatus) error |
32 | 33 | SaveBlockHeader(*types.BlockHeader) error |
@@ -122,6 +122,7 @@ func (s *mockStore) GetConsensusResult(uint64) (*state.ConsensusResult, error) | ||
122 | 122 | func (s *mockStore) GetMainChainHash(uint64) (*bc.Hash, error) { return nil, nil } |
123 | 123 | func (s *mockStore) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) { return nil, nil } |
124 | 124 | func (s *mockStore) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil } |
125 | +func (s *mockStore) DeleteConsensusResult(seq uint64) error { return nil } | |
125 | 126 | func (s *mockStore) DeleteBlock(*types.Block) error { return nil } |
126 | 127 | func (s *mockStore) SaveBlockHeader(*types.BlockHeader) error { return nil } |
127 | 128 | func (s *mockStore) SaveChainStatus(*types.BlockHeader, *types.BlockHeader, []*types.BlockHeader, *state.UtxoViewpoint, []*state.ConsensusResult) error { |
@@ -675,6 +676,7 @@ func (s *mockStore1) GetMainChainHash(uint64) (*bc.Hash, error) | ||
675 | 676 | func (s *mockStore1) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) { return nil, nil } |
676 | 677 | func (s *mockStore1) DeleteBlock(*types.Block) error { return nil } |
677 | 678 | func (s *mockStore1) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil } |
679 | +func (s *mockStore1) DeleteConsensusResult(seq uint64) error { return nil } | |
678 | 680 | func (s *mockStore1) SaveBlockHeader(*types.BlockHeader) error { return nil } |
679 | 681 | func (s *mockStore1) SaveChainStatus(*types.BlockHeader, *types.BlockHeader, []*types.BlockHeader, *state.UtxoViewpoint, []*state.ConsensusResult) error { |
680 | 682 | return nil |
@@ -251,6 +251,11 @@ func (cfg *walletTestConfig) Run() error { | ||
251 | 251 | if err != nil { |
252 | 252 | return err |
253 | 253 | } |
254 | + | |
255 | + if err = wallet.Run(); err != nil { | |
256 | + return err | |
257 | + } | |
258 | + | |
254 | 259 | ctx := &walletTestContext{ |
255 | 260 | Wallet: wallet, |
256 | 261 | Chain: chain, |
@@ -80,16 +80,22 @@ func NewWallet(store WalletStore, account *account.Manager, asset *asset.Registr | ||
80 | 80 | return nil, err |
81 | 81 | } |
82 | 82 | |
83 | + return w, nil | |
84 | +} | |
85 | + | |
86 | +// Run go to run some wallet recorvery and clean tx thread | |
87 | +func (w *Wallet) Run() error { | |
83 | 88 | var err error |
84 | 89 | w.TxMsgSub, err = w.EventDispatcher.Subscribe(protocol.TxMsgEvent{}) |
85 | 90 | if err != nil { |
86 | - return nil, err | |
91 | + return err | |
87 | 92 | } |
88 | 93 | |
89 | 94 | go w.walletUpdater() |
90 | 95 | go w.delUnconfirmedTx() |
91 | 96 | go w.MemPoolTxQueryLoop() |
92 | - return w, nil | |
97 | + | |
98 | + return nil | |
93 | 99 | } |
94 | 100 | |
95 | 101 | // MemPoolTxQueryLoop constantly pass a transaction accepted by mempool to the wallet. |
@@ -307,6 +313,22 @@ func (w *Wallet) DeleteAccount(accountID string) (err error) { | ||
307 | 313 | return nil |
308 | 314 | } |
309 | 315 | |
316 | +// Rollback wallet to target height | |
317 | +func (w *Wallet) Rollback(targetHeight uint64) error { | |
318 | + for w.Status.WorkHeight > targetHeight { | |
319 | + block, err := w.Chain.GetBlockByHash(&w.Status.WorkHash) | |
320 | + if err != nil { | |
321 | + return err | |
322 | + } | |
323 | + | |
324 | + if err = w.DetachBlock(block); err != nil { | |
325 | + return err | |
326 | + } | |
327 | + } | |
328 | + | |
329 | + return nil | |
330 | +} | |
331 | + | |
310 | 332 | func (w *Wallet) UpdateAccountAlias(accountID string, newAlias string) (err error) { |
311 | 333 | w.rw.Lock() |
312 | 334 | defer w.rw.Unlock() |