예제 #1
1
		public Transaction Finalize(Key payeeKey)
		{
			var builder = new TransactionBuilder();
			var tx =  
				builder
				.AddCoins(FundCoin)
				.AddKeys(payeeKey)
				.SignTransaction(Payment);
			if(!builder.Verify(tx, Arguments.Fees))
				throw new MicroPaymentException("Payment incorrectly signed");
			return tx;
		}
        public void soft_delete_only_updates_parent_type()
        {
            var dto = new ParentDto
            {
                ParentKey = 1,
                OneToManyChildDto = new[]
                {
                    new OneToManyChildDto
                    {
                        ChildKey = 2
                    }
                }
            };

            var cache = new DtoMetadataCache();
            var builder = new TransactionBuilder(cache);
            var scripts = builder.BuildUpdateScripts(dto, null, true);

            Assert.AreEqual(1, scripts.Count, "Unexpected number of scripts.");

            var script = scripts[0];
            var sql = script.Buffer.ToString();

            Assert.IsTrue(sql.Contains("UPDATE dbo.[Parent]"), "No update on parent.");

            Assert.AreEqual(1, Regex.Matches(sql, "UPDATE").Count, "Unexpected number of UPDATEs.");
            Assert.AreEqual(0, Regex.Matches(sql, "INSERT").Count, "Should be no INSERTs.");
            Assert.AreEqual(0, Regex.Matches(sql, "DELETE").Count, "Should be no DELETEs.");
            Assert.AreEqual(0, Regex.Matches(sql, "SELECT").Count, "Should be no SELECTs.");
        }
예제 #3
0
		public void AssertAckOpenChannel(OpenChannelAckMessage ackMsg)
		{
			var builder = new TransactionBuilder();
			var fullySigned =
				builder
				.AddCoins(FundCoin)
				.CombineSignatures(Refund, ackMsg.SignedRefund);
			if(!builder.Verify(fullySigned, Arguments.Fees))
			{
				throw new MicroPaymentException("Transaction incorrectly signed");
			}
			Refund = fullySigned;
		}
예제 #4
0
		public OpenChannelAckMessage AckOpenChannel(OpenChannelMessage openMsg,
													Key payeeKey)
		{
			if(payeeKey.PubKey != Arguments.Payee.PaymentPubKey)
				throw new ArgumentException("Invalid payeeKey", "payeeKey");

			Arguments.Assert(openMsg.UnsignedRefund, true, Arguments.Fees);
			var fundCoin = new Coin(openMsg.UnsignedRefund.Inputs[0].PrevOut, new TxOut(Arguments.Amount + Arguments.Fees, Arguments.Redeem.Hash)).ToScriptCoin(Arguments.Redeem);

			var signed =
				new TransactionBuilder()
				.AddCoins(fundCoin)
				.AddKeys(payeeKey)
				.SignTransaction(openMsg.UnsignedRefund);
			Refund = signed;
			return new OpenChannelAckMessage()
			{
				SignedRefund = signed
			};
		}
예제 #5
0
		public OpenChannelMessage OpenChannel(ICoin[] fundingCoins,
											  Key[] fundingKeys,
											  Key payerKey)
		{
			if(payerKey.PubKey != Arguments.Payer.PaymentPubKey)
				throw new ArgumentException("Invalid payerKey", "payerKey");
			var p2sh = Arguments.Redeem.Hash.ScriptPubKey;

			var builder = new TransactionBuilder();
			Fund =
				builder
				.AddCoins(fundingCoins)
				.AddKeys(fundingKeys)
				.Send(p2sh, Arguments.GetFundAmount())
				.SetChange(Arguments.Payer.ScriptPubkey)
				.SendFees(Arguments.Fees)
				.Shuffle()
				.BuildTransaction(true);

			if(!builder.Verify(Fund, Arguments.Fees))
				throw new MicroPaymentException("Funding transaction incorreclty signed");

			var fundCoin = Fund.Outputs.AsCoins().First(c => c.ScriptPubKey == p2sh).ToScriptCoin(Arguments.Redeem);

			var unsignedRefund = Arguments.CreatePayment(Arguments.Fees, fundCoin);
			unsignedRefund.LockTime = Arguments.Expiration;
			builder =
			   new TransactionBuilder()
			   .AddKeys(payerKey)
			   .AddCoins(fundCoin);
			Refund = builder.SignTransaction(unsignedRefund);
			return new OpenChannelMessage()
			{
				UnsignedRefund = unsignedRefund
			};
		}
예제 #6
0
        public async Task ExecuteNextPhaseAsync(CcjRoundPhase expectedPhase)
        {
            using (await RoundSyncronizerLock.LockAsync())
            {
                try
                {
                    Logger.LogInfo <CcjRound>($"Round ({RoundId}): Phase change requested: {expectedPhase.ToString()}.");

                    if (Status == CcjRoundStatus.NotStarted)                     // So start the input registration phase
                    {
                        if (expectedPhase != CcjRoundPhase.InputRegistration)
                        {
                            return;
                        }

                        // Calculate fees
                        var inputSizeInBytes  = (int)Math.Ceiling(((3 * Constants.P2wpkhInputSizeInBytes) + Constants.P2pkhInputSizeInBytes) / 4m);
                        var outputSizeInBytes = Constants.OutputSizeInBytes;
                        try
                        {
                            var estimateSmartFeeResponse = await RpcClient.EstimateSmartFeeAsync(ConfirmationTarget, EstimateSmartFeeMode.Conservative, simulateIfRegTest : true);

                            if (estimateSmartFeeResponse == null)
                            {
                                throw new InvalidOperationException("FeeRate is not yet initialized");
                            }
                            var   feeRate     = estimateSmartFeeResponse.FeeRate;
                            Money feePerBytes = (feeRate.FeePerK / 1000);

                            // Make sure min relay fee (1000 sat) is hit.
                            FeePerInputs  = Math.Max(feePerBytes * inputSizeInBytes, new Money(500));
                            FeePerOutputs = Math.Max(feePerBytes * outputSizeInBytes, new Money(250));
                        }
                        catch (Exception ex)
                        {
                            // If fee hasn't been initialized once, fall back.
                            if (FeePerInputs == null || FeePerOutputs == null)
                            {
                                var feePerBytes = new Money(100);                                 // 100 satoshi per byte

                                // Make sure min relay fee (1000 sat) is hit.
                                FeePerInputs  = Math.Max(feePerBytes * inputSizeInBytes, new Money(500));
                                FeePerOutputs = Math.Max(feePerBytes * outputSizeInBytes, new Money(250));
                            }

                            Logger.LogError <CcjRound>(ex);
                        }

                        Status = CcjRoundStatus.Running;
                    }
                    else if (Status != CcjRoundStatus.Running)                     // Failed or succeeded, swallow
                    {
                        return;
                    }
                    else if (Phase == CcjRoundPhase.InputRegistration)
                    {
                        if (expectedPhase != CcjRoundPhase.ConnectionConfirmation)
                        {
                            return;
                        }

                        RoundHash = NBitcoinHelpers.HashOutpoints(Alices.SelectMany(x => x.Inputs).Select(y => y.OutPoint));

                        Phase = CcjRoundPhase.ConnectionConfirmation;
                    }
                    else if (Phase == CcjRoundPhase.ConnectionConfirmation)
                    {
                        if (expectedPhase != CcjRoundPhase.OutputRegistration)
                        {
                            return;
                        }

                        Phase = CcjRoundPhase.OutputRegistration;
                    }
                    else if (Phase == CcjRoundPhase.OutputRegistration)
                    {
                        if (expectedPhase != CcjRoundPhase.Signing)
                        {
                            return;
                        }

                        // Build CoinJoin

                        // 1. Set new denomination: minor optimization.
                        Money newDenomination = Alices.Min(x => x.OutputSumWithoutCoordinatorFeeAndDenomination);
                        var   transaction     = new Transaction();

                        // 2. Add Bob outputs.
                        foreach (Bob bob in Bobs)
                        {
                            transaction.AddOutput(newDenomination, bob.ActiveOutputAddress.ScriptPubKey);
                        }

                        BitcoinWitPubKeyAddress coordinatorAddress = Constants.GetCoordinatorAddress(RpcClient.Network);
                        // 3. If there are less Bobs than Alices, then add our own address. The malicious Alice, who will refuse to sign.
                        for (int i = 0; i < Alices.Count - Bobs.Count; i++)
                        {
                            transaction.AddOutput(newDenomination, coordinatorAddress);
                        }

                        // 4. Start building Coordinator fee.
                        Money coordinatorFeePerAlice = newDenomination.Percentange(CoordinatorFeePercent);
                        Money coordinatorFee         = Alices.Count * coordinatorFeePerAlice;

                        // 5. Add the inputs and the changes of Alices.
                        foreach (Alice alice in Alices)
                        {
                            foreach (var input in alice.Inputs)
                            {
                                transaction.AddInput(new TxIn(input.OutPoint));
                            }
                            Money changeAmount = alice.GetChangeAmount(newDenomination, coordinatorFeePerAlice);
                            if (changeAmount > Money.Zero)                                        // If the coordinator fee would make change amount to be negative or zero then no need to pay it.
                            {
                                Money minimumOutputAmount      = Money.Coins(0.0001m);            // If the change would be less than about $1 then add it to the coordinator.
                                Money onePercentOfDenomination = newDenomination.Percentange(1m); // If the change is less than about 1% of the newDenomination then add it to the coordinator fee.
                                Money minimumChangeAmount      = Math.Max(minimumOutputAmount, onePercentOfDenomination);
                                if (changeAmount < minimumChangeAmount)
                                {
                                    coordinatorFee += changeAmount;
                                }
                                else
                                {
                                    transaction.AddOutput(changeAmount, alice.ChangeOutputAddress.ScriptPubKey);
                                }
                            }
                            else
                            {
                                coordinatorFee -= coordinatorFeePerAlice;
                            }
                        }

                        // 6. Add Coordinator fee only if > about $3, else just let it to be miner fee.
                        if (coordinatorFee > Money.Coins(0.0003m))
                        {
                            transaction.AddOutput(coordinatorFee, coordinatorAddress);
                        }

                        // 7. Create the unsigned transaction.
                        var builder = new TransactionBuilder();
                        UnsignedCoinJoin = builder
                                           .ContinueToBuild(transaction)
                                           .Shuffle()
                                           .BuildTransaction(false);

                        SignedCoinJoin = new Transaction(UnsignedCoinJoin.ToHex());

                        Phase = CcjRoundPhase.Signing;
                    }
                    else
                    {
                        return;
                    }

                    Logger.LogInfo <CcjRound>($"Round ({RoundId}): Phase initialized: {expectedPhase.ToString()}.");
                }
                catch (Exception ex)
                {
                    Logger.LogError <CcjRound>(ex);
                    Status = CcjRoundStatus.Failed;
                    throw;
                }
            }

#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            Task.Run(async() =>
            {
                TimeSpan timeout;
                switch (expectedPhase)
                {
                case CcjRoundPhase.InputRegistration:
                    timeout = InputRegistrationTimeout;
                    break;

                case CcjRoundPhase.ConnectionConfirmation:
                    timeout = ConnectionConfirmationTimeout;
                    break;

                case CcjRoundPhase.OutputRegistration:
                    timeout = OutputRegistrationTimeout;
                    break;

                case CcjRoundPhase.Signing:
                    timeout = SigningTimeout;
                    break;

                default: throw new InvalidOperationException("This is impossible to happen.");
                }

                // Delay asyncronously to the requested timeout.
                await Task.Delay(timeout);

                var executeRunFailure = false;
                using (await RoundSyncronizerLock.LockAsync())
                {
                    executeRunFailure = Status == CcjRoundStatus.Running && Phase == expectedPhase;
                }
                if (executeRunFailure)
                {
                    Logger.LogInfo <CcjRound>($"Round ({RoundId}): {expectedPhase.ToString()} timed out after {timeout.TotalSeconds} seconds. Failure mode is executing.");

                    // This will happen outside the lock.
                    Task.Run(async() =>
                    {
                        try
                        {
                            switch (expectedPhase)
                            {
                            case CcjRoundPhase.InputRegistration:
                                {
                                    // Only fail if less two one Alice is registered.
                                    // Don't ban anyone, it's ok if they lost connection.
                                    await RemoveAlicesIfInputsSpentAsync();
                                    int aliceCountAfterInputRegistrationTimeout = CountAlices();
                                    if (aliceCountAfterInputRegistrationTimeout < 2)
                                    {
                                        Fail();
                                    }
                                    else
                                    {
                                        UpdateAnonymitySet(aliceCountAfterInputRegistrationTimeout);
                                        // Progress to the next phase, which will be ConnectionConfirmation
                                        await ExecuteNextPhaseAsync(CcjRoundPhase.ConnectionConfirmation);
                                    }
                                }
                                break;

                            case CcjRoundPhase.ConnectionConfirmation:
                                {
                                    // Only fail if less than two one alices are registered.
                                    // What if an attacker registers all the time many alices, then drops out. He'll achieve only 2 alices to participate?
                                    // If he registers many alices at InputRegistration
                                    // AND never confirms in connection confirmation
                                    // THEN connection confirmation will go with 2 alices in every round
                                    // Therefore Alices those didn't confirm, nor requested dsconnection should be banned:
                                    IEnumerable <Alice> alicesToBan1 = GetAlicesBy(AliceState.InputsRegistered);
                                    IEnumerable <Alice> alicesToBan2 = await RemoveAlicesIfInputsSpentAsync();                                            // So ban only those who confirmed participation, yet spent their inputs.

                                    IEnumerable <OutPoint> inputsToBan = alicesToBan1.SelectMany(x => x.Inputs).Select(y => y.OutPoint).Concat(alicesToBan2.SelectMany(x => x.Inputs).Select(y => y.OutPoint).ToArray()).Distinct();

                                    if (inputsToBan.Any())
                                    {
                                        await UtxoReferee.BanUtxosAsync(1, DateTimeOffset.UtcNow, inputsToBan.ToArray());
                                    }

                                    RemoveAlicesBy(alicesToBan1.Select(x => x.UniqueId).Concat(alicesToBan2.Select(y => y.UniqueId)).Distinct().ToArray());

                                    int aliceCountAfterConnectionConfirmationTimeout = CountAlices();
                                    if (aliceCountAfterConnectionConfirmationTimeout < 2)
                                    {
                                        Fail();
                                    }
                                    else
                                    {
                                        UpdateAnonymitySet(aliceCountAfterConnectionConfirmationTimeout);
                                        // Progress to the next phase, which will be OutputRegistration
                                        await ExecuteNextPhaseAsync(CcjRoundPhase.OutputRegistration);
                                    }
                                }
                                break;

                            case CcjRoundPhase.OutputRegistration:
                                {
                                    // Output registration never fails.
                                    // We don't know which Alice to ban.
                                    // Therefore proceed to signing, and whichever Alice doesn't sign ban.
                                    await ExecuteNextPhaseAsync(CcjRoundPhase.Signing);
                                }
                                break;

                            case CcjRoundPhase.Signing:
                                {
                                    var outpointsToBan = new List <OutPoint>();
                                    using (await RoundSyncronizerLock.LockAsync())
                                    {
                                        foreach (Alice alice in Alices)
                                        {
                                            if (alice.State != AliceState.SignedCoinJoin)
                                            {
                                                outpointsToBan.AddRange(alice.Inputs.Select(x => x.OutPoint));
                                            }
                                        }
                                    }
                                    if (outpointsToBan.Any())
                                    {
                                        await UtxoReferee.BanUtxosAsync(1, DateTimeOffset.UtcNow, outpointsToBan.ToArray());
                                    }
                                    Fail();
                                }
                                break;

                            default: throw new InvalidOperationException("This is impossible to happen.");
                            }
                        }
                        catch (Exception ex)
                        {
                            Logger.LogWarning <CcjRound>($"Round ({RoundId}): {expectedPhase.ToString()} timeout failed with exception: {ex}");
                        }
                    });
                }
            });
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
        }
예제 #7
0
        public void Native_MultiSig_two_of_three_no_scripthash_no_witness()
        {
            var network = ObsidianXNetworksSelector.Obsidian.Mainnet();
            Key bob     = new Key();
            Key alice   = new Key();
            Key satoshi = new Key();

            Script scriptPubKey = PayToMultiSigTemplate
                                  .Instance
                                  .GenerateScriptPubKey(2, new[] { bob.PubKey.Compress(), alice.PubKey.Compress(), satoshi.PubKey.Compress() });

            this.output.WriteLine(scriptPubKey.ToString());

            Transaction received = network.CreateTransaction();

            received.Outputs.Add(new TxOut(Money.Coins(5), scriptPubKey));

            Coin coin = received.Outputs.AsCoins().First();

            BitcoinWitPubKeyAddress spendMultiSigToAddress = new Key().PubKey.Compress().GetSegwitAddress(network);
            TransactionBuilder      builder = new TransactionBuilder(network);

            Transaction unsigned =
                builder
                .AddCoins(coin)
                .Send(spendMultiSigToAddress, Money.Coins(1.0m))
                .SetChange(scriptPubKey)
                .SendFees(this.fee)
                .BuildTransaction(sign: false);

            Transaction aliceSigned =
                builder
                .AddCoins(coin)
                .AddKeys(alice)
                .SignTransaction(unsigned);

            Transaction bobSigned =
                builder
                .AddCoins(coin)
                .AddKeys(bob)
                //At this line, SignTransaction(unSigned) has the identical functionality with the SignTransaction(aliceSigned).
                //It's because unsigned transaction has already been signed by Alice privateKey from above.
                .SignTransaction(aliceSigned);

            Transaction fullySigned =
                builder
                .AddCoins(coin)
                .CombineSignatures(aliceSigned, bobSigned);

            bool isVerifyPassing = builder.Verify(fullySigned, out var errors);

            foreach (var err in errors)
            {
                this.output.WriteLine(err.ToString());
            }

            this.output.WriteLine($"isVerifyPassing: {isVerifyPassing}");
            this.output.WriteLine(fullySigned.ToString());

            Assert.True(isVerifyPassing);
            Assert.False(fullySigned.HasWitness);
        }
예제 #8
0
		//https://gist.github.com/gavinandresen/3966071
		public void CanBuildTransactionWithDustPrevention()
		{
			var bob = new Key();
			var alice = new Key();
			var tx = new Transaction()
			{
				Outputs =
				{
					new TxOut(Money.Coins(1.0m), bob)
				}
			};
			var coins = tx.Outputs.AsCoins().ToArray();

			var builder = new TransactionBuilder();
			var signed =
				builder
				.AddCoins(coins)
				.AddKeys(bob)
				.Send(alice, Money.Coins(0.99m))
				.Send(alice, Money.Satoshis(599))
				.Send(TxNullDataTemplate.Instance.GenerateScriptPubKey(new byte[] { 1, 2 }), Money.Zero)
				.SendFees(Money.Coins(0.0001m))
				.SetChange(bob)
				.BuildTransaction(true);
			Assert.True(signed.Outputs.Count == 3);
			Assert.True(builder.Verify(signed, Money.Coins(0.0001m)));
			builder.DustPrevention = false;
			var ex = Assert.Throws<NotEnoughFundsException>(() => builder.Verify(signed, Money.Coins(0.0001m)));
			Assert.True((Money)ex.Missing == Money.Parse("-0.00000599"));
		}
예제 #9
0
		public Transaction CreatePayment(Money paid, ScriptCoin fundingCoin)
		{
			if(paid > GetFundAmount())
				throw new MicroPaymentException("Payment reached the maximum");
			var builder = new TransactionBuilder();
			if(fundingCoin.Redeem != Redeem || fundingCoin.Amount != Amount + Fees)
				throw new MicroPaymentException("Invalid funding coin");

			var fees = Money.Min(paid, Fees);
			var toPayer = GetFundAmount() - paid;
			var toPayee = paid - fees;
			return builder
				.AddCoins(fundingCoin)
				.Send(Payee.ScriptPubkey, toPayee)
				.Send(Payer.ScriptPubkey, toPayer)
				.SendFees(fees)
				.Shuffle()
				.BuildTransaction(false);
		}
예제 #10
0
		public void CanBuildColoredTransaction()
		{
			var gold = new Key();
			var silver = new Key();
			var goldId = gold.PubKey.ScriptPubKey.Hash.ToAssetId();
			var silverId = silver.PubKey.ScriptPubKey.Hash.ToAssetId();

			var satoshi = new Key();
			var bob = new Key();
			var alice = new Key();

			var repo = new NoSqlColoredTransactionRepository();

			var init = new Transaction()
			{
				Outputs =
				{
					new TxOut("1.0", gold.PubKey),
					new TxOut("1.0", silver.PubKey),
					new TxOut("1.0", satoshi.PubKey)
				}
			};

			repo.Transactions.Put(init);

			var issuanceCoins =
				init
				.Outputs
				.AsCoins()
				.Take(2)
				.Select((c, i) => new IssuanceCoin(c))
				.OfType<ICoin>().ToArray();

			var satoshiBTC = init.Outputs.AsCoins().Last();

			var coins = new List<ICoin>();
			coins.AddRange(issuanceCoins);
			var txBuilder = new TransactionBuilder();
			txBuilder.StandardTransactionPolicy = RelayPolicy;
			//Can issue gold to satoshi and bob
			var tx = txBuilder
				.AddCoins(coins.ToArray())
				.AddKeys(gold)
				.IssueAsset(satoshi.PubKey, new AssetMoney(goldId, 1000))
				.IssueAsset(bob.PubKey, new AssetMoney(goldId, 500))
				.SendFees("0.1")
				.SetChange(gold.PubKey)
				.BuildTransaction(true);
			Assert.True(txBuilder.Verify(tx, "0.1"));

			//Ensure BTC from the IssuanceCoin are returned
			Assert.Equal(Money.Parse("0.89994240"), tx.Outputs[2].Value);
			Assert.Equal(gold.PubKey.ScriptPubKey, tx.Outputs[2].ScriptPubKey);

			repo.Transactions.Put(tx);

			var colored = tx.GetColoredTransaction(repo);
			Assert.Equal(2, colored.Issuances.Count);
			Assert.True(colored.Issuances.All(i => i.Asset.Id == goldId));
			AssertHasAsset(tx, colored, colored.Issuances[0], goldId, 500, bob.PubKey);
			AssertHasAsset(tx, colored, colored.Issuances[1], goldId, 1000, satoshi.PubKey);

			var coloredCoins = ColoredCoin.Find(tx, colored).ToArray();
			Assert.Equal(2, coloredCoins.Length);

			//Can issue silver to bob, and send some gold to satoshi
			coins.Add(coloredCoins.First(c => c.ScriptPubKey == bob.PubKey.ScriptPubKey));
			txBuilder = new TransactionBuilder();
			txBuilder.StandardTransactionPolicy = EasyPolicy;
			tx = txBuilder
				.AddCoins(coins.ToArray())
				.AddKeys(silver, bob)
				.SetChange(bob.PubKey)
				.IssueAsset(bob.PubKey, new AssetMoney(silverId, 10))
				.SendAsset(satoshi.PubKey, new AssetMoney(goldId, 30))
				.BuildTransaction(true);

			Assert.True(txBuilder.Verify(tx));
			colored = tx.GetColoredTransaction(repo);
			Assert.Equal(1, colored.Inputs.Count);
			Assert.Equal(goldId, colored.Inputs[0].Asset.Id);
			Assert.Equal(500, colored.Inputs[0].Asset.Quantity);
			Assert.Equal(1, colored.Issuances.Count);
			Assert.Equal(2, colored.Transfers.Count);
			AssertHasAsset(tx, colored, colored.Transfers[0], goldId, 470, bob.PubKey);
			AssertHasAsset(tx, colored, colored.Transfers[1], goldId, 30, satoshi.PubKey);

			repo.Transactions.Put(tx);


			//Can swap : 
			//satoshi wants to send 100 gold to bob 
			//bob wants to send 200 silver, 5 gold and 0.9 BTC to satoshi

			//Satoshi receive gold
			txBuilder = new TransactionBuilder();
			txBuilder.StandardTransactionPolicy = RelayPolicy;
			tx = txBuilder
					.AddKeys(gold)
					.AddCoins(issuanceCoins)
					.IssueAsset(satoshi.PubKey, new AssetMoney(goldId, 1000UL))
					.SetChange(gold.PubKey)
					.SendFees(Money.Coins(0.0004m))
					.BuildTransaction(true);
			Assert.True(txBuilder.Verify(tx));
			repo.Transactions.Put(tx);
			var satoshiCoin = ColoredCoin.Find(tx, repo).First();


			//Gold receive 2.5 BTC
			tx = new Transaction()
			{
				Outputs =
				{
					new TxOut("2.5",gold.PubKey)
				}
			};
			repo.Transactions.Put(tx.GetHash(), tx);

			//Bob receive silver and 2 btc
			txBuilder = new TransactionBuilder();
			txBuilder.StandardTransactionPolicy = RelayPolicy;
			tx = txBuilder
					.AddKeys(silver, gold)
					.AddCoins(issuanceCoins)
					.AddCoins(new Coin(new OutPoint(tx.GetHash(), 0), new TxOut("2.5", gold.PubKey.ScriptPubKey)))
					.IssueAsset(bob.PubKey, new AssetMoney(silverId, 300UL))
					.Send(bob.PubKey, "2.00")
					.SendFees(Money.Coins(0.0004m))
					.SetChange(gold.PubKey)
					.BuildTransaction(true);
			Assert.True(txBuilder.Verify(tx));
			repo.Transactions.Put(tx);

			var bobSilverCoin = ColoredCoin.Find(tx, repo).First();
			var bobBitcoin = new Coin(new OutPoint(tx.GetHash(), 2), tx.Outputs[2]);

			//Bob receive gold
			txBuilder = new TransactionBuilder();
			txBuilder.StandardTransactionPolicy = RelayPolicy;
			tx = txBuilder
					.AddKeys(gold)
					.AddCoins(issuanceCoins)
					.IssueAsset(bob.PubKey, new AssetMoney(goldId, 50UL))
					.SetChange(gold.PubKey)
					.SendFees(Money.Coins(0.0004m))
					.BuildTransaction(true);
			Assert.True(txBuilder.Verify(tx));
			repo.Transactions.Put(tx.GetHash(), tx);

			var bobGoldCoin = ColoredCoin.Find(tx, repo).First();

			txBuilder = new TransactionBuilder();
			txBuilder.StandardTransactionPolicy = RelayPolicy;
			tx = txBuilder
				.AddCoins(satoshiCoin)
				.AddCoins(satoshiBTC)
				.SendAsset(bob.PubKey, new AssetMoney(goldId, 100))
				.SendFees(Money.Coins(0.0004m))
				.SetChange(satoshi.PubKey)
				.Then()
				.AddCoins(bobSilverCoin, bobGoldCoin, bobBitcoin)
				.SendAsset(satoshi.PubKey, new AssetMoney(silverId, 200))
				.Send(satoshi.PubKey, "0.9")
				.SendAsset(satoshi.PubKey, new AssetMoney(goldId, 5))
				.SetChange(bob.PubKey)
				.BuildTransaction(false);

			colored = tx.GetColoredTransaction(repo);

			AssertHasAsset(tx, colored, colored.Inputs[0], goldId, 1000, null);
			AssertHasAsset(tx, colored, colored.Inputs[1], silverId, 300, null);

			AssertHasAsset(tx, colored, colored.Transfers[0], goldId, 900, satoshi.PubKey);
			AssertHasAsset(tx, colored, colored.Transfers[1], goldId, 100, bob.PubKey);

			AssertHasAsset(tx, colored, colored.Transfers[2], silverId, 100, bob.PubKey);
			AssertHasAsset(tx, colored, colored.Transfers[3], silverId, 200, satoshi.PubKey);

			AssertHasAsset(tx, colored, colored.Transfers[4], goldId, 45, bob.PubKey);
			AssertHasAsset(tx, colored, colored.Transfers[5], goldId, 5, satoshi.PubKey);

			Assert.True(tx.Outputs[8].Value == Money.Parse("1.0999424"));
			Assert.True(tx.Outputs[8].ScriptPubKey == bob.PubKey.ScriptPubKey);
			Assert.True(tx.Outputs[9].Value == Money.Parse("0.9"));
			Assert.True(tx.Outputs[9].ScriptPubKey == satoshi.PubKey.ScriptPubKey);

			tx = txBuilder.AddKeys(satoshi, bob).SignTransaction(tx);
			Assert.True(txBuilder.Verify(tx));


			//Bob send coins to Satoshi, but alice pay for the dust
			var funding =
				new TransactionBuilder()
				{
					StandardTransactionPolicy = RelayPolicy
				}
				.AddCoins(issuanceCoins)
				.AddKeys(gold)
				.IssueAsset(bob.PubKey.Hash, new AssetMoney(goldId, 100UL))
				.SetChange(gold.PubKey.Hash)
				.SendFees(Money.Coins(0.0004m))
				.BuildTransaction(true);

			repo.Transactions.Put(funding);

			var bobGold = ColoredCoin.Find(funding, repo).ToArray();

			Transaction transfer = null;
			try
			{
				transfer =
					new TransactionBuilder()
					{
						StandardTransactionPolicy = RelayPolicy
					}
					.AddCoins(bobGold)
					.SendAsset(alice.PubKey.Hash, new AssetMoney(goldId, 40UL))
					.SetChange(bob.PubKey.Hash)
					.BuildTransaction(true);
				Assert.False(true, "Should have thrown");
			}
			catch(NotEnoughFundsException ex) //Not enough dust to send the change
			{
				Assert.True(((Money)ex.Missing).Satoshi == 2730);
				var rate = new FeeRate(Money.Coins(0.0004m));
				txBuilder = new TransactionBuilder();
				txBuilder.StandardTransactionPolicy = RelayPolicy;
				transfer =
					txBuilder
					.AddCoins(bobGold)
					.AddCoins(((IssuanceCoin)issuanceCoins[0]).Bearer)
					.AddKeys(gold, bob)
					.SendAsset(alice.PubKey, new AssetMoney(goldId, 40UL))
					.SetChange(bob.PubKey, ChangeType.Colored)
					.SetChange(gold.PubKey.Hash, ChangeType.Uncolored)
					.SendEstimatedFees(rate)
					.BuildTransaction(true);
				var fee = transfer.GetFee(txBuilder.FindSpentCoins(transfer));
				Assert.True(txBuilder.Verify(transfer, fee));

				repo.Transactions.Put(funding.GetHash(), funding);

				colored = ColoredTransaction.FetchColors(transfer, repo);
				AssertHasAsset(transfer, colored, colored.Transfers[0], goldId, 60, bob.PubKey);
				AssertHasAsset(transfer, colored, colored.Transfers[1], goldId, 40, alice.PubKey);

				var change = transfer.Outputs.Last(o => o.ScriptPubKey == gold.PubKey.Hash.ScriptPubKey);
				Assert.Equal(Money.Coins(0.99980450m), change.Value);

				Assert.Equal(gold.PubKey.Hash, change.ScriptPubKey.GetDestination());

				//Verify issuancecoin can have an url
				var issuanceCoin = (IssuanceCoin)issuanceCoins[0];
				issuanceCoin.DefinitionUrl = new Uri("http://toto.com/");
				txBuilder = new TransactionBuilder();
				tx = txBuilder
					.AddKeys(gold)
					.AddCoins(issuanceCoin)
					.IssueAsset(bob, new AssetMoney(gold.PubKey, 10))
					.SetChange(gold)
					.BuildTransaction(true);

				Assert.Equal("http://toto.com/", tx.GetColoredMarker().GetMetadataUrl().AbsoluteUri);
			}
		}
예제 #11
0
		private static TransactionBuilder CreateBuilder(CKeyStore keystore, Transaction txFrom)
		{
			var coins = txFrom.Outputs.AsCoins().ToArray();
			var builder = new TransactionBuilder()
			{
				StandardTransactionPolicy = new StandardTransactionPolicy()
				{
					CheckFee = false,
					MinRelayTxFee = null,
					UseConsensusLib = false,
					CheckScriptPubKey = false
				}
			}
			.AddCoins(coins)
			.AddKeys(keystore._Keys.Select(k => k.Item1).ToArray())
			.AddKnownRedeems(keystore._Scripts.ToArray());
			return builder;
		}
예제 #12
0
		public void CanBuildTransaction()
		{
			var keys = Enumerable.Range(0, 5).Select(i => new Key()).ToArray();

			var multiSigPubKey = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, keys.Select(k => k.PubKey).Take(3).ToArray());
			var pubKeyPubKey = PayToPubkeyTemplate.Instance.GenerateScriptPubKey(keys[4].PubKey);
			var pubKeyHashPubKey = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(keys[4].PubKey.Hash);
			var scriptHashPubKey1 = PayToScriptHashTemplate.Instance.GenerateScriptPubKey(multiSigPubKey.Hash);
			var scriptHashPubKey2 = PayToScriptHashTemplate.Instance.GenerateScriptPubKey(pubKeyPubKey.Hash);
			var scriptHashPubKey3 = PayToScriptHashTemplate.Instance.GenerateScriptPubKey(pubKeyHashPubKey.Hash);


			var coins = new[] { multiSigPubKey, pubKeyPubKey, pubKeyHashPubKey }.Select((script, i) =>
				new Coin
					(
					new OutPoint(Rand(), i),
					new TxOut(new Money((i + 1) * Money.COIN), script)
					)).ToList();

			var scriptCoins =
				new[] { scriptHashPubKey1, scriptHashPubKey2, scriptHashPubKey3 }
				.Zip(new[] { multiSigPubKey, pubKeyPubKey, pubKeyHashPubKey },
					(script, redeem) => new
					{
						script,
						redeem
					})
				.Select((_, i) =>
				new ScriptCoin
					(
					new OutPoint(Rand(), i),
					new TxOut(new Money((i + 1) * Money.COIN), _.script), _.redeem
					)).ToList();

			var witCoins =
			new[] { scriptHashPubKey1, scriptHashPubKey2, scriptHashPubKey3 }
			.Zip(new[] { multiSigPubKey, pubKeyPubKey, pubKeyHashPubKey },
				(script, redeem) => new
				{
					script,
					redeem
				})
			.Select((_, i) =>
			new WitScriptCoin
				(
				new OutPoint(Rand(), i),
				new TxOut(new Money((i + 1) * Money.COIN), _.redeem.WitHash.ScriptPubKey.Hash),
				_.redeem
				)).ToList();
			var a = witCoins.Select(c => c.Amount).Sum();
			var allCoins = coins.Concat(scriptCoins).Concat(witCoins).ToArray();
			var destinations = keys.Select(k => k.PubKey.GetAddress(Network.Main)).ToArray();

			var txBuilder = new TransactionBuilder(0);
			txBuilder.StandardTransactionPolicy = EasyPolicy;
			var tx = txBuilder
				.AddCoins(allCoins)
				.AddKeys(keys)
				.Send(destinations[0], Money.Parse("6") * 2)
				.Send(destinations[2], Money.Parse("5"))
				.Send(destinations[2], Money.Parse("0.9999"))
				.SendFees(Money.Parse("0.0001"))
				.SetChange(destinations[3])
				.BuildTransaction(true);
			Assert.True(txBuilder.Verify(tx, "0.0001"));

			Assert.Equal(3, tx.Outputs.Count);

			txBuilder = new TransactionBuilder(0);
			txBuilder.StandardTransactionPolicy = EasyPolicy;
			tx = txBuilder
			   .AddCoins(allCoins)
			   .AddKeys(keys)
			   .SetGroupName("test")
			   .Send(destinations[0], Money.Parse("6") * 2)
			   .Send(destinations[2], Money.Parse("5"))
			   .Send(destinations[2], Money.Parse("0.9998"))
			   .SendFees(Money.Parse("0.0001"))
			   .SetChange(destinations[3])
			   .BuildTransaction(true);

			Assert.Equal(4, tx.Outputs.Count); //+ Change

			txBuilder.Send(destinations[4], Money.Parse("1"));
			var ex = Assert.Throws<NotEnoughFundsException>(() => txBuilder.BuildTransaction(true));
			Assert.True(ex.Group == "test");
			Assert.True((Money)ex.Missing == Money.Parse("0.9999"));
			//Can sign partially
			txBuilder = new TransactionBuilder(0);
			txBuilder.StandardTransactionPolicy = EasyPolicy;
			tx = txBuilder
					.AddCoins(allCoins)
					.AddKeys(keys.Skip(2).ToArray())  //One of the multi key missing
					.Send(destinations[0], Money.Parse("6") * 2)
					.Send(destinations[2], Money.Parse("5"))
					.Send(destinations[2], Money.Parse("0.9998"))
					.SendFees(Money.Parse("0.0001"))
					.SetChange(destinations[3])
					.Shuffle()
					.BuildTransaction(true);
			Assert.False(txBuilder.Verify(tx, "0.0001"));

			txBuilder = new TransactionBuilder(0);
			tx = txBuilder
					.AddKeys(keys[0])
					.AddCoins(allCoins)
					.SignTransaction(tx);

			Assert.True(txBuilder.Verify(tx));

			//Test if signing separatly
			txBuilder = new TransactionBuilder(0);
			txBuilder.StandardTransactionPolicy = EasyPolicy;
			tx = txBuilder
					.AddCoins(allCoins)
					.AddKeys(keys.Skip(2).ToArray())  //One of the multi key missing
					.Send(destinations[0], Money.Parse("6") * 2)
					.Send(destinations[2], Money.Parse("5"))
					.Send(destinations[2], Money.Parse("0.9998"))
					.SendFees(Money.Parse("0.0001"))
					.SetChange(destinations[3])
					.Shuffle()
					.BuildTransaction(false);

			var signed1 = txBuilder.SignTransaction(tx);

			txBuilder = new TransactionBuilder(0);
			var signed2 = txBuilder
					.AddKeys(keys[0])
					.AddCoins(allCoins)
					.SignTransaction(tx);

			Assert.False(txBuilder.Verify(signed1));
			Assert.False(txBuilder.Verify(signed2));

			txBuilder = new TransactionBuilder(0);
			txBuilder.StandardTransactionPolicy = EasyPolicy;
			tx = txBuilder
				.AddCoins(allCoins)
				.CombineSignatures(signed1, signed2);
			Assert.True(txBuilder.Verify(tx));

			//Check if can deduce scriptPubKey from P2SH and P2SPKH scriptSig
			allCoins = new[]
				{ 
					RandomCoin(Money.Parse("1.0"), keys[0].PubKey.Hash.ScriptPubKey, false),
					RandomCoin(Money.Parse("1.0"), keys[0].PubKey.Hash.ScriptPubKey, false),
					RandomCoin(Money.Parse("1.0"), keys[1].PubKey.Hash.ScriptPubKey, false)
				};

			txBuilder = new TransactionBuilder(0);
			txBuilder.StandardTransactionPolicy = EasyPolicy;
			tx =
				txBuilder.AddCoins(allCoins)
					 .Send(destinations[0], Money.Parse("3.0"))
					 .BuildTransaction(false);

			signed1 = new TransactionBuilder(0)
						.AddCoins(allCoins)
						.AddKeys(keys[0])
						.SignTransaction(tx);

			signed2 = new TransactionBuilder(0)
						.AddCoins(allCoins)
						.AddKeys(keys[1])
						.SignTransaction(tx);

			Assert.False(txBuilder.Verify(signed1));
			Assert.False(txBuilder.Verify(signed2));

			tx = new TransactionBuilder(0)
				.CombineSignatures(signed1, signed2);

			Assert.True(txBuilder.Verify(tx));

			//Using the same set of coin in 2 group should not use two times the sames coins
			for(int i = 0 ; i < 3 ; i++)
			{
				txBuilder = new TransactionBuilder();
				txBuilder.StandardTransactionPolicy = EasyPolicy;
				tx =
					txBuilder
					.AddCoins(allCoins)
					.AddKeys(keys)
					.Send(destinations[0], Money.Parse("2.0"))
					.Then()
					.AddCoins(allCoins)
					.AddKeys(keys)
					.Send(destinations[0], Money.Parse("1.0"))
					.BuildTransaction(true);
				Assert.True(txBuilder.Verify(tx));
			}
		}
예제 #13
0
        public void WhenGetUnspentTransactionsThreeBlocks()
        {
            RemoveBlockChain();
            var serviceProvider     = BuildServiceProvider();
            var blockChainFactory   = serviceProvider.GetService <IBlockChainFactory>();
            var blockChain          = blockChainFactory.Build(_network);
            var genesisBlock        = blockChain.GetCurrentBlock();
            var firstTransaction    = genesisBlock.Transactions.First() as BcBaseTransaction;
            var firstTransactionOut = firstTransaction.TransactionOut.First();

            var genesisKey = KeyStore.GetGenesisKey();
            var genesisAdr = new BlockChainAddress(_scriptTypes, _network, genesisKey); // Create block chain address.
            var destinationBlockChainAddress = GenerateBlockChainAddress();
            var minerBlockChainAddress       = GenerateBlockChainAddress();

            var signature     = genesisKey.GetSignature(); // Create the script.
            var scriptBuilder = new ScriptBuilder();
            var genesisScript = scriptBuilder
                                .New()
                                .AddToStack(signature)
                                .AddToStack(genesisKey.GetPublicKey())
                                .Build();
            var destinationScript = Script.CreateP2PKHScript(destinationBlockChainAddress.PublicKeyHash);
            var minerScript       = Script.CreateP2PKHScript(minerBlockChainAddress.PublicKeyHash);
            var genesisScriptDest = Script.CreateP2PKHScript(genesisKey.GetPublicKeyHashed());

            var transactionBuilder  = new TransactionBuilder();
            var coinBaseTransaction = transactionBuilder // Add COIN-BASE TRANSACTION.
                                      .NewCoinbaseTransaction()
                                      .SetBlockNumber(1)
                                      .AddOutput(1, minerScript)
                                      .Build();
            var noneCoinBaseTransaction = transactionBuilder // ADD GENESIS (10 BTC) => DESTINATION TRANSACTION.
                                          .NewNoneCoinbaseTransaction()
                                          .Spend(firstTransaction, 0, genesisScript.Serialize())
                                          .AddOutput(10, destinationScript)
                                          .Build();
            var otherCoinBaseTransaction = transactionBuilder
                                           .NewNoneCoinbaseTransaction()
                                           .Spend(firstTransaction, 0, genesisScript.Serialize())
                                           .AddOutput(39, genesisScriptDest)
                                           .Build();

            var nonce      = NonceHelper.GetNonceUInt32(); // CREATE A BLOCK.
            var firstBlock = new Block(genesisBlock.GetHashHeader(), Constants.DEFAULT_NBITS, nonce);

            firstBlock.Transactions.Add(coinBaseTransaction);
            firstBlock.Transactions.Add(noneCoinBaseTransaction);
            firstBlock.Transactions.Add(otherCoinBaseTransaction);
            firstBlock.UpdateMerkleRoot();
            blockChain.AddBlock(firstBlock);
            var unspentTransactions = blockChain.GetUnspentTransactions();

            Assert.IsNotNull(unspentTransactions);
            Assert.IsTrue(unspentTransactions.Count() == 3);

            // ADD THIRD BLOCK.
            coinBaseTransaction = transactionBuilder // Add COIN-BASE TRANSACTION.
                                  .NewCoinbaseTransaction()
                                  .SetBlockNumber(2)
                                  .AddOutput(1, minerScript)
                                  .Build();
            noneCoinBaseTransaction = transactionBuilder // ADD GENESIS (2 BTC) => DESTINATION TRANSACTION.
                                      .NewNoneCoinbaseTransaction()
                                      .Spend(otherCoinBaseTransaction, 0, genesisScript.Serialize())
                                      .AddOutput(2, destinationScript)
                                      .Build();
            otherCoinBaseTransaction = transactionBuilder
                                       .NewNoneCoinbaseTransaction()
                                       .Spend(otherCoinBaseTransaction, 0, genesisScript.Serialize())
                                       .AddOutput(36, genesisScriptDest)
                                       .Build();
            nonce = NonceHelper.GetNonceUInt32(); // CREATE A BLOCK.
            var secondBlock = new Block(genesisBlock.GetHashHeader(), Constants.DEFAULT_NBITS, nonce);

            secondBlock.Transactions.Add(coinBaseTransaction);
            secondBlock.Transactions.Add(noneCoinBaseTransaction);
            secondBlock.Transactions.Add(otherCoinBaseTransaction);
            secondBlock.UpdateMerkleRoot();
            blockChain.AddBlock(secondBlock);

            unspentTransactions = blockChain.GetUnspentTransactions();
            Assert.IsTrue(unspentTransactions.Count() == 5);
            Assert.IsTrue(unspentTransactions.Sum(t => t.Value) == 50);
        }
예제 #14
0
		public void CanSplitFees()
		{
			var satoshi = new Key();
			var alice = new Key();
			var bob = new Key();

			var aliceCoins = new ICoin[] { RandomCoin("0.4", alice), RandomCoin("0.6", alice) };
			var bobCoins = new ICoin[] { RandomCoin("0.2", bob), RandomCoin("0.3", bob) };

			TransactionBuilder builder = new TransactionBuilder();
			FeeRate rate = new FeeRate(Money.Coins(0.0004m));
			var tx = builder
				.AddCoins(aliceCoins)
				.AddKeys(alice)
				.Send(satoshi, Money.Coins(0.1m))
				.SetChange(alice)
				.Then()
				.AddCoins(bobCoins)
				.AddKeys(bob)
				.Send(satoshi, Money.Coins(0.01m))
				.SetChange(bob)
				.SendEstimatedFeesSplit(rate)
				.BuildTransaction(true);

			var estimated = builder.EstimateFees(tx, rate);

			Assert.True(builder.Verify(tx, estimated));
		}
예제 #15
0
        public void CanGetWalletOrderedBalances()
        {
            using(var tester = CreateTester())
            {
                var bob = new Key();
                var alice1 = new Key();
                var alice2 = new Key();
                var satoshi = new Key();

                var expectedRule = tester.Client.AddWalletRule("Alice", new ScriptRule(alice1)
                {
                    CustomData = "hello"
                });
                Assert.True(expectedRule.Rule.ToString().Contains("hello"));
                var rules = tester.Client.GetWalletRules("Alice");
                Assert.Equal(1, rules.Length);
                Assert.Equal(expectedRule.WalletId, rules[0].WalletId);
                Assert.Equal(expectedRule.Rule.ToString(), rules[0].Rule.ToString());
                var aliceR1 = expectedRule.Rule;

                var chainBuilder = tester.CreateChainBuilder();
                chainBuilder.EmitMoney(bob, "50.0");
                var tx = chainBuilder.EmitMoney(alice1, "10.0");
                chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                var aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(aliceBalance.Length == 1);
                Assert.Equal("Alice", aliceBalance[0].BalanceId.GetWalletId());
                Assert.True(aliceBalance[0].Amount == Money.Parse("10.0"));
                Assert.True(aliceBalance[0].IsCoinbase);
                Assert.True(aliceBalance[0].ScriptPubKey == alice1.ScriptPubKey);
                Assert.True(!aliceBalance[0].HasOpReturn);
                Assert.Equal(
                    aliceR1.ToString()
                   , aliceBalance[0].GetMatchedRules(0, MatchLocation.Output).First().ToString());

                var aliceR2 = tester.Client.AddWalletRule("Alice", new ScriptRule(alice2)).Rule;
                rules = tester.Client.GetWalletRules("Alice");
                Assert.Equal(2, rules.Length);

                //Adding two time same rule should be idempotent
                tester.Client.AddWalletRule("Alice", new ScriptRule(alice2));
                Assert.Equal(2, rules.Length);
                /////////////////////////////////////////////


                tx
                    = new TransactionBuilder()
                        .AddKeys(alice1)
                        .AddCoins(new Coin(tx.GetHash(), 0, tx.Outputs[0].Value, tx.Outputs[0].ScriptPubKey))
                        .Send(alice2, "2.0")
                        .Send(alice1, "3.9")
                        .Send(bob, "2.1")
                        .Send(alice1, "0.1")
                        .SendFees("1.9")
                        .BuildTransaction(true);

                chainBuilder.Emit(tx);
                chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();


                aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(aliceBalance[0].Amount == Money.Parse("-4.0"));

                Assert.Equal(
                   aliceR1.ToString()
                  , aliceBalance[0].GetMatchedRules(aliceBalance[0].SpentCoins[0]).First().ToString());

                Assert.Equal(
                   aliceR2.ToString()
                  , aliceBalance[0].GetMatchedRules(0, MatchLocation.Output).First().ToString());

                Assert.Equal(
                   aliceR1.ToString()
                  , aliceBalance[0].GetMatchedRules(1, MatchLocation.Output).First().ToString());

                Assert.Equal(
                aliceR1.ToString()
               , aliceBalance[0].GetMatchedRules(3, MatchLocation.Output).First().ToString());

                Assert.True(aliceBalance[0].GetMatchedRules(2, MatchLocation.Output).Count() == 0);

                var prevTx = tx;
                var newtx = new Transaction()
                {
                    Inputs =
                    {
                        new TxIn(new OutPoint(tx,0)), //alice2 2
                        new TxIn(new OutPoint(tx,1)), //alice1 3.9
                        new TxIn(new OutPoint(tx,2)), //bob 2.1
                        new TxIn(new OutPoint(tx,3)), //alice1 0.1
                    }
                };

                tx = new TransactionBuilder()
                        .ContinueToBuild(newtx)
                        .AddKeys(alice1, alice2)
                        .AddCoins(new Coin(prevTx.GetHash(), 0, prevTx.Outputs[0].Value, prevTx.Outputs[0].ScriptPubKey))
                        .AddCoins(new Coin(prevTx.GetHash(), 1, prevTx.Outputs[1].Value, prevTx.Outputs[1].ScriptPubKey))
                        .AddCoins(new Coin(prevTx.GetHash(), 3, prevTx.Outputs[3].Value, prevTx.Outputs[3].ScriptPubKey))
                        .Then()
                        .AddKeys(bob)
                        .AddCoins(new Coin(prevTx.GetHash(), 2, prevTx.Outputs[2].Value, prevTx.Outputs[2].ScriptPubKey))
                        .Send(alice1, "0.10")
                        .Send(alice2, "0.22")
                        .Send(bob, "1.0")
                        .Send(alice2, "0.23")
                        .SetChange(satoshi)
                        .BuildTransaction(true);

                chainBuilder.Emit(tx);
                var b3 = chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();


                aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                var entry = aliceBalance[0];

                Assert.Equal(entry.GetMatchedRules(new OutPoint(prevTx, 0)).First().ToString(), aliceR2.ToString());
                Assert.Equal(entry.GetMatchedRules(new OutPoint(prevTx, 1)).First().ToString(), aliceR1.ToString());
                Assert.Null(entry.GetMatchedRules(new OutPoint(prevTx, 2)).FirstOrDefault());
                Assert.Equal(entry.GetMatchedRules(new OutPoint(prevTx, 3)).First().ToString(), aliceR1.ToString());

                var receivedOutpoints = tx.Outputs.Select((o, i) => new OutPoint(tx.GetHash(), i)).ToArray();
                Assert.Equal(entry.GetMatchedRules(new OutPoint(tx, 1)).First().ToString(), aliceR1.ToString());
                Assert.Equal(entry.GetMatchedRules(new OutPoint(tx, 2)).First().ToString(), aliceR2.ToString());
                Assert.Null(entry.GetMatchedRules(new OutPoint(tx, 3)).FirstOrDefault());
                Assert.Equal(entry.GetMatchedRules(new OutPoint(tx, 4)).First().ToString(), aliceR2.ToString());
                ////

                //Send money to P2SH address, should receive script coins

                tester.Client.AddWalletRule("Alice", new ScriptRule(alice1.PubKey, true));
                tester.Client.AddWalletRule("Alice", new ScriptRule(alice2.PubKey.ScriptPubKey.Hash, false));

                tx = new TransactionBuilder()
                        .ContinueToBuild(newtx)
                        .AddKeys(alice1, alice2)
                        .AddCoins(new Coin(prevTx.GetHash(), 0, prevTx.Outputs[0].Value, prevTx.Outputs[0].ScriptPubKey))
                        .AddCoins(new Coin(prevTx.GetHash(), 1, prevTx.Outputs[1].Value, prevTx.Outputs[1].ScriptPubKey))
                        .AddCoins(new Coin(prevTx.GetHash(), 3, prevTx.Outputs[3].Value, prevTx.Outputs[3].ScriptPubKey))
                        .Then()
                        .AddKeys(bob)
                        .AddCoins(new Coin(prevTx.GetHash(), 2, prevTx.Outputs[2].Value, prevTx.Outputs[2].ScriptPubKey))
                        .Send(alice1.PubKey.ScriptPubKey.Hash, "0.10")
                        .Send(alice2.PubKey.ScriptPubKey.Hash, "0.22")
                        .Send(bob, "1.0")
                        .Send(alice2.PubKey.ScriptPubKey.Hash, "0.23")
                        .SetChange(satoshi)
                        .BuildTransaction(true);


                chainBuilder.Emit(tx);
                chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(aliceBalance[0].ReceivedCoins[0] is ScriptCoin);
                Assert.True(aliceBalance[0].ReceivedCoins[0].TxOut.ScriptPubKey == alice1.PubKey.ScriptPubKey.Hash.ScriptPubKey);
                Assert.True(((ScriptCoin)(aliceBalance[0].ReceivedCoins[0])).Redeem == alice1.PubKey.ScriptPubKey);
                Assert.False(aliceBalance[0].ReceivedCoins[1] is ScriptCoin);
                Assert.False(aliceBalance[0].ReceivedCoins[2] is ScriptCoin);

                tx = new TransactionBuilder()
                        .AddKeys(alice1, alice2)
                        .AddCoins(aliceBalance[0].ReceivedCoins[0])
                        .Send(satoshi, "0.0001")
                        .SetChange(alice1)
                        .BuildTransaction(true);
                chainBuilder.Emit(tx);
                chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                var aliceBalance2 = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(((ScriptCoin)(aliceBalance2[0].SpentCoins[0])).Redeem == alice1.PubKey.ScriptPubKey);
                /////
            }

        }
예제 #16
0
        public void CanMergeBalance()
        {
            using(var tester = CreateTester())
            {
                var bob = new Key();
                var alice1 = new Key();
                var alice2 = new Key();
                var satoshi = new Key();

                var chainBuilder = tester.CreateChainBuilder();
                chainBuilder.EmitMoney(bob, "50.0");
                var tx = chainBuilder.EmitMoney(alice1, "10.0");
                chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                //Can merge address balance into wallet
                tester.Client.MergeIntoWallet("Alice", alice1);

                var aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(aliceBalance.Length == 1);
                Assert.True(aliceBalance[0].Amount == Money.Parse("10.0"));
                Assert.True(aliceBalance[0].IsCoinbase);
                Assert.True(aliceBalance[0].ScriptPubKey == alice1.ScriptPubKey);
                Assert.True(!aliceBalance[0].HasOpReturn);
                ////

                //Merging duplicate order balance should not change anything
                tester.Client.AddWalletRule("Alice", new ScriptRule(alice1));

                chainBuilder.EmitMoney(alice1, "9.0");
                chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(aliceBalance.Length == 2);
                Assert.True(aliceBalance[0].Amount == Money.Parse("9.0"));
                Assert.True(aliceBalance[0].ScriptPubKey == alice1.ScriptPubKey);

                tester.Client.MergeIntoWallet("Alice", alice1);
                aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(aliceBalance.Length == 2);
                Assert.True(aliceBalance[0].Amount == Money.Parse("9.0"));
                Assert.True(aliceBalance[0].ScriptPubKey == alice1.ScriptPubKey);
                Assert.True(aliceBalance[1].Amount == Money.Parse("10.0"));
                Assert.True(aliceBalance[1].ScriptPubKey == alice1.ScriptPubKey);
                ////

                //Merge alice2 into Alice with a tx involving alice1
                tx
                   = new TransactionBuilder()
                       .AddKeys(alice1)
                       .AddCoins(new Coin(tx.GetHash(), 0, tx.Outputs[0].Value, tx.Outputs[0].ScriptPubKey)) //Alice1 10
                       .Send(alice2, "2.0")
                       .Send(alice1, "3.9")
                       .Send(bob, "2.1")
                       .Send(alice1, "0.1")
                       .SendFees("1.9")
                       .BuildTransaction(true);

                chainBuilder.Emit(tx);
                chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(aliceBalance.Length == 3);
                Assert.True(aliceBalance[0].Amount ==
                    -Money.Parse("10.0")
                    + Money.Parse("3.9")
                    + Money.Parse("0.1"));
                Assert.True(aliceBalance[0].ScriptPubKey == alice1.ScriptPubKey);

                tester.Client.MergeIntoWallet("Alice", alice2);
                aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(aliceBalance.Length == 3);
                Assert.True(aliceBalance[0].Amount ==
                    -Money.Parse("10.0")
                    + Money.Parse("3.9")
                    + Money.Parse("0.1")
                    + Money.Parse("2.0"));
                Assert.True(aliceBalance[0].ScriptPubKey == alice1.ScriptPubKey);

                var newtx = new Transaction()
                {
                    Inputs =
                    {
                        new TxIn(new OutPoint(tx,0)), //alice2 2
                        new TxIn(new OutPoint(tx,1)), //alice1 3.9
                        new TxIn(new OutPoint(tx,2)), //bob 2.1
                        new TxIn(new OutPoint(tx,3)), //alice1 0.1
                    }
                };

                tx = new TransactionBuilder()
                        .ContinueToBuild(newtx)
                        .AddKeys(alice1, alice2)
                        .AddCoins(new Coin(tx.GetHash(), 0, tx.Outputs[0].Value, tx.Outputs[0].ScriptPubKey))
                        .AddCoins(new Coin(tx.GetHash(), 1, tx.Outputs[1].Value, tx.Outputs[1].ScriptPubKey))
                        .AddCoins(new Coin(tx.GetHash(), 3, tx.Outputs[3].Value, tx.Outputs[3].ScriptPubKey))
                        .Then()
                        .AddKeys(bob)
                        .AddCoins(new Coin(tx.GetHash(), 2, tx.Outputs[2].Value, tx.Outputs[2].ScriptPubKey))
                        .Send(alice1, "0.10")
                        .Send(alice2, "0.22")
                        .Send(bob, "1.0")
                        .Send(alice2, "0.23")
                        .SetChange(satoshi)
                        .BuildTransaction(true);

                chainBuilder.Emit(tx);
                chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(aliceBalance.Length == 4);
                Assert.True(aliceBalance[0].Amount ==
                    -Money.Parse("3.9")
                    - Money.Parse("0.1")
                    + Money.Parse("0.10")
                    );
                Assert.True(aliceBalance[0].ScriptPubKey == alice1.ScriptPubKey);

                tester.Client.MergeIntoWallet("Alice", alice2, new ScriptRule()
                {
                    ScriptPubKey = alice2.ScriptPubKey,
                    CustomData = "hello"
                });
                aliceBalance = tester.Client.GetOrderedBalance("Alice").ToArray();
                Assert.True(aliceBalance.Length == 4);
                Assert.True(aliceBalance[0].Amount ==
                    -Money.Parse("3.9")
                    - Money.Parse("0.1")
                    + Money.Parse("0.10")
                    - Money.Parse("2.0")
                    + Money.Parse("0.22")
                    + Money.Parse("0.23")
                    );
                Assert.True(aliceBalance[0].ScriptPubKey == alice1.ScriptPubKey);
                Assert.True(aliceBalance[0].MatchedRules.Any(m => m.Rule.CustomData == "hello"));
                ////
            }
        }
예제 #17
0
        public void CanGetColoredBalance()
        {
            using(var tester = CreateTester())
            {
                var chainBuilder = tester.CreateChainBuilder();
                tester.Client.ColoredBalance = true;

                //Colored coin Payment
                //GoldGuy emits gold to Nico
                var txBuilder = new TransactionBuilder();

                var issuanceCoinsTransaction
                    = new Transaction()
                    {
                        Outputs =
                        {
                            new TxOut("1.0", goldGuy.PrivateKey.PubKey),
                            new TxOut("1.0", silverGuy.PrivateKey.PubKey),
                            new TxOut("1.0", nico.GetAddress()),
                            new TxOut("1.0", alice.GetAddress()),
                        }
                    };

                IssuanceCoin[] issuanceCoins = issuanceCoinsTransaction
                                        .Outputs
                                        .Take(2)
                                        .Select((o, i) => new Coin(new OutPoint(issuanceCoinsTransaction.GetHash(), i), o))
                                        .Select(c => new IssuanceCoin(c))
                                        .ToArray();
                var goldIssuanceCoin = issuanceCoins[0];
                var silverIssuanceCoin = issuanceCoins[1];
                var nicoCoin = new Coin(new OutPoint(issuanceCoinsTransaction, 2), issuanceCoinsTransaction.Outputs[2]);
                var aliceCoin = new Coin(new OutPoint(issuanceCoinsTransaction, 3), issuanceCoinsTransaction.Outputs[3]);

                var goldId = goldIssuanceCoin.AssetId;
                var silverId = silverIssuanceCoin.AssetId;

                chainBuilder.Emit(issuanceCoinsTransaction);
                var b = chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                var balance = tester.Client.GetOrderedBalance(nico).ToArray();
                var entry = balance[0];
                Assert.NotNull(entry.ColoredTransaction);
                Assert.Equal(Money.Parse("1.0"), entry.Amount);

                txBuilder = new TransactionBuilder();
                txBuilder.StandardTransactionPolicy.MinRelayTxFee = new FeeRate(Money.Satoshis(1000));
                var tx = txBuilder
                    .AddKeys(goldGuy)
                    .AddCoins(goldIssuanceCoin)
                    .IssueAsset(nico.GetAddress(), new AssetMoney(goldId, 30))
                    .SetChange(goldGuy.PrivateKey.PubKey)
                    .BuildTransaction(true);

                chainBuilder.Emit(tx);
                b = chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                var ctx = new IndexerColoredTransactionRepository(tester.Indexer.Configuration);

                balance = tester.Client.GetOrderedBalance(nico.GetAddress()).ToArray();
                var coloredEntry = balance[0];
                Assert.Equal(Money.Parse("0.0"), coloredEntry.Amount);
                Assert.True(coloredEntry.GetAssetAmount(goldId).CompareTo(30L) == 0);

                var coloredCoins = ColoredCoin.Find(tx, ctx).ToArray();
                var nicoGold = coloredCoins[0];

                txBuilder = new TransactionBuilder(1);
                txBuilder.StandardTransactionPolicy.MinRelayTxFee = new FeeRate(Money.Satoshis(1000));
                //GoldGuy sends 20 gold to alice against 0.6 BTC. Nico sends 10 gold to alice + 0.02 BTC.
                tx = txBuilder
                    .AddKeys(goldGuy)
                    .AddCoins(goldIssuanceCoin)
                    .IssueAsset(alice.GetAddress(), new AssetMoney(goldId, 20))
                    .SetChange(goldGuy.PrivateKey.PubKey)
                    .Then()
                    .AddKeys(nico.PrivateKey)
                    .AddCoins(nicoCoin)
                    .AddCoins(nicoGold)
                    .SendAsset(alice.GetAddress(), new AssetMoney(goldId, 10))
                    .Send(alice.GetAddress(), Money.Parse("0.02"))
                    .SetChange(nico.GetAddress())
                    .Then()
                    .AddKeys(alice)
                    .AddCoins(aliceCoin)
                    .Send(goldGuy.GetAddress(), Money.Parse("0.6"))
                    .SetChange(alice.GetAddress())
                    .Shuffle()
                    .BuildTransaction(true);

                chainBuilder.Emit(tx);
                b = chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                //Nico, should have lost 0.02 BTC and 10 gold
                balance = tester.Client.GetOrderedBalance(nico.GetAddress()).ToArray();
                balance = tester.Client.GetOrderedBalance(nico.GetAddress()).ToArray();
                coloredEntry = balance[0];
                Assert.Equal(Money.Parse("-0.02") - Money.Satoshis(546), coloredEntry.Amount);
                Assert.True(coloredEntry.GetAssetAmount(goldId).CompareTo(-10L) == 0);

                //Alice, should have lost 0.58 BTC, but win 10 + 20 gold (one is a transfer, the other issuance)
                balance = tester.Client.GetOrderedBalance(alice.GetAddress()).ToArray();
                coloredEntry = balance[0];
                Assert.Equal(Money.Parse("-0.58"), coloredEntry.Amount);
                Assert.True(coloredEntry.GetAssetAmount(goldId).CompareTo(30L) == 0);
            }
        }
예제 #18
0
        public void CanGetOrderedBalances()
        {
            using(var tester = CreateTester())
            {
                var bob = new Key();
                var alice = new Key();
                var satoshi = new Key();

                var chainBuilder = tester.CreateChainBuilder();
                chainBuilder.EmitMoney(bob, "50.0");
                chainBuilder.EmitMoney(alice, "50.0");
                chainBuilder.SubmitBlock();

                chainBuilder.EmitMoney(bob, "20.0");
                chainBuilder.SubmitBlock();

                chainBuilder.SyncIndexer();

                var bobBalance = tester.Client.GetOrderedBalance(bob).ToArray();
                Assert.True(bobBalance.Length == 2);
                Assert.True(bobBalance[0].Amount == Money.Parse("20.0"));
                Assert.True(bobBalance[0].IsCoinbase);
                Assert.True(!bobBalance[0].HasOpReturn);
                Assert.True(bobBalance[1].Amount == Money.Parse("50.0"));

                var aliceBalance = tester.Client.GetOrderedBalance(alice).ToArray();
                var tx = new TransactionBuilder()
                    .AddCoins(bobBalance[0].ReceivedCoins)
                    .AddKeys(bob)
                    .Send(alice, "5.0")
                    .SetChange(bob)
                    .Then()
                    .AddCoins(aliceBalance[0].ReceivedCoins)
                    .AddKeys(alice)
                    .Send(satoshi, "1.0")
                    .SendFees("0.05")
                    .SetChange(alice)
                    .BuildTransaction(true);
                tx.AddOutput(new TxOut(Money.Zero, TxNullDataTemplate.Instance.GenerateScriptPubKey(RandomUtils.GetBytes(3)))); //Add OP_RETURN
                chainBuilder.Emit(tx, false);
                var mempoolDate1 = tester.Client.GetTransaction(tx.GetHash()).MempoolDate.Value;

                var block = chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                var mempoolDate2 = tester.Client.GetTransaction(tx.GetHash()).MempoolDate.Value;
                Assert.Equal(mempoolDate1, mempoolDate2);

                bobBalance = tester.Client.GetOrderedBalance(bob).ToArray();
                Assert.True(bobBalance[0].Amount == -Money.Parse("5.0"));

                for(int i = 0; i < 2; i++)
                {

                    aliceBalance = tester.Client.GetOrderedBalance(alice).ToArray();
                    Assert.True(aliceBalance[0].Amount == -Money.Parse("1.0") - Money.Parse("0.05") + Money.Parse("5.0"));

                    Assert.True(aliceBalance[0].SpentIndices.Count == 1);
                    Assert.True(aliceBalance[0].SpentIndices[0] == 1);
                    Assert.True(aliceBalance[0].SpentOutpoints[0] == tx.Inputs[1].PrevOut);
                    Assert.True(aliceBalance[0].SpentCoins[0].Outpoint == aliceBalance[1].ReceivedCoins[0].Outpoint);
                    Assert.True(aliceBalance[0].TransactionId == tx.GetHash());
                    Assert.True(aliceBalance[0].Height == 3);
                    Assert.True(aliceBalance[0].BlockId == block.GetHash());
                    Assert.True(!aliceBalance[0].IsCoinbase);
                    Assert.True(aliceBalance[0].HasOpReturn);
                    Assert.True(aliceBalance[0].ReceivedCoins[0].Outpoint == new OutPoint(tx.GetHash(), 1)); //Bob coin
                    Assert.True(aliceBalance[0].ReceivedCoins[1].Outpoint == new OutPoint(tx.GetHash(), 2)); //Change
                }

                var satoshiBalance = tester.Client.GetOrderedBalance(satoshi).ToArray();
                Assert.True(satoshiBalance[0].Amount == Money.Parse("1.0"));

                tx = new TransactionBuilder()
                        .AddCoins(satoshiBalance[0].ReceivedCoins)
                        .AddKeys(satoshi)
                        .Send(alice, "0.2")
                        .SetChange(satoshi)
                        .BuildTransaction(true);

                tester.Indexer.Index(new TransactionEntry.Entity(null, tx, null));
                tester.Indexer.IndexOrderedBalance(tx);

                tx = new TransactionBuilder()
                       .AddCoins(satoshiBalance[0].ReceivedCoins)
                       .AddKeys(satoshi)
                       .Send(alice, "0.3")
                       .SetChange(satoshi)
                       .BuildTransaction(true);

                tester.Indexer.Index(new TransactionEntry.Entity(null, tx, null));
                tester.Indexer.IndexOrderedBalance(tx);

                satoshiBalance = tester.Client.GetOrderedBalance(satoshi).ToArray();
                Assert.True(satoshiBalance[0].Amount == -Money.Parse("0.3"));
                Assert.True(satoshiBalance[1].Amount == -Money.Parse("0.2"));

                tx = new TransactionBuilder()
                       .AddCoins(satoshiBalance[0].ReceivedCoins)
                       .AddKeys(satoshi)
                       .Send(alice, "0.1")
                       .SetChange(satoshi)
                       .BuildTransaction(true);

                Thread.Sleep(1000);
                chainBuilder.Emit(tx);
                chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                satoshiBalance = tester.Client.GetOrderedBalance(satoshi).ToArray();
                Assert.True(satoshiBalance[0].Amount == -Money.Parse("0.1"));

                tester.Client.CleanUnconfirmedChanges(satoshi, TimeSpan.Zero);

                satoshiBalance = tester.Client.GetOrderedBalance(satoshi).ToArray();
                Assert.True(satoshiBalance.Length == 2);
            }
        }
예제 #19
0
        public void CanGetOrderedBalancesP2WSH()
        {
            using(var tester = CreateTester())
            {
                var bob = new Key();
                var alice = new Key();
                var satoshi = new Key();

                var chainBuilder = tester.CreateChainBuilder();
                chainBuilder.EmitMoney(bob.PubKey.ScriptPubKey.WitHash, "20.0");
                chainBuilder.EmitMoney(alice.PubKey.ScriptPubKey.WitHash, "50.0");
                chainBuilder.SubmitBlock();

                chainBuilder.SyncIndexer();

                var aliceBalance = tester.Client.GetOrderedBalance(alice.PubKey.ScriptPubKey.WitHash).ToArray();
                Assert.True(aliceBalance.Length == 1);

                var tx = new TransactionBuilder()
                    .AddCoins(new WitScriptCoin((Coin)aliceBalance[0].ReceivedCoins[0], alice.PubKey.ScriptPubKey))
                    .AddKeys(alice)
                    .Send(bob.PubKey.ScriptPubKey.WitHash, "5.0")
                    .SetChange(alice.PubKey.ScriptPubKey.WitHash)
                    .BuildTransaction(true);
                chainBuilder.Emit(tx);

                var block = chainBuilder.SubmitBlock();
                chainBuilder.SyncIndexer();

                aliceBalance = tester.Client.GetOrderedBalance(alice.PubKey.ScriptPubKey.WitHash).ToArray();
                Assert.True(aliceBalance.Length == 2);
                Assert.True(aliceBalance[0].Amount == -Money.Coins(5.0m));

                var bobBalance = tester.Client.GetOrderedBalance(bob.PubKey.ScriptPubKey.WitHash).ToArray();
                Assert.True(bobBalance.Length == 2);
                Assert.True(bobBalance[0].Amount == Money.Coins(5.0m));
            }
        }
예제 #20
0
        public void TestPuzzleSolver()
        {
            RsaKey         key = TestKeys.Default;
            PuzzleSolution expectedSolution = null;
            Puzzle         puzzle           = key.PubKey.GeneratePuzzle(ref expectedSolution);

            var parameters = new SolverParameters
            {
                FakePuzzleCount = 50,
                RealPuzzleCount = 10,
                ServerKey       = key.PubKey
            };
            SolverClientSession client = new SolverClientSession(parameters);
            SolverServerSession server = new SolverServerSession(key, parameters);

            var clientEscrow = new Key();
            var serverEscrow = new Key();

            var escrow            = CreateEscrowCoin(clientEscrow.PubKey, serverEscrow.PubKey);
            var redeemDestination = new Key().ScriptPubKey;

            client.ConfigureEscrowedCoin(escrow, clientEscrow, redeemDestination);
            client.AcceptPuzzle(puzzle.PuzzleValue);
            RoundTrip(ref client, parameters);
            Assert.True(client.GetInternalState().RedeemDestination == redeemDestination);
            PuzzleValue[] puzzles = client.GeneratePuzzles();
            RoundTrip(ref client, parameters);
            RoundTrip(ref puzzles);

            server.ConfigureEscrowedCoin(escrow, serverEscrow);
            var commitments = server.SolvePuzzles(puzzles);

            RoundTrip(ref server, parameters, key);
            RoundTrip(ref commitments);

            var revelation = client.Reveal(commitments);

            RoundTrip(ref client, parameters);
            RoundTrip(ref revelation);

            SolutionKey[] fakePuzzleKeys = server.CheckRevelation(revelation);
            RoundTrip(ref server, parameters, key);
            RoundTrip(ref fakePuzzleKeys);


            BlindFactor[] blindFactors = client.GetBlindFactors(fakePuzzleKeys);
            RoundTrip(ref client, parameters);
            RoundTrip(ref blindFactors);

            var offerInformation = server.CheckBlindedFactors(blindFactors, FeeRate);

            RoundTrip(ref server, parameters, key);

            var clientOfferSig = client.SignOffer(offerInformation);


            //Verify if the scripts are correctly created
            var fulfill     = server.FulfillOffer(clientOfferSig, new Key().ScriptPubKey, FeeRate);
            var offerRedeem = client.CreateOfferRedeemTransaction(FeeRate);

            var offerTransaction = server.GetSignedOfferTransaction();
            var offerCoin        = offerTransaction.Transaction.Outputs.AsCoins().First();
            var resigned         = offerTransaction.ReSign(client.EscrowedCoin);

            TransactionBuilder txBuilder = new TransactionBuilder();

            txBuilder.AddCoins(client.EscrowedCoin);
            Assert.True(txBuilder.Verify(resigned));

            resigned  = fulfill.ReSign(offerCoin);
            txBuilder = new TransactionBuilder();
            txBuilder.AddCoins(offerCoin);
            Assert.True(txBuilder.Verify(resigned));

            var offerRedeemTx = offerRedeem.ReSign(offerCoin);

            txBuilder = new TransactionBuilder();
            txBuilder.AddCoins(offerCoin);
            Assert.True(txBuilder.Verify(offerRedeemTx));


            client.CheckSolutions(fulfill.Transaction);
            RoundTrip(ref client, parameters);

            var clientEscapeSignature = client.SignEscape();
            var escapeTransaction     = server.GetSignedEscapeTransaction(clientEscapeSignature, FeeRate, new Key().ScriptPubKey);

            txBuilder = new TransactionBuilder();
            txBuilder.AddCoins(client.EscrowedCoin);
            Assert.True(txBuilder.Verify(escapeTransaction));

            var solution = client.GetSolution();

            RoundTrip(ref client, parameters);
            Assert.True(solution == expectedSolution);
        }
예제 #21
0
		public void CanBuildWitTransaction()
		{
			Key alice = new Key();
			Key bob = new Key();
			Transaction previousTx = null;
			Coin previousCoin = null;
			WitScriptCoin witnessCoin = null;
			TransactionBuilder builder = null;
			Transaction signedTx = null;
			ScriptCoin scriptCoin = null;

			//P2WPKH
			previousTx = new Transaction();
			previousTx.Outputs.Add(new TxOut(Money.Coins(1.0m), alice.PubKey.WitHash));
			previousCoin = previousTx.Outputs.AsCoins().First();

			builder = new TransactionBuilder();
			builder.AddKeys(alice);
			builder.AddCoins(previousCoin);
			builder.Send(bob, Money.Coins(0.4m));
			builder.SendFees(Money.Satoshis(30000));
			builder.SetChange(alice);
			signedTx = builder.BuildTransaction(true);
			Assert.True(builder.Verify(signedTx));

			//P2WSH
			previousTx = new Transaction();
			previousTx.Outputs.Add(new TxOut(Money.Coins(1.0m), alice.PubKey.ScriptPubKey.WitHash));
			previousCoin = previousTx.Outputs.AsCoins().First();

			witnessCoin = new WitScriptCoin(previousCoin, alice.PubKey.ScriptPubKey);
			builder = new TransactionBuilder();
			builder.AddKeys(alice);
			builder.AddCoins(witnessCoin);
			builder.Send(bob, Money.Coins(0.4m));
			builder.SendFees(Money.Satoshis(30000));
			builder.SetChange(alice);
			signedTx = builder.BuildTransaction(true);
			Assert.True(builder.Verify(signedTx));


			//P2SH(P2WPKH)
			previousTx = new Transaction();
			previousTx.Outputs.Add(new TxOut(Money.Coins(1.0m), alice.PubKey.WitHash.ScriptPubKey.Hash));
			previousCoin = previousTx.Outputs.AsCoins().First();

			scriptCoin = new ScriptCoin(previousCoin, alice.PubKey.WitHash.ScriptPubKey);
			builder = new TransactionBuilder();
			builder.AddKeys(alice);
			builder.AddCoins(scriptCoin);
			builder.Send(bob, Money.Coins(0.4m));
			builder.SendFees(Money.Satoshis(30000));
			builder.SetChange(alice);
			signedTx = builder.BuildTransaction(true);
			Assert.True(builder.Verify(signedTx));

			//P2SH(P2WSH)
			previousTx = new Transaction();
			previousTx.Outputs.Add(new TxOut(Money.Coins(1.0m), alice.PubKey.ScriptPubKey.WitHash.ScriptPubKey.Hash));
			previousCoin = previousTx.Outputs.AsCoins().First();

			witnessCoin = new WitScriptCoin(previousCoin, alice.PubKey.ScriptPubKey);
			builder = new TransactionBuilder();
			builder.AddKeys(alice);
			builder.AddCoins(witnessCoin);
			builder.Send(bob, Money.Coins(0.4m));
			builder.SendFees(Money.Satoshis(30000));
			builder.SetChange(alice);
			signedTx = builder.BuildTransaction(true);
			Assert.True(builder.Verify(signedTx));

			//Can remove witness data from tx
			var signedTx2 = signedTx.WithOptions(TransactionOptions.None);
			Assert.Equal(signedTx.GetHash(), signedTx2.GetHash());
			Assert.True(signedTx2.GetSerializedSize() < signedTx.GetSerializedSize());
		}
예제 #22
0
파일: Program.cs 프로젝트: Wind2esg/GWallet
        public void Send(string address, string btc = "all", string path = "")
        {
            var walletFilePath = GetWalletFilePath(path);
            BitcoinAddress addressToSend;
            try
            {
                addressToSend = BitcoinAddress.Create(address, Config.network);
            }
            catch (Exception ex)
            {
                Exit(ex.ToString());
                throw ex;
            }
            Safe safe = DecryptWalletByAskingForPassword(walletFilePath);

            if (Config.connectionType == ConnectionType.Http)
            {
                Dictionary<BitcoinAddress, List<BalanceOperation>> operationsPerAddresses = QBitNinjaJutsus.QueryOperationsPerSafeAddresses(safe, 7);

                // 获取非空私钥
                WriteLine("Finding not empty private keys...");
                var operationsPerNotEmptyPrivateKeys = new Dictionary<BitcoinExtKey, List<BalanceOperation>>();
                foreach (var elem in operationsPerAddresses)
                {
                    var balance = Money.Zero;
                    foreach (var op in elem.Value) balance += op.Amount;
                    if (balance > Money.Zero)
                    {
                        var secret = safe.FindPrivateKey(elem.Key);
                        operationsPerNotEmptyPrivateKeys.Add(secret, elem.Value);
                    }
                }

                // 获取 pubkey 脚本
                WriteLine("Select change address...");
                Script changeScriptPubKey = null;
                Dictionary<BitcoinAddress, List<BalanceOperation>> operationsPerChangeAddresses = QBitNinjaJutsus.QueryOperationsPerSafeAddresses(safe, minUnusedKeys: 1, hdPathType: HdPathType.Change);
                foreach (var elem in operationsPerChangeAddresses)
                {
                    if (elem.Value.Count == 0)
                        changeScriptPubKey = safe.FindPrivateKey(elem.Key).ScriptPubKey;
                }
                if (changeScriptPubKey == null)
                    throw new ArgumentNullException();

                // 获取 UXTO
                WriteLine("Gathering unspent coins...");
                Dictionary<Coin, bool> unspentCoins = QBitNinjaJutsus.GetUnspentCoins(operationsPerNotEmptyPrivateKeys.Keys);

                // 获取费用
                WriteLine("Calculating transaction fee...");
                Money fee;
                try
                {
                    var txSizeInBytes = 250;
                    using (var client = new HttpClient())
                    {

                        const string request = @"https://bitcoinfees.21.co/api/v1/fees/recommended";
                        var result = client.GetAsync(request, HttpCompletionOption.ResponseContentRead).Result;
                        var json = JObject.Parse(result.Content.ReadAsStringAsync().Result);
                        var fastestSatoshiPerByteFee = json.Value<decimal>("fastestFee");
                        fee = new Money(fastestSatoshiPerByteFee * txSizeInBytes, MoneyUnit.Satoshi);
                    }
                }
                catch
                {
                    Exit("Couldn't calculate transaction fee, try it again later.");
                    throw new Exception("Can't get tx fee");
                }
                WriteLine($"Fee: {fee.ToDecimal(MoneyUnit.BTC).ToString("0.#############################")}btc");

                // 有多少 btc 可花费 (有尚未确认的 btc)
                Money availableAmount = Money.Zero;
                Money unconfirmedAvailableAmount = Money.Zero;
                foreach (var elem in unspentCoins)
                {
                    // If can spend unconfirmed add all
                    if (Config.canSpendUnconfirmed)
                    {
                        availableAmount += elem.Key.Amount;
                        if (!elem.Value)
                            unconfirmedAvailableAmount += elem.Key.Amount;
                    }
                    // else only add confirmed ones
                    else
                    {
                        if (elem.Value)
                        {
                            availableAmount += elem.Key.Amount;
                        }
                    }
                }

                // 花费多少
                Money amountToSend = null;
                string amountString = btc;
                if (string.Equals(amountString, "all", StringComparison.OrdinalIgnoreCase))
                {
                    amountToSend = availableAmount;
                    amountToSend -= fee;
                }
                else
                {
                    amountToSend = ParseBtcString(amountString);
                }

                // 检查
                if (amountToSend < Money.Zero || availableAmount < amountToSend + fee)
                    Exit("Not enough coins.");

                decimal feePc = Math.Round((100 * fee.ToDecimal(MoneyUnit.BTC)) / amountToSend.ToDecimal(MoneyUnit.BTC));
                if (feePc > 1)
                {
                    WriteLine();
                    WriteLine($"The transaction fee is {feePc.ToString("0.#")}% of your transaction amount.");
                    WriteLine($"Sending:\t {amountToSend.ToDecimal(MoneyUnit.BTC).ToString("0.#############################")}btc");
                    WriteLine($"Fee:\t\t {fee.ToDecimal(MoneyUnit.BTC).ToString("0.#############################")}btc");
                    ConsoleKey response = GetYesNoAnswerFromUser();
                    if (response == ConsoleKey.N)
                    {
                        Exit("User interruption.");
                    }
                }

                var confirmedAvailableAmount = availableAmount - unconfirmedAvailableAmount;
                var totalOutAmount = amountToSend + fee;
                if (confirmedAvailableAmount < totalOutAmount)
                {
                    var unconfirmedToSend = totalOutAmount - confirmedAvailableAmount;
                    WriteLine();
                    WriteLine($"In order to complete this transaction you have to spend {unconfirmedToSend.ToDecimal(MoneyUnit.BTC).ToString("0.#############################")} unconfirmed btc.");
                    ConsoleKey response = GetYesNoAnswerFromUser();
                    if (response == ConsoleKey.N)
                    {
                        Exit("User interruption.");
                    }
                }

                // 选哪些 UTXO
                WriteLine("Selecting coins...");
                var coinsToSpend = new HashSet<Coin>();
                var unspentConfirmedCoins = new List<Coin>();
                var unspentUnconfirmedCoins = new List<Coin>();
                foreach (var elem in unspentCoins)
                    if (elem.Value) unspentConfirmedCoins.Add(elem.Key);
                    else unspentUnconfirmedCoins.Add(elem.Key);

                bool haveEnough = QBitNinjaJutsus.SelectCoins(ref coinsToSpend, totalOutAmount, unspentConfirmedCoins);
                if (!haveEnough)
                    haveEnough = QBitNinjaJutsus.SelectCoins(ref coinsToSpend, totalOutAmount, unspentUnconfirmedCoins);
                if (!haveEnough)
                    throw new Exception("Not enough funds.");

                // 获取签名密钥
                var signingKeys = new HashSet<ISecret>();
                foreach (var coin in coinsToSpend)
                {
                    foreach (var elem in operationsPerNotEmptyPrivateKeys)
                    {
                        if (elem.Key.ScriptPubKey == coin.ScriptPubKey)
                            signingKeys.Add(elem.Key);
                    }
                }

                // 创建交易
                WriteLine("Signing transaction...");
                var builder = new TransactionBuilder();
                var tx = builder
                    .AddCoins(coinsToSpend)
                    .AddKeys(signingKeys.ToArray())
                    .Send(addressToSend, amountToSend)
                    .SetChange(changeScriptPubKey)
                    .SendFees(fee)
                    .BuildTransaction(true);

                if (!builder.Verify(tx))
                    Exit("Couldn't build the transaction.");

                WriteLine($"Transaction Id: {tx.GetHash()}");

                var qBitClient = new QBitNinjaClient(Config.network);

                // Qbit 相应有 bug, 手动确认		
                BroadcastResponse broadcastResponse;
                var success = false;
                var tried = 0;
                var maxTry = 7;
                do
                {
                    tried++;
                    WriteLine($"Try broadcasting transaction... ({tried})");
                    broadcastResponse = qBitClient.Broadcast(tx).Result;
                    var getTxResp = qBitClient.GetTransaction(tx.GetHash()).Result;
                    if (getTxResp == null)
                    {
                        Thread.Sleep(3000);
                        continue;
                    }
                    else
                    {
                        success = true;
                        break;
                    }
                } while (tried <= maxTry);
                if (!success)
                {
                    if (broadcastResponse.Error != null)
                    {
                        WriteLine($"Error code: {broadcastResponse.Error.ErrorCode} Reason: {broadcastResponse.Error.Reason}");
                    }
                    Exit($"The transaction might not have been successfully broadcasted. Please check the Transaction ID in a block explorer.", ConsoleColor.Blue);
                }
                Exit("Transaction is successfully propagated on the network.", ConsoleColor.Green);
            }
            else if (Config.connectionType == ConnectionType.FullNode)
            {
                throw new NotImplementedException();
            }
            else
            {
                Exit("Invalid connection type.");
            }
        }
예제 #23
0
		public void CanMutateSignature()
		{
			Transaction funding = new Transaction("010000000189632848f99722915727c5c75da8db2dbf194342a0429828f66ff88fab2af7d6000000008b483045022100abbc8a73fe2054480bda3f3281da2d0c51e2841391abd4c09f4f908a2034c18d02205bc9e4d68eafb918f3e9662338647a4419c0de1a650ab8983f1d216e2a31d8e30141046f55d7adeff6011c7eac294fe540c57830be80e9355c83869c9260a4b8bf4767a66bacbd70b804dc63d5beeb14180292ad7f3b083372b1d02d7a37dd97ff5c9effffffff0140420f000000000017a914f815b036d9bbbce5e9f2a00abd1bf3dc91e955108700000000");

			Transaction spending = new Transaction("0100000001aca7f3b45654c230e0886a57fb988c3044ef5e8f7f39726d305c61d5e818903c00000000fd5d010048304502200187af928e9d155c4b1ac9c1c9118153239aba76774f775d7c1f9c3e106ff33c0221008822b0f658edec22274d0b6ae9de10ebf2da06b1bbdaaba4e50eb078f39e3d78014730440220795f0f4f5941a77ae032ecb9e33753788d7eb5cb0c78d805575d6b00a1d9bfed02203e1f4ad9332d1416ae01e27038e945bc9db59c732728a383a6f1ed2fb99da7a4014cc952410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d4621353aeffffffff0140420f00000000001976a914ae56b4db13554d321c402db3961187aed1bbed5b88ac00000000");


			TransactionBuilder builder = new TransactionBuilder();
			builder.StandardTransactionPolicy = EasyPolicy;
			builder.AddCoins(funding.Outputs.AsCoins());
			Assert.True(builder.Verify(spending));

			foreach(var input in spending.Inputs.AsIndexedInputs())
			{
				var ops = input.TxIn.ScriptSig.ToOps().ToArray();
				foreach(var sig in ops.Select(o =>
					{
						try
						{
							return new TransactionSignature(o.PushData);
						}
						catch
						{
							return null;
						}
					})
					.Select((sig, i) => new
					{
						sig,
						i
					})
					.Where(i => i.sig != null))
				{
					ops[sig.i] = Op.GetPushOp(sig.sig.MakeCanonical().ToBytes());
				}
				input.TxIn.ScriptSig = new Script(ops);
			}
			Assert.True(builder.Verify(spending));
		}
예제 #24
0
        public void SegWit_CS_Setup_Tx_and_Withdrawal()
        {
            var network = ObsidianXNetworksSelector.Obsidian.Mainnet();

            // I have received 100_000 in my wallet in this address
            Key         myBudgetKey    = new Key();
            PubKey      myBudgetPubKey = myBudgetKey.PubKey.Compress();
            Transaction received       = network.CreateTransaction();

            received.Outputs.Add(new TxOut(Money.Coins(100_000), myBudgetPubKey.WitHash.ScriptPubKey));
            List <Coin> myBudgetCoins = received.Outputs.AsCoins().ToList();

            // for 90_000, I want to set up ColdStaking
            Key    coldKey    = new Key();
            PubKey coldPubKey = coldKey.PubKey.Compress();
            Key    hotKey     = new Key();
            PubKey hotPubKey  = hotKey.PubKey.Compress();

            Script csRedeemScript = ColdStakingScriptTemplate.Instance.GenerateScriptPubKey(hotPubKey.WitHash.AsKeyId(), coldPubKey.WitHash.AsKeyId());

            this.output.WriteLine($"csRedeemScript: {csRedeemScript}");

            string csScriptAddress = csRedeemScript.WitHash.GetAddress(network).ToString();

            this.output.WriteLine($"{nameof(csScriptAddress)}: {csScriptAddress}");

            Script csScriptAddressScriptPubKey = csRedeemScript.WitHash.ScriptPubKey;

            this.output.WriteLine($"{nameof(csScriptAddressScriptPubKey)}: {csScriptAddressScriptPubKey}");


            var builder = new TransactionBuilder(network);

            Transaction csSetupTx =
                builder
                .AddCoins(myBudgetCoins)
                .Send(csScriptAddressScriptPubKey, Money.Coins(90_000)) // 90_000 to cold staking script address
                .SetChange(myBudgetPubKey.WitHash.ScriptPubKey)         // 10_000 back to original source
                .SendFees(this.fee)
                .AddKeys(myBudgetKey)
                .BuildTransaction(sign: true);

            bool isVerifyPassing = builder.Verify(csSetupTx, out var errors);

            foreach (var err in errors)
            {
                this.output.WriteLine(err.ToString());
            }

            bool hasEmptyScriptSig = csSetupTx.Inputs.All(i => i.ScriptSig.Length == 0);

            this.output.WriteLine($"isVerifyPassing: {isVerifyPassing}");
            this.output.WriteLine($"hasWitness: {csSetupTx.HasWitness}");
            this.output.WriteLine($"hasEmptyScriptSig: {hasEmptyScriptSig}");
            this.output.WriteLine(csSetupTx.ToString());

            Assert.True(isVerifyPassing);
            Assert.True(csSetupTx.HasWitness);
            Assert.True(hasEmptyScriptSig);

            this.output.WriteLine("*** Now, immediate Withdrawal after the CS Setup tx ***");

            var csScriptCoins     = csSetupTx.Outputs.AsCoins().Select(cs => cs.ToScriptCoin(csRedeemScript)).ToList();
            var builderWithdrawal = new TransactionBuilder(network);

            builderWithdrawal.Extensions.Add(new ColdStakingBuilderExtension(false));


            Transaction withDrawTx =
                builderWithdrawal
                .AddCoins(csScriptCoins)
                .Send(myBudgetPubKey.WitHash.ScriptPubKey, Money.Coins(80_000)) // withdraw 80_000 of the 90_000 I previously sent to the cold staking script address
                .SetChange(csScriptAddressScriptPubKey)                         // Change goes back the CS script address
                .SendFees(this.fee)
                .AddKeys(coldKey)                                               // use the cold private key for the withdrawal
                .BuildTransaction(sign: true);


            bool isWithdrawVerifyPassing = builderWithdrawal.Verify(withDrawTx, out var errors2);

            foreach (var err in errors2)
            {
                this.output.WriteLine(err.ToString());
            }

            bool hasEmptyScriptSig2 = withDrawTx.Inputs.All(i => i.ScriptSig.Length == 0);

            this.output.WriteLine($"isVerifyPassing: {isWithdrawVerifyPassing}");
            this.output.WriteLine($"hasWitness: {withDrawTx.HasWitness}");
            this.output.WriteLine($"hasEmptyScriptSig: {hasEmptyScriptSig2}");
            this.output.WriteLine(withDrawTx.ToString());

            Assert.True(isWithdrawVerifyPassing);
            Assert.True(withDrawTx.HasWitness);
            Assert.True(hasEmptyScriptSig2);
        }
예제 #25
0
		//https://github.com/NicolasDorier/NBitcoin/issues/34
		public void CanBuildAnyoneCanPayTransaction()
		{
			//Carla is buying from Alice. Bob is acting as a mediator between Alice and Carla.
			var aliceKey = new Key();
			var bobKey = new Key();
			var carlaKey = new Key();

			// Alice + Bob 2 of 2 multisig "wallet"
			var aliceBobRedeemScript = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, new PubKey[] { aliceKey.PubKey, bobKey.PubKey });

			var txBuilder = new TransactionBuilder();
			var funding = txBuilder
				.AddCoins(GetCoinSource(aliceKey))
				.AddKeys(aliceKey)
				.Send(aliceBobRedeemScript.Hash, "0.5")
				.SetChange(aliceKey.PubKey.Hash)
				.SendFees(Money.Satoshis(5000))
				.BuildTransaction(true);

			Assert.True(txBuilder.Verify(funding));

			List<ICoin> aliceBobCoins = new List<ICoin>();
			aliceBobCoins.Add(new ScriptCoin(funding, funding.Outputs.To(aliceBobRedeemScript.Hash).First(), aliceBobRedeemScript));

			// first Bob constructs the TX
			txBuilder = new TransactionBuilder();
			var unsigned = txBuilder
				// spend from the Alice+Bob wallet to Carla
				.AddCoins(aliceBobCoins)
				.Send(carlaKey.PubKey.Hash, "0.01")
				//and Carla pays Alice
				.Send(aliceKey.PubKey.Hash, "0.02")
				.CoverOnly("0.01")
				.SetChange(aliceBobRedeemScript.Hash)
				// Bob does not sign anything yet
				.BuildTransaction(false);

			Assert.True(unsigned.Outputs.Count == 3);
			Assert.True(unsigned.Outputs[0].IsTo(aliceBobRedeemScript.Hash));
			//Only 0.01 should be covered, not 0.03 so 0.49 goes back to Alice+Bob
			Assert.True(unsigned.Outputs[0].Value == Money.Parse("0.49"));


			Assert.True(unsigned.Outputs[1].IsTo(carlaKey.PubKey.Hash));
			Assert.True(unsigned.Outputs[1].Value == Money.Parse("0.01"));

			Assert.True(unsigned.Outputs[2].IsTo(aliceKey.PubKey.Hash));
			Assert.True(unsigned.Outputs[2].Value == Money.Parse("0.02"));

			//Alice signs	
			txBuilder = new TransactionBuilder();
			var aliceSigned = txBuilder
					.AddCoins(aliceBobCoins)
					.AddKeys(aliceKey)
					.SignTransaction(unsigned, SigHash.All | SigHash.AnyoneCanPay);

			var carlaCoins = GetCoinSource(carlaKey, "1.0", "0.8", "0.6", "0.2", "0.05");

			//Scenario 1 : Carla knows aliceBobCoins so she can calculate how much coin she need to complete the transaction
			//Carla fills and signs
			txBuilder = new TransactionBuilder();
			var carlaSigned = txBuilder
				.AddCoins(aliceBobCoins)
				.Then()
				.AddKeys(carlaKey)
				//Carla should complete 0.02, but with 0.03 of fees, she should have a coins of 0.05
				.AddCoins(carlaCoins)
				.ContinueToBuild(aliceSigned)
				.SendFees("0.03")
				.CoverTheRest()
				.BuildTransaction(true);


			//Bob review and signs
			txBuilder = new TransactionBuilder();
			var bobSigned = txBuilder
				.AddCoins(aliceBobCoins)
				.AddKeys(bobKey)
				.SignTransaction(carlaSigned);

			txBuilder.AddCoins(carlaCoins);
			Assert.True(txBuilder.Verify(bobSigned));


			//Scenario 2 : Carla is told by Bob to complete 0.05 BTC
			//Carla fills and signs
			txBuilder = new TransactionBuilder();
			carlaSigned = txBuilder
				.AddKeys(carlaKey)
				.AddCoins(carlaCoins)
				//Carla should complete 0.02, but with 0.03 of fees, she should have a coins of 0.05
				.ContinueToBuild(aliceSigned)
				.CoverOnly("0.05")
				.BuildTransaction(true);


			//Bob review and signs
			txBuilder = new TransactionBuilder();
			bobSigned = txBuilder
				.AddCoins(aliceBobCoins)
				.AddKeys(bobKey)
				.SignTransaction(carlaSigned);

			txBuilder.AddCoins(carlaCoins);
			Assert.True(txBuilder.Verify(bobSigned));
		}
예제 #26
0
        public async Task AcceptToMemoryPool_WithMultiInOutValidTxns_IsSuccessfullAsync()
        {
            string dataDir = GetTestDirectoryPath(this);

            var miner = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            ITestChainContext context = await TestChainFactory.CreateAsync(KnownNetworks.RegTest, miner.PubKey.Hash.ScriptPubKey, dataDir);

            IMempoolValidator validator = context.MempoolValidator;

            Assert.NotNull(validator);

            var alice   = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            var bob     = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            var satoshi = new BitcoinSecret(new Key(), KnownNetworks.RegTest);

            // Fund Alice, Bob, Satoshi
            // 50 Coins come from first tx on chain - send satoshi 1, bob 2, Alice 1.5 and change back to miner
            var         coin          = new Coin(context.SrcTxs[0].GetHash(), 0, context.SrcTxs[0].TotalOut, miner.ScriptPubKey);
            var         txBuilder     = new TransactionBuilder(KnownNetworks.RegTest);
            Transaction multiOutputTx = txBuilder
                                        .AddCoins(new List <Coin> {
                coin
            })
                                        .AddKeys(miner)
                                        .Send(satoshi.GetAddress(), "1.00")
                                        .Send(bob.GetAddress(), "2.00")
                                        .Send(alice.GetAddress(), "1.50")
                                        .SendFees("0.001")
                                        .SetChange(miner.GetAddress())
                                        .BuildTransaction(true);

            Assert.True(txBuilder.Verify(multiOutputTx)); //check fully signed
            var state = new MempoolValidationState(false);

            Assert.True(await validator.AcceptToMemoryPool(state, multiOutputTx), $"Transaction: {nameof(multiOutputTx)} failed mempool validation.");

            // Alice then Bob sends to Satoshi
            Coin[] aliceCoins = multiOutputTx.Outputs
                                .Where(o => o.ScriptPubKey == alice.ScriptPubKey)
                                .Select(o => new Coin(new OutPoint(multiOutputTx.GetHash(), multiOutputTx.Outputs.IndexOf(o)), o))
                                .ToArray();
            Coin[] bobCoins = multiOutputTx.Outputs
                              .Where(o => o.ScriptPubKey == bob.ScriptPubKey)
                              .Select(o => new Coin(new OutPoint(multiOutputTx.GetHash(), multiOutputTx.Outputs.IndexOf(o)), o))
                              .ToArray();

            txBuilder = new TransactionBuilder(KnownNetworks.RegTest);
            Transaction multiInputTx = txBuilder
                                       .AddCoins(aliceCoins)
                                       .AddKeys(alice)
                                       .Send(satoshi.GetAddress(), "0.8")
                                       .SetChange(alice.GetAddress())
                                       .SendFees("0.0005")
                                       .Then()
                                       .AddCoins(bobCoins)
                                       .AddKeys(bob)
                                       .Send(satoshi.GetAddress(), "0.2")
                                       .SetChange(bob.GetAddress())
                                       .SendFees("0.0005")
                                       .BuildTransaction(true);

            Assert.True(txBuilder.Verify(multiInputTx)); //check fully signed
            Assert.True(await validator.AcceptToMemoryPool(state, multiInputTx), $"Transaction: {nameof(multiInputTx)} failed mempool validation.");
        }
예제 #27
0
		public void CanSplitFees()
		{
			var satoshi = new Key();
			var alice = new Key();
			var bob = new Key();

			var aliceCoins = new ICoin[] { RandomCoin("0.4", alice), RandomCoin("0.6", alice) };
			var bobCoins = new ICoin[] { RandomCoin("0.2", bob), RandomCoin("0.3", bob) };

			TransactionBuilder builder = new TransactionBuilder();
			FeeRate rate = new FeeRate(Money.Coins(0.0004m));
			var tx = builder
				.AddCoins(aliceCoins)
				.AddKeys(alice)
				.Send(satoshi, Money.Coins(0.1m))
				.SetChange(alice)
				.Then()
				.AddCoins(bobCoins)
				.AddKeys(bob)
				.Send(satoshi, Money.Coins(0.01m))
				.SetChange(bob)
				.SendEstimatedFeesSplit(rate)
				.BuildTransaction(true);

			var estimated = builder.EstimateFees(tx, rate);

			Assert.True(builder.Verify(tx, estimated));

			// Alice should pay two times more fee than bob
			builder = new TransactionBuilder();
			tx = builder
				.AddCoins(aliceCoins)
				.AddKeys(alice)
				.SetFeeWeight(2.0m)
				.Send(satoshi, Money.Coins(0.1m))
				.SetChange(alice)
				.Then()
				.AddCoins(bobCoins)
				.AddKeys(bob)
				.Send(satoshi, Money.Coins(0.01m))
				.SetChange(bob)
				.SendFeesSplit(Money.Coins(0.6m))
				.BuildTransaction(true);

			var spentAlice = builder.FindSpentCoins(tx).Where(c => aliceCoins.Contains(c)).OfType<Coin>().Select(c => c.Amount).Sum();
			var receivedAlice = tx.Outputs.AsCoins().Where(c => c.ScriptPubKey == alice.PubKey.Hash.ScriptPubKey).Select(c => c.Amount).Sum();
			Assert.Equal(Money.Coins(0.1m + 0.4m), spentAlice - receivedAlice);

			var spentBob = builder.FindSpentCoins(tx).Where(c => bobCoins.Contains(c)).OfType<Coin>().Select(c => c.Amount).Sum();
			var receivedBob = tx.Outputs.AsCoins().Where(c => c.ScriptPubKey == bob.PubKey.Hash.ScriptPubKey).Select(c => c.Amount).Sum();
			Assert.Equal(Money.Coins(0.01m + 0.2m), spentBob - receivedBob);
		}
예제 #28
0
        public async Task AcceptToMemoryPool_WithP2SHValidTxns_IsSuccessfullAsync()
        {
            string dataDir = GetTestDirectoryPath(this);

            var miner = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            ITestChainContext context = await TestChainFactory.CreateAsync(KnownNetworks.RegTest, miner.PubKey.Hash.ScriptPubKey, dataDir);

            IMempoolValidator validator = context.MempoolValidator;

            Assert.NotNull(validator);

            var alice   = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            var bob     = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            var satoshi = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            var nico    = new BitcoinSecret(new Key(), KnownNetworks.RegTest);

            // corp needs two out of three of alice, bob, nico
            Script corpMultiSig = PayToMultiSigTemplate
                                  .Instance
                                  .GenerateScriptPubKey(2, new[] { alice.PubKey, bob.PubKey, nico.PubKey });

            // P2SH address for corp multi-sig
            BitcoinScriptAddress corpRedeemAddress = corpMultiSig.GetScriptAddress(KnownNetworks.RegTest);

            // Fund corp
            // 50 Coins come from first tx on chain - send corp 42 and change back to miner
            var         coin       = new Coin(context.SrcTxs[0].GetHash(), 0, context.SrcTxs[0].TotalOut, miner.ScriptPubKey);
            var         txBuilder  = new TransactionBuilder(KnownNetworks.RegTest);
            Transaction fundP2shTx = txBuilder
                                     .AddCoins(new List <Coin> {
                coin
            })
                                     .AddKeys(miner)
                                     .Send(corpRedeemAddress, "42.00")
                                     .SendFees("0.001")
                                     .SetChange(miner.GetAddress())
                                     .BuildTransaction(true);

            Assert.True(txBuilder.Verify(fundP2shTx)); //check fully signed
            var state = new MempoolValidationState(false);

            Assert.True(await validator.AcceptToMemoryPool(state, fundP2shTx), $"Transaction: {nameof(fundP2shTx)} failed mempool validation.");

            // AliceBobNico corp. send 20 to Satoshi
            Coin[] corpCoins = fundP2shTx.Outputs
                               .Where(o => o.ScriptPubKey == corpRedeemAddress.ScriptPubKey)
                               .Select(o => ScriptCoin.Create(KnownNetworks.RegTest, new OutPoint(fundP2shTx.GetHash(), fundP2shTx.Outputs.IndexOf(o)), o, corpMultiSig))
                               .ToArray();

            txBuilder = new TransactionBuilder(KnownNetworks.RegTest);
            Transaction p2shSpendTx = txBuilder
                                      .AddCoins(corpCoins)
                                      .AddKeys(alice, bob)
                                      .Send(satoshi.GetAddress(), "20")
                                      .SendFees("0.001")
                                      .SetChange(corpRedeemAddress)
                                      .BuildTransaction(true);

            Assert.True(txBuilder.Verify(p2shSpendTx));

            Assert.True(await validator.AcceptToMemoryPool(state, p2shSpendTx), $"Transaction: {nameof(p2shSpendTx)} failed mempool validation.");
        }
예제 #29
0
        //take in tx parameters, return tx object
        public static TxSerial CreateTx(string extPubKey, string pubToAddr, string chgAddr, int walletId, long satToSend, long fee, bool testnet)
        {
            CheckNullOrEmpty(new object[] { extPubKey, pubToAddr, ElectrumXhost },
                             new string[] { "extPubKey", "pubToAddr", "ElectrumXhost" });

            string err = "";

            if (satToSend == 0)
            {
                err += "satoshiToSend = 0, ";
            }
            if (fee == 0)
            {
                err += "satoshiFee = 0, ";
            }
            if (err != "")
            {
                throw new Exception("[CreateTx] " + err);
            }

            //get first 100+100 child address from ext pub key
            List <Tuple <string, string> > recAddrList = GetDerivedKeys(extPubKey, 0, 20, false, testnet);         //receive addresses
            List <Tuple <string, string> > chgAddrList = GetDerivedKeys(extPubKey, 0, 20, true, testnet);          //change addresses

            //TODO - create process for getting next change address, so address never used twice
            if (chgAddr == null || chgAddr == "")             //get first chg addr for extPubKey
            {
                chgAddr = chgAddrList.First().Item1;
            }

            //server status check
            string info = ElectrumX.GetServerInfo(ElectrumXhost, ElectrumXport);

            if (info == null)
            {
                throw new Exception("[CreateTx] ElectrumX Server Check Failed");
            }

            string[] recAddrListAddr = new string[recAddrList.Count];            //short address
            string[] recAddrListExt  = new string[recAddrList.Count];            //long address
            int      ctr             = 0;

            foreach (var t in recAddrList)
            {
                recAddrListAddr[ctr++] = t.Item1;                 //short
            }
            ctr = 0;
            foreach (var t in recAddrList)
            {
                recAddrListExt[ctr++] = t.Item2;                 //long - hash
            }
            string[] chgAddrListAddr = new string[recAddrList.Count];
            string[] chgAddrListExt  = new string[recAddrList.Count];
            ctr = 0;
            foreach (var t in chgAddrList)
            {
                chgAddrListAddr[ctr++] = t.Item1;
            }
            ctr = 0;
            foreach (var t in chgAddrList)
            {
                chgAddrListExt[ctr++] = t.Item2;
            }

            //get all UTXOs (unspent inputs) from receive addresses
            UTXO[] recUTXOs = ElectrumX.GetUTXOs(recAddrList, ElectrumXhost, ElectrumXport);
            UTXO.SetAddressType(recUTXOs, 0);             //receiver
            //get all UTXOs (unspent inputs) from change addresses
            UTXO[] chgUTXOs = ElectrumX.GetUTXOs(chgAddrList, ElectrumXhost, ElectrumXport);
            UTXO.SetAddressType(chgUTXOs, 1);             //change

            //start new tx
            TransactionBuilder bldr = new TransactionBuilder();

            bldr.Send(new BitcoinPubKeyAddress(pubToAddr), Money.Satoshis(satToSend)); //amount to send to recipient
            bldr.SetChange(new BitcoinPubKeyAddress(chgAddr));                         //send change to this address
            bldr.SendFees(Money.Satoshis(fee));                                        //miner (tx) fee

            //collect all UTXOs
            List <UTXO> allUTXOs = new List <UTXO>();

            allUTXOs.AddRange(recUTXOs);
            allUTXOs.AddRange(chgUTXOs);

            List <ICoin> lstTxCoins = new List <ICoin>();           //Coin is a UTXO

            //add new coin for each UTXO
            foreach (UTXO x in allUTXOs)             //tx builder will select coins from this list
            {
                BitcoinPubKeyAddress fromAddr = new BitcoinPubKeyAddress(x.Address);
                NBitcoin.Coin        cn       = null;
                //create new coin from UTXO
                bldr.AddCoins(cn = new NBitcoin.Coin(
                                  new OutPoint(new uint256(x.Tx_hash), x.Tx_pos),              //tx that funded wallet, spend this coin
                                  new TxOut(Money.Satoshis(x.Value), fromAddr.ScriptPubKey))); //specify full coin amount, else SetChange ignored
                lstTxCoins.Add(cn);                                                            //add coin to transaction, may not be used
                x.tmp = cn;                                                                    //link UTXO with coin
            }

            List <UTXO> usedUTXOs = new List <UTXO>();              //coins actually used in tx

            NBitcoin.Transaction tx = bldr.BuildTransaction(false); //sort\filter coins, some coins will not be needed\used
            //coin objects not stored in tx, so we need to determine which coins were used
            //scan tx inputs for matching coins, ignore other coins
            foreach (UTXO u in allUTXOs)
            {
                foreach (TxIn i in tx.Inputs)
                {
                    if (i.PrevOut == ((NBitcoin.Coin)u.tmp).Outpoint) //this coin in tx
                    {
                        usedUTXOs.Add(u);                             //this UTXO will be used\spent in tx
                    }
                }
            }
            //populate return object
            TxSerial txs = new TxSerial()
            {
                SendAmt      = satToSend,
                Fee          = fee,
                ExtPublicKey = extPubKey,
                ToAddress    = pubToAddr,
                ChgAddress   = chgAddr,
                WalletId     = walletId
            };
            txs.ExtPublicKey = extPubKey;

            foreach (UTXO u in usedUTXOs)
            {
                u.tmp = null;                 //don't serialize coin object, will rebuild coins in signing process
            }
            txs.InputUTXOs = new List <UTXO>();
            txs.InputUTXOs.AddRange(usedUTXOs);
            //string jsn = Newtonsoft.Json.JsonConvert.SerializeObject(txs, Newtonsoft.Json.Formatting.Indented);
            return(txs);
        }
예제 #30
0
        public async Task Test()
        {
            var hotWallet    = OpenAssetsHelper.ParseAddress("mj5FEqrC2P4FjFNfX8q3eZ4UABWUcRNy9r");
            var changeWallet = OpenAssetsHelper.ParseAddress("minog49vnNVuWK5kSs5Ut1iPyNZcR1i7he");

            var hotWalletOutputs = GenerateOutputs(5);

            var hotWalletBalance = new Money(hotWalletOutputs.Select(o => o.Amount).DefaultIfEmpty().Sum(o => o?.Satoshi ?? 0));

            var maxFeeForTransaction = Money.FromUnit(0.099M, MoneyUnit.BTC);

            var selectedCoins = new List <Coin>();

            var _feeProvider            = Config.Services.GetService <IFeeProvider>();
            var _transactionBuildHelper = Config.Services.GetService <ITransactionBuildHelper>();
            var cashoutRequests         = (GenerateCashoutRequest(200)).ToList();

            var maxInputsCount = maxFeeForTransaction.Satoshi / (await _feeProvider.GetFeeRate()).GetFee(Constants.InputSize).Satoshi;

            do
            {
                if (selectedCoins.Count > maxInputsCount && cashoutRequests.Count > 1)
                {
                    cashoutRequests = cashoutRequests.Take(cashoutRequests.Count - 1).ToList();
                    selectedCoins.Clear();
                }
                else
                if (selectedCoins.Count > 0)
                {
                    break;
                }

                var totalAmount = Money.FromUnit(cashoutRequests.Select(o => o.Amount).Sum(), MoneyUnit.BTC);

                if (hotWalletBalance < totalAmount + maxFeeForTransaction)
                {
                    var         changeBalance       = Money.Zero;
                    List <Coin> changeWalletOutputs = new List <Coin>();
                    if (hotWallet != changeWallet)
                    {
                        changeWalletOutputs = GenerateOutputs(1).ToList();
                        changeBalance       = new Money(changeWalletOutputs.Select(o => o.Amount).DefaultIfEmpty().Sum(o => o?.Satoshi ?? 0));
                    }
                    if (changeBalance + hotWalletBalance >= totalAmount + maxFeeForTransaction)
                    {
                        selectedCoins.AddRange(hotWalletOutputs);
                        selectedCoins.AddRange(OpenAssetsHelper
                                               .CoinSelect(changeWalletOutputs, totalAmount + maxFeeForTransaction - hotWalletBalance).OfType <Coin>());
                    }
                    else
                    {
                        selectedCoins.AddRange(hotWalletOutputs);
                        selectedCoins.AddRange(changeWalletOutputs);

                        int cashoutsUsedCount = 0;
                        var cashoutsAmount    = await _transactionBuildHelper.CalcFee(selectedCoins.Count, cashoutRequests.Count + 1);

                        foreach (var cashoutRequest in cashoutRequests)
                        {
                            cashoutsAmount += Money.FromUnit(cashoutRequest.Amount, MoneyUnit.BTC);
                            if (cashoutsAmount > hotWalletBalance + changeBalance)
                            {
                                break;
                            }
                            cashoutsUsedCount++;
                        }
                        if (cashoutsUsedCount == 0)
                        {
                            throw new BackendException("Not enough bitcoin available", ErrorCode.NotEnoughBitcoinAvailable);
                        }
                        cashoutRequests = cashoutRequests.Take(cashoutsUsedCount).ToList();
                    }
                }
                else
                {
                    selectedCoins.AddRange(OpenAssetsHelper.CoinSelect(hotWalletOutputs, totalAmount + maxFeeForTransaction).OfType <Coin>());
                }
            } while (true);

            var selectedCoinsAmount = new Money(selectedCoins.Sum(o => o.Amount));
            var sendAmount          = new Money(cashoutRequests.Sum(o => o.Amount), MoneyUnit.BTC);
            var builder             = new TransactionBuilder();

            builder.AddCoins(selectedCoins);
            foreach (var cashout in cashoutRequests)
            {
                var amount = Money.FromUnit(cashout.Amount, MoneyUnit.BTC);
                builder.Send(OpenAssetsHelper.ParseAddress(cashout.DestinationAddress), amount);
            }

            builder.Send(changeWallet, selectedCoinsAmount - sendAmount);

            builder.SubtractFees();
            builder.SendEstimatedFees(await _feeProvider.GetFeeRate());
            builder.SetChange(changeWallet);

            var tx = builder.BuildTransaction(true);

            _transactionBuildHelper.AggregateOutputs(tx);
        }
예제 #31
0
        public async Task AcceptToMemoryPool_WithMultiSigValidTxns_IsSuccessfullAsync()
        {
            string dataDir = GetTestDirectoryPath(this);

            var miner = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            ITestChainContext context = await TestChainFactory.CreateAsync(KnownNetworks.RegTest, miner.PubKey.Hash.ScriptPubKey, dataDir);

            IMempoolValidator validator = context.MempoolValidator;

            Assert.NotNull(validator);

            var alice   = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            var bob     = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            var satoshi = new BitcoinSecret(new Key(), KnownNetworks.RegTest);
            var nico    = new BitcoinSecret(new Key(), KnownNetworks.RegTest);

            // corp needs two out of three of alice, bob, nico
            Script corpMultiSig = PayToMultiSigTemplate
                                  .Instance
                                  .GenerateScriptPubKey(2, new[] { alice.PubKey, bob.PubKey, nico.PubKey });

            // Fund corp
            // 50 Coins come from first tx on chain - send corp 42 and change back to miner
            var         coin             = new Coin(context.SrcTxs[0].GetHash(), 0, context.SrcTxs[0].TotalOut, miner.ScriptPubKey);
            var         txBuilder        = new TransactionBuilder(KnownNetworks.RegTest);
            Transaction sendToMultiSigTx = txBuilder
                                           .AddCoins(new List <Coin> {
                coin
            })
                                           .AddKeys(miner)
                                           .Send(corpMultiSig, "42.00")
                                           .SendFees("0.001")
                                           .SetChange(miner.GetAddress())
                                           .BuildTransaction(true);

            Assert.True(txBuilder.Verify(sendToMultiSigTx)); //check fully signed
            var state = new MempoolValidationState(false);

            Assert.True(await validator.AcceptToMemoryPool(state, sendToMultiSigTx), $"Transaction: {nameof(sendToMultiSigTx)} failed mempool validation.");

            // AliceBobNico corp. send to Satoshi
            Coin[] corpCoins = sendToMultiSigTx.Outputs
                               .Where(o => o.ScriptPubKey == corpMultiSig)
                               .Select(o => new Coin(new OutPoint(sendToMultiSigTx.GetHash(), sendToMultiSigTx.Outputs.IndexOf(o)), o))
                               .ToArray();

            // Alice initiates the transaction
            txBuilder = new TransactionBuilder(KnownNetworks.RegTest);
            Transaction multiSigTx = txBuilder
                                     .AddCoins(corpCoins)
                                     .AddKeys(alice)
                                     .Send(satoshi.GetAddress(), "4.5")
                                     .SendFees("0.001")
                                     .SetChange(corpMultiSig)
                                     .BuildTransaction(true);

            Assert.True(!txBuilder.Verify(multiSigTx)); //Well, only one signature on the two required...

            // Nico completes the transaction
            txBuilder  = new TransactionBuilder(KnownNetworks.RegTest);
            multiSigTx = txBuilder
                         .AddCoins(corpCoins)
                         .AddKeys(nico)
                         .SignTransaction(multiSigTx);
            Assert.True(txBuilder.Verify(multiSigTx));

            Assert.True(await validator.AcceptToMemoryPool(state, multiSigTx), $"Transaction: {nameof(multiSigTx)} failed mempool validation.");
        }
예제 #32
0
        public string SendToAddress(decimal Amount, string Address, string ID, TickerSymbol Symbol, WalletType Wallet, int MinerFee = -1)
        {
            string result = string.Empty;

            if (MinerFee < 0)
            {
                MinerFee = WalletUtilities.GetMinerFee(Symbol);
            }

            if (Symbol == TickerSymbol.BTC)
            {
                if (Wallet == WalletType.VaultWallet)
                {
                    Key wKey = new Key(walletDat.LoadVault(ID));

                    BitcoinSecret  bitSecret  = wKey.GetBitcoinSecret(net);           //your wallet
                    BitcoinAddress bitAddress = bitSecret.PubKey.GetAddress(net);     //your address

                    BitcoinAddress SendAddress = BitcoinAddress.Create(Address, net); // address you are sending to

                    Transaction tx = new Transaction();


                    Coin[] CoinPurse = walletDat.GetCoinsByAddress(bitAddress, net, 6).ToArray();

                    TransactionBuilder txBuilder = new TransactionBuilder();
                    Transaction        builtTx   = new Transaction();

                    builtTx = txBuilder
                              .AddCoins(CoinPurse)
                              .AddKeys(wKey)
                              .Send(SendAddress, new Money(Amount, MoneyUnit.BTC))
                              .SendFees(MinerFee)
                              .SetChange(bitAddress)
                              .BuildTransaction(true);

                    builtTx = txBuilder.SignTransaction(builtTx); // sign the transaction
                    bool very = txBuilder.Verify(builtTx);        // verify the transaction for sending

                    if (netNode == null)
                    {
                        using (Node node = Node.ConnectToLocal(net)) //Connect to local if no node is set
                        {
                            node.VersionHandshake();

                            node.SendMessage(new InvPayload(InventoryType.MSG_TX, builtTx.GetHash()));

                            node.SendMessage(builtTx.CreatePayload()); // broadcast message to send funds
                            System.Threading.Thread.Sleep(500);        //Wait a bit
                        }
                    }
                    else
                    {
                        using (Node node = netNode)
                        {
                            node.VersionHandshake();

                            node.SendMessage(new InvPayload(InventoryType.MSG_TX, builtTx.GetHash()));

                            node.SendMessage(builtTx.CreatePayload()); // broadcast message to send funds
                            System.Threading.Thread.Sleep(500);        //Wait a bit
                        }
                    }

                    if (very)
                    {
                        result = "BTC VERIFIED";       // need better error codes / error catching
                    }
                    else
                    {
                        result = "BTC ERROR";
                    }
                }
            }

            return(result);
        }
예제 #33
0
        public async Task TestSpendScript()
        {
            var multisigFirstPartPk = new BitcoinSecret("cMahea7zqjxrtgAbB7LSGbcZDo359LNtib5kYpwbiSqBqvs6cqPV");
            var singlePk            = new BitcoinSecret("cPsQrkj1xqQUomDyDHXqsgnjCbZ41yfr923tWR7EuaSKH7WtDXb9");
            var revokePk            = new BitcoinSecret("cPsQrkj1xqQUomDyDHXqsgnjCbZ41yfr923tWR7EuaSKH7WtDXb9");


            var bitcoinClient     = Config.Services.GetService <IRpcBitcoinClient>();
            var bitcoinOutService = Config.Services.GetService <IBitcoinOutputsService>();
            var assetRepo         = Config.Services.GetService <IAssetRepository>();

            var helper = Config.Services.GetService <ITransactionBuildHelper>();

            var prevTx = new Transaction("0100000002347328a86bbd9d2a40e420e8d0a7da9986fd916b3ca02365c8d480936067a36cce0000008a47304402201eebb0365b67e534b72302987453d43833594965d7180c746dd5b1af27a7d6be02204496df6a47858fe9a3f6fd09dff90382e7c22a5a52c41300466bcfed808a1f39014104ff20028f41de7e2bac4f8e90884becad36c1390d2ab991a16fbcf745db478fd37cbf65c57a84e5a485bd5934c659f94aff35fe9fc50ebd2281ede40366190f57ffffffffab496631bf50d77e36303fb6156c3c73809c2023048e7e28b59c7272a8c509fc2f0000008b483045022100c6cb855117224ff9e7334ccf1030649c34a8e48eb7b6c7c4bf746d3ff67c1641022038257e6100c34c568f24d490a69b4591f93a56c433b6ad30102627a5a48ce0da01410496052ef8fb660bb338ba186dd2f52362c66b23f824295a6b74d0c60cf61a12e2b1f8b97512e09c20693f00dba9df3c644f245c120983d0582e4a88cb466ffa69ffffffff0300e1f5050000000017a914a9168848118a24ff8f848bac2eaaa248105b0307870084d717000000001976a91497a515ec03d9aada5e6f0d895f4aa10eb8f07e8d88ac800f0200000000001976a914ed75405f426601f5493117b5a22dc0082269e32288ac00000000");
            var coin   = new Coin(prevTx, 0);

            var redeem = CreateScript(multisigFirstPartPk.PubKey, revokePk.PubKey, singlePk.PubKey);

            var addr = redeem.WitHash.ScriptPubKey.Hash.GetAddress(Network.TestNet);

            var scriptCoin = new ScriptCoin(coin, redeem);

            TransactionBuilder      builder = new TransactionBuilder();
            TransactionBuildContext context = new TransactionBuildContext(Network.TestNet, null, null);

            //builder.AddKeys(pk);
            builder.AddCoins(scriptCoin);

            builder.Send(multisigFirstPartPk.PubKey.ScriptPubKey, "0.5");
            builder.SetChange(addr);
            builder.SendFees("0.001");

            var tr = builder.BuildTransaction(false);


            //tr.Inputs[0].Sequence = new Sequence(144);
            // tr.Version = 2;
            var hash = Script.SignatureHash(redeem, tr, 0, SigHash.All, scriptCoin.Amount, HashVersion.Witness);

            var signature = singlePk.PrivateKey.Sign(hash, SigHash.All).Signature.ToDER().Concat(new byte[] { 0x01 }).ToArray();

            var push1 = multisigFirstPartPk.PrivateKey.Sign(hash, SigHash.All).Signature.ToDER().Concat(new byte[] { 0x01 }).ToArray();
            var push2 = revokePk.PrivateKey.Sign(hash, SigHash.All).Signature.ToDER().Concat(new byte[] { 0x01 }).ToArray();

            var scriptParams = new OffchainScriptParams
            {
                IsMultisig   = true,
                RedeemScript = redeem.ToBytes(),
                Pushes       = new[] { new byte[0], new byte[0], new byte[0], }
            };

            tr.Inputs[0].WitScript = OffchainScriptCommitmentTemplate.GenerateScriptSig(scriptParams);
            tr.Inputs[0].ScriptSig = new Script(Op.GetPushOp(redeem.WitHash.ScriptPubKey.ToBytes(true)));

            ScriptError error;

            tr.Inputs.AsIndexedInputs().First().VerifyScript(scriptCoin.ScriptPubKey, out error);
            await bitcoinClient.BroadcastTransaction(tr, Guid.NewGuid());

            //CheckSequence(1, tr, 0);
            //CheckSig(signature.ToBytes(), pk.PubKey.ToBytes(), redeem, new TransactionChecker(tr, 0, scriptCoin.Amount), 0);
        }
예제 #34
0
        public static void TestMultisigPayout()
        {
            throw new InvalidOperationException("This function is only for testing purposes. It pays real money. Don't use except for dev/test.");

            // disable "code unreachable" warning for this code
            // ReSharper disable once CSharpWarnings::CS0162
            #pragma warning disable 162,219
            Organization organization = Organization.Sandbox; // a few testing cents here in test environment

            string bitcoinTestAddress = "3KS6AuQbZARSvqnaHoHfL1Xhm3bTLFAzoK";

            // Make a small test payment to a multisig address

            TransactionBuilder txBuilder = null; // TODO TODO TODO TODO  new TransactionBuilder();
            Int64 satoshis = new Money(100, Currency.FromCode("SEK")).ToCurrency(Currency.BitcoinCore).Cents;
            BitcoinTransactionInputs inputs      = null;
            Int64 satoshisMaximumAnticipatedFees = BitcoinUtility.GetRecommendedFeePerThousandBytesSatoshis(BitcoinChain.Cash) * 20; // assume max 20k transaction size

            try
            {
                inputs = BitcoinUtility.GetInputsForAmount(organization, satoshis + satoshisMaximumAnticipatedFees);
            }
            catch (NotEnoughFundsException)
            {
                Debugger.Break();
            }

            // If we arrive at this point, the previous function didn't throw, and we have enough money. Add the inputs to the transaction.

            txBuilder = txBuilder.AddCoins(inputs.Coins);
            txBuilder = txBuilder.AddKeys(inputs.PrivateKeys);
            Int64 satoshisInput = inputs.AmountSatoshisTotal;

            // Add outputs and prepare notifications

            Int64 satoshisUsed = 0;
            //Dictionary<int, List<string>> notificationSpecLookup = new Dictionary<int, List<string>>();
            //Dictionary<int, List<Int64>> notificationAmountLookup = new Dictionary<int, List<Int64>>();
            Payout            masterPayoutPrototype = Payout.Empty;
            HotBitcoinAddress changeAddress         = HotBitcoinAddress.OrganizationWalletZero(organization, BitcoinChain.Core);

            // Add the test payment

            if (bitcoinTestAddress.StartsWith("1")) // regular address
            {
                txBuilder = txBuilder.Send(new BitcoinPubKeyAddress(bitcoinTestAddress),
                                           new Satoshis(satoshis));
            }
            else if (bitcoinTestAddress.StartsWith("3")) // multisig
            {
                txBuilder = txBuilder.Send(new BitcoinScriptAddress(bitcoinTestAddress, Network.Main),
                                           new Satoshis(satoshis));
            }
            else
            {
                throw new InvalidOperationException("Unhandled address case");
            }
            satoshisUsed += satoshis;

            // Set change address to wallet slush

            txBuilder.SetChange(new BitcoinPubKeyAddress(changeAddress.Address));

            // Add fee

            int transactionSizeBytes = txBuilder.EstimateSize(txBuilder.BuildTransaction(false)) + inputs.Count;
            // +inputs.Count for size variability

            Int64 feeSatoshis = (transactionSizeBytes / 1000 + 1) *
                                BitcoinUtility.GetRecommendedFeePerThousandBytesSatoshis(BitcoinChain.Cash);

            txBuilder     = txBuilder.SendFees(new Satoshis(feeSatoshis));
            satoshisUsed += feeSatoshis;

            // Sign transaction - ready to execute

            Transaction txReady = txBuilder.BuildTransaction(true);

            // Verify that transaction is ready

            if (!txBuilder.Verify(txReady))
            {
                // Transaction was not signed with the correct keys. This is a serious condition.

                NotificationStrings primaryStrings = new NotificationStrings();
                primaryStrings[NotificationString.OrganizationName] = organization.Name;

                OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_PrivateKeyError,
                                                primaryStrings);

                throw new InvalidOperationException("Transaction is not signed enough");
            }

            // Broadcast transaction

            BitcoinUtility.BroadcastTransaction(txReady, BitcoinChain.Cash);

            // Note the transaction hash

            string transactionHash = txReady.GetHash().ToString();

            // Delete all old inputs, adjust balance for addresses (re-register unused inputs)

            inputs.AsUnspents.DeleteAll();

            // Log the new unspent created by change (if there is any)

            if (satoshisInput - satoshisUsed > 0)
            {
                SwarmDb.GetDatabaseForWriting()
                .CreateHotBitcoinAddressUnspentConditional(changeAddress.Identity, transactionHash,
                                                           +/* the change address seems to always get index 0? is this a safe assumption? */ 0, satoshisInput - satoshisUsed, /* confirmation count*/ 0);
            }

            // Restore "code unreachable", "unsued var" warnings
            #pragma warning restore 162,219

            // This puts the ledger out of sync, so only do this on Sandbox for various small-change (cents) testing
        }
예제 #35
0
        public void P2WSH_MultiSig_one_of_two()
        {
            var network = ObsidianXNetworksSelector.Obsidian.Mainnet();

            Key    myPrivateKey   = new Key();
            PubKey alicePublicKey = new Key().PubKey.Compress();

            Script msRedeemScript = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(1, myPrivateKey.PubKey.Compress(), alicePublicKey);

            this.output.WriteLine($"{nameof(msRedeemScript)}: {msRedeemScript}");
            this.output.WriteLine($"{nameof(msRedeemScript)}.{nameof(msRedeemScript.PaymentScript)}: {msRedeemScript.PaymentScript}");
            this.output.WriteLine("In P2SH payments, we refer to the hash of the Redeem Script as the ScriptPubKey:");
            this.output.WriteLine($"{nameof(msRedeemScript)}.{nameof(msRedeemScript.WitHash)}: {msRedeemScript.WitHash}");
            this.output.WriteLine($"{nameof(msRedeemScript)}.{nameof(msRedeemScript.WitHash)}.{nameof(msRedeemScript.WitHash.ScriptPubKey)}: {msRedeemScript.WitHash.ScriptPubKey}");

            string bech32ScriptAddress = msRedeemScript.WitHash.GetAddress(network).ToString();

            this.output.WriteLine($"{nameof(msRedeemScript)}.{nameof(msRedeemScript.WitHash)}.GetAddress(network): {bech32ScriptAddress}");

            Script msScriptPubKey = msRedeemScript.WitHash.ScriptPubKey;  // In P2SH payments, we refer to the hash of the Redeem Script as the ScriptPubKey.

            // Receive 5 coins on the ms address we just created
            Transaction received = network.CreateTransaction();

            received.Outputs.Add(new TxOut(Money.Coins(5), msRedeemScript.WitHash.ScriptPubKey));

            // Spend 1 coin from the 5 coins we received
            Coin coin = received.Outputs.AsCoins().First().ToScriptCoin(msRedeemScript);

            BitcoinWitPubKeyAddress spendMultiSigToAddress = new Key().PubKey.Compress().GetSegwitAddress(network);

            TransactionBuilder builder = new TransactionBuilder(network);

            Transaction unsigned =
                builder
                .AddCoins(coin)
                .Send(spendMultiSigToAddress, Money.Coins(1.0m))
                .SetChange(msRedeemScript.WitHash.ScriptPubKey)
                .SendFees(this.fee)
                .BuildTransaction(sign: false);

            Transaction mySigned = builder
                                   .AddCoins(coin)
                                   .AddKeys(myPrivateKey)
                                   .SignTransaction(unsigned);



            Transaction fullySigned =
                builder
                .AddCoins(coin)
                .CombineSignatures(mySigned);

            bool isVerifyPassing = builder.Verify(fullySigned, out var errors);

            foreach (var err in errors)
            {
                this.output.WriteLine(err.ToString());
            }

            this.output.WriteLine($"isVerifyPassing: {isVerifyPassing}");
            this.output.WriteLine(fullySigned.ToString());

            Assert.True(isVerifyPassing);
            Assert.True(fullySigned.HasWitness);
        }
예제 #36
0
        public override async Task <WalletBuildTransactionModel> OfflineSignRequest(OfflineSignRequest request, CancellationToken cancellationToken)
        {
            return(await Task.Run(() =>
            {
                Transaction unsignedTransaction = this.network.CreateTransaction(request.UnsignedTransaction);

                uint256 originalTxId = unsignedTransaction.GetHash();

                var builder = new TransactionBuilder(this.network);
                var coins = new List <Coin>();
                var signingKeys = new List <ISecret>();

                ExtKey seedExtKey = this.walletManager.GetExtKey(new WalletAccountReference()
                {
                    AccountName = request.WalletAccount, WalletName = request.WalletName
                }, request.WalletPassword);

                // Have to determine which private key to use for each UTXO being spent.
                bool coldStakingWithdrawal = false;
                foreach (UtxoDescriptor utxo in request.Utxos)
                {
                    Script scriptPubKey = Script.FromHex(utxo.ScriptPubKey);

                    coins.Add(new Coin(uint256.Parse(utxo.TransactionId), uint.Parse(utxo.Index), Money.Parse(utxo.Amount), scriptPubKey));

                    // Now try get the associated private key. We therefore need to determine the address that contains the UTXO.
                    string address;
                    if (scriptPubKey.IsScriptType(ScriptType.ColdStaking))
                    {
                        ColdStakingScriptTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey, out KeyId hotPubKeyHash, out KeyId coldPubKeyHash);

                        address = coldPubKeyHash.GetAddress(this.network).ToString();
                        coldStakingWithdrawal = true;
                    }
                    else
                    {
                        // We assume that if it wasn't a cold staking scriptPubKey then it must have been P2PKH.
                        address = scriptPubKey.GetDestinationAddress(this.network)?.ToString();

                        if (address == null)
                        {
                            throw new FeatureException(HttpStatusCode.BadRequest, "Could not resolve address.",
                                                       $"Could not resolve address from UTXO's scriptPubKey '{scriptPubKey.ToHex()}'.");
                        }
                    }

                    IEnumerable <HdAccount> accounts = this.walletManager.GetAccounts(request.WalletName);
                    IEnumerable <HdAddress> addresses = accounts.SelectMany(hdAccount => hdAccount.GetCombinedAddresses());

                    HdAddress hdAddress = addresses.FirstOrDefault(a => a.Address == address || a.Bech32Address == address);

                    if (coldStakingWithdrawal && hdAddress == null)
                    {
                        var coldStakingManager = this.walletManager as ColdStakingManager;
                        Wallet.Wallet wallet = coldStakingManager.GetWallet(request.WalletName);
                        HdAccount coldAccount = coldStakingManager.GetColdStakingAccount(wallet, true);
                        IEnumerable <HdAddress> coldAccountAddresses = coldAccount.GetCombinedAddresses();
                        hdAddress = coldAccountAddresses.FirstOrDefault(a => a.Address == address || a.Bech32Address == address);
                    }

                    // It is possible that the address is outside the gap limit. So if it is not found we optimistically presume the address descriptors will fill in the missing information later.
                    if (hdAddress != null)
                    {
                        ExtKey addressExtKey = seedExtKey.Derive(new KeyPath(hdAddress.HdPath));
                        BitcoinExtKey addressPrivateKey = addressExtKey.GetWif(this.network);
                        signingKeys.Add(addressPrivateKey);
                    }
                }

                // Address descriptors are 'easier' to look the private key up against if provided, but may not always be available.
                foreach (AddressDescriptor address in request.Addresses)
                {
                    ExtKey addressExtKey = seedExtKey.Derive(new KeyPath(address.KeyPath));
                    BitcoinExtKey addressPrivateKey = addressExtKey.GetWif(this.network);
                    signingKeys.Add(addressPrivateKey);
                }

                // Offline cold staking transaction handling. We check both the offline setup and the offline withdrawal cases here.
                if (unsignedTransaction.Outputs.Any(o => o.ScriptPubKey.IsScriptType(ScriptType.ColdStaking)) || coldStakingWithdrawal)
                {
                    // This will always be added in 'cold' mode if we are processing an offline signing request.
                    builder.Extensions.Add(new ColdStakingBuilderExtension(false));
                }

                builder.AddCoins(coins);
                builder.AddKeys(signingKeys.ToArray());
                builder.SignTransactionInPlace(unsignedTransaction);

                if (!builder.Verify(unsignedTransaction, out TransactionPolicyError[] errors))
                {
                    throw new FeatureException(HttpStatusCode.BadRequest, "Failed to validate signed transaction.",
                                               $"Failed to validate signed transaction '{unsignedTransaction.GetHash()}' from offline request '{originalTxId}'.");
                }

                var builtTransactionModel = new WalletBuildTransactionModel()
                {
                    TransactionId = unsignedTransaction.GetHash(), Hex = unsignedTransaction.ToHex(), Fee = request.Fee
                };

                return builtTransactionModel;
            }, cancellationToken));
예제 #37
0
        public void P2WSH_MultiSig_two_of_three()
        {
            var network = ObsidianXNetworksSelector.Obsidian.Mainnet();
            Key bob     = new Key();
            Key alice   = new Key();
            Key satoshi = new Key();

            Script msRedeemScript = PayToMultiSigTemplate
                                    .Instance
                                    .GenerateScriptPubKey(2, new[] { bob.PubKey.Compress(), alice.PubKey.Compress(), satoshi.PubKey.Compress() });

            this.output.WriteLine($"{nameof(msRedeemScript)}: {msRedeemScript}");
            this.output.WriteLine($"{nameof(msRedeemScript)}.{nameof(msRedeemScript.PaymentScript)}: {msRedeemScript.PaymentScript}");
            this.output.WriteLine("In P2SH payments, we refer to the hash of the Redeem Script as the ScriptPubKey:");
            this.output.WriteLine($"{nameof(msRedeemScript)}.{nameof(msRedeemScript.WitHash)}: {msRedeemScript.WitHash}");
            this.output.WriteLine($"{nameof(msRedeemScript)}.{nameof(msRedeemScript.WitHash)}.{nameof(msRedeemScript.WitHash.ScriptPubKey)}: {msRedeemScript.WitHash.ScriptPubKey}");

            string bech32ScriptAddress = msRedeemScript.WitHash.GetAddress(network).ToString();

            Assert.Equal(new BitcoinWitScriptAddress(bech32ScriptAddress, network).ToString(), bech32ScriptAddress);


            this.output.WriteLine($"{nameof(msRedeemScript)}.{nameof(msRedeemScript.WitHash)}.GetAddress(network): {msRedeemScript.WitHash.GetAddress(network)}");

            Script msScriptPubKey = msRedeemScript.WitHash.ScriptPubKey;  // In P2SH payments, we refer to the hash of the Redeem Script as the ScriptPubKey.

            // Receive 5 coins on the ms address we just created
            Transaction received = network.CreateTransaction();

            received.Outputs.Add(new TxOut(Money.Coins(5), msRedeemScript.WitHash.ScriptPubKey));

            // Spend 1 coin from the 5 coins we received
            Coin coin = received.Outputs.AsCoins().First().ToScriptCoin(msRedeemScript);

            BitcoinWitPubKeyAddress spendMultiSigToAddress = new Key().PubKey.Compress().GetSegwitAddress(network);
            TransactionBuilder      builder = new TransactionBuilder(network);

            Transaction unsigned =
                builder
                .AddCoins(coin)
                .Send(spendMultiSigToAddress, Money.Coins(1.0m))
                .SetChange(msRedeemScript.WitHash.ScriptPubKey)
                .SendFees(this.fee)
                .BuildTransaction(sign: false);

            Transaction aliceSigned =
                builder
                .AddCoins(coin)
                .AddKeys(alice)
                .SignTransaction(unsigned);

            Transaction bobSigned =
                builder
                .AddCoins(coin)
                .AddKeys(bob)
                //At this line, SignTransaction(unSigned) has the identical functionality with the SignTransaction(aliceSigned).
                //It's because unsigned transaction has already been signed by Alice privateKey from above.
                .SignTransaction(aliceSigned);

            Transaction fullySigned =
                builder
                .AddCoins(coin)
                .CombineSignatures(aliceSigned, bobSigned);

            bool isVerifyPassing = builder.Verify(fullySigned, out var errors);

            foreach (var err in errors)
            {
                this.output.WriteLine(err.ToString());
            }

            this.output.WriteLine($"isVerifyPassing: {isVerifyPassing}");
            this.output.WriteLine(fullySigned.ToString());

            Assert.True(isVerifyPassing);
            Assert.True(fullySigned.HasWitness);
        }
예제 #38
0
        public void NewCreateBasicSwap(uint path, params string[] seed)
        {
            List <ExtKey> keys   = new List <ExtKey>();
            Segwit        segwit = new Segwit(NBitcoin.Network.TestNet);

            for (int i = 0; i < seed.Length; i++)
            {
                var key = GetKey(path, seed[i]);
                //var address = segwit.GetP2SHAddress(key);
                keys.Add(key);
                //Console.WriteLine(address.ToString());
            }
            NBitcoin.Network _Network = NBitcoin.Network.TestNet;

            MultiSig      multi   = new MultiSig(NBitcoin.Network.TestNet);
            List <PubKey> pubKeys = new List <PubKey>();

            for (int i = 0; i < keys.Count; i++)
            {
                pubKeys.Add(keys[i].PrivateKey.PubKey);
            }
            Script pubKeyScript = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, pubKeys.ToArray());

            BitcoinAddress       address = pubKeyScript.WitHash.GetAddress(_Network);
            BitcoinScriptAddress p2sh    = address.GetScriptAddress();

            Console.WriteLine("Send money here: " + p2sh.ToString());
            REST.BlockExplorer explorer = new REST.BlockExplorer("https://testnet.blockexplorer.com/");
            var response = explorer.GetUnspent(p2sh.ToString());
            List <ExplorerUnspent> unspent      = response.Convert <List <ExplorerUnspent> >();
            List <Transaction>     transactions = new List <Transaction>();

            foreach (var item in unspent)
            {
                ExplorerResponse txResponse = explorer.GetTransaction(item.txid);
                RawFormat        format     = RawFormat.Satoshi;
                var tx = Transaction.Parse(txResponse.data, format, Network.TestNet);
                transactions.Add(tx);
            }
            //Create send transaction.

            //get redeem script
            //var redeemScript = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, pubKeys.ToArray());// multi.GetRedeemScript(2, keys.ToArray());

            Transaction received = transactions[0];
            ScriptCoin  coin     = received.Outputs.AsCoins().First().ToScriptCoin(pubKeyScript.WitHash.ScriptPubKey.Hash.ScriptPubKey);
            //create transaction:
            BitcoinAddress     destination = BitcoinAddress.Create("2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF"); //the faucet return address
            TransactionBuilder builder     = new TransactionBuilder();

            builder.AddCoins(coin);
            builder.Send(destination, Money.Coins(1.299m));
            builder.SendFees(Money.Coins(0.001m));
            builder.SetChange(destination);
            //builder.
            var unsigned = builder.BuildTransaction(sign: false);

            var         signedA = builder.AddCoins(coin).AddKeys(keys[0].PrivateKey).SignTransaction(unsigned);
            Transaction signedB = builder.AddCoins(coin).AddKeys(keys[1].PrivateKey).SignTransaction(signedA);

            Transaction fullySigned = builder.AddCoins(coin).CombineSignatures(signedA, signedB);

            Console.WriteLine(fullySigned.ToHex());
            Console.ReadLine();
        }
예제 #39
0
        public void TestPuzzlePromise()
        {
            RsaKey key = TestKeys.Default;

            Key serverEscrow = new Key();
            Key clientEscrow = new Key();

            var parameters = new PromiseParameters(key.PubKey)
            {
                FakeTransactionCount = 5,
                RealTransactionCount = 5
            };

            var client = new PromiseClientSession(parameters);
            var server = new PromiseServerSession(parameters);

            var coin = CreateEscrowCoin(serverEscrow.PubKey, clientEscrow.PubKey);

            client.ConfigureEscrowedCoin(coin, clientEscrow);
            SignaturesRequest request = client.CreateSignatureRequest(clientEscrow.PubKey.Hash, FeeRate);

            RoundTrip(ref client, parameters);
            RoundTrip(ref request);

            server.ConfigureEscrowedCoin(coin, serverEscrow, new Key().ScriptPubKey);
            PuzzlePromise.ServerCommitment[] commitments = server.SignHashes(request);
            RoundTrip(ref server, parameters);
            RoundTrip(ref commitments);

            PuzzlePromise.ClientRevelation revelation = client.Reveal(commitments);
            RoundTrip(ref client, parameters);
            RoundTrip(ref revelation);

            ServerCommitmentsProof proof = server.CheckRevelation(revelation);

            RoundTrip(ref server, parameters);
            RoundTrip(ref proof);

            var puzzleToSolve = client.CheckCommitmentProof(proof);

            RoundTrip(ref client, parameters);
            Assert.NotNull(puzzleToSolve);

            var solution     = key.SolvePuzzle(puzzleToSolve);
            var transactions = client.GetSignedTransactions(solution).ToArray();

            RoundTrip(ref client, parameters);
            Assert.True(transactions.Length == parameters.RealTransactionCount);


            var escrow = server.GetInternalState().EscrowedCoin;
            // In case things do not go well and timeout is hit...
            var redeemTransaction = server.CreateRedeemTransaction(FeeRate);
            var resigned          = redeemTransaction.ReSign(escrow);
            TransactionBuilder bb = new TransactionBuilder();

            bb.AddCoins(server.GetInternalState().EscrowedCoin);
            Assert.True(bb.Verify(resigned));

            //Check can ve reclaimed if malleated
            bb = new TransactionBuilder();
            escrow.Outpoint = new OutPoint(escrow.Outpoint.Hash, 10);
            bb.AddCoins(escrow);
            resigned = redeemTransaction.ReSign(escrow);
            Assert.False(bb.Verify(redeemTransaction.Transaction));
            Assert.True(bb.Verify(resigned));
        }
예제 #40
0
        public void CreateBasicSwap(uint path, params string[] seed)
        {
            List <ExtKey> keys   = new List <ExtKey>();
            Segwit        segwit = new Segwit(NBitcoin.Network.TestNet);

            for (int i = 0; i < seed.Length; i++)
            {
                var key     = GetKey(path, seed[i]);
                var address = segwit.GetP2SHAddress(key);
                keys.Add(key);
                //Console.WriteLine(address.ToString());
            }

            MultiSig multi = new MultiSig(NBitcoin.Network.TestNet);
            var      p2sh  = multi.GetP2SHAddress(2, keys.ToArray());

            Console.WriteLine(p2sh.ToString());
            //multi: b2366808fa2396a5a32120a27f55571055491d6ff8e6bd1e31e52bdd14b91dfb

            REST.BlockExplorer explorer = new REST.BlockExplorer("https://testnet.blockexplorer.com/");

            //var tx = explorer.GetTransaction("b2366808fa2396a5a32120a27f55571055491d6ff8e6bd1e31e52bdd14b91dfb");

            var response = explorer.GetUnspent(p2sh.ToString());
            List <ExplorerUnspent> unspent      = response.Convert <List <ExplorerUnspent> >();
            List <Transaction>     transactions = new List <Transaction>();

            foreach (var item in unspent)
            {
                ExplorerResponse txResponse = explorer.GetTransaction(item.txid);
                RawFormat        format     = RawFormat.Satoshi;
                var tx = Transaction.Parse(txResponse.data, format, Network.TestNet);
                transactions.Add(tx);
            }
            //Create send transaction.

            //get redeem script
            var         redeemScript = multi.GetRedeemScript(2, keys.ToArray());
            Transaction received     = transactions[0];
            ScriptCoin  coin         = received.Outputs.AsCoins().First().ToScriptCoin(redeemScript);

            //create transaction:

            BitcoinAddress     destination = BitcoinAddress.Create("2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF"); //the faucet return address
            TransactionBuilder builder     = new TransactionBuilder();

            builder.AddCoins(coin);
            builder.Send(destination, Money.Coins(1.299m));
            builder.SendFees(Money.Coins(0.001m));
            builder.SetChange(destination);
            //builder.
            var unsigned = builder.BuildTransaction(sign: false);

            var         signedA = builder.AddCoins(coin).AddKeys(keys[0].PrivateKey).SignTransaction(unsigned);
            Transaction signedB = builder.AddCoins(coin).AddKeys(keys[1].PrivateKey).SignTransaction(signedA);

            Transaction fullySigned = builder.AddCoins(coin).CombineSignatures(signedA, signedB);

            Console.WriteLine(fullySigned.ToHex());
            Console.ReadLine();
        }
예제 #41
0
        public static void PerformAutomated(BitcoinChain chain)
        {
            // Perform all waiting hot payouts for all orgs in the installation

            throw new NotImplementedException("Waiting for rewrite for Bitcoin Cash");

            // TODO

            DateTime utcNow = DateTime.UtcNow;

            foreach (Organization organization in Organizations.GetAll())
            {
                // If this org doesn't do hotwallet, continue
                if (organization.FinancialAccounts.AssetsBitcoinHot == null)
                {
                    continue;
                }

                Payouts orgPayouts     = Payouts.Construct(organization);
                Payouts bitcoinPayouts = new Payouts();
                Dictionary <string, Int64> satoshiPayoutLookup     = new Dictionary <string, long>();
                Dictionary <string, Int64> nativeCentsPayoutLookup = new Dictionary <string, long>();
                Dictionary <int, Int64>    satoshiPersonLookup     = new Dictionary <int, long>();
                Dictionary <int, Int64>    nativeCentsPersonLookup = new Dictionary <int, long>();
                Int64 satoshisTotal = 0;

                string currencyCode = organization.Currency.Code;

                // For each ready payout that can automate, add an output to a constructed transaction

                TransactionBuilder txBuilder = null; // TODO TODO TODO TODO new TransactionBuilder();
                foreach (Payout payout in orgPayouts)
                {
                    if (payout.ExpectedTransactionDate > utcNow)
                    {
                        continue; // payout is not due yet
                    }

                    if (payout.RecipientPerson != null && payout.RecipientPerson.BitcoinPayoutAddress.Length > 2 &&
                        payout.Account.Length < 4)
                    {
                        // If the payout address is still in quarantine, don't pay out yet

                        string addressSetTime = payout.RecipientPerson.BitcoinPayoutAddressTimeSet;
                        if (addressSetTime.Length > 4 && DateTime.Parse(addressSetTime, CultureInfo.InvariantCulture).AddHours(48) > utcNow)
                        {
                            continue; // still in quarantine
                        }

                        // Test the payout address - is it valid and can we handle it?

                        if (!BitcoinUtility.IsValidBitcoinAddress(payout.RecipientPerson.BitcoinPayoutAddress))
                        {
                            // Notify person that address is invalid, then clear it

                            NotificationStrings       primaryStrings   = new NotificationStrings();
                            NotificationCustomStrings secondaryStrings = new NotificationCustomStrings();
                            primaryStrings[NotificationString.OrganizationName] = organization.Name;
                            secondaryStrings["BitcoinAddress"] = payout.RecipientPerson.BitcoinPayoutAddress;

                            OutboundComm.CreateNotification(organization, NotificationResource.BitcoinPayoutAddress_Bad,
                                                            primaryStrings, secondaryStrings,
                                                            People.FromSingle(payout.RecipientPerson));

                            payout.RecipientPerson.BitcoinPayoutAddress = string.Empty;

                            continue; // do not add this payout
                        }

                        // Ok, so it seems we're making this payout at this time.

                        bitcoinPayouts.Add(payout);

                        int recipientPersonId = payout.RecipientPerson.Identity;

                        if (!satoshiPersonLookup.ContainsKey(recipientPersonId))
                        {
                            satoshiPersonLookup[recipientPersonId]     = 0;
                            nativeCentsPersonLookup[recipientPersonId] = 0;
                        }

                        nativeCentsPersonLookup[recipientPersonId] += payout.AmountCents;

                        // Find the amount of satoshis for this payout

                        if (organization.Currency.IsBitcoinCore)
                        {
                            satoshiPayoutLookup[payout.ProtoIdentity]     = payout.AmountCents;
                            nativeCentsPayoutLookup[payout.ProtoIdentity] = payout.AmountCents;
                            satoshisTotal += payout.AmountCents;
                            satoshiPersonLookup[recipientPersonId] += payout.AmountCents;
                        }
                        else
                        {
                            // Convert currency
                            Money payoutAmount = new Money(payout.AmountCents, organization.Currency);
                            Int64 satoshis     = payoutAmount.ToCurrency(Currency.BitcoinCore).Cents;
                            satoshiPayoutLookup[payout.ProtoIdentity]     = satoshis;
                            nativeCentsPayoutLookup[payout.ProtoIdentity] = payout.AmountCents;
                            satoshisTotal += satoshis;
                            satoshiPersonLookup[recipientPersonId] += satoshis;
                        }
                    }
                    else if (payout.RecipientPerson != null && payout.RecipientPerson.BitcoinPayoutAddress.Length < 3 && payout.Account.Length < 4)
                    {
                        // There is a payout for this person, but they don't have a bitcoin payout address set. Send notification to this effect once a day.

                        if (utcNow.Minute != 0)
                        {
                            continue;
                        }
                        if (utcNow.Hour != 12)
                        {
                            continue;
                        }

                        NotificationStrings primaryStrings = new NotificationStrings();
                        primaryStrings[NotificationString.OrganizationName] = organization.Name;
                        OutboundComm.CreateNotification(organization, NotificationResource.BitcoinPayoutAddress_PleaseSet, primaryStrings, People.FromSingle(payout.RecipientPerson));
                    }
                    else if (payout.Account.StartsWith("bitcoin:"))
                    {
                    }
                }

                if (bitcoinPayouts.Count == 0)
                {
                    // no automated payments pending for this organization, nothing more to do
                    continue;
                }

                // We now have our desired payments. The next step is to find enough inputs to reach the required amount (plus fees; we're working a little blind here still).

                BitcoinTransactionInputs inputs      = null;
                Int64 satoshisMaximumAnticipatedFees = BitcoinUtility.GetRecommendedFeePerThousandBytesSatoshis(chain) * 20; // assume max 20k transaction size

                try
                {
                    inputs = BitcoinUtility.GetInputsForAmount(organization, satoshisTotal + satoshisMaximumAnticipatedFees);
                }
                catch (NotEnoughFundsException)
                {
                    // If we're at the whole hour, send a notification to people responsible for refilling the hotwallet

                    if (utcNow.Minute != 0)
                    {
                        continue; // we're not at the whole hour, so continue with next org instead
                    }

                    // Send urgent notification to top up the damn wallet so we can make payments

                    NotificationStrings primaryStrings = new NotificationStrings();
                    primaryStrings[NotificationString.CurrencyCode]     = organization.Currency.Code;
                    primaryStrings[NotificationString.OrganizationName] = organization.Name;
                    NotificationCustomStrings secondaryStrings = new NotificationCustomStrings();
                    Int64 satoshisAvailable = HotBitcoinAddresses.ForOrganization(organization).BalanceSatoshisTotal;

                    secondaryStrings["AmountMissingMicrocoinsFloat"] =
                        ((satoshisTotal - satoshisAvailable + satoshisMaximumAnticipatedFees) / 100.0).ToString("N2");

                    if (organization.Currency.IsBitcoinCore)
                    {
                        secondaryStrings["AmountNeededFloat"] = ((satoshisTotal + satoshisMaximumAnticipatedFees) / 100.0).ToString("N2");
                        secondaryStrings["AmountWalletFloat"] = (satoshisAvailable / 100.0).ToString("N2");
                    }
                    else
                    {
                        // convert to org native currency

                        secondaryStrings["AmountNeededFloat"] =
                            (new Money(satoshisTotal, Currency.BitcoinCore).ToCurrency(organization.Currency).Cents / 100.0).ToString("N2");
                        secondaryStrings["AmountWalletFloat"] =
                            (new Money(satoshisAvailable, Currency.BitcoinCore).ToCurrency(organization.Currency).Cents / 100.0).ToString("N2");
                    }

                    OutboundComm.CreateNotification(organization,
                                                    NotificationResource.Bitcoin_Shortage_Critical, primaryStrings, secondaryStrings, People.FromSingle(Person.FromIdentity(1)));

                    continue; // with next organization
                }

                // If we arrive at this point, the previous function didn't throw, and we have enough money.
                // Ensure the existence of a cost account for bitcoin miner fees.

                organization.EnsureMinerFeeAccountExists();

                // Add the inputs to the transaction.

                txBuilder = txBuilder.AddCoins(inputs.Coins);
                txBuilder = txBuilder.AddKeys(inputs.PrivateKeys);
                Int64 satoshisInput = inputs.AmountSatoshisTotal;

                // Add outputs and prepare notifications

                Int64 satoshisUsed = 0;
                Dictionary <int, List <string> > notificationSpecLookup   = new Dictionary <int, List <string> >();
                Dictionary <int, List <Int64> >  notificationAmountLookup = new Dictionary <int, List <Int64> >();
                Payout            masterPayoutPrototype = Payout.Empty;
                HotBitcoinAddress changeAddress         = HotBitcoinAddress.OrganizationWalletZero(organization, BitcoinChain.Core); // TODO: CHAIN!

                foreach (Payout payout in bitcoinPayouts)
                {
                    int recipientPersonId = payout.RecipientPerson.Identity;
                    if (!notificationSpecLookup.ContainsKey(recipientPersonId))
                    {
                        notificationSpecLookup[recipientPersonId]   = new List <string>();
                        notificationAmountLookup[recipientPersonId] = new List <Int64>();
                    }
                    notificationSpecLookup[recipientPersonId].Add(payout.Specification);
                    notificationAmountLookup[recipientPersonId].Add(payout.AmountCents);

                    if (payout.RecipientPerson.BitcoinPayoutAddress.StartsWith("1"))  // regular address
                    {
                        txBuilder = txBuilder.Send(new BitcoinPubKeyAddress(payout.RecipientPerson.BitcoinPayoutAddress),
                                                   new Satoshis(satoshiPayoutLookup[payout.ProtoIdentity]));
                    }
                    else if (payout.RecipientPerson.BitcoinPayoutAddress.StartsWith("3"))  // multisig
                    {
                        txBuilder = txBuilder.Send(new BitcoinScriptAddress(payout.RecipientPerson.BitcoinPayoutAddress, Network.Main),
                                                   new Satoshis(satoshiPayoutLookup[payout.ProtoIdentity]));
                    }
                    else
                    {
                        throw new InvalidOperationException("Unhandled bitcoin address type in Payouts.PerformAutomated(): " + payout.RecipientPerson.BitcoinPayoutAddress);
                    }

                    satoshisUsed += satoshiPayoutLookup[payout.ProtoIdentity];

                    payout.MigrateDependenciesTo(masterPayoutPrototype);
                }

                // Set change address to wallet slush

                txBuilder.SetChange(new BitcoinPubKeyAddress(changeAddress.Address));

                // Add fee

                int transactionSizeBytes = txBuilder.EstimateSize(txBuilder.BuildTransaction(false)) + inputs.Count;
                // +inputs.Count for size variability

                Int64 feeSatoshis = (transactionSizeBytes / 1000 + 1) * BitcoinUtility.GetRecommendedFeePerThousandBytesSatoshis(chain);

                txBuilder     = txBuilder.SendFees(new Satoshis(feeSatoshis));
                satoshisUsed += feeSatoshis;

                // Sign transaction - ready to execute

                Transaction txReady = txBuilder.BuildTransaction(true);

                // Verify that transaction is ready

                if (!txBuilder.Verify(txReady))
                {
                    // Transaction was not signed with the correct keys. This is a serious condition.

                    NotificationStrings primaryStrings = new NotificationStrings();
                    primaryStrings[NotificationString.OrganizationName] = organization.Name;

                    OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_PrivateKeyError,
                                                    primaryStrings);

                    throw new InvalidOperationException("Transaction is not signed enough");
                }

                // Broadcast transaction

                BitcoinUtility.BroadcastTransaction(txReady, BitcoinChain.Cash);

                // Note the transaction hash

                string transactionHash = txReady.GetHash().ToString();

                // Delete all old inputs, adjust balance for addresses (re-register unused inputs)

                inputs.AsUnspents.DeleteAll();

                // Log the new unspent created by change (if there is any)

                if (satoshisInput - satoshisUsed > 0)
                {
                    SwarmDb.GetDatabaseForWriting()
                    .CreateHotBitcoinAddressUnspentConditional(changeAddress.Identity, transactionHash,
                                                               +/* the change address seems to always get index 0? is this a safe assumption? */ 0, satoshisInput - satoshisUsed, /* confirmation count*/ 0);
                }

                // Register new balance of change address, should have increased by (satoshisInput-satoshisUsed)

                // TODO

                // Send notifications

                foreach (int personId in notificationSpecLookup.Keys)
                {
                    Person person = Person.FromIdentity(personId);

                    string spec = string.Empty;
                    for (int index = 0; index < notificationSpecLookup[personId].Count; index++)
                    {
                        spec += String.Format(" * {0,-40} {1,14:N2} {2,-4}\r\n", notificationSpecLookup[personId][index], notificationAmountLookup[personId][index] / 100.0, currencyCode);
                    }

                    spec = spec.TrimEnd();

                    NotificationStrings       primaryStrings   = new NotificationStrings();
                    NotificationCustomStrings secondaryStrings = new NotificationCustomStrings();

                    primaryStrings[NotificationString.OrganizationName]         = organization.Name;
                    primaryStrings[NotificationString.CurrencyCode]             = organization.Currency.DisplayCode;
                    primaryStrings[NotificationString.EmbeddedPreformattedText] = spec;
                    secondaryStrings["AmountFloat"]        = (nativeCentsPersonLookup[personId] / 100.0).ToString("N2");
                    secondaryStrings["BitcoinAmountFloat"] = (satoshiPersonLookup[personId] / 100.0).ToString("N2");
                    secondaryStrings["BitcoinAddress"]     = person.BitcoinPayoutAddress; // warn: potential rare race condition here

                    OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_PaidOut, primaryStrings,
                                                    secondaryStrings, People.FromSingle(person));
                }

                // Create the master payout from its prototype

                Payout masterPayout = Payout.CreateBitcoinPayoutFromPrototype(organization, masterPayoutPrototype, txReady.GetHash().ToString());

                // Finally, create ledger entries and notify

                NotificationStrings       masterPrimaryStrings   = new NotificationStrings();
                NotificationCustomStrings masterSecondaryStrings = new NotificationCustomStrings();

                masterPrimaryStrings[NotificationString.OrganizationName] = organization.Name;
                masterPrimaryStrings[NotificationString.CurrencyCode]     = organization.Currency.DisplayCode;
                masterSecondaryStrings["AmountFloat"] =
                    (new Swarmops.Logic.Financial.Money(satoshisUsed, Currency.BitcoinCore).ToCurrency(
                         organization.Currency).Cents / 100.0).ToString("N2", CultureInfo.InvariantCulture);
                masterSecondaryStrings["BitcoinAmountFloat"] = (satoshisUsed / 100.0).ToString("N2", CultureInfo.InvariantCulture);
                masterSecondaryStrings["PaymentCount"]       = bitcoinPayouts.Count.ToString("N0", CultureInfo.InvariantCulture);

                OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_Hotwallet_Outflow,
                                                masterPrimaryStrings, masterSecondaryStrings);

                // TODO: special case for native-bitcoin organizations vs. fiat-currency organizations

                FinancialTransaction ledgerTransaction = FinancialTransaction.Create(organization, utcNow,
                                                                                     "Bitcoin automated payout");

                if (organization.Currency.IsBitcoinCore)
                {
                    ledgerTransaction.AddRow(organization.FinancialAccounts.AssetsBitcoinHot, -(masterPayoutPrototype.AmountCents + feeSatoshis), null);
                    ledgerTransaction.AddRow(organization.FinancialAccounts.CostsBitcoinFees, feeSatoshis, null);
                }
                else
                {
                    // If the ledger isn't using bitcoin natively, we need to translate the miner fee paid to ledger cents before entering it into ledger

                    Int64 feeCentsLedger = new Money(feeSatoshis, Currency.BitcoinCore).ToCurrency(organization.Currency).Cents;
                    ledgerTransaction.AddRow(organization.FinancialAccounts.AssetsBitcoinHot, -(masterPayoutPrototype.AmountCents + feeCentsLedger), null).AmountForeignCents = new Money(satoshisUsed, Currency.BitcoinCore);
                    ledgerTransaction.AddRow(organization.FinancialAccounts.CostsBitcoinFees, feeCentsLedger, null);
                }

                ledgerTransaction.BlockchainHash = transactionHash;

                masterPayout.BindToTransactionAndClose(ledgerTransaction, null);
            }
        }
예제 #42
0
 public PostAccountTransactionRequest(string requestSourceName, TransactionBuilder builder)
     : base(requestSourceName)
 {
     Builder = builder ?? throw new ArgumentNullException(nameof(builder));
 }
예제 #43
0
		public void CanEstimatedFeesCorrectlyIfFeesChangeTransactionSize()
		{
			var redeem = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, new Key().PubKey, new Key().PubKey, new Key().PubKey);
			var transactionBuilder = new TransactionBuilder();
			transactionBuilder.AddCoins(new Coin(new OutPoint(uint256.Parse("75425c904289f21feef0cffab2081ba22030b633623115adf0780edad443e6c7"), 1), new TxOut("0.00010000", PayToScriptHashTemplate.Instance.GenerateScriptPubKey(redeem).GetDestinationAddress(Network.Main))).ToScriptCoin(redeem));
			transactionBuilder.AddCoins(new Coin(new OutPoint(uint256.Parse("75425c904289f21feef0cffab2081ba22030b633623115adf0780edad443e6c7"), 2), new TxOut("0.00091824", PayToScriptHashTemplate.Instance.GenerateScriptPubKey(redeem).GetDestinationAddress(Network.Main))).ToScriptCoin(redeem));
			transactionBuilder.AddCoins(new Coin(new OutPoint(uint256.Parse("75425c904289f21feef0cffab2081ba22030b633623115adf0780edad443e6c7"), 3), new TxOut("0.00100000", PayToScriptHashTemplate.Instance.GenerateScriptPubKey(redeem).GetDestinationAddress(Network.Main))).ToScriptCoin(redeem));
			transactionBuilder.AddCoins(new Coin(new OutPoint(uint256.Parse("75425c904289f21feef0cffab2081ba22030b633623115adf0780edad443e6c7"), 4), new TxOut("0.00100000", PayToScriptHashTemplate.Instance.GenerateScriptPubKey(redeem).GetDestinationAddress(Network.Main))).ToScriptCoin(redeem));
			transactionBuilder.AddCoins(new Coin(new OutPoint(uint256.Parse("75425c904289f21feef0cffab2081ba22030b633623115adf0780edad443e6c7"), 5), new TxOut("0.00246414", PayToScriptHashTemplate.Instance.GenerateScriptPubKey(redeem).GetDestinationAddress(Network.Main))).ToScriptCoin(redeem));
			transactionBuilder.AddCoins(new Coin(new OutPoint(uint256.Parse("75425c904289f21feef0cffab2081ba22030b633623115adf0780edad443e6c7"), 6), new TxOut("0.00250980", PayToScriptHashTemplate.Instance.GenerateScriptPubKey(redeem).GetDestinationAddress(Network.Main))).ToScriptCoin(redeem));
			transactionBuilder.AddCoins(new Coin(new OutPoint(uint256.Parse("75425c904289f21feef0cffab2081ba22030b633623115adf0780edad443e6c7"), 7), new TxOut("0.01000000", PayToScriptHashTemplate.Instance.GenerateScriptPubKey(redeem).GetDestinationAddress(Network.Main))).ToScriptCoin(redeem));
			transactionBuilder.Send(new Key().PubKey.GetAddress(Network.Main), "0.01000000");
			transactionBuilder.SetChange(new Key().PubKey.GetAddress(Network.Main));

			var feeRate = new FeeRate((long)32563);
			//Adding the estimated fees will cause 6 more coins to be included, so let's verify the actual sent fees take that into account
			transactionBuilder.SendEstimatedFees(feeRate);
			var tx = transactionBuilder.BuildTransaction(false);
			var estimation = transactionBuilder.EstimateFees(tx, feeRate);
			Assert.Equal(estimation, tx.GetFee(transactionBuilder.FindSpentCoins(tx)));
		}
예제 #44
0
        public void Mining()
        {
            var(privateKey, publicKey) = SignManager.GenerateKeys();
            var publickKeyHash = new HexString(HashUtil.RIPEMD_SHA256(publicKey));
            //Genesis Mining
            var genesis = BlockchainManager.CreateGenesis();
            var miner   = new Miner
            {
                MinerKeyHash = publickKeyHash
            };

            Console.WriteLine("Mining");
            miner.Mining(genesis, Context.CancellationToken);
            BlockchainManager.Chain.Add(genesis);


            for (var i = 0; i < 10; i++)
            {
                var gg = BlockchainManager.CreateCoinBaseTransaction(i + 1, publickKeyHash.Bytes, $"まかろに{i}");
                gg.TimeStamp = DateTime.UtcNow;
                var txs = new List <Transaction>()
                {
                    gg
                };
                var rootHash = HashUtil.ComputeMerkleRootHash(txs.Select(x => x.Id).ToList());

                var b = new Block()
                {
                    PreviousBlockHash = BlockchainManager.Chain.Last().Id,
                    Transactions      = txs,
                    MerkleRootHash    = rootHash,
                    Timestamp         = DateTime.UtcNow,
                    Bits = 1
                };
                miner.Mining(b, Context.CancellationToken);
                BlockchainManager.Chain.Add(b);
                Task.Delay(10).GetAwaiter().GetResult();
            }

            //Second Block Mining
            Console.WriteLine($"{genesis.Transactions.Count}");
            var tb  = new TransactionBuilder();
            var ttx = BlockchainManager.Chain.SelectMany(x => x.Transactions).First(x => x.Engraving == "まかろに0");

            var input = new Input()
            {
                TransactionId = ttx.Id,
                OutputIndex   = 0,
            };
            var output = new Output()
            {
                Amount        = 10,
                PublicKeyHash = publickKeyHash.Bytes
            };

            tb.Inputs.Add(input);
            tb.Outputs.Add(output);
            var tx = tb.ToSignedTransaction(privateKey, publicKey);

            BlockchainManager.TransactionPool.Add(tx);
            miner.Start();

            Console.WriteLine($"{BlockchainManager.VerifyBlockchain()} : OK");
            Console.ReadLine();
        }
예제 #45
0
		//https://gist.github.com/gavinandresen/3966071
		public void CanBuildTransactionWithDustPrevention()
		{
			var bob = new Key();
			var alice = new Key();
			var tx = new Transaction()
			{
				Outputs =
				{
					new TxOut(Money.Coins(1.0m), bob)
				}
			};
			var coins = tx.Outputs.AsCoins().ToArray();

			var builder = new TransactionBuilder();
			builder.StandardTransactionPolicy = EasyPolicy.Clone();
			builder.StandardTransactionPolicy.MinRelayTxFee = new FeeRate(new Money(1000));

			Func<Transaction> create = () => builder
				.AddCoins(coins)
				.AddKeys(bob)
				.Send(alice, Money.Coins(0.99m))
				.Send(alice, Money.Satoshis(500))
				.Send(TxNullDataTemplate.Instance.GenerateScriptPubKey(new byte[] { 1, 2 }), Money.Zero)
				.SendFees(Money.Coins(0.0001m))
				.SetChange(bob)
				.BuildTransaction(true);

			var signed = create();

			Assert.True(signed.Outputs.Count == 3);
			Assert.True(builder.Verify(signed, Money.Coins(0.0001m)));
			builder.DustPrevention = false;

			TransactionPolicyError[] errors;
			Assert.False(builder.Verify(signed, Money.Coins(0.0001m), out errors));
			var ex = (NotEnoughFundsPolicyError)errors.Single();
			Assert.True((Money)ex.Missing == Money.Parse("-0.00000500"));

			builder = new TransactionBuilder();
			builder.DustPrevention = false;
			builder.StandardTransactionPolicy = EasyPolicy.Clone();
			builder.StandardTransactionPolicy.MinRelayTxFee = new FeeRate(new Money(1000));
			signed = create();
			Assert.True(signed.Outputs.Count == 4);
			Assert.False(builder.Verify(signed, out errors));
			Assert.True(errors.Length == 1);
			Assert.True(errors[0] is DustPolicyError);
		}
예제 #46
0
        public void CanSyncWallet2()
        {
            using (NodeServerTester servers = new NodeServerTester(Network.TestNet))
            {
                var chainBuilder = new BlockchainBuilder();
                SetupSPVBehavior(servers, chainBuilder);
                NodesGroup aliceConnection = CreateGroup(servers, 1);
                NodesGroup bobConnection   = CreateGroup(servers, 1);

                var    aliceKey = new ExtKey();
                Wallet alice    = new Wallet(new WalletCreation()
                {
                    Network           = Network.TestNet,
                    RootKeys          = new[] { aliceKey.Neuter() },
                    SignatureRequired = 1,
                    UseP2SH           = false
                }, 11);
                Wallet bob = new Wallet(new WalletCreation()
                {
                    Network           = Network.TestNet,
                    RootKeys          = new[] { new ExtKey().Neuter() },
                    SignatureRequired = 1,
                    UseP2SH           = false
                }, 11);

                alice.Configure(aliceConnection);
                alice.Connect();

                bob.Configure(bobConnection);
                bob.Connect();

                TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 1);

                //New address tracked
                var addressAlice = alice.GetNextScriptPubKey();
                chainBuilder.GiveMoney(addressAlice, Money.Coins(1.0m));
                TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 0);                 //Purge
                TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 1);                 //Reconnect
                //////

                TestUtils.Eventually(() => alice.GetTransactions().Count == 1);

                //Alice send tx to bob
                var coins = alice.GetTransactions().GetSpendableCoins();
                var keys  = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey))
                            .Select(k => aliceKey.Derive(k))
                            .ToArray();
                var builder = new TransactionBuilder();
                var tx      =
                    builder
                    .SetTransactionPolicy(new Policy.StandardTransactionPolicy()
                {
                    MinRelayTxFee = new FeeRate(0)
                })
                    .AddCoins(coins)
                    .AddKeys(keys)
                    .Send(bob.GetNextScriptPubKey(), Money.Coins(0.4m))
                    .SetChange(alice.GetNextScriptPubKey(true))
                    .BuildTransaction(true);

                Assert.True(builder.Verify(tx));

                chainBuilder.BroadcastTransaction(tx);

                //Alice get change
                TestUtils.Eventually(() => alice.GetTransactions().Count == 2);
                coins = alice.GetTransactions().GetSpendableCoins();
                Assert.True(coins.Single().Amount == Money.Coins(0.6m));
                //////

                //Bob get coins
                TestUtils.Eventually(() => bob.GetTransactions().Count == 1);
                coins = bob.GetTransactions().GetSpendableCoins();
                Assert.True(coins.Single().Amount == Money.Coins(0.4m));
                //////


                MemoryStream bobWalletBackup = new MemoryStream();
                bob.Save(bobWalletBackup);
                bobWalletBackup.Position = 0;

                MemoryStream bobTrakerBackup = new MemoryStream();
                bob.Tracker.Save(bobTrakerBackup);
                bobTrakerBackup.Position = 0;

                bob.Disconnect();

                //Restore bob
                bob = Wallet.Load(bobWalletBackup);
                bobConnection.NodeConnectionParameters.TemplateBehaviors.Remove <TrackerBehavior>();
                bobConnection.NodeConnectionParameters.TemplateBehaviors.Add(new TrackerBehavior(Tracker.Load(bobTrakerBackup), chainBuilder.Chain));
                /////

                bob.Configure(bobConnection);

                //Bob still has coins
                TestUtils.Eventually(() => bob.GetTransactions().Count == 1);
                coins = bob.GetTransactions().GetSpendableCoins();
                Assert.True(coins.Single().Amount == Money.Coins(0.4m));
                //////

                bob.Connect();
                TestUtils.Eventually(() => bobConnection.ConnectedNodes.Count == 1);

                //New block found !
                chainBuilder.FindBlock();

                //Alice send tx to bob
                coins = alice.GetTransactions().GetSpendableCoins();
                keys  = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey))
                        .Select(k => aliceKey.Derive(k))
                        .ToArray();
                builder = new TransactionBuilder();
                tx      =
                    builder
                    .SetTransactionPolicy(new Policy.StandardTransactionPolicy()
                {
                    MinRelayTxFee = new FeeRate(0)
                })
                    .AddCoins(coins)
                    .AddKeys(keys)
                    .Send(bob.GetNextScriptPubKey(), Money.Coins(0.1m))
                    .SetChange(alice.GetNextScriptPubKey(true))
                    .BuildTransaction(true);

                Assert.True(builder.Verify(tx));

                chainBuilder.BroadcastTransaction(tx);

                //Bob still has coins
                TestUtils.Eventually(() => bob.GetTransactions().Count == 2);                 //Bob has both, old and new tx
                coins = bob.GetTransactions().GetSpendableCoins();
                //////
            }
        }
예제 #47
0
		//http://brainwallet.org/#tx
		public void CanGetTransactionErrors()
		{
			Key bob = new Key();
			Key alice = new Key();

			var funding = new Transaction();
			funding.Outputs.Add(new TxOut(Money.Coins(1.0m), bob));
			funding.Outputs.Add(new TxOut(Money.Coins(1.1m), bob));
			funding.Outputs.Add(new TxOut(Money.Coins(1.2m), alice));

			var spending = new Transaction();
			spending.Inputs.Add(new TxIn(new OutPoint(funding, 0)));
			spending.Inputs.Add(new TxIn(new OutPoint(funding, 0))); //Duplicate
			spending.Inputs.Add(new TxIn(new OutPoint(funding, 1)));
			spending.Inputs.Add(new TxIn(new OutPoint(funding, 2))); //Alice will not sign

			spending.Outputs.Add(new TxOut(Money.Coins(4.0m), bob));


			TransactionPolicyError[] errors = null;
			TransactionBuilder builder = new TransactionBuilder();
			builder.StandardTransactionPolicy = EasyPolicy;
			builder.AddKeys(bob);
			builder.AddCoins(funding.Outputs.AsCoins());
			builder.SignTransactionInPlace(spending);
			Assert.False(builder.Verify(spending, Money.Coins(1.0m), out errors));

			var dup = errors.OfType<DuplicateInputPolicyError>().Single();
			AssertEx.CollectionEquals(new uint[] { 0, 1 }, dup.InputIndices);
			AssertEx.Equals(new OutPoint(funding, 0), dup.OutPoint);

			var script = errors.OfType<ScriptPolicyError>().Single();
			AssertEx.Equals(alice.ScriptPubKey, script.ScriptPubKey);
			AssertEx.Equals(3, script.InputIndex);

			var fees = errors.OfType<NotEnoughFundsPolicyError>().Single();
			Assert.Equal(fees.Missing, Money.Coins(0.7m));

			spending.Inputs.Add(new TxIn(new OutPoint(funding, 3))); //Coins not found
			builder.Verify(spending, Money.Coins(1.0m), out errors);
			var coin = errors.OfType<CoinNotFoundPolicyError>().Single();
			Assert.Equal(coin.InputIndex, 4UL);
			Assert.Equal(coin.OutPoint.N, 3UL);
		}
예제 #48
0
        public static async Task MainAsync(string[] args)
        {
            // Parse options and show helptext if insufficient
            g_Options = new Options();
            Parser.Default.ParseArguments(args, g_Options);
            if (!g_Options.IsValid())
            {
                var help = HelpText.AutoBuild(g_Options);
                Console.WriteLine(help.ToString());
                return;
            }

            // Parse server URI
            if (String.IsNullOrEmpty(g_Options.Environment) || !g_Options.Environment.StartsWith("http"))
            {
                Console.WriteLine($"Invalid URI: {g_Options.Environment}");
                return;
            }

            // Set up AvaTax
            g_Client = new AvaTaxClient("AvaTax-Connect", "1.0", Environment.MachineName, new Uri(g_Options.Environment))
                       .WithSecurity(g_Options.Username, g_Options.Password);

            // Attempt to connect
            PingResultModel ping = null;

            try {
                ping = g_Client.Ping();
            } catch (Exception ex) {
                Console.WriteLine("Unable to contact AvaTax:");
                HandleException(ex);
                return;
            }

            // Fetch the company
            FetchResult <CompanyModel> companies = null;

            try {
                companies = await g_Client.QueryCompaniesAsync(null, $"companyCode eq '{g_Options.CompanyCode}'", null, null, null);
            } catch (Exception ex) {
                Console.WriteLine("Exception fetching companies");
                HandleException(ex);
                return;
            }

            // Check if the company exists
            if (companies == null || companies.count != 1)
            {
                Console.WriteLine($"Company with code '{g_Options.CompanyCode}' not found.\r\nPlease provide a valid companyCode using the '-c' parameter.");
                return;
            }

            // Check if the company is flagged as a test
            if ((companies.value[0].isTest != true) && IsPermanent(g_Options.DocType))
            {
                Console.WriteLine($"Company with code '{g_Options.CompanyCode}' is not flagged as a test company.\r\nYour test is configured to use document type '{g_Options.DocType}'.\r\nThis is a permanent document type.\r\nWhen testing with permanent document types, AvaTax-Connect can only be run against a test company.");
                return;
            }

            // Did we authenticate?
            if (ping.authenticated != true)
            {
                Console.WriteLine("Authentication did not succeed.  Please check your credentials and try again.");
                Console.WriteLine($"       Username: {g_Options.Username}");
                Console.WriteLine($"       Password: REDACTED - PLEASE CHECK COMMAND LINE");
                Console.WriteLine($"    Environment: {g_Options.Environment}");
                return;
            }

            // Print out information about our configuration
            Console.WriteLine($"AvaTax-Connect Performance Testing Tool");
            Console.WriteLine($"=======================================");
            Console.WriteLine($"         User: {g_Options.Username}");
            Console.WriteLine($"      Account: {ping.authenticatedAccountId}");
            Console.WriteLine($"       UserId: {ping.authenticatedUserId}");
            Console.WriteLine($"  CompanyCode: {g_Options.CompanyCode}");
            Console.WriteLine($"          SDK: {AvaTaxClient.API_VERSION}");
            Console.WriteLine($"  Environment: {g_Options.Environment}");
            Console.WriteLine($"    Tax Lines: {g_Options.Lines}");
            Console.WriteLine($"         Type: {g_Options.DocType}");
            Console.WriteLine($"      Threads: {g_Options.Threads}");
            Console.WriteLine();
            Console.WriteLine("  Call   Server   DB       Svc      Net      Client    Total");

            // Use transaction builder
            var tb = new TransactionBuilder(g_Client, g_Options.CompanyCode, g_Options.DocType, "ABC");

            // Add lines
            for (int i = 0; i < g_Options.Lines; i++)
            {
                tb.WithLine(100.0m)
                .WithLineAddress(TransactionAddressType.PointOfOrderAcceptance, "123 Main Street", null, null, "Irvine", "CA", "92615", "US")
                .WithLineAddress(TransactionAddressType.PointOfOrderOrigin, "123 Main Street", null, null, "Irvine", "CA", "92615", "US")
                .WithLineAddress(TransactionAddressType.ShipFrom, "123 Main Street", null, null, "Irvine", "CA", "92615", "US")
                .WithLineAddress(TransactionAddressType.ShipTo, "123 Main Street", null, null, "Irvine", "CA", "92615", "US");
            }
            g_Model = tb.GetCreateTransactionModel();

            // Discard the first call?
            try {
                if (g_Options.DiscardFirstCall.HasValue && g_Options.DiscardFirstCall.Value)
                {
                    var t = g_Client.CreateTransaction(null, g_Model);
                }
            } catch (Exception ex) {
                Console.WriteLine("Cannot connect to AvaTax.");
                HandleException(ex);
                return;
            }

            // Connect to AvaTax and print debug information
            g_TotalDuration = new CallDuration();
            g_TotalMs       = 0;
            List <Task> threads = new List <Task>();

            for (int i = 0; i < g_Options.Threads; i++)
            {
                var task = Task.Run(ConnectThread);
                threads.Add(task);
            }
            await Task.WhenAll(threads);

            // Compute some averages
            double avg            = g_TotalMs * 1.0 / g_Count;
            double total_overhead = (g_TotalDuration.SetupDuration.TotalMilliseconds + g_TotalDuration.ParseDuration.TotalMilliseconds);
            double total_transit  = g_TotalDuration.TransitDuration.TotalMilliseconds;
            double total_server   = g_TotalDuration.ServerDuration.TotalMilliseconds;
            double avg_overhead   = total_overhead / g_Count;
            double avg_transit    = total_transit / g_Count;
            double avg_server     = total_server / g_Count;
            double pct_overhead   = total_overhead / g_TotalMs;
            double pct_transit    = total_transit / g_TotalMs;
            double pct_server     = total_server / g_TotalMs;

            // Print out the totals
            Console.WriteLine();
            Console.WriteLine($"Finished {g_Count} calls in {g_TotalMs} milliseconds.");
            Console.WriteLine($"    Average: {avg.ToString("0.00")}ms; {avg_overhead.ToString("0.00")}ms overhead, {avg_transit.ToString("0.00")}ms transit, {avg_server.ToString("0.00")}ms server.");
            Console.WriteLine($"    Percentage: {pct_overhead.ToString("P")} overhead, {pct_transit.ToString("P")} transit, {pct_server.ToString("P")} server.");
            Console.WriteLine($"    Total: {total_overhead} overhead, {total_transit} transit, {total_server} server.");
        }
예제 #49
0
		public void CanBuildIssueColoredCoinWithMultiSigP2SH()
		{
			var satoshi = new Key();
			var bob = new Key();
			var alice = new Key();

			var goldRedeem = PayToMultiSigTemplate.Instance
									.GenerateScriptPubKey(2, new[] { satoshi.PubKey, bob.PubKey, alice.PubKey });

			var goldScriptPubKey = goldRedeem.Hash.ScriptPubKey;
			var goldAssetId = goldScriptPubKey.Hash.ToAssetId();

			var issuanceCoin = new IssuanceCoin(
				new ScriptCoin(RandOutpoint(), new TxOut(new Money(2880), goldScriptPubKey), goldRedeem));

			var nico = new Key();

			var bobSigned =
				new TransactionBuilder()
				.AddCoins(issuanceCoin)
				.AddKeys(bob)
				.IssueAsset(nico.PubKey, new AssetMoney(goldAssetId, 1000))
				.BuildTransaction(true);

			var aliceSigned =
				new TransactionBuilder()
					.AddCoins(issuanceCoin)
					.AddKeys(alice)
					.SignTransaction(bobSigned);

			Assert.True(
				new TransactionBuilder()
				{
					StandardTransactionPolicy = EasyPolicy
				}
					.AddCoins(issuanceCoin)
					.Verify(aliceSigned));

			//In one two one line

			var builder = new TransactionBuilder();
			builder.StandardTransactionPolicy = RelayPolicy.Clone();
			builder.StandardTransactionPolicy.CheckFee = false;
			var tx =
				builder
				.AddCoins(issuanceCoin)
				.AddKeys(alice, satoshi)
				.IssueAsset(nico.PubKey, new AssetMoney(goldAssetId, 1000))
				.BuildTransaction(true);
			Assert.True(builder.Verify(tx));
		}
예제 #50
0
        static void Main()
        {
            /* Create a fake transaction */
            var bob     = new Key();
            var alice   = new Key();
            var satoshi = new Key();

            Script bobAlice =
                PayToMultiSigTemplate.Instance.GenerateScriptPubKey(
                    2,
                    bob.PubKey, alice.PubKey);

            var init = new Transaction();

            init.Outputs.Add(new TxOut(Money.Coins(1m), bob.PubKey));        // P2PK
            init.Outputs.Add(new TxOut(Money.Coins(1m), alice.PubKey.Hash)); // P2PKH
            init.Outputs.Add(new TxOut(Money.Coins(1m), bobAlice));

            /* Get the coins of the initial transaction */
            Coin[] coins = init.Outputs.AsCoins().ToArray();

            Coin bobCoin      = coins[0];
            Coin aliceCoin    = coins[1];
            Coin bobAliceCoin = coins[2];

            /* Build the transaction */
            var         builder = new TransactionBuilder();
            Transaction tx      = builder
                                  .AddCoins(bobCoin)
                                  .AddKeys(bob)
                                  .Send(satoshi, Money.Coins(0.2m))
                                  .SetChange(bob)
                                  .Then()
                                  .AddCoins(aliceCoin)
                                  .AddKeys(alice)
                                  .Send(satoshi, Money.Coins(0.3m))
                                  .SetChange(alice)
                                  .Then()
                                  .AddCoins(bobAliceCoin)
                                  .AddKeys(bob, alice)
                                  .Send(satoshi, Money.Coins(0.5m))
                                  .SetChange(bobAlice)
                                  .SendFees(Money.Coins(0.0001m))
                                  .BuildTransaction(sign: true);


            /* Verify you did not screw up */
            Console.WriteLine(builder.Verify(tx)); // True



            /* ScriptCoin */
            init = new Transaction();
            init.Outputs.Add(new TxOut(Money.Coins(1.0m), bobAlice.Hash));

            coins = init.Outputs.AsCoins().ToArray();
            ScriptCoin bobAliceScriptCoin = coins[0].ToScriptCoin(bobAlice);

            builder = new TransactionBuilder();
            tx      = builder
                      .AddCoins(bobAliceScriptCoin)
                      .AddKeys(bob, alice)
                      .Send(satoshi, Money.Coins(0.9m))
                      .SetChange(bobAlice.Hash)
                      .SendFees(Money.Coins(0.0001m))
                      .BuildTransaction(true);
            Console.WriteLine(builder.Verify(tx)); // True



            /* STEALTH COIN */

            Key scanKey = new Key();
            BitcoinStealthAddress darkAliceBob =
                new BitcoinStealthAddress
                (
                    scanKey: scanKey.PubKey,
                    pubKeys: new[] { alice.PubKey, bob.PubKey },
                    signatureCount: 2,
                    bitfield: null,
                    network: Network.Main
                );

            //Someone sent to darkAliceBob
            init = new Transaction();
            darkAliceBob
            .SendTo(init, Money.Coins(1.0m));

            //Get the stealth coin with the scanKey
            StealthCoin stealthCoin
                = StealthCoin.Find(init, darkAliceBob, scanKey);

            //Spend it
            tx = builder
                 .AddCoins(stealthCoin)
                 .AddKeys(bob, alice, scanKey)
                 .Send(satoshi, Money.Coins(0.9m))
                 .SetChange(bobAlice.Hash)
                 .SendFees(Money.Coins(0.0001m))
                 .BuildTransaction(true);
            Console.WriteLine(builder.Verify(tx)); // True

            Console.ReadLine();
        }
예제 #51
0
		public void CanBuildShuffleColoredTransaction()
		{
			var gold = new Key();
			var silver = new Key();
			var goldId = gold.PubKey.ScriptPubKey.Hash.ToAssetId();
			var silverId = silver.PubKey.ScriptPubKey.Hash.ToAssetId();

			var satoshi = new Key();
			var bob = new Key();

			var repo = new NoSqlColoredTransactionRepository(new NoSqlTransactionRepository(), new InMemoryNoSqlRepository());

			var init = new Transaction()
			{
				Outputs =
				{
					new TxOut("1.0", gold.PubKey),
					new TxOut("1.0", silver.PubKey),
					new TxOut("1.0", satoshi.PubKey)
				}
			};
			repo.Transactions.Put(init.GetHash(), init);

			var issuanceCoins =
				init
				.Outputs
				.Take(2)
				.Select((o, i) => new IssuanceCoin(new OutPoint(init.GetHash(), i), init.Outputs[i]))
				.OfType<ICoin>().ToArray();

			var satoshiBTC = new Coin(new OutPoint(init.GetHash(), 2), init.Outputs[2]);

			var coins = new List<ICoin>();
			coins.AddRange(issuanceCoins);
			var txBuilder = new TransactionBuilder(1);
			txBuilder.StandardTransactionPolicy = RelayPolicy;
			//Can issue gold to satoshi and bob
			var tx = txBuilder
				.AddCoins(coins.ToArray())
				.AddKeys(gold)
				.IssueAsset(satoshi.PubKey, new AssetMoney(goldId, 1000))
				.IssueAsset(bob.PubKey, new AssetMoney(goldId, 500))
				.SendFees("0.1")
				.SetChange(gold.PubKey)
				.BuildTransaction(true);
			Assert.True(txBuilder.Verify(tx, "0.1"));

			//Ensure BTC from the IssuanceCoin are returned
			Assert.Equal(Money.Parse("0.89994240"), tx.Outputs[2].Value);
			Assert.Equal(gold.PubKey.ScriptPubKey, tx.Outputs[2].ScriptPubKey);

			//Can issue and send in same transaction
			repo.Transactions.Put(tx.GetHash(), tx);


			var cc = ColoredCoin.Find(tx, repo);
			for(int i = 0 ; i < 20 ; i++)
			{
				txBuilder = new TransactionBuilder(i);
				txBuilder.StandardTransactionPolicy = RelayPolicy;
				tx = txBuilder
					.AddCoins(satoshiBTC)
					.AddCoins(cc)
					.AddKeys(satoshi)
					.SendAsset(gold, new AssetMoney(goldId, 10))
					.SetChange(satoshi)
					.Then()
					.AddKeys(gold)
					.AddCoins(issuanceCoins)
					.IssueAsset(bob, new AssetMoney(goldId, 1))
					.SetChange(gold)
					.Shuffle()
					.BuildTransaction(true);

				repo.Transactions.Put(tx.GetHash(), tx);

				var ctx = tx.GetColoredTransaction(repo);
				Assert.Equal(1, ctx.Issuances.Count);
				Assert.Equal(2, ctx.Transfers.Count);
			}
		}
예제 #52
0
        static void Main()
        {
            var coin = new Coin(
                fromTxHash: new uint256("eb49a599c749c82d824caf9dd69c4e359261d49bbb0b9d6dc18c59bc9214e43b"),
                fromOutputIndex: 0,
                amount: Money.Satoshis(2000000),
                scriptPubKey: new Script(Encoders.Hex.DecodeData("76a914c81e8e7b7ffca043b088a992795b15887c96159288ac")));

            var issuance = new IssuanceCoin(coin);


            var nico = BitcoinAddress.Create("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe");
            //var bookKey = new BitcoinSecret("???????");
            var bookKey = new Key().GetBitcoinSecret(Network.Main); // Just a fake key in order to not get an exception

            var builder = new TransactionBuilder();

            Transaction tx = builder
                             .AddKeys(bookKey)
                             .AddCoins(issuance)
                             .IssueAsset(nico, new AssetMoney(issuance.AssetId, quantity: 10))
                             .SendFees(Money.Coins(0.0001m))
                             .SetChange(bookKey.GetAddress())
                             .BuildTransaction(true);

            Console.WriteLine(tx);

            Console.WriteLine(builder.Verify(tx));

            nico = BitcoinAddress.Create("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe");
            Console.WriteLine(nico.ToColoredAddress());


            /* QBITNINJA */

            //var client = new QBitNinjaClient(Network.Main);
            //BroadcastResponse broadcastResponse = client.Broadcast(tx).Result;

            //if (!broadcastResponse.Success)
            //{
            //    Console.WriteLine("ErrorCode: " + broadcastResponse.Error.ErrorCode);
            //    Console.WriteLine("Error message: " + broadcastResponse.Error.Reason);
            //}
            //else
            //{
            //    Console.WriteLine("Success!");
            //}

            /* OR BITCOIN CORE */

            //using (var node = Node.ConnectToLocal(Network.Main)) //Connect to the node
            //{
            //    node.VersionHandshake(); //Say hello
            //                             //Advertize your transaction (send just the hash)
            //    node.SendMessage(new InvPayload(InventoryType.MSG_TX, tx.GetHash()));
            //    //Send it
            //    node.SendMessage(new TxPayload(tx));
            //    Thread.Sleep(500); //Wait a bit
            //}


            coin = new Coin(
                fromTxHash: new uint256("fa6db7a2e478f3a8a0d1a77456ca5c9fa593e49fd0cf65c7e349e5a4cbe58842"),
                fromOutputIndex: 0,
                amount: Money.Satoshis(2000000),
                scriptPubKey: new Script(Encoders.Hex.DecodeData("76a914356facdac5f5bcae995d13e667bb5864fd1e7d5988ac")));
            BitcoinAssetId assetId = new BitcoinAssetId("AVAVfLSb1KZf9tJzrUVpktjxKUXGxUTD4e");
            ColoredCoin    colored = coin.ToColoredCoin(assetId, 10);

            var book       = BitcoinAddress.Create("1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB");
            var nicoSecret = new BitcoinSecret("??????????");

            nico = nicoSecret.GetAddress(); //15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe

            var forFees = new Coin(
                fromTxHash: new uint256("7f296e96ec3525511b836ace0377a9fbb723a47bdfb07c6bc3a6f2a0c23eba26"),
                fromOutputIndex: 0,
                amount: Money.Satoshis(4425000),
                scriptPubKey: new Script(Encoders.Hex.DecodeData("76a914356facdac5f5bcae995d13e667bb5864fd1e7d5988ac")));

            builder = new TransactionBuilder();
            tx      = builder
                      .AddKeys(nicoSecret)
                      .AddCoins(colored, forFees)
                      .SendAsset(book, new AssetMoney(assetId, 10))
                      .SetChange(nico)
                      .SendFees(Money.Coins(0.0001m))
                      .BuildTransaction(true);
            Console.WriteLine(tx);



            Console.ReadLine();
        }
예제 #53
0
		public void CanBuildStealthTransaction()
		{
			var stealthKeys = Enumerable.Range(0, 3).Select(_ => new Key()).ToArray();
			var scanKey = new Key();

			var darkSatoshi = new BitcoinStealthAddress(scanKey.PubKey, stealthKeys.Select(k => k.PubKey).ToArray(), 2, new BitField(3, 5), Network.Main);

			var bob = new Key();
			var coins = new Coin[] { 
				new Coin() 
				{ 
					Outpoint = RandOutpoint(),
					TxOut = new TxOut("1.00",bob.PubKey.Hash)
				} };

			//Bob sends money to satoshi
			TransactionBuilder builder = new TransactionBuilder();
			builder.StandardTransactionPolicy = EasyPolicy;
			var tx =
				builder
				.AddCoins(coins)
				.AddKeys(bob)
				.Send(darkSatoshi, "1.00")
				.BuildTransaction(true);
			Assert.True(builder.Verify(tx));

			//Satoshi scans a StealthCoin in the transaction with his scan key
			var stealthCoin = StealthCoin.Find(tx, darkSatoshi, scanKey);
			Assert.NotNull(stealthCoin);

			//Satoshi sends back the money to Bob
			builder = new TransactionBuilder();
			builder.StandardTransactionPolicy = EasyPolicy;
			tx =
				builder
					.AddCoins(stealthCoin)
					.AddKeys(stealthKeys)
					.AddKeys(scanKey)
					.Send(bob.PubKey.Hash, "1.00")
					.BuildTransaction(true);

			Assert.True(builder.Verify(tx)); //Signed !


			//Same scenario, Satoshi wants to send money back to Bob
			//However, his keys are spread on two machines
			//He partially signs on the 1st machine
			builder = new TransactionBuilder();
			builder.StandardTransactionPolicy = EasyPolicy;
			tx =
				builder
					.AddCoins(stealthCoin)
					.AddKeys(stealthKeys.Skip(2).ToArray()) //Only one Stealth Key
					.AddKeys(scanKey)
					.Send(bob.PubKey.Hash, "1.00")
					.BuildTransaction(true);

			Assert.False(builder.Verify(tx)); //Not fully signed

			//Then he partially signs on the 2nd machine
			builder = new TransactionBuilder();
			builder.StandardTransactionPolicy = EasyPolicy;
			tx =
				builder
					.AddCoins(stealthCoin)
					.AddKeys(stealthKeys[0]) //Other key
					.AddKeys(scanKey)
					.SignTransaction(tx);

			Assert.True(builder.Verify(tx)); //Fully signed !
		}
예제 #54
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);
        }
예제 #55
0
		public void CanEstimateFees()
		{
			var alice = new Key();
			var bob = new Key();
			var satoshi = new Key();
			var bobAlice = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, alice.PubKey, bob.PubKey);

			//Alice sends money to bobAlice
			//Bob sends money to bobAlice
			//bobAlice sends money to satoshi

			var aliceCoins = new ICoin[] { RandomCoin("0.4", alice), RandomCoin("0.6", alice) };
			var bobCoins = new ICoin[] { RandomCoin("0.2", bob), RandomCoin("0.3", bob) };
			var bobAliceCoins = new ICoin[] { RandomCoin("1.5", bobAlice, false), RandomCoin("0.25", bobAlice, true) };

			TransactionBuilder builder = new TransactionBuilder();
			builder.StandardTransactionPolicy = EasyPolicy;
			var unsigned = builder
				.AddCoins(aliceCoins)
				.Send(bobAlice, "1.0")
				.Then()
				.AddCoins(bobCoins)
				.Send(bobAlice, "0.5")
				.Then()
				.AddCoins(bobAliceCoins)
				.Send(satoshi.PubKey, "1.74")
				.SetChange(bobAlice)
				.BuildTransaction(false);

			builder.AddKeys(alice, bob, satoshi);
			var signed = builder.BuildTransaction(true);
			Assert.True(builder.Verify(signed));

			Assert.True(Math.Abs(signed.ToBytes().Length - builder.EstimateSize(unsigned)) < 20);

			var rate = new FeeRate(Money.Coins(0.0004m));
			var estimatedFees = builder.EstimateFees(unsigned, rate);
			builder.SendEstimatedFees(rate);
			signed = builder.BuildTransaction(true);
			Assert.True(builder.Verify(signed, estimatedFees));
		}
        private static void UpdateInternal <T>(
            IDbConnection connection,
            IEnumerable <Tuple <T, T> > oldAndNewObjects,
            bool softDelete            = false,
            IDbTransaction transaction = null)
        {
            var builder = new TransactionBuilder(MetadataCache);
            IDictionary <Tuple <T, T>, IList <IScript> > scripts = new Dictionary <Tuple <T, T>, IList <IScript> >();

            foreach (var pair in oldAndNewObjects)
            {
                scripts[pair] = builder.BuildUpdateScripts(pair.Item1, pair.Item2, softDelete);
            }

            Transaction ambient = null;

            if (transaction == null)
            {
                ambient = Transaction.Current;
            }

            if (transaction == null && ambient == null)
            {
                using (var myTransaction = connection.BeginTransaction())
                {
                    try
                    {
                        ExecuteScriptsForTuples(
                            connection,
                            oldAndNewObjects,
                            scripts,
                            softDelete,
                            myTransaction);

                        myTransaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        if (Logger.Wrapped.IsErrorEnabled)
                        {
                            Logger.Wrapped.Error(new
                            {
                                message   = "Error executing internal transaction",
                                exception = ex,
                                scripts
                            });
                        }
                        myTransaction.Rollback();
                        throw;
                    }
                }
            }
            else
            {
                try
                {
                    ExecuteScriptsForTuples(
                        connection,
                        oldAndNewObjects,
                        scripts,
                        softDelete,
                        transaction);
                }
                catch (Exception ex)
                {
                    if (Logger.Wrapped.IsErrorEnabled)
                    {
                        Logger.Wrapped.Error(new
                        {
                            message   = "Error executing external transaction",
                            exception = ex,
                            scripts
                        });
                    }
                    throw;
                }
            }
        }
예제 #57
0
파일: spv_tests.cs 프로젝트: vebin/NBitcoin
		public void CanSyncWallet2()
		{
			using(NodeServerTester servers = new NodeServerTester(Network.TestNet))
			{
				var chainBuilder = new BlockchainBuilder();
				SetupSPVBehavior(servers, chainBuilder);
				NodesGroup aliceConnection = CreateGroup(servers, 1);
				NodesGroup bobConnection = CreateGroup(servers, 1);

				var aliceKey = new ExtKey();
				Wallet alice = new Wallet(new WalletCreation()
				{
					Network = Network.TestNet,
					RootKeys = new[] { aliceKey.Neuter() },
					SignatureRequired = 1,
					UseP2SH = false
				}, 11);
				Wallet bob = new Wallet(new WalletCreation()
				{
					Network = Network.TestNet,
					RootKeys = new[] { new ExtKey().Neuter() },
					SignatureRequired = 1,
					UseP2SH = false
				}, 11);

				alice.Configure(aliceConnection);
				alice.Connect();

				bob.Configure(bobConnection);
				bob.Connect();

				TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 1);

				//New address tracked
				var addressAlice = alice.GetNextScriptPubKey();
				chainBuilder.GiveMoney(addressAlice, Money.Coins(1.0m));
				TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 0); //Purge
				TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 1); //Reconnect
				//////

				TestUtils.Eventually(() => alice.GetTransactions().Count == 1);

				//Alice send tx to bob
				var coins = alice.GetTransactions().GetSpendableCoins();
				var keys = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey))
								.Select(k => aliceKey.Derive(k))
								.ToArray();
				var builder = new TransactionBuilder();
				var tx =
					builder
					.SetTransactionPolicy(new Policy.StandardTransactionPolicy()
					{
						MinRelayTxFee = new FeeRate(0)
					})
					.AddCoins(coins)
					.AddKeys(keys)
					.Send(bob.GetNextScriptPubKey(), Money.Coins(0.4m))
					.SetChange(alice.GetNextScriptPubKey(true))
					.BuildTransaction(true);

				Assert.True(builder.Verify(tx));

				chainBuilder.BroadcastTransaction(tx);

				//Alice get change
				TestUtils.Eventually(() => alice.GetTransactions().Count == 2);
				coins = alice.GetTransactions().GetSpendableCoins();
				Assert.True(coins.Single().Amount == Money.Coins(0.6m));
				//////

				//Bob get coins
				TestUtils.Eventually(() => bob.GetTransactions().Count == 1);
				coins = bob.GetTransactions().GetSpendableCoins();
				Assert.True(coins.Single().Amount == Money.Coins(0.4m));
				//////


				MemoryStream bobWalletBackup = new MemoryStream();
				bob.Save(bobWalletBackup);
				bobWalletBackup.Position = 0;

				MemoryStream bobTrakerBackup = new MemoryStream();
				bob.Tracker.Save(bobTrakerBackup);
				bobTrakerBackup.Position = 0;

				bob.Disconnect();

				//Restore bob
				bob = Wallet.Load(bobWalletBackup);
				bobConnection.NodeConnectionParameters.TemplateBehaviors.Remove<TrackerBehavior>();
				bobConnection.NodeConnectionParameters.TemplateBehaviors.Add(new TrackerBehavior(Tracker.Load(bobTrakerBackup), chainBuilder.Chain));
				/////

				bob.Configure(bobConnection);

				//Bob still has coins
				TestUtils.Eventually(() => bob.GetTransactions().Count == 1);
				coins = bob.GetTransactions().GetSpendableCoins();
				Assert.True(coins.Single().Amount == Money.Coins(0.4m));
				//////

				bob.Connect();
				TestUtils.Eventually(() => bobConnection.ConnectedNodes.Count == 1);

				//New block found !
				chainBuilder.FindBlock();

				//Alice send tx to bob
				coins = alice.GetTransactions().GetSpendableCoins();
				keys = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey))
								.Select(k => aliceKey.Derive(k))
								.ToArray();
				builder = new TransactionBuilder();
				tx =
					builder
					.SetTransactionPolicy(new Policy.StandardTransactionPolicy()
					{
						MinRelayTxFee = new FeeRate(0)
					})
					.AddCoins(coins)
					.AddKeys(keys)
					.Send(bob.GetNextScriptPubKey(), Money.Coins(0.1m))
					.SetChange(alice.GetNextScriptPubKey(true))
					.BuildTransaction(true);

				Assert.True(builder.Verify(tx));

				chainBuilder.BroadcastTransaction(tx);

				//Bob still has coins
				TestUtils.Eventually(() => bob.GetTransactions().Count == 2); //Bob has both, old and new tx
				coins = bob.GetTransactions().GetSpendableCoins();
				//////
			}
		}
예제 #58
0
        private static (int expectedBaseSize, int expectedWitsize, int baseSize, int witSize) VerifyFees(TransactionBuilder builder, FeeRate feeRate = null)
        {
            feeRate = feeRate ?? builder.StandardTransactionPolicy.MinRelayTxFee;
            var result = builder.BuildTransaction(true);

            builder.EstimateSizes(result, out int witSize, out int baseSize);
            var expectedWitsize  = result.ToBytes().Length - result.WithOptions(TransactionOptions.None).ToBytes().Length;
            var expectedBaseSize = result.WithOptions(TransactionOptions.None).ToBytes().Length;

            Assert.True(expectedBaseSize <= baseSize);
            Assert.True(expectedWitsize <= witSize);
            Assert.True(feeRate.FeePerK.Almost(result.GetFeeRate(builder.FindSpentCoins(result)).FeePerK, 0.01m));
            Assert.True(feeRate.FeePerK <= result.GetFeeRate(builder.FindSpentCoins(result)).FeePerK);

            return(expectedBaseSize, expectedWitsize, baseSize, witSize);
        }