diff --git a/src/Blockcore/Consensus/ScriptAddressReader.cs b/src/Blockcore/Consensus/ScriptAddressReader.cs index 80ca7d3a7..0950f5a4f 100644 --- a/src/Blockcore/Consensus/ScriptAddressReader.cs +++ b/src/Blockcore/Consensus/ScriptAddressReader.cs @@ -9,29 +9,36 @@ namespace Blockcore.Consensus public class ScriptAddressReader : IScriptAddressReader { /// - public string GetAddressFromScriptPubKey(Network network, Script script) + public ScriptAddressResult GetAddressFromScriptPubKey(Network network, Script script) { ScriptTemplate scriptTemplate = network.StandardScriptsRegistry.GetTemplateFromScriptPubKey(script); - string destinationAddress = null; + var destinationAddress = new ScriptAddressResult(); switch (scriptTemplate?.Type) { // Pay to PubKey can be found in outputs of staking transactions. case TxOutType.TX_PUBKEY: PubKey pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(script); - destinationAddress = pubKey.GetAddress(network).ToString(); + destinationAddress.Address = pubKey.GetAddress(network).ToString(); break; // Pay to PubKey hash is the regular, most common type of output. case TxOutType.TX_PUBKEYHASH: - destinationAddress = script.GetDestinationAddress(network).ToString(); + destinationAddress.Address = script.GetDestinationAddress(network).ToString(); break; + case TxOutType.TX_SCRIPTHASH: - destinationAddress = script.GetDestinationAddress(network).ToString(); + destinationAddress.Address = script.GetDestinationAddress(network).ToString(); + break; + + case TxOutType.TX_COLDSTAKE: + destinationAddress = this.GetColdStakeAddresses(network, script); break; + case TxOutType.TX_SEGWIT: - destinationAddress = script.GetDestinationAddress(network).ToString(); + destinationAddress.Address = script.GetDestinationAddress(network).ToString(); break; + case TxOutType.TX_NONSTANDARD: case TxOutType.TX_MULTISIG: case TxOutType.TX_NULL_DATA: @@ -40,5 +47,15 @@ public string GetAddressFromScriptPubKey(Network network, Script script) return destinationAddress; } + + public ScriptAddressResult GetColdStakeAddresses(Network network, Script script) + { + var destinationAddressResult = script.GetColdStakeDestinationAddress(network); + return new ScriptAddressResult() + { + HotAddress = destinationAddressResult.hotAddress.ToString(), + ColdAddress = destinationAddressResult.coldAddress.ToString() + }; + } } -} +} \ No newline at end of file diff --git a/src/Blockcore/Consensus/ScriptAddressResult.cs b/src/Blockcore/Consensus/ScriptAddressResult.cs new file mode 100644 index 000000000..9aa1dede4 --- /dev/null +++ b/src/Blockcore/Consensus/ScriptAddressResult.cs @@ -0,0 +1,42 @@ +namespace Blockcore.Consensus +{ + /// + /// The script result address. + /// + public class ScriptAddressResult + { + /// + /// Will return the script public address if exists, Otherwise returns + /// + public static implicit operator string(ScriptAddressResult scriptAddressResult) + { + return scriptAddressResult.Address.ToString(); + } + + /// + /// If Address, Hot and Cold addresses are all empty, it will return true, otherwise false. + /// + public bool IsNullOrEmpty() + { + return string.IsNullOrEmpty(this.Address) && + string.IsNullOrEmpty(this.HotAddress) && + string.IsNullOrEmpty(this.ColdAddress); + } + + /// + /// Will return the script public address if exists, Otherwise returns + /// + public string Address { get; set; } = string.Empty; + + /// + /// Will return the script hot public address if exists, Otherwise returns + /// + public string HotAddress { get; set; } = string.Empty; + + /// + /// Will return the script cold public address if exists, Otherwise returns + /// + public string ColdAddress { get; set; } = string.Empty; + + } +} diff --git a/src/Features/Blockcore.Features.ColdStaking/ColdStakingScriptSigParameters.cs b/src/Blockcore/Consensus/ScriptInfo/ColdStakingScriptSigParameters.cs similarity index 82% rename from src/Features/Blockcore.Features.ColdStaking/ColdStakingScriptSigParameters.cs rename to src/Blockcore/Consensus/ScriptInfo/ColdStakingScriptSigParameters.cs index 94f1daa4a..0fdc542a5 100644 --- a/src/Features/Blockcore.Features.ColdStaking/ColdStakingScriptSigParameters.cs +++ b/src/Blockcore/Consensus/ScriptInfo/ColdStakingScriptSigParameters.cs @@ -1,8 +1,7 @@ -using Blockcore.Consensus.ScriptInfo; -using Blockcore.Consensus.TransactionInfo; +using Blockcore.Consensus.TransactionInfo; using NBitcoin; -namespace Blockcore.Features.ColdStaking +namespace Blockcore.Consensus.ScriptInfo { /// /// The scriptSig parameters used for cold staking script. diff --git a/src/Features/Blockcore.Features.ColdStaking/ColdStakingScriptTemplate.cs b/src/Blockcore/Consensus/ScriptInfo/ColdStakingScriptTemplate.cs similarity index 99% rename from src/Features/Blockcore.Features.ColdStaking/ColdStakingScriptTemplate.cs rename to src/Blockcore/Consensus/ScriptInfo/ColdStakingScriptTemplate.cs index 8a7b6f563..87be52743 100644 --- a/src/Features/Blockcore.Features.ColdStaking/ColdStakingScriptTemplate.cs +++ b/src/Blockcore/Consensus/ScriptInfo/ColdStakingScriptTemplate.cs @@ -1,15 +1,13 @@ using System; using System.Linq; -using Blockcore.Consensus; using Blockcore.Consensus.BlockInfo; -using Blockcore.Consensus.ScriptInfo; using Blockcore.Consensus.TransactionInfo; using Blockcore.Networks; using Blockcore.Utilities; using NBitcoin; using static Blockcore.Consensus.ScriptInfo.OpcodeType; -namespace Blockcore.Features.ColdStaking +namespace Blockcore.Consensus.ScriptInfo { /// /// Script template for the cold staking script. diff --git a/src/Blockcore/Consensus/ScriptInfo/Script.cs b/src/Blockcore/Consensus/ScriptInfo/Script.cs index c7040987c..aeab1a54f 100644 --- a/src/Blockcore/Consensus/ScriptInfo/Script.cs +++ b/src/Blockcore/Consensus/ScriptInfo/Script.cs @@ -1011,6 +1011,24 @@ public BitcoinAddress GetDestinationAddress(Network network) return dest == null ? null : dest.GetAddress(network); } + /// + /// Extract ColdStakingScript addresses from scriptPubKey + /// + /// + /// Both hot and cold BitcoinAddress addresses + public (BitcoinAddress hotAddress, BitcoinAddress coldAddress) GetColdStakeDestinationAddress(Network network) + { + bool hasAddresses = ColdStakingScriptTemplate.Instance.ExtractScriptPubKeyParameters(this, out KeyId hotKeyId, out KeyId coldKeyId); + if (hasAddresses) + { + return (hotKeyId.GetAddress(network), coldKeyId.GetAddress(network)); + } + else + { + return (null, null); + } + } + /// /// Extract P2SH/P2PH/P2WSH/P2WPKH id from scriptPubKey /// diff --git a/src/Blockcore/Interfaces/IScriptAddressReader.cs b/src/Blockcore/Interfaces/IScriptAddressReader.cs index 28458058c..5b0549c92 100644 --- a/src/Blockcore/Interfaces/IScriptAddressReader.cs +++ b/src/Blockcore/Interfaces/IScriptAddressReader.cs @@ -1,4 +1,5 @@ -using Blockcore.Consensus.ScriptInfo; +using Blockcore.Consensus; +using Blockcore.Consensus.ScriptInfo; using Blockcore.Networks; using NBitcoin; @@ -12,9 +13,6 @@ public interface IScriptAddressReader /// /// Extracts an address from a given Script, if available. Otherwise returns /// - /// - /// - /// - string GetAddressFromScriptPubKey(Network network, Script script); + ScriptAddressResult GetAddressFromScriptPubKey(Network network, Script script); } -} +} \ No newline at end of file diff --git a/src/Features/Blockcore.Features.BlockStore/AddressIndexing/AddressIndexer.cs b/src/Features/Blockcore.Features.BlockStore/AddressIndexing/AddressIndexer.cs index a337b3f78..194ff56e9 100644 --- a/src/Features/Blockcore.Features.BlockStore/AddressIndexing/AddressIndexer.cs +++ b/src/Features/Blockcore.Features.BlockStore/AddressIndexing/AddressIndexer.cs @@ -447,15 +447,22 @@ private bool ProcessBlock(Block block, ChainedHeader header) if (amountSpent == 0) continue; - string address = this.scriptAddressReader.GetAddressFromScriptPubKey(this.network, new Script(consumedOutputData.ScriptPubKeyBytes)); + var address = this.scriptAddressReader.GetAddressFromScriptPubKey(this.network, new Script(consumedOutputData.ScriptPubKeyBytes)); - if (string.IsNullOrEmpty(address)) + if (address.IsNullOrEmpty()) { // This condition need not be logged, as the address reader should be aware of all possible address formats already. continue; } - this.ProcessBalanceChangeLocked(header.Height, address, amountSpent, false); + if (address.Address != string.Empty) + this.ProcessBalanceChangeLocked(header.Height, address.Address, amountSpent, false); + + if (address.HotAddress != string.Empty) + this.ProcessBalanceChangeLocked(header.Height, address.HotAddress, amountSpent, false); + + if (address.ColdAddress != string.Empty) + this.ProcessBalanceChangeLocked(header.Height, address.ColdAddress, amountSpent, false); } // Process outputs. @@ -469,16 +476,23 @@ private bool ProcessBlock(Block block, ChainedHeader header) if (amountReceived == 0 || txOut.IsEmpty || txOut.ScriptPubKey.IsUnspendable) continue; - string address = this.scriptAddressReader.GetAddressFromScriptPubKey(this.network, txOut.ScriptPubKey); + var address = this.scriptAddressReader.GetAddressFromScriptPubKey(this.network, txOut.ScriptPubKey); - if (string.IsNullOrEmpty(address)) + if (address.IsNullOrEmpty()) { // This condition need not be logged, as the address reader should be aware of all // possible address formats already. continue; } - this.ProcessBalanceChangeLocked(header.Height, address, amountReceived, true); + if (address.Address != string.Empty) + this.ProcessBalanceChangeLocked(header.Height, address.Address, amountReceived, true); + + if (address.HotAddress != string.Empty) + this.ProcessBalanceChangeLocked(header.Height, address.HotAddress, amountReceived, true); + + if (address.ColdAddress != string.Empty) + this.ProcessBalanceChangeLocked(header.Height, address.ColdAddress, amountReceived, true); } } diff --git a/src/Features/Blockcore.Features.ColdStaking/ColdStakingFeature.cs b/src/Features/Blockcore.Features.ColdStaking/ColdStakingFeature.cs index 510c08121..f3f6e28c4 100644 --- a/src/Features/Blockcore.Features.ColdStaking/ColdStakingFeature.cs +++ b/src/Features/Blockcore.Features.ColdStaking/ColdStakingFeature.cs @@ -9,6 +9,7 @@ using Blockcore.Configuration.Logging; using Blockcore.Connection; using Blockcore.Connection.Broadcasting; +using Blockcore.Consensus.ScriptInfo; using Blockcore.Features.BlockStore; using Blockcore.Features.MemoryPool; using Blockcore.Features.RPC;