|
4 | 4 | "bytes" |
5 | 5 | "encoding/base64" |
6 | 6 | "fmt" |
| 7 | + "math/big" |
7 | 8 |
|
8 | 9 | "github.com/btcsuite/btcd/blockchain" |
9 | 10 | "github.com/btcsuite/btcd/btcutil" |
@@ -95,73 +96,72 @@ func (k Keeper) SetBlockHeader(ctx sdk.Context, header *types.BlockHeader) { |
95 | 96 | store.Set(types.BtcBlockHeaderHeightKey(header.Height), []byte(header.Hash)) |
96 | 97 | } |
97 | 98 |
|
98 | | -func (k Keeper) SetBlockHeaders(ctx sdk.Context, blockHeaders []*types.BlockHeader) error { |
| 99 | +func (k Keeper) SetBlockHeaders(ctx sdk.Context, headers []*types.BlockHeader) { |
| 100 | + for _, h := range headers { |
| 101 | + k.SetBlockHeader(ctx, h) |
| 102 | + } |
| 103 | +} |
| 104 | + |
| 105 | +func (k Keeper) InsertBlockHeaders(ctx sdk.Context, blockHeaders []*types.BlockHeader) error { |
99 | 106 | store := ctx.KVStore(k.storeKey) |
100 | 107 |
|
101 | | - // first check if some block header already exists |
102 | | - for _, header := range blockHeaders { |
103 | | - if store.Has(types.BtcBlockHeaderHashKey(header.Hash)) { |
104 | | - // return no error |
105 | | - return nil |
106 | | - } |
| 108 | + startBlockHeader := blockHeaders[0] |
| 109 | + newBestBlockHeader := blockHeaders[len(blockHeaders)-1] |
| 110 | + |
| 111 | + // check if the starting block header already exists |
| 112 | + if store.Has(types.BtcBlockHeaderHashKey(startBlockHeader.Hash)) { |
| 113 | + // return no error |
| 114 | + return nil |
107 | 115 | } |
108 | 116 |
|
109 | 117 | params := k.GetParams(ctx) |
110 | 118 |
|
111 | 119 | // get the best block header |
112 | 120 | best := k.GetBestBlockHeader(ctx) |
113 | 121 |
|
114 | | - for _, header := range blockHeaders { |
115 | | - // validate the block header |
116 | | - if err := header.Validate(); err != nil { |
117 | | - return err |
| 122 | + if startBlockHeader.PreviousBlockHash == best.Hash { |
| 123 | + if startBlockHeader.Height != best.Height+1 { |
| 124 | + return errorsmod.Wrap(types.ErrInvalidBlockHeaders, "invalid block height") |
| 125 | + } |
| 126 | + } else { |
| 127 | + // reorg detected |
| 128 | + // check if the reorg depth exceeds the safe confirmations |
| 129 | + if best.Height-startBlockHeader.Height+1 > uint64(params.Confirmations) { |
| 130 | + return types.ErrInvalidReorgDepth |
118 | 131 | } |
119 | 132 |
|
120 | 133 | // check if the previous block exists |
121 | | - if !store.Has(types.BtcBlockHeaderHashKey(header.PreviousBlockHash)) { |
122 | | - return errorsmod.Wrap(types.ErrInvalidBlockHeader, "previous block does not exist") |
| 134 | + if !store.Has(types.BtcBlockHeaderHashKey(startBlockHeader.PreviousBlockHash)) { |
| 135 | + return errorsmod.Wrap(types.ErrInvalidBlockHeaders, "previous block does not exist") |
123 | 136 | } |
124 | 137 |
|
125 | 138 | // check the block height |
126 | | - prevBlock := k.GetBlockHeader(ctx, header.PreviousBlockHash) |
127 | | - if header.Height != prevBlock.Height+1 { |
128 | | - return errorsmod.Wrap(types.ErrInvalidBlockHeader, "incorrect block height") |
| 139 | + prevBlock := k.GetBlockHeader(ctx, startBlockHeader.PreviousBlockHash) |
| 140 | + if startBlockHeader.Height != prevBlock.Height+1 { |
| 141 | + return errorsmod.Wrap(types.ErrInvalidBlockHeaders, "invalid block height") |
129 | 142 | } |
130 | 143 |
|
131 | | - // check whether it's next block header or not |
132 | | - if best.Hash != header.PreviousBlockHash { |
133 | | - // check if the reorg depth exceeds the safe confirmations |
134 | | - if best.Height-header.Height+1 > uint64(params.Confirmations) { |
135 | | - return types.ErrInvalidReorgDepth |
136 | | - } |
137 | | - |
138 | | - // check if the new block header has more work than the old one |
139 | | - oldNode := k.GetBlockHeaderByHeight(ctx, header.Height) |
140 | | - worksOld := blockchain.CalcWork(types.BitsToTargetUint32(oldNode.Bits)) |
141 | | - worksNew := blockchain.CalcWork(types.BitsToTargetUint32(header.Bits)) |
142 | | - if sdk.GetConfig().GetBtcChainCfg().Net == wire.MainNet && worksNew.Cmp(worksOld) <= 0 || worksNew.Cmp(worksOld) < 0 { |
143 | | - return types.ErrForkedBlockHeader |
144 | | - } |
145 | | - |
146 | | - // remove the block headers after the forked block header |
147 | | - // and consider the forked block header as the best block header |
148 | | - for i := header.Height; i <= best.Height; i++ { |
149 | | - ctx.Logger().Info("Removing block header: ", i) |
150 | | - thash := k.GetBlockHashByHeight(ctx, i) |
151 | | - store.Delete(types.BtcBlockHeaderHashKey(thash)) |
152 | | - store.Delete(types.BtcBlockHeaderHeightKey(i)) |
153 | | - } |
| 144 | + // check if the new block headers has more work than the work accumulated from the forked block to the current tip |
| 145 | + totalWorkOldToTip := k.CalcTotalWork(ctx, startBlockHeader.Height, best.Height) |
| 146 | + totalWorkNew := types.BlockHeaders(blockHeaders).GetTotalWork() |
| 147 | + if sdk.GetConfig().GetBtcChainCfg().Net == wire.MainNet && totalWorkNew.Cmp(totalWorkOldToTip) <= 0 || totalWorkNew.Cmp(totalWorkOldToTip) < 0 { |
| 148 | + return errorsmod.Wrap(types.ErrInvalidBlockHeaders, "invalid forking block headers") |
154 | 149 | } |
155 | 150 |
|
156 | | - // set the block header |
157 | | - k.SetBlockHeader(ctx, header) |
158 | | - |
159 | | - // update the best block header |
160 | | - best = header |
| 151 | + // remove the block headers starting from the forked block height |
| 152 | + for i := startBlockHeader.Height; i <= best.Height; i++ { |
| 153 | + ctx.Logger().Info("Removing block header: ", i) |
| 154 | + thash := k.GetBlockHashByHeight(ctx, i) |
| 155 | + store.Delete(types.BtcBlockHeaderHashKey(thash)) |
| 156 | + store.Delete(types.BtcBlockHeaderHeightKey(i)) |
| 157 | + } |
161 | 158 | } |
162 | 159 |
|
| 160 | + // set block headers |
| 161 | + k.SetBlockHeaders(ctx, blockHeaders) |
| 162 | + |
163 | 163 | // set the best block header |
164 | | - k.SetBestBlockHeader(ctx, best) |
| 164 | + k.SetBestBlockHeader(ctx, newBestBlockHeader) |
165 | 165 |
|
166 | 166 | return nil |
167 | 167 | } |
@@ -210,6 +210,18 @@ func (k Keeper) IterateBlockHeaders(ctx sdk.Context, process func(header types.B |
210 | 210 | } |
211 | 211 | } |
212 | 212 |
|
| 213 | +// CalcTotalWork calculates the total work of the given range of block headers |
| 214 | +func (k Keeper) CalcTotalWork(ctx sdk.Context, startHeight uint64, endHeight uint64) *big.Int { |
| 215 | + totalWork := new(big.Int) |
| 216 | + |
| 217 | + for i := startHeight; i <= endHeight; i++ { |
| 218 | + work := k.GetBlockHeaderByHeight(ctx, i).GetWork() |
| 219 | + totalWork = new(big.Int).Add(totalWork, work) |
| 220 | + } |
| 221 | + |
| 222 | + return totalWork |
| 223 | +} |
| 224 | + |
213 | 225 | // ValidateTransaction validates the given transaction |
214 | 226 | func (k Keeper) ValidateTransaction(ctx sdk.Context, txBytes string, prevTxBytes string, blockHash string, proof []string) (*btcutil.Tx, *btcutil.Tx, error) { |
215 | 227 | params := k.GetParams(ctx) |
|
0 commit comments