Skip to content

Commit

Permalink
Add XDS network (#83)
Browse files Browse the repository at this point in the history
* First commit copy of stratis

* Add XDS network

* Get network to sync. and fix a consensus bug

* Fix create genesis bug

* Fix dust error
  • Loading branch information
dangershony authored Apr 15, 2020
1 parent 138ad7c commit 2273fd4
Show file tree
Hide file tree
Showing 71 changed files with 2,025 additions and 280 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ private void MockStakeChain()
{
Flags = BlockFlag.BLOCK_PROOF_OF_STAKE,
StakeModifierV2 = 0,
StakeTime = (this.chainIndexer.Tip.Header.Time + 60) & ~PosConsensusOptions.StakeTimestampMask
StakeTime = (this.chainIndexer.Tip.Header.Time + 60) & ~this.Network.Consensus.ProofOfStakeTimestampMask
});
}

Expand Down
38 changes: 20 additions & 18 deletions src/Blockcore.Features.ColdStaking/ColdStakingManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ internal Transaction GetColdStakingSetupTransaction(IWalletTransactionHandler wa
coldPubKeyHash = new BitcoinWitPubKeyAddress(coldWalletAddress, wallet.Network).Hash.AsKeyId();
destination = ColdStakingScriptTemplate.Instance.GenerateScriptPubKey(hotPubKeyHash, coldPubKeyHash);

if(payToScript)
if (payToScript)
{
HdAddress address = coldAddress ?? hotAddress;
address.RedeemScript = destination;
Expand All @@ -382,7 +382,7 @@ internal Transaction GetColdStakingSetupTransaction(IWalletTransactionHandler wa
}

// Only normal accounts should be allowed.
if (!this.GetAccounts(walletName).Any(a => a.Name == walletAccount))
if (this.GetAccounts(walletName).All(a => a.Name != walletAccount))
{
this.logger.LogTrace("(-)[COLDSTAKE_ACCOUNT_NOT_FOUND]");
throw new WalletException($"Can't find wallet account '{walletAccount}'.");
Expand All @@ -403,20 +403,22 @@ internal Transaction GetColdStakingSetupTransaction(IWalletTransactionHandler wa
{
// In the case of P2SH and P2WSH, to avoid the possibility of lose of funds
// we add an opreturn with the hot and cold key hashes to the setup transaction
// this will allow a user to recreate the redeem script of the output in case they lose
// access to one of the keys.
// The special marker will help a wallet that is tracking cold staking accounts to monitor
// the hot and cold keys, if a special marker is found then the keys are in the opreturn are checked
// this will allow a user to recreate the redeem script of the output in case they lose
// access to one of the keys.
// The special marker will help a wallet that is tracking cold staking accounts to monitor
// the hot and cold keys, if a special marker is found then the keys are in the opreturn are checked
// against the current wallet, if found and validated the wallet will track that ScriptPubKey

var opreturnKeys = new List<byte>();
opreturnKeys.AddRange(hotPubKeyHash.ToBytes());
opreturnKeys.AddRange(coldPubKeyHash.ToBytes());

context.OpReturnRawData = opreturnKeys.ToArray();
//context.OpReturnAmount = Money.Satoshis(1); // mandatory fee must be paid.

// The P2SH and P2WSH hide the cold stake keys in the script hash so the wallet cannot track
var template = this.network.StandardScriptsRegistry.GetScriptTemplates.OfType<TxNullDataTemplate>().First();
if (template.MinRequiredSatoshiFee > 0)
context.OpReturnAmount = Money.Satoshis(template.MinRequiredSatoshiFee); // mandatory fee must be paid.

// The P2SH and P2WSH hide the cold stake keys in the script hash so the wallet cannot track
// the ouputs based on the derived keys when the trx is subbmited to the network.
// So we add the output script manually.
}
Expand Down Expand Up @@ -611,7 +613,7 @@ public override void TransactionFoundInternal(Script script, Func<HdAccount, boo
/// <summary>
/// The purpose of this method is to try to identify the P2SH and P2WSH that are coldstake outputs for this wallet
/// We look for an opreturn script that is created when seting up a P2SH and P2WSH cold stake trx
/// if we find any then try to find the keys and track the script before calling in to the main wallet.
/// if we find any then try to find the keys and track the script before calling in to the main wallet.
/// </summary>
/// <inheritdoc/>
public override bool ProcessTransaction(Transaction transaction, int? blockHeight = null, Block block = null, bool isPropagated = true)
Expand All @@ -628,7 +630,7 @@ public override bool ProcessTransaction(Transaction transaction, int? blockHeigh
if (data[0].Length == 40)
{
HdAddress address = null;

Span<byte> span = data[0].AsSpan();
var hotPubKey = new KeyId(span.Slice(0, 20).ToArray());
var coldPubKey = new KeyId(span.Slice(20, 20).ToArray());
Expand All @@ -642,11 +644,11 @@ public override bool ProcessTransaction(Transaction transaction, int? blockHeigh
// Find the type of script for the opreturn (P2SH or P2WSH)
foreach (TxOut utxoInner in transaction.Outputs)
{
if(utxoInner.ScriptPubKey == destination.Hash.ScriptPubKey)
if (utxoInner.ScriptPubKey == destination.Hash.ScriptPubKey)
{
if (!this.scriptToAddressLookup.TryGetValue(destination.Hash.ScriptPubKey,out HdAddress _))
if (!this.scriptToAddressLookup.TryGetValue(destination.Hash.ScriptPubKey, out HdAddress _))
this.scriptToAddressLookup[destination.Hash.ScriptPubKey] = address;

break;
}

Expand All @@ -673,9 +675,9 @@ protected override void AddAddressToIndex(HdAddress address)
{
base.AddAddressToIndex(address);

if(address.RedeemScript != null)
if (address.RedeemScript != null)
{
// The redeem script has no indication on the script type (P2SH or P2WSH),
// The redeem script has no indication on the script type (P2SH or P2WSH),
// so we track both, add both to the indexer then.

if (!this.scriptToAddressLookup.TryGetValue(address.RedeemScript.Hash.ScriptPubKey, out HdAddress _))
Expand All @@ -686,4 +688,4 @@ protected override void AddAddressToIndex(HdAddress address)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Blockcore.Consensus;
using Blockcore.Features.BlockStore;
using Blockcore.Features.Wallet;
using Blockcore.Features.Wallet.Controllers;
using Blockcore.Features.Wallet.Interfaces;
using Blockcore.Interfaces;
using Microsoft.AspNetCore.Mvc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ private async Task<ConsensusManager> CreateConsensusManagerAsync(Dictionary<OutP
new Mock<IPeerBanning>().Object, initialBlockDownloadState, this.ChainIndexer, new Mock<IBlockPuller>().Object, new Mock<IBlockStore>().Object,
new Mock<IConnectionManager>().Object, new Mock<INodeStats>().Object, new Mock<INodeLifetime>().Object, this.consensusSettings, this.dateTimeProvider.Object);

// Mock the coinviews "FetchCoinsAsync" method. We will use the "unspentOutputs" dictionary to track spendable outputs.
// Mock the coinviews "FetchCoinsAsync" method. We will use the "unspentOutputs" dictionary to track spendable outputs.
this.coinView.Setup(d => d.FetchCoins(It.IsAny<OutPoint[]>()))
.Returns((OutPoint[] txIds) =>
{
Expand Down Expand Up @@ -106,7 +106,7 @@ private async Task<ConsensusManager> CreateConsensusManagerAsync(Dictionary<OutP
{
Flags = BlockFlag.BLOCK_PROOF_OF_STAKE,
StakeModifierV2 = 0,
StakeTime = (this.ChainIndexer.Tip.Header.Time + 60) & ~PosConsensusOptions.StakeTimestampMask
StakeTime = (this.ChainIndexer.Tip.Header.Time + 60) & ~this.network.Consensus.ProofOfStakeTimestampMask
});

// Since we are mocking the chainState ensure that the BlockStoreTip returns a usable value.
Expand Down Expand Up @@ -142,7 +142,6 @@ public async Task PosCoinViewRuleFailsAsync()

ConsensusManager consensusManager = await this.CreateConsensusManagerAsync(unspentOutputs);


// The keys used by miner 1 and miner 2.
var minerKey1 = new Key();
var minerKey2 = new Key();
Expand All @@ -166,13 +165,13 @@ public async Task PosCoinViewRuleFailsAsync()
// Create a previous transaction with scriptPubKey outputs.
Transaction prevTransaction = this.network.CreateTransaction();

uint blockTime = (this.ChainIndexer.Tip.Header.Time + 60) & ~PosConsensusOptions.StakeTimestampMask;
uint blockTime = (this.ChainIndexer.Tip.Header.Time + 60) & ~this.network.Consensus.ProofOfStakeTimestampMask;

// To avoid violating the transaction timestamp consensus rule
// we need to ensure that the transaction used for the coinstake's
// input occurs well before the block time (as the coinstake time
// is set to the block time)
if(prevTransaction is IPosTransactionWithTime posTrx)
if (prevTransaction is IPosTransactionWithTime posTrx)
posTrx.Time = blockTime - 100;

// Coins sent to miner 2.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public override Task RunAsync(RuleContext context)
/// <returns><c>true</c> if block timestamp is equal to transaction timestamp, <c>false</c> otherwise.</returns>
private bool CheckCoinStakeTimestamp(long blockTime)
{
return (blockTime & PosConsensusOptions.StakeTimestampMask) == 0;
return (blockTime & this.Parent.Network.Consensus.ProofOfStakeTimestampMask) == 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private void CheckHeaderAndCoinstakeTimes(ProvenBlockHeader header)
}
}

if ((header.Time & PosConsensusOptions.StakeTimestampMask) != 0)
if ((header.Time & this.Parent.Network.Consensus.ProofOfStakeTimestampMask) != 0)
{
this.Logger.LogTrace("(-)[BAD_TIME]");
ConsensusErrors.StakeTimeViolation.Throw();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Blockcore.Features.Consensus.Rules.UtxosetRules
/// <summary>
/// Proof of stake override for the coinview rules - BIP68, MaxSigOps and BlockReward checks.
/// </summary>
public sealed class CheckPosUtxosetRule : CheckUtxosetRule
public class CheckPosUtxosetRule : CheckUtxosetRule
{
/// <summary>Provides functionality for checking validity of PoS blocks.</summary>
private IStakeValidator stakeValidator;
Expand All @@ -21,7 +21,7 @@ public sealed class CheckPosUtxosetRule : CheckUtxosetRule
private IStakeChain stakeChain;

/// <summary>The consensus of the parent Network.</summary>
private IConsensus consensus;
protected IConsensus consensus;

/// <inheritdoc />
public override void Initialize()
Expand Down Expand Up @@ -203,7 +203,7 @@ public override Money GetProofOfWorkReward(int height)
/// </summary>
/// <param name="height">Target block height.</param>
/// <returns>Miner's coin stake reward.</returns>
public Money GetProofOfStakeReward(int height)
public virtual Money GetProofOfStakeReward(int height)
{
if (this.IsPremine(height))
return this.consensus.PremineReward;
Expand Down
20 changes: 4 additions & 16 deletions src/Blockcore.Features.Consensus/StakeValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public Target GetNextTargetRequired(IStakeChain stakeChain, ChainedHeader chaine
}

// This is used in tests to allow quickly mining blocks.
if (!proofOfStake && consensus.PowNoRetargeting)
if (!proofOfStake && consensus.PowNoRetargeting)
{
this.logger.LogTrace("(-)[NO_POW_RETARGET]:'{0}'", lastPowPosBlock.Header.Bits);
return lastPowPosBlock.Header.Bits;
Expand Down Expand Up @@ -308,11 +308,6 @@ public bool CheckStakeKernelHash(PosRuleContext context, uint headerBits, uint25
// Base target.
BigInteger target = new Target(headerBits).ToBigInteger();

// TODO: Investigate:
// The POS protocol should probably put a limit on the max amount that can be staked
// not a hard limit but a limit that allow any amount to be staked with a max weight value.
// the max weight should not exceed the max uint256 array size (array size = 32).

// Weighted target.
long valueIn = stakingCoins.Coins.TxOut.Value.Satoshi;
BigInteger weight = BigInteger.ValueOf(valueIn);
Expand All @@ -326,7 +321,8 @@ public bool CheckStakeKernelHash(PosRuleContext context, uint headerBits, uint25
{
var serializer = new BitcoinStream(ms, true);
serializer.ReadWrite(prevStakeModifier);
serializer.ReadWrite(stakingCoins.Coins.Time);
if (this.network.Consensus.PosUseTimeFieldInKernalHash) // old posv3 time field
serializer.ReadWrite(stakingCoins.Coins.Time);
serializer.ReadWrite(prevout.Hash);
serializer.ReadWrite(prevout.N);
serializer.ReadWrite(transactionTime);
Expand Down Expand Up @@ -358,13 +354,6 @@ public bool VerifySignature(UnspentOutput coin, Transaction txTo, int txToInN, S

TxIn input = txTo.Inputs[txToInN];

//if (input.PrevOut.N >= coin.Outputs.Length)
//{
// this.logger.LogTrace("(-)[OUTPUT_INCORRECT_LENGTH]");
// return false;
//}

//if (input.PrevOut.Hash != coin.TransactionId)
if (input.PrevOut.Hash != coin.OutPoint.Hash)
{
this.logger.LogTrace("(-)[INCORRECT_TX]");
Expand Down Expand Up @@ -486,5 +475,4 @@ public bool CheckStakeSignature(BlockSignature signature, uint256 blockHash, Tra
return verifyRes;
}
}
}

}
4 changes: 2 additions & 2 deletions src/Blockcore.Features.MemoryPool/MempoolPersistence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public override int GetHashCode()
/// <summary>
/// Object used for persisting memory pool transactions.
/// </summary>
internal class MempoolPersistence : IMempoolPersistence
public class MempoolPersistence : IMempoolPersistence
{
/// <summary>Current memory pool version number for persistence.</summary>
public const ulong MempoolDumpVersion = 0;
Expand Down Expand Up @@ -328,4 +328,4 @@ internal IEnumerable<MempoolPersistenceEntry> LoadFromStream(Network network, St
return toReturn;
}
}
}
}
2 changes: 1 addition & 1 deletion src/Blockcore.Features.MemoryPool/MempoolValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public class MempoolValidator : IMempoolValidator
public const bool DefaultPermitBareMultisig = true;

/// <summary>Maximum age of our tip in seconds for us to be considered current for fee estimation.</summary>
private const int MaxFeeEstimationTipAge = 3 * 60 * 60;
public const int MaxFeeEstimationTipAge = 3 * 60 * 60;

/// <summary>A lock for managing asynchronous access to memory pool.</summary>
private readonly MempoolSchedulerLock mempoolLock;
Expand Down
20 changes: 10 additions & 10 deletions src/Blockcore.Features.Miner/Staking/PosMinting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ namespace Blockcore.Features.Miner.Staking
/// </remarks>
public class PosMinting : IPosMinting
{

/// <summary>
/// Indicates the current state: idle, staking requested, staking in progress and stop staking requested.
/// </summary>
Expand Down Expand Up @@ -133,6 +132,7 @@ public enum CurrentState

/// <summary>Factory for creating loggers.</summary>
private readonly ILoggerFactory loggerFactory;

private readonly MinerSettings minerSettings;

/// <summary>Instance logger.</summary>
Expand Down Expand Up @@ -401,7 +401,7 @@ public async Task GenerateBlocksAsync(WalletSecret walletSecret, CancellationTok
blockTemplate = null;
}

uint coinstakeTimestamp = (uint)this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp() & ~PosConsensusOptions.StakeTimestampMask;
uint coinstakeTimestamp = (uint)this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp() & ~this.network.Consensus.ProofOfStakeTimestampMask;
if (coinstakeTimestamp <= this.lastCoinStakeSearchTime)
{
this.logger.LogDebug("Current coinstake time {0} is not greater than last search timestamp {1}.", coinstakeTimestamp, this.lastCoinStakeSearchTime);
Expand Down Expand Up @@ -653,7 +653,7 @@ public async Task<bool> CreateCoinstakeAsync(List<UtxoStakeDescription> utxoStak

// If the time after applying the mask is lower than minimal allowed time,
// it is simply too early for us to mine, there can't be any valid solution.
if ((coinstakeContext.StakeTimeSlot & ~PosConsensusOptions.StakeTimestampMask) < minimalAllowedTime)
if ((coinstakeContext.StakeTimeSlot & ~this.network.Consensus.ProofOfStakeTimestampMask) < minimalAllowedTime)
{
this.logger.LogTrace("(-)[TOO_EARLY_TIME_AFTER_LAST_BLOCK]:false");
return false;
Expand Down Expand Up @@ -735,7 +735,7 @@ await Task.Run(() => Parallel.ForEach(workerContexts, cwc =>
int eventuallyStakableUtxosCount = utxoStakeDescriptions.Count;
Transaction coinstakeTx = this.PrepareCoinStakeTransactions(chainTip.Height, coinstakeContext, coinstakeOutputValue, eventuallyStakableUtxosCount, ourWeight);

if(coinstakeTx is IPosTransactionWithTime posTrxn)
if (coinstakeTx is IPosTransactionWithTime posTrxn)
{
posTrxn.Time = coinstakeContext.StakeTimeSlot;
}
Expand Down Expand Up @@ -853,7 +853,7 @@ private void CoinstakeWorker(CoinstakeWorkerContext context, ChainedHeader chain
if (txTime < minimalAllowedTime)
break;

if ((txTime & PosConsensusOptions.StakeTimestampMask) != 0)
if ((txTime & this.network.Consensus.ProofOfStakeTimestampMask) != 0)
continue;

context.Logger.LogDebug("Trying with transaction time {0}.", txTime);
Expand Down Expand Up @@ -902,7 +902,7 @@ private void CoinstakeWorker(CoinstakeWorkerContext context, ChainedHeader chain

context.CoinstakeContext.CoinstakeTx.Outputs.Add(new TxOut(0, scriptPubKeyOut));
context.Result.KernelCoin = utxoStakeInfo;

context.Logger.LogDebug("Kernel accepted, coinstake input is '{0}', stopping work.", prevoutStake);
}
else context.Logger.LogDebug("Kernel found, but worker #{0} announced its kernel earlier, stopping work.", context.Result.KernelFoundIndex);
Expand Down Expand Up @@ -942,9 +942,9 @@ private bool SignTransactionInput(UtxoStakeDescription input, Transaction transa
{
if (input.Address.RedeemScript == null)
throw new MinerException("Redeem script does not match output");

var scriptCoin = ScriptCoin.Create(this.network, input.OutPoint, input.TxOut, input.Address.RedeemScript);

transactionBuilder.AddCoins(scriptCoin);
}
else
Expand Down Expand Up @@ -1177,7 +1177,7 @@ public double GetNetworkWeight()

if (stakesTime != 0) res = stakeKernelsAvg / stakesTime;

res *= PosConsensusOptions.StakeTimestampMask + 1;
res *= this.network.Consensus.ProofOfStakeTimestampMask + 1;

return res;
}
Expand Down Expand Up @@ -1227,4 +1227,4 @@ internal bool ShouldSplitStake(int stakedUtxosCount, long amountStaked, long coi
return shouldSplitCoin;
}
}
}
}
Loading

0 comments on commit 2273fd4

Please sign in to comment.