private JsonResult GetDirect(int Height, int startHeight, int chainHeight) { List <int> blockHeights = new List <int>(); for (var x = startHeight; x < chainHeight; x++) { blockHeights.Add(x); } //fetch the transactions directly from the Blockchain var args = new Dictionary <string, object>(); args.Add("blockHeights", blockHeights); var blocks = RpcHelper.Request <BlockResp>("get_blocks_details_by_heights", args).blocks; List <CachedTx> transactions = new List <CachedTx>(); foreach (var block in blocks) { foreach (var transaction in block.transactions) { var cachedTx = TransactionHelpers.MapTx(transaction); //persist tx's to cache if (cachedTx != null) { transactions.Add(cachedTx); } } } return(new JsonResult(JsonConvert.SerializeObject(transactions))); }
public JsonResult Index() { var rawTxs = RpcHelper.RequestJson <TxPoolResp>("f_on_transactions_pool_json", new Dictionary <string, object>()).result.transactions; var txHashes = new List <string>(); foreach (var rawTx in rawTxs) { txHashes.Add(rawTx.hash); } var tx_args = new Dictionary <string, object>(); tx_args.Add("transactionHashes", txHashes.ToArray()); var txs = RpcHelper.Request <TxDetailResp>("get_transaction_details_by_hashes", tx_args); List <CachedTx> transactions = new List <CachedTx>(); if (txs != null) { foreach (var rawTx in txs.transactions) { var memPoolTx = TransactionHelpers.MapTx(rawTx); memPoolTx.height = 0; transactions.Add(memPoolTx); } } return(new JsonResult(JsonConvert.SerializeObject(transactions))); }
// private static async Task StartConcurrentRpc() // { // Connection conn2 = null; // try // { // conn2 = await new ConnectionFactory().Connect(TargetHost, // vhost: VHost, username: _username, password: _password); // // Console.WriteLine("[Connected]"); // // Console.WriteLine("Starting Rpc Parallel calls..."); // // var newChannel2 = await conn2.CreateChannel(); // Console.WriteLine("[channel created] " + newChannel2.ChannelNumber); // await newChannel2.BasicQos(0, Prefetch, false); // // var rpcHelper = await newChannel2.CreateRpcHelper(ConsumeMode.ParallelWithBufferCopy); // // var watch = new Stopwatch(); // watch.Start(); // // var totalReceived = 0; // // // var tasks = new Task[ConcurrentCalls]; // // for (int i = 0; i < TotalPublish; i += ConcurrentCalls) // { // for (int j = 0; j < ConcurrentCalls; j++) // { // var t = MakeCall(rpcHelper, i + j); // tasks[j] = t; // } // // Task.WaitAll(tasks); // // totalReceived += ConcurrentCalls; // //// Console.WriteLine("calls " + totalReceived); // // if (totalReceived >= TotalPublish) // { // watch.Stop(); // Console.WriteLine("Rpc stress done. Took " + // watch.Elapsed.TotalMilliseconds + // "ms - rate of " + (TotalPublish / watch.Elapsed.TotalSeconds) + " messages per second"); // totalReceived = 0; // } // } // // await Task.Delay(TimeSpan.FromMinutes(5)); // // await newChannel2.Close(); // } // catch (AggregateException ex) // { // Console.WriteLine("[Captured error] " + ex.Message); // } // catch (Exception ex) // { // Console.WriteLine("[Captured error 2] " + ex.Message); // } // // if (conn2 != null) // conn2.Dispose(); // } private static async Task <int> MakeCall(RpcHelper rpcHelper, int y) { var prop2 = new BasicProperties(); var req = new byte[4]; req[3] = (byte)((y & 0xFF000000) >> 24); req[2] = (byte)((y & 0x00FF0000) >> 16); req[1] = (byte)((y & 0x0000FF00) >> 8); req[0] = (byte)((y & 0x000000FF)); var rpcCallResult = await rpcHelper.Call("test_ex", "rpc1", prop2, new ArraySegment <byte>(req, 0, 4)); if (rpcCallResult.stream != null) { var reply = new byte[4]; rpcCallResult.stream.Read(reply, 0, rpcCallResult.bodySize); var x = BitConverter.ToInt32(reply, 0); if (x != y) { throw new Exception("Invalid result for call"); } } // else if (rpcCallResult.Body != null) // { // var x = BitConverter.ToInt32(rpcCallResult.Body, 0); // if (x != y) throw new Exception("Invalid result for call"); // } // Console.WriteLine("Call " + y + " completed"); return(y); }
public void RpcPost() { string result = RpcHelper.PostAsync("https://www.baidu.com", new LoginDto { LoginName = "hqmcq", LoginPwd = "123456" }).Result; Assert.IsNotNull(result); }
public JsonResult Get(int height = 0) { //TODO: Update this to split get Tx's from Split BC cache var sizeBlock = 1000; var startHeight = height; var endHeight = startHeight + 100; if (startHeight < 1) { startHeight = 1; } //todo, we don't have to query the chain height every time this is requested var chainHeight = RpcHelper.Request <GetHeightResp>("getheight").Height; if (chainHeight == 0) { chainHeight = endHeight + 100; } if (endHeight > chainHeight) { endHeight = chainHeight; } List <int> dbSegments = new List <int>(); var segmentStart = Convert.ToInt32(Math.Floor((double)(startHeight / sizeBlock) * sizeBlock)) + 1; dbSegments.Add(segmentStart); if (startHeight + 100 > segmentStart + sizeBlock - 1) { dbSegments.Add(segmentStart + sizeBlock); } try { //TODO:... if we find a problem - ie: there are ANY tx's that don't return at least one Tx poer height, then we need to re-cache the DB file we're working with for this height... //we need to ensure that there is at least one Tx per block //should this be in a seperate "validation background job that's checking completed files - maybe to run once per day? List <LightTx> transactions = new List <LightTx>(); foreach (var start in dbSegments) { var end = start + sizeBlock - 1; using (var db = new LiteDatabase(string.Concat(AppContext.BaseDirectory, @"App_Data\", "transactions_", start, "-", end, ".db"))) { var cachedtxs = db.GetCollection <CachedTx>("cached_txs"); var txs = cachedtxs.Find(x => x.height >= startHeight && x.height <= endHeight).Distinct().ToList(); transactions.AddRange(TransactionHelpers.MapTxs(txs)); } } return(new JsonResult(JsonConvert.SerializeObject(transactions))); } catch (Exception ex) { //todo: log and return client handlable exception } return(new JsonResult("")); }
public JsonResult Index([FromForm] string value) { var x = value; var args = new Dictionary <string, object>(); args.Add("tx_as_hex", value); var response = RpcHelper.Request <RawTxResp>("sendrawtransaction", args); return(new JsonResult(response)); }
public JsonResult Get(int height = 0) { //TODO: Update this to split get Tx's from Split BC cache var startHeight = Convert.ToInt32(Math.Floor((double)(height / 100) * 100)); var endHeight = startHeight + 100; if (startHeight < 1) { startHeight = 1; } var chainHeight = RpcHelper.Request <GetHeightResp>("getheight").Height; if (chainHeight == 0) { chainHeight = endHeight + 100; } if (endHeight > chainHeight) { endHeight = chainHeight; } //used to pick the correct file var start = Convert.ToInt32(Math.Floor((double)(height / 10000) * 10000)) + 1; var end = start + 10000 - 1; try { //if we are close to the top of the chain (within 100 blocks) get the data directly from the node and return it.. //this is a bit slower than a cache hit, but ensures we keep the wallet sync'd up to it's current actual height, rather than working a block or two behind. var lastSegment = Convert.ToInt32(Math.Floor((double)(chainHeight / 100) * 100)); if (lastSegment < height) { return(GetDirect(height, startHeight, chainHeight)); } else { //get from cache //TODO: If there's an error, (wallet cache get's stuck), we need to re-create that segment of the chain somehow //drop the cache file, and let it rebuild, and return the queries from the chain directly... //need some for of checking mechanism to ensure that each block in the segment wh're querying has at least one transaction using (var db = new LiteDatabase(string.Concat(AppContext.BaseDirectory, @"App_Data\", "transactions_", start, "-", end, ".db"))) { var transactions = db.GetCollection <CachedTx>("cached_txs"); var txs = transactions.Find(x => x.height >= startHeight && x.height <= endHeight).Distinct().ToList(); return(new JsonResult(JsonConvert.SerializeObject(txs))); } } } catch (Exception ex) { //todo: log and return client handlable exception } return(new JsonResult("")); }
public ContentResult Get() { try { return(Content((RpcHelper.Request <GetHeightResp>("getheight").Height - 1).ToString())); } catch (Exception ex) { //if the daemon is down, getheight will return an error and get stuck, so we return the latest cache height that we have available in this instance... return(Content("0")); //fail if daemon not responding } }
public JsonResult Get(int height = 0) { //TODO: Update this to split get Tx's from Split BC cache var sizeBlock = 1000; var startHeight = Convert.ToInt32(Math.Floor((double)(height / 100) * 100)); var endHeight = startHeight + 100; if (startHeight < 1) { startHeight = 1; } var chainHeight = RpcHelper.Request <GetHeightResp>("getheight").Height; if (chainHeight == 0) { chainHeight = endHeight + 100; } if (endHeight > chainHeight) { endHeight = chainHeight; } //used to pick the correct file var start = Convert.ToInt32(Math.Floor((double)(height / sizeBlock) * sizeBlock)) + 1; var end = start + sizeBlock - 1; try { //TODO:... if we find a problem - ie: there are ANY tx's that don't return at least one Tx poer height, then we need to re-cache the DB file we're working with for this height... //we need to ensure that there is at least one Tx per block //should this be in a seperate "validation background job that's checking completed files - maybe to run once per day? using (var db = new LiteDatabase(string.Concat(AppContext.BaseDirectory, @"App_Data/", "transactions_", start, "-", end, ".db"))) { var transactions = db.GetCollection <CachedTx>("cached_txs"); var txs = transactions.Find(x => x.height >= startHeight && x.height <= endHeight).Distinct().ToList(); return(new JsonResult(JsonConvert.SerializeObject(txs))); } } catch (Exception ex) { //todo: log and return client handlable exception throw ex; } return(new JsonResult("")); }
internal static bool TryTestUserCache(string databaseServer, string primarySmtpAddress, SubscriptionCacheAction cacheAction, out string failureReason, out uint cacheActionResult, out List <SubscriptionCacheObject> cacheObjects, out ObjectState objectState) { SyncUtilities.ThrowIfArgumentNullOrEmpty("databaseServer", databaseServer); SyncUtilities.ThrowIfArgumentNullOrEmpty("primarySmtpAddress", primarySmtpAddress); failureReason = null; cacheActionResult = 268435456U; cacheObjects = null; objectState = ObjectState.Unchanged; byte[] testUserCacheInputBytes = SubscriptionCacheClient.GetTestUserCacheInputBytes(primarySmtpAddress, cacheAction); byte[] array = null; using (SubscriptionCacheRpcClient subscriptionCacheRpcClient = new SubscriptionCacheRpcClient(databaseServer)) { try { array = subscriptionCacheRpcClient.TestUserCache(0, testUserCacheInputBytes); } catch (RpcException exception) { failureReason = Strings.CacheRpcExceptionEncountered(exception); return(false); } } MdbefPropertyCollection args = MdbefPropertyCollection.Create(array, 0, array.Length); int num; if (!RpcHelper.TryGetProperty <int>(args, 2835349507U, out num)) { failureReason = Strings.CacheRpcInvalidServerVersionIssue(databaseServer); return(false); } cacheActionResult = (uint)num; RpcHelper.TryGetProperty <string>(args, 2835415071U, out failureReason); byte[] buffer; if (RpcHelper.TryGetProperty <byte[]>(args, 2835480834U, out buffer)) { using (MemoryStream memoryStream = new MemoryStream(buffer)) { cacheObjects = (List <SubscriptionCacheObject>)SubscriptionCacheClient.binaryFormatter.Deserialize(memoryStream); } } if (RpcHelper.TryGetProperty <int>(args, 2835546115U, out num)) { objectState = (ObjectState)num; } return(true); }
private static Signer getSigner(IConfiguration config) { string signerHexStr = config["signer"]; if (string.IsNullOrWhiteSpace(signerHexStr)) { throw new Exception("Signer is not configured"); } try { return(new Signer(RpcHelper.HexToBytes(signerHexStr))); } catch (Exception x) { throw new Exception("Failed to initialize signer: " + x.Message); } }
private static BigInteger GetHeadIdx() { string msg; string head = RpcHelper.LastBlock(httpClient, creditcoinUrl, out msg); Debug.Assert(head != null && msg == null || head == null && msg != null); if (head == null) { throw new Exception(msg); } BigInteger headIdx; if (!BigInteger.TryParse(head, out headIdx)) { throw new Exception("Invalid numerics"); } return(headIdx); }
public JsonResult Get(int height = 0) { //TODO: Update this to split get Tx's from Split BC cache var startHeight = height; var endHeight = startHeight + 100; if (startHeight < 1) { startHeight = 1; } var chainHeight = RpcHelper.Request <GetHeightResp>("getheight").Height; if (chainHeight == 0) { chainHeight = endHeight + 100; } if (endHeight > chainHeight) { endHeight = chainHeight; } //used to pick the correct file var start = Convert.ToInt32(Math.Floor((double)(height / 10000) * 10000)) + 1; var end = start + 10000 - 1; try { using (var db = new LiteDatabase(string.Concat(AppContext.BaseDirectory, @"App_Data\", "transactions_", start, "-", end, ".db"))) { var transactions = db.GetCollection <CachedTx>("cached_txs"); var txs = transactions.Find(x => x.height >= startHeight && x.height <= endHeight); return(new JsonResult(JsonConvert.SerializeObject(txs))); } } catch (Exception ex) { //todo: log and return client handlable exception } return(new JsonResult("")); }
public bool Run(IConfiguration cfg, HttpClient httpClient, ITxBuilder txBuilder, Dictionary <string, string> settings, string pluginsFolder, string url, string[] command, out bool inProgress, out string msg) { Debug.Assert(command != null); Debug.Assert(command.Length > 1); if (command[0].Equals("registerTransfer", StringComparison.OrdinalIgnoreCase)) { // bitcoin RegisterTransfer registeredSourceId amount sourceTxId string progress = Path.Combine(pluginsFolder, $"{name}_progress.txt"); inProgress = false; Debug.Assert(command.Length == 4); string registeredSourceId = command[1]; string amountString = command[2]; string sourceTxIdString = command[3]; string secret = cfg["secret"]; if (string.IsNullOrWhiteSpace(secret)) { msg = "bitcoin.secret is not set"; return(false); } string rpcAddress = cfg["rpc"]; if (string.IsNullOrWhiteSpace(rpcAddress)) { msg = "bitcoin.rpc is not set"; return(false); } string credential = cfg["credential"]; if (string.IsNullOrWhiteSpace(credential)) { msg = "bitcoin.credential is not set"; return(false); } //TODO disable confirmation count config for release build string confirmationsCount = cfg["confirmationsCount"]; if (string.IsNullOrWhiteSpace(confirmationsCount)) { msg = "bitcoin.confirmationsCount is not set"; return(false); } int confirmationsExpected = 1; if (!int.TryParse(confirmationsCount, out confirmationsExpected)) { msg = "bitcoin.confirmationsCount is not an int"; return(false); } string feeString = cfg["fee"]; if (string.IsNullOrWhiteSpace(feeString)) { msg = "bitcoin.fee is not set"; return(false); } if (!int.TryParse(feeString, out int fee)) { msg = "bitcoin.fee is not an int"; return(false); } var bitcoinPrivateKey = new BitcoinSecret(secret); var network = bitcoinPrivateKey.Network; var rpcClient = new RPCClient(credential, new Uri(rpcAddress), network); string payTxIdHash; uint256 payTxId; if (File.Exists(progress)) { Console.WriteLine("Found unfinished action, retrying..."); payTxIdHash = File.ReadAllText(progress); if (!uint256.TryParse(payTxIdHash, out payTxId)) { msg = "corrupted progress file"; return(false); } } else { var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{url}/state/{registeredSourceId}", out msg); if (protobuf == null) { return(false); } var address = Address.Parser.ParseFrom(protobuf); string destinationAddress; if (!settings.TryGetValue("sawtooth.escrow." + name, out destinationAddress)) { msg = "Escrow not found for " + name; return(false); } Money transferAmount; if (!Money.TryParse(amountString, out transferAmount) || transferAmount <= 0) { msg = "Invalid amount"; return(false); } if (!address.Blockchain.Equals(name)) { msg = $"bitcoin RegisterTransfer can only transfer bitcoins.\nThis source is registered for {address.Blockchain}"; return(false); } var sourceAddress = bitcoinPrivateKey.GetAddress(); if (!sourceAddress.ToString().Equals(address.Address_, StringComparison.OrdinalIgnoreCase)) { msg = "The deal is for a different client"; return(false); } #if RELEASE if (network != NBitcoin.Network.Main) { msg = "bitcoin.secret is not defined for main network"; return(false); } #endif var sourceTxId = uint256.Parse(sourceTxIdString); var transactionResponse = rpcClient.GetRawTransaction(sourceTxId); //TODO: fix (assuming that the transaction has one output intended for the private key with the exact amount) var receivedCoins = transactionResponse.Outputs.AsCoins(); OutPoint outPointToSpend = null; TxOut outTxToSpend = null; Money amount = null; foreach (var coin in receivedCoins) { if (coin.TxOut.ScriptPubKey == bitcoinPrivateKey.ScriptPubKey) { outPointToSpend = coin.Outpoint; outTxToSpend = coin.TxOut; amount = (Money)coin.Amount; if (amount.CompareTo(transferAmount + fee * 2) < 0) { msg = $"Invalid transaction - needed: {transferAmount}, has: {amount.ToString()}"; return(false); } break; } } if (outPointToSpend == null) { msg = "Invalid transaction - no outputs that the client can spend"; return(false); } var addressFrom = BitcoinAddress.Create(address.Address_, network); var addressTo = BitcoinAddress.Create(destinationAddress, network); var message = address.Sighash; var bytes = Encoding.UTF8.GetBytes(message); var bitcoinTransactionBuilder = new TransactionBuilder(); var transaction = bitcoinTransactionBuilder .AddCoins(new Coin(outPointToSpend, outTxToSpend)) .AddKeys(bitcoinPrivateKey) .Send(addressTo.ScriptPubKey, transferAmount + fee) .Send(TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes), Money.Zero) .SendFees(new Money(fee, MoneyUnit.Satoshi)) .SetChange(addressFrom.ScriptPubKey) .BuildTransaction(true); if (!bitcoinTransactionBuilder.Verify(transaction)) { msg = "failed verify transaction"; return(false); } try { payTxId = rpcClient.SendRawTransaction(transaction); } catch (RPCException e) { msg = $"failed to broadcast - error: {e.RPCCode}, reason: {e.RPCCodeMessage}"; return(false); } if (payTxId == null) { msg = "failed to broadcast - unknown error"; return(false); } payTxIdHash = payTxId.ToString(); File.WriteAllText(progress, payTxIdHash); } inProgress = true; while (true) { var transactionResponse = rpcClient.GetRawTransactionInfo(payTxId); if (transactionResponse != null && transactionResponse.BlockHash != null && transactionResponse.Confirmations >= confirmationsExpected) { break; } Thread.Sleep(1000); } command = new string[] { command[0], registeredSourceId, amountString, feeString, payTxIdHash, ((network == Network.Main) ? "1" : "0") }; var tx = txBuilder.BuildTx(command, out msg); Debug.Assert(tx != null); Debug.Assert(msg == null); var content = new ByteArrayContent(tx); content.Headers.Add("Content-Type", "application/octet-stream"); msg = RpcHelper.CompleteBatch(httpClient, $"{url}/batches", content); File.Delete(progress); inProgress = false; return(true); } else { inProgress = false; msg = "Unknown command: " + command[0]; return(false); } }
public bool Run(bool txid, IConfiguration cfg, HttpClient httpClient, ITxBuilder txBuilder, Dictionary <string, string> settings, string progressId, string pluginsFolder, string url, string[] command, out bool inProgress, out string msg) { Debug.Assert(command != null); if (command.Length < 2) { inProgress = false; msg = "invalid parameter count"; return(false); } if (command[0].Equals("registerTransfer", StringComparison.OrdinalIgnoreCase)) { // bitcoin RegisterTransfer srcAddressId dstAddressId orderId amount sourceTxId string progress = Path.Combine(pluginsFolder, $"{name}_progress{progressId}.txt"); inProgress = false; if (command.Length != 4) { msg = "invalid parameter count"; return(false); } string gainString = command[1]; string orderId = command[2]; string sourceTxIdString = command[3]; string secret = cfg["secret"]; if (string.IsNullOrWhiteSpace(secret)) { msg = "bitcoin.secret is not set"; return(false); } string rpcAddress = cfg["rpc"]; if (string.IsNullOrWhiteSpace(rpcAddress)) { msg = "bitcoin.rpc is not set"; return(false); } string credential = cfg["credential"]; if (string.IsNullOrWhiteSpace(credential)) { msg = "bitcoin.credential is not set"; return(false); } #if DEBUG string confirmationsCount = cfg["confirmationsCount"]; if (int.TryParse(confirmationsCount, out int parsedCount)) { mConfirmationsExpected = parsedCount; } #endif string feeString = cfg["fee"]; if (string.IsNullOrWhiteSpace(feeString)) { msg = "bitcoin.fee is not set"; return(false); } if (!int.TryParse(feeString, out int fee)) { msg = "bitcoin.fee is not an int"; return(false); } var bitcoinPrivateKey = new BitcoinSecret(secret); var network = bitcoinPrivateKey.Network; var rpcClient = new RPCClient(credential, new Uri(rpcAddress), network); string srcAddressId; string dstAddressId; string amountString; var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{url}/state/{orderId}", out msg); if (protobuf == null) { msg = "failed to extract address data through RPC"; return(false); } if (orderId.StartsWith(RpcHelper.creditCoinNamespace + RpcHelper.dealOrderPrefix)) { var dealOrder = DealOrder.Parser.ParseFrom(protobuf); if (gainString.Equals("0")) { srcAddressId = dealOrder.SrcAddress; dstAddressId = dealOrder.DstAddress; } else { dstAddressId = dealOrder.SrcAddress; srcAddressId = dealOrder.DstAddress; } amountString = dealOrder.Amount; } else if (orderId.StartsWith(RpcHelper.creditCoinNamespace + RpcHelper.repaymentOrderPrefix)) { var repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); if (gainString.Equals("0")) { srcAddressId = repaymentOrder.SrcAddress; dstAddressId = repaymentOrder.DstAddress; } else { dstAddressId = repaymentOrder.SrcAddress; srcAddressId = repaymentOrder.DstAddress; } amountString = repaymentOrder.Amount; } else { msg = "unexpected referred order"; return(false); } string payTxIdHash; uint256 payTxId; if (File.Exists(progress)) { Console.WriteLine("Found unfinished action, retrying..."); payTxIdHash = File.ReadAllText(progress); if (!uint256.TryParse(payTxIdHash, out payTxId)) { msg = "corrupted progress file"; return(false); } } else { protobuf = RpcHelper.ReadProtobuf(httpClient, $"{url}/state/{srcAddressId}", out msg); if (protobuf == null) { msg = "failed to extract address data through RPC"; return(false); } var srcAddress = Address.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{url}/state/{dstAddressId}", out msg); if (protobuf == null) { msg = "failed to extract address data through RPC"; return(false); } var dstAddress = Address.Parser.ParseFrom(protobuf); long transferAmount; if (!long.TryParse(amountString, out transferAmount) || transferAmount <= 0) { msg = "Invalid amount"; return(false); } long gain; if (!long.TryParse(gainString, out gain)) { msg = "Invalid amount"; return(false); } if (transferAmount + gain < transferAmount) { msg = "Overflow"; return(false); } transferAmount = transferAmount + gain; if (transferAmount < 0) { msg = "Invalid amount"; return(false); } if (transferAmount + fee < transferAmount) { msg = "Overflow"; return(false); } if (!srcAddress.Blockchain.Equals(name) || !dstAddress.Blockchain.Equals(name)) { msg = $"bitcoin RegisterTransfer can only transfer bitcoins.\nThis source is registered for {srcAddress.Blockchain} and destination for {dstAddress.Blockchain}"; return(false); } var sourceAddress = bitcoinPrivateKey.GetAddress(); if (!sourceAddress.ToString().Equals(srcAddress.Value, StringComparison.OrdinalIgnoreCase)) { msg = "The deal is for a different client"; return(false); } #if RELEASE if (network != NBitcoin.Network.Main) { msg = "bitcoin.secret is not defined for main network"; return(false); } #endif var sourceTxId = uint256.Parse(sourceTxIdString); var transactionResponse = rpcClient.GetRawTransaction(sourceTxId); //TODO: fix (assuming that the transaction has one output intended for the private key with the exact amount) var receivedCoins = transactionResponse.Outputs.AsCoins(); OutPoint outPointToSpend = null; TxOut outTxToSpend = null; foreach (var coin in receivedCoins) { if (coin.TxOut.ScriptPubKey == bitcoinPrivateKey.ScriptPubKey) { outPointToSpend = coin.Outpoint; outTxToSpend = coin.TxOut; long amount = coin.Amount.Satoshi; if (amount < transferAmount + fee) { msg = $"Invalid transaction - needed: {transferAmount} + {fee}, has: {amount.ToString()}"; return(false); } break; } } if (outPointToSpend == null) { msg = "Invalid transaction - no outputs that the client can spend"; return(false); } var addressFrom = BitcoinAddress.Create(srcAddress.Value, network); var addressTo = BitcoinAddress.Create(dstAddress.Value, network); var message = orderId; var bytes = Encoding.UTF8.GetBytes(message); var bitcoinTransactionBuilder = new TransactionBuilder(); var transaction = bitcoinTransactionBuilder .AddCoins(new Coin(outPointToSpend, outTxToSpend)) .AddKeys(bitcoinPrivateKey) .Send(addressTo.ScriptPubKey, transferAmount) .Send(TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes), Money.Zero) .SendFees(new Money(fee, MoneyUnit.Satoshi)) .SetChange(addressFrom.ScriptPubKey) .BuildTransaction(true); if (!bitcoinTransactionBuilder.Verify(transaction)) { msg = "failed verify transaction"; return(false); } try { payTxId = rpcClient.SendRawTransaction(transaction); } catch (RPCException e) { msg = $"failed to broadcast - error: {e.RPCCode}, reason: {e.RPCCodeMessage}"; return(false); } if (payTxId == null) { msg = "failed to broadcast - unknown error"; return(false); } payTxIdHash = payTxId.ToString(); File.WriteAllText(progress, payTxIdHash); } inProgress = true; while (true) { var transactionResponse = rpcClient.GetRawTransactionInfo(payTxId); if (transactionResponse != null && transactionResponse.BlockHash != null && transactionResponse.Confirmations >= mConfirmationsExpected) { break; } Thread.Sleep(1000); } command = new string[] { command[0], gainString, orderId, payTxIdHash }; var tx = txBuilder.BuildTx(command, out msg); Debug.Assert(tx != null); Debug.Assert(msg == null); var content = new ByteArrayContent(tx); content.Headers.Add("Content-Type", "application/octet-stream"); msg = RpcHelper.CompleteBatch(httpClient, url, "batches", content, txid); File.Delete(progress); inProgress = false; return(true); } else { inProgress = false; msg = "Unknown command: " + command[0]; return(false); } }
static void Main(string[] args) { try { string root = Directory.GetCurrentDirectory(); string pluginFolder = TxBuilder.GetPluginsFolder(root); if (pluginFolder == null) { Console.WriteLine("plugins subfolder not found"); return; } string progressId = ""; bool ignoreOldProgress = false; if (args.Length > 0 && args[0].StartsWith(progressParamPrefix)) { progressId = args[0].Substring(progressParamPrefix.Length); if (progressId[0] == '*') { ignoreOldProgress = true; progressId = progressId.Substring(1); } args = args.Skip(1).ToArray(); } string progress = Path.Combine(pluginFolder, $"progress{progressId}.txt"); if (ignoreOldProgress) { File.Delete(progress); } if (File.Exists(progress)) { Console.WriteLine("Found unfinished action, retrying..."); args = File.ReadAllText(progress).Split(); } else if (args.Length > 0) { File.WriteAllText(progress, string.Join(' ', args)); } if (args.Length < 1) { Console.WriteLine("Usage: ccclient [-progress:[*]progressId] [-config:configFileName] [-txid] command [parameters]"); Console.WriteLine("commands:"); Console.WriteLine("sighash"); Console.WriteLine("tip [numBlocksBelow]"); Console.WriteLine("list Settings"); Console.WriteLine("list Wallets"); Console.WriteLine("list Addresses"); Console.WriteLine("list Transfers"); Console.WriteLine("list AskOrders"); Console.WriteLine("list BidOrders"); Console.WriteLine("list Offers"); Console.WriteLine("list DealOrders"); Console.WriteLine("list RepaymentOrders"); Console.WriteLine("show Balance sighash|0"); Console.WriteLine("show Address sighash|0 blockchain address network"); Console.WriteLine("show MatchingOrders sighash|0"); Console.WriteLine("show CurrentOffers sighash|0"); Console.WriteLine("show CreditHistory sighash|0"); Console.WriteLine("show NewDeals sighash|0"); Console.WriteLine("show Transfer sighash|0 orderId"); Console.WriteLine("show CurrentLoans sighash|0"); Console.WriteLine("show NewRepaymentOrders sighash|0"); Console.WriteLine("show CurrentRepaymentOrders sighash|0"); Console.WriteLine("creditcoin SendFunds amount sighash "); Console.WriteLine("creditcoin RegisterAddress blockchain address network"); Console.WriteLine("creditcoin RegisterTransfer gain orderId txId"); Console.WriteLine("creditcoin AddAskOrder addressId amount interest maturity fee expiration"); Console.WriteLine("creditcoin AddBidOrder addressId amount interest maturity fee expiration"); Console.WriteLine("creditcoin AddOffer askOrderId bidOrderId expiration"); Console.WriteLine("creditcoin AddDealOrder offerId expiration"); Console.WriteLine("creditcoin CompleteDealOrder dealOrderId transferId"); Console.WriteLine("creditcoin LockDealOrder dealOrderId"); Console.WriteLine("creditcoin CloseDealOrder dealOrderId transferId"); Console.WriteLine("creditcoin Exempt dealOrderId transferId"); Console.WriteLine("creditcoin AddRepaymentOrder dealOrderId addressId amount expiration"); Console.WriteLine("creditcoin CompleteRepaymentOrder repaymentOrderId"); Console.WriteLine("creditcoin CloseRepaymentOrder repaymentOrderId transferId"); Console.WriteLine("creditcoin CollectCoins addressId amount txId"); Console.WriteLine("bitcoin RegisterTransfer gain orderId sourceTxId"); Console.WriteLine("ethereum RegisterTransfer gain orderId"); Console.WriteLine("ethereum CollectCoins amount"); return; } string configFile = null; if (args.Length > 0 && args[0].StartsWith(configParamPrefix)) { configFile = args[0].Substring(configParamPrefix.Length); args = args.Skip(1).ToArray(); if (!File.Exists(configFile)) { configFile = Path.Combine(pluginFolder, configFile); if (!File.Exists(configFile)) { Console.WriteLine("Cannot find the specified config file"); return; } } } bool txid = false; if (args.Length > 0 && args[0].Equals(txidParam)) { args = args.Skip(1).ToArray(); txid = true; } string action; string[] command; var builder = new ConfigurationBuilder() .AddJsonFile("appsettings.json", true, false) #if DEBUG .AddJsonFile("appsettings.dev.json", true, false) #endif ; if (configFile != null) { builder.AddJsonFile(configFile, true, false); } IConfiguration config = builder.Build(); string creditcoinRestApiURL = config["creditcoinRestApiURL"]; if (!string.IsNullOrWhiteSpace(creditcoinRestApiURL)) { creditcoinUrl = creditcoinRestApiURL; } Signer signer = getSigner(config); if (args.Length < 1) { Console.WriteLine("Command is not provided"); return; } action = args[0].ToLower(); command = args.Skip(1).ToArray(); bool inProgress = false; if (action.Equals("sighash")) { Console.WriteLine(TxBuilder.getSighash(signer)); } else if (action.Equals("tip")) { BigInteger headIdx = GetHeadIdx(); if (command.Length == 1) { BigInteger num; if (!BigInteger.TryParse(command[0], out num)) { throw new Exception("Invalid numerics"); } headIdx -= num; } Console.WriteLine(headIdx); } else if (action.Equals("list")) { if (command.Length > 2) { throw new Exception("1 or 2 parametersd expected"); } string id = null; if (command.Length == 2) { id = command[1]; } if (command[0].Equals("settings", StringComparison.OrdinalIgnoreCase)) { filter(RpcHelper.settingNamespace, (string objid, byte[] protobuf) => { if (id == null || id != null && id.Equals(objid)) { Setting setting = Setting.Parser.ParseFrom(protobuf); foreach (var entry in setting.Entries) { Console.WriteLine($"{entry.Key}: {entry.Value}"); } } }); } else if (command[0].Equals("wallets", StringComparison.OrdinalIgnoreCase)) { filter(RpcHelper.creditCoinNamespace + RpcHelper.walletPrefix, (string objid, byte[] protobuf) => { if (id == null || id != null && id.Equals(objid)) { Wallet wallet = Wallet.Parser.ParseFrom(protobuf); Console.WriteLine($"wallet({objid}) amount:{wallet.Amount}"); } }); } else if (command[0].Equals("addresses", StringComparison.OrdinalIgnoreCase)) { filter(RpcHelper.creditCoinNamespace + RpcHelper.addressPrefix, (string objid, byte[] protobuf) => { if (id == null || id != null && id.Equals(objid)) { Address address = Address.Parser.ParseFrom(protobuf); Console.WriteLine($"address({objid}) blockchain:{address.Blockchain} value:{address.Value} network:{address.Network} sighash:{address.Sighash}"); } }); } else if (command[0].Equals("transfers", StringComparison.OrdinalIgnoreCase)) { filter(RpcHelper.creditCoinNamespace + RpcHelper.transferPrefix, (string objid, byte[] protobuf) => { if (id == null || id != null && id.Equals(objid)) { Transfer transfer = Transfer.Parser.ParseFrom(protobuf); Console.WriteLine($"transfer({objid}) blockchain:{transfer.Blockchain} srcAddress:{transfer.SrcAddress} dstAddress:{transfer.DstAddress} order:{transfer.Order} amount:{transfer.Amount} tx:{transfer.Tx} block:{transfer.Block} processed:{transfer.Processed} sighash:{transfer.Sighash}"); } }); } else if (command[0].Equals("askOrders", StringComparison.OrdinalIgnoreCase)) { filter(RpcHelper.creditCoinNamespace + RpcHelper.askOrderPrefix, (string objid, byte[] protobuf) => { if (id == null || id != null && id.Equals(objid)) { AskOrder askOrder = AskOrder.Parser.ParseFrom(protobuf); Console.WriteLine($"askOrder({objid}) blockchain:{askOrder.Blockchain} address:{askOrder.Address} amount:{askOrder.Amount} interest:{askOrder.Interest} maturity:{askOrder.Maturity} fee:{askOrder.Fee} expiration:{askOrder.Expiration} block:{askOrder.Block} sighash:{askOrder.Sighash}"); } }); } else if (command[0].Equals("bidOrders", StringComparison.OrdinalIgnoreCase)) { filter(RpcHelper.creditCoinNamespace + RpcHelper.bidOrderPrefix, (string objid, byte[] protobuf) => { if (id == null || id != null && id.Equals(objid)) { BidOrder bidOrder = BidOrder.Parser.ParseFrom(protobuf); Console.WriteLine($"bidOrder({objid}) blockchain:{bidOrder.Blockchain} address:{bidOrder.Address} amount:{bidOrder.Amount} interest:{bidOrder.Interest} maturity:{bidOrder.Maturity} fee:{bidOrder.Fee} expiration:{bidOrder.Expiration} block:{bidOrder.Block} sighash:{bidOrder.Sighash}"); } }); } else if (command[0].Equals("offers", StringComparison.OrdinalIgnoreCase)) { filter(RpcHelper.creditCoinNamespace + RpcHelper.offerPrefix, (string objid, byte[] protobuf) => { if (id == null || id != null && id.Equals(objid)) { Offer offer = Offer.Parser.ParseFrom(protobuf); Console.WriteLine($"offer({objid}) blockchain:{offer.Blockchain} askOrder:{offer.AskOrder} bidOrder:{offer.BidOrder} expiration:{offer.Expiration} block:{offer.Block}"); } }); } else if (command[0].Equals("dealOrders", StringComparison.OrdinalIgnoreCase)) { filter(RpcHelper.creditCoinNamespace + RpcHelper.dealOrderPrefix, (string objid, byte[] protobuf) => { if (id == null || id != null && id.Equals(objid)) { DealOrder dealOrder = DealOrder.Parser.ParseFrom(protobuf); Console.WriteLine($"dealOrder({objid}) blockchain:{dealOrder.Blockchain} srcAddress:{dealOrder.SrcAddress} dstAddress:{dealOrder.DstAddress} amount:{dealOrder.Amount} interest:{dealOrder.Interest} maturity:{dealOrder.Maturity} fee:{dealOrder.Fee} expiration:{dealOrder.Expiration} block:{dealOrder.Block} loanTransfer:{(dealOrder.LoanTransfer.Equals(string.Empty) ? "*" : dealOrder.LoanTransfer)} repaymentTransfer:{(dealOrder.RepaymentTransfer.Equals(string.Empty) ? "*" : dealOrder.RepaymentTransfer)} lock:{(dealOrder.Lock.Equals(string.Empty) ? "*" : dealOrder.Lock)} sighash:{dealOrder.Sighash}"); } }); } else if (command[0].Equals("repaymentOrders", StringComparison.OrdinalIgnoreCase)) { filter(RpcHelper.creditCoinNamespace + RpcHelper.repaymentOrderPrefix, (string objid, byte[] protobuf) => { if (id == null || id != null && id.Equals(objid)) { RepaymentOrder repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); Console.WriteLine($"repaymentOrder({objid}) blockchain:{repaymentOrder.Blockchain} srcAddress:{repaymentOrder.SrcAddress} dstAddress:{repaymentOrder.DstAddress} amount:{repaymentOrder.Amount} expiration:{repaymentOrder.Expiration} block:{repaymentOrder.Block} deal:{repaymentOrder.Deal} previousOwner:{(repaymentOrder.PreviousOwner.Equals(string.Empty)? "*": repaymentOrder.PreviousOwner)} transfer:{(repaymentOrder.Transfer.Equals(string.Empty) ? "*" : repaymentOrder.Transfer)} sighash:{repaymentOrder.Sighash}"); } }); } } else if (action.Equals("show")) { bool success = true; BigInteger headIdx = GetHeadIdx(); if (command.Length <= 1) { throw new Exception("1 or more parametersd expected"); } string sighash; if (command[1].Equals("0")) { sighash = TxBuilder.getSighash(signer); } else { sighash = command[1]; } if (command[0].Equals("balance", StringComparison.OrdinalIgnoreCase)) { if (command.Length != 2) { throw new Exception("2 parametersd expected"); } string prefix = RpcHelper.creditCoinNamespace + RpcHelper.walletPrefix; string id = prefix + sighash; string amount = "0"; filter(prefix, (string objid, byte[] protobuf) => { Wallet wallet = Wallet.Parser.ParseFrom(protobuf); if (objid.Equals(id)) { amount = wallet.Amount; } }); Console.WriteLine($"{amount}"); } else if (command[0].Equals("address", StringComparison.OrdinalIgnoreCase)) { if (command.Length != 5) { throw new Exception("5 parametersd expected"); } var blockchain = command[2].ToLower(); var addr = command[3]; var network = command[4].ToLower(); filter(RpcHelper.creditCoinNamespace + RpcHelper.addressPrefix, (string objid, byte[] protobuf) => { Address address = Address.Parser.ParseFrom(protobuf); if (address.Sighash == sighash && address.Blockchain == blockchain && address.Value == addr && address.Network == network) { Console.WriteLine(objid); } }); } else if (command[0].Equals("matchingOrders", StringComparison.OrdinalIgnoreCase)) { if (command.Length != 2) { throw new Exception("2 parametersd expected"); } var askOrders = new Dictionary <string, AskOrder>(); filter(RpcHelper.creditCoinNamespace + RpcHelper.askOrderPrefix, (string objid, byte[] protobuf) => { AskOrder askOrder = AskOrder.Parser.ParseFrom(protobuf); BigInteger block; if (!BigInteger.TryParse(askOrder.Block, out block)) { throw new Exception("Invalid numerics"); } if (block + askOrder.Expiration > headIdx) { askOrders.Add(objid, askOrder); } }); var bidOrders = new Dictionary <string, BidOrder>(); filter(RpcHelper.creditCoinNamespace + RpcHelper.bidOrderPrefix, (string objid, byte[] protobuf) => { BidOrder bidOrder = BidOrder.Parser.ParseFrom(protobuf); BigInteger block; if (!BigInteger.TryParse(bidOrder.Block, out block)) { throw new Exception("Invalid numerics"); } if (block + bidOrder.Expiration > headIdx) { bidOrders.Add(objid, bidOrder); } }); match(sighash, askOrders, bidOrders); } else if (command[0].Equals("currentOffers", StringComparison.OrdinalIgnoreCase)) { if (command.Length != 2) { throw new Exception("2 parametersd expected"); } var bidOrders = new Dictionary <string, BidOrder>(); filter(RpcHelper.creditCoinNamespace + RpcHelper.bidOrderPrefix, (string objid, byte[] protobuf) => { BidOrder bidOrder = BidOrder.Parser.ParseFrom(protobuf); bidOrders.Add(objid, bidOrder); }); filter(RpcHelper.creditCoinNamespace + RpcHelper.offerPrefix, (string objid, byte[] protobuf) => { Offer offer = Offer.Parser.ParseFrom(protobuf); BidOrder bidOrder = bidOrders[offer.BidOrder]; if (bidOrder.Sighash == sighash) { BigInteger block; if (!BigInteger.TryParse(offer.Block, out block)) { throw new Exception("Invalid numerics"); } if (block + offer.Expiration > headIdx) { Console.WriteLine(objid); } } }); } else if (command[0].Equals("creditHistory", StringComparison.OrdinalIgnoreCase)) { if (command.Length != 2) { throw new Exception("2 parametersd expected"); } filterDeals(null, sighash, (string dealAddress, DealOrder dealOrder) => { var status = dealOrder.LoanTransfer.Equals(string.Empty) ? "NEW" : "COMPLETE"; if (!dealOrder.RepaymentTransfer.Equals(string.Empty)) { status = "CLOSED"; } Console.WriteLine($"status:{status}, amount:{dealOrder.Amount}, blockchain:{dealOrder.Blockchain}"); }); } else if (command[0].Equals("newDeals", StringComparison.OrdinalIgnoreCase)) { if (command.Length != 2) { throw new Exception("2 parametersd expected"); } filterDeals(sighash, null, (string dealAddress, DealOrder dealOrder) => { if (dealOrder.LoanTransfer.Equals(string.Empty)) { BigInteger block; if (!BigInteger.TryParse(dealOrder.Block, out block)) { throw new Exception("Invalid numerics"); } if (block + dealOrder.Expiration > headIdx) { Console.WriteLine(dealAddress); } } }); } else if (command[0].Equals("transfer", StringComparison.OrdinalIgnoreCase)) { if (command.Length != 3) { throw new Exception("3 parametersd expected"); } var orderId = command[2]; filter(RpcHelper.creditCoinNamespace + RpcHelper.transferPrefix, (string objid, byte[] protobuf) => { Transfer transfer = Transfer.Parser.ParseFrom(protobuf); if (transfer.Sighash == sighash && transfer.Order == orderId && !transfer.Processed) { Console.WriteLine(objid); } }); } else if (command[0].Equals("currentLoans", StringComparison.OrdinalIgnoreCase)) { if (command.Length != 2) { throw new Exception("2 parametersd expected"); } filterDeals(null, sighash, (string dealAddress, DealOrder dealOrder) => { if (!dealOrder.LoanTransfer.Equals(string.Empty) && dealOrder.RepaymentTransfer.Equals(string.Empty)) { Console.WriteLine(dealAddress); } }); } else if (command[0].Equals("newRepaymentOrders", StringComparison.OrdinalIgnoreCase)) { if (command.Length != 2) { throw new Exception("2 parametersd expected"); } var addresses = new Dictionary <string, Address>(); filter(RpcHelper.creditCoinNamespace + RpcHelper.addressPrefix, (string objid, byte[] protobuf) => { Address address = Address.Parser.ParseFrom(protobuf); addresses.Add(objid, address); }); var dealOrders = new Dictionary <string, DealOrder>(); filter(RpcHelper.creditCoinNamespace + RpcHelper.dealOrderPrefix, (string objid, byte[] protobuf) => { DealOrder dealOrder = DealOrder.Parser.ParseFrom(protobuf); dealOrders.Add(objid, dealOrder); }); filter(RpcHelper.creditCoinNamespace + RpcHelper.repaymentOrderPrefix, (string objid, byte[] protobuf) => { RepaymentOrder repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); DealOrder deal = dealOrders[repaymentOrder.Deal]; Address address = addresses[deal.SrcAddress]; if (repaymentOrder.Transfer.Equals(string.Empty) && repaymentOrder.PreviousOwner.Equals(string.Empty) && address.Sighash.Equals(sighash)) { BigInteger block; if (!BigInteger.TryParse(repaymentOrder.Block, out block)) { throw new Exception("Invalid numerics"); } if (block + repaymentOrder.Expiration > headIdx) { Console.WriteLine(objid); } } }); } else if (command[0].Equals("currentRepaymentOrders", StringComparison.OrdinalIgnoreCase)) { filter(RpcHelper.creditCoinNamespace + RpcHelper.repaymentOrderPrefix, (string objid, byte[] protobuf) => { RepaymentOrder repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); if (repaymentOrder.Transfer.Equals(string.Empty) && !repaymentOrder.PreviousOwner.Equals(string.Empty) && repaymentOrder.Sighash.Equals(sighash)) { Console.WriteLine(objid); } }); } else { Console.WriteLine("Invalid command " + command[0]); success = false; } if (success) { Console.WriteLine("Success"); } } else { var txBuilder = new TxBuilder(signer); if (action.Equals("creditcoin")) { string msg; var tx = txBuilder.BuildTx(command, out msg); if (tx == null) { Debug.Assert(msg != null); Console.WriteLine(msg); } else { Debug.Assert(msg == null); var content = new ByteArrayContent(tx); content.Headers.Add("Content-Type", "application/octet-stream"); Console.WriteLine(RpcHelper.CompleteBatch(httpClient, creditcoinUrl, "batches", content, txid)); } } else { var loader = new Loader <ICCClientPlugin>(); var msgs = new List <string>(); loader.Load(pluginFolder, msgs); foreach (var msg in msgs) { Console.WriteLine(msg); } ICCClientPlugin plugin = loader.Get(action); var pluginConfig = config.GetSection(action); if (plugin == null) { Console.WriteLine("Error: Unknown action " + action); } else { string msg; var settings = getSettings(); bool done = plugin.Run(txid, pluginConfig, httpClient, txBuilder, settings, progressId, pluginFolder, creditcoinUrl, command, out inProgress, out msg); if (done) { if (msg == null) { msg = "Success"; } Console.WriteLine(msg); } else { Console.WriteLine("Error: " + msg); } } } } if (!inProgress) { File.Delete(progress); } } catch (Exception x) { Console.WriteLine($"Error: unexpected failure - {x.Message}"); } }
public ContentResult Get() { return(Content((RpcHelper.Request <GetHeightResp>("getheight").Height - 1).ToString())); }
public bool Run(bool txid, IConfiguration cfg, HttpClient httpClient, ITxBuilder txBuilder, Dictionary <string, string> settings, string progressId, string pluginsFolder, string url, string[] command, out bool inProgress, out string msg) { Debug.Assert(command != null); if (command.Length < 2) { inProgress = false; msg = "invalid parameter count"; return(false); } bool erc20 = command[0].Equals("collectCoins", StringComparison.OrdinalIgnoreCase); if (erc20 || command[0].Equals("registerTransfer", StringComparison.OrdinalIgnoreCase)) { // ethereum RegisterTransfer gain orderId // ethereum CollectCoins amount string progress = Path.Combine(pluginsFolder, $"{name}_progress{progressId}.txt"); inProgress = false; if (erc20 && command.Length != 2 || !erc20 && command.Length != 3) { msg = "invalid parameter count"; return(false); } string gainString = "0"; string orderId = null; string amountString = null; if (erc20) { amountString = command[1]; } else { gainString = command[1]; orderId = command[2]; } string secret = cfg["secret"]; if (string.IsNullOrWhiteSpace(secret)) { msg = "ethereum.secret is not set"; return(false); } var ethereumPrivateKey = secret; #if DEBUG string confirmationsCount = cfg["confirmationsCount"]; if (int.TryParse(confirmationsCount, out int parsedCount)) { mConfirmationsExpected = parsedCount; } #endif string rpcUrl = cfg["rpc"]; if (string.IsNullOrWhiteSpace(rpcUrl)) { msg = "ethereum.rpc is not set"; return(false); } string ethSrcAddress = EthECKey.GetPublicAddress(ethereumPrivateKey); var web3 = new Nethereum.Web3.Web3(rpcUrl); string srcAddressId = null; string dstAddressId = null; if (!erc20) { var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{url}/state/{orderId}", out msg); if (protobuf == null) { msg = "failed to extract address data through RPC"; return(false); } if (orderId.StartsWith(RpcHelper.creditCoinNamespace + RpcHelper.dealOrderPrefix)) { var dealOrder = DealOrder.Parser.ParseFrom(protobuf); if (gainString.Equals("0")) { srcAddressId = dealOrder.SrcAddress; dstAddressId = dealOrder.DstAddress; } else { dstAddressId = dealOrder.SrcAddress; srcAddressId = dealOrder.DstAddress; } amountString = dealOrder.Amount; } else if (orderId.StartsWith(RpcHelper.creditCoinNamespace + RpcHelper.repaymentOrderPrefix)) { var repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); if (gainString.Equals("0")) { srcAddressId = repaymentOrder.SrcAddress; dstAddressId = repaymentOrder.DstAddress; } else { dstAddressId = repaymentOrder.SrcAddress; srcAddressId = repaymentOrder.DstAddress; } amountString = repaymentOrder.Amount; } else { msg = "unexpected referred order"; return(false); } } string payTxId; if (File.Exists(progress)) { Console.WriteLine("Found unfinished action, retrying..."); payTxId = File.ReadAllText(progress); } else { string ethDstAddress = null; if (!erc20) { var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{url}/state/{srcAddressId}", out msg); if (protobuf == null) { msg = "failed to extract address data through RPC"; return(false); } var srcAddress = Address.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{url}/state/{dstAddressId}", out msg); if (protobuf == null) { msg = "failed to extract address data through RPC"; return(false); } Address dstAddress = Address.Parser.ParseFrom(protobuf); if (!srcAddress.Blockchain.Equals(name) || !dstAddress.Blockchain.Equals(name)) { msg = $"ethereum RegisterTransfer can only transfer ether.\nThis source is registered for {srcAddress.Blockchain} and destination for {dstAddress.Blockchain}"; return(false); } ethDstAddress = dstAddress.Value; if (!ethSrcAddress.Equals(srcAddress.Value, StringComparison.OrdinalIgnoreCase)) { msg = "The deal is for a different client"; return(false); } } BigInteger transferAmount; if (!BigInteger.TryParse(amountString, out transferAmount) || transferAmount <= 0) { msg = "Invalid amount"; return(false); } BigInteger gain; if (!BigInteger.TryParse(gainString, out gain)) { msg = "Invalid amount"; return(false); } transferAmount = transferAmount + gain; if (transferAmount < 0) { msg = "Invalid amount"; return(false); } var txCount = web3.Eth.Transactions.GetTransactionCount.SendRequestAsync(ethSrcAddress).Result; TransactionSigner signer = new TransactionSigner(); HexBigInteger gasPrice; string gasPriceInGweiString = cfg["gasPriceInGwei"]; if (int.TryParse(gasPriceInGweiString, out int gasPriceOverride)) { gasPrice = new HexBigInteger(Nethereum.Util.UnitConversion.Convert.ToWei(gasPriceOverride, Nethereum.Util.UnitConversion.EthUnit.Gwei)); } else { gasPrice = web3.Eth.GasPrice.SendRequestAsync().Result; } Console.WriteLine("gasPrice: " + gasPrice.Value.ToString()); string to; string data; BigInteger amount; HexBigInteger gasLimit; if (erc20) { string creditcoinContract = cfg["creditcoinContract"]; string creditcoinContractAbi = cfg["creditcoinContractAbi"]; to = creditcoinContract; var contract = web3.Eth.GetContract(creditcoinContractAbi, creditcoinContract); var burn = contract.GetFunction("exchange"); var functionInput = new object[] { transferAmount, txBuilder.getSighash() }; data = burn.GetData(functionInput); gasLimit = burn.EstimateGasAsync(functionInput).Result; amount = 0; } else { gasLimit = web3.Eth.Transactions.EstimateGas.SendRequestAsync(new Nethereum.RPC.Eth.DTOs.CallInput(orderId, ethDstAddress, new Nethereum.Hex.HexTypes.HexBigInteger(transferAmount))).Result; Console.WriteLine("gasLimit: " + gasLimit.Value.ToString()); to = ethDstAddress; data = orderId; amount = transferAmount; } string txRaw = signer.SignTransaction(ethereumPrivateKey, to, amount, txCount, gasPrice, gasLimit, data); payTxId = web3.Eth.Transactions.SendRawTransaction.SendRequestAsync("0x" + txRaw).Result; Console.WriteLine("Ethereum Transaction ID: " + payTxId); File.WriteAllText(progress, $"{payTxId}"); } inProgress = true; while (true) { var receipt = web3.Eth.TransactionManager.TransactionReceiptService.PollForReceiptAsync(payTxId).Result; if (receipt.BlockNumber != null) { var blockNumber = web3.Eth.Blocks.GetBlockNumber.SendRequestAsync().Result; if (blockNumber.Value - receipt.BlockNumber.Value >= mConfirmationsExpected) { break; } } Thread.Sleep(1000); } File.Delete(progress); inProgress = false; if (erc20) { command = new string[] { command[0], ethSrcAddress, amountString, payTxId }; } else { command = new string[] { command[0], gainString, orderId, payTxId }; } var tx = txBuilder.BuildTx(command, out msg); if (tx == null) { return(false); } Debug.Assert(msg == null); var content = new ByteArrayContent(tx); content.Headers.Add("Content-Type", "application/octet-stream"); msg = RpcHelper.CompleteBatch(httpClient, url, "batches", content, txid); return(true); } else { inProgress = false; msg = "Unknown command: " + command[0]; return(false); } }
public void RpcGet() { string result = RpcHelper.GetAsync("https://www.baidu.com").Result; Assert.IsNotNull(result); }
public bool Run(IConfiguration cfg, HttpClient httpClient, ITxBuilder txBuilder, Dictionary <string, string> settings, string pluginsFolder, string url, string[] command, out bool inProgress, out string msg) { Debug.Assert(command != null); Debug.Assert(command.Length > 1); if (command[0].Equals("registerTransfer", StringComparison.OrdinalIgnoreCase)) { // ethereum RegisterTransfer registeredSourceId amount string progress = Path.Combine(pluginsFolder, $"{name}_progress.txt"); inProgress = false; Debug.Assert(command.Length == 3 || command.Length == 4); string registeredSourceId = command[1]; string amountString = command[2]; bool erc20 = command.Length == 4; string secret = cfg["secret"]; if (string.IsNullOrWhiteSpace(secret)) { msg = "ethereum.secret is not set"; return(false); } var ethereumPrivateKey = secret; // TODO disable confirmation count config for release build string confirmationsCount = cfg["confirmationsCount"]; if (string.IsNullOrWhiteSpace(confirmationsCount)) { msg = "ethereum.confirmationsCount is not set"; return(false); } if (!int.TryParse(confirmationsCount, out int confirmationsExpected)) { msg = "ethereum.confirmationsCount is not an int"; return(false); } string rpcUrl = cfg["rpc"]; if (string.IsNullOrWhiteSpace(rpcUrl)) { msg = "ethereum.rpc is not set"; return(false); } var web3 = new Nethereum.Web3.Web3(rpcUrl); string payTxId; BigInteger fee; if (File.Exists(progress)) { Console.WriteLine("Found unfinished action, retrying..."); var data = File.ReadAllText(progress).Split(':'); if (data.Length != 2) { msg = "Invalid progress data"; return(false); } payTxId = data[0]; if (!BigInteger.TryParse(data[1], out fee)) { msg = "Invalid progress data"; return(false); } } else { var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{url}/state/{registeredSourceId}", out msg); if (protobuf == null) { return(false); } var address = Address.Parser.ParseFrom(protobuf); string destinationAddress; if (!settings.TryGetValue("sawtooth.escrow." + name, out destinationAddress)) { msg = "Escrow not found for " + name; return(false); } BigInteger transferAmount; if (!BigInteger.TryParse(amountString, out transferAmount) || transferAmount <= 0) { msg = "Invalid amount"; return(false); } if (!address.Blockchain.Equals(name)) { msg = $"ethereum RegisterTransfer can only transfer ether.\nThis source is registered for {address.Blockchain}"; return(false); } string sourceAddress = EthECKey.GetPublicAddress(ethereumPrivateKey); if (!sourceAddress.Equals(address.Address_, StringComparison.OrdinalIgnoreCase)) { msg = "The deal is for a different client"; return(false); } var txCount = web3.Eth.Transactions.GetTransactionCount.SendRequestAsync(sourceAddress).Result; TransactionSigner signer = new TransactionSigner(); // TODO maybe add a y/n choice for user to decline or configurable gas price override var gasPrice = web3.Eth.GasPrice.SendRequestAsync().Result; Console.WriteLine("gasPrice: " + gasPrice.Value.ToString()); string to; string data; BigInteger amount; HexBigInteger gasLimit; if (erc20) { string creditcoinContract = cfg["creditcoinContract"]; string creditcoinContractAbi = cfg["creditcoinContractAbi"]; to = creditcoinContract; var contract = web3.Eth.GetContract(creditcoinContractAbi, creditcoinContract); var burn = contract.GetFunction("exchange"); var functionInput = new object[] { transferAmount, address.Sighash }; data = burn.GetData(functionInput); gasLimit = burn.EstimateGasAsync(functionInput).Result; fee = gasLimit.Value * gasPrice.Value; amount = 0; } else { gasLimit = web3.Eth.Transactions.EstimateGas.SendRequestAsync(new Nethereum.RPC.Eth.DTOs.CallInput(registeredSourceId, destinationAddress, new Nethereum.Hex.HexTypes.HexBigInteger(transferAmount))).Result; Console.WriteLine("gasLimit: " + gasLimit.Value.ToString()); fee = gasLimit.Value * gasPrice.Value; to = destinationAddress; data = address.Sighash; amount = transferAmount + fee; } string txRaw = signer.SignTransaction(ethereumPrivateKey, to, amount, txCount, gasPrice, gasLimit, data); payTxId = web3.Eth.Transactions.SendRawTransaction.SendRequestAsync("0x" + txRaw).Result; Console.WriteLine("Ethereum Transaction ID: " + payTxId); File.WriteAllText(progress, $"{payTxId}:{fee.ToString()}"); } inProgress = true; while (true) { var receipt = web3.Eth.TransactionManager.TransactionReceiptService.PollForReceiptAsync(payTxId).Result; if (receipt.BlockNumber != null) { var blockNumber = web3.Eth.Blocks.GetBlockNumber.SendRequestAsync().Result; if (blockNumber.Value - receipt.BlockNumber.Value >= confirmationsExpected) { break; } } Thread.Sleep(1000); } File.Delete(progress); inProgress = false; command = new string[] { command[0], registeredSourceId, amountString, erc20 ? "0" : fee.ToString(), payTxId, erc20 ? "creditcoin" : "1" }; var tx = txBuilder.BuildTx(command, out msg); if (tx == null) { return(false); } Debug.Assert(msg == null); var content = new ByteArrayContent(tx); content.Headers.Add("Content-Type", "application/octet-stream"); msg = RpcHelper.CompleteBatch(httpClient, $"{url}/batches", content); return(true); } else { inProgress = false; msg = "Unknown command: " + command[0]; return(false); } }
public async void RpcGetTest() { string result = await RpcHelper.GetAsync("https://www.baidu.com"); Console.WriteLine(result); }
static async void RpcPostTest() { ApiResponseBase <User> response = await RpcHelper.PostAsync <LoginDto, ApiResponseBase <User> >("http://192.168.6.191:8086/api/Login/Login ", new LoginDto { LoginName = "hqmcq", LoginPwd = "123456" }); Console.WriteLine(response.ToJson()); }
public bool Run(IConfiguration cfg, string signerHexStr, string[] command, out string msg) { Debug.Assert(command != null); Debug.Assert(command.Length > 0); if (command[0].Equals("verify")) { Debug.Assert(command.Length == 7); string txId = command[1]; string destinationAdressString = command[2]; string destinationAmount = command[3]; string sighash = command[4]; string sourceAddressString = command[5]; string networkId = command[6]; string rpcAddress = cfg["rpc"]; if (string.IsNullOrWhiteSpace(rpcAddress)) { msg = "bitcoin.rpc is not set"; return(false); } string credential = cfg["credential"]; if (string.IsNullOrWhiteSpace(credential)) { msg = "bitcoin.credential is not set"; return(false); } // TODO disable confirmation customization for release build string confirmationsCount = cfg["confirmationsCount"]; if (string.IsNullOrWhiteSpace(confirmationsCount)) { msg = "bitcoin.confirmationsCount is not set"; return(false); } var confirmationsExpected = 1; if (!int.TryParse(confirmationsCount, out confirmationsExpected)) { msg = "bitcoin.confirmationsCount is not an int"; return(false); } Network network = ((networkId == "1") ? Network.Main : Network.TestNet); var rpcClient = new RPCClient(credential, new Uri(rpcAddress), network); if (!uint256.TryParse(txId, out var transactionId)) { msg = "Invalid transaction: transaction ID invalid"; return(false); } var transactionInfoResponse = rpcClient.GetRawTransactionInfo(transactionId); if (transactionInfoResponse.Confirmations < confirmationsExpected) { msg = "Invalid transaction: not enough confirmations"; return(false); } if (transactionInfoResponse.Transaction.Outputs.Count < 2 || transactionInfoResponse.Transaction.Outputs.Count > 3) { msg = "Invalid transaction: unexpected amount of output"; return(false); } var destinationAddress = BitcoinAddress.Create(destinationAdressString, network); var outputCoins = transactionInfoResponse.Transaction.Outputs.AsCoins(); var paymentCoin = outputCoins.SingleOrDefault(oc => oc.ScriptPubKey == destinationAddress.ScriptPubKey); if (paymentCoin == null) { msg = "Invalid transaction: wrong destinationAdressString"; return(false); } if (paymentCoin.TxOut.Value.Satoshi.ToString() != destinationAmount.ToString()) { msg = "Invalid transaction: wrong amount"; return(false); } var bytes = Encoding.UTF8.GetBytes(sighash); var nullDataCoin = outputCoins.SingleOrDefault(oc => oc.ScriptPubKey == TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes)); if (nullDataCoin == null) { msg = "Invalid transaction: wrong sighash"; return(false); } if (nullDataCoin.TxOut.Value.CompareTo(Money.Zero) != 0) { msg = "Invalid transaction: expecting a message"; return(false); } var sourceAddress = BitcoinAddress.Create(sourceAddressString, network); var input = transactionInfoResponse.Transaction.Inputs[0]; if (!Script.VerifyScript(input.ScriptSig, sourceAddress.ScriptPubKey, transactionInfoResponse.Transaction, 0)) { msg = "Invalid transaction: wrong sourceAddressString"; return(false); } msg = null; return(true); } else if (command[0].Equals("unlock")) { var ccSigner = new Signer(RpcHelper.HexToBytes(signerHexStr)); var txBuilder = new TxBuilder(ccSigner); Debug.Assert(command.Length == 5); string kind = command[1]; string addressFromString = command[2]; HttpClient httpClient = new HttpClient(); string txFrom; string amountString; string feeString; string addressToString; string networkId; string ccCommand; if (kind.Equals("funds")) { string dealOrderId = command[3]; string addressToUnlockFundsTo = command[4]; var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{dealOrderId}", out msg); if (protobuf == null) { return(false); } var dealOrder = DealOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{dealOrder.AskOrderId}", out msg); if (protobuf == null) { return(false); } var askOrder = AskOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{askOrder.TransferId}", out msg); if (protobuf == null) { return(false); } var transfer = Transfer.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{addressToUnlockFundsTo}", out msg); if (protobuf == null) { return(false); } var address = Address.Parser.ParseFrom(protobuf); if (!askOrder.Sighash.Equals(address.Sighash)) { msg = "The adress doesn't match the ask order"; return(false); } txFrom = transfer.Txid; amountString = transfer.Amount; feeString = transfer.Fee; addressToString = address.Address_; networkId = transfer.Network; ccCommand = "UnlockFunds"; } else if (kind.Equals("collateral")) { string repaymentOrderId = command[3]; string addressToUnlockCollateralsTo = command[4]; var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{repaymentOrderId}", out msg); if (protobuf == null) { return(false); } var repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{repaymentOrder.DealId}", out msg); if (protobuf == null) { return(false); } var dealOrder = DealOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{dealOrder.AskOrderId}", out msg); if (protobuf == null) { return(false); } var askOrder = AskOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{askOrder.TransferId}", out msg); if (protobuf == null) { return(false); } var transfer = Transfer.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{addressToUnlockCollateralsTo}", out msg); if (protobuf == null) { return(false); } var address = Address.Parser.ParseFrom(protobuf); if (!askOrder.Sighash.Equals(address.Sighash)) { msg = "The adress doesn't match the ask order"; return(false); } txFrom = transfer.Txid; amountString = transfer.Amount; feeString = transfer.Fee; addressToString = address.Address_; networkId = transfer.Network; ccCommand = "UnlockCollateral"; } else { msg = "unknown unlock kind"; return(false); } string rpcAddress = cfg["rpc"]; if (string.IsNullOrWhiteSpace(rpcAddress)) { msg = "bitcoin.rpc is not set"; return(false); } string credential = cfg["credential"]; if (string.IsNullOrWhiteSpace(credential)) { msg = "bitcoin.credential is not set"; return(false); } string secret = cfg["secret"]; if (string.IsNullOrWhiteSpace(secret)) { msg = "bitcoin.secret is not set"; return(false); } // TODO disable confirmation config for release build string confirmationsCount = cfg["confirmationsCount"]; if (string.IsNullOrWhiteSpace(confirmationsCount)) { msg = "bitcoin.confirmationsCount is not set"; return(false); } var confirmationsExpected = 1; if (!int.TryParse(confirmationsCount, out confirmationsExpected)) { msg = "bitcoin.confirmationsCount is not an int"; return(false); } Network network = ((networkId == "1") ? Network.Main : Network.TestNet); var rpcClient = new RPCClient(credential, new Uri(rpcAddress), network); var transactionId = uint256.Parse(txFrom); var bitcoinPrivateKey = new BitcoinSecret(secret); if (bitcoinPrivateKey.Network != network) { msg = "Mismatching networks"; return(false); } var transactionResponse = rpcClient.GetRawTransaction(transactionId); Money transferAmount; if (!Money.TryParse(amountString, out transferAmount) || transferAmount <= 0) { msg = "Invalid amount"; return(false); } if (!int.TryParse(feeString, out int fee)) { msg = "bitcoin.fee is not an int"; return(false); } var receivedCoins = transactionResponse.Outputs.AsCoins(); OutPoint outPointToSpend = null; TxOut outTxToSpend = null; Money amount = null; foreach (var coin in receivedCoins) { if (coin.TxOut.ScriptPubKey == bitcoinPrivateKey.ScriptPubKey) { outPointToSpend = coin.Outpoint; outTxToSpend = coin.TxOut; amount = (Money)coin.Amount; if (amount.CompareTo(transferAmount + fee * 2) < 0) { msg = $"Invalid transaction - needed: {transferAmount}, has: {amount.ToString()}"; return(false); } break; } } if (outPointToSpend == null) { msg = "Invalid transaction - no outputs that the client can spend"; return(false); } var addressFrom = BitcoinAddress.Create(addressFromString, network); var addressTo = BitcoinAddress.Create(addressToString, network); var bitcoinTransactionBuilder = new TransactionBuilder(); var transaction = bitcoinTransactionBuilder .AddCoins(new Coin(outPointToSpend, outTxToSpend)) .AddKeys(bitcoinPrivateKey) .Send(addressTo.ScriptPubKey, transferAmount) .SendFees(new Money(fee, MoneyUnit.Satoshi)) .SetChange(addressFrom.ScriptPubKey) .BuildTransaction(true); if (!bitcoinTransactionBuilder.Verify(transaction)) { msg = "failed verify transaction"; return(false); } uint256 payTxId; try { payTxId = rpcClient.SendRawTransaction(transaction); } catch (RPCException e) { msg = $"failed to broadcast - error: {e.RPCCode}, reason: {e.RPCCodeMessage}"; return(false); } if (payTxId == null) { msg = "failed to broadcast - unknown error"; return(false); } while (true) //TODO: this may lock for a very long time or forever, fix --------------------------------------------------------------------------------------------------------------------------------------- { var transactionInfo = rpcClient.GetRawTransactionInfo(payTxId); if (transactionInfo != null && transactionInfo.BlockHash != null && transactionInfo.Confirmations >= confirmationsExpected) { break; } Thread.Sleep(1000); } command = new string[] { ccCommand, command[3], command[4] }; var tx = txBuilder.BuildTx(command, out msg); Debug.Assert(tx != null); Debug.Assert(msg == null); var content = new ByteArrayContent(tx); content.Headers.Add("Content-Type", "application/octet-stream"); msg = RpcHelper.CompleteBatch(httpClient, $"{creditcoinUrl}/batches", content); } msg = "Unknown command: " + command[0]; return(false); }
private const int SKIP_TO_GET_60 = 512 / 8 * 2 - 60; // 512 - hash size, 8 - bits in byte, 2 - hex digits for byte, 60 - merkle address length (70) without namespace length (6) and prexix length (4) static void Main(string[] args) { try { IConfiguration config = new ConfigurationBuilder() .AddJsonFile("appsettings.json", true, false) #if DEBUG .AddJsonFile("appsettings.dev.json", true, false) #endif .Build(); string signerHexStr = config["signer"]; if (string.IsNullOrWhiteSpace(signerHexStr)) { Console.WriteLine("Signer is not configured"); return; } var signer = new Signer(RpcHelper.HexToBytes(signerHexStr)); string creditcoinRestApiURL = config["creditcoinRestApiURL"]; if (!string.IsNullOrWhiteSpace(creditcoinRestApiURL)) { creditcoinUrl = creditcoinRestApiURL; } string root = Directory.GetCurrentDirectory(); string folder = TxBuilder.GetPluginsFolder(root); if (folder == null) { Console.WriteLine("plugins subfolder not found"); return; } string progress = Path.Combine(folder, "progress.txt"); string action; string[] command; if (File.Exists(progress)) { Console.WriteLine("Found unfinished action, retrying..."); args = File.ReadAllText(progress).Split(); } else { File.WriteAllText(progress, string.Join(' ', args)); } action = args[0].ToLower(); command = args.Skip(1).ToArray(); var txBuilder = new TxBuilder(signer); var settings = new Dictionary <string, string>(); filter(settingNamespace, (string address, byte[] protobuf) => { Setting setting = Setting.Parser.ParseFrom(protobuf); foreach (var entry in setting.Entries) { settings.Add(entry.Key, entry.Value); } }); bool inProgress = false; // API: // list Settings|Wallets|Addresses|Transfers|AskOrders|BidOrders|DealOrders|RepaymentOrders| // list Balance // list Sighash // list Address blockchain address // list UnusedTransfers addressId amount // list MatchingOrders // list CreditHistory sighash // list NewDeals // list RunningDeals // list NewRepaymentOrders // creditcoin AddFunds amount sighash (creditcoin AddFunds 1000000 16de574ac8ac3067977df056ecff51345672d25d528303b3555ab2aa4cd5) // AddFunds only works for a registered signer // creditcoin SendFunds amount sighash (creditcoin SendFunds 200000 8704a4f77befea5c8082d414f98dc16e4ba82a0898422d031f41693260a0) // creditcoin RegisterAddress blockchain address (creditcoin RegisterAddress bitcoin <BITCOIN-ADDRESS>) // creditcoin AddAskOrder blockchain amount interest blockchain collateral fee expiration capitalLockTransferId (creditcoin AddAskOrder bitcoin 1000000 10 bitcoin 50 100 1565386152 <TRANSFER-ID>) // creditcoin AddBidOrder blockchain amount interest blockchain collateral fee expiration (creditcoin AddAskOrder bitcoin 1000000 10 bitcoin 50 100 1565386152) // creditcoin AddDealOrder askOrderId bidOrderId // creditcoin CompleteDealOrder dealOrderId collateralLockTransferId // creditcoin AddRepaymentOrder dealOrderId // creditcoin CompleteRepaymentOrder repaymentOrderId transferId // creditcoin CollectCoins transferId // <blockchain> RegisterTransfer ... // bitcoin RegisterTransfer registeredAddressId amount sourceTxId // ethereum RegisterTransfer registeredAddressId amount [erc20] // unlock Funds dealOrderId addressToUnlockFundsTo // unlock Collateral repaymentOrderId addressToUnlockCollateralTo if (action.Equals("unlock")) { string externalGatewayAddress; if (!settings.TryGetValue("sawtooth.validator.gateway", out externalGatewayAddress)) { Console.WriteLine("Error: external gateway is not configured"); } else { if (!externalGatewayAddress.StartsWith("tcp://")) { externalGatewayAddress = "tcp://" + externalGatewayAddress; } bool success = true; switch (command[0].ToLower()) { case "funds": Debug.Assert(command.Length == 3); { var dealOrderId = command[1].ToLower(); var addressToUnlockFundsTo = command[2].ToLower(); string msg; string blockchain = null; var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{dealOrderId}", out msg); if (protobuf != null) { var dealOrder = DealOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{dealOrder.AskOrderId}", out msg); if (protobuf != null) { var askOrder = AskOrder.Parser.ParseFrom(protobuf); blockchain = askOrder.Blockchain; } } if (blockchain == null) { success = false; Console.WriteLine("Error: " + msg); } else { string escrow; if (!settings.TryGetValue("sawtooth.escrow." + blockchain, out escrow)) { success = false; Console.WriteLine("Error: escrow is not configured for " + blockchain); } else { using (var socket = new RequestSocket()) { socket.Connect(externalGatewayAddress); var request = $"{blockchain} unlock funds {escrow} {dealOrderId} {addressToUnlockFundsTo}"; socket.SendFrame(request); string response = socket.ReceiveFrameString(); if (response != "good") { success = false; Console.WriteLine("Error: failed to execute the global gateway command"); } } } } } break; case "collateral": Debug.Assert(command.Length == 3); { var repaymentOrderId = command[1].ToLower(); var addressToUnlockCollateralsTo = command[2].ToLower(); string msg; string blockchain = null; var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{repaymentOrderId}", out msg); if (protobuf != null) { var repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{repaymentOrder.DealId}", out msg); if (protobuf != null) { var dealOrder = DealOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{dealOrder.AskOrderId}", out msg); if (protobuf != null) { var askOrder = AskOrder.Parser.ParseFrom(protobuf); blockchain = askOrder.Blockchain; } } } if (blockchain == null) { success = false; Console.WriteLine("Error: " + msg); } else { string escrow; if (!settings.TryGetValue("sawtooth.escrow." + blockchain, out escrow)) { success = false; Console.WriteLine("Error: escrow is not configured for " + blockchain); } else { using (var socket = new RequestSocket()) { socket.Connect(externalGatewayAddress); var request = $"{blockchain} unlock collateral {escrow} {repaymentOrderId} {addressToUnlockCollateralsTo}"; socket.SendFrame(request); string response = socket.ReceiveFrameString(); if (response != "good") { success = false; Console.WriteLine("Error: failed to execute the global gateway command"); } } } } } break; } if (success) { Console.WriteLine("Success"); } } } else if (action.Equals("list")) { bool success = true; if (command[0].Equals("settings", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); filter(settingNamespace, (string objid, byte[] protobuf) => { Setting setting = Setting.Parser.ParseFrom(protobuf); foreach (var entry in setting.Entries) { Console.WriteLine($"{entry.Key}: {entry.Value}"); } }); } else if (command[0].Equals("wallets", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); filter(creditCoinNamespace + walletPrefix, (string objid, byte[] protobuf) => { Wallet wallet = Wallet.Parser.ParseFrom(protobuf); Console.WriteLine($"wallet({objid}) amount:{wallet.Amount}"); }); } else if (command[0].Equals("balance", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); { string sighash = sha256(signer.GetPublicKey().ToHexString()); string prefix = creditCoinNamespace + walletPrefix; string id = prefix + sighash; filter(prefix, (string objid, byte[] protobuf) => { Wallet wallet = Wallet.Parser.ParseFrom(protobuf); if (objid.Equals(id)) { Console.WriteLine($"balance for {sighash} is {wallet.Amount}"); } }); } } else if (command[0].Equals("addresses", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); filter(creditCoinNamespace + addressPrefix, (string objid, byte[] protobuf) => { Address address = Address.Parser.ParseFrom(protobuf); Console.WriteLine($"address({objid}) sighash:{address.Sighash}, blockchain: {address.Blockchain}, address:{address.Address_}"); }); } else if (command[0].Equals("transfers", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); filter(creditCoinNamespace + transferPrefix, (string objid, byte[] protobuf) => { Transfer transfer = Transfer.Parser.ParseFrom(protobuf); Console.WriteLine($"transfer({objid}) sighash:{transfer.Sighash}, blockchain: {transfer.Blockchain}, amount:{transfer.Amount}, fee:{transfer.Fee}, txid:{transfer.Txid}"); }); } else if (command[0].Equals("askOrders", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); filter(creditCoinNamespace + askOrderPrefix, (string objid, byte[] protobuf) => { AskOrder askOrder = AskOrder.Parser.ParseFrom(protobuf); Console.WriteLine($"askOrder({objid}) sighash:{askOrder.Sighash}, blockchain: {askOrder.Blockchain}, amount:{askOrder.Amount}, interest:{askOrder.Interest}, collateralBlockchain:{askOrder.CollateralBlockchain}, collateral:{askOrder.Collateral}, fee:{askOrder.Fee}, expiration:{askOrder.Expiration}, transferId:{askOrder.TransferId}"); }); } else if (command[0].Equals("bidOrders", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); filter(creditCoinNamespace + bidOrderPrefix, (string objid, byte[] protobuf) => { BidOrder bidOrder = BidOrder.Parser.ParseFrom(protobuf); Console.WriteLine($"bidOrder({objid}) sighash:{bidOrder.Sighash}, blockchain: {bidOrder.Blockchain}, amount:{bidOrder.Amount}, interest:{bidOrder.Interest}, collateralBlockchain:{bidOrder.CollateralBlockchain}, collateral:{bidOrder.Collateral}, fee:{bidOrder.Fee}, expiration:{bidOrder.Expiration}"); }); } else if (command[0].Equals("dealOrders", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); filter(creditCoinNamespace + dealOrderPrefix, (string objid, byte[] protobuf) => { DealOrder dealOrder = DealOrder.Parser.ParseFrom(protobuf); Console.WriteLine($"dealOrder({objid}) askOrderId:{dealOrder.AskOrderId}, bidOrderId: {dealOrder.BidOrderId}, collateralTransferId:{dealOrder.CollateralTransferId}"); }); } else if (command[0].Equals("repaymentOrders", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); filter(creditCoinNamespace + repaymentOrderPrefix, (string objid, byte[] protobuf) => { RepaymentOrder repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); Console.WriteLine($"repaymentOrder({objid}) dealId:{repaymentOrder.DealId}, transferId:{repaymentOrder.TransferId}"); }); } else if (command[0].Equals("sighash", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine(sha256(signer.GetPublicKey().ToHexString())); } else if (command[0].Equals("address", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 3); var blockchain = command[1].ToLower(); var addr = command[2]; filter(creditCoinNamespace + addressPrefix, (string objid, byte[] protobuf) => { Address address = Address.Parser.ParseFrom(protobuf); if (address.Blockchain == blockchain && address.Address_ == addr) { Console.WriteLine(objid); } }); } else if (command[0].Equals("unusedTransfers", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 3); var addressId = command[1]; var amount = command[2]; var sighash = sha256(signer.GetPublicKey().ToHexString()); Address address = null; filter(creditCoinNamespace + addressPrefix, (string objid, byte[] protobuf) => { if (objid == addressId) { address = Address.Parser.ParseFrom(protobuf); } }); if (address == null) { Console.WriteLine("Invalid command " + command[0]); success = false; } else { filter(creditCoinNamespace + transferPrefix, (string objid, byte[] protobuf) => { Transfer transfer = Transfer.Parser.ParseFrom(protobuf); if (transfer.Sighash == sighash && transfer.Orderid.Equals(string.Empty) && transfer.Blockchain == address.Blockchain && transfer.Amount == amount) { Console.WriteLine(objid); } }); } } else if (command[0].Equals("matchingOrders", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); var askOrders = new Dictionary <string, AskOrder>(); filter(creditCoinNamespace + askOrderPrefix, (string objid, byte[] protobuf) => { AskOrder askOrder = AskOrder.Parser.ParseFrom(protobuf); askOrders.Add(objid, askOrder); }); var bidOrders = new Dictionary <string, BidOrder>(); filter(creditCoinNamespace + bidOrderPrefix, (string objid, byte[] protobuf) => { BidOrder bidOrder = BidOrder.Parser.ParseFrom(protobuf); bidOrders.Add(objid, bidOrder); }); match(signer, askOrders, bidOrders); } else if (command[0].Equals("creditHistory", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 2); var fundraiser = command[1].ToLower(); filterDeals(null, fundraiser, (string dealAddress, DealOrder dealOrder, AskOrder askOrder, BidOrder bidOrder) => { Debug.Assert(askOrder == null); var status = dealOrder.CollateralTransferId.Equals(string.Empty) ? "INCOMPLETE" : "COMPLETE"; if (!dealOrder.UnlockCollateralDestinationAddressId.Equals(string.Empty)) { status = "CLOSED"; } else if (!dealOrder.UnlockFundsDestinationAddressId.Equals(string.Empty)) { status = "ACTIVE"; } Console.WriteLine($"status: {status}, amount:{bidOrder.Amount}, blockchain: {bidOrder.Blockchain}, collateral:{bidOrder.Collateral}"); }); } else if (command[0].Equals("newDeals", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); var fundraiser = sha256(signer.GetPublicKey().ToHexString()); filterDeals(null, fundraiser, (string dealAddress, DealOrder dealOrder, AskOrder askOrder, BidOrder bidOrder) => { Debug.Assert(askOrder == null); if (dealOrder.CollateralTransferId.Equals(string.Empty)) { Console.WriteLine(dealAddress); } }); } else if (command[0].Equals("runningDeals", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); var investor = sha256(signer.GetPublicKey().ToHexString()); var deals = new List <string>(); filterDeals(investor, null, (string dealAddress, DealOrder dealOrder, AskOrder askOrder, BidOrder bidOrder) => { Debug.Assert(bidOrder == null); if (!dealOrder.CollateralTransferId.Equals(string.Empty) && dealOrder.UnlockCollateralDestinationAddressId.Equals(string.Empty)) { deals.Add(dealAddress); } }); filter(creditCoinNamespace + repaymentOrderPrefix, (string objid, byte[] protobuf) => { RepaymentOrder repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); if (deals.SingleOrDefault(x => x.Equals(repaymentOrder.DealId)) != null) { deals.Remove(repaymentOrder.DealId); } }); foreach (var deal in deals) { Console.WriteLine(deal); } } else if (command[0].Equals("newRepaymentOrders", StringComparison.OrdinalIgnoreCase)) { Debug.Assert(command.Length == 1); var fundraiser = sha256(signer.GetPublicKey().ToHexString()); var deals = new List <string>(); filterDeals(null, fundraiser, (string dealAddress, DealOrder dealOrder, AskOrder askOrder, BidOrder bidOrder) => { Debug.Assert(askOrder == null); if (!dealOrder.CollateralTransferId.Equals(string.Empty) && dealOrder.UnlockCollateralDestinationAddressId.Equals(string.Empty)) { deals.Add(dealAddress); } }); filter(creditCoinNamespace + repaymentOrderPrefix, (string objid, byte[] protobuf) => { RepaymentOrder repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); if (repaymentOrder.TransferId.Equals(string.Empty) && deals.SingleOrDefault(x => x.Equals(repaymentOrder.DealId)) != null) { Console.WriteLine(objid); } }); } else { Console.WriteLine("Invalid command " + command[0]); success = false; } if (success) { Console.WriteLine("Success"); } } else if (action.Equals("creditcoin")) { string msg; var tx = txBuilder.BuildTx(command, out msg); if (tx == null) { Debug.Assert(msg != null); Console.WriteLine(msg); } else { Debug.Assert(msg == null); var content = new ByteArrayContent(tx); content.Headers.Add("Content-Type", "application/octet-stream"); Console.WriteLine(RpcHelper.CompleteBatch(httpClient, $"{creditcoinUrl}/batches", content)); } } else { var loader = new Loader <ICCClientPlugin>(); var msgs = new List <string>(); loader.Load(folder, msgs); foreach (var msg in msgs) { Console.WriteLine(msg); } ICCClientPlugin plugin = loader.Get(action); var pluginConfig = config.GetSection(action); if (plugin == null) { Console.WriteLine("Error: Unknown action " + action); } else { string msg; bool done = plugin.Run(pluginConfig, httpClient, txBuilder, settings, folder, creditcoinUrl, command, out inProgress, out msg); if (done) { if (msg == null) { msg = "Success"; } Console.WriteLine(msg); } else { Console.WriteLine("Error: " + msg); } } } if (!inProgress) { File.Delete(progress); } } catch (Exception x) { Console.WriteLine($"Error: unexpected failure - {x.Message}"); } }
public JsonResult Index() { var stats = RpcHelper.RequestJson <NetworkStats>("getlastblockheader").result.block_header; return(new JsonResult(stats)); }
public bool Run(IConfiguration cfg, string signerHexStr, string[] command, out string msg) { Debug.Assert(command != null); Debug.Assert(command.Length > 0); if (command[0].Equals("verify")) { Debug.Assert(command.Length == 7); string txId = command[1]; string destinationAddressString = command[2]; string destinationAmount = command[3]; string sighash = command[4]; string sourceAddressString = command[5]; string networkId = command[6]; // TODO disable confirmation count config for release build string confirmationsCount = cfg["confirmationsCount"]; if (string.IsNullOrWhiteSpace(confirmationsCount)) { msg = "ethereum.confirmationsCount is not set"; return(false); } if (!int.TryParse(confirmationsCount, out int confirmationsExpected)) { msg = "ethereum.confirmationsCount is not an int"; return(false); } string rpcUrl = cfg["rpc"]; if (string.IsNullOrWhiteSpace(rpcUrl)) { msg = "ethereum.rpc is not set"; return(false); } var web3 = new Nethereum.Web3.Web3(rpcUrl); var tx = web3.Eth.Transactions.GetTransactionByHash.SendRequestAsync(txId).Result; int confirmations = 0; if (tx.BlockNumber != null) { var blockNumber = web3.Eth.Blocks.GetBlockNumber.SendRequestAsync().Result; confirmations = (int)(blockNumber.Value - tx.BlockNumber.Value); } if (confirmations < confirmationsExpected) { msg = "Invalid transaction: not enough confirmations"; return(false); } if (!sourceAddressString.Equals(tx.From, System.StringComparison.OrdinalIgnoreCase)) { msg = "Invalid transaction: wrong sourceAddressString"; return(false); } if (networkId.Equals("creditcoin")) { string creditcoinContract = cfg["creditcoinContract"]; if (string.IsNullOrWhiteSpace(creditcoinContract)) { msg = "ethereum.creditcoinContract is not set"; return(false); } string creditcoinContractAbi = cfg["creditcoinContractAbi"]; if (string.IsNullOrWhiteSpace(creditcoinContractAbi)) { msg = "ethereum.creditcoinContractAbi is not set"; return(false); } var contract = web3.Eth.GetContract(creditcoinContractAbi, creditcoinContract); var burn = contract.GetFunction("exchange"); var inputs = burn.DecodeInput(tx.Input); Debug.Assert(inputs.Count == 2); var value = inputs[0].Result.ToString(); if (destinationAmount != value) { msg = "Invalid transaction: wrong amount"; return(false); } var tag = inputs[1].Result.ToString(); if (!tag.Equals(sighash)) { msg = "Invalid transaction: wrong sighash"; return(false); } } else { if (destinationAmount != tx.Value.Value.ToString()) { msg = "Invalid transaction: wrong amount"; return(false); } if (tx.Input == null) { msg = "Invalid transaction: expecting data"; return(false); } if (!sighash.StartsWith("0x")) { sighash = "0x" + sighash; } if (!tx.Input.Equals(sighash, System.StringComparison.OrdinalIgnoreCase)) { msg = "Invalid transaction: wrong sighash"; return(false); } if (!tx.To.Equals(destinationAddressString, System.StringComparison.OrdinalIgnoreCase)) { msg = "Invalid transaction: wrong destinationAddressString"; return(false); } } msg = null; return(true); } else if (command[0].Equals("unlock")) { var ccSigner = new Signer(RpcHelper.HexToBytes(signerHexStr)); var txBuilder = new TxBuilder(ccSigner); Debug.Assert(command.Length == 5); string kind = command[1]; string addressFromString = command[2]; HttpClient httpClient = new HttpClient(); string txFrom; string amountString; string feeString; string addressToString; string networkId; string ccCommand; if (kind.Equals("funds")) { string dealOrderId = command[3]; string addressToUnlockFundsTo = command[4]; var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{dealOrderId}", out msg); if (protobuf == null) { return(false); } var dealOrder = DealOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{dealOrder.AskOrderId}", out msg); if (protobuf == null) { return(false); } var askOrder = AskOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{askOrder.TransferId}", out msg); if (protobuf == null) { return(false); } var transfer = Transfer.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{addressToUnlockFundsTo}", out msg); if (protobuf == null) { return(false); } var address = Address.Parser.ParseFrom(protobuf); if (!askOrder.Sighash.Equals(address.Sighash)) { msg = "The address doesn't match the ask order"; return(false); } txFrom = transfer.Txid; amountString = transfer.Amount; feeString = transfer.Fee; addressToString = address.Address_; networkId = transfer.Network; ccCommand = "UnlockFunds"; } else if (kind.Equals("collateral")) { string repaymentOrderId = command[3]; string addressToUnlockCollateralsTo = command[4]; var protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{repaymentOrderId}", out msg); if (protobuf == null) { return(false); } var repaymentOrder = RepaymentOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{repaymentOrder.DealId}", out msg); if (protobuf == null) { return(false); } var dealOrder = DealOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{dealOrder.AskOrderId}", out msg); if (protobuf == null) { return(false); } var askOrder = AskOrder.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{askOrder.TransferId}", out msg); if (protobuf == null) { return(false); } var transfer = Transfer.Parser.ParseFrom(protobuf); protobuf = RpcHelper.ReadProtobuf(httpClient, $"{creditcoinUrl}/state/{addressToUnlockCollateralsTo}", out msg); if (protobuf == null) { return(false); } var address = Address.Parser.ParseFrom(protobuf); if (!askOrder.Sighash.Equals(address.Sighash)) { msg = "The address doesn't match the ask order"; return(false); } txFrom = transfer.Txid; amountString = transfer.Amount; feeString = transfer.Fee; addressToString = address.Address_; networkId = transfer.Network; ccCommand = "UnlockCollateral"; } else { msg = "unknown unlock kind"; return(false); } string secret = cfg["secret"]; if (string.IsNullOrWhiteSpace(secret)) { msg = "ethereum.secret is not set"; return(false); } var ethereumPrivateKey = secret; // TODO disable confirmation count config for release build string confirmationsCount = cfg["confirmationsCount"]; if (string.IsNullOrWhiteSpace(confirmationsCount)) { msg = "ethereum.confirmationsCount is not set"; return(false); } if (!int.TryParse(confirmationsCount, out int confirmationsExpected)) { msg = "ethereum.confirmationsCount is not an int"; return(false); } string rpcUrl = cfg["rpc"]; if (string.IsNullOrWhiteSpace(rpcUrl)) { msg = "ethereum.rpc is not set"; return(false); } BigInteger fee; if (!BigInteger.TryParse(feeString, out fee)) { msg = "Invalid progress data"; return(false); } var web3 = new Nethereum.Web3.Web3(rpcUrl); BigInteger transferAmount; if (!BigInteger.TryParse(amountString, out transferAmount) || transferAmount <= 0) { msg = "Invalid amount"; return(false); } string sourceAddress = EthECKey.GetPublicAddress(ethereumPrivateKey); if (!sourceAddress.Equals(addressFromString, StringComparison.OrdinalIgnoreCase)) { msg = "The deal is for a different client"; return(false); } var txCount = web3.Eth.Transactions.GetTransactionCount.SendRequestAsync(sourceAddress).Result; TransactionSigner signer = new TransactionSigner(); var gasLimit = web3.Eth.Transactions.EstimateGas.SendRequestAsync(new Nethereum.RPC.Eth.DTOs.CallInput(string.Empty, addressToString, new Nethereum.Hex.HexTypes.HexBigInteger(transferAmount))).Result; var gasPrice = web3.Eth.GasPrice.SendRequestAsync().Result; string txRaw = signer.SignTransaction(ethereumPrivateKey, addressToString, transferAmount + fee, txCount, gasPrice, gasLimit, string.Empty); string payTxId = web3.Eth.Transactions.SendRawTransaction.SendRequestAsync("0x" + txRaw).Result; while (true) { var receipt = web3.Eth.TransactionManager.TransactionReceiptService.PollForReceiptAsync(payTxId).Result; if (receipt.BlockNumber != null) { var blockNumber = web3.Eth.Blocks.GetBlockNumber.SendRequestAsync().Result; if (blockNumber.Value - receipt.BlockNumber.Value >= confirmationsExpected) { break; } } Thread.Sleep(1000); } command = new string[] { ccCommand, command[3], command[4] }; var tx = txBuilder.BuildTx(command, out msg); Debug.Assert(tx != null); Debug.Assert(msg == null); var content = new ByteArrayContent(tx); content.Headers.Add("Content-Type", "application/octet-stream"); msg = RpcHelper.CompleteBatch(httpClient, $"{creditcoinUrl}/batches", content); } msg = "Unknown command: " + command[0]; return(false); }