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 TokenSwapper(Spook node, PhantasmaKeys swapKey, NeoAPI neoAPI, EthAPI ethAPI, BigInteger minFee, string[] supportedPlatforms) { this.Node = node; this.SwapKeys = swapKey; this.OracleReader = Nexus.GetOracleReader(); this.MinimumFee = minFee; this.neoAPI = neoAPI; this.ethAPI = ethAPI; this.Storage = new KeyStoreStorage(Nexus.CreateKeyStoreAdapter("swaps")); this.interopBlocks = new Dictionary <string, BigInteger>(); interopBlocks[DomainSettings.PlatformName] = BigInteger.Parse(Settings.Oracle.PhantasmaInteropHeight); interopBlocks["neo"] = BigInteger.Parse(Settings.Oracle.NeoInteropHeight); interopBlocks["ethereum"] = BigInteger.Parse(Settings.Oracle.EthInteropHeight); _supportedPlatforms.Add(DomainSettings.PlatformName); foreach (var entry in supportedPlatforms) { if (_supportedPlatforms.Contains(entry)) { throw new SwapException($"Duplicated swap platform {entry}, check config"); } if (!interopBlocks.ContainsKey(entry)) { throw new SwapException($"Unknown swap platform {entry}, check config"); } _supportedPlatforms.Add(entry); } }
private InteropBlock GetInteropBlock(BigInteger blockId) { var url = DomainExtensions.GetOracleBlockURL( "neo", "neo", PBigInteger.FromUnsignedArray(blockId.ToByteArray(), true)); return(OracleReader.Read <InteropBlock>(DateTime.Now, url)); }
public TokenSwapper(Spook node, PhantasmaKeys swapKey, NeoAPI neoAPI, EthAPI ethAPI, EthAPI bscAPI, BigInteger minFee) { this.Node = node; this.SwapKeys = swapKey; this.OracleReader = Nexus.GetOracleReader(); this.MinimumFee = minFee; this.neoAPI = neoAPI; this.ethAPI = ethAPI; this.bscAPI = bscAPI; this.Storage = new KeyStoreStorage(Nexus.CreateKeyStoreAdapter("swaps")); this._interopBlocks = new Dictionary <SwapPlatformChain, BigInteger>(); foreach (var platform in Settings.Oracle.SwapPlatforms) { _interopBlocks[platform.Chain] = platform.InteropHeight; } var inProgressMap = new StorageMap(InProgressTag, this.Storage); Console.WriteLine($"inProgress count: {inProgressMap.Count()}"); inProgressMap.Visit <Hash, string>((key, value) => { if (!string.IsNullOrEmpty(value)) { Console.WriteLine($"inProgress: {key} - {value}"); } }); }
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 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);; }
private void ProcessBlock(InteropBlock block, ref List <PendingSwap> result) { foreach (var txHash in block.Transactions) { var interopTx = OracleReader.ReadTransaction(BSCWallet.BSCPlatform, "bsc", txHash); foreach (var interopTransfer in interopTx.Transfers) { result.Add( new PendingSwap( this.PlatformName , txHash , interopTransfer.sourceAddress , interopTransfer.interopAddress) ); } } }
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 Task <InteropBlock> CreateTask(string url) { return(new Task <InteropBlock>(() => { var delay = 1000; while (true) { try { return OracleReader.Read <InteropBlock>(DateTime.Now, url); } catch (Exception e) { var logMessage = "oracleReader.Read() 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.Contains("Neo block is null") ? "oracleReader.Read(): Neo block is null, possible connection failure" : logMessage); } Thread.Sleep(delay); if (delay >= 60000) // Once we reach 1 minute, we stop increasing delay and just repeat every minute. { delay = 60000; } else { delay *= 2; } } })); }
public RuntimeVM(byte[] script, Chain chain, Timestamp time, Transaction transaction, StorageChangeSetContext changeSet, OracleReader oracle, bool readOnlyMode, bool delayPayment = false) : base(script) { Core.Throw.IfNull(chain, nameof(chain)); Core.Throw.IfNull(changeSet, nameof(changeSet)); // NOTE: block and transaction can be null, required for Chain.InvokeContract //Throw.IfNull(block, nameof(block)); //Throw.IfNull(transaction, nameof(transaction)); this.MinimumFee = 1; this.GasPrice = 0; this.UsedGas = 0; this.PaidGas = 0; this.GasTarget = chain.Address; this.MaxGas = 10000; // a minimum amount required for allowing calls to Gas contract etc this.DelayPayment = delayPayment; this.Time = time; this.Chain = chain; this.Transaction = transaction; this.Oracle = oracle; this.ChangeSet = changeSet; this.readOnlyMode = readOnlyMode; this.isBlockOperation = false; this.randomized = false; this.FeeTargetAddress = Address.Null; if (this.Chain != null && !Chain.IsRoot) { var parentName = chain.Nexus.GetParentChainByName(chain.Name); this.ParentChain = chain.Nexus.FindChainByName(parentName); } else { this.ParentChain = null; } Chain.RegisterExtCalls(this); }
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); } }
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 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>()); } }