private HistoricalTransactionDto MapToHistoricalTransaction(BitcoinTransaction tx, string requestedAddress) { var isSending = tx.Inputs.Where(p => p.Address == requestedAddress).Sum(p => p.Value) >= tx.Outputs.Where(p => p.Address == requestedAddress).Sum(p => p.Value); string from; string to; long amount; if (isSending) { from = requestedAddress; to = tx.Outputs.Select(o => o.Address).FirstOrDefault(o => o != null && o != requestedAddress) ?? requestedAddress; amount = tx.Outputs.Where(o => o.Address != requestedAddress).Sum(o => o.Value); } else { to = requestedAddress; from = tx.Inputs.Select(o => o.Address).FirstOrDefault(o => o != null && o != requestedAddress) ?? requestedAddress; amount = tx.Outputs.Where(o => o.Address == requestedAddress).Sum(o => o.Value); } return(new HistoricalTransactionDto { TxHash = tx.Hash, IsSending = isSending, AmountSatoshi = amount, FromAddress = from, AssetId = Constants.Assets.Bitcoin.AssetId, ToAddress = to, TimeStamp = tx.Timestamp }); }
/// <summary> /// Reads a single transaction off the blockchain in sequential order /// /// thanks /// https://en.bitcoin.it/wiki/Protocol_specification#block /// http://james.lab6.com/2012/01/12/bitcoin-285-bytes-that-changed-the-world/ /// https://code.google.com/p/blockchain/source/browse/trunk/BlockChain.h /// </summary> public Boolean Read() { var newBlock = new BitcoinBlock { MagicBytes = Dequeue(4) }; if (BitConverter.ToUInt32(newBlock.MagicBytes, 0) != 3652501241) { throw new Exception("Invalid magic number at the start of this block"); } newBlock.BlockSize = BitConverter.ToUInt32(Dequeue(4), 0); newBlock.BlockFormatVersion = BitConverter.ToUInt32(Dequeue(4), 0); newBlock.PreviousBlockHash = ReverseArray(Dequeue(32)); newBlock.MerkleRoot = ReverseArray(Dequeue(32)); newBlock.TimeStamp = Helper.UnixToDateTime(BitConverter.ToUInt32(Dequeue(4), 0)); newBlock.Bits = BitConverter.ToUInt32(Dequeue(4), 0); newBlock.Nonce = BitConverter.ToUInt32(Dequeue(4), 0); newBlock.TransactionCount = ReadVariableLengthInteger(Dequeue(1)); for (var t = 0; t < newBlock.TransactionCount; t++) { var newTransaction = new BitcoinTransaction(); newTransaction.TransactionVersionNumber = BitConverter.ToUInt32(Dequeue(4), 0); newTransaction.InputCount = ReadVariableLengthInteger(Dequeue(1)); for (var i = 0; i < newTransaction.InputCount; i++) { var newInput = new BitcoinInput(); newInput.InputTransactionHash = Dequeue(32); newInput.InputTransactionIndex = BitConverter.ToUInt32(Dequeue(4), 0); newInput.ResponseScriptLength = ReadVariableLengthInteger(Dequeue(1)); newInput.ResponseScript = Dequeue((int)newInput.ResponseScriptLength); newInput.SequenceNumber = BitConverter.ToUInt32(Dequeue(4), 0); newTransaction.Inputs.Add(newInput); } newTransaction.OutputCount = ReadVariableLengthInteger(Dequeue(1)); for (var o = 0; o < newTransaction.OutputCount; o++) { var newOutput = new BitcoinOutput(); newOutput.OutputValue = BitConverter.ToUInt64(Dequeue(8), 0); newOutput.ChallengeScriptLength = ReadVariableLengthInteger(Dequeue(1)); newOutput.ChallengeScript = Dequeue((int)newOutput.ChallengeScriptLength); newOutput.EcdsaPublickey = ExtractPublicKey(newOutput.ChallengeScript); newOutput.BitcoinAddress = ComputeBitcoinAddress(newOutput.EcdsaPublickey); // todo: expensive operation, should it be a feature flag? newTransaction.Outputs.Add(newOutput); } newTransaction.LockTime = BitConverter.ToUInt32(Dequeue(4), 0); newBlock.Transactions.Add(newTransaction); } CurrentBlock = newBlock; return(true); }
/// <summary> /// Converts Raw Transaction hex into a JSON string representation of the transaction. /// </summary> private void SetJson() { try { BitcoinTransaction btx = Transaction.DecodeRawTx(RawTx); TxJson = JsonConvert.SerializeObject(btx, Formatting.Indented); } catch (Exception ex) { TxJson = "Not a Valid Transaction hex" + Environment.NewLine + ex.ToString(); } }
public static async Task <BitcoinTransaction> SignBitcoinTransaction(Page parentPage, BitcoinTransaction bitcoinTX) { _parentPage = parentPage; _bitcoinTX = bitcoinTX; _waitHandle.Reset(); var deviceCommPage = new DeviceCommPage(CommFunction.SignBitcoinTx); await _parentPage.Navigation.PushModalAsync(deviceCommPage); await Task.Run(() => _waitHandle.WaitOne()); return(_bitcoinTX); }
public static void Main(string[] args) { string hash = "0ade9b2864672074864c1d509ac427ffdcf16fe983ef6ebe8b0606afd3fb50a4"; BitcoinTransaction transaction = BlockchainAPI.GetTransaction(hash); Console.WriteLine("Block Number = {0}", transaction.BlockHeight); BitcoinBlock latestBlock = BlockchainAPI.GetLatestBlock(); Console.WriteLine("LatestBlock = {0}", latestBlock.BlockHeight); Console.WriteLine("Number of Confirmations = {0}", latestBlock.BlockHeight - transaction.BlockHeight); Console.ReadLine(); }
/// <summary> /// Deserialize the transaction hex string. /// </summary> /// <param name="tx">Transaction hex string</param> /// <returns>Bitcoin Transaction</returns> public static BitcoinTransaction DecodeRawTx(string tx) { BitcoinTransaction btx = new BitcoinTransaction(); int index = 0; // 1) 4 byte - version string version = tx.Substring(index, 8); btx.Version = (UInt32)NumberConversions.HexToUInt(version); index += 8; // 2) ? byte - tx_in count (CompactSize uint) btx.TxInCount = NumberConversions.ReadCompactSize(tx, ref index); bool isSigned = true; bool isRbf = false; // Initialize the array btx.TxInList = new TxIn[btx.TxInCount]; for (UInt64 i = 0; i < btx.TxInCount; i++) { TxIn temp = new TxIn(); // 3) 32 byte - TX hash (reverse) temp.TxId = tx.Substring(index, 64); temp.TxId = ReverseTx(temp.TxId); index += 64; // 4) 4 byte - output Index string outIndex = tx.Substring(index, 8); temp.OutIndex = (UInt32)NumberConversions.HexToUInt(outIndex); index += 8; // 5) ? byte - scriptSig length (CompactSize uint) (Maximum value is 10,000 bytes) string scriptSigLength = tx.Substring(index, 2); temp.ScriptSigLength = (int)NumberConversions.ReadCompactSize(tx, ref index); // 6) ? byte - scriptSig or a placeholder for unsigned (can be empty too) temp.ScriptSig = tx.Substring(index, temp.ScriptSigLength * 2); index += temp.ScriptSigLength * 2; //7) 4 byte - sequence - max is 0xffffffff - can change for RBF transactions string sequence = tx.Substring(index, 8); temp.Sequence = (UInt32)NumberConversions.HexToUInt(sequence); index += 8; btx.TxInList[i] = temp; // Check to see if all the inputs are signed if (temp.ScriptSigLength <= 25) { isSigned = false; } // Check for opt-in Replace By Fee if (temp.Sequence != UInt32.MaxValue) { isRbf = true; } } // Set transaction sign and RBF status. btx.Status = (isSigned) ? BitcoinTransaction.TxStatus.Signed : BitcoinTransaction.TxStatus.Unsigned; btx.IsRbf = isRbf; //8) ? byte - tx_out count (compactSize uint) btx.TxOutCount = NumberConversions.ReadCompactSize(tx, ref index); // Initialize the array btx.TxOutList = new TxOut[btx.TxOutCount]; for (UInt64 i = 0; i < btx.TxOutCount; i++) { TxOut temp = new TxOut(); //9) 8 byte - amout to transfer string amount = tx.Substring(index, 16); temp.Amount = NumberConversions.HexToUInt(amount); index += 16; //10) ? byte - pk_script length (compactSize uint) string pkScriptLength = tx.Substring(index, 2); temp.PkScriptLength = (Int32)NumberConversions.HexToUInt(pkScriptLength); index += 2; //11) ? byte - pk_script temp.PkScript = tx.Substring(index, temp.PkScriptLength * 2); index += temp.PkScriptLength * 2; btx.TxOutList[i] = temp; } //12) 4 byte - lock time string lockTime = tx.Substring(index, 8); btx.LockTime = (UInt32)NumberConversions.HexToUInt(lockTime); index += 8; // If the transaction is signed, then it has a TxId if (isSigned) { btx.TxId = BitcoinConversions.GetTxId(tx); } return(btx); }
public async Task <byte[]> SignTransactionAsync(BitcoinTransaction transaction) { var txDic = new Dictionary <string, TransactionType>(); var unsignedTx = new TransactionType { Version = transaction.Version, InputsCnt = (uint)transaction.Inputs.Count, // must be exact number of Inputs count OutputsCnt = (uint)transaction.Outputs.Count, // must be exact number of Outputs count LockTime = transaction.LockTime, ExtraData = transaction.ExtraData, ExtraDataLen = transaction.ExtraData == null ? 0 : (uint)transaction.ExtraData.Length }; CopyInputs(transaction.Inputs, unsignedTx.Inputs, false); CopyOutputs(transaction.Outputs, unsignedTx.Outputs); txDic.Add("unsigned", unsignedTx); foreach (var txInput in transaction.Inputs) { BitcoinTransaction prevTran = txInput.PrevTransaction; var tx = new TransactionType { Version = prevTran.Version, LockTime = prevTran.LockTime, InputsCnt = (uint)prevTran.Inputs.Count, OutputsCnt = (uint)prevTran.Outputs.Count, }; CopyInputs(prevTran.Inputs, tx.Inputs, true); CopyBinOutputs(prevTran.Outputs, tx.BinOutputs); txDic.Add(txInput.PrevHash.ToHex(), tx); } var serializedTx = new List <byte>(); var request = await SendMessageAsync <TxRequest, SignTx>(new SignTx { CoinName = transaction.CoinName, InputsCount = (uint)transaction.Inputs.Count, OutputsCount = (uint)transaction.Outputs.Count }); TxAck txAck; // We do loop here since we need to send over and over the same transactions to trezor because his 64 kilobytes memory // and he will sign chunks and return part of signed chunk in serialized manner, until we receive finall type of Txrequest TxFinished while (request.RequestType != RequestType.Txfinished) { TransactionType currentTx; if ((request.Details != null) && (request.Details.TxHash != null)) { string hash = request.Details.TxHash.ToHex(); if (txDic.ContainsKey(hash)) { currentTx = txDic[hash]; } else { Log.Error($"Unknown hash {hash}"); currentTx = txDic["unsigned"]; } } else { currentTx = txDic["unsigned"]; } switch (request.RequestType) { case RequestType.Txinput: { var msg = new TransactionType(); foreach (var input in currentTx.Inputs) { msg.Inputs.Add(input); } ; txAck = new TxAck { Tx = msg }; //We send TxAck() with TxInputs request = await SendMessageAsync <TxRequest, TxAck>(txAck); // Now we have to check every response is there any SerializedTx chunk if (request.Serialized != null) { serializedTx.AddRange(request.Serialized.SerializedTx); } break; } case RequestType.Txoutput: { var msg = new TransactionType(); if ((request.Details != null) && (request.Details.TxHash != null)) { msg.BinOutputs.Add(currentTx.BinOutputs[(int)request.Details.RequestIndex]); } else { msg.Outputs.Add(currentTx.Outputs[(int)request.Details.RequestIndex]); } txAck = new TxAck { Tx = msg }; //We send TxAck() with TxOutputs request = await SendMessageAsync <TxRequest, TxAck>(txAck); // Now we have to check every response is there any SerializedTx chunk if (request.Serialized != null) { serializedTx.AddRange(request.Serialized.SerializedTx); } break; } case RequestType.Txextradata: { var offset = request.Details.ExtraDataOffset; var length = request.Details.ExtraDataLen; var msg = new TransactionType { ExtraData = currentTx.ExtraData.Skip((int)offset).Take((int)length).ToArray() }; txAck = new TxAck { Tx = msg }; //We send TxAck() with TxInputs request = await SendMessageAsync <TxRequest, TxAck>(txAck); // Now we have to check every response is there any SerializedTx chunk if (request.Serialized != null) { serializedTx.AddRange(request.Serialized.SerializedTx); } break; } case RequestType.Txmeta: { var msg = new TransactionType { Version = currentTx.Version, LockTime = currentTx.LockTime, InputsCnt = currentTx.InputsCnt, OutputsCnt = (request.Details != null) && (request.Details.TxHash != null) ? (uint)currentTx.BinOutputs.Count : (uint)currentTx.Outputs.Count, //ExtraDataLen = currentTx.ExtraData != null ? (uint)currentTx.ExtraData.Length : 0 }; txAck = new TxAck { Tx = msg }; //We send TxAck() with TxInputs request = await SendMessageAsync <TxRequest, TxAck>(txAck); // Now we have to check every response is there any SerializedTx chunk if (request.Serialized != null) { serializedTx.AddRange(request.Serialized.SerializedTx); } break; } } } return(serializedTx.ToArray()); }
async void NextButton_Clicked(object sender, EventArgs e) { var bitcoinTransaction = new BitcoinTransaction(selectedCoin.Name); var bitcoinService = BitcoinService.GetBitcoinService(selectedCoin.Shortcut); var tx = await bitcoinService.CreateTransactionAsync(new BitcoinTX() { Inputs = new List <BitcoinTXInput> { new BitcoinTXInput { Addresses = new List <string> { selectedCoin.DefaultAddress } } }, Outputs = new List <BitcoinTXOutput> { new BitcoinTXOutput { Addresses = new List <string> { toAddressEntry.Text }, Value = 100000 } } }); foreach (var input in tx.Tx.Inputs) { var newInput = new BitcoinTransactionInput { AddressNs = Repository.GetAddress(input.Addresses[0]).GetAddressPath(), Amount = (ulong)input.OutputValue, PrevHash = input.PrevHash.ToBytes(), PrevIndex = (uint)input.OutputIndex, Sequence = (uint)input.Sequence }; var inputTx = await bitcoinService.GetTransactionAsync(input.PrevHash); newInput.PrevTransaction = new BitcoinTransaction(selectedCoin.Shortcut, (uint)inputTx.Ver, (uint)inputTx.LockTime); foreach (var prevInput in inputTx.Inputs) { newInput.PrevTransaction.Inputs.Add(new BitcoinTransactionInput { PrevHash = prevInput.PrevHash.ToBytes(), ScriptSig = prevInput.Script.ToBytes(), PrevIndex = (uint)prevInput.OutputIndex, Sequence = (uint)prevInput.Sequence, ScriptType = InputScriptType.Spendaddress }); } foreach (var prevOutput in inputTx.Outputs) { newInput.PrevTransaction.Outputs.Add(new BitcoinTransactionOutput { Amount = (ulong)prevOutput.Value, Script = prevOutput.Script.ToBytes() }); } bitcoinTransaction.Inputs.Add(newInput); } foreach (var output in tx.Tx.Outputs) { bitcoinTransaction.Outputs.Add(new BitcoinTransactionOutput { Address = output.Addresses[0], Script = output.Script.ToBytes(), Amount = (ulong)output.Value, AddressType = OutputAddressType.Spend, ScriptType = OutputScriptType.Paytoscripthash, }); } var signedTx = await DeviceCommPage.SignBitcoinTransaction(this, bitcoinTransaction); Log.Debug(JsonConvert.SerializeObject(signedTx)); var txPushResult = await bitcoinService.PushRawTransactionAsync(new BitcoinTXRaw { Tx = signedTx.SerializedTx.ToHex().ToLower() }); Log.Debug(JsonConvert.SerializeObject(txPushResult)); }