public BigInteger GetActiveValidators()
 {
     return(_entryList.Count());
 }
Exemple #2
0
        private BigInteger GetBlockHeight()
        {
            var hashList = new StorageList(BlockHeightListTag, this.Storage);

            return(hashList.Count());
        }
Exemple #3
0
        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 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 balances = Runtime.Chain.GetTokenBalances(token.Symbol);
            var fee      = GetTableFee(tableID);

            Runtime.Expect(Runtime.Nexus.TransferTokens(token.Symbol, this.Storage, balances, 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);
            }
        }
Exemple #5
0
        public void AddBlock(Block block, IEnumerable <Transaction> transactions, BigInteger minimumFee)
        {
            /*if (CurrentEpoch != null && CurrentEpoch.IsSlashed(Timestamp.Now))
             * {
             *  return false;
             * }*/

            var lastBlockHash = GetLastBlockHash();
            var lastBlock     = GetBlockByHash(lastBlockHash);

            if (lastBlock != null)
            {
                if (lastBlock.Height != block.Height - 1)
                {
                    throw new BlockGenerationException($"height of block should be {lastBlock.Height + 1}");
                }

                if (block.PreviousHash != lastBlock.Hash)
                {
                    throw new BlockGenerationException($"previous hash should be {lastBlock.PreviousHash}");
                }
            }

            var inputHashes = new HashSet <Hash>(transactions.Select(x => x.Hash));

            foreach (var hash in block.TransactionHashes)
            {
                if (!inputHashes.Contains(hash))
                {
                    throw new BlockGenerationException($"missing in inputs transaction with hash {hash}");
                }
            }

            var outputHashes = new HashSet <Hash>(block.TransactionHashes);

            foreach (var tx in transactions)
            {
                if (!outputHashes.Contains(tx.Hash))
                {
                    throw new BlockGenerationException($"missing in outputs transaction with hash {tx.Hash}");
                }
            }

            // TODO avoid fetching this every time
            var expectedProtocol = Nexus.GetGovernanceValue(Nexus.RootStorage, Nexus.NexusProtocolVersionTag);

            if (block.Protocol != expectedProtocol)
            {
                throw new BlockGenerationException($"invalid protocol number {block.Protocol}, expected protocol {expectedProtocol}");
            }

            foreach (var tx in transactions)
            {
                if (!tx.IsValid(this))
                {
                    throw new InvalidTransactionException(tx.Hash, $"invalid transaction with hash {tx.Hash}");
                }
            }

            var changeSet = new StorageChangeSetContext(this.Storage);

            var oracle = Nexus.CreateOracleReader();

            foreach (var tx in transactions)
            {
                byte[] result;
                try
                {
                    if (ExecuteTransaction(tx, block.Timestamp, changeSet, block.Notify, oracle, minimumFee, out result))
                    {
                        if (result != null)
                        {
                            block.SetResultForHash(tx.Hash, result);
                        }
                    }
                    else
                    {
                        throw new InvalidTransactionException(tx.Hash, $"execution failed");
                    }
                }
                catch (Exception e)
                {
                    if (e.InnerException != null)
                    {
                        e = e.InnerException;
                    }
                    throw new InvalidTransactionException(tx.Hash, e.Message);
                }
            }

            block.MergeOracle(oracle);

            var hashList            = new StorageList(BlockHeightListTag, this.Storage);
            var expectedBlockHeight = hashList.Count() + 1;

            if (expectedBlockHeight != block.Height)
            {
                throw new ChainException("unexpected block height");
            }

            // from here on, the block is accepted
            changeSet.Execute();

            hashList.Add <Hash>(block.Hash);

            var blockMap = new StorageMap(BlockHashMapTag, this.Storage);

            blockMap.Set <Hash, Block>(block.Hash, block);

            var txMap      = new StorageMap(TransactionHashMapTag, this.Storage);
            var txBlockMap = new StorageMap(TxBlockHashMapTag, this.Storage);

            foreach (Transaction tx in transactions)
            {
                txMap.Set <Hash, Transaction>(tx.Hash, tx);
                txBlockMap.Set <Hash, Hash>(tx.Hash, block.Hash);
            }

            var blockValidator = GetValidatorForBlock(block);

            if (blockValidator.IsNull)
            {
                throw new BlockGenerationException("no validator for this block");
            }

            Nexus.PluginTriggerBlock(this, block);
        }
        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))
                    {
                        continue;
                    }

                    logger.Message($"Detected {finder.PlatformName} swap: {swap.source} => {swap.destination}");
                    _pendingSwaps[swap.hash] = swap;
                    MapSwap(swap.source, swap.hash);
                    MapSwap(swap.destination, swap.hash);
                }
            }
        }
Exemple #7
0
        public void AddBlock(Block block, IEnumerable <Transaction> transactions, BigInteger minimumFee)
        {
            if (!block.IsSigned)
            {
                throw new BlockGenerationException($"block must be signed");
            }

            StorageChangeSetContext changeSet;

            using (var m = new ProfileMarker("ValidateBlock"))
                changeSet = ValidateBlock(block, transactions, minimumFee);

            var unsignedBytes = block.ToByteArray(false);

            if (!block.Signature.Verify(unsignedBytes, block.Validator))
            {
                throw new BlockGenerationException($"block signature does not match validator {block.Validator.Text}");
            }

            var hashList            = new StorageList(BlockHeightListTag, this.Storage);
            var expectedBlockHeight = hashList.Count() + 1;

            if (expectedBlockHeight != block.Height)
            {
                throw new ChainException("unexpected block height");
            }

            // from here on, the block is accepted
            using (var m = new ProfileMarker("changeSet.Execute"))
                changeSet.Execute();

            hashList.Add <Hash>(block.Hash);

            using (var m = new ProfileMarker("Compress"))
            {
                var blockMap   = new StorageMap(BlockHashMapTag, this.Storage);
                var blockBytes = block.ToByteArray(true);
                blockBytes = CompressionUtils.Compress(blockBytes);
                blockMap.Set <Hash, byte[]>(block.Hash, blockBytes);

                var txMap      = new StorageMap(TransactionHashMapTag, this.Storage);
                var txBlockMap = new StorageMap(TxBlockHashMapTag, this.Storage);
                foreach (Transaction tx in transactions)
                {
                    var txBytes = tx.ToByteArray(true);
                    txBytes = CompressionUtils.Compress(txBytes);
                    txMap.Set <Hash, byte[]>(tx.Hash, txBytes);
                    txBlockMap.Set <Hash, Hash>(tx.Hash, block.Hash);
                }
            }

            using (var m = new ProfileMarker("AddressBlockHashMapTag"))
                foreach (var transaction in transactions)
                {
                    var addresses = new HashSet <Address>();
                    var events    = block.GetEventsForTransaction(transaction.Hash);

                    foreach (var evt in events)
                    {
                        if (evt.Address.IsSystem)
                        {
                            continue;
                        }

                        addresses.Add(evt.Address);
                    }

                    var addressTxMap = new StorageMap(AddressBlockHashMapTag, this.Storage);
                    foreach (var address in addresses)
                    {
                        var addressList = addressTxMap.Get <Address, StorageList>(address);
                        addressList.Add <Hash>(transaction.Hash);
                    }
                }

            using (var m = new ProfileMarker("Nexus.PluginTriggerBlock"))
                Nexus.PluginTriggerBlock(this, block);
        }
        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);
        }
Exemple #9
0
        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 = _platformHashes.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");
                    Runtime.Expect(token.Flags.HasFlag(TokenFlags.Swappable), "transfer token must be swappable");

                    var withdraw = _withdraws.Get <InteropWithdraw>(index);
                    _withdraws.RemoveAt(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);
                    }

                    RegisterHistory(hash, withdraw.hash, DomainSettings.PlatformName, Runtime.Chain.Name, transfer.sourceAddress, hash, platform, chain, withdraw.destination, transfer.Symbol, transfer.Value);
                    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(token.Flags.HasFlag(TokenFlags.Swappable), "transfer token must be swappable");

                                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(from, transfer.Symbol, transfer.Value, ram); // TODO "from" here might fail due to contract triggers, review this later
                                }

                                var settleHash = Runtime.Transaction.Hash;
                                RegisterHistory(settleHash, hash, platform, chain, transfer.sourceAddress, settleHash, DomainSettings.PlatformName, Runtime.Chain.Name, transfer.interopAddress, transfer.Symbol, transfer.Value);

                                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));
        }
Exemple #10
0
        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));
        }