github.com/lbryio/lbcd@v0.22.119/integration/bip0009_test.go (about) 1 // Copyright (c) 2016 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 // This file is ignored during the regular tests due to the following build tag. 6 //go:build rpctest 7 // +build rpctest 8 9 package integration 10 11 import ( 12 "fmt" 13 "runtime" 14 "testing" 15 "time" 16 17 "github.com/lbryio/lbcd/blockchain" 18 "github.com/lbryio/lbcd/chaincfg" 19 "github.com/lbryio/lbcd/chaincfg/chainhash" 20 "github.com/lbryio/lbcd/integration/rpctest" 21 ) 22 23 const ( 24 // vbLegacyBlockVersion is the highest legacy block version before the 25 // version bits scheme became active. 26 vbLegacyBlockVersion = 4 27 28 // vbTopBits defines the bits to set in the version to signal that the 29 // version bits scheme is being used. 30 vbTopBits = 0x20000000 31 ) 32 33 // assertVersionBit gets the passed block hash from the given test harness and 34 // ensures its version either has the provided bit set or unset per the set 35 // flag. 36 func assertVersionBit(r *rpctest.Harness, t *testing.T, hash *chainhash.Hash, bit uint8, set bool) { 37 block, err := r.Client.GetBlock(hash) 38 if err != nil { 39 t.Fatalf("failed to retrieve block %v: %v", hash, err) 40 } 41 switch { 42 case set && block.Header.Version&(1<<bit) == 0: 43 _, _, line, _ := runtime.Caller(1) 44 t.Fatalf("assertion failed at line %d: block %s, version 0x%x "+ 45 "does not have bit %d set", line, hash, 46 block.Header.Version, bit) 47 case !set && block.Header.Version&(1<<bit) != 0: 48 _, _, line, _ := runtime.Caller(1) 49 t.Fatalf("assertion failed at line %d: block %s, version 0x%x "+ 50 "has bit %d set", line, hash, block.Header.Version, bit) 51 } 52 } 53 54 // assertChainHeight retrieves the current chain height from the given test 55 // harness and ensures it matches the provided expected height. 56 func assertChainHeight(r *rpctest.Harness, t *testing.T, expectedHeight uint32) { 57 height, err := r.Client.GetBlockCount() 58 if err != nil { 59 t.Fatalf("failed to retrieve block height: %v", err) 60 } 61 if uint32(height) != expectedHeight { 62 _, _, line, _ := runtime.Caller(1) 63 t.Fatalf("assertion failed at line %d: block height of %d "+ 64 "is not the expected %d", line, height, expectedHeight) 65 } 66 } 67 68 // thresholdStateToStatus converts the passed threshold state to the equivalent 69 // status string returned in the getblockchaininfo RPC. 70 func thresholdStateToStatus(state blockchain.ThresholdState) (string, error) { 71 switch state { 72 case blockchain.ThresholdDefined: 73 return "defined", nil 74 case blockchain.ThresholdStarted: 75 return "started", nil 76 case blockchain.ThresholdLockedIn: 77 return "lockedin", nil 78 case blockchain.ThresholdActive: 79 return "active", nil 80 case blockchain.ThresholdFailed: 81 return "failed", nil 82 } 83 84 return "", fmt.Errorf("unrecognized threshold state: %v", state) 85 } 86 87 // assertSoftForkStatus retrieves the current blockchain info from the given 88 // test harness and ensures the provided soft fork key is both available and its 89 // status is the equivalent of the passed state. 90 func assertSoftForkStatus(r *rpctest.Harness, t *testing.T, forkKey string, state blockchain.ThresholdState) { 91 // Convert the expected threshold state into the equivalent 92 // getblockchaininfo RPC status string. 93 status, err := thresholdStateToStatus(state) 94 if err != nil { 95 _, _, line, _ := runtime.Caller(1) 96 t.Fatalf("assertion failed at line %d: unable to convert "+ 97 "threshold state %v to string", line, state) 98 } 99 100 info, err := r.Client.GetBlockChainInfo() 101 if err != nil { 102 t.Fatalf("failed to retrieve chain info: %v", err) 103 } 104 105 // Ensure the key is available. 106 desc, ok := info.SoftForks.Bip9SoftForks[forkKey] 107 if !ok { 108 _, _, line, _ := runtime.Caller(1) 109 t.Fatalf("assertion failed at line %d: softfork status for %q "+ 110 "is not in getblockchaininfo results", line, forkKey) 111 } 112 113 // Ensure the status it the expected value. 114 if desc.Status != status { 115 _, _, line, _ := runtime.Caller(1) 116 t.Fatalf("assertion failed at line %d: softfork status for %q "+ 117 "is %v instead of expected %v", line, forkKey, 118 desc.Status, status) 119 } 120 } 121 122 // testBIP0009 ensures the BIP0009 soft fork mechanism follows the state 123 // transition rules set forth by the BIP for the provided soft fork key. It 124 // uses the regression test network to signal support and advance through the 125 // various threshold states including failure to achieve locked in status. 126 // 127 // See TestBIP0009 for an overview of what is tested. 128 // 129 // NOTE: This only differs from the exported version in that it accepts the 130 // specific soft fork deployment to test. 131 func testBIP0009(t *testing.T, forkKey string, deploymentID uint32) { 132 // Initialize the primary mining node with only the genesis block. 133 r, err := rpctest.New(&chaincfg.SimNetParams, nil, nil, "") 134 if err != nil { 135 t.Fatalf("unable to create primary harness: %v", err) 136 } 137 if err := r.SetUp(false, 0); err != nil { 138 t.Fatalf("unable to setup test chain: %v", err) 139 } 140 defer r.TearDown() 141 142 // *** ThresholdDefined *** 143 // 144 // Assert the chain height is the expected value and the soft fork 145 // status starts out as defined. 146 assertChainHeight(r, t, 0) 147 assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdDefined) 148 149 // *** ThresholdDefined part 2 - 1 block prior to ThresholdStarted *** 150 // 151 // Generate enough blocks to reach the height just before the first 152 // state transition without signalling support since the state should 153 // move to started once the start time has been reached regardless of 154 // support signalling. 155 // 156 // NOTE: This is two blocks before the confirmation window because the 157 // getblockchaininfo RPC reports the status for the block AFTER the 158 // current one. All of the heights below are thus offset by one to 159 // compensate. 160 // 161 // Assert the chain height is the expected value and soft fork status is 162 // still defined and did NOT move to started. 163 confirmationWindow := r.ActiveNet.MinerConfirmationWindow 164 for i := uint32(0); i < confirmationWindow-2; i++ { 165 _, err := r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion, 166 time.Time{}) 167 if err != nil { 168 t.Fatalf("failed to generated block %d: %v", i, err) 169 } 170 } 171 assertChainHeight(r, t, confirmationWindow-2) 172 assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdDefined) 173 174 // *** ThresholdStarted *** 175 // 176 // Generate another block to reach the next window. 177 // 178 // Assert the chain height is the expected value and the soft fork 179 // status is started. 180 _, err = r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion, time.Time{}) 181 if err != nil { 182 t.Fatalf("failed to generated block: %v", err) 183 } 184 assertChainHeight(r, t, confirmationWindow-1) 185 assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdStarted) 186 187 // *** ThresholdStarted part 2 - Fail to achieve ThresholdLockedIn *** 188 // 189 // Generate enough blocks to reach the next window in such a way that 190 // the number blocks with the version bit set to signal support is 1 191 // less than required to achieve locked in status. 192 // 193 // Assert the chain height is the expected value and the soft fork 194 // status is still started and did NOT move to locked in. 195 if deploymentID > uint32(len(r.ActiveNet.Deployments)) { 196 t.Fatalf("deployment ID %d does not exist", deploymentID) 197 } 198 deployment := &r.ActiveNet.Deployments[deploymentID] 199 activationThreshold := r.ActiveNet.RuleChangeActivationThreshold 200 signalForkVersion := int32(1<<deployment.BitNumber) | vbTopBits 201 for i := uint32(0); i < activationThreshold-1; i++ { 202 _, err := r.GenerateAndSubmitBlock(nil, signalForkVersion, 203 time.Time{}) 204 if err != nil { 205 t.Fatalf("failed to generated block %d: %v", i, err) 206 } 207 } 208 for i := uint32(0); i < confirmationWindow-(activationThreshold-1); i++ { 209 _, err := r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion, 210 time.Time{}) 211 if err != nil { 212 t.Fatalf("failed to generated block %d: %v", i, err) 213 } 214 } 215 assertChainHeight(r, t, (confirmationWindow*2)-1) 216 assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdStarted) 217 218 // *** ThresholdLockedIn *** 219 // 220 // Generate enough blocks to reach the next window in such a way that 221 // the number blocks with the version bit set to signal support is 222 // exactly the number required to achieve locked in status. 223 // 224 // Assert the chain height is the expected value and the soft fork 225 // status moved to locked in. 226 for i := uint32(0); i < activationThreshold; i++ { 227 _, err := r.GenerateAndSubmitBlock(nil, signalForkVersion, 228 time.Time{}) 229 if err != nil { 230 t.Fatalf("failed to generated block %d: %v", i, err) 231 } 232 } 233 for i := uint32(0); i < confirmationWindow-activationThreshold; i++ { 234 _, err := r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion, 235 time.Time{}) 236 if err != nil { 237 t.Fatalf("failed to generated block %d: %v", i, err) 238 } 239 } 240 assertChainHeight(r, t, (confirmationWindow*3)-1) 241 assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdLockedIn) 242 243 // *** ThresholdLockedIn part 2 -- 1 block prior to ThresholdActive *** 244 // 245 // Generate enough blocks to reach the height just before the next 246 // window without continuing to signal support since it is already 247 // locked in. 248 // 249 // Assert the chain height is the expected value and the soft fork 250 // status is still locked in and did NOT move to active. 251 for i := uint32(0); i < confirmationWindow-1; i++ { 252 _, err := r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion, 253 time.Time{}) 254 if err != nil { 255 t.Fatalf("failed to generated block %d: %v", i, err) 256 } 257 } 258 assertChainHeight(r, t, (confirmationWindow*4)-2) 259 assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdLockedIn) 260 261 // *** ThresholdActive *** 262 // 263 // Generate another block to reach the next window without continuing to 264 // signal support since it is already locked in. 265 // 266 // Assert the chain height is the expected value and the soft fork 267 // status moved to active. 268 _, err = r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion, time.Time{}) 269 if err != nil { 270 t.Fatalf("failed to generated block: %v", err) 271 } 272 assertChainHeight(r, t, (confirmationWindow*4)-1) 273 assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdActive) 274 } 275 276 // TestBIP0009 ensures the BIP0009 soft fork mechanism follows the state 277 // transition rules set forth by the BIP for all soft forks. It uses the 278 // regression test network to signal support and advance through the various 279 // threshold states including failure to achieve locked in status. 280 // 281 // Overview: 282 // - Assert the chain height is 0 and the state is ThresholdDefined 283 // - Generate 1 fewer blocks than needed to reach the first state transition 284 // - Assert chain height is expected and state is still ThresholdDefined 285 // 286 // - Generate 1 more block to reach the first state transition 287 // - Assert chain height is expected and state moved to ThresholdStarted 288 // - Generate enough blocks to reach the next state transition window, but only 289 // signal support in 1 fewer than the required number to achieve 290 // ThresholdLockedIn 291 // - Assert chain height is expected and state is still ThresholdStarted 292 // - Generate enough blocks to reach the next state transition window with only 293 // the exact number of blocks required to achieve locked in status signalling 294 // support. 295 // - Assert chain height is expected and state moved to ThresholdLockedIn 296 // - Generate 1 fewer blocks than needed to reach the next state transition 297 // - Assert chain height is expected and state is still ThresholdLockedIn 298 // - Generate 1 more block to reach the next state transition 299 // - Assert chain height is expected and state moved to ThresholdActive 300 func TestBIP0009(t *testing.T) { 301 t.Parallel() 302 303 testBIP0009(t, "dummy", chaincfg.DeploymentTestDummy) 304 testBIP0009(t, "segwit", chaincfg.DeploymentSegwit) 305 } 306 307 // TestBIP0009Mining ensures blocks built via btcd's CPU miner follow the rules 308 // set forth by BIP0009 by using the test dummy deployment. 309 // 310 // Overview: 311 // - Generate block 1 312 // - Assert bit is NOT set (ThresholdDefined) 313 // 314 // - Generate enough blocks to reach first state transition 315 // - Assert bit is NOT set for block prior to state transition 316 // - Assert bit is set for block at state transition (ThresholdStarted) 317 // 318 // - Generate enough blocks to reach second state transition 319 // - Assert bit is set for block at state transition (ThresholdLockedIn) 320 // 321 // - Generate enough blocks to reach third state transition 322 // - Assert bit is set for block prior to state transition (ThresholdLockedIn) 323 // - Assert bit is NOT set for block at state transition (ThresholdActive) 324 func TestBIP0009Mining(t *testing.T) { 325 t.Parallel() 326 327 // Initialize the primary mining node with only the genesis block. 328 r, err := rpctest.New(&chaincfg.RegressionNetParams, nil, nil, "") 329 if err != nil { 330 t.Fatalf("unable to create primary harness: %v", err) 331 } 332 if err := r.SetUp(true, 0); err != nil { 333 t.Fatalf("unable to setup test chain: %v", err) 334 } 335 defer r.TearDown() 336 337 // Assert the chain only consists of the gensis block. 338 assertChainHeight(r, t, 0) 339 340 // *** ThresholdDefined *** 341 // 342 // Generate a block that extends the genesis block. It should not have 343 // the test dummy bit set in the version since the first window is 344 // in the defined threshold state. 345 deployment := &r.ActiveNet.Deployments[chaincfg.DeploymentTestDummy] 346 testDummyBitNum := deployment.BitNumber 347 hashes, err := r.Client.Generate(1) 348 if err != nil { 349 t.Fatalf("unable to generate blocks: %v", err) 350 } 351 assertChainHeight(r, t, 1) 352 assertVersionBit(r, t, hashes[0], testDummyBitNum, false) 353 354 // *** ThresholdStarted *** 355 // 356 // Generate enough blocks to reach the first state transition. 357 // 358 // The second to last generated block should not have the test bit set 359 // in the version. 360 // 361 // The last generated block should now have the test bit set in the 362 // version since the btcd mining code will have recognized the test 363 // dummy deployment as started. 364 confirmationWindow := r.ActiveNet.MinerConfirmationWindow 365 numNeeded := confirmationWindow - 1 366 hashes, err = r.Client.Generate(numNeeded) 367 if err != nil { 368 t.Fatalf("failed to generated %d blocks: %v", numNeeded, err) 369 } 370 assertChainHeight(r, t, confirmationWindow) 371 assertVersionBit(r, t, hashes[len(hashes)-2], testDummyBitNum, false) 372 assertVersionBit(r, t, hashes[len(hashes)-1], testDummyBitNum, true) 373 374 // *** ThresholdLockedIn *** 375 // 376 // Generate enough blocks to reach the next state transition. 377 // 378 // The last generated block should still have the test bit set in the 379 // version since the btcd mining code will have recognized the test 380 // dummy deployment as locked in. 381 hashes, err = r.Client.Generate(confirmationWindow) 382 if err != nil { 383 t.Fatalf("failed to generated %d blocks: %v", confirmationWindow, 384 err) 385 } 386 assertChainHeight(r, t, confirmationWindow*2) 387 assertVersionBit(r, t, hashes[len(hashes)-1], testDummyBitNum, true) 388 389 // *** ThresholdActivated *** 390 // 391 // Generate enough blocks to reach the next state transition. 392 // 393 // The second to last generated block should still have the test bit set 394 // in the version since it is still locked in. 395 // 396 // The last generated block should NOT have the test bit set in the 397 // version since the btcd mining code will have recognized the test 398 // dummy deployment as activated and thus there is no longer any need 399 // to set the bit. 400 hashes, err = r.Client.Generate(confirmationWindow) 401 if err != nil { 402 t.Fatalf("failed to generated %d blocks: %v", confirmationWindow, 403 err) 404 } 405 assertChainHeight(r, t, confirmationWindow*3) 406 assertVersionBit(r, t, hashes[len(hashes)-2], testDummyBitNum, true) 407 assertVersionBit(r, t, hashes[len(hashes)-1], testDummyBitNum, false) 408 }