public Task <CreateTransactionResponse> GetDestroyTransaction(BitcoinAddress bitcoinAddres, decimal modelAmount, IAsset asset, Guid transactionId) { return(Retry.Try(async() => { var context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); return await context.Build(async() => { var builder = new TransactionBuilder(); var assetId = new BitcoinAssetId(asset.BlockChainAssetId, _connectionParams.Network).AssetId; var coins = (await _bitcoinOutputsService.GetColoredUnspentOutputs(bitcoinAddres.ToString(), assetId)).ToList(); builder.SetChange(bitcoinAddres, ChangeType.Colored); builder.AddCoins(coins); var assetMoney = new AssetMoney(assetId, modelAmount, asset.MultiplierPower); var changeAddress = BitcoinAddress.Create(_baseSettings.ChangeAddress, _connectionParams.Network); _transactionBuildHelper.SendAssetWithChange(builder, context, coins, changeAddress, assetMoney, bitcoinAddres); await _transactionBuildHelper.AddFee(builder, context); var tx = builder.BuildTransaction(true); uint markerPosition; var colorMarker = ColorMarker.Get(tx, out markerPosition); for (var i = 0; i < colorMarker.Quantities.Length; i++) { if ((long)colorMarker.Quantities[i] == assetMoney.Quantity && tx.Outputs[i + 1].ScriptPubKey.GetDestinationAddress(_connectionParams.Network) == changeAddress) { colorMarker.Quantities[i] = 0; break; } } tx.Outputs[markerPosition].ScriptPubKey = colorMarker.GetScript(); await SaveSpentOutputs(transactionId, tx); await _signRequestRepository.InsertTransactionId(transactionId); await SaveNewOutputs(transactionId, tx, context); return new CreateTransactionResponse(tx.ToHex(), transactionId); }); }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log)); }
private TxOut EnsureMarkerInserted() { uint position; if (ColorMarker.Get(Transaction, out position) != null) { return(Transaction.Outputs[position]); } var txout = Transaction.AddOutput(new TxOut() { ScriptPubKey = new ColorMarker().GetScript() }); txout.Value = Money.Zero; return(txout); }
private ICoin FindCoin(Transaction tr, string multisig, string walletRedeemScript, decimal amount, IAsset asset) { if (OpenAssetsHelper.IsBitcoin(asset.Id)) { var money = new Money(amount, MoneyUnit.BTC); return(tr.Outputs.AsCoins().FirstOrDefault(o => o.Amount == money && o.ScriptPubKey.GetDestinationAddress(_connectionParams.Network).ToString() == multisig)); } var assetMoney = new AssetMoney(new BitcoinAssetId(asset.BlockChainAssetId), amount, asset.MultiplierPower); uint markerPosition; var marker = ColorMarker.Get(tr, out markerPosition); var found = tr.Outputs.AsIndexedOutputs() .FirstOrDefault(o => o.TxOut.ScriptPubKey.GetDestinationAddress(_connectionParams.Network)?.ToString() == multisig && o.N > markerPosition && marker.Quantities[o.N - markerPosition - 1] == (ulong)assetMoney.Quantity); return(found?.ToCoin().ToScriptCoin(new Script(walletRedeemScript)).ToColoredCoin(assetMoney)); }
public void AggregateOutputs(Transaction tr) { var finalOutputs = new Dictionary <string, TxOut>(); var marker = ColorMarker.Get(tr); var quantites = new Dictionary <string, ulong>(); foreach (var trOutput in tr.Outputs.AsIndexedOutputs().Skip(marker == null ? 0 : 1)) { var key = trOutput.TxOut.ScriptPubKey.ToHex(); if (finalOutputs.ContainsKey(key)) { finalOutputs[key].Value += trOutput.TxOut.Value; if (marker != null && marker.Quantities.Length >= trOutput.N) { quantites[key] += marker.Quantities[(int)trOutput.N - 1]; } } else { finalOutputs[key] = trOutput.TxOut; if (marker != null && marker.Quantities.Length >= trOutput.N) { quantites[key] = marker.Quantities[(int)trOutput.N - 1]; } } } tr.Outputs.Clear(); var outputs = finalOutputs.ToList(); if (marker != null) { var newMarker = new ColorMarker(); newMarker.Quantities = outputs.Select(o => quantites.ContainsKey(o.Key) ? quantites[o.Key] : 0).ToArray(); tr.Outputs.Add(new TxOut() { ScriptPubKey = newMarker.GetScript(), Value = Money.Zero }); } tr.Outputs.AddRange(outputs.Select(o => o.Value)); }
public static void DestroyColorCoin(Transaction tr, AssetMoney money, BitcoinAddress destination, Network network) { if (money == null || money.Quantity <= 0) { return; } uint markerPosition; var colorMarker = ColorMarker.Get(tr, out markerPosition); for (var i = 0; i < colorMarker.Quantities.Length; i++) { if ((long)colorMarker.Quantities[i] == money.Quantity && tr.Outputs[i + 1].ScriptPubKey.GetDestinationAddress(network) == destination) { colorMarker.Quantities[i] = 0; break; } } tr.Outputs[markerPosition].ScriptPubKey = colorMarker.GetScript(); }
public async Task <TransactionNinjaModel> GetTransactionAsync(string txId, bool colored = false) { var relativePath = String.Format("transactions/{0}", txId); var transactionUrl = new Uri(Url, relativePath); if (colored) { var builder = new UriBuilder(transactionUrl); builder.Query = "colored=true"; transactionUrl = builder.Uri; } var json = await InvokeMethod(transactionUrl); var result = JsonConvert.DeserializeObject <TransactionNinjaModel>(json); var transactionInfo = Transaction.Parse(result.Hex); result.IsCoinBase = transactionInfo.IsCoinBase; result.IsColor = transactionInfo.HasValidColoredMarker(); if (result.IsColor && colored) { var colorMarker = ColorMarker.Get(transactionInfo); result.TransactionUrl = colorMarker.GetMetadataUrl(); } //if(!result.IsColor) //{ // result.CleanupOutputs(); //} if (result.IsColor && colored == false) { return(await GetTransactionAsync(txId, true)); } result.CalculateInputsWithReturnedChange(); return(result); }
/// <summary> /// determine asset ids using order-based coloring method /// </summary> public static IEnumerable <ICoin> OrderBasedColoringOutputs(Transaction tr, TransactionBuildContext context) { uint markerPosition; var marker = ColorMarker.Get(tr, out markerPosition); if (marker == null) { return(tr.Outputs.AsCoins()); } List <ICoin> outputsWithAsset = new List <ICoin>(); var inputAmounts = context.GetAssetAmounts.ToList(); var inputAssets = context.GetAssetIds().ToList(); int inputIndex = 0; int outputIndex = (int)markerPosition + 1; int quantityIndex = 0; int issueIndex = 0; while (issueIndex < markerPosition && quantityIndex < marker.Quantities.Length) { var outputAmount = marker.Quantities[quantityIndex]; if (outputAmount == 0) { outputsWithAsset.Add(new Coin(tr, (uint)issueIndex)); } else { outputsWithAsset.Add(new Coin(tr, (uint)issueIndex).ToColoredCoin(context.IssuedAssetId, outputAmount)); } issueIndex++; quantityIndex++; } outputsWithAsset.AddRange(tr.Outputs.AsCoins().Skip(issueIndex).Take((int)markerPosition - issueIndex).Where(x => x.Amount != Money.Zero)); try { while (inputIndex < context.Coins.Count && outputIndex < tr.Outputs.Count && quantityIndex < marker.Quantities.Length) { var outputAmount = (long)marker.Quantities[quantityIndex]; if (outputAmount == 0) { outputsWithAsset.Add(new Coin(tr, (uint)outputIndex)); } else { long coverAmount = 0; string assetId = null; while (coverAmount < outputAmount && inputIndex < context.Coins.Count) { var currentAsset = inputAssets[inputIndex]; if (currentAsset != null && currentAsset != assetId && assetId != null) { throw new BackendException("Can't determine assets of outputs", ErrorCode.Exception); } assetId = assetId ?? currentAsset; var amount = Math.Min(outputAmount - coverAmount, inputAmounts[inputIndex]); inputAmounts[inputIndex] -= amount; coverAmount += amount; if (inputAmounts[inputIndex] <= 0) { inputIndex++; } } if (coverAmount < outputAmount) { throw new BackendException("Invalid color marker", ErrorCode.Exception); } var coin = new Coin(tr, (uint)outputIndex); if (assetId != null) { outputsWithAsset.Add(coin.ToColoredCoin(new BitcoinAssetId(assetId, context.Network).AssetId, (ulong)outputAmount)); } else { outputsWithAsset.Add(coin); } } outputIndex++; quantityIndex++; } outputsWithAsset.AddRange(tr.Outputs.AsCoins().Skip(1 + marker.Quantities.Length).Where(x => x.Amount != Money.Zero)); return(outputsWithAsset); } catch (BackendException) { return(new List <ICoin>()); } }
public async Task <Core.Domain.Block> GetBlock(string id) { var blockId = NBitcoin.uint256.Parse(id); var blockRecord = await Task.Run <NBitcoin.Block>(() => _client.GetBlock(blockId)); ConcurrentChain chain; if (_chain == null) { _chain = _client.GetMainChain(); } chain = _chain; var confirmed = chain.GetBlock(blockId); if (blockRecord == null) { return(null); } var blockInformation = new BlockInformation { BlockId = confirmed.HashBlock, BlockHeader = confirmed.Header, Confirmations = chain.Tip.Height - confirmed.Height + 1, Height = confirmed.Height, MedianTimePast = confirmed.GetMedianTimePast(), BlockTime = confirmed.Header.BlockTime }; var transactionList = new List <Core.Domain.Transaction>(); foreach (var t in blockRecord.Transactions) { var transInfo = _client.GetTransaction(t.GetHash()); var colored = NBitcoin.OpenAsset.Extensions.HasValidColoredMarker(t); var tranExtraInfo = GetTransactionInfo(transInfo, colored, blockInformation); var transJson = Serializer.ToString <GetTransactionResponse>(tranExtraInfo, NBitcoin.Network.TestNet); var model = Serializer.ToObject <TransactionNinjaModel>(transJson, NBitcoin.Network.TestNet); if (colored) { var colorMarker = ColorMarker.Get(t); model.TransactionUrl = colorMarker.GetMetadataUrl(); } model.CalculateInputsWithReturnedChange(); var transaction = GetTransaction(model); transactionList.Add(transaction); } var block = new Core.Domain.Block() { Hash = id, Height = confirmed.Height, Time = confirmed.Header.BlockTime.UtcDateTime, Confirmations = chain.Tip.Height - confirmed.Height + 1, Difficulty = confirmed.Header.Bits.Difficulty, MerkleRoot = confirmed.Header.HashMerkleRoot.ToString(), Nonce = confirmed.Header.Nonce, PreviousBlock = confirmed.Header.HashPrevBlock.ToString(), Transactions = transactionList, TotalTransactions = transactionList.Count }; return(block); }