Skip to content
Merged
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
20 changes: 20 additions & 0 deletions src/NBitcoin.Tests/sigopcount_tests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using Moq;
using Stratis.Bitcoin.Tests.Common;
using Xunit;

Expand Down Expand Up @@ -39,5 +40,24 @@ public void GetSigOpCount()
scriptSig2 = scriptSig2 + OpcodeType.OP_1 + dummy.ToBytes() + dummy.ToBytes() + s2.ToBytes();
Assert.Equal(3U, p2sh.GetSigOpCount(KnownNetworks.Main, scriptSig2));
}

[Fact]
[Trait("Core", "Core")]
public void GetSigOpCountForFederation()
{
PubKey[] keys = Enumerable.Range(0, 3).Select(_ => new Key(true).PubKey).ToArray();
var federations = new Federations();
federations.RegisterFederation(new Federation(keys.Take(2), 1));
var network = KnownNetworks.StraxRegTest;
network.SetPrivatePropertyValue("Federations", federations);

// Test CScript::GetSigOpCount()
var s1 = new Script();
s1 = s1 + OpcodeType.OP_1 + OpcodeType.OP_FEDERATION + OpcodeType.OP_CHECKMULTISIG;
Assert.Equal(2U, s1.GetSigOpCount(true, network));
s1 = s1 + OpcodeType.OP_IF + OpcodeType.OP_CHECKSIG + OpcodeType.OP_ENDIF;
Assert.Equal(3U, s1.GetSigOpCount(true, network));
Assert.Equal(21U, s1.GetSigOpCount(false, network));
}
}
}
5 changes: 5 additions & 0 deletions src/NBitcoin/Network.cs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,11 @@ public byte[] MagicBytes
/// </summary>
public string CirrusRewardDummyAddress { get; protected set; }

/// <summary>
/// This is used for conversion transaction fee distribution transactions.
/// </summary>
public string ConversionTransactionFeeDistributionDummyAddress { get; protected set; }

/// <summary>
/// The height at which reward batching will be activated.
/// </summary>
Expand Down
10 changes: 6 additions & 4 deletions src/NBitcoin/Script.cs
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ public IList<Op> ToOps()
}
}

public uint GetSigOpCount(bool fAccurate)
public uint GetSigOpCount(bool fAccurate, Network network = null)
{
uint n = 0;
Op lastOpcode = null;
Expand All @@ -864,7 +864,9 @@ public uint GetSigOpCount(bool fAccurate)
n++;
else if (op.Code == OpcodeType.OP_CHECKMULTISIG || op.Code == OpcodeType.OP_CHECKMULTISIGVERIFY)
{
if (fAccurate && lastOpcode != null && lastOpcode.Code >= OpcodeType.OP_1 && lastOpcode.Code <= OpcodeType.OP_16)
if (fAccurate && network?.Federations != null && lastOpcode.Code == OpcodeType.OP_FEDERATION)
n += (uint)network.Federations.GetOnlyFederation().GetFederationDetails().transactionSigningKeys.Length;
else if (fAccurate && lastOpcode != null && lastOpcode.Code >= OpcodeType.OP_1 && lastOpcode.Code <= OpcodeType.OP_16)
n += (lastOpcode.PushData == null || lastOpcode.PushData.Length == 0) ? 0U : (uint)lastOpcode.PushData[0];
else
n += 20;
Expand Down Expand Up @@ -913,12 +915,12 @@ public uint GetSigOpCount(Network network, Script scriptSig)
{
// TODO: Is the network needed?
if (!IsPayToScriptHash(network))
return GetSigOpCount(true);
return GetSigOpCount(true, network);
// This is a pay-to-script-hash scriptPubKey;
// get the last item that the scriptSig
// pushes onto the stack:
bool validSig = new PayToScriptHashTemplate().CheckScriptSig(network, scriptSig, this);
return !validSig ? 0 : new Script(scriptSig.ToOps().Last().PushData).GetSigOpCount(true);
return !validSig ? 0 : new Script(scriptSig.ToOps().Last().PushData).GetSigOpCount(true, network);
// ... and return its opcount:
}

Expand Down
7 changes: 6 additions & 1 deletion src/Stratis.Bitcoin.Features.Api/NodeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public class NodeController : Controller
/// <summary>An interface implementation used to retrieve unspent transactions.</summary>
private readonly IGetUnspentTransaction getUnspentTransaction;

private readonly IInitialBlockDownloadState initialBlockDownloadState;

/// <summary>Specification of the network the node runs on.</summary>
private Network network; // Not readonly because of ValidateAddress

Expand All @@ -97,6 +99,7 @@ public NodeController(
ISelfEndpointTracker selfEndpointTracker,
IConsensusManager consensusManager,
IBlockStore blockStore,
IInitialBlockDownloadState initialBlockDownloadState,
IGetUnspentTransaction getUnspentTransaction = null,
INetworkDifficulty networkDifficulty = null,
IPooledGetUnspentTransaction pooledGetUnspentTransaction = null,
Expand Down Expand Up @@ -127,6 +130,7 @@ public NodeController(
this.consensusManager = consensusManager;
this.blockStore = blockStore;
this.getUnspentTransaction = getUnspentTransaction;
this.initialBlockDownloadState = initialBlockDownloadState;
this.networkDifficulty = networkDifficulty;
this.pooledGetUnspentTransaction = pooledGetUnspentTransaction;
this.pooledTransaction = pooledTransaction;
Expand Down Expand Up @@ -158,7 +162,8 @@ public IActionResult Status()
RunningTime = this.dateTimeProvider.GetUtcNow() - this.fullNode.StartTime,
CoinTicker = this.network.CoinTicker,
State = this.fullNode.State.ToString(),
BestPeerHeight = this.chainState.BestPeerTip?.Height
BestPeerHeight = this.chainState.BestPeerTip?.Height,
InIbd = this.initialBlockDownloadState.IsInitialBlockDownload()
};

// Add the list of features that are enabled.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using NBitcoin;
using Newtonsoft.Json;
using Stratis.Bitcoin.Utilities.JsonConverters;

namespace Stratis.Bitcoin.Features.BlockStore.Models
{
public sealed class AddressIndexerTipModel
{
[JsonConverter(typeof(UInt256JsonConverter))]
public uint256 TipHash { get; set; }

public int? TipHeight { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public InitialBlockDownloadTest()
public void InIBDIfChainTipIsNull()
{
this.chainState.ConsensusTip = null;
var blockDownloadState = new InitialBlockDownloadState(this.chainState, this.network, this.consensusSettings, this.checkpoints, this.loggerFactory.Object, DateTimeProvider.Default);
var blockDownloadState = new InitialBlockDownloadState(this.chainState, this.network, this.consensusSettings, this.checkpoints, DateTimeProvider.Default);
Assert.True(blockDownloadState.IsInitialBlockDownload());
}

Expand All @@ -43,7 +43,7 @@ public void InIBDIfBehindCheckpoint()
{
BlockHeader blockHeader = this.network.Consensus.ConsensusFactory.CreateBlockHeader();
this.chainState.ConsensusTip = new ChainedHeader(blockHeader, blockHeader.GetHash(), 1000);
var blockDownloadState = new InitialBlockDownloadState(this.chainState, this.network, this.consensusSettings, this.checkpoints, this.loggerFactory.Object, DateTimeProvider.Default);
var blockDownloadState = new InitialBlockDownloadState(this.chainState, this.network, this.consensusSettings, this.checkpoints, DateTimeProvider.Default);
Assert.True(blockDownloadState.IsInitialBlockDownload());
}

Expand All @@ -52,7 +52,7 @@ public void InIBDIfChainWorkIsLessThanMinimum()
{
BlockHeader blockHeader = this.network.Consensus.ConsensusFactory.CreateBlockHeader();
this.chainState.ConsensusTip = new ChainedHeader(blockHeader, blockHeader.GetHash(), this.checkpoints.GetLastCheckpointHeight() + 1);
var blockDownloadState = new InitialBlockDownloadState(this.chainState, this.network, this.consensusSettings, this.checkpoints, this.loggerFactory.Object, DateTimeProvider.Default);
var blockDownloadState = new InitialBlockDownloadState(this.chainState, this.network, this.consensusSettings, this.checkpoints, DateTimeProvider.Default);
Assert.True(blockDownloadState.IsInitialBlockDownload());
}

Expand All @@ -68,7 +68,7 @@ public void InIBDIfTipIsOlderThanMaxAge()
blockHeader.Time = ((uint) DateTimeOffset.Now.ToUnixTimeSeconds()) - (uint) this.network.MaxTipAge - 1;

this.chainState.ConsensusTip = new ChainedHeader(blockHeader, blockHeader.GetHash(), this.checkpoints.GetLastCheckpointHeight() + 1);
var blockDownloadState = new InitialBlockDownloadState(this.chainState, this.network, this.consensusSettings, this.checkpoints, this.loggerFactory.Object, DateTimeProvider.Default);
var blockDownloadState = new InitialBlockDownloadState(this.chainState, this.network, this.consensusSettings, this.checkpoints, DateTimeProvider.Default);
Assert.True(blockDownloadState.IsInitialBlockDownload());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class PosCoinViewRuleTests : TestPosConsensusRulesUnitTestBase
private async Task<ConsensusManager> CreateConsensusManagerAsync(Dictionary<OutPoint, UnspentOutput> unspentOutputs)
{
this.consensusSettings = new ConsensusSettings(Configuration.NodeSettings.Default(this.network));
var initialBlockDownloadState = new InitialBlockDownloadState(this.chainState.Object, this.network, this.consensusSettings, new Checkpoints(), this.loggerFactory.Object, DateTimeProvider.Default);
var initialBlockDownloadState = new InitialBlockDownloadState(this.chainState.Object, this.network, this.consensusSettings, new Checkpoints(), DateTimeProvider.Default);
var signals = new Signals.Signals(this.loggerFactory.Object, null);
var asyncProvider = new AsyncProvider(this.loggerFactory.Object, signals);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class StraxCoinViewRuleTests : TestPosConsensusRulesUnitTestBase
private async Task<ConsensusManager> CreateConsensusManagerAsync(Dictionary<OutPoint, UnspentOutput> unspentOutputs)
{
this.consensusSettings = new ConsensusSettings(Configuration.NodeSettings.Default(this.network));
var initialBlockDownloadState = new InitialBlockDownloadState(this.chainState.Object, this.network, this.consensusSettings, new Checkpoints(), this.loggerFactory.Object, DateTimeProvider.Default);
var initialBlockDownloadState = new InitialBlockDownloadState(this.chainState.Object, this.network, this.consensusSettings, new Checkpoints(), DateTimeProvider.Default);
var signals = new Signals.Signals(this.loggerFactory.Object, null);
var asyncProvider = new AsyncProvider(this.loggerFactory.Object, signals);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public static async Task<TestChainContext> CreateAsync(Network network, string d
testChainContext.Checkpoints = new Checkpoints();
testChainContext.ChainIndexer = new ChainIndexer(network);
testChainContext.ChainState = new ChainState();
testChainContext.InitialBlockDownloadState = new InitialBlockDownloadState(testChainContext.ChainState, testChainContext.Network, consensusSettings, new Checkpoints(), testChainContext.NodeSettings.LoggerFactory, testChainContext.DateTimeProvider);
testChainContext.InitialBlockDownloadState = new InitialBlockDownloadState(testChainContext.ChainState, testChainContext.Network, consensusSettings, new Checkpoints(), testChainContext.DateTimeProvider);

var inMemoryCoinView = new InMemoryCoinView(new HashHeightPair(testChainContext.ChainIndexer.Tip));
var cachedCoinView = new CachedCoinView(network, new Checkpoints(), inMemoryCoinView, DateTimeProvider.Default, testChainContext.LoggerFactory, new NodeStats(testChainContext.DateTimeProvider, testChainContext.NodeSettings, new Mock<IVersionProvider>().Object), new ConsensusSettings(testChainContext.NodeSettings));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ private long CountWitnessSignatureOperation(Script scriptPubKey, WitScript witne
if (witParams.Program.Length == 32 && witness.PushCount > 0)
{
Script subscript = Script.FromBytesUnsafe(witness.GetUnsafePush(witness.PushCount - 1));
return subscript.GetSigOpCount(true);
return subscript.GetSigOpCount(true, this.Parent.Network);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Stratis.Bitcoin.Features.ExternalApi.Models;

namespace Stratis.Bitcoin.Features.ExternalApi.ApiClients
{
public class CoinGeckoClient : IDisposable
{
public const string DummyUserAgent = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36";

private readonly ExternalApiSettings externalApiSettings;
private readonly HttpClient client;

private decimal stratisPrice = -1;
private decimal ethereumPrice = -1;

public CoinGeckoClient(ExternalApiSettings externalApiSettings)
{
this.externalApiSettings = externalApiSettings;

this.client = new HttpClient();
}

public decimal GetStratisPrice()
{
return this.stratisPrice;
}

public decimal GetEthereumPrice()
{
return this.ethereumPrice;
}

public async Task<CoinGeckoResponse> PriceDataRetrievalAsync()
{
var targetUri = new Uri(this.externalApiSettings.PriceUrl);
var requestMessage = new HttpRequestMessage(HttpMethod.Get, targetUri);
requestMessage.Headers.TryAddWithoutValidation("User-Agent", DummyUserAgent);

HttpResponseMessage resp = await this.client.SendAsync(requestMessage).ConfigureAwait(false);
string content = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);

CoinGeckoResponse response = JsonConvert.DeserializeObject<CoinGeckoResponse>(content);

if (response?.stratis == null || response?.ethereum == null)
{
return null;
}

this.stratisPrice = response.stratis.usd;
this.ethereumPrice = response.ethereum.usd;

return response;
}

public void Dispose()
{
this.client?.Dispose();
}
}
}
110 changes: 110 additions & 0 deletions src/Stratis.Bitcoin.Features.ExternalAPI/ApiClients/EtherscanClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Stratis.Bitcoin.Features.ExternalApi.Models;

namespace Stratis.Bitcoin.Features.ExternalApi.ApiClients
{
public class EtherscanClient : IDisposable
{
private readonly ExternalApiSettings externalApiSettings;
private readonly HttpClient client;

private int[] fastSamples = new[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private int[] proposeSamples = new [] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private int[] safeSamples = new[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

private bool sampled = false;
private int samplePointer = 0;

public EtherscanClient(ExternalApiSettings externalApiSettings)
{
this.externalApiSettings = externalApiSettings;

this.client = new HttpClient();
}

/// <summary>
/// Retrieves a recommended gas price based on historical measured samples.
/// </summary>
/// <remarks>We use the average of the historic proposed price.</remarks>
/// <returns>The recommended gas price in gwei. Returns -1 if price data is not yet available.</returns>
public int GetGasPrice()
{
if (!this.sampled)
{
return -1;
}

// In future this could be made more responsive to sudden changes in the recent price, and possibly use the safe price if it can be reasonably anticipated that the transaction will still confirm timeously.

// The weightings should add up to 1.
decimal fastWeighting = 0.0m;
decimal proposedWeighting = 1.0m;
decimal safeWeighting = 0.0m;

decimal totalFast = 0m;
decimal totalProposed = 0m;
decimal totalSafe = 0m;

for (int i = 0; i < this.fastSamples.Length; i++)
{
totalFast += this.fastSamples[i];
totalProposed += this.proposeSamples[i];
totalSafe += this.safeSamples[i];
}

return (int)Math.Ceiling(((totalFast * fastWeighting) + (totalProposed * proposedWeighting) + (totalSafe * safeWeighting) / this.fastSamples.Length));
}

public async Task<EtherscanGasOracleResponse> GasOracle(bool recordSamples)
{
string content = await this.client.GetStringAsync(this.externalApiSettings.EtherscanGasOracleUrl);

EtherscanGasOracleResponse response = JsonConvert.DeserializeObject<EtherscanGasOracleResponse>(content);

if (response?.result == null)
{
return null;
}

// We do not know how long the node was shut down for, so the very first sample must populate every array element (regardless of whether the caller requested sample recording).
// There would be little point storing the historic data in the key value store, for instance.
if (!this.sampled)
{
for (int i = 0; i < this.fastSamples.Length; i++)
{
this.fastSamples[i] = response.result.FastGasPrice;
this.proposeSamples[i] = response.result.ProposeGasPrice;
this.safeSamples[i] = response.result.SafeGasPrice;
}

this.sampled = true;

return response;
}

if (recordSamples)
{
this.fastSamples[this.samplePointer] = response.result.FastGasPrice;
this.proposeSamples[this.samplePointer] = response.result.ProposeGasPrice;
this.safeSamples[this.samplePointer] = response.result.SafeGasPrice;

this.samplePointer++;

if (this.samplePointer > this.fastSamples.Length)
{
this.samplePointer = 0;
}
}

return response;
}

public void Dispose()
{
this.client?.Dispose();
}
}
}
Loading