コード例 #1
0
        /// <summary>
        /// Initializes an instance of the <see cref="ScriptPubKey"/> class.
        /// </summary>
        /// <param name="script">The script.</param>
        /// <param name="network">The network where the transaction was conducted.</param>
        public ScriptPubKey(NBitcoin.Script script, Network network) : base(script)
        {
            var destinations = new List <TxDestination> {
                script.GetDestination(network)
            };

            this.Type = this.GetScriptType(script.FindTemplate(network));
            if (destinations[0] == null)
            {
                destinations = script.GetDestinationPublicKeys(network)
                               .Select(p => p.Hash)
                               .ToList <TxDestination>();
            }
            else
            {
                if (destinations.Count == 1)
                {
                    this.ReqSigs   = 1;
                    this.Addresses = new List <string> {
                        destinations[0].GetAddress(network).ToString()
                    };
                }
                else
                {
                    PayToMultiSigTemplateParameters multi = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(script);
                    this.ReqSigs   = multi.SignatureCount;
                    this.Addresses = multi.PubKeys.Select(m => m.GetAddress(network).ToString()).ToList();
                }
            }
        }
コード例 #2
0
        public override Script GenerateScriptSig(Network network, Script scriptPubKey, IKeyRepository keyRepo, ISigner signer)
        {
            PayToMultiSigTemplateParameters multiSigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(network, scriptPubKey);
            var signatures = new TransactionSignature[multiSigParams.PubKeys.Length];

            Key[] keys =
                multiSigParams
                .PubKeys
                .Select(p => keyRepo.FindKey(p.ScriptPubKey))
                .ToArray();

            int sigCount = 0;

            for (int i = 0; i < keys.Length; i++)
            {
                if (sigCount == multiSigParams.SignatureCount)
                {
                    break;
                }
                if (keys[i] != null)
                {
                    TransactionSignature sig = signer.Sign(keys[i]);
                    signatures[i] = sig;
                    sigCount++;
                }
            }

            IEnumerable <TransactionSignature> sigs = signatures;

            if (sigCount == multiSigParams.SignatureCount)
            {
                sigs = sigs.Where(s => s != TransactionSignature.Empty && s != null);
            }
            return(PayToMultiSigTemplate.Instance.GenerateScriptSig(sigs));
        }
コード例 #3
0
        public string Signature()
        {
            PayToMultiSigTemplateParameters multisigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(this.RedeemScript);

            int index = multisigParams.PubKeys.IndexOf(this.PubKey);

            TransactionSignature signature = PayToMultiSigTemplate.Instance.ExtractScriptSigParameters(this.Network, new Script(tx.Inputs[0].ScriptSig.ToOps().SkipLast(1)))[index];

            return(signature.ToString());
        }
コード例 #4
0
        public FederatedPegSettings(NodeSettings nodeSettings, IFederatedPegOptions federatedPegOptions = null)
        {
            Guard.NotNull(nodeSettings, nameof(nodeSettings));

            TextFileConfiguration configReader = nodeSettings.ConfigReader;

            this.IsMainChain = configReader.GetOrDefault("mainchain", false);
            if (!this.IsMainChain && !configReader.GetOrDefault("sidechain", false))
            {
                throw new ConfigurationException("Either -mainchain or -sidechain must be specified");
            }

            string redeemScriptRaw = configReader.GetOrDefault <string>(RedeemScriptParam, null);

            Console.WriteLine(redeemScriptRaw);
            if (redeemScriptRaw == null)
            {
                throw new ConfigurationException($"could not find {RedeemScriptParam} configuration parameter");
            }

            this.MultiSigRedeemScript = new Script(redeemScriptRaw);
            this.MultiSigAddress      = this.MultiSigRedeemScript.Hash.GetAddress(nodeSettings.Network);
            PayToMultiSigTemplateParameters payToMultisigScriptParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(this.MultiSigRedeemScript);

            this.MultiSigM            = payToMultisigScriptParams.SignatureCount;
            this.MultiSigN            = payToMultisigScriptParams.PubKeys.Length;
            this.FederationPublicKeys = payToMultisigScriptParams.PubKeys;

            this.PublicKey = configReader.GetOrDefault <string>(PublicKeyParam, null);

            if (this.FederationPublicKeys.All(p => p != new PubKey(this.PublicKey)))
            {
                throw new ConfigurationException("Please make sure the public key passed as parameter was used to generate the multisig redeem script.");
            }

            // Federation IPs - These are required to receive and sign withdrawal transactions.
            string federationIpsRaw = configReader.GetOrDefault <string>(FederationIpsParam, null);

            if (federationIpsRaw == null)
            {
                throw new ConfigurationException("Federation IPs must be specified.");
            }

            IEnumerable <IPEndPoint> endPoints = federationIpsRaw.Split(',').Select(a => a.ToIPEndPoint(nodeSettings.Network.DefaultPort));

            this.FederationNodeIpEndPoints = new HashSet <IPEndPoint>(endPoints, new IPEndPointComparer());
            this.FederationNodeIpAddresses = new HashSet <IPAddress>(endPoints.Select(x => x.Address), new IPAddressComparer());

            // These values are only configurable for tests at the moment. Fed members on live networks shouldn't play with them.
            this.CounterChainDepositStartBlock     = configReader.GetOrDefault(CounterChainDepositBlock, this.IsMainChain ? 1 : StratisMainDepositStartBlock);
            this.FasterDepositThresholdAmount      = Money.Coins(configReader.GetOrDefault(FasterDepositThresholdAmountParam, 100));
            this.FasterDepositMinimumConfirmations = configReader.GetOrDefault(FasterDepositMinimumConfirmationsParam, 10);
            this.MinimumDepositConfirmations       = configReader.GetOrDefault(MinimumDepositConfirmationsParam, (int)nodeSettings.Network.Consensus.MaxReorgLength + 1);
            this.WalletSyncFromHeight = configReader.GetOrDefault(WalletSyncFromHeightParam, federatedPegOptions?.WalletSyncFromHeight ?? 0);
        }
コード例 #5
0
        /// <summary>
        /// Initializes an instance of the <see cref="ScriptPubKey"/> class.
        /// </summary>
        /// <param name="script">The script.</param>
        /// <param name="network">The network where the transaction was conducted.</param>
        public ScriptPubKey(NBitcoin.Script script, Network network) : base(script)
        {
            this.Type = this.GetScriptType(script.FindTemplate(network));

            // To avoid modifying the very low-level GetDestination logic, check for a cold staking script first.
            // The decision to show the cold pubkey's address in the 'addresses' list is based on the following:
            // 1. It seems more intuitive from a user's perspective that their balance will appear against this address.
            // 2. A balance should never appear against a hot address from an exchange's perspective, as they have no guarantee they will be able to spend those funds.
            // It is also presumed that it is preferable to show an address rather than not, as a block explorer would then have to show only a relatively meaningless raw script as the output's destination.
            // Another considered alternative was to show both addresses, but with a modified version byte. The underlying pubkey hashes would be the same, but the resulting addresses would be visually distinct from regular P2PKH.
            // This may have caused user confusion, however, as the modified addresses would not look like those they used to configure the cold staking setup, making searching for them on a block explorer more difficult.
            if (script.IsScriptType(ScriptType.ColdStaking))
            {
                var coldPubKeyHash = new KeyId(script.ToBytes(true).SafeSubarray(28, 20));

                this.ReqSigs   = 1;
                this.Addresses = new List <string> {
                    coldPubKeyHash.GetAddress(network).ToString()
                };

                return;
            }

            var destinations = new List <TxDestination> {
                script.GetDestination(network)
            };

            if (destinations[0] == null)
            {
                destinations = script.GetDestinationPublicKeys(network).Select(p => p.Hash).ToList <TxDestination>();
            }

            // TODO: We do not want to put the cold staking addresses into the 'addresses' element due to the high potential for confusion. Maybe introduce an additional element?
            if (destinations.Count == 1)
            {
                this.ReqSigs   = 1;
                this.Addresses = new List <string> {
                    destinations[0].GetAddress(network).ToString()
                };
            }
            else if (destinations.Count > 1)
            {
                PayToMultiSigTemplateParameters multi = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(script) ??
                                                        PayToFederationTemplate.Instance.ExtractScriptPubKeyParameters(script, network);
                if (multi != null)
                {
                    this.ReqSigs   = multi.SignatureCount;
                    this.Addresses = multi.PubKeys.Select(m => m.GetAddress(network).ToString()).ToList();
                }
            }
        }
コード例 #6
0
        public FederationGatewaySettings(NodeSettings nodeSettings)
        {
            Guard.NotNull(nodeSettings, nameof(nodeSettings));

            TextFileConfiguration configReader = nodeSettings.ConfigReader;

            this.IsMainChain = configReader.GetOrDefault <bool>("mainchain", false);
            if (!this.IsMainChain && !configReader.GetOrDefault("sidechain", false))
            {
                throw new ConfigurationException("Either -mainchain or -sidechain must be specified");
            }

            string redeemScriptRaw = configReader.GetOrDefault <string>(RedeemScriptParam, null);

            Console.WriteLine(redeemScriptRaw);
            if (redeemScriptRaw == null)
            {
                throw new ConfigurationException($"could not find {RedeemScriptParam} configuration parameter");
            }
            this.MultiSigRedeemScript = new Script(redeemScriptRaw);
            this.MultiSigAddress      = this.MultiSigRedeemScript.Hash.GetAddress(nodeSettings.Network);
            PayToMultiSigTemplateParameters payToMultisigScriptParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(this.MultiSigRedeemScript);

            this.MultiSigM            = payToMultisigScriptParams.SignatureCount;
            this.MultiSigN            = payToMultisigScriptParams.PubKeys.Length;
            this.FederationPublicKeys = payToMultisigScriptParams.PubKeys;

            this.PublicKey       = configReader.GetOrDefault <string>(PublicKeyParam, null);
            this.MinCoinMaturity = configReader.GetOrDefault <int>(MinCoinMaturityParam, (int)nodeSettings.Network.Consensus.MaxReorgLength + 1);
            if (this.MinCoinMaturity <= 0)
            {
                throw new ConfigurationException("The minimum coin maturity can't be set to zero or less.");
            }

            this.TransactionFee = new Money(configReader.GetOrDefault <decimal>(TransactionFeeParam, 0.01m), MoneyUnit.BTC);

            if (this.FederationPublicKeys.All(p => p != new PubKey(this.PublicKey)))
            {
                throw new ConfigurationException("Please make sure the public key passed as parameter was used to generate the multisig redeem script.");
            }

            this.CounterChainApiPort       = configReader.GetOrDefault(CounterChainApiPortParam, 0);
            this.FederationNodeIpEndPoints = configReader.GetOrDefault <string>(FederationIpsParam, null)?.Split(',')
                                             .Select(a => a.ToIPEndPoint(nodeSettings.Network.DefaultPort)) ?? new List <IPEndPoint>();

            //todo : remove that for prod code
            this.MinimumDepositConfirmations = (uint)configReader.GetOrDefault <int>(MinimumDepositConfirmationsParam, (int)nodeSettings.Network.Consensus.MaxReorgLength + 1);
            //this.MinimumDepositConfirmations = nodeSettings.Network.Consensus.MaxReorgLength + 1;
        }
コード例 #7
0
        public static KeyId[] ExtractKeyIDs(Script script)
        {
            KeyId keyId = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(script);

            if (keyId != null)
            {
                return(new[] { keyId });
            }
            else
            {
                PayToMultiSigTemplateParameters para = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(Network.Main, script);
                if (para == null)
                {
                    throw new ArgumentException("Invalid stealth spendable output script", "spendable");
                }
                return(para.PubKeys.Select(k => k.Hash).ToArray());
            }
        }
コード例 #8
0
        public override Script CombineScriptSig(Network network, Script scriptPubKey, Script a, Script b)
        {
            PayToMultiSigTemplateParameters para = PayToFederationTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey, network);

            // Combine all the signatures we've got:
            TransactionSignature[] aSigs = PayToFederationTemplate.Instance.ExtractScriptSigParameters(network, a);
            if (aSigs == null)
            {
                return(b);
            }
            TransactionSignature[] bSigs = PayToFederationTemplate.Instance.ExtractScriptSigParameters(network, b);
            if (bSigs == null)
            {
                return(a);
            }
            int sigCount = 0;
            var sigs     = new TransactionSignature[para.PubKeys.Length];

            for (int i = 0; i < para.PubKeys.Length; i++)
            {
                TransactionSignature aSig = i < aSigs.Length ? aSigs[i] : null;
                TransactionSignature bSig = i < bSigs.Length ? bSigs[i] : null;
                TransactionSignature sig  = aSig ?? bSig;
                if (sig != null)
                {
                    sigs[i] = sig;
                    sigCount++;
                }
                if (sigCount == para.SignatureCount)
                {
                    break;
                }
            }
            if (sigCount == para.SignatureCount)
            {
                sigs = sigs.Where(s => s != null && s != TransactionSignature.Empty).ToArray();
            }
            return(PayToFederationTemplate.Instance.GenerateScriptSig(sigs));
        }
コード例 #9
0
        private static void HandleSwitchGenerateFundsRecoveryTransaction(string[] args, bool newFormat = false)
        {
            ConfigReader = new TextFileConfiguration(args);

            // datadir = Directory of old federation.
            if (newFormat)
            {
                ConfirmArguments(ConfigReader, "network", "datadir", "fedpubkeys", "password", "txtime");
            }
            else
            {
                ConfirmArguments(ConfigReader, "network", "datadir", "fedpubkeys", "quorum", "password", "txtime");
            }

            PayToMultiSigTemplateParameters para = new PayToMultiSigTemplateParameters()
            {
                PubKeys        = GetFederatedPublicKeysFromArguments().Select(p => new PubKey(p)).ToArray(),
                SignatureCount = newFormat ? 0 : GetQuorumFromArguments()
            };

            string password = GetPasswordFromArguments();

            string dataDirPath = GetDataDirFromArguments();

            (Network mainChain, Network sideChain) = GetMainAndSideChainNetworksFromArguments();

            DateTime txTime = GetTransactionTimeFromArguments();

            Console.WriteLine($"Creating funds recovery transaction for {sideChain.Name}.");
            FundsRecoveryTransactionModel sideChainInfo = (new RecoveryTransactionCreator()).CreateFundsRecoveryTransaction(true, sideChain, mainChain, dataDirPath, para, password, txTime, newFormat);

            sideChainInfo.DisplayInfo();

            Console.WriteLine($"Creating funds recovery transaction for {mainChain.Name}.");
            FundsRecoveryTransactionModel mainChainInfo = (new RecoveryTransactionCreator()).CreateFundsRecoveryTransaction(false, mainChain, sideChain, dataDirPath, para, password, txTime, newFormat, true);

            mainChainInfo.DisplayInfo();
        }
コード例 #10
0
        public override int EstimateScriptSigSize(Network network, Script scriptPubKey)
        {
            PayToMultiSigTemplateParameters para = PayToFederationTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey, network);

            return(PayToFederationTemplate.Instance.GenerateScriptSig(Enumerable.Range(0, para.SignatureCount).Select(o => DummySignature).ToArray()).Length);
        }
コード例 #11
0
        protected override void WriteTransaction(JsonTextWriter writer, Transaction tx)
        {
            WritePropertyValue(writer, "txid", tx.GetHash().ToString());
            WritePropertyValue(writer, "version", tx.Version);
            WritePropertyValue(writer, "locktime", tx.LockTime.Value);

            writer.WritePropertyName("vin");
            writer.WriteStartArray();
            foreach (TxIn txin in tx.Inputs)
            {
                writer.WriteStartObject();

                if (txin.PrevOut.Hash == uint256.Zero)
                {
                    WritePropertyValue(writer, "coinbase", Encoders.Hex.EncodeData(txin.ScriptSig.ToBytes()));
                }
                else
                {
                    WritePropertyValue(writer, "txid", txin.PrevOut.Hash.ToString());
                    WritePropertyValue(writer, "vout", txin.PrevOut.N);
                    writer.WritePropertyName("scriptSig");
                    writer.WriteStartObject();

                    WritePropertyValue(writer, "asm", txin.ScriptSig.ToString());
                    WritePropertyValue(writer, "hex", Encoders.Hex.EncodeData(txin.ScriptSig.ToBytes()));

                    writer.WriteEndObject();
                }
                WritePropertyValue(writer, "sequence", (uint)txin.Sequence);
                writer.WriteEndObject();
            }
            writer.WriteEndArray();

            writer.WritePropertyName("vout");
            writer.WriteStartArray();

            int i = 0;

            foreach (TxOut txout in tx.Outputs)
            {
                writer.WriteStartObject();
                writer.WritePropertyName("value");
                writer.WriteRawValue(ValueFromAmount(txout.Value));
                WritePropertyValue(writer, "n", i);

                writer.WritePropertyName("scriptPubKey");
                writer.WriteStartObject();

                WritePropertyValue(writer, "asm", txout.ScriptPubKey.ToString());
                WritePropertyValue(writer, "hex", Encoders.Hex.EncodeData(txout.ScriptPubKey.ToBytes()));

                var destinations = new List <TxDestination>()
                {
                    txout.ScriptPubKey.GetDestination(this.Network)
                };
                if (destinations[0] == null)
                {
                    destinations = txout.ScriptPubKey.GetDestinationPublicKeys(this.Network)
                                   .Select(p => p.Hash)
                                   .ToList <TxDestination>();
                }
                if (destinations.Count == 1)
                {
                    WritePropertyValue(writer, "reqSigs", 1);
                    WritePropertyValue(writer, "type", GetScriptType(txout.ScriptPubKey.FindTemplate(this.Network)));
                    writer.WritePropertyName("addresses");
                    writer.WriteStartArray();
                    writer.WriteValue(destinations[0].GetAddress(this.Network).ToString());
                    writer.WriteEndArray();
                }
                else
                {
                    PayToMultiSigTemplateParameters multi = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(this.Network, txout.ScriptPubKey);
                    if (multi != null)
                    {
                        WritePropertyValue(writer, "reqSigs", multi.SignatureCount);
                    }
                    WritePropertyValue(writer, "type", GetScriptType(txout.ScriptPubKey.FindTemplate(this.Network)));
                    if (multi != null)
                    {
                        writer.WritePropertyName("addresses");
                        writer.WriteStartArray();
                        foreach (PubKey key in multi.PubKeys)
                        {
                            writer.WriteValue(key.Hash.GetAddress(this.Network).ToString());
                        }
                        writer.WriteEndArray();
                    }
                }

                writer.WriteEndObject(); //endscript
                writer.WriteEndObject(); //in out
                i++;
            }
            writer.WriteEndArray();
        }
コード例 #12
0
        /// <summary>
        /// Creates a transaction to transfers funds from an old federation to a new federation.
        /// </summary>
        /// <param name="isSideChain">Indicates whether the <paramref name="network"/> is the sidechain.</param>
        /// <param name="network">The network that we are creating the recovery transaction for.</param>
        /// <param name="counterChainNetwork">The counterchain network.</param>
        /// <param name="dataDirPath">The root folder containing the old federation.</param>
        /// <param name="redeemScript">The new redeem script.</param>
        /// <param name="password">The password required to generate transactions using the federation wallet.</param>
        /// <param name="txTime">Any deposits beyond this UTC date will be ignored when selecting coin inputs.</param>
        /// <returns>A funds recovery transaction that moves funds to the new redeem script.</returns>
        public FundsRecoveryTransactionModel CreateFundsRecoveryTransaction(bool isSideChain, Network network, Network counterChainNetwork, string dataDirPath, Script redeemScript, string password, DateTime txTime)
        {
            var model = new FundsRecoveryTransactionModel()
            {
                Network = network, IsSideChain = isSideChain, RedeemScript = redeemScript
            };

            // Get the old redeem script from the wallet file.
            PayToMultiSigTemplateParameters multisigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(redeemScript);
            string           theChain          = isSideChain ? "sidechain" : "mainchain";
            var              nodeSettings      = new NodeSettings(network, args: new string[] { $"datadir={dataDirPath}", $"redeemscript={redeemScript}", $"-{theChain}" });
            var              walletFileStorage = new FileStorage <FederationWallet>(nodeSettings.DataFolder.WalletPath);
            FederationWallet wallet            = walletFileStorage.LoadByFileName("multisig_wallet.json");
            Script           oldRedeemScript   = wallet.MultiSigAddress.RedeemScript;
            PayToMultiSigTemplateParameters oldMultisigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(oldRedeemScript);

            model.oldMultisigAddress = oldRedeemScript.Hash.GetAddress(network);
            model.newMultisigAddress = redeemScript.Hash.GetAddress(network);

            // Create dummy inputs to avoid errors when constructing FederatedPegSettings.
            var extraArgs = new Dictionary <string, string>();

            extraArgs[FederatedPegSettings.FederationIpsParam] = oldMultisigParams.PubKeys.Select(p => "0.0.0.0".ToIPEndPoint(nodeSettings.Network.DefaultPort)).Join(",");
            var privateKey = Key.Parse(wallet.EncryptedSeed, password, network);

            extraArgs[FederatedPegSettings.PublicKeyParam] = privateKey.PubKey.ToHex(network);
            (new TextFileConfiguration(extraArgs.Select(i => $"{i.Key}={i.Value}").ToArray())).MergeInto(nodeSettings.ConfigReader);

            model.PubKey = privateKey.PubKey;

            var dBreezeSerializer = new DBreezeSerializer(network.Consensus.ConsensusFactory);
            var blockStore        = new BlockRepository(network, nodeSettings.DataFolder, nodeSettings.LoggerFactory, dBreezeSerializer);

            blockStore.Initialize();

            var           chain        = new ChainRepository(nodeSettings.DataFolder, nodeSettings.LoggerFactory, dBreezeSerializer);
            Block         genesisBlock = network.GetGenesis();
            ChainedHeader tip          = chain.LoadAsync(new ChainedHeader(genesisBlock.Header, genesisBlock.GetHash(), 0)).GetAwaiter().GetResult();
            var           chainIndexer = new ChainIndexer(network, tip);

            var nodeLifetime = new NodeLifetime();
            IDateTimeProvider dateTimeProvider = DateTimeProvider.Default;
            var federatedPegSettings           = new FederatedPegSettings(nodeSettings);
            var opReturnDataReader             = new OpReturnDataReader(nodeSettings.LoggerFactory, new CounterChainNetworkWrapper(counterChainNetwork));
            var walletFeePolicy = new WalletFeePolicy(nodeSettings);

            var walletManager = new FederationWalletManager(nodeSettings.LoggerFactory, network, chainIndexer, nodeSettings.DataFolder, walletFeePolicy,
                                                            new AsyncProvider(nodeSettings.LoggerFactory, new Signals(nodeSettings.LoggerFactory, new DefaultSubscriptionErrorHandler(nodeSettings.LoggerFactory)), nodeLifetime), nodeLifetime,
                                                            dateTimeProvider, federatedPegSettings, new WithdrawalExtractor(nodeSettings.LoggerFactory, federatedPegSettings, opReturnDataReader, network), blockStore);

            walletManager.Start();
            walletManager.EnableFederationWallet(password);

            if (!walletManager.IsFederationWalletActive())
            {
                throw new ArgumentException($"Could not activate the federation wallet on {network}.");
            }

            // Retrieves the unspent outputs in deterministic order.
            List <Stratis.Features.FederatedPeg.Wallet.UnspentOutputReference> coinRefs = walletManager.GetSpendableTransactionsInWallet().ToList();

            // Exclude coins (deposits) beyond the transaction (switch-over) time!
            coinRefs = coinRefs.Where(r => r.Transaction.CreationTime < txTime).ToList();
            if (!coinRefs.Any())
            {
                throw new ArgumentException($"There are no coins to recover from the federation wallet on {network}.");
            }

            Money fee = federatedPegSettings.GetWithdrawalTransactionFee(coinRefs.Count());

            var builder = new TransactionBuilder(network);

            builder.AddKeys(privateKey);
            builder.AddCoins(coinRefs.Select(c => ScriptCoin.Create(network, c.Transaction.Id, (uint)c.Transaction.Index, c.Transaction.Amount, c.Transaction.ScriptPubKey, oldRedeemScript)));

            // Split the coins into multiple outputs.
            Money     amount         = coinRefs.Sum(r => r.Transaction.Amount) - fee;
            const int numberOfSplits = 10;
            Money     splitAmount    = new Money((long)amount / numberOfSplits);
            var       recipients     = new List <Stratis.Features.FederatedPeg.Wallet.Recipient>();

            for (int i = 0; i < numberOfSplits; i++)
            {
                Money sendAmount = (i != (numberOfSplits - 1)) ? splitAmount : amount - splitAmount * (numberOfSplits - 1);

                builder.Send(redeemScript.PaymentScript, sendAmount);
            }

            builder.SetTimeStamp((uint)(new DateTimeOffset(txTime)).ToUnixTimeSeconds());
            builder.CoinSelector = new DeterministicCoinSelector();
            builder.SendFees(fee);

            model.tx = builder.BuildTransaction(true);

            File.WriteAllText(Path.Combine(dataDirPath, $"{network.Name}_{model.PubKey.ToHex(network).Substring(0, 8)}.hex"), model.tx.ToHex(network));

            // Merge our transaction with other transactions which have been placed in the data folder.
            Transaction oldTransaction = model.tx;
            string      namePattern    = $"{network.Name}_*.hex";
            int         sigCount       = 1;

            foreach (string fileName in Directory.EnumerateFiles(dataDirPath, namePattern))
            {
                Transaction incomingPartialTransaction = network.CreateTransaction(File.ReadAllText(fileName));

                // Don't merge with self.
                if (incomingPartialTransaction.GetHash() == oldTransaction.GetHash())
                {
                    continue;
                }

                // Transaction times must match.
                if (incomingPartialTransaction is PosTransaction && incomingPartialTransaction.Time != model.tx.Time)
                {
                    Console.WriteLine($"The locally generated transaction is time-stamped differently from the transaction contained in '{fileName}'. The imported signature can't be used.");
                    continue;
                }

                // Combine signatures.
                Transaction newTransaction = SigningUtils.CheckTemplateAndCombineSignatures(builder, model.tx, new[] { incomingPartialTransaction });

                if (oldTransaction.GetHash() == newTransaction.GetHash())
                {
                    Console.WriteLine($"The locally generated transaction is not similar to '{fileName}'. The imported signature can't be used.");
                    continue;
                }

                model.tx = newTransaction;
                sigCount++;
            }

            Console.WriteLine($"{sigCount} of {multisigParams.SignatureCount} signatures collected for {network.Name}.");

            if (sigCount >= multisigParams.SignatureCount)
            {
                if (builder.Verify(model.tx))
                {
                    // Write the transaction to file.
                    File.WriteAllText(Path.Combine(dataDirPath, $"{(txTime > DateTime.Now ? "Preliminary " : "")}{network.Name}Recovery.txt"), model.tx.ToHex(network));
                }
                else
                {
                    Console.WriteLine("Could not verify the transaction.");
                }
            }

            // Stop the wallet manager to release the database folder.
            nodeLifetime.StopApplication();
            walletManager.Stop();

            return(model);
        }
コード例 #13
0
        public FederatedPegSettings(NodeSettings nodeSettings, CounterChainNetworkWrapper counterChainNetworkWrapper)
        {
            Guard.NotNull(nodeSettings, nameof(nodeSettings));

            TextFileConfiguration configReader = nodeSettings.ConfigReader;

            this.IsMainChain = configReader.GetOrDefault("mainchain", false);
            if (!this.IsMainChain && !configReader.GetOrDefault("sidechain", false))
            {
                throw new ConfigurationException("Either -mainchain or -sidechain must be specified");
            }

            string redeemScriptRaw = configReader.GetOrDefault <string>(RedeemScriptParam, null);

            Console.WriteLine(redeemScriptRaw);

            PayToMultiSigTemplateParameters para = null;

            if (!string.IsNullOrEmpty(redeemScriptRaw))
            {
                var redeemScript = new Script(redeemScriptRaw);
                para = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(redeemScript) ??
                       PayToFederationTemplate.Instance.ExtractScriptPubKeyParameters(redeemScript, nodeSettings.Network);
            }

            if (para == null)
            {
                string pubKeys = configReader.GetOrDefault <string>(FederationKeysParam, null);
                if (string.IsNullOrEmpty(pubKeys))
                {
                    throw new ConfigurationException($"Either -{RedeemScriptParam} or -{FederationKeysParam} must be specified.");
                }

                para.PubKeys        = pubKeys.Split(",").Select(s => new PubKey(s.Trim())).ToArray();
                para.SignatureCount = (pubKeys.Length + 1) / 2;
            }

            para.SignatureCount = configReader.GetOrDefault(FederationQuorumParam, para.SignatureCount);

            IFederation federation;

            federation = new Federation(para.PubKeys, para.SignatureCount);
            nodeSettings.Network.Federations.RegisterFederation(federation);
            counterChainNetworkWrapper.CounterChainNetwork.Federations.RegisterFederation(federation);

            this.MultiSigRedeemScript = federation.MultisigScript;
            this.MultiSigAddress      = this.MultiSigRedeemScript.Hash.GetAddress(nodeSettings.Network);
            this.MultiSigM            = para.SignatureCount;
            this.MultiSigN            = para.PubKeys.Length;
            this.FederationPublicKeys = para.PubKeys;

            this.PublicKey = configReader.GetOrDefault <string>(PublicKeyParam, null);

            if (this.FederationPublicKeys.All(p => p != new PubKey(this.PublicKey)))
            {
                throw new ConfigurationException("Please make sure the public key passed as parameter was used to generate the multisig redeem script.");
            }

            // Federation IPs - These are required to receive and sign withdrawal transactions.
            string federationIpsRaw = configReader.GetOrDefault <string>(FederationIpsParam, null);

            if (federationIpsRaw == null)
            {
                throw new ConfigurationException("Federation IPs must be specified.");
            }

            IEnumerable <IPEndPoint> endPoints = federationIpsRaw.Split(',').Select(a => a.ToIPEndPoint(nodeSettings.Network.DefaultPort));

            this.FederationNodeIpEndPoints = new HashSet <IPEndPoint>(endPoints, new IPEndPointComparer());
            this.FederationNodeIpAddresses = new HashSet <IPAddress>(endPoints.Select(x => x.Address), new IPAddressComparer());

            // These values are only configurable for tests at the moment. Fed members on live networks shouldn't play with them.
            this.CounterChainDepositStartBlock = configReader.GetOrDefault(CounterChainDepositBlock, 1);

            this.SmallDepositThresholdAmount  = Money.Coins(configReader.GetOrDefault(ThresholdAmountSmallDepositParam, 50));
            this.NormalDepositThresholdAmount = Money.Coins(configReader.GetOrDefault(ThresholdAmountNormalDepositParam, 1000));

            this.MinimumConfirmationsSmallDeposits        = configReader.GetOrDefault(MinimumConfirmationsSmallDepositsParam, 25);
            this.MinimumConfirmationsNormalDeposits       = configReader.GetOrDefault(MinimumConfirmationsNormalDepositsParam, 80);
            this.MinimumConfirmationsLargeDeposits        = (int)nodeSettings.Network.Consensus.MaxReorgLength + 1;
            this.MinimumConfirmationsDistributionDeposits = (int)nodeSettings.Network.Consensus.MaxReorgLength + 1;

            this.MaximumPartialTransactionThreshold = configReader.GetOrDefault(MaximumPartialTransactionsParam, CrossChainTransferStore.MaximumPartialTransactions);
            this.WalletSyncFromHeight = configReader.GetOrDefault(WalletSyncFromHeightParam, 0);
        }