@@ -24,6 +24,7 @@ import (
2424 "github.com/lightningnetwork/lnd/input"
2525 "github.com/lightningnetwork/lnd/lntest/mock"
2626 "github.com/lightningnetwork/lnd/lnwallet"
27+ "github.com/lightningnetwork/lnd/lnwire"
2728 "github.com/lightningnetwork/lnd/sweep"
2829 "github.com/stretchr/testify/require"
2930)
@@ -1262,3 +1263,197 @@ func TestKidOutputDecode(t *testing.T) {
12621263 })
12631264 }
12641265}
1266+
1267+ // TestPatchZeroHeightHint tests the patchZeroHeightHint function to ensure
1268+ // it correctly handles both normal cases and the edge case where classHeight
1269+ // is zero due to a historical bug.
1270+ func TestPatchZeroHeightHint (t * testing.T ) {
1271+ t .Parallel ()
1272+
1273+ tests := []struct {
1274+ name string
1275+ classHeight uint32
1276+ closeHeight uint32
1277+ confDepth uint32
1278+ shortChanID lnwire.ShortChannelID
1279+ fetchError error
1280+ expectedHeight uint32
1281+ expectError bool
1282+ errorContains string
1283+ }{
1284+ {
1285+ name : "normal case - non-zero class height" ,
1286+ classHeight : 100 ,
1287+ closeHeight : 200 ,
1288+ confDepth : 6 ,
1289+ shortChanID : lnwire.ShortChannelID {BlockHeight : 50 },
1290+ expectedHeight : 100 ,
1291+ expectError : false ,
1292+ },
1293+ {
1294+ name : "zero class height - fetch closed " +
1295+ "channel error" ,
1296+ classHeight : 0 ,
1297+ closeHeight : 100 ,
1298+ confDepth : 6 ,
1299+ shortChanID : lnwire.ShortChannelID {BlockHeight : 50 },
1300+ fetchError : fmt .Errorf ("channel not found" ),
1301+ expectError : true ,
1302+ errorContains : "cannot fetch close summary" ,
1303+ },
1304+ {
1305+ name : "zero class height - both close " +
1306+ "height and short chan ID = 0" ,
1307+ classHeight : 0 ,
1308+ closeHeight : 0 ,
1309+ confDepth : 6 ,
1310+ shortChanID : lnwire.ShortChannelID {BlockHeight : 0 },
1311+ expectedHeight : 0 ,
1312+ expectError : true ,
1313+ errorContains : "cannot use fallback height hint: " +
1314+ "close height is 0 and short channel " +
1315+ "ID block height is 0" ,
1316+ },
1317+ {
1318+ name : "zero class height - fallback height hint " +
1319+ "= conf depth" ,
1320+ classHeight : 0 ,
1321+ closeHeight : 6 ,
1322+ confDepth : 6 ,
1323+ shortChanID : lnwire.ShortChannelID {BlockHeight : 50 },
1324+ expectedHeight : 0 ,
1325+ expectError : true ,
1326+ errorContains : "fallback height hint 6 <= " +
1327+ "confirmation depth 6" ,
1328+ },
1329+ {
1330+ name : "zero class height - fallback height hint " +
1331+ "< conf depth" ,
1332+ classHeight : 0 ,
1333+ closeHeight : 3 ,
1334+ confDepth : 6 ,
1335+ shortChanID : lnwire.ShortChannelID {BlockHeight : 50 },
1336+ expectedHeight : 0 ,
1337+ expectError : true ,
1338+ errorContains : "fallback height hint 3 <= " +
1339+ "confirmation depth 6" ,
1340+ },
1341+ {
1342+ name : "zero class height - close " +
1343+ "height = 0, fallback height hint = conf depth" ,
1344+ classHeight : 0 ,
1345+ closeHeight : 0 ,
1346+ confDepth : 6 ,
1347+ shortChanID : lnwire.ShortChannelID {BlockHeight : 6 },
1348+ expectError : true ,
1349+ errorContains : "fallback height hint 6 <= " +
1350+ "confirmation depth 6" ,
1351+ },
1352+ {
1353+ name : "zero class height - close " +
1354+ "height = 0, fallback height hint < conf depth" ,
1355+ classHeight : 0 ,
1356+ closeHeight : 0 ,
1357+ confDepth : 6 ,
1358+ shortChanID : lnwire.ShortChannelID {BlockHeight : 3 },
1359+ expectedHeight : 0 ,
1360+ expectError : true ,
1361+ errorContains : "fallback height hint 3 <= " +
1362+ "confirmation depth 6" ,
1363+ },
1364+ {
1365+ name : "zero class height, fallback height is " +
1366+ "valid" ,
1367+ classHeight : 0 ,
1368+ closeHeight : 100 ,
1369+ confDepth : 6 ,
1370+ shortChanID : lnwire.ShortChannelID {BlockHeight : 50 },
1371+ // heightHint - confDepth = 100 - 6 = 94.
1372+ expectedHeight : 94 ,
1373+ expectError : false ,
1374+ },
1375+ {
1376+ name : "zero class height - close " +
1377+ "height = 0, fallback height is valid" ,
1378+ classHeight : 0 ,
1379+ closeHeight : 0 ,
1380+ confDepth : 6 ,
1381+ shortChanID : lnwire.ShortChannelID {BlockHeight : 50 },
1382+ // heightHint - confDepth = 50 - 6 = 44.
1383+ expectedHeight : 44 ,
1384+ expectError : false ,
1385+ },
1386+ }
1387+
1388+ for _ , tc := range tests {
1389+ tc := tc
1390+
1391+ t .Run (tc .name , func (t * testing.T ) {
1392+ t .Parallel ()
1393+
1394+ // Create a mock baby output.
1395+ chanPoint := & wire.OutPoint {
1396+ Hash : [chainhash .HashSize ]byte {
1397+ 0x51 , 0xb6 , 0x37 , 0xd8 , 0xfc , 0xd2 ,
1398+ 0xc6 , 0xda , 0x48 , 0x59 , 0xe6 , 0x96 ,
1399+ 0x31 , 0x13 , 0xa1 , 0x17 , 0x2d , 0xe7 ,
1400+ 0x93 , 0xe4 , 0xb7 , 0x25 , 0xb8 , 0x4d ,
1401+ 0x1f , 0xb , 0x4c , 0xf9 , 0x9e , 0xc5 ,
1402+ 0x8c , 0xe9 ,
1403+ },
1404+ Index : 9 ,
1405+ }
1406+
1407+ baby := & babyOutput {
1408+ expiry : tc .classHeight ,
1409+ kidOutput : kidOutput {
1410+ breachedOutput : breachedOutput {
1411+ outpoint : * chanPoint ,
1412+ },
1413+ originChanPoint : * chanPoint ,
1414+ },
1415+ }
1416+
1417+ cfg := & NurseryConfig {
1418+ ConfDepth : tc .confDepth ,
1419+ FetchClosedChannel : func (
1420+ chanID * wire.OutPoint ) (
1421+ * channeldb.ChannelCloseSummary ,
1422+ error ) {
1423+
1424+ if tc .fetchError != nil {
1425+ return nil , tc .fetchError
1426+ }
1427+
1428+ return & channeldb.ChannelCloseSummary {
1429+ CloseHeight : tc .closeHeight ,
1430+ ShortChanID : tc .shortChanID ,
1431+ }, nil
1432+ },
1433+ }
1434+
1435+ nursery := & UtxoNursery {
1436+ cfg : cfg ,
1437+ }
1438+
1439+ resultHeight , err := nursery .patchZeroHeightHint (
1440+ baby , tc .classHeight ,
1441+ )
1442+
1443+ if tc .expectError {
1444+ require .Error (t , err )
1445+ if tc .errorContains != "" {
1446+ require .Contains (
1447+ t , err .Error (),
1448+ tc .errorContains ,
1449+ )
1450+ }
1451+
1452+ return
1453+ }
1454+
1455+ require .NoError (t , err )
1456+ require .Equal (t , tc .expectedHeight , resultHeight )
1457+ })
1458+ }
1459+ }
0 commit comments