Skip to content

Commit

Permalink
- Fix over payment issue for the KAS family, due to the node acceptin…
Browse files Browse the repository at this point in the history
…g the submission of multiple identical blocks
  • Loading branch information
ceedii committed Dec 9, 2024
1 parent 1669d3f commit da79dfc
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 3 deletions.
30 changes: 27 additions & 3 deletions src/Miningcore/Blockchain/Kaspa/KaspaPayoutHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,27 @@ public virtual async Task<Block[]> ClassifyBlocksAsync(IMiningPool pool, Block[]
.Skip(i * pageSize)
.Take(pageSize)
.ToArray();

for(var j = 0; j < page.Length; j++)
{
var block = page[j];


// There is a case scenario:
// https://github.com/blackmennewstyle/miningcore/issues/191
// Sadly miners can submit different solutions which will produce the exact same blockHash for the same block
// We must handle that case carefully here, otherwise we will overpay our miners.
// Only one of these blocks must will be confirmed, the others will all become Orphans
uint totalDuplicateBlockBefore = await cf.Run(con => blockRepo.GetPoolDuplicateBlockBeforeCountByPoolHeightAndHashNoTypeAndStatusAsync(con, poolConfig.Id, Convert.ToInt64(block.BlockHeight), block.Hash, new[]
{
BlockStatus.Confirmed,
BlockStatus.Orphaned,
BlockStatus.Pending
}, block.Created));

var request = new kaspad.KaspadMessage();
request.GetBlockRequest = new kaspad.GetBlockRequestMessage
{
Hash = (string) block.Hash,
Hash = block.Hash,
IncludeTransactions = true,
};
await Guard(() => stream.RequestStream.WriteAsync(request),
Expand All @@ -181,6 +193,18 @@ await Guard(() => stream.RequestStream.WriteAsync(request),

messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin);
}
// multiple blocks with the exact same height & hash recorded in the database
else if(totalDuplicateBlockBefore > 0)
{
result.Add(block);

block.Status = BlockStatus.Orphaned;
block.Reward = 0;

logger.Info(() => $"[{LogCategory}] Block {block.BlockHeight} [{block.Hash}] classified as orphaned because we already have in the database {totalDuplicateBlockBefore} block(s) with the same height and hash");

messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin);
}
else
{
logger.Info(() => $"[{LogCategory}] Block {block.BlockHeight} uses a custom minimum confirmations calculation [{minConfirmations}]");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,4 +244,32 @@ public async Task<uint> GetPoolDuplicateBlockAfterCountByPoolHeightNoTypeAndStat
after
}));
}

public async Task<uint> GetPoolDuplicateBlockBeforeCountByPoolHeightAndHashNoTypeAndStatusAsync(IDbConnection con, string poolId, long height, string hash, BlockStatus[] status, DateTime before)
{
const string query = @"SELECT COUNT(id) FROM blocks WHERE poolid = @poolId AND blockheight = @height AND hash = @hash AND status = ANY(@status) AND created < @before";

return await con.ExecuteScalarAsync<uint>(new CommandDefinition(query, new
{
poolId,
height,
hash,
status = status.Select(x => x.ToString().ToLower()).ToArray(),
before
}));
}

public async Task<uint> GetPoolDuplicateBlockAfterCountByPoolHeightAndHashNoTypeAndStatusAsync(IDbConnection con, string poolId, long height, string hash, BlockStatus[] status, DateTime after)
{
const string query = @"SELECT COUNT(id) FROM blocks WHERE poolid = @poolId AND blockheight = @height AND hash = @hash AND status = ANY(@status) AND created > @after";

return await con.ExecuteScalarAsync<uint>(new CommandDefinition(query, new
{
poolId,
height,
hash,
status = status.Select(x => x.ToString().ToLower()).ToArray(),
after
}));
}
}
2 changes: 2 additions & 0 deletions src/Miningcore/Persistence/Repositories/IBlockRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ public interface IBlockRepository
Task<uint> GetPoolDuplicateBlockCountByPoolHeightNoTypeAndStatusAsync(IDbConnection con, string poolId, long height, BlockStatus[] status);
Task<uint> GetPoolDuplicateBlockBeforeCountByPoolHeightNoTypeAndStatusAsync(IDbConnection con, string poolId, long height, BlockStatus[] status, DateTime before);
Task<uint> GetPoolDuplicateBlockAfterCountByPoolHeightNoTypeAndStatusAsync(IDbConnection con, string poolId, long height, BlockStatus[] status, DateTime after);
Task<uint> GetPoolDuplicateBlockBeforeCountByPoolHeightAndHashNoTypeAndStatusAsync(IDbConnection con, string poolId, long height, string hash, BlockStatus[] status, DateTime before);
Task<uint> GetPoolDuplicateBlockAfterCountByPoolHeightAndHashNoTypeAndStatusAsync(IDbConnection con, string poolId, long height, string hash, BlockStatus[] status, DateTime after);
}

0 comments on commit da79dfc

Please sign in to comment.