private Address FindLender(BigInteger amount) { var count = _lenderList.Count(); if (count > 0) { var index = Runtime.GenerateRandomNumber() % count; var originalIndex = index; do { var address = _lenderList.Get <Address>(index); var lender = _lenderMap.Get <Address, GasLender>(address); if (lender.balance >= amount) { return(address); } index++; if (index == originalIndex) { break; } if (index >= count) { index = 0; } } while (true); } return(Address.Null); }
private void CreateOTC(Address from, string baseSymbol, string quoteSymbol, BigInteger amount, BigInteger price) { var uid = Runtime.GenerateUID(); var count = _otcBook.Count(); ExchangeOrder lockUpOrder; for (int i = 0; i < count; i++) { lockUpOrder = _otcBook.Get <ExchangeOrder>(i); if (lockUpOrder.Creator == from) { throw new Exception("Already have an offer created"); return; } } var baseBalance = Runtime.GetBalance(baseSymbol, from); Runtime.Expect(baseBalance >= amount, "invalid seller amount"); Runtime.TransferTokens(baseSymbol, from, this.Address, price); var order = new ExchangeOrder(uid, Runtime.Time, from, this.Address, amount, baseSymbol, price, quoteSymbol, ExchangeOrderSide.Sell, ExchangeOrderType.OTC); _otcBook.Add <ExchangeOrder>(order); }
private Address FindLender() { var count = _lenderList.Count(); if (count > 0) { var index = Runtime.GetRandomNumber() % count; return(_lenderList.Get <Address>(index)); } return(Address.Null); }
private int FindAppIndex(string name) { var count = _apps.Count(); for (int i = 0; i < count; i++) { var app = _apps.Get <AppInfo>(i); if (app.id == name) { return(i); } } return(-1); }
private bool UpdatePendingSettle(StorageList list, int index) { var swap = list.Get <PendingSettle>(index); var prevStatus = swap.status; switch (swap.status) { case SwapStatus.Settle: { var diff = Timestamp.Now - swap.time; if (diff >= 60) { swap.settleHash = SettleTransaction(DomainSettings.PlatformName, DomainSettings.RootChainName, swap.sourceHash); if (swap.settleHash != Hash.Null) { swap.status = SwapStatus.Confirm; } } break; } case SwapStatus.Confirm: { var result = this.NexusAPI.GetTransaction(swap.settleHash.ToString()); if (result is TransactionResult) { var tx = (TransactionResult)result; swap.status = SwapStatus.Finished; } else if (result is ErrorResult) { var error = ((ErrorResult)result).error; if (error != "pending") { swap.settleHash = Hash.Null; swap.time = Timestamp.Now; swap.status = SwapStatus.Settle; } } break; } default: return(false); } if (swap.status == SwapStatus.Finished) { var settlements = new StorageMap(SettlementTag, this.Storage); settlements.Set <Hash, Hash>(swap.sourceHash, swap.destinationHash); return(true); } if (swap.status != prevStatus) { list.Replace <PendingSettle>(index, swap); } return(false); }
public void TestStorageListWithNestedMap() { var context = new MemoryStorageContext(); var map = new StorageMap("map".AsByteArray(), context); Assert.IsTrue(map.Count() == 0); map.Set(1, "hello"); map.Set(3, "world"); var count = map.Count(); Assert.IsTrue(count == 2); var list = new StorageList("list".AsByteArray(), context); Assert.IsTrue(list.Count() == 0); list.Add(map); count = list.Count(); Assert.IsTrue(count == 1); var another = list.Get <StorageMap>(0); count = another.Count(); Assert.IsTrue(count == 2); }
// should only be called from inside lock block private Hash GetSettleHash(string sourcePlatform, Hash sourceHash) { var settlements = new StorageMap(SettlementTag, this.Storage); if (settlements.ContainsKey <Hash>(sourceHash)) { return(settlements.Get <Hash, Hash>(sourceHash)); } var pendingList = new StorageList(PendingTag, this.Storage); var count = pendingList.Count(); for (int i = 0; i < count; i++) { var settlement = pendingList.Get <PendingFee>(i); if (settlement.sourceHash == sourceHash) { return(settlement.destinationHash); } } var hash = (Hash)Nexus.RootChain.InvokeContract(Nexus.RootStorage, "interop", nameof(InteropContract.GetSettlement), sourcePlatform, sourceHash).ToObject(); if (hash != Hash.Null && !settlements.ContainsKey <Hash>(sourceHash)) { // This modification should be locked when GetSettleHash() is called from SettleSwap(), // so we lock it in SettleSwap(). settlements.Set <Hash, Hash>(sourceHash, hash); } return(hash); }
public Hash GetSettleHash(string sourcePlatform, Hash sourceHash) { var settlements = new StorageMap(SettlementTag, this.Storage); if (settlements.ContainsKey <Hash>(sourceHash)) { return(settlements.Get <Hash, Hash>(sourceHash)); } var pendingList = new StorageList(PendingTag, this.Storage); var count = pendingList.Count(); for (int i = 0; i < count; i++) { var settlement = pendingList.Get <PendingSettle>(i); if (settlement.sourceHash == sourceHash) { return(settlement.destinationHash); } } var hash = (Hash)Nexus.RootChain.InvokeContract(Nexus.RootStorage, "interop", nameof(InteropContract.GetSettlement), sourcePlatform, sourceHash).ToObject(); if (hash != Hash.Null && !settlements.ContainsKey <Hash>(sourceHash)) { settlements.Set <Hash, Hash>(sourceHash, hash); } return(hash); }
public EnergyMaster GetMaster(Address address) { var count = _mastersList.Count(); for (int i = 0; i < count; i++) { var master = _mastersList.Get <EnergyMaster>(i); if (master.address == address) { return(master); } } return(new EnergyMaster { address = Address.Null }); }
public Address GetValidatorByIndex(BigInteger index) { Runtime.Expect(index >= 0, "invalid validator index"); var count = _validatorList.Count(); Runtime.Expect(index < count, "invalid validator index"); var address = _validatorList.Get <Address>(index); return(address); }
public Hash GetLatestSaleHash() { var count = (int)_saleList.Count(); if (count <= 0) { return(Hash.Null); } var index = count - 1; var firstHash = _saleList.Get <Hash>(index); return(firstHash); }
public void TestStorageList() { var context = new MemoryStorageContext(); var list = new StorageList("test".AsByteArray(), context); Assert.IsTrue(list.Count() == 0); list.Add("hello"); list.Add("world"); Assert.IsTrue(list.Count() == 2); list.RemoveAt(0); Assert.IsTrue(list.Count() == 1); var temp = list.Get <string>(0); Assert.IsTrue(temp == "world"); list.Replace <string>(0, "hello"); temp = list.Get <string>(0); Assert.IsTrue(temp == "hello"); }
public bool IsExchange(Address address) { var count = _exchanges.Count(); for (int i = 0; i < count; i++) { var exchange = _exchanges.Get <ExchangeProvider>(i); if (exchange.address == address) { return(true); } } return(false); }
public Hash GetBlockHashAtHeight(BigInteger height) { if (height <= 0) { throw new ChainException("invalid block height"); } if (height > this.Height) { return(Hash.Null); } var hashList = new StorageList(BlockHeightListTag, this.Storage); // NOTE chain heights start at 1, but list index start at 0 var hash = hashList.Get <Hash>(height - 1); return(hash); }
private IEnumerable <Transaction> ProcessPendingTasks(Block block, OracleReader oracle, BigInteger minimumFee, StorageChangeSetContext changeSet, bool allowModify) { var taskList = new StorageList(TaskListTag, changeSet); var taskCount = taskList.Count(); List <Transaction> transactions = null; int i = 0; while (i < taskCount) { var taskID = taskList.Get <BigInteger>(i); var task = GetTask(changeSet, taskID); Transaction tx; var taskResult = ProcessPendingTask(block, oracle, minimumFee, changeSet, allowModify, task, out tx); if (taskResult == TaskResult.Running) { i++; } else { taskList.RemoveAt <BigInteger>(i); } if (tx != null) { if (transactions == null) { transactions = new List <Transaction>(); } transactions.Add(tx); } } if (transactions != null) { return(transactions); } return(Enumerable.Empty <Transaction>()); }
public void TakeOrder(Address from, BigInteger uid) { Runtime.Expect(Runtime.IsWitness(from), "invalid witness"); var count = _otcBook.Count(); for (int i = 0; i < count; i++) { var order = _otcBook.Get <ExchangeOrder>(i); if (order.Uid == uid) { throw new NotImplementedException(); return; } } Runtime.Expect(false, "order not found"); }
public void CancelOrder(BigInteger uid) { Runtime.Expect(_orderMap.ContainsKey <BigInteger>(uid), "order not found"); var key = _orderMap.Get <BigInteger, string>(uid); StorageList orderList = _orders.Get <string, StorageList>(key); var count = orderList.Count(); for (int i = 0; i < count; i++) { var order = orderList.Get <ExchangeOrder>(i); if (order.Uid == uid) { Runtime.Expect(IsWitness(order.Creator), "invalid witness"); orderList.RemoveAt <ExchangeOrder>(i); _orderMap.Remove <BigInteger>(uid); _fills.Remove <BigInteger>(uid); if (_escrows.ContainsKey <BigInteger>(uid)) { var leftoverEscrow = _escrows.Get <BigInteger, BigInteger>(uid); if (leftoverEscrow > 0) { var escrowSymbol = order.Side == ExchangeOrderSide.Sell ? order.QuoteSymbol : order.BaseSymbol; Runtime.Nexus.TransferTokens(Runtime, escrowSymbol, this.Address, order.Creator, leftoverEscrow); Runtime.Notify(EventKind.TokenReceive, order.Creator, new TokenEventData() { chainAddress = this.Address, symbol = escrowSymbol, value = leftoverEscrow }); } } return; } } // if it reaches here, it means it not found nothing in previous part throw new Exception("order not found"); }
public ExchangeOrder GetExchangeOrder(BigInteger uid) { Runtime.Expect(_orderMap.ContainsKey <BigInteger>(uid), "order not found"); var key = _orderMap.Get <BigInteger, string>(uid); StorageList orderList = _orders.Get <string, StorageList>(key); var count = orderList.Count(); var order = new ExchangeOrder(); for (int i = 0; i < count; i++) { order = orderList.Get <ExchangeOrder>(i); if (order.Uid == uid) { //Runtime.Expect(Runtime.IsWitness(order.Creator), "invalid witness"); break; } } return(order); }
private PrivacyQueue FetchQueue(string symbol) { StorageList list = _queues.Get <string, StorageList>(symbol); PrivacyQueue queue; var count = list.Count(); if (count > 0) { var last = list.Get <PrivacyQueue>(count - 1); if (last.addresses.Count() < last.size) { return(last); } } var id = (uint)(count + 1); var baseKey = $"{symbol}.{id}"; var addrKey = $"{baseKey}.addr".AsByteArray(); var addressList = new StorageList(addrKey, Runtime.ChangeSet); addressList.Clear(); var ringKey = $"{baseKey}.ring".AsByteArray(); var ringList = new StorageList(addrKey, Runtime.ChangeSet); ringList.Clear(); queue = new PrivacyQueue() { addresses = addressList, signatures = ringList, ID = id, size = 3 }; list.Add(queue); return(queue); }
public void SettleTransaction(Address from, string platform, string chain, Hash hash) { PlatformSwapAddress[] swapAddresses; if (platform != DomainSettings.PlatformName) { Runtime.Expect(Runtime.PlatformExists(platform), "unsupported platform"); var platformInfo = Runtime.GetPlatformByName(platform); swapAddresses = platformInfo.InteropAddresses; } else { swapAddresses = null; } Runtime.Expect(Runtime.IsWitness(from), "invalid witness"); Runtime.Expect(from.IsUser, "must be user address"); var chainHashes = _hashes.Get <string, StorageMap>(platform); Runtime.Expect(!chainHashes.ContainsKey <Hash>(hash), "hash already seen"); var interopTx = Runtime.ReadTransactionFromOracle(platform, chain, hash); Runtime.Expect(interopTx.Hash == hash, "unxpected hash"); int swapCount = 0; foreach (var transfer in interopTx.Transfers) { var count = _withdraws.Count(); var index = -1; for (int i = 0; i < count; i++) { var entry = _withdraws.Get <InteropWithdraw>(i); if (entry.destination == transfer.destinationAddress && entry.transferAmount == transfer.Value && entry.transferSymbol == transfer.Symbol) { index = i; break; } } if (index >= 0) { Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero"); Runtime.Expect(Runtime.TokenExists(transfer.Symbol), "invalid token"); var token = this.Runtime.GetToken(transfer.Symbol); Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible"); Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable"); Runtime.Expect(token.Flags.HasFlag(TokenFlags.External), "token must be external"); var withdraw = _withdraws.Get <InteropWithdraw>(index); _withdraws.RemoveAt <InteropWithdraw>(index); Runtime.TransferTokens(withdraw.feeSymbol, this.Address, transfer.sourceAddress, withdraw.feeAmount); swapCount++; } else if (swapAddresses != null) { foreach (var entry in swapAddresses) { if (transfer.destinationAddress == entry.LocalAddress) { Runtime.Expect(!transfer.sourceAddress.IsNull, "invalid source address"); Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero"); Runtime.Expect(Runtime.TokenExists(transfer.Symbol), "invalid token"); var token = this.Runtime.GetToken(transfer.Symbol); Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible"); Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable"); Runtime.Expect(token.Flags.HasFlag(TokenFlags.External), "token must be external"); Runtime.Expect(transfer.interopAddress.IsUser, "invalid destination address"); // TODO support NFT Runtime.SwapTokens(platform, transfer.sourceAddress, Runtime.Chain.Name, transfer.interopAddress, transfer.Symbol, transfer.Value, null, null); //Runtime.Notify(EventKind.TokenSwap, destination, new TokenEventData(token.Symbol, amount, Runtime.Chain.Name)); swapCount++; break; } } } } Runtime.Expect(swapCount > 0, "nothing to settle"); chainHashes.Set <Hash, Hash>(hash, Runtime.Transaction.Hash); Runtime.Notify(EventKind.ChainSwap, from, new TransactionSettleEventData(hash, platform, chain)); }
public void SettleTransaction(Address from, string platform, Hash hash) { Runtime.Expect(platform != DomainSettings.PlatformName, "must be external platform"); Runtime.Expect(Runtime.PlatformExists(platform), "unsupported platform"); var platformInfo = Runtime.GetPlatform(platform); Runtime.Expect(Runtime.IsWitness(from), "invalid witness"); Runtime.Expect(from.IsUser, "must be user address"); var chainHashes = _hashes.Get <string, StorageSet>(platform); Runtime.Expect(!chainHashes.Contains <Hash>(hash), "hash already seen"); var interopTx = Runtime.ReadTransactionFromOracle(platform, DomainSettings.RootChainName, hash); Runtime.Expect(interopTx.Platform == platform, "unxpected platform name"); Runtime.Expect(interopTx.Hash == hash, "unxpected hash"); int swapCount = 0; foreach (var evt in interopTx.Events) { if (evt.Kind == EventKind.TokenReceive && evt.Address == platformInfo.Address) { Runtime.Expect(!evt.Address.IsNull, "invalid source address"); var transfer = evt.GetContent <TokenEventData>(); Runtime.Expect(transfer.value > 0, "amount must be positive and greater than zero"); Runtime.Expect(Runtime.TokenExists(transfer.symbol), "invalid token"); var token = this.Runtime.GetToken(transfer.symbol); Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible"); Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable"); Runtime.Expect(token.Flags.HasFlag(TokenFlags.External), "token must be external"); Address destination = Address.Null; foreach (var otherEvt in interopTx.Events) { if (otherEvt.Kind == EventKind.TokenSend) { var otherTransfer = otherEvt.GetContent <TokenEventData>(); if (otherTransfer.chainAddress == transfer.chainAddress && otherTransfer.symbol == transfer.symbol) { destination = otherEvt.Address; break; } } } if (destination.IsInterop) { destination = GetLink(destination, DomainSettings.PlatformName); } else { Runtime.Expect(destination.IsUser, "invalid destination address"); } Runtime.Expect(Runtime.TransferTokens(transfer.symbol, platformInfo.Address, destination, transfer.value), "mint failed"); Runtime.Notify(EventKind.TokenReceive, destination, new TokenEventData() { chainAddress = platformInfo.Address, value = transfer.value, symbol = transfer.symbol }); swapCount++; break; } if (evt.Kind == EventKind.TokenClaim) { var destination = evt.Address; Runtime.Expect(!destination.IsNull, "invalid destination"); var transfer = evt.GetContent <TokenEventData>(); Runtime.Expect(transfer.value > 0, "amount must be positive and greater than zero"); Runtime.Expect(Runtime.TokenExists(transfer.symbol), "invalid token"); var token = this.Runtime.GetToken(transfer.symbol); Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible"); Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable"); Runtime.Expect(token.Flags.HasFlag(TokenFlags.External), "token must be external"); var count = _withdraws.Count(); var index = -1; for (int i = 0; i < count; i++) { var entry = _withdraws.Get <InteropWithdraw>(i); if (entry.destination == destination && entry.transferAmount == transfer.value && entry.transferSymbol == transfer.symbol) { index = i; break; } } Runtime.Expect(index >= 0, "invalid withdraw, possible leak found"); var withdraw = _withdraws.Get <InteropWithdraw>(index); Runtime.Expect(withdraw.broker == from, "invalid broker"); _withdraws.RemoveAt <InteropWithdraw>(index); Runtime.Expect(Runtime.TransferTokens(withdraw.feeSymbol, this.Address, from, withdraw.feeAmount), "fee payment failed"); Runtime.Notify(EventKind.TokenReceive, from, new TokenEventData() { chainAddress = this.Runtime.Chain.Address, value = withdraw.feeAmount, symbol = withdraw.feeSymbol }); Runtime.Notify(EventKind.TokenReceive, destination, new TokenEventData() { chainAddress = platformInfo.Address, value = withdraw.transferAmount, symbol = withdraw.transferSymbol }); swapCount++; break; } } Runtime.Expect(swapCount > 0, "nothing to settle"); chainHashes.Add <Hash>(hash); }
public void Queue(Address from, string symbol, BigInteger tableID) { Runtime.Expect(IsWitness(from), "invalid witness"); Runtime.Expect(tableID == 0, "invalid table"); var token = Runtime.Nexus.GetTokenInfo(Nexus.StakingTokenSymbol); Runtime.Expect(symbol == token.Symbol, "invalid symbol"); Runtime.Expect(!_matchMap.ContainsKey <Address>(from), "already in match"); var fee = GetTableFee(tableID); Runtime.Expect(Runtime.Nexus.TransferTokens(token.Symbol, this.Storage, Runtime.Chain, from, Runtime.Chain.Address, fee), "fee transfer failed"); int queueIndex = -1; var count = _queue.Count(); for (var i = 0; i < count; i++) { var temp = _queue.Get <CasinoQueue>(i); if (temp.table == tableID) { queueIndex = i; break; } } if (queueIndex >= 0) { var other = _queue.Get <CasinoQueue>(queueIndex); _matchCount++; _matchMap.Set <Address, BigInteger>(from, _matchCount); _matchMap.Set <Address, BigInteger>(other.player, _matchCount); Runtime.Notify(EventKind.CasinoTableStart, from, other.player); Runtime.Notify(EventKind.CasinoTableStart, other.player, from); var match = new CasinoMatch() { amount = fee * 2, host = other.player, opponent = from, matchTurn = 0, hostTurn = 0, opponentTurn = 0, timestamp = Runtime.Time, cards = new byte[52], }; for (int j = 0; j < 2; j++) { for (int i = 0; i < 2; i++) { var card = DrawCard(match.cards, (byte)(i + 1)); Runtime.Expect(card >= 0, "card draw failed"); Runtime.Notify(EventKind.CasinoTableCard, i == 0 ? match.host : match.opponent, i); } } _matchData.Set <BigInteger, CasinoMatch>(_matchCount, match); } else { var entry = new CasinoQueue() { player = from, amount = fee, symbol = symbol, table = tableID }; _queue.Add <CasinoQueue>(entry); Runtime.Notify(EventKind.CasinoTableQueued, from, tableID); } }
public void Update() { try { if (this.platforms == null) { if (!Nexus.HasGenesis) { return; } var platforms = Nexus.GetPlatforms(Nexus.RootStorage); this.platforms = platforms.Select(x => Nexus.GetPlatformInfo(Nexus.RootStorage, x)).ToArray(); if (this.platforms.Length == 0) { Logger.Warning("No interop platforms found. Make sure that the Nexus was created correctly."); return; } if (IsPlatformSupported(SwapPlatformChain.Neo)) { InitSwapper(SwapPlatformChain.Neo, new NeoInterop(this, neoAPI, _interopBlocks[SwapPlatformChain.Neo], Settings.Oracle.NeoQuickSync)); } if (IsPlatformSupported(SwapPlatformChain.Ethereum)) { InitSwapper(SwapPlatformChain.Ethereum, new EthereumInterop(this, ethAPI, _interopBlocks[SwapPlatformChain.Ethereum], Nexus.GetPlatformTokenHashes(EthereumWallet.EthereumPlatform, Nexus.RootStorage).Select(x => x.ToString().Substring(0, 40)).ToArray(), Settings.Oracle.EthConfirmations)); } if (IsPlatformSupported(SwapPlatformChain.BSC) && Nexus.PlatformExists(Nexus.RootStorage, BSCWallet.BSCPlatform)) { InitSwapper(SwapPlatformChain.BSC, new BSCInterop(this, bscAPI, _interopBlocks[SwapPlatformChain.BSC], Nexus.GetPlatformTokenHashes(BSCWallet.BSCPlatform, Nexus.RootStorage).Select(x => x.ToString().Substring(0, 40)).ToArray(), Settings.Oracle.EthConfirmations)); } Logger.Message("Available swap addresses:"); foreach (var x in SwapAddresses) { Logger.Message("platform: " + x.Key + " address: " + string.Join(", ", x.Value)); } } if (this.platforms.Length == 0) { return; } else { if (_taskMap.Count == 0) { foreach (var platform in Settings.Oracle.SwapPlatforms) { if (platform.Chain != SwapPlatformChain.Phantasma) // TODO is this IF necessary? { _taskMap.Add(platform.Chain, null); } } } } lock (StateModificationLock) { var pendingList = new StorageList(PendingTag, this.Storage); int i = 0; var count = pendingList.Count(); while (i < count) { var settlement = pendingList.Get <PendingFee>(i); if (UpdatePendingSettle(pendingList, i)) { pendingList.RemoveAt(i); count--; } else { i++; } } } ProcessCompletedTasks(); for (var j = 0; j < _taskMap.Count; j++) { var platform = _taskMap.Keys.ElementAt(j); var task = _taskMap[platform]; if (task == null) { if (_swappers.TryGetValue(platform, out ChainSwapper finder)) { _taskMap[platform] = new Task <IEnumerable <PendingSwap> >(() => { return(finder.Update()); }); } } } // start new tasks foreach (var entry in _taskMap) { var task = entry.Value; if (task != null && task.Status.Equals(TaskStatus.Created)) { task.ContinueWith(t => { Console.WriteLine($"===> task {task.ToString()} failed"); }, TaskContinuationOptions.OnlyOnFaulted); task.Start(); } } } catch (Exception e) { var logMessage = "TokenSwapper.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); } }
public void Update() { if (this.platforms == null) { if (!Nexus.HasGenesis) { return; } var platforms = Nexus.GetPlatforms(Nexus.RootStorage); this.platforms = platforms.Select(x => Nexus.GetPlatformInfo(Nexus.RootStorage, x)).ToArray(); if (this.platforms.Length == 0) { logger.Warning("No interop platforms found. Make sure that the Nexus was created correctly."); return; } _finders["neo"] = new NeoInterop(this, wifs["neo"], interopBlocks["neo"], neoscanAPI, logger); } if (this.platforms.Length == 0) { return; } var pendingList = new StorageList(PendingTag, this.Storage); int i = 0; var count = pendingList.Count(); while (i < count) { var settlement = pendingList.Get <PendingSettle>(i); if (UpdatePendingSettle(pendingList, i)) { pendingList.RemoveAt <PendingSettle>(i); count--; } else { i++; } } foreach (var finder in _finders.Values) { var swaps = finder.Update(); foreach (var swap in swaps) { if (_pendingSwaps.ContainsKey(swap.hash)) { logger.Message($"Already known swap, ignore {finder.PlatformName} swap: {swap.source} => {swap.destination}"); continue; } logger.Message($"Detected {finder.PlatformName} swap: {swap.source} => {swap.destination} hash: {swap.hash}"); _pendingSwaps[swap.hash] = swap; MapSwap(swap.source, swap.hash); MapSwap(swap.destination, swap.hash); } } }
public void SettleTransaction(Address from, string platform, string chain, Hash hash) { PlatformSwapAddress[] swapAddresses; if (platform != DomainSettings.PlatformName) { Runtime.Expect(Runtime.PlatformExists(platform), "unsupported platform"); var platformInfo = Runtime.GetPlatformByName(platform); swapAddresses = platformInfo.InteropAddresses; } else { swapAddresses = null; } Runtime.Expect(Runtime.IsWitness(from), "invalid witness"); Runtime.Expect(from.IsUser, "must be user address"); var chainHashes = _hashes.Get <string, StorageMap>(platform); Runtime.Expect(!chainHashes.ContainsKey <Hash>(hash), "hash already seen"); var interopTx = Runtime.ReadTransactionFromOracle(platform, chain, hash); Runtime.Expect(interopTx.Hash == hash, "unxpected hash"); int swapCount = 0; foreach (var transfer in interopTx.Transfers) { var count = _withdraws.Count(); var index = -1; for (int i = 0; i < count; i++) { var entry = _withdraws.Get <InteropWithdraw>(i); if (entry.destination == transfer.destinationAddress && entry.transferAmount == transfer.Value && entry.transferSymbol == transfer.Symbol) { index = i; break; } } if (index >= 0) { Runtime.Expect(Runtime.TokenExists(transfer.Symbol), "invalid token"); var token = this.Runtime.GetToken(transfer.Symbol); if (Runtime.ProtocolVersion >= 4) { if (token.Flags.HasFlag(TokenFlags.Fungible)) { Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero"); } else { Runtime.Expect(Runtime.NFTExists(transfer.Symbol, transfer.Value), $"nft {transfer.Value} must exist"); } } else { Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero"); Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible"); } Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable"); var withdraw = _withdraws.Get <InteropWithdraw>(index); _withdraws.RemoveAt <InteropWithdraw>(index); if (Runtime.ProtocolVersion >= 3) { var org = Runtime.GetOrganization(DomainSettings.ValidatorsOrganizationName); Runtime.Expect(org.IsMember(from), $"{from.Text} is not a validator node"); Runtime.TransferTokens(withdraw.feeSymbol, this.Address, from, withdraw.feeAmount); } else { Runtime.TransferTokens(withdraw.feeSymbol, this.Address, transfer.sourceAddress, withdraw.feeAmount); } swapCount++; } else if (swapAddresses != null) { foreach (var entry in swapAddresses) { if (transfer.destinationAddress == entry.LocalAddress) { Runtime.Expect(!transfer.sourceAddress.IsNull, "invalid source address"); // Here we detect if this transfer occurs between two swap addresses var isInternalTransfer = Runtime.IsPlatformAddress(transfer.sourceAddress); if (!isInternalTransfer) { Runtime.Expect(Runtime.TokenExists(transfer.Symbol), "invalid token"); var token = this.Runtime.GetToken(transfer.Symbol); if (Runtime.ProtocolVersion >= 4) { if (token.Flags.HasFlag(TokenFlags.Fungible)) { Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero"); } else { Runtime.Expect(Runtime.NFTExists(transfer.Symbol, transfer.Value), $"nft {transfer.Value} must exist"); } } else { Runtime.Expect(transfer.Value > 0, "amount must be positive and greater than zero"); Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible"); } Runtime.Expect(token.Flags.HasFlag(TokenFlags.Transferable), "token must be transferable"); Runtime.Expect(transfer.interopAddress.IsUser, "invalid destination address"); Runtime.SwapTokens(platform, transfer.sourceAddress, Runtime.Chain.Name, transfer.interopAddress, transfer.Symbol, transfer.Value); if (Runtime.ProtocolVersion >= 4 && !token.Flags.HasFlag(TokenFlags.Fungible)) { var externalNft = Runtime.ReadNFTFromOracle(platform, transfer.Symbol, transfer.Value); var ram = Serialization.Serialize(externalNft); var localNft = Runtime.ReadToken(transfer.Symbol, transfer.Value); Runtime.WriteToken(transfer.Symbol, transfer.Value, ram); } swapCount++; } break; } } } } Runtime.Expect(swapCount > 0, "nothing to settle"); chainHashes.Set <Hash, Hash>(hash, Runtime.Transaction.Hash); Runtime.Notify(EventKind.ChainSwap, from, new TransactionSettleEventData(hash, platform, chain)); }
public void Update() { try { if (this.platforms == null) { if (!Nexus.HasGenesis) { return; } var platforms = Nexus.GetPlatforms(Nexus.RootStorage); this.platforms = platforms.Select(x => Nexus.GetPlatformInfo(Nexus.RootStorage, x)).ToArray(); if (this.platforms.Length == 0) { Logger.Warning("No interop platforms found. Make sure that the Nexus was created correctly."); return; } _swappers["neo"] = new NeoInterop(this, neoAPI, interopBlocks["neo"], Settings.Oracle.NeoQuickSync); SwapAddresses["neo"] = _swappers["neo"].LocalAddress; _swappers["ethereum"] = new EthereumInterop(this, ethAPI, interopBlocks["ethereum"], Nexus.GetPlatformTokenHashes("ethereum", Nexus.RootStorage).Select(x => x.ToString().Substring(0, 40)).ToArray(), Settings.Oracle.EthConfirmations); SwapAddresses["ethereum"] = _swappers["ethereum"].LocalAddress; Logger.Message("Available swap addresses:"); foreach (var x in SwapAddresses) { Logger.Message("platform: " + x.Key + " address: " + x.Value); } } if (this.platforms.Length == 0) { return; } else { if (taskDict.Count == 0) { foreach (var platform in this.platforms) { taskDict.Add(platform.Name, null); } } } lock (StateModificationLock) { var pendingList = new StorageList(PendingTag, this.Storage); int i = 0; var count = pendingList.Count(); while (i < count) { var settlement = pendingList.Get <PendingFee>(i); if (UpdatePendingSettle(pendingList, i)) { pendingList.RemoveAt(i); count--; } else { i++; } } } ProcessCompletedTasks(); for (var j = 0; j < taskDict.Count; j++) { var platform = taskDict.Keys.ElementAt(j); var task = taskDict[platform]; if (task == null) { if (_swappers.TryGetValue(platform, out ChainSwapper finder)) { taskDict[platform] = new Task <IEnumerable <PendingSwap> >(() => { return(finder.Update()); }); } } } // start new tasks foreach (var entry in taskDict) { var task = entry.Value; if (task != null && task.Status.Equals(TaskStatus.Created)) { task.ContinueWith(t => { Console.WriteLine($"===> task {task.ToString()} failed"); }, TaskContinuationOptions.OnlyOnFaulted); task.Start(); } } } catch (Exception e) { var logMessage = "TokenSwapper.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); } }