コード例 #1
0
        /// <summary>
        /// Issues a new colored coin.
        /// </summary>
        /// <param name="transactionId"></param>
        /// <param name="scriptPubKey"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static IssuanceCoin IssueCoin(uint256 transactionId, Script scriptPubKey, string url)
        {
            uint256 fromTransactionHash = transactionId;
            uint    fromOutputIndex     = 0;
            Money   amount = Money.Satoshis(6000);//an abitrary sum that is greater than the minimum 5460 satoshis accepted by Bitcoin miners.
            //Script scriptPubKey = new Script(Encoders.Hex.DecodeData(scriptPubKeyString));

            Coin coin = new Coin(fromTransactionHash, fromOutputIndex, amount, scriptPubKey);

            IssuanceCoin issuanceCoin = new IssuanceCoin(coin);

            issuanceCoin.DefinitionUrl = new Uri(url);

            return(issuanceCoin);
        }
コード例 #2
0
        private async Task IssueCoinsAsync(Key sourceKey, BitcoinAddress destination, ulong quantity)
        {
            var sourceAddr = sourceKey.ScriptPubKey.GetDestinationAddress(this.network);
            var allCoins   = await GetUnspentCoinsAsync(sourceAddr);

            var isssuance = new IssuanceCoin(allCoins.OfType <Coin>().First());

            var tx = network.CreateTransactionBuilder()
                     .AddKeys(sourceKey)
                     .AddCoins(isssuance)
                     .IssueAsset(destination, new AssetMoney(isssuance.AssetId, quantity))
                     .SetChange(sourceAddr)
                     .SendEstimatedFees(this.feeRate)
                     .BuildTransaction(true);

            await BroadcastAsync(tx);
        }
コード例 #3
0
ファイル: Community.cs プロジェクト: PlumpMath/LushCoin
        /// <summary>
        /// Simple method to add a person to a community.
        /// A new issuance coin is used for every invite.
        /// This is so that the colored coin can be traced back to the inviters scriptPubKey.
        /// The payment of the community application coin to the registration address adds the new
        /// community to the community registration address list. This enables the referencing of a
        /// community through its public key.
        /// </summary>
        /// <param name="rpcClient"></param>
        /// <param name="community"></param>
        /// <param name="member"></param>
        /// <param name="invitee"></param>
        /// <param name="definitionUrl"></param>
        /// <returns></returns>
        public static bool Invite(RPCClient rpcClient, Community community, CommunityMember member, CommunityMember invitee, string definitionUrl, Network network)
        {
            decimal amount        = 0.0001m;
            Script  scriptPubKey  = new Script();
            uint256 transactionId = new uint256();
            bool    canProceed    = false;

            //check is member belongs in community to which they are inviting invitee
            if (IsCommunityMember(community, member))
            {
                //check that the inviter has at least one transaction balance that is big enough for the current transaction.
                UnspentCoin[] transactions = rpcClient.ListUnspent();
                for (int i = 0; i < transactions.Length; i++)
                {
                    if (transactions[i].Amount.ToUnit(MoneyUnit.Satoshi) >= amount)
                    {
                        scriptPubKey  = transactions[i].ScriptPubKey;
                        transactionId = transactions[i].OutPoint.Hash;
                        canProceed    = true;
                        break;
                    }
                }

                if (!canProceed)
                {
                    return(false);
                }

                IssuanceCoin issuanceCoin = MarketService.IssueCoin(transactionId, scriptPubKey, definitionUrl);

                //build and verify the transaction
                BitcoinColoredAddress inviteePubKey = BitcoinAddress.Create(invitee.Key).ToColoredAddress();
                BitcoinSecret         bitcoinSecret = new BitcoinSecret(community.Key);

                Transaction transaction = MarketService.BuildCommunityTransaction(issuanceCoin, inviteePubKey, bitcoinSecret, amount, 1);

                if (transaction != null)
                {
                    MarketService.Broadcast(transaction, network);
                    return(true);
                }
            }

            return(false);
        }
コード例 #4
0
        public Task <CreateTransactionResponse> GetIssueTransaction(BitcoinAddress bitcoinAddres, decimal amount, IAsset asset, Guid transactionId)
        {
            return(Retry.Try(async() =>
            {
                var context = _transactionBuildContextFactory.Create(_connectionParams.Network);

                return await context.Build(async() =>
                {
                    var builder = new TransactionBuilder();
                    var queue = _pregeneratedOutputsQueueFactory.Create(asset.BlockChainAssetId);

                    var coin = await queue.DequeueCoin();

                    try
                    {
                        var issueCoin = new IssuanceCoin(coin)
                        {
                            DefinitionUrl = new Uri(asset.DefinitionUrl)
                        };

                        var assetId = new BitcoinAssetId(asset.BlockChainAssetId, _connectionParams.Network).AssetId;

                        builder.AddCoins(issueCoin)
                        .IssueAsset(bitcoinAddres, new AssetMoney(assetId, amount, asset.MultiplierPower));
                        context.IssueAsset(assetId);

                        await _transactionBuildHelper.AddFee(builder, context);

                        var buildedTransaction = builder.BuildTransaction(true);

                        await _spentOutputService.SaveSpentOutputs(transactionId, buildedTransaction);

                        await SaveNewOutputs(transactionId, buildedTransaction, context);

                        return new CreateTransactionResponse(buildedTransaction.ToHex(), transactionId);
                    }
                    catch (Exception)
                    {
                        await queue.EnqueueOutputs(coin);
                        throw;
                    }
                });
            }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log));
        }
コード例 #5
0
        /// <summary>
        /// Builds a colored coin transaction.
        /// </summary>
        /// <param name="issuanceCoin"></param>
        /// <param name="destinitionPubKey"></param>
        /// <param name="bitcoinSecret"></param>
        /// <param name="amount"></param>
        /// <param name="quantity"></param>
        /// <returns></returns>
        public static Transaction BuildCommunityTransaction
            (IssuanceCoin issuanceCoin, BitcoinColoredAddress destinitionPubKey, BitcoinSecret bitcoinSecret, decimal amount, int quantity)
        {
            TransactionBuilder transactionBuilder = new TransactionBuilder();

            Transaction transaction = transactionBuilder
                                      .AddKeys(bitcoinSecret)
                                      .AddCoins(issuanceCoin)
                                      .IssueAsset(destinitionPubKey, new AssetMoney(issuanceCoin.AssetId, quantity: quantity))
                                      .SendFees(Money.Coins(amount))
                                      .SetChange(bitcoinSecret.GetAddress())
                                      .BuildTransaction(true);

            if (transactionBuilder.Verify(transaction))
            {
                return(transaction);
            }

            return(null);
        }
コード例 #6
0
        static void Main(string[] args)
        {
            var coin = new Coin(
                fromTxHash: new uint256("e2b308ea38112ae79ff2cc67d686c3dcd737214290802c1905b76e76c89783d4"),
                fromOutputIndex: 0,
                amount: Money.Satoshis(300000000),
                scriptPubKey: new Script(Encoders.Hex.DecodeData("76a9141c770f57182260756319ca5e0af31e19b37288b288ac"))
                );

            var issuance = new IssuanceCoin(coin);

            var nico    = BitcoinAddress.Create("mwU3UJ1VXX3GxKKQHdscw1TvWSrMKdtymR");
            var bookKey = new BitcoinSecret("cRETrCgfbU273XmpFDQr4GhsqU4cXB7ECQuoGtCrRmgy1U2jLu1f");
            TransactionBuilder builder = new TransactionBuilder();

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

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

            var client = new QBitNinjaClient(Network.TestNet);
            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!");
            }
        }
コード例 #7
0
        // Colored Coinの発行
        public static void createIssuanceCoin(String TxId)
        {
            // UTXOから、ColoredCoin発行に消費するcoinを作成
            // ここで設定したScriptPubKeyが、ColoredCoinの所有者となる
            BitcoinAddress myAddress = BitcoinAddress.Create(mMyBitcoinAddress, Network.TestNet);
            var            coin      = new Coin(
                fromTxHash: new uint256(mUtXo),
                fromOutputIndex: mUtXoIndex,
                amount: Money.Coins(mUtXoCoin),
                scriptPubKey: myAddress.ScriptPubKey);
            var issuance = new IssuanceCoin(coin);

            // Broadcastするトランザクションの作成
            // Assetのquantitis(量)は'10'。
            TransactionBuilder builder = new TransactionBuilder();
            var mykey = new BitcoinSecret(mMyWif);
            var tx    = builder
                        .AddKeys(mykey)
                        .AddCoins(issuance)
                        .IssueAsset(myAddress, new AssetMoney(issuance.AssetId, 10))
                        .SendFees(Money.Coins(0.0004m))
                        .SetChange(mykey.GetAddress())
                        .BuildTransaction(true);

            // 作成したトランザクションのVerify
            Console.WriteLine(builder.Verify(tx));

            // トランザクションのHexデータ(Broadcast用)
            Console.WriteLine("ToHex:{0}", tx.ToHex());

            // 発行したColoredCoinの確認用アドレス、AssetID
            Console.WriteLine("ColoredAddress:{0}", myAddress.ToColoredAddress());
            var assetId = new AssetId(myAddress).GetWif(Network.TestNet);

            Console.WriteLine("AssetID:{0}", assetId);
        }
コード例 #8
0
        public static void Execute()
        {
            // use the following coin for issuing assets.

            /*
             * {
             *  "transactionId": "a416cc07134b6049aebfc36712fe3385b57325ae8c0dc1218adc2bda839ae319",
             *  "index": 0,
             *  "value": 2000000,
             *  "scriptPubKey": "OP_DUP OP_HASH160 761165aeb773479007b4bad25dc594980b0deb68 OP_EQUALVERIFY OP_CHECKSIG",
             *  "redeemScript": null
             * }
             */
            Keys    keys         = JsonConvert.DeserializeObject <Keys>(File.ReadAllText(@"Keys.json"));
            Network btcTestNet   = Network.TestNet;
            string  txnId        = "a416cc07134b6049aebfc36712fe3385b57325ae8c0dc1218adc2bda839ae319";
            string  scriptPubKey = "OP_DUP OP_HASH160 761165aeb773479007b4bad25dc594980b0deb68 OP_EQUALVERIFY OP_CHECKSIG";

            string         aliceAddressHex  = keys.alice.Address;
            string         bobPrivateKeyHex = keys.bob.PrivateKey;
            BitcoinAddress aliceAddress     = BitcoinAddress.Create(aliceAddressHex, btcTestNet);
            BitcoinSecret  bobPrivateKey    = new BitcoinSecret(bobPrivateKeyHex);

            Coin coin = new Coin(fromTxHash: new uint256(txnId),
                                 fromOutputIndex: 0,
                                 amount: Money.Satoshis(490000),
                                 scriptPubKey: new Script(scriptPubKey));
            IssuanceCoin issuance = new IssuanceCoin(coin);

            // build transaction and sign the transaction using TransactionBuilder
            TransactionBuilder builder = new TransactionBuilder();
            Transaction        tx      = builder.AddKeys(bobPrivateKey)
                                         .AddCoins(issuance)
                                         .IssueAsset(aliceAddress, new AssetMoney(issuance.AssetId, quantity: 10))
                                         .SendFees(Money.Coins(0.0001m))
                                         .SetChange(bobPrivateKey.GetAddress())
                                         .BuildTransaction(sign: true);

            // After transaction verifications it is ready to be sent to the network.
            Console.WriteLine(tx);
            Console.WriteLine("Verify Txn: " + builder.Verify(tx));

            /*
             * // With QBitNinja
             * var client = new QBitNinjaClient(btcNetwork);
             * 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("Txn Broadcast Success!");
             * }
             *
             * // Or with local 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
             * }
             */

            // preventing a user from sending Colored Coin to a wallet that do not support it,
            // Open Asset have its own address format, that only colored coin wallets understand
            Console.WriteLine("receiverAddress: " + aliceAddress);
            Console.WriteLine("ColoredCoinAddress: " + aliceAddress.ToColoredAddress());

            // Asset ID is derived from the issuer’s ScriptPubKey, here is how to get it in code
            var assetId = (new AssetId(aliceAddress)).GetWif(btcTestNet);

            Console.WriteLine("assetId: " + assetId); // oNRXXFo48zQ5AMtTMuW5Ss1NtMoSe39Cek
        }
コード例 #9
0
ファイル: transaction_tests.cs プロジェクト: knocte/NBitcoin
		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));
		}
コード例 #10
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();
        }
コード例 #11
0
        private async Task GenerateIssueAllowedCoins()
        {
            foreach (var asset in await _assetRepostory.Values())
            {
                if (OpenAssetsHelper.IsBitcoin(asset.Id) || OpenAssetsHelper.IsLkk(asset.Id) || !asset.IssueAllowed)
                {
                    continue;
                }

                try
                {
                    var setting = await GetAssetSetting(asset.Id);

                    if (setting.HotWallet != setting.ChangeWallet)
                    {
                        continue;
                    }

                    var hotWallet = OpenAssetsHelper.ParseAddress(setting.HotWallet);
                    var assetId   = new BitcoinAssetId(asset.BlockChainAssetId).AssetId;

                    var coins = await _bitcoinOutputsService.GetColoredUnspentOutputs(setting.HotWallet, assetId);

                    var outputSize = new AssetMoney(assetId, setting.OutputSize, asset.MultiplierPower);

                    await _logger.WriteInfoAsync("GenerateOffchainOutputsFunction", "GenerateIssueAllowedCoins", "AssetId " + asset.Id, "Start process");

                    var existingCoinsCount = coins.Count(o => o.Amount <= outputSize && o.Amount * 2 > outputSize);

                    if (existingCoinsCount > setting.MinOutputsCount)
                    {
                        continue;
                    }

                    var generateCnt = setting.MaxOutputsCount - existingCoinsCount;
                    var generated   = 0;
                    while (generated < generateCnt)
                    {
                        var outputsCount = Math.Min(setting.MaxOutputsCountInTx, generateCnt - generated);

                        var context = _transactionBuildContextFactory.Create(_connectionParams.Network);

                        await context.Build(async() =>
                        {
                            var builder = new TransactionBuilder();
                            var queue   = _pregeneratedOutputsQueueFactory.Create(asset.BlockChainAssetId);
                            var coin    = await queue.DequeueCoin();

                            try
                            {
                                var issueCoin = new IssuanceCoin(coin)
                                {
                                    DefinitionUrl = new Uri(asset.DefinitionUrl)
                                };

                                builder.AddCoins(issueCoin);

                                for (var i = 0; i < outputsCount; i++)
                                {
                                    builder.IssueAsset(hotWallet, outputSize);
                                }
                                context.IssueAsset(assetId);
                                await _transactionBuildHelper.AddFee(builder, context);

                                var tr = builder.BuildTransaction(true);

                                await SignAndBroadcastTransaction(tr, context);

                                return("");
                            }
                            catch (Exception)
                            {
                                await queue.EnqueueOutputs(coin);

                                throw;
                            }
                        });

                        generated += outputsCount;
                    }
                }
                catch (Exception ex)
                {
                    await _logger.WriteWarningAsync("GenerateOffchainOutputsFunction", "GenerateIssueAllowedCoins", "AssetId " + asset.Id, ex);
                }
                finally
                {
                    await _logger.WriteInfoAsync("GenerateOffchainOutputsFunction", "GenerateIssueAllowedCoins", "AssetId " + asset.Id, "End process");
                }
            }
        }
コード例 #12
0
        static void Main(string[] args)
        {
            // 実際はAPIでJSONを受け取る
            string jsonString = @"{""commandType"":""issuance"",""fromTxHash"":""ce56e1d60efe0f5a3d93b837ce208f559214a5ec10cb9715ac0357475ae72576"",""fromOutputIndex"":""1"",""amount"":""100000000"",""scriptPubkey"":""76a9146255517104577282389fdce86d4e9e67f796759e88ac"",""bitcoinAddress"":""mpUtirtqBzQXuH9MRw3u1YgMFhBqRhknqu"",""bitcoinSecret"":""cTxQtwjYch3uscAPDUyUd2ZkcuMDTY3dp7X2HEvXoVzsFZiLFKYX"",""quantity"":""100000000""}";

            // string jsonString = @"{""commandType"":""send"",""fromTxHash"":""5dd0250910238a134c19a6ae5867cb239754b4d69d1a0f5589b29afba55b8315"",""fromOutputIndex"":""0"",""amount"":""2730"",""scriptPubkey"":""76a9146255517104577282389fdce86d4e9e67f796759e88ac"",""issuranceAddress"":""mpUtirtqBzQXuH9MRw3u1YgMFhBqRhknqu"",""balanceQuantity"":""100000000"",""bitcoinAddress"":""myMmSWRcRvrKPiQioF6QLfhkNkn1Krsz4J"",""bitcoinSecret"":""cTxQtwjYch3uscAPDUyUd2ZkcuMDTY3dp7X2HEvXoVzsFZiLFKYX"",""feeFromTxHash"":""5dd0250910238a134c19a6ae5867cb239754b4d69d1a0f5589b29afba55b8315"",""feeFromOutputIndex"":""1"",""feeAmount"":""99987270"",""feeScriptPubkey"":""76a9146255517104577282389fdce86d4e9e67f796759e88ac"",""quantity"":""1""}";
            // string jsonString = @"{""commandType"":""other""}";

            JsonHandler.PersonData pd = (JsonHandler.PersonData)JsonHandler.getObjectFromJson(
                jsonString,
                typeof(JsonHandler.PersonData)
                );

            Console.WriteLine(jsonString);

            if (pd.commandType.Equals("issuance"))
            {
                var coin = new Coin(
                    fromTxHash: new uint256(pd.fromTxHash),
                    fromOutputIndex: pd.fromOutputIndex,
                    amount: Money.Satoshis(pd.amount),
                    scriptPubKey: new Script(Encoders.Hex.DecodeData(pd.scriptPubkey)));

                var issuance = new IssuanceCoin(coin);

                var receiveAddress         = BitcoinAddress.Create(pd.bitcoinAddress);
                var bookKey                = new BitcoinSecret(pd.bitcoinSecret);
                TransactionBuilder builder = new TransactionBuilder();

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

                System.Diagnostics.Debug.WriteLine(tx);
                Console.WriteLine(tx);

                System.Diagnostics.Debug.WriteLine(builder.Verify(tx));
                Console.WriteLine(builder.Verify(tx));

                System.Diagnostics.Debug.WriteLine(issuance.AssetId);
                Console.WriteLine(issuance.AssetId);

                var client = new QBitNinjaClient(Network.TestNet);
                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!");
                }

                /* 連結ではbitcoindでのブロードキャストを行う
                 * using (var node = Node.ConnectToLocal(Network.TestNet)) //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
                 * }
                 */
            }
            else if (pd.commandType.Equals("send"))
            {
                var booka = BitcoinAddress.Create(pd.issuranceAddress);
                // System.Diagnostics.Debug.WriteLine(booka.ToColoredAddress());
                // Console.WriteLine(booka.ToColoredAddress());
                var assetId = new AssetId(booka).GetWif(Network.TestNet);
                System.Diagnostics.Debug.WriteLine(assetId);
                Console.WriteLine(assetId);

                var coin = new Coin(
                    fromTxHash: new uint256(pd.fromTxHash),
                    fromOutputIndex: pd.fromOutputIndex,
                    amount: Money.Satoshis(2730),
                    scriptPubKey: new Script(Encoders.Hex.DecodeData(pd.scriptPubkey)));
                // BitcoinAssetId assetId = new BitcoinAssetId(assetId);
                ColoredCoin colored = coin.ToColoredCoin(assetId, pd.balanceQuantity);

                var book        = BitcoinAddress.Create(pd.bitcoinAddress);
                var sendSecret  = new BitcoinSecret(pd.bitcoinSecret);
                var sendAddress = sendSecret.GetAddress();

                var forFees = new Coin(
                    fromTxHash: new uint256(pd.feeFromTxHash),
                    fromOutputIndex: pd.feeFromOutputIndex,
                    amount: Money.Satoshis(pd.feeAmount),
                    scriptPubKey: new Script(Encoders.Hex.DecodeData(pd.feeScriptPubkey)));

                TransactionBuilder builder = new TransactionBuilder();
                var tx = builder
                         .AddKeys(sendSecret)
                         .AddCoins(colored, forFees)
                         .SendAsset(book, new AssetMoney(assetId, pd.quantity))
                         .SetChange(sendAddress)
                         .SendFees(Money.Coins(0.0001m))
                         .BuildTransaction(true);
                System.Diagnostics.Debug.WriteLine(tx);
                Console.WriteLine(tx);
                System.Diagnostics.Debug.WriteLine(builder.Verify(tx));
                Console.WriteLine(builder.Verify(tx));

                var client = new QBitNinjaClient(Network.TestNet);
                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!");
                }

                /* 連結ではbitcoindでのブロードキャストを行う
                 * using (var node = Node.ConnectToLocal(Network.TestNet)) //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
                 * }
                 */
            }
            else
            {
                Console.WriteLine("CommandTypeError");
            }

            Console.ReadKey();
        }
コード例 #13
0
        static void Main()
        {
            //==========================================================================================
            //Other types of asset

            //In the previous chapters, we have seen several types of ownership. You have seen all the different kind of ownership and proof of ownership, and understand how Bitcoin can be coded to invent new kinds of ownership.



            //==========================================================================================
            //Colored Coins

            //So until now, you have seen how to exchange Bitcoins on the network. However you can use the Bitcoin network for transferring and exchanging any type of assets.
            //We call such assets “colored coins”.
            //As far as the Blockchain is concerned, there is no difference between a Coin and a Colored Coin.
            //A colored coin is represented by a standard TxOut.Most of the time, such TxOut have a residual Bitcoin value called “Dust”. (600 satoshi)
            //The real value of a colored coin reside in what the issuer of the coin will exchange against it.


            //Since a colored coin is nothing but a standard coin with special meaning, it follows that all what you saw about proof of ownership and the TransactionBuilder stays true.You can transfer a colored coin with exactly the same rules as before.
            //As far as the blockchain is concerned, a Colored Coin is a Coin like all others.
            //You can represent several type of asset with a colored coin: company shares, bonds, stocks, votes.
            //But no matter what type of asset you will represent, there will always have a trust relationship between the issuer of the asset and the owner.
            //If you own some company share, then the company might decide to not send you dividends.
            //If you own a bond, then the bank might not exchange it at maturity.
            //However, a violation of contract might be automatically detected with the help of Ricardian Contracts.
            //A Ricardian Contract is a contract signed by the issuer with the rights attached to the asset. Such contract can be not only human-readable (PDF) but also structured (JSON), so tools can automatically prove any violation.
            //The issuer can’t change the ricardian contract attached to an asset.
            //The Blockchain is only the transport medium of a financial instrument.
            //The innovation is that everyone can create and transfer its own asset without intermediary, whereas traditional asset transport mediums (clearing houses), are either heavily regulated or purposefully kept secret, and closed to the general public.
            //Open Asset is the name of the protocol created by Flavien Charlon that describes how to transfer and emit colored coins on the Blockchain.
            //Other protocols exist, but Open Asset is the most easy and flexible and the only one supported by NBitcoin.
            //In the rest of the book, I will not go in the details of the Open Asset protocol, the GitHub page of the specification is better suited to this need.



            //===========================================================================================
            //Section. Issuing an Asset

            //===========================================================================================
            //Section1. Objective

            //For the purpose of this exercise, I will emit BlockchainProgramming coins.
            //You get one of these BlockchainProgramming coins for every 0.004 bitcoin you send me.
            //One more if you add some kind words.
            //Furthermore, this is a great opportunity to make it to the Hall of The Makers.
            //Let’s see how I would code such feature.



            //==========================================================================================
            //Section2. Issuance Coin
            //In Open Asset, the Asset ID is derived from the issuer's ScriptPubKey.
            //If you want to issue a Colored Coin, you need to prove ownership of such ScriptPubKey.And the only way to do that on the Blockchain is by spending a coin belonging to such ScriptPubKey.
            //The coin that you will choose to spend for issuing colored coins is called “Issuance Coin” in NBitcoin.
            //I want to emit an Asset from this book's bitcoin address: 1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB.
            //Take a look at my balance by some blockchain explorers, I decided to use the following coin(2,000,000 satoshis) for issuing assets.

            //{
            //       "transactionId": "eb49a599c749c82d824caf9dd69c4e359261d49bbb0b9d6dc18c59bc9214e43b",
            //       "index": 0,
            //       "value": 2000000,
            //       "scriptPubKey": "76a914c81e8e7b7ffca043b088a992795b15887c96159288ac",
            //       "redeemScript": null
            //}


            //Here is how to create my issuance coin:
            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);

            //Now I need to build transaction and sign the transaction with the help of the TransactionBuilder.
            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

            TransactionBuilder 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);
            //Output:
            //{
            //  …
            //  "out": [
            //    {
            //      "value": "0.00000600",
            //      "scriptPubKey": "OP_DUP OP_HASH160 356facdac5f5bcae995d13e667bb5864fd1e7d59 OP_EQUALVERIFY OP_CHECKSIG"
            //    },
            //    {
            //      "value": "0.01989400",
            //      "scriptPubKey": "OP_DUP OP_HASH160 c81e8e7b7ffca043b088a992795b15887c961592 OP_EQUALVERIFY OP_CHECKSIG"
            //    },
            //    {
            //      "value": "0.00000000",
            //      "scriptPubKey": "OP_RETURN 4f410100010a00"
            //    }
            //  ]
            //}

            //You can see it includes an OP_RETURN output. In fact, this is the location where information about colored coins are stuffed.
            //Here is the format of the data in the OP_RETURN.
            //Picture depiction:
            //IBitcoinSerializable. ColorMarker class. Of the class, properties and methods.



            //In our case, Quantities have only 10, which is the number of Asset I issued to nico. Metadata is arbitrary data. We will see that we can put an url that points to an “Asset Definition”.
            //An Asset Definition is a document that describes what the Asset is.It is optional, so we are not using it in our case. (We’ll come back later on it in the Ricardian Contract part.)
            //For more information check out the Open Asset Specification link.
            //After transaction verifications it is ready to be sent to the network.

            //Trasaction verification.
            Console.WriteLine(builder.Verify(tx));



            //=============================================================================================
            //Section3. With QBitNinja

            //We can do the same thing by a 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!");
            //}


            //=============================================================================================
            //Section4. Or With local Bitcoin Core

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

            //My Bitcoin Wallet has both, the book address and the “Nico”'s address.
            //Picture depiction:



            //As you can see, Bitcoin Core only shows the 0.0001 BTC of fees I paid, and ignores the 2,000,000 satoshis coin because of a spam prevention feature.
            //This classical bitcoin wallet knows nothing about Colored Coins.
            //Worse: If a classical bitcoin wallet spends a colored coin, it will destroy the underlying asset and transfer only the bitcoin value of the TxOut. (200,000 satoshis)
            //For preventing a user from sending Colored Coin to a wallet that does not support it, Open Asset has its own address format, that only colored coin wallets understand.

            nico = BitcoinAddress.Create("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe");
            Console.WriteLine(nico.ToColoredAddress());
            //Output:
            //akFqRqfdmAaXfPDmvQZVpcAQnQZmqrx4gcZ


            //Now, you can take a look on an Open Asset compatible wallet like Coinprism, and see my asset correctly detected:
            //Picture depiction:



            //As I have told you before, the Asset ID is derived from the issuer’s ScriptPubKey, here is how to get it in code:
            var book     = BitcoinAddress.Create("1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB");
            var assetId1 = new AssetId(book).GetWif(Network.Main);

            Console.WriteLine(assetId1); // AVAVfLSb1KZf9tJzrUVpktjxKUXGxUTD4e



            //=============================================================================================
            //Section. Transfer an Asset

            //So now, let’s imagine I sent you some BlockchainProgramming Coins.
            //How can you send me back the coins?
            //You need to build a ColoredCoin.
            //In the sample above, let’s say I want to spend the 10 assets I received on the address “nico”.
            //Here is the coin I want to spend:
            //{
            //  "transactionId": "fa6db7a2e478f3a8a0d1a77456ca5c9fa593e49fd0cf65c7e349e5a4cbe58842",
            //  "index": 0,
            //  "value": 600,
            //  "scriptPubKey": "76a914356facdac5f5bcae995d13e667bb5864fd1e7d5988ac",
            //  "redeemScript": null,
            //  "assetId": "AVAVfLSb1KZf9tJzrUVpktjxKUXGxUTD4e",
            //  "quantity": 10
            //}

            //Here is how to instantiate such Colored Coin in code:
            coin = new Coin(
                fromTxHash: new uint256("fa6db7a2e478f3a8a0d1a77456ca5c9fa593e49fd0cf65c7e349e5a4cbe58842"),
                fromOutputIndex: 0,
                amount: Money.Satoshis(600),
                scriptPubKey: new Script(Encoders.Hex.DecodeData("76a914356facdac5f5bcae995d13e667bb5864fd1e7d5988ac")));
            BitcoinAssetId assetId = new BitcoinAssetId("AVAVfLSb1KZf9tJzrUVpktjxKUXGxUTD4e");
            ColoredCoin    colored = coin.ToColoredCoin(assetId, 10);


            //We will show you later how you can use some web services or custom code to get the coins more easily.
            //I also needed another coin(forFees), to pay the fees.
            //The asset transfer is actually very easy with the TransactionBuilder.
            var book1      = BitcoinAddress.Create("1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB");
            var nicoSecret = new BitcoinSecret("??????????");
            var nico1      = 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(book1, new AssetMoney(assetId, 10))
                      .SetChange(nico1)
                      .SendFees(Money.Coins(0.0001m))
                      .BuildTransaction(true);
            Console.WriteLine(tx);
            //Output:
            //{
            //  ….
            //  "out": [
            //    {
            //      "value": "0.00000000",
            //      "scriptPubKey": "OP_RETURN 4f410100010a00"
            //    },
            //    {
            //      "value": "0.00000600",
            //      "scriptPubKey": "OP_DUP OP_HASH160 c81e8e7b7ffca043b088a992795b15887c961592 OP_EQUALVERIFY OP_CHECKSIG"
            //    },
            //    {
            //      "value": "0.04415000",
            //      "scriptPubKey": "OP_DUP OP_HASH160 356facdac5f5bcae995d13e667bb5864fd1e7d59 OP_EQUALVERIFY OP_CHECKSIG"
            //    }
            //  ]
            //}


            //Which basically succeed:
            //Picture depiction:
        }
コード例 #14
0
        public static void ColoredCoins()
        {
            // ISSUING AN ASSET
            // In Open Asset, the Asset ID is derived from the issuers ScriptPubKey. To issue a Colored Coin, you must prove ownership of the ScriptPubKey by spending from it.
            // The coin you spend for issuing colored coins is called the "Issuance Coin" in NBitcoin. Create an issuance coin:
            // coin being used for issuing the asset:
            //{
            //    "transactionId": "eb49a599c749c82d824caf9dd69c4e359261d49bbb0b9d6dc18c59bc9214e43b",
            //    "index": 0,
            //    "value": 2000000,
            //    "scriptPubKey": "76a914c81e8e7b7ffca043b088a992795b15887c96159288ac",
            //    "redeemScript": null
            //}
            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);
            // build and sign transaction using TransactionBuilder
            var nico    = BitcoinAddress.Create("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe");
            var bookKey = new BitcoinSecret("???"); // program errors bc we don't have nico's private key/secret
            TransactionBuilder builder = new TransactionBuilder();

            var tx = builder
                     .AddCoins(coin)
                     .AddKeys(bookKey)
                     .IssueAsset(nico, new NBitcoin.OpenAsset.AssetMoney(issuance.AssetId, quantity: 10)) // this is new part relevant to issuing colored coins
                     .SendFees(Money.Coins(0.0001m))
                     .SetChange(bookKey.GetAddress())
                     .BuildTransaction(sign: true);

            Console.WriteLine(tx);
            // after tx verification, it's ready to be sent to the network
            Console.WriteLine(builder.Verify(tx));
            // review: broadcasting with 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 broadcast with your own Bitcoin Core node:
            using (var node = Node.ConnectToLocal(Network.Main))
            {
                node.VersionHandshake();
                node.SendMessage(new InvPayload(InventoryType.MSG_TX, tx.GetHash())); // first send just the hash of your transaction
                node.SendMessage(new TxPayload(tx));                                  // then send signed transaction
                Thread.Sleep(500);
            }

            // Colored Coins have their own address format that only colored coin wallets understand, to prevent sending colored coins to wallets that don't support it
            nico = BitcoinAddress.Create("15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe");
            Console.WriteLine(nico.ToColoredAddress());

            // colored coin Asset ID is derived from issuer's ScriptPubKey. here is how to get the ID:
            var book    = BitcoinAddress.Create("1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB");
            var assetId = new AssetId(book).GetWif(Network.Main);

            Console.WriteLine(assetId);

            // TRANSFER AN ASSET
            // to send received colored coins, you need to build a ColredCoin
            // received transaction to spent:
            //{
            //    "transactionId": "fa6db7a2e478f3a8a0d1a77456ca5c9fa593e49fd0cf65c7e349e5a4cbe58842",
            //    "index": 0,
            //    "value": 600,
            //    "scriptPubKey": "76a914356facdac5f5bcae995d13e667bb5864fd1e7d5988ac",
            //    "redeemScript": null,
            //    "assetId": "AVAVfLSb1KZf9tJzrUVpktjxKUXGxUTD4e",
            //    "quantity": 10
            //}
            //var coin2 = new Coin(
            //    fromTxHash: new uint256("fa6db7a2e478f3a8a0d1a77456ca5c9fa593e49fd0cf65c7e349e5a4cbe58842"),
            //    fromOutputIndex: 0,
            //    amount: Money.Satoshis(600),
            //    scriptPubKey: new Script(Encoders.Hex.DecodeData("76a914356facdac5f5bcae995d13e667bb5864fd1e7d5988ac")));
            //BitcoinAssetId assetId2 = new BitcoinAssetId("AVAVfLSb1KZf9tJzrUVpktjxKUXGxUTD4e");
            //ColoredCoin colored = coin2.ToColoredCoin(assetId2, 10);

            //// also, in this exampled, we needed another coin forFees to pay the tx fee
            //var forFees = new Coin(
            //    fromTxHash: new uint256("7f296e96ec3525511b836ace0377a9fbb723a47bdfb07c6bc3a6f2a0c23eba26"),
            //    fromOutputIndex: 0,
            //    amount: Money.Satoshis(4425000),
            //    scriptPubKey: new Script(Encoders.Hex.DecodeData("76a914356facdac5f5bcae995d13e667bb5864fd1e7d5988ac")));

            //TransactionBuilder builder2 = new TransactionBuilder();
            //var tx2 = builder2
            //    .AddCoins(colored, forFees) // note: added newly created colored coin, and coin to pay fees in this example
            //    .AddKeys(bookKey)
            //    .SendAsset(book, new AssetMoney(assetId2, 10))
            //    .SetChange(nico)
            //    .SendFees(Money.Coins(0.0001m))
            //    .BuildTransaction(true);

            //Console.WriteLine(tx2); // again, errors b/c we don't have Nico's secret and I don't feel like creating another real tx and dealing with privacy when I push this to github

            // UNIT TESTS
            // create 2 issuers silver and gold, and fake tx to give bitcoin to silver, gold, satoshi
            var gold     = new Key();
            var silver   = new Key();
            var goldId   = gold.PubKey.ScriptPubKey.Hash.ToAssetId();
            var silverId = silver.PubKey.ScriptPubKey.Hash.ToAssetId();

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

            var init = new Transaction()
            {
                Outputs =
                {
                    new TxOut("1.0", gold),
                    new TxOut("1.0", silver),
                    new TxOut("1.0", satoshi)
                }
            };
            // in NBitcoin the sammary of color coin issuance and transfer is described by class ColoredTransaction
            // the trick to writing unit tests is to use an in memory IColoredTransactionRepository
            var repo = new NoSqlColoredTransactionRepository();

            // now we can put the init tx inside
            repo.Transactions.Put(init);
            // now we can get the color
            ColoredTransaction color = ColoredTransaction.FetchColors(init, repo);

            Console.WriteLine(color);
            // now we will use the coins sent to silver and gold as Issuance coins
            var issuanceCoins = init
                                .Outputs
                                .AsCoins()
                                .Take(2)
                                .Select((c, i) => new IssuanceCoin(c))
                                .OfType <ICoin>()
                                .ToArray();
            var sendGoldToSatoshi = new Transaction(); // use TransactionBuilder to send Gold to Satoshi, then put resulting tx in repo and print result

            var goldCoin = ColoredCoin.Find(sendGoldToSatoshi, color).FirstOrDefault();

            builder = new TransactionBuilder();
            var sendToBobAndAlice = builder
                                    .AddKeys(satoshi)
                                    .AddCoins(goldCoin)
                                    .SendAsset(alice, new AssetMoney(goldId, 4))
                                    .SetChange(satoshi)
                                    .BuildTransaction(true);
            // not enough funds; goldCoin input only has 600sats and need 1200 for output to transfer assets to Alice and change to satoshi; add coin
            var satoshiBtc = init.Outputs.AsCoins().Last();

            builder = new TransactionBuilder();
            var sendToAlice = builder
                              .AddKeys(satoshi)
                              .AddCoins(goldCoin, satoshiBtc)
                              .SendAsset(alice, new AssetMoney(goldId, 4))
                              .SetChange(satoshi)
                              .BuildTransaction(true);

            repo.Transactions.Put(sendToAlice);
            color = ColoredTransaction.FetchColors(sendToAlice, repo);
            // see transaction and its color
            Console.WriteLine(sendToAlice);
            Console.WriteLine(color);
            // have made a unit test that emits and transfers some assets w/o any external dependencies
        }