Skip to content
This repository has been archived by the owner on Oct 20, 2023. It is now read-only.

Commit

Permalink
Merge pull request #1501 from jon4hz/eth-payout-bug
Browse files Browse the repository at this point in the history
fix: ethereum payout bug
  • Loading branch information
Oliver Weichhold authored Nov 9, 2022
2 parents 603972d + cc2d9c5 commit ed97d52
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ public class EthereumPoolPaymentProcessingConfigExtra
/// maximum amount you’re willing to pay
/// </summary>
public ulong MaxFeePerGas { get; set; }

/// <summary>
/// Search offset to start looking for uncles
/// </summary>
public uint BlockSearchOffset { get; set; } = 50;
}
1 change: 1 addition & 0 deletions src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class EthereumConstants
public const string EthereumStratumVersion = "EthereumStratum/1.0.0";
public const decimal StaticTransactionFeeReserve = 0.0025m; // in ETH
public const string BlockTypeUncle = "uncle";
public const string BlockTypeBlock = "block";

#if !DEBUG
public const int MinPayoutPeerCount = 1;
Expand Down
55 changes: 34 additions & 21 deletions src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public EthereumPayoutHandler(
private RpcClient rpcClient;
private EthereumNetworkType networkType;
private GethChainType chainType;
private const int BlockSearchOffset = 50;
private EthereumPoolConfigExtra extraPoolConfig;
private EthereumPoolPaymentProcessingConfigExtra extraConfig;

Expand Down Expand Up @@ -133,7 +132,7 @@ public async Task<Block[]> ClassifyBlocksAsync(IMiningPool pool, Block[] blocks,
block.ConfirmationProgress = 1;
block.BlockHeight = (ulong) blockInfo.Height;
block.Reward = GetBaseBlockReward(chainType, block.BlockHeight); // base reward
block.Type = "block";
block.Type = EthereumConstants.BlockTypeBlock;

if(extraConfig?.KeepUncles == false)
block.Reward += blockInfo.Uncles.Length * (block.Reward / 32); // uncle rewards
Expand All @@ -150,8 +149,8 @@ public async Task<Block[]> ClassifyBlocksAsync(IMiningPool pool, Block[] blocks,
}

// search for a block containing our block as an uncle by checking N blocks in either direction
var heightMin = block.BlockHeight - BlockSearchOffset;
var heightMax = Math.Min(block.BlockHeight + BlockSearchOffset, latestBlockHeight);
var heightMin = block.BlockHeight - extraConfig.BlockSearchOffset;
var heightMax = Math.Min(block.BlockHeight + extraConfig.BlockSearchOffset, latestBlockHeight);
var range = new List<long>();

for(var k = heightMin; k < heightMax; k++)
Expand Down Expand Up @@ -180,30 +179,44 @@ public async Task<Block[]> ClassifyBlocksAsync(IMiningPool pool, Block[] blocks,
.Select(x => x.Response.ToObject<DaemonResponses.Block>())
.FirstOrDefault(x => string.Equals(x.Miner, poolConfig.Address, StringComparison.OrdinalIgnoreCase));

if(uncle != null)
if(uncle != null)
{
// mature?
if(block.Reward == 0)
block.Reward = GetUncleReward(chainType, uncle.Height.Value, blockInfo2.Height.Value);

if(latestBlockHeight - uncle.Height.Value >= EthereumConstants.MinConfimations)
{
// mature?
if(block.Reward == 0)
block.Reward = GetUncleReward(chainType, uncle.Height.Value, blockInfo2.Height.Value);

if(latestBlockHeight - uncle.Height.Value >= EthereumConstants.MinConfimations)
// make sure there is no other uncle from that block stored in the DB already.
// when there is more than 1 uncle mined by us within the BlockSearchOffset
// range, the pool automatically assumes the first found block is the correct one.
// This is not always the case, so we need to check the DB for any other
// uncles from that block and continue searching if there any others.
// Otherwise the payouter will crash and no further blocks will be unlocked.
var duplBlock = await cf.Run(con => blockRepo.GetBlockByHeightAsync(con, poolConfig.Id, Convert.ToInt64(uncle.Height.Value)));
if(duplBlock != null && duplBlock.Type == EthereumConstants.BlockTypeUncle)
{
block.Reward = GetUncleReward(chainType, uncle.Height.Value, blockInfo2.Height.Value);
block.Status = BlockStatus.Confirmed;
block.ConfirmationProgress = 1;
block.BlockHeight = uncle.Height.Value;
block.Type = EthereumConstants.BlockTypeUncle;

logger.Info(() => $"[{LogCategory}] Unlocked uncle for block {blockInfo2.Height.Value} at height {uncle.Height.Value} worth {FormatAmount(block.Reward)}");

messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin);
logger.Info(() => $"[{LogCategory}] Found another uncle from block {uncle.Height.Value} in the DB. Continuing search for uncle.");
continue;
}

else
logger.Info(() => $"[{LogCategory}] Got immature matching uncle for block {blockInfo2.Height.Value}. Will try again.");
block.Reward = GetUncleReward(chainType, uncle.Height.Value, blockInfo2.Height.Value);
block.Status = BlockStatus.Confirmed;
block.ConfirmationProgress = 1;
block.BlockHeight = uncle.Height.Value;
block.Type = EthereumConstants.BlockTypeUncle;

break;
logger.Info(() => $"[{LogCategory}] Unlocked uncle for block {blockInfo2.Height.Value} at height {uncle.Height.Value} worth {FormatAmount(block.Reward)}");

messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin);
}

else
logger.Info(() => $"[{LogCategory}] Got immature matching uncle for block {blockInfo2.Height.Value}. Will try again.");

break;
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/Miningcore/Payments/PayoutManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ public PayoutManager(IComponentContext ctx,
private readonly ClusterConfig clusterConfig;
private readonly CompositeDisposable disposables = new();

#if !DEBUG
#if !DEBUG
private static readonly TimeSpan initialRunDelay = TimeSpan.FromMinutes(1);
#else
#else
private static readonly TimeSpan initialRunDelay = TimeSpan.FromSeconds(15);
#endif
#endif

private void AttachPool(IMiningPool pool)
{
Expand Down
41 changes: 25 additions & 16 deletions src/Miningcore/Persistence/Postgres/Repositories/BlockRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ public async Task<Block[]> PageBlocksAsync(IDbConnection con, string poolId, Blo
ORDER BY created DESC OFFSET @offset FETCH NEXT @pageSize ROWS ONLY";

return (await con.QueryAsync<Entities.Block>(new CommandDefinition(query, new
{
poolId,
status = status.Select(x => x.ToString().ToLower()).ToArray(),
offset = page * pageSize,
pageSize
}, cancellationToken: ct)))
{
poolId,
status = status.Select(x => x.ToString().ToLower()).ToArray(),
offset = page * pageSize,
pageSize
}, cancellationToken: ct)))
.Select(mapper.Map<Block>)
.ToArray();
}
Expand All @@ -67,11 +67,11 @@ public async Task<Block[]> PageBlocksAsync(IDbConnection con, BlockStatus[] stat
ORDER BY created DESC OFFSET @offset FETCH NEXT @pageSize ROWS ONLY";

return (await con.QueryAsync<Entities.Block>(new CommandDefinition(query, new
{
status = status.Select(x => x.ToString().ToLower()).ToArray(),
offset = page * pageSize,
pageSize
}, cancellationToken: ct)))
{
status = status.Select(x => x.ToString().ToLower()).ToArray(),
offset = page * pageSize,
pageSize
}, cancellationToken: ct)))
.Select(mapper.Map<Block>)
.ToArray();
}
Expand All @@ -91,11 +91,11 @@ public async Task<Block> GetBlockBeforeAsync(IDbConnection con, string poolId, B
ORDER BY created DESC FETCH NEXT 1 ROWS ONLY";

return (await con.QueryAsync<Entities.Block>(query, new
{
poolId,
before,
status = status.Select(x => x.ToString().ToLower()).ToArray()
}))
{
poolId,
before,
status = status.Select(x => x.ToString().ToLower()).ToArray()
}))
.Select(mapper.Map<Block>)
.FirstOrDefault();
}
Expand All @@ -113,4 +113,13 @@ public Task<uint> GetPoolBlockCountAsync(IDbConnection con, string poolId, Cance

return con.ExecuteScalarAsync<DateTime?>(query, new { poolId });
}

public async Task<Block> GetBlockByHeightAsync(IDbConnection con, string poolId, long height)
{
const string query = @"SELECT * FROM blocks WHERE poolid = @poolId AND blockheight = @height";

var entity = await con.QuerySingleOrDefaultAsync<Entities.Block>(new CommandDefinition(query, new { poolId, height }));

return entity == null ? null : mapper.Map<Block>(entity);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ public interface IBlockRepository
Task<Block> GetBlockBeforeAsync(IDbConnection con, string poolId, BlockStatus[] status, DateTime before);
Task<uint> GetPoolBlockCountAsync(IDbConnection con, string poolId, CancellationToken ct);
Task<DateTime?> GetLastPoolBlockTimeAsync(IDbConnection con, string poolId);
Task<Block> GetBlockByHeightAsync(IDbConnection con, string poolId, long height);
}

0 comments on commit ed97d52

Please sign in to comment.