private InteropBlock GetInteropBlock(BigInteger blockId) { var url = DomainExtensions.GetOracleBlockURL( "neo", "neo", PBigInteger.FromUnsignedArray(blockId.ToByteArray(), true)); return(OracleReader.Read <InteropBlock>(DateTime.Now, url)); }
private List <Task <InteropBlock> > CreateTaskList(BigInteger batchCount, BigInteger[] blockIds = null) { List <Task <InteropBlock> > taskList = new List <Task <InteropBlock> >(); if (blockIds == null) { var _interopBlockHeight = BigInteger.Parse(OracleReader.GetCurrentHeight("neo", "neo")); var nextCurrentBlockHeight = _interopBlockHeight + batchCount; for (var i = _interopBlockHeight; i < nextCurrentBlockHeight; i++) { var url = DomainExtensions.GetOracleBlockURL( "neo", "neo", PBigInteger.FromUnsignedArray(i.ToByteArray(), true)); taskList.Add(CreateTask(url)); } } else { foreach (var blockId in blockIds) { var url = DomainExtensions.GetOracleBlockURL( "neo", "neo", PBigInteger.FromUnsignedArray(blockId.ToByteArray(), true)); taskList.Add(CreateTask(url)); } } return(taskList); }
public BSCInterop(TokenSwapper swapper, EthAPI ethAPI, PBigInteger interopBlockHeight, string[] contracts, uint confirmations) : base(swapper, BSCWallet.BSCPlatform) { string lastBlockHeight = OracleReader.GetCurrentHeight(BSCWallet.BSCPlatform, BSCWallet.BSCPlatform); if (string.IsNullOrEmpty(lastBlockHeight)) { OracleReader.SetCurrentHeight(BSCWallet.BSCPlatform, BSCWallet.BSCPlatform, new BigInteger(interopBlockHeight.ToSignedByteArray()).ToString()); } Logger.Message($"interopHeight: {OracleReader.GetCurrentHeight(BSCWallet.BSCPlatform, BSCWallet.BSCPlatform)}"); Console.WriteLine("encoded bsc: " + BSCWallet.EncodeAddress("0x44E8743A6CAC3E59594C19DD462863A5AA5E06BB")); Console.WriteLine("encoded eth: " + EthereumWallet.EncodeAddress("0x44E8743A6CAC3E59594C19DD462863A5AA5E06BB")); Console.WriteLine("from encoded bsc: " + BSCWallet.EncodeAddress("0xA89a34c37Da826085E458c17067DA2F38b6e4763")); Console.WriteLine("from encoded eth: " + EthereumWallet.EncodeAddress("0xA89a34c37Da826085E458c17067DA2F38b6e4763")); this.contracts = contracts.ToList(); // add local swap address to contracts this.contracts.Add(LocalAddress); this.confirmations = confirmations; this.ethAPI = ethAPI; }
public override List <Block> GetBlockRange(PBigInteger start, PBigInteger end) { List <Task <DataNode> > taskList = new List <Task <DataNode> >(); List <Block> blockList = new List <Block>(); for (var i = start; i < end; i++) { var height = i; object[] heightData = new object[] { (int)height }; taskList.Add( new Task <DataNode>(() => { return(QueryRPC("getblock", heightData, 1, true)); }) ); } foreach (var task in taskList) { task.Start(); } Task.WaitAll(taskList.ToArray()); foreach (var task in taskList) { var response = task.Result; if (response == null || !response.HasNode("result")) { return(null); } var result = response.GetString("result"); var bytes = result.HexToBytes(); using (var stream = new MemoryStream(bytes)) { using (var reader = new BinaryReader(stream)) { var block = Block.Unserialize(reader); blockList.Add(block); } } } return(blockList); }
public NeoInterop(TokenSwapper swapper, NeoAPI neoAPI, PBigInteger interopBlockHeight, bool quickSync) : base(swapper, NeoWallet.NeoPlatform) { string lastBlockHeight = OracleReader.GetCurrentHeight("neo", "neo"); if (string.IsNullOrEmpty(lastBlockHeight)) { OracleReader.SetCurrentHeight("neo", "neo", new BigInteger(interopBlockHeight.ToUnsignedByteArray()).ToString()); } this.quickSync = quickSync; Logger.Message($"interopHeight: {OracleReader.GetCurrentHeight("neo", "neo")}"); this.neoAPI = neoAPI; this.lastScan = DateTime.UtcNow.AddYears(-1);; }
public EthereumInterop(TokenSwapper swapper, EthAPI ethAPI, PBigInteger interopBlockHeight, string[] contracts, uint confirmations) : base(swapper, EthereumWallet.EthereumPlatform) { string lastBlockHeight = OracleReader.GetCurrentHeight(EthereumWallet.EthereumPlatform, EthereumWallet.EthereumPlatform); if (string.IsNullOrEmpty(lastBlockHeight)) { OracleReader.SetCurrentHeight(EthereumWallet.EthereumPlatform, EthereumWallet.EthereumPlatform, new BigInteger(interopBlockHeight.ToSignedByteArray()).ToString()); } Logger.Message($"interopHeight: {OracleReader.GetCurrentHeight(EthereumWallet.EthereumPlatform, EthereumWallet.EthereumPlatform)}"); this.contracts = contracts.ToList(); // add local swap address to contracts this.contracts.Add(LocalAddress); this.confirmations = confirmations; this.ethAPI = ethAPI; }
private void ProcessBlock(InteropBlock block, List <PendingSwap> result) { foreach (var txHash in block.Transactions) { var interopTx = OracleReader.ReadTransaction("neo", "neo", txHash); if (interopTx.Transfers.Length == 0) { continue; } if (interopTx.Transfers.Length == 0) { continue; } InteropTransfer transfer; if (interopTx.Transfers.Length != 1) { var sources = interopTx.Transfers.Select(x => x.sourceAddress).Distinct(); if (sources.Count() > 1) { throw new OracleException("neo transfers with multiple source addresses not supported yet"); } var dests = interopTx.Transfers.Select(x => x.destinationAddress).Distinct(); if (dests.Count() > 1) { throw new OracleException("neo transfers with multiple destination addresses not supported yet"); } var interops = interopTx.Transfers.Select(x => x.interopAddress).Distinct(); if (interops.Count() > 1) { throw new OracleException("neo transfers with multiple interop addresses not supported yet"); } var symbols = interopTx.Transfers.Select(x => x.Symbol).Distinct(); if (symbols.Count() > 1) { throw new OracleException("neo transfers with multiple tokens not supported yet"); } PBigInteger sum = 0; foreach (var temp in interopTx.Transfers) { sum += temp.Value; } var first = interopTx.Transfers.First(); if (first.Data != null && first.Data.Length > 0) { throw new OracleException("neo transfers with custom data are not supported yet"); } transfer = new InteropTransfer(first.sourceChain, first.sourceAddress, first.destinationChain, first.destinationAddress, first.interopAddress, first.Symbol, sum); } else { transfer = interopTx.Transfers.First(); } if (transfer.sourceAddress == transfer.destinationAddress) // ignore this tx, this is a utxo consolidation { continue; } result.Add( new PendingSwap( this.PlatformName , txHash , transfer.sourceAddress , transfer.interopAddress) ); } }
public override ApplicationLog[] GetApplicationLog(UInt256 hash) { if (!HasPlugin("ApplicationLogs")) { return(null); } var response = QueryRPC("getapplicationlog", new object[] { hash.ToString() }); if (response != null && response.HasNode("result")) { //var json = LunarLabs.Parser.JSON.JSONReader.ReadFromString(response); List <ApplicationLog> appLogList = new List <ApplicationLog>(); var executions = response["result"]["executions"]; //LogData(executions); for (var i = 0; i < executions.ChildCount; i++) { VMState vmstate; if (Enum.TryParse(executions[i].GetString("vmstate"), out vmstate)) { //LogData(executions[i]["notifications"][0]["state"]["value"]); var notifications = executions[i]["notifications"]; for (var j = 0; j < notifications.ChildCount; j++) { var states = notifications[j]["state"]["value"]; string txevent = ""; UInt160 source = UInt160.Zero; UInt160 target = UInt160.Zero; PBigInteger amount = 0; var contract = notifications[j].GetString("contract"); if (states[0].GetString("type") == "ByteArray") { txevent = (states[0].GetString("value")); } if (states[1].GetString("type") == "ByteArray" && !string.IsNullOrEmpty(states[1].GetString("value"))) { source = UInt160.Parse(states[1].GetString("value")); } if (states[2].GetString("type") == "ByteArray" && !string.IsNullOrEmpty(states[2].GetString("value"))) { target = UInt160.Parse(states[2].GetString("value")); } if (states[3].GetString("type") == "ByteArray") { amount = PBigInteger.FromUnsignedArray(states[3].GetString("value").HexToBytes(), true); // needs to be Phantasma.Numerics.BigInteger for now. } appLogList.Add(new ApplicationLog(vmstate, contract, txevent, source, target, amount)); } } } return(appLogList.ToArray()); } else { return(null); } }
private static Dictionary <string, List <InteropTransfer> > GetInteropTransfers(Nexus nexus, Logger logger, TransactionReceipt txr, EthAPI api, string[] swapAddresses) { logger.Debug($"get interop transfers for tx {txr.TransactionHash}"); var interopTransfers = new Dictionary <string, List <InteropTransfer> >(); Nethereum.RPC.Eth.DTOs.Transaction tx = null; try { // tx to get the eth transfer if any tx = api.GetTransaction(txr.TransactionHash); } catch (Exception e) { logger.Error("Getting eth tx failed: " + e.Message); } logger.Debug("Transaction status: " + txr.Status.Value); // check if tx has failed if (txr.Status.Value == 0) { logger.Error($"tx {txr.TransactionHash} failed"); return(interopTransfers); } var nodeSwapAddresses = swapAddresses.Select(x => EthereumWallet.EncodeAddress(x)); Address interopAddress; try { interopAddress = ExtractInteropAddress(tx, logger); } catch (Exception e) { if (e.ToString().Contains("Header byte out of range")) { // Ignoring this exception and skipping this tx. // RecoverFromSignature() crashed and we cannot avoid it atm. // Related to EIP-1559, example of probematic tx: https://etherscan.io/tx/0xb022c146d8d1e684de0c1faae43e7ce36afb6969719adfdcafcc5bb7d5913185 // TODO Fix by updating to new Nethereum and dealing with EIP-1559 better. logger.Debug("Warning: Skipping 'Header byte out of range' tx: " + tx.TransactionHash); return(interopTransfers); } else { throw; } } // ERC721 (NFT) // TODO currently this code block is mostly copypaste from ERC20 block, later make a single method for both... //var erc721_events = txr.DecodeAllEvents<Nethereum.StandardNonFungibleTokenERC721.ContractDefinition.TransferEventDTOBase>(); //foreach (var evt in erc721_events) //{ // var asset = EthUtils.FindSymbolFromAsset(nexus, evt.Log.Address); // if (asset == null) // { // logger.Warning($"Asset [{evt.Log.Address}] not supported"); // continue; // } // var targetAddress = EthereumWallet.EncodeAddress(evt.Event.To); // var sourceAddress = EthereumWallet.EncodeAddress(evt.Event.From); // var tokenID = PBigInteger.Parse(evt.Event.TokenId.ToString()); // if (nodeSwapAddresses.Contains(targetAddress)) // { // if (!interopTransfers.ContainsKey(evt.Log.TransactionHash)) // { // interopTransfers.Add(evt.Log.TransactionHash, new List<InteropTransfer>()); // } // string tokenURI = FetchTokenURI(evt.Log.Address, evt.Event.TokenId); // interopTransfers[evt.Log.TransactionHash].Add // ( // new InteropTransfer // ( // EthereumWallet.EthereumPlatform, // sourceAddress, // DomainSettings.PlatformName, // targetAddress, // interopAddress, // asset, // tokenID, // System.Text.Encoding.UTF8.GetBytes(tokenURI) // ) // ); // } //} // ERC20 var erc20_events = txr.DecodeAllEvents <TransferEventDTO>(); foreach (var evt in erc20_events) { var asset = EthUtils.FindSymbolFromAsset(EthereumWallet.EthereumPlatform, nexus, evt.Log.Address); if (asset == null) { logger.Warning($"Asset [{evt.Log.Address}] not supported"); continue; } var targetAddress = EthereumWallet.EncodeAddress(evt.Event.To); var sourceAddress = EthereumWallet.EncodeAddress(evt.Event.From); var amount = PBigInteger.Parse(evt.Event.Value.ToString()); if (nodeSwapAddresses.Contains(targetAddress)) { if (!interopTransfers.ContainsKey(evt.Log.TransactionHash)) { interopTransfers.Add(evt.Log.TransactionHash, new List <InteropTransfer>()); } interopTransfers[evt.Log.TransactionHash].Add ( new InteropTransfer ( EthereumWallet.EthereumPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, asset, amount ) ); } } if (tx.Value != null && tx.Value.Value > 0) { var targetAddress = EthereumWallet.EncodeAddress(tx.To); var sourceAddress = EthereumWallet.EncodeAddress(tx.From); if (nodeSwapAddresses.Contains(targetAddress)) { var amount = PBigInteger.Parse(tx.Value.ToString()); if (!interopTransfers.ContainsKey(tx.TransactionHash)) { interopTransfers.Add(tx.TransactionHash, new List <InteropTransfer>()); } interopTransfers[tx.TransactionHash].Add ( new InteropTransfer ( EthereumWallet.EthereumPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, "ETH", // TODO use const amount ) ); } } return(interopTransfers); }
public override IEnumerable <PendingSwap> Update() { // wait another 10s to execute eth interop //Task.Delay(10000).Wait(); try { lock (String.Intern("PendingSetCurrentHeight_" + BSCWallet.BSCPlatform)) { var result = new List <PendingSwap>(); // initial start, we have to verify all processed swaps if (initialStart) { Logger.Debug($"Read all bsc blocks now."); var allInteropBlocks = OracleReader.ReadAllBlocks(BSCWallet.BSCPlatform, BSCWallet.BSCPlatform); Logger.Debug($"Found {allInteropBlocks.Count} blocks"); foreach (var block in allInteropBlocks) { ProcessBlock(block, ref result); } initialStart = false; // return after the initial start to be able to process all swaps that happend in the mean time. return(result); } var currentHeight = ethAPI.GetBlockHeight(); var _interopBlockHeight = BigInteger.Parse(OracleReader.GetCurrentHeight(BSCWallet.BSCPlatform, BSCWallet.BSCPlatform)); Logger.Debug($"Swaps: Current Eth chain height: {currentHeight}, interop: {_interopBlockHeight}, delta: {currentHeight - _interopBlockHeight}"); var blocksProcessedInOneBatch = 0; while (blocksProcessedInOneBatch < 50) { if (_resyncBlockIds.Any()) { for (var i = 0; i < _resyncBlockIds.Count; i++) { var blockId = _resyncBlockIds.ElementAt(i); if (blockId > _interopBlockHeight) { this.Logger.Warning($"EthInterop:Update() resync block {blockId} higher than current interop height, can't resync."); _resyncBlockIds.RemoveAt(i); continue; } try { this.Logger.Debug($"EthInterop:Update() resync block {blockId} now."); var block = GetInteropBlock(blockId); ProcessBlock(block, ref result); } catch (Exception e) { this.Logger.Error($"EthInterop:Update() resync block {blockId} failed: " + e); } _resyncBlockIds.RemoveAt(i); } } blocksProcessedInOneBatch++; var blockDifference = currentHeight - _interopBlockHeight; if (blockDifference < confirmations) { // no need to query the node yet break; } //TODO quick sync not done yet, requieres a change to the oracle impl to fetch multiple blocks //var nextHeight = (blockDifference > 50) ? 50 : blockDifference; //TODO //var transfers = new Dictionary<string, Dictionary<string, List<InteropTransfer>>>(); //if (nextHeight > 1) //{ // var blockCrawler = new EthBlockCrawler(logger, contracts.ToArray(), 0/*confirmations*/, ethAPI); //TODO settings confirmations // blockCrawler.Fetch(currentHeight, nextHeight); // transfers = blockCrawler.ExtractInteropTransfers(logger, LocalAddress); // foreach (var entry in transfers) // { // foreach (var txInteropTransfer in entry.Value) // { // foreach (var interopTransfer in txInteropTransfer.Value) // { // result.Add(new PendingSwap( // this.PlatformName // ,Hash.Parse(entry.Key) // ,interopTransfer.sourceAddress // ,interopTransfer.interopAddress) // ); // } // } // } // _interopBlockHeight = nextHeight; // oracleReader.SetCurrentHeight(BSCWallet.BSCPlatform, BSCWallet.BSCPlatform, _interopBlockHeight.ToString()); //} //else //{ /* Future improvement, implement oracle call to fetch multiple blocks */ var url = DomainExtensions.GetOracleBlockURL( BSCWallet.BSCPlatform, BSCWallet.BSCPlatform, PBigInteger.FromUnsignedArray(_interopBlockHeight.ToByteArray(), true)); var interopBlock = OracleReader.Read <InteropBlock>(DateTime.Now, url); ProcessBlock(interopBlock, ref result); _interopBlockHeight++; //} } OracleReader.SetCurrentHeight(BSCWallet.BSCPlatform, BSCWallet.BSCPlatform, _interopBlockHeight.ToString()); var total = result.Count(); if (total > 0) { Logger.Message($"startup: found {total} bsc swaps"); } else { Logger.Debug($"did not find any bsc swaps"); } return(result); } } catch (Exception e) { var logMessage = "BSCInterop.Update() exception caught:\n" + e.Message; var inner = e.InnerException; while (inner != null) { logMessage += "\n---> " + inner.Message + "\n\n" + inner.StackTrace; inner = inner.InnerException; } logMessage += "\n\n" + e.StackTrace; Logger.Error(logMessage); return(new List <PendingSwap>()); } }
private static Dictionary <string, List <InteropTransfer> > GetInteropTransfers(Nexus nexus, Logger logger, TransactionReceipt txr, EthAPI api, string[] swapAddresses) { logger.Debug($"get interop transfers for tx {txr.TransactionHash}"); var interopTransfers = new Dictionary <string, List <InteropTransfer> >(); Nethereum.RPC.Eth.DTOs.Transaction tx = null; try { // tx to get the eth transfer if any tx = api.GetTransaction(txr.TransactionHash); } catch (Exception e) { logger.Error("Getting eth tx failed: " + e.Message); } Console.WriteLine("txr: " + txr.Status); logger.Debug("Transaction status: " + txr.Status.Value); // check if tx has failed if (txr.Status.Value == 0) { logger.Error($"tx {txr.TransactionHash} failed"); return(interopTransfers); } foreach (var a in swapAddresses) { Console.WriteLine("swap address: " + a); } var nodeSwapAddresses = swapAddresses.Select(x => BSCWallet.EncodeAddress(x)); var interopAddress = ExtractInteropAddress(tx); Console.WriteLine("interop address: " + interopAddress); // ERC721 (NFT) // TODO currently this code block is mostly copypaste from BEP20 block, later make a single method for both... //var erc721_events = txr.DecodeAllEvents<Nethereum.StandardNonFungibleTokenERC721.ContractDefinition.TransferEventDTOBase>(); //foreach (var evt in erc721_events) //{ // var asset = EthUtils.FindSymbolFromAsset(nexus, evt.Log.Address); // if (asset == null) // { // logger.Warning($"Asset [{evt.Log.Address}] not supported"); // continue; // } // var targetAddress = BSCWallet.EncodeAddress(evt.Event.To); // var sourceAddress = BSCWallet.EncodeAddress(evt.Event.From); // var tokenID = PBigInteger.Parse(evt.Event.TokenId.ToString()); // if (nodeSwapAddresses.Contains(targetAddress)) // { // if (!interopTransfers.ContainsKey(evt.Log.TransactionHash)) // { // interopTransfers.Add(evt.Log.TransactionHash, new List<InteropTransfer>()); // } // string tokenURI = FetchTokenURI(evt.Log.Address, evt.Event.TokenId); // interopTransfers[evt.Log.TransactionHash].Add // ( // new InteropTransfer // ( // BSCWallet.BSCPlatform, // sourceAddress, // DomainSettings.PlatformName, // targetAddress, // interopAddress, // asset, // tokenID, // System.Text.Encoding.UTF8.GetBytes(tokenURI) // ) // ); // } //} // BEP20 var bep20_events = txr.DecodeAllEvents <TransferEventDTO>(); foreach (var evt in bep20_events) { var asset = EthUtils.FindSymbolFromAsset(BSCWallet.BSCPlatform, nexus, evt.Log.Address); if (asset == null) { logger.Warning($"Asset [{evt.Log.Address}] not supported"); continue; } var targetAddress = BSCWallet.EncodeAddress(evt.Event.To); var sourceAddress = BSCWallet.EncodeAddress(evt.Event.From); var amount = PBigInteger.Parse(evt.Event.Value.ToString()); if (nodeSwapAddresses.Contains(targetAddress)) { if (!interopTransfers.ContainsKey(evt.Log.TransactionHash)) { interopTransfers.Add(evt.Log.TransactionHash, new List <InteropTransfer>()); } interopTransfers[evt.Log.TransactionHash].Add ( new InteropTransfer ( BSCWallet.BSCPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, asset, amount ) ); } } Console.WriteLine("value: " + tx.Value); Console.WriteLine("value: " + tx.Value.Value); if (tx.Value != null && tx.Value.Value > 0) { var targetAddress = BSCWallet.EncodeAddress(tx.To); var sourceAddress = BSCWallet.EncodeAddress(tx.From); foreach (var a in nodeSwapAddresses) { Console.WriteLine("node swap address: " + a); } Console.WriteLine("target address: " + targetAddress); if (nodeSwapAddresses.Contains(targetAddress)) { var amount = PBigInteger.Parse(tx.Value.ToString()); if (!interopTransfers.ContainsKey(tx.TransactionHash)) { interopTransfers.Add(tx.TransactionHash, new List <InteropTransfer>()); } interopTransfers[tx.TransactionHash].Add ( new InteropTransfer ( BSCWallet.BSCPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, "BSC", // TODO use const amount ) ); } } return(interopTransfers); }
private static Dictionary <string, List <InteropTransfer> > GetInteropTransfers(Nexus nexus, Logger logger, TransactionReceipt txr, EthAPI api, string swapAddress) { logger.Debug($"get interop transfers for tx {txr.TransactionHash}"); var interopTransfers = new Dictionary <string, List <InteropTransfer> >(); Nethereum.RPC.Eth.DTOs.Transaction tx = null; try { // tx to get the eth transfer if any tx = api.GetTransaction(txr.TransactionHash); } catch (Exception e) { logger.Error("Getting eth tx failed: " + e.Message); } logger.Debug("Transaction status: " + txr.Status.Value); // check if tx has failed if (txr.Status.Value == 0) { logger.Error($"tx {txr.TransactionHash} failed"); return(interopTransfers); } var nodeSwapAddress = EthereumWallet.EncodeAddress(swapAddress); var events = txr.DecodeAllEvents <TransferEventDTO>(); var interopAddress = ExtractInteropAddress(tx); // ERC20 foreach (var evt in events) { var asset = EthUtils.FindSymbolFromAsset(nexus, evt.Log.Address); if (asset == null) { logger.Warning($"Asset [{evt.Log.Address}] not supported"); continue; } var targetAddress = EthereumWallet.EncodeAddress(evt.Event.To); var sourceAddress = EthereumWallet.EncodeAddress(evt.Event.From); var amount = PBigInteger.Parse(evt.Event.Value.ToString()); if (targetAddress.Equals(nodeSwapAddress)) { if (!interopTransfers.ContainsKey(evt.Log.TransactionHash)) { interopTransfers.Add(evt.Log.TransactionHash, new List <InteropTransfer>()); } interopTransfers[evt.Log.TransactionHash].Add ( new InteropTransfer ( EthereumWallet.EthereumPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, asset, amount ) ); } } if (tx.Value != null && tx.Value.Value > 0) { var targetAddress = EthereumWallet.EncodeAddress(tx.To); var sourceAddress = EthereumWallet.EncodeAddress(tx.From); if (targetAddress.Equals(nodeSwapAddress)) { var amount = PBigInteger.Parse(tx.Value.ToString()); if (!interopTransfers.ContainsKey(tx.TransactionHash)) { interopTransfers.Add(tx.TransactionHash, new List <InteropTransfer>()); } interopTransfers[tx.TransactionHash].Add ( new InteropTransfer ( EthereumWallet.EthereumPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, "ETH", // TODO use const amount ) ); } } return(interopTransfers); }
public abstract List <Block> GetBlockRange(PBigInteger start, PBigInteger end);
public static InteropTransaction MakeInteropTx(Logger logger, NeoTx tx, NeoAPI api, string[] origSwapAddresses, string coldStorage) { logger.Debug("checking tx: " + tx.Hash); var swapAddresses = new List <Address>(); foreach (var addr in origSwapAddresses) { swapAddresses.Add(NeoWallet.EncodeAddress(addr)); } List <InteropTransfer> interopTransfers = new List <InteropTransfer>(); var emptyTx = new InteropTransaction(Hash.Null, interopTransfers.ToArray()); PBigInteger amount; var witness = tx.witnesses.ElementAtOrDefault(0); if (witness == null) { // tx has no witness return(emptyTx); } var interopAddress = witness.ExtractAddress(); if (tx.witnesses.Length != 1 || interopAddress == Address.Null || interopAddress == null) { //currently only one witness allowed // if ExtractAddress returns Address.Null, the tx is not properly signed return(emptyTx); } var sourceScriptHash = witness.verificationScript.Sha256().RIPEMD160(); var sourceAddress = NeoWallet.EncodeByteArray(sourceScriptHash); var sourceDecoded = NeoWallet.DecodeAddress(sourceAddress); if (sourceAddress == interopAddress || sourceDecoded == coldStorage) { logger.Warning("self send tx or cold storage transfer found, ignoring: " + tx.Hash); // self send, probably consolidation tx, ignore return(emptyTx); } //logger.Debug("interop address: " + interopAddress); //logger.Debug("xswapAddress: " + swapAddress); //logger.Debug("interop sourceAddress: " + sourceAddress); //logger.Debug("neo sourceAddress: " + NeoWallet.DecodeAddress(sourceAddress)); if (tx.attributes != null && tx.attributes.Length > 0) { foreach (var attr in tx.attributes) { if (attr.Usage == TransactionAttributeUsage.Description) { try { var text = Encoding.UTF8.GetString(attr.Data); if (Address.IsValidAddress(text)) { interopAddress = Address.FromText(text); //logger.Debug("new interop address: " + interopAddress); } } catch {} } } } if (tx.outputs.Length > 0) { foreach (var output in tx.outputs) { var targetAddress = NeoWallet.EncodeByteArray(output.scriptHash.ToArray()); //logger.Debug("interop targetAddress : " + targetAddress); //logger.Debug("neo targetAddress: " + NeoWallet.DecodeAddress(targetAddress)); //logger.Debug("interopSwapAddress: " + interopSwapAddress); //logger.Debug("targetAddress: " + targetAddress); //var swpAddress = NeoWallet.EncodeAddress(swapAddress); //logger.Debug("interop swpAddress: " + swpAddress); //logger.Debug("neo swpAddress: " + NeoWallet.DecodeAddress(swpAddress)); //if (targetAddress.ToString() == swapAddress) if (swapAddresses.Contains(targetAddress)) { var token = FindSymbolFromAsset(new UInt256(output.assetID).ToString()); CryptoCurrencyInfo tokenInfo; if (NeoTokenInfo.TryGetValue(token, out tokenInfo)) { amount = Phantasma.Numerics.UnitConversion.ToBigInteger( output.value, tokenInfo.Decimals); } else { // asset not swapable at the moment... //logger.Debug("Asset not swapable"); return(emptyTx); } //logger.Debug("UTXO " + amount); interopTransfers.Add ( new InteropTransfer ( NeoWallet.NeoPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address token.ToString(), amount ) ); } } } if (tx.script != null && tx.script.Length > 0) // NEP5 transfers { var script = NeoDisassembler.Disassemble(tx.script, true); //logger.Debug("SCRIPT ===================="); //foreach (var entry in script.lines) //{ // logger.Debug($"{entry.name} : { entry.opcode }"); //} //logger.Debug("SCRIPT ===================="); if (script.lines.Count() < 7) { //logger.Debug("NO SCRIPT!!!!"); return(emptyTx); } var disasmEntry = script.lines.ElementAtOrDefault(6); //if ( disasmEntry == null ) //{ // logger.Debug("disasmEntry is null"); //} //if ( disasmEntry != null ) //{ // if ( disasmEntry.data == null) // logger.Debug("disasmEntry.data is 0"); //} if (disasmEntry.name != "APPCALL" || disasmEntry.data == null || disasmEntry.data.Length == 0) { //logger.Debug("NO APPCALL"); return(emptyTx); } else { var assetString = new UInt160(disasmEntry.data).ToString(); if (string.IsNullOrEmpty(assetString) || FindSymbolFromAsset(assetString) == null) { //logger.Debug("Ignore TX due to non swapable token."); return(emptyTx); } } int pos = 0; foreach (var entry in script.lines) { pos++; if (pos > 3) { // we are only interested in the first three elements break; } if (entry.data == null || entry.data.Length == 0) { logger.Debug("Ignore tx, invalid data field: " + tx); return(emptyTx); } if (pos == 1) { amount = PBigInteger.FromUnsignedArray(entry.data, true); } if (pos == 2 || pos == 3) { if (pos == 2) { if (entry.data == null || entry.data.Length == 0) { logger.Debug("Invalid op on pos 2, ignoring tx: " + tx); return(emptyTx); } var targetScriptHash = new UInt160(entry.data); //logger.Debug("neo targetAddress: " + targetScriptHash.ToAddress()); var targetAddress = NeoWallet.EncodeByteArray(entry.data); //logger.Debug("targetAddress : " + targetAddress); //logger.Debug("interopSwapAddress: " + interopSwapAddress); //logger.Debug("SwapAddress: " + swapAddress); if (swapAddresses.Contains(targetAddress)) { // found a swap, call getapplicationlog now to get transaction details and verify the tx was actually processed. ApplicationLog[] appLogs = null; try { appLogs = api.GetApplicationLog(tx.Hash); } catch (Exception e) { logger.Error("Getting application logs failed: " + e.Message); return(new InteropTransaction(Hash.Null, interopTransfers.ToArray())); } if (appLogs != null) { for (var i = 0; i < appLogs.Length; i++) { //logger.Debug("appLogs[i].contract" + appLogs[i].contract); var token = FindSymbolFromAsset(appLogs[i].contract); //logger.Debug("TOKEN::::::::::::::::::: " + token); //logger.Debug("amount: " + appLogs[i].amount + " " + token); var sadd = NeoWallet.EncodeByteArray(appLogs[i].sourceAddress.ToArray()); var tadd = NeoWallet.EncodeByteArray(appLogs[i].targetAddress.ToArray()); interopTransfers.Add ( new InteropTransfer ( "neo", // todo Pay.Chains.NeoWallet.NeoPlatform //NeoWallet.EncodeByteArray(appLogs[i].sourceAddress.ToArray()), sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address token, appLogs[i].amount ) ); } } else { logger.Warning("Neo swap is found but application log is not available for tx " + tx.Hash); } } } else { //TODO reverse swap sourceScriptHash = new UInt160(entry.data).ToArray(); sourceAddress = NeoWallet.EncodeByteArray(sourceScriptHash.ToArray()); } } } } var total = interopTransfers.Count(); if (total > 0) { logger.Message($"Found {total} swaps in neo tx {tx.Hash}"); } else { logger.Debug($"No swaps in neo tx {tx.Hash}"); } return((interopTransfers.Count() > 0) ? new InteropTransaction(Hash.Parse(tx.Hash.ToString()), interopTransfers.ToArray()) : new InteropTransaction(Hash.Null, interopTransfers.ToArray())); }
public InteropTransfers ExtractInteropTransfers(Blockchain.Nexus nexus, Logger logger, string swapAddress) { var interopTransfers = new InteropTransfers(); lock (transactions) { foreach (var txVo in transactions) { var block = txVo.Block; var txr = txVo.TransactionReceipt; var tx = txVo.Transaction; var interopAddress = EthereumInterop.ExtractInteropAddress(tx); var transferEvents = txr.DecodeAllEvents <TransferEventDTO>(); //var swapEvents = txr.DecodeAllEvents<SwapEventDTO>(); var nodeSwapAddress = EthereumWallet.EncodeAddress(swapAddress); if (transferEvents.Count > 0 || tx.Value != null && tx.Value.Value > 0) { if (!interopTransfers.ContainsKey(block.BlockHash)) { interopTransfers.Add(block.BlockHash, new Dictionary <string, List <InteropTransfer> >()); } } if (transferEvents.Count > 0) { var blockId = block.Number.ToString(); var hash = txr.TransactionHash; foreach (var evt in transferEvents) { var targetAddress = EthereumWallet.EncodeAddress(evt.Event.To); // If it's not our address, skip immediatly, don't log it if (targetAddress != nodeSwapAddress) { continue; } logger.Message($"Found ERC20 swap: {blockId} hash: {hash} to: {evt.Event.To} from: {evt.Event.From} value: {evt.Event.Value}"); var asset = EthUtils.FindSymbolFromAsset(nexus, evt.Log.Address); logger.Message("asset: " + asset); if (asset == null) { logger.Message($"Asset [{evt.Log.Address}] not supported"); continue; } var sourceAddress = EthereumWallet.EncodeAddress(evt.Event.From); var amount = PBigInteger.Parse(evt.Event.Value.ToString()); logger.Message("nodeSwapAddress: " + nodeSwapAddress); logger.Message("sourceAddress: " + sourceAddress); logger.Message("targetAddress: " + targetAddress); logger.Message("amount: " + amount); if (!interopTransfers[block.BlockHash].ContainsKey(evt.Log.TransactionHash)) { interopTransfers[block.BlockHash].Add(evt.Log.TransactionHash, new List <InteropTransfer>()); } interopTransfers[block.BlockHash][evt.Log.TransactionHash].Add ( new InteropTransfer ( EthereumWallet.EthereumPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address asset, amount ) ); } } if (tx.Value != null && tx.Value.Value > 0) { logger.Message("ETH:"); logger.Message(block.Number.ToString()); logger.Message(tx.TransactionHash); logger.Message(tx.To); logger.Message(tx.From); logger.Message(tx.Value.ToString()); var targetAddress = EthereumWallet.EncodeAddress(tx.To); if (targetAddress != nodeSwapAddress) { continue; } if (!interopTransfers[block.BlockHash].ContainsKey(tx.TransactionHash)) { interopTransfers[block.BlockHash].Add(tx.TransactionHash, new List <InteropTransfer>()); } var sourceAddress = EthereumWallet.EncodeAddress(tx.From); var amount = PBigInteger.Parse(tx.Value.ToString()); interopTransfers[block.BlockHash][tx.TransactionHash].Add ( new InteropTransfer ( EthereumWallet.EthereumPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address "ETH", // TODO use const amount ) ); } } transactions.Clear(); } // clear transactions after extraction was done return(interopTransfers); }
public override IEnumerable <PendingSwap> Update() { lock (String.Intern("PendingSetCurrentHeight_" + "neo")) { var result = new List <PendingSwap>(); try { var _interopBlockHeight = BigInteger.Parse(OracleReader.GetCurrentHeight("neo", "neo")); // initial start, we have to verify all processed swaps if (initialStart) { Logger.Debug($"Read all neo blocks now."); // TODO check if quick sync nodes are configured, if so use quick sync // we need to find a better solution for that though var allInteropBlocks = OracleReader.ReadAllBlocks("neo", "neo"); Logger.Debug($"Found {allInteropBlocks.Count} blocks"); foreach (var block in allInteropBlocks) { try { ProcessBlock(block, result); } catch (Exception e) { Logger.Debug($"Block {block.Hash} was not processed correctly: " + e); } } initialStart = false; Logger.Debug($"QuickSync: " + quickSync); // quick sync is only done once after startup if (quickSync) { // if quick sync is active, we can use a specific plugin installed on the nodes (EventTracker) try { var blockIds = neoAPI.GetSwapBlocks("ed07cffad18f1308db51920d99a2af60ac66a7b3", LocalAddress, _interopBlockHeight.ToString()); Logger.Debug($"Found {blockIds.Count} blocks to process "); List <InteropBlock> blockList = new List <InteropBlock>(); foreach (var entry in blockIds) { //logger.Debug($"read block {entry.Value}"); var url = DomainExtensions.GetOracleBlockURL("neo", "neo", PBigInteger.Parse(entry.Value.ToString())); blockList.Add(OracleReader.Read <InteropBlock>(DateTime.Now, url)); } // get blocks and order them for processing var blocksToProcess = blockList.Where(x => blockIds.ContainsKey(x.Hash.ToString())) .Select(x => new { block = x, id = blockIds[x.Hash.ToString()] }) .OrderBy(x => x.id); Logger.Debug($"blocks to process: {blocksToProcess.Count()}"); foreach (var entry in blocksToProcess.OrderBy(x => x.id)) { Logger.Debug($"process block {entry.id}"); ProcessBlock(entry.block, result); OracleReader.SetCurrentHeight("neo", "neo", _interopBlockHeight.ToString()); _interopBlockHeight = BigInteger.Parse(entry.id.ToString()); } } catch (Exception e) { Logger.Error("Inital start failed: " + e.ToString()); } } // return after the initial start to be able to process all swaps that happend in the mean time. return(result); } var blockIterator = new BlockIterator(neoAPI); var blockDifference = blockIterator.currentBlock - _interopBlockHeight; var batchCount = (blockDifference > 8) ? 8 : blockDifference; //TODO make it a constant, should be no more than 8 while (blockIterator.currentBlock > _interopBlockHeight) { if (_resyncBlockIds.Any()) { for (var i = 0; i < _resyncBlockIds.Count; i++) { var blockId = _resyncBlockIds.ElementAt(i); if (blockId > _interopBlockHeight) { Logger.Debug($"NeoInterop: Update() resync block {blockId} higher than current interop height, can't resync."); _resyncBlockIds.RemoveAt(i); continue; } try { Logger.Debug($"NeoInterop: Update() resync block {blockId} now."); var interopBlock = GetInteropBlock(blockId); ProcessBlock(interopBlock, result); } catch (Exception e) { Logger.Error($"NeoInterop: Update() resync block {blockId} failed: " + e); } _resyncBlockIds.RemoveAt(i); } } Logger.Debug($"Swaps: Current Neo chain height: {blockIterator.currentBlock}, interop: {_interopBlockHeight}, delta: {blockIterator.currentBlock - _interopBlockHeight}"); blockDifference = blockIterator.currentBlock - _interopBlockHeight; batchCount = (blockDifference > 8) ? 8 : blockDifference; if (batchCount > 1) { List <Task <InteropBlock> > taskList = CreateTaskList(batchCount); foreach (var task in taskList) { task.Start(); } Task.WaitAll(taskList.ToArray()); foreach (var task in taskList) { var block = task.Result; ProcessBlock(block, result); } OracleReader.SetCurrentHeight("neo", "neo", _interopBlockHeight.ToString()); _interopBlockHeight += batchCount; } else { var interopBlock = GetInteropBlock(_interopBlockHeight); ProcessBlock(interopBlock, result); OracleReader.SetCurrentHeight("neo", "neo", _interopBlockHeight.ToString()); _interopBlockHeight++; } } } catch (Exception e) { Logger.Error("Neo block sync failed: " + e); } return(result); } }
public InteropTransfers ExtractInteropTransfers(Blockchain.Nexus nexus, Logger logger, string[] swapAddresses) { var interopTransfers = new InteropTransfers(); lock (transactions) { foreach (var txVo in transactions) { var block = txVo.Block; var txr = txVo.TransactionReceipt; var tx = txVo.Transaction; Address interopAddress; try { interopAddress = EthereumInterop.ExtractInteropAddress(tx, logger); } catch (Exception e) { if (e.ToString().Contains("Header byte out of range")) { // Ignoring this exception and skipping this tx. // RecoverFromSignature() crashed and we cannot avoid it atm. // Related to EIP-1559, example of probematic tx: https://etherscan.io/tx/0xb022c146d8d1e684de0c1faae43e7ce36afb6969719adfdcafcc5bb7d5913185 // TODO Fix by updating to new Nethereum and dealing with EIP-1559 better. logger.Debug("Warning: Skipping 'Header byte out of range' tx: " + tx.TransactionHash); continue; } else { throw; } } var transferEvents = txr.DecodeAllEvents <TransferEventDTO>(); //var swapEvents = txr.DecodeAllEvents<SwapEventDTO>(); var nodeSwapAddresses = swapAddresses.Select(x => encodeHandler(x)).ToList(); //var nodeSwapAddresses = encodeHandler(swapAddress); if (transferEvents.Count > 0 || tx.Value != null && tx.Value.Value > 0) { if (!interopTransfers.ContainsKey(block.BlockHash)) { interopTransfers.Add(block.BlockHash, new Dictionary <string, List <InteropTransfer> >()); } } if (transferEvents.Count > 0) { var blockId = block.Number.ToString(); var hash = txr.TransactionHash; foreach (var evt in transferEvents) { var targetAddress = encodeHandler(evt.Event.To); // If it's not our address, skip immediatly, don't log it if (!nodeSwapAddresses.Contains(targetAddress)) { continue; } var asset = EthUtils.FindSymbolFromAsset(this.platform, nexus, evt.Log.Address); logger.Debug($@"Found {this.platform} swap: {blockId} hash: {hash} to: {evt.Event.To} from: {evt.Event.From} value: {evt.Event.Value} asset: {asset}"); if (asset == null) { logger.Debug($"Asset [{evt.Log.Address}] not supported"); continue; } var sourceAddress = encodeHandler(evt.Event.From); var amount = PBigInteger.Parse(evt.Event.Value.ToString()); //logger.Message("nodeSwapAddress: " + nodeSwapAddress); //logger.Message("sourceAddress: " + sourceAddress); //logger.Message("targetAddress: " + targetAddress); //logger.Message("amount: " + amount); if (!interopTransfers[block.BlockHash].ContainsKey(evt.Log.TransactionHash)) { interopTransfers[block.BlockHash].Add(evt.Log.TransactionHash, new List <InteropTransfer>()); } interopTransfers[block.BlockHash][evt.Log.TransactionHash].Add ( new InteropTransfer ( this.platform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address asset, amount ) ); } } if (tx.Value != null && tx.Value.Value > 0) { //logger.Message("ETH:"); //logger.Message(block.Number.ToString()); //logger.Message(tx.TransactionHash); //logger.Message(tx.To); //logger.Message(tx.From); //logger.Message(tx.Value.ToString()); var targetAddress = encodeHandler(tx.To); Console.WriteLine("target eth: " + targetAddress); if (!nodeSwapAddresses.Contains(targetAddress)) { continue; } if (!interopTransfers[block.BlockHash].ContainsKey(tx.TransactionHash)) { interopTransfers[block.BlockHash].Add(tx.TransactionHash, new List <InteropTransfer>()); } var sourceAddress = encodeHandler(tx.From); var amount = PBigInteger.Parse(tx.Value.ToString()); var nativeSymbol = (this.platform == "ethereum") ? "ETH" : "BNB"; interopTransfers[block.BlockHash][tx.TransactionHash].Add ( new InteropTransfer ( this.platform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address nativeSymbol, amount ) ); } } transactions.Clear(); } // clear transactions after extraction was done return(interopTransfers); }