Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate a PoS/PoW scaling factor, long PoW diff adjustment intervals #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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: 3 additions & 0 deletions src/chainparams/MainNet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using NBitcoin;
using NBitcoin.BouncyCastle.math;
using NBitcoin.DataEncoders;
using UnnamedCoin.Bitcoin.Features.Consensus;
using UnnamedCoin.Bitcoin.Features.Consensus.Rules.CommonRules;
using UnnamedCoin.Bitcoin.Features.Consensus.Rules.ProvenHeaderRules;
using UnnamedCoin.Bitcoin.Features.MemoryPool.Rules;
Expand Down Expand Up @@ -100,6 +101,8 @@ public MainNet()
proofOfStakeReward: Money.Coins(50),
posEmptyCoinbase: false);

this.Consensus.LongPosPowPowDifficultyAdjustments = true;
this.Consensus.UsePosPowScaling = true;

this.StandardScriptsRegistry = new MainNetStandardScriptsRegistry();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Diagnostics;
using NBitcoin;
using NBitcoin.BouncyCastle.math;

namespace UnnamedCoin.Bitcoin.Features.Consensus
{
public class PosPowScaling
{
/// <summary>
/// Gets a factor to compare PoS and PoW difficulties based on past averages.
/// </summary>
/// <param name="consensus">Consensus</param>
/// <param name="tip">The current tip</param>
/// <param name="stakeChain">StakeChain</param>
/// <returns>The posPowAdjustmentFactor</returns>
public static double GetPosPowAdjustmentFactor(IConsensus consensus, ChainedHeader tip, IStakeChain stakeChain)
{
var posPoWScalingAdjustmentInterval = GetPoSPoWScalingAdjustmentInterval(consensus); // 2016 blocks

if (tip.Height <= posPoWScalingAdjustmentInterval)
{
return 1.0; // for the first 2016 blocks, do not scale (factor is 1.0)
}

// the first time we are here is block 2017
int posBlockCount = 0;
int powBlockCount = 0;

BigInteger sumPosTarget = BigInteger.Zero;
BigInteger sumPowTarget = BigInteger.Zero;

var height = tip.Height;

while (height % posPoWScalingAdjustmentInterval != 0) // go back till last scaling adjustment
{
height--;
}

int blocksToCount = posPoWScalingAdjustmentInterval;
var it = tip;

while (blocksToCount-- > 0)
{
BlockStake blockStake = stakeChain.Get(it.HashBlock);

if (blockStake.IsProofOfStake())
{
posBlockCount++;
sumPosTarget.Add(it.Header.Bits.ToBigInteger());
it.Header.Bits.ToBigInteger();
}
else
{
powBlockCount++;
sumPowTarget.Add(it.Header.Bits.ToBigInteger());
it.Header.Bits.ToBigInteger();
}

it = it.Previous;
}

Debug.Assert(posBlockCount + powBlockCount == posPoWScalingAdjustmentInterval, "invalid sum");
BigInteger avgPosTarget = sumPosTarget.Divide(BigInteger.ValueOf(posBlockCount));
BigInteger avgPowTarget = sumPowTarget.Divide(BigInteger.ValueOf(powBlockCount));

double posPowAdjustmentFactor = avgPosTarget.Multiply(BigInteger.ValueOf(1000)).Divide(avgPowTarget).LongValue / 1000.0;
return posPowAdjustmentFactor;
}

/// <summary>
/// Calculate the PoS / PoW scaling adjustment interval in blocks based on settings defined in <see cref="IConsensus" />.
/// Note that this comes down to the same points as the difficulty adjustment.
/// </summary>
/// <returns>The PoS / PoW scaling adjustment interval in blocks.</returns>
static int GetPoSPoWScalingAdjustmentInterval(IConsensus consensus)
{
// ‭1,209,600‬ / 600 = 2016
return (int)consensus.PowTargetTimespan.TotalSeconds / (int)consensus.PowTargetSpacing.TotalSeconds;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ public BlockTemplate BuildPosBlock(ChainedHeader chainTip, Script script)
/// <inheritdoc />
public BlockTemplate BuildPowBlock(ChainedHeader chainTip, Script script)
{
if (this.network.Consensus.IsProofOfStake)
// when building a PoW block in a permanent PoS + PoW network (PosPowOptions is not null), use the Bitcoin two week difficulty retarget.
if (this.network.Consensus.IsProofOfStake && !this.network.Consensus.LongPosPowPowDifficultyAdjustments)
return this.posPowBlockDefinition.Build(chainTip, script);

// XDS will choose this execution path.
return this.powBlockDefinition.Build(chainTip, script);
}

Expand Down
11 changes: 8 additions & 3 deletions src/components/NBitcoin/ChainIndexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ public ChainIndexer(Network network) : this()
{
this.Network = network;

Initialize(new ChainedHeader(network.GetGenesis().Header, network.GetGenesis().GetHash(), 0));
ChainedHeader chainedHeader;
if (network.Consensus.UsePosPowScaling)
chainedHeader = new PosPowChainedHeader(network.GetGenesis().Header, network.GetGenesis().GetHash(), 0, network, this);
else
chainedHeader = new ChainedHeader(network.GetGenesis().Header, network.GetGenesis().GetHash(), 0);
Initialize(chainedHeader);
}

public ChainIndexer(Network network, ChainedHeader chainedHeader) : this()
Expand Down Expand Up @@ -187,7 +192,7 @@ public virtual IEnumerable<ChainedHeader> EnumerateAfter(ChainedHeader block)
}
}


public void Add(ChainedHeader addTip)
{
lock (this.lockObject)
Expand All @@ -202,7 +207,7 @@ public void Add(ChainedHeader addTip)
}
}


public void Remove(ChainedHeader removeTip)
{
lock (this.lockObject)
Expand Down
7 changes: 5 additions & 2 deletions src/components/NBitcoin/ChainedHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public class ChainedHeader
static readonly BigInteger Pow256 = BigInteger.ValueOf(2).Pow(256);

/// <summary>Integer representation of the <see cref="ChainWork" />.</summary>
BigInteger chainWork;
protected BigInteger chainWork;

/// <summary>
/// Constructs a chained block.
Expand Down Expand Up @@ -150,6 +150,9 @@ public ChainedHeader(BlockHeader header, uint256 headerHash, int height) : this(
/// <summary>Total amount of work in the chain up to and including this block.</summary>
public uint256 ChainWork => Target.ToUInt256(this.chainWork);

/// <summary>Total amount of work in the chain up to and including this block.</summary>
public BigInteger ChainWorkBigInteger => this.chainWork;

/// <inheritdoc cref="BlockDataAvailabilityState" />
public BlockDataAvailabilityState BlockDataAvailability { get; set; }

Expand Down Expand Up @@ -187,7 +190,7 @@ public bool IsReferenceConnected
/// <summary>
/// Calculates the total amount of work in the chain up to and including this block.
/// </summary>
void CalculateChainWork()
protected virtual void CalculateChainWork()
{
this.chainWork = (this.Previous == null ? BigInteger.Zero : this.Previous.chainWork).Add(GetBlockProof());
}
Expand Down
7 changes: 7 additions & 0 deletions src/components/NBitcoin/Consensus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,5 +161,12 @@ public Consensus(

/// <inheritdoc />
public List<Type> MempoolRules { get; set; }

/// <inheritdoc />
public bool UsePosPowScaling { get; set; }

/// <inheritdoc />
public bool LongPosPowPowDifficultyAdjustments { get; set; }

}
}
6 changes: 6 additions & 0 deletions src/components/NBitcoin/IConsensus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,11 @@ public interface IConsensus

/// <summary>Group of mempool validation rules used by the given network.</summary>
List<Type> MempoolRules { get; set; }

/// <summary>PoS-to-PoW economy balancing.</summary>
bool UsePosPowScaling { get; set; }

/// <summary>Use long difficulty adjustment intervals in PoS + PoW networks.</summary>
bool LongPosPowPowDifficultyAdjustments { get; set; }
}
}
115 changes: 115 additions & 0 deletions src/components/NBitcoin/PosPowChainedHeader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using NBitcoin.BouncyCastle.math;

namespace NBitcoin
{
public class PosPowChainedHeader : ChainedHeader
{
readonly Network network;
readonly ChainIndexer chainIndexer;

public PosPowChainedHeader(BlockHeader header, uint256 headerHash, ChainedHeader previous, Network network) : base(header, headerHash, previous)
{
this.IsProofOfStake = ((ProvenBlockHeader)header).Coinstake.IsCoinStake;
this.network = network;
}

public PosPowChainedHeader(BlockHeader header, uint256 headerHash, int height, Network network, ChainIndexer chainIndexer) : base(header, headerHash, height)
{
this.IsProofOfStake = ((ProvenBlockHeader)header).Coinstake.IsCoinStake;
this.network = network;
this.chainIndexer = chainIndexer;
}

protected override void CalculateChainWork()
{
if (!this.IsProofOfStake)
this.chainWork = (this.Previous == null ? BigInteger.Zero : this.Previous.ChainWorkBigInteger).Add(GetBlockProof());
else
{
var factor = GetPosPowAdjustmentFactor(this.network.Consensus, this, this.chainIndexer);
var blockProof = GetBlockProof();
var adjusted =
this.chainWork = blockProof.Divide(factor);
}

}

public bool IsProofOfStake { get; }

/// <summary>
/// Gets a factor to compare PoS and PoW difficulties based on past averages.
/// </summary>
/// <param name="consensus">Consensus</param>
/// <param name="tip">The current tip</param>
/// <param name="chainIndexer">ChainIndexer</param>
/// <returns>The posPowAdjustmentFactor</returns>
public static BigInteger GetPosPowAdjustmentFactor(IConsensus consensus, ChainedHeader tip, ChainIndexer chainIndexer)
{
var posPoWScalingAdjustmentInterval = GetPoSPoWScalingAdjustmentInterval(consensus); // 2016 blocks

if (tip.Height <= posPoWScalingAdjustmentInterval)
{
return BigInteger.One; // for the first 2016 blocks, do not scale (factor is 1.0)
}

// the first time we are here is block 2017
int posBlockCount = 0;
int powBlockCount = 0;

BigInteger sumPosTarget = BigInteger.Zero;
BigInteger sumPowTarget = BigInteger.Zero;

var height = tip.Height;

while (height % posPoWScalingAdjustmentInterval != 0) // go back till last scaling adjustment
{
height--;
}

int blocksToCount = posPoWScalingAdjustmentInterval;
var it = tip;

while (blocksToCount-- > 0)
{
var posPowChainedHeader = (PosPowChainedHeader) chainIndexer.GetHeader(it.HashBlock);

if (posPowChainedHeader.IsProofOfStake)
{
posBlockCount++;
sumPosTarget.Add(it.Header.Bits.ToBigInteger());
it.Header.Bits.ToBigInteger();
}
else
{
powBlockCount++;
sumPowTarget.Add(it.Header.Bits.ToBigInteger());
it.Header.Bits.ToBigInteger();
}

it = it.Previous;
}

Debug.Assert(posBlockCount + powBlockCount == posPoWScalingAdjustmentInterval, "invalid sum");
BigInteger avgPosTarget = sumPosTarget.Divide(BigInteger.ValueOf(posBlockCount));
BigInteger avgPowTarget = sumPowTarget.Divide(BigInteger.ValueOf(powBlockCount));

var posPowAdjustmentFactor = avgPosTarget.Divide(avgPowTarget);
return posPowAdjustmentFactor;
}

/// <summary>
/// Calculate the PoS / PoW scaling adjustment interval in blocks based on settings defined in <see cref="IConsensus" />.
/// Note that this comes down to the same points as the difficulty adjustment.
/// </summary>
/// <returns>The PoS / PoW scaling adjustment interval in blocks.</returns>
static int GetPoSPoWScalingAdjustmentInterval(IConsensus consensus)
{
// ‭1,209,600‬ / 600 = 2016
return (int)consensus.PowTargetTimespan.TotalSeconds / (int)consensus.PowTargetSpacing.TotalSeconds;
}
}
}