Skip to content

Commit 960008f

Browse files
committed
add tests for removing blocks
1 parent 3795a75 commit 960008f

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

xcode/DirectBindingsApp/DirectBindingsApp/app-batteries/BlockchainObserver.swift

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -195,18 +195,24 @@ class BlockchainObserver {
195195

196196
// create an array of the new blocks
197197
var addedBlocks = [RPCBlockDetails]()
198-
for addedBlockHeight in (knownChaintip.height + 1)...currentChaintipHeight {
199-
let addedBlockHash = try await self.getBlockHashHex(height: addedBlockHeight)
200-
let addedBlock = try await self.getBlock(hash: addedBlockHash)
201-
addedBlocks.append(addedBlock)
198+
if knownChaintip.height < currentChaintipHeight {
199+
// without this precondition, the range won't even work to begin with
200+
for addedBlockHeight in (knownChaintip.height + 1)...currentChaintipHeight {
201+
let addedBlockHash = try await self.getBlockHashHex(height: addedBlockHeight)
202+
let addedBlock = try await self.getBlock(hash: addedBlockHash)
203+
addedBlocks.append(addedBlock)
204+
}
202205
}
203206

204207
while addedBlocks.isEmpty || addedBlocks.first!.previousblockhash != self.connectedBlocks.last!.hash {
205208
// we must keep popping until it matches
206209
let trimmedLocalTip = try await self.disconnectBlock()
207-
let reorgedBlockHash = try await self.getBlockHashHex(height: trimmedLocalTip.height)
208-
let reorgedBlock = try await self.getBlock(hash: reorgedBlockHash)
209-
addedBlocks.insert(reorgedBlock, at: 0)
210+
if trimmedLocalTip.height <= currentChaintipHeight {
211+
// if the trimmed block is simply gone, we cannot retrieve the current block hash hex
212+
let reorgedBlockHash = try await self.getBlockHashHex(height: trimmedLocalTip.height)
213+
let reorgedBlock = try await self.getBlock(hash: reorgedBlockHash)
214+
addedBlocks.insert(reorgedBlock, at: 0)
215+
}
210216
}
211217

212218
for addedBlock in addedBlocks {
@@ -247,7 +253,7 @@ class BlockchainObserver {
247253
}
248254
}
249255

250-
print("connecting block \(block.height) with hex: \(block.hash)")
256+
// print("connecting block \(block.height) with hex: \(block.hash)")
251257

252258
if self.chainListeners.count > 0 {
253259
let binary = try await self.getBlockBinary(hash: block.hash)

xcode/DirectBindingsApp/DirectBindingsApp/app-batteries/RegtestBlockchainManager.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,14 @@ class RegtestBlockchainManager: BlockchainObserver {
2929
return result
3030
}
3131

32+
/**
33+
Invalidate or un-mine a block
34+
- Parameter hash: The block hash hex to invalidate
35+
- Returns:
36+
- Throws:
37+
*/
38+
public func unmineBlock(hash: String) async throws {
39+
let response = try await self.callRpcMethod(method: "invalidateblock", params: [hash])
40+
}
41+
3242
}

xcode/DirectBindingsApp/DirectBindingsAppTests/test-batteries/bitcoin/BitcoinTests.swift

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public class BitcoinTests: XCTestCase {
123123
// let address = try await rpcInterface.generateAddress()
124124
// let details = try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: address)
125125
let helpDetails = try await rpcInterface.getHelp()
126-
print(helpDetails)
126+
// print(helpDetails)
127127

128128
let exampleFundingTx: [UInt8] = [1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 1, 160, 134, 1, 0, 0, 0, 0, 0, 34, 0, 32, 15, 45, 153, 123, 98, 37, 242, 142, 113, 51, 126, 45, 206, 175, 66, 247, 173, 2, 141, 2, 27, 84, 38, 188, 34, 74, 82, 164, 28, 229, 39, 139, 1, 1, 1, 0, 0, 0, 0]
129129

@@ -132,16 +132,18 @@ public class BitcoinTests: XCTestCase {
132132

133133
class Listener: BlockchainListener {
134134
var initializationComplete = false
135-
var newBlockDetected = false
135+
var newBlocksDetected = 0
136+
var blocksLost = 0
136137

137138
func blockConnected(block: [UInt8], height: UInt32) {
138139
if self.initializationComplete {
139-
self.newBlockDetected = true
140+
self.newBlocksDetected += 1
140141
}
141142
print("block connected at height \(height): \(block)")
142143
}
143144

144145
func blockDisconnected(header: [UInt8]?, height: UInt32) {
146+
self.blocksLost += 1
145147
print("block disconnected from height \(height): \(header)")
146148
}
147149
}
@@ -157,10 +159,36 @@ public class BitcoinTests: XCTestCase {
157159
let exampleOutputScript: [UInt8] = [0, 32, 200, 194, 75, 55, 227, 33, 251, 71, 196, 33, 177, 196, 155, 145, 17, 78, 244, 226, 155, 141, 216, 230, 180, 183, 149, 172, 116, 249, 56, 6, 118, 255]
158160
let scriptInfo = try await rpcInterface.decodeScript(script: exampleOutputScript)
159161
let outputAddress = (scriptInfo["addresses"] as! [String]).first!
162+
try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: testAddress)
160163
try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: outputAddress)
164+
161165
// sleep for six seconds
162166
try await Task.sleep(nanoseconds: 6_000_000_000)
163-
XCTAssertEqual(listener.newBlockDetected, true)
167+
XCTAssertEqual(listener.newBlocksDetected, 2)
168+
XCTAssertEqual(listener.blocksLost, 0)
169+
170+
do {
171+
let testAddress = try await rpcInterface.generateAddress()
172+
let chaintip = try await rpcInterface.getChaintipHeight()
173+
let penultimateBlockHash = try await rpcInterface.getBlockHashHex(height: chaintip - 1)
174+
try await rpcInterface.unmineBlock(hash: penultimateBlockHash)
175+
try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: testAddress)
176+
177+
try await rpcInterface.reconcileChaintips()
178+
XCTAssertEqual(listener.newBlocksDetected, 3)
179+
XCTAssertEqual(listener.blocksLost, 2)
180+
}
181+
182+
do {
183+
let testAddress = try await rpcInterface.generateAddress()
184+
let chaintipHash = try await rpcInterface.getChaintipHashHex()
185+
try await rpcInterface.unmineBlock(hash: chaintipHash)
186+
try await rpcInterface.mineBlocks(number: 2, coinbaseDestinationAddress: testAddress)
187+
188+
try await rpcInterface.reconcileChaintips()
189+
XCTAssertEqual(listener.newBlocksDetected, 5)
190+
XCTAssertEqual(listener.blocksLost, 3)
191+
}
164192
}
165193

166194
}

0 commit comments

Comments
 (0)