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

Ltc fix #1563

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open

Ltc fix #1563

Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/litecoin_dash_pool.json
Original file line number Diff line number Diff line change
@@ -86,7 +86,8 @@
"payoutSchemeConfig": {
"factor": 2.0
}
}
},
"addressType": "litecoin"
},
{
"id": "dash1",
3 changes: 2 additions & 1 deletion examples/litecoin_pool.json
Original file line number Diff line number Diff line change
@@ -90,6 +90,7 @@
"payoutSchemeConfig": {
"factor": 2.0
}
}
},
"addressType": "litecoin"
}]
}
5 changes: 5 additions & 0 deletions src/Miningcore/Blockchain/Bitcoin/BitcoinConstants.cs
Original file line number Diff line number Diff line change
@@ -19,6 +19,11 @@ public enum BitcoinAddressType
/// Bitcoin Cash
/// </summary>
BCash,

/// <summary>
/// Litecoin
/// </summary>
Litecoin
}

public enum BitcoinTransactionCategory
26 changes: 19 additions & 7 deletions src/Miningcore/Blockchain/Bitcoin/BitcoinJob.cs
Original file line number Diff line number Diff line change
@@ -243,10 +243,10 @@ protected virtual Transaction CreateOutputTransaction()
if(coin.HasMasterNodes)
rewardToPool = CreateMasternodeOutputs(tx, rewardToPool);

if (coin.HasFounderFee)
if(coin.HasFounderFee)
rewardToPool = CreateFounderOutputs(tx, rewardToPool);

if (coin.HasMinerFund)
if(coin.HasMinerFund)
rewardToPool = CreateMinerFundOutputs(tx, rewardToPool);

// Remaining amount goes to pool
@@ -410,6 +410,18 @@ protected virtual byte[] SerializeBlock(byte[] header, byte[] coinbase)
if(isPoS)
bs.ReadWrite((byte) 0);

// if pool supports MWEB, we have to append the MWEB data to the block
// https://github.com/litecoin-project/litecoin/blob/0.21/doc/mweb/mining-changes.md
if(coin.HasMWEB)
{
var separator = new byte[] { 0x01 };
var mweb = BlockTemplate.Extra.SafeExtensionDataAs<MwebBlockTemplateExtra>();
var mwebRaw = mweb.Mweb.HexToByteArray();

bs.ReadWrite(ref separator);
bs.ReadWrite(ref mwebRaw);
}

return stream.ToArray();
}
}
@@ -492,10 +504,10 @@ protected virtual Money CreateMasternodeOutputs(Transaction tx, Money reward)

protected virtual Money CreateFounderOutputs(Transaction tx, Money reward)
{
if (founderParameters.Founder != null)
if(founderParameters.Founder != null)
{
Founder[] founders;
if (founderParameters.Founder.Type == JTokenType.Array)
if(founderParameters.Founder.Type == JTokenType.Array)
founders = founderParameters.Founder.ToObject<Founder[]>();
else
founders = new[] { founderParameters.Founder.ToObject<Founder>() };
@@ -529,7 +541,7 @@ protected virtual Money CreateMinerFundOutputs(Transaction tx, Money reward)
{
var payeeReward = minerFundParameters.MinimumValue;

if (!string.IsNullOrEmpty(minerFundParameters.Addresses?.FirstOrDefault()))
if(!string.IsNullOrEmpty(minerFundParameters.Addresses?.FirstOrDefault()))
{
var payeeAddress = BitcoinUtils.AddressToDestination(minerFundParameters.Addresses[0], network);
tx.Outputs.Add(payeeReward, payeeAddress);
@@ -612,10 +624,10 @@ public void Init(BlockTemplate blockTemplate, string jobId,
if(coin.HasPayee)
payeeParameters = BlockTemplate.Extra.SafeExtensionDataAs<PayeeBlockTemplateExtra>();

if (coin.HasFounderFee)
if(coin.HasFounderFee)
founderParameters = BlockTemplate.Extra.SafeExtensionDataAs<FounderBlockTemplateExtra>();

if (coin.HasMinerFund)
if(coin.HasMinerFund)
minerFundParameters = BlockTemplate.Extra.SafeExtensionDataAs<MinerFundTemplateExtra>("coinbasetxn", "minerfund");

this.coinbaseHasher = coinbaseHasher;
49 changes: 47 additions & 2 deletions src/Miningcore/Blockchain/Bitcoin/BitcoinJobManager.cs
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using Org.BouncyCastle.Crypto.Parameters;

namespace Miningcore.Blockchain.Bitcoin;

@@ -33,17 +34,61 @@ protected override object[] GetBlockTemplateParams()
{
var result = base.GetBlockTemplateParams();

if(coin.HasMWEB)
{
result = new object[]
{
new
{
rules = new[] {"segwit", "mweb"},
}
};
}

if(coin.BlockTemplateRpcExtraParams != null)
{
if(coin.BlockTemplateRpcExtraParams.Type == JTokenType.Array)
result = result.Concat(coin.BlockTemplateRpcExtraParams.ToObject<object[]>() ?? Array.Empty<object>()).ToArray();
else
result = result.Concat(new []{ coin.BlockTemplateRpcExtraParams.ToObject<object>()}).ToArray();
result = result.Concat(new[] { coin.BlockTemplateRpcExtraParams.ToObject<object>() }).ToArray();
}

return result;
}

protected override async Task EnsureDaemonsSynchedAsync(CancellationToken ct)
{
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));

var syncPendingNotificationShown = false;

do
{
var response = await rpc.ExecuteAsync<BlockTemplate>(logger,
BitcoinCommands.GetBlockTemplate, ct, GetBlockTemplateParams());

var isSynched = response.Error == null;

if(isSynched)
{
logger.Info(() => "All daemons synched with blockchain");
break;
}
else
{
logger.Debug(() => $"Daemon reports error: {response.Error?.Message}");
}

if(!syncPendingNotificationShown)
{
logger.Info(() => "Daemon is still syncing with network. Manager will be started once synced.");
syncPendingNotificationShown = true;
}

await ShowDaemonSyncProgressAsync(ct);
} while(await timer.WaitForNextTickAsync(ct));
}

protected async Task<RpcResponse<BlockTemplate>> GetBlockTemplateAsync(CancellationToken ct)
{
var result = await rpc.ExecuteAsync<BlockTemplate>(logger,
@@ -71,7 +116,7 @@ protected override void PostChainIdentifyConfigure()
if(poolConfig.EnableInternalStratum == true && coin.HeaderHasherValue is IHashAlgorithmInit hashInit)
{
if(!hashInit.DigestInit(poolConfig))
logger.Error(()=> $"{hashInit.GetType().Name} initialization failed");
logger.Error(() => $"{hashInit.GetType().Name} initialization failed");
}
}

26 changes: 17 additions & 9 deletions src/Miningcore/Blockchain/Bitcoin/BitcoinJobManagerBase.cs
Original file line number Diff line number Diff line change
@@ -262,7 +262,7 @@ protected async Task<SubmitResult> SubmitBlockAsync(Share share, string blockHex
? new RpcRequest(BitcoinCommands.SubmitBlock, new[] { blockHex })
: new RpcRequest(BitcoinCommands.GetBlockTemplate, new { mode = "submit", data = blockHex });

var batch = new []
var batch = new[]
{
submitBlockRequest,
new RpcRequest(BitcoinCommands.GetBlock, new[] { share.BlockHash })
@@ -381,7 +381,12 @@ protected override async Task<bool> AreDaemonsHealthyAsync(CancellationToken ct)

var response = await rpc.ExecuteAsync<BlockchainInfo>(logger, BitcoinCommands.GetBlockchainInfo, ct);

return response.Error == null;
if(response.Error != null)
{
logger.Error(() => $"Daemon reports: {response.Error.Message}");
return false;
}
return true;
}

protected override async Task<bool> AreDaemonsConnectedAsync(CancellationToken ct)
@@ -466,17 +471,17 @@ protected override async Task PostStartInitAsync(CancellationToken ct)
PostChainIdentifyConfigure();

// ensure pool owns wallet
if(validateAddressResponse is not {IsValid: true})
if(validateAddressResponse is not { IsValid: true })
throw new PoolStartupException($"Daemon reports pool-address '{poolConfig.Address}' as invalid", poolConfig.Id);

isPoS = poolConfig.Template is BitcoinTemplate {IsPseudoPoS: true} ||
isPoS = poolConfig.Template is BitcoinTemplate { IsPseudoPoS: true } ||
(difficultyResponse.Values().Any(x => x.Path == "proof-of-stake" && !difficultyResponse.Values().Any(x => x.Path == "proof-of-work")));

// Create pool address script from response
if(!isPoS)
{
if(extraPoolConfig != null && extraPoolConfig.AddressType != BitcoinAddressType.Legacy)
logger.Info(()=> $"Interpreting pool address {poolConfig.Address} as type {extraPoolConfig?.AddressType.ToString()}");
logger.Info(() => $"Interpreting pool address {poolConfig.Address} as type {extraPoolConfig?.AddressType.ToString()}");

poolAddressDestination = AddressToDestination(poolConfig.Address, extraPoolConfig?.AddressType);
}
@@ -488,8 +493,8 @@ protected override async Task PostStartInitAsync(CancellationToken ct)
if(clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true)
{
// ensure pool owns wallet
if(validateAddressResponse is {IsMine: false} && addressInfoResponse is {IsMine: false})
logger.Warn(()=> $"Daemon does not own pool-address '{poolConfig.Address}'");
if(validateAddressResponse is { IsMine: false } && addressInfoResponse is { IsMine: false })
logger.Warn(() => $"Daemon does not own pool-address '{poolConfig.Address}'");
}

// update stats
@@ -512,7 +517,7 @@ protected override async Task PostStartInitAsync(CancellationToken ct)
// Periodically update network stats
Observable.Interval(TimeSpan.FromMinutes(10))
.Select(_ => Observable.FromAsync(() =>
Guard(()=> !hasLegacyDaemon ? UpdateNetworkStatsAsync(ct) : UpdateNetworkStatsLegacyAsync(ct),
Guard(() => !hasLegacyDaemon ? UpdateNetworkStatsAsync(ct) : UpdateNetworkStatsLegacyAsync(ct),
ex => logger.Error(ex))))
.Concat()
.Subscribe();
@@ -534,6 +539,9 @@ protected virtual IDestination AddressToDestination(string address, BitcoinAddre
case BitcoinAddressType.BCash:
return BitcoinUtils.BCashAddressToDestination(poolConfig.Address, network);

case BitcoinAddressType.Litecoin:
return BitcoinUtils.LitecoinAddressToDestination(poolConfig.Address, network);

default:
return BitcoinUtils.AddressToDestination(poolConfig.Address, network);
}
@@ -573,7 +581,7 @@ public virtual async Task<bool> ValidateAddressAsync(string address, Cancellatio

var result = await rpc.ExecuteAsync<ValidateAddressResponse>(logger, BitcoinCommands.ValidateAddress, ct, new[] { address });

return result.Response is {IsValid: true};
return result.Response is { IsValid: true };
}

#endregion // API-Surface
13 changes: 13 additions & 0 deletions src/Miningcore/Blockchain/Bitcoin/BitcoinUtils.cs
Original file line number Diff line number Diff line change
@@ -41,4 +41,17 @@ public static IDestination BCashAddressToDestination(string address, Network exp
var trashAddress = bcash.Parse<NBitcoin.Altcoins.BCash.BTrashPubKeyAddress>(address);
return trashAddress.ScriptPubKey.GetDestinationAddress(bcash);
}

public static IDestination LitecoinAddressToDestination(string address, Network expectedNetwork)
{
var litecoin = NBitcoin.Altcoins.Litecoin.Instance.GetNetwork(expectedNetwork.ChainName);
var encoder = litecoin.GetBech32Encoder(Bech32Type.WITNESS_PUBKEY_ADDRESS, true);

var decoded = encoder.Decode(address, out var witVersion);
var result = new WitKeyId(decoded);

Debug.Assert(result.GetAddress(litecoin).ToString() == address);
return result;
}
}

6 changes: 6 additions & 0 deletions src/Miningcore/Blockchain/Bitcoin/DaemonResponses/Mweb.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Miningcore.Blockchain.Bitcoin.DaemonResponses;

public class MwebBlockTemplateExtra
{
public string Mweb { get; set; }
}
11 changes: 7 additions & 4 deletions src/Miningcore/Configuration/ClusterConfig.cs
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ public enum CoinFamily

[EnumMember(Value = "conceal")]
Conceal,

[EnumMember(Value = "cryptonote")]
Cryptonote,

@@ -214,6 +214,9 @@ public class BitcoinNetworkParams
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public JToken BlockTemplateRpcExtraParams { get; set; }

[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool HasMWEB { get; set; }

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public Dictionary<string, BitcoinNetworkParams> Networks { get; set; }

@@ -438,13 +441,13 @@ public partial class ConcealCoinTemplate : CoinTemplate
/// </summary>
[JsonProperty(Order = -4, DefaultValueHandling = DefaultValueHandling.Include)]
public int HashVariant { get; set; }

/// <summary>
/// Conceal network hashrate = `Difficulty / DifficultyTarget`
/// See: parameter -> DIFFICULTY_TARGET in src/CryptoNoteConfig.h
/// </summary>
public ulong DifficultyTarget { get; set; }

/// <summary>
/// Smallest unit for Blockreward formatting
/// </summary>
@@ -830,7 +833,7 @@ public partial class ClusterPaymentProcessingConfig
/// <summary>
/// Indentifier used in coinbase transactions to identify the pool
/// </summary>
public string CoinbaseString { get; set; }
public string CoinbaseString { get; set; }
}

public partial class PersistenceConfig
Loading