示例#1
0
        private void RecordTransferHistory(Snapshot snapshot, UInt160 scriptHash, UInt160 from, UInt160 to, BigInteger amount, UInt256 txHash, ref ushort transferIndex)
        {
            if (!_shouldTrackHistory)
            {
                return;
            }
            if (_recordNullAddressHistory || from != UInt160.Zero)
            {
                _transfersSent.Add(new Nep5TransferKey(from,
                                                       snapshot.GetHeader(snapshot.Height).Timestamp, scriptHash, transferIndex),
                                   new Nep5Transfer
                {
                    Amount         = amount,
                    UserScriptHash = to,
                    BlockIndex     = snapshot.Height,
                    TxHash         = txHash
                });
            }

            if (_recordNullAddressHistory || to != UInt160.Zero)
            {
                _transfersReceived.Add(new Nep5TransferKey(to,
                                                           snapshot.GetHeader(snapshot.Height).Timestamp, scriptHash, transferIndex),
                                       new Nep5Transfer
                {
                    Amount         = amount,
                    UserScriptHash = from,
                    BlockIndex     = snapshot.Height,
                    TxHash         = txHash
                });
            }
            transferIndex++;
        }
示例#2
0
        private bool AddClaims(JArray claimableOutput, ref Fixed8 runningTotal, int maxClaims,
                               Snapshot snapshot, DataCache <UInt256, SpentCoinState> storeSpentCoins,
                               KeyValuePair <UserSystemAssetCoinOutputsKey, UserSystemAssetCoinOutputs> claimableInTx)
        {
            foreach (var claimTransaction in claimableInTx.Value.AmountByTxIndex)
            {
                var utxo = new JObject();
                var txId = claimableInTx.Key.TxHash.ToString().Substring(2);
                utxo["txid"] = txId;
                utxo["n"]    = claimTransaction.Key;
                var spentCoinState = storeSpentCoins.TryGet(claimableInTx.Key.TxHash);
                var startHeight    = spentCoinState.TransactionHeight;
                var endHeight      = spentCoinState.Items[claimTransaction.Key];
                CalculateClaimable(snapshot, claimTransaction.Value, startHeight, endHeight, out var generated,
                                   out var sysFee);
                var unclaimed = generated + sysFee;
                utxo["value"]        = (double)(decimal)claimTransaction.Value;
                utxo["start_height"] = startHeight;
                utxo["end_height"]   = endHeight;
                utxo["generated"]    = (double)(decimal)generated;
                utxo["sys_fee"]      = (double)(decimal)sysFee;
                utxo["unclaimed"]    = (double)(decimal)unclaimed;
                runningTotal        += unclaimed;
                claimableOutput.Add(utxo);
                if (claimableOutput.Count > maxClaims)
                {
                    return(false);
                }
            }

            return(true);
        }
示例#3
0
        private JObject ProcessGetClaimableSpents(JArray parameters)
        {
            UInt160 scriptHash = GetScriptHashFromParam(parameters[0].AsString());
            var     dbCache    = new DbCache <UserSystemAssetCoinOutputsKey, UserSystemAssetCoinOutputs>(
                _db, null, null, SystemAssetSpentUnclaimedCoinsPrefix);

            JObject json      = new JObject();
            JArray  claimable = new JArray();

            json["claimable"] = claimable;
            json["address"]   = scriptHash.ToAddress();

            Fixed8 totalUnclaimed = Fixed8.Zero;

            using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot())
            {
                var    storeSpentCoins = snapshot.SpentCoins;
                byte[] prefix = new [] { (byte)1 }.Concat(scriptHash.ToArray()).ToArray();
                foreach (var claimableInTx in dbCache.Find(prefix))
                {
                    if (!AddClaims(claimable, ref totalUnclaimed, _rpcMaxUnspents, snapshot, storeSpentCoins,
                                   claimableInTx))
                    {
                        break;
                    }
                }
            }
            json["unclaimed"] = (double)(decimal)totalUnclaimed;
            return(json);
        }
        private void RecordTransferHistory(Snapshot snapshot, UInt160 scriptHash, UInt160 from, UInt160 to, BigInteger amount, UInt256 txHash, ref ushort transferIndex)
        {
            Header header = snapshot.GetHeader(snapshot.Height);

            JObject transfer = new JObject();

            transfer["from"]          = from.ToAddress();
            transfer["to"]            = to.ToAddress();
            transfer["time"]          = header.Timestamp;
            transfer["scripthash"]    = scriptHash.ToString();
            transfer["amount"]        = amount.ToString();
            transfer["block"]         = snapshot.Height;
            transfer["txid"]          = txHash.ToString();
            transfer["transferindex"] = $"{snapshot.Height}.{transferIndex}";

            Document a = Document.FromJson(transfer.ToString());

            TransfersTable.UpdateItemAsync(a);

            transferIndex++;

            // store the asset metadata if this is the first time we're seeing this scripthash
            if (Assets.Count == 0)
            {
                PreloadAssets();
            }
            if (!Assets.Contains(scriptHash.ToString()))
            {
                AddAsset(snapshot, scriptHash);
            }
        }
示例#5
0
        private void CalculateClaimable(Snapshot snapshot, Fixed8 value, uint startHeight, uint endHeight, out Fixed8 generated, out Fixed8 sysFee)
        {
            uint amount = 0;
            uint ustart = startHeight / Blockchain.DecrementInterval;

            if (ustart < Blockchain.GenerationAmount.Length)
            {
                uint istart = startHeight % Blockchain.DecrementInterval;
                uint uend   = endHeight / Blockchain.DecrementInterval;
                uint iend   = endHeight % Blockchain.DecrementInterval;
                if (uend >= Blockchain.GenerationAmount.Length)
                {
                    uend = (uint)Blockchain.GenerationAmount.Length;
                    iend = 0;
                }
                if (iend == 0)
                {
                    uend--;
                    iend = Blockchain.DecrementInterval;
                }
                while (ustart < uend)
                {
                    amount += (Blockchain.DecrementInterval - istart) * Blockchain.GenerationAmount[ustart];
                    ustart++;
                    istart = 0;
                }
                amount += (iend - istart) * Blockchain.GenerationAmount[ustart];
            }

            Fixed8 fractionalShare = value / 100000000;

            generated = fractionalShare * amount;
            sysFee    = fractionalShare * (GetSysFeeAmountForHeight(snapshot.Blocks, endHeight - 1) -
                                           (startHeight == 0 ? 0 : GetSysFeeAmountForHeight(snapshot.Blocks, startHeight - 1)));
        }
示例#6
0
        public void OnPersist(Snapshot snapshot, IReadOnlyList <Blockchain.ApplicationExecuted> applicationExecutedList)
        {
            if (snapshot.PersistingBlock.Index > _lastPersistedBlock + 1)
            {
                ProcessSkippedBlocks(snapshot);
            }

            _shouldPersistBlock = ProcessBlock(snapshot, snapshot.PersistingBlock);
        }
示例#7
0
        public void OnCommit(Snapshot snapshot)
        {
            _balances.Commit();
            if (_shouldTrackHistory)
            {
                _transfersSent.Commit();
                _transfersReceived.Commit();
            }

            _db.Write(WriteOptions.Default, _writeBatch);
        }
示例#8
0
 public void OnCommit(Snapshot snapshot)
 {
     if (!_shouldPersistBlock)
     {
         return;
     }
     _userUnspentCoins.Commit();
     if (_shouldTrackUnclaimed)
     {
         _userSpentUnclaimedCoins.Commit();
     }
     _db.Write(WriteOptions.Default, _writeBatch);
 }
示例#9
0
        public void PreProcess(HttpContext context, string method, JArray _params)
        {
            if (method == "sendrawtransaction")
            {
                JObject res = new JObject();

                Transaction tx       = Transaction.DeserializeFrom(_params[0].AsString().HexToBytes());
                Snapshot    snapshot = Blockchain.Singleton.GetSnapshot();
                MemoryPool  MemPool  = Blockchain.Singleton.MemPool;

                PreVerify(tx, snapshot, MemPool.GetVerifiedTransactions());
            }
        }
示例#10
0
        private JObject ProcessGetUnclaimed(JArray parameters)
        {
            UInt160 scriptHash = GetScriptHashFromParam(parameters[0].AsString());
            JObject json       = new JObject();

            Fixed8 available   = Fixed8.Zero;
            Fixed8 unavailable = Fixed8.Zero;
            var    spentsCache = new DbCache <UserSystemAssetCoinOutputsKey, UserSystemAssetCoinOutputs>(
                _db, null, null, SystemAssetSpentUnclaimedCoinsPrefix);
            var unspentsCache = new DbCache <UserSystemAssetCoinOutputsKey, UserSystemAssetCoinOutputs>(
                _db, null, null, SystemAssetUnspentCoinsPrefix);

            using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot())
            {
                var    storeSpentCoins = snapshot.SpentCoins;
                byte[] prefix = new [] { (byte)1 }.Concat(scriptHash.ToArray()).ToArray();
                foreach (var claimableInTx in spentsCache.Find(prefix))
                {
                    var spentCoinState = storeSpentCoins.TryGet(claimableInTx.Key.TxHash);
                    foreach (var claimTxIndex in claimableInTx.Value.AmountByTxIndex)
                    {
                        var startHeight = spentCoinState.TransactionHeight;
                        var endHeight   = spentCoinState.Items[claimTxIndex.Key];
                        CalculateClaimable(snapshot, claimTxIndex.Value, startHeight, endHeight, out var generated,
                                           out var sysFee);
                        available += generated + sysFee;
                    }
                }

                var transactionsCache = snapshot.Transactions;
                foreach (var claimableInTx in unspentsCache.Find(prefix))
                {
                    var transaction = transactionsCache.TryGet(claimableInTx.Key.TxHash);

                    foreach (var claimTxIndex in claimableInTx.Value.AmountByTxIndex)
                    {
                        var startHeight = transaction.BlockIndex;
                        var endHeight   = Blockchain.Singleton.Height;
                        CalculateClaimable(snapshot, claimTxIndex.Value, startHeight, endHeight,
                                           out var generated,
                                           out var sysFee);
                        unavailable += generated + sysFee;
                    }
                }
            }

            json["available"]   = (double)(decimal)available;
            json["unavailable"] = (double)(decimal)unavailable;
            json["unclaimed"]   = (double)(decimal)(available + unavailable);
            return(json);
        }
示例#11
0
 private void VerifyWitnesses(IVerifiable verifiable, Snapshot snapshot)
 {
     UInt160[] hashes;
     try
     {
         hashes = verifiable.GetScriptHashesForVerifying(snapshot);
     }
     catch (InvalidOperationException)
     {
         throw new RpcException(-500, "InvalidOperationException getting script hashes for verifying");
     }
     if (hashes.Length != verifiable.Witnesses.Length)
     {
         throw new RpcException(-500, "Number of script hashes doesn't match number of witnesses");
     }
     for (int i = 0; i < hashes.Length; i++)
     {
         byte[] verification = verifiable.Witnesses[i].VerificationScript;
         if (verification.Length == 0)
         {
             using (ScriptBuilder sb = new ScriptBuilder())
             {
                 sb.EmitAppCall(hashes[i].ToArray());
                 verification = sb.ToArray();
             }
         }
         else
         {
             if (hashes[i] != verifiable.Witnesses[i].ScriptHash)
             {
                 throw new RpcException(-500, $"Script hash {i} didn't match witness hash {i}");
             }
         }
         using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, Fixed8.Zero))
         {
             engine.LoadScript(verification);
             engine.LoadScript(verifiable.Witnesses[i].InvocationScript);
             engine.Execute();
             if (engine.State.HasFlag(VMState.FAULT))
             {
                 throw new RpcException(-500, "VM returned FAULT during verification trigger (invalid signature data)");
             }
             if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean())
             {
                 throw new RpcException(-500, "VM returned false from verification trigger (bad signature)");
             }
         }
     }
 }
示例#12
0
        private void ProcessSkippedBlocks(Snapshot snapshot)
        {
            for (uint blockIndex = _lastPersistedBlock + 1; blockIndex < snapshot.PersistingBlock.Index; blockIndex++)
            {
                var skippedBlock = Blockchain.Singleton.Store.GetBlock(blockIndex);
                if (skippedBlock.Transactions.Length <= 1)
                {
                    _lastPersistedBlock = skippedBlock.Index;
                    continue;
                }

                _shouldPersistBlock = ProcessBlock(snapshot, skippedBlock);
                OnCommit(snapshot);
            }
        }
示例#13
0
        public void OnCommit(Snapshot snapshot)
        {
            _balances.Commit(snapshot.Height, EnumDataTpye.nep5);
            foreach (var k in _balances.dictionary.Keys)
            {
                Nep5State nep5State = new Nep5State()
                {
                    Address = k.UserScriptHash, AssetHash = k.AssetScriptHash, Balance = _balances.TryGet(k)?.Balance ?? 0, LastUpdatedBlock = _balances.TryGet(k)?.LastUpdatedBlock ?? snapshot.Height
                };
                RecordToMongo(nep5State);
            }
            if (_shouldTrackHistory)
            {
                _transfersSent.Commit(snapshot.Height, EnumDataTpye.nep5);
                _transfersReceived.Commit(snapshot.Height, EnumDataTpye.nep5);
            }

            _db.Write(WriteOptions.Default, _writeBatch);
        }
        private void AddAsset(Snapshot snapshot, UInt160 scriptHash)
        {
            JObject    asset = new JObject();
            string     n     = GetAssetString(snapshot, scriptHash, "name");
            string     s     = GetAssetString(snapshot, scriptHash, "symbol");
            BigInteger d     = GetAssetInteger(snapshot, scriptHash, "decimals");

            if (n.Length > 0 && s.Length > 0)
            {
                ContractState contract = snapshot.Contracts.TryGet(scriptHash);
                asset["scripthash"] = scriptHash.ToString();
                asset["firstseen"]  = snapshot.Height;
                asset["name"]       = n;
                asset["symbol"]     = s;
                asset["decimals"]   = d.ToString();
                asset["state"]      = contract.ToJson();
            }
            Document b = Document.FromJson(asset.ToString());

            AssetsTable.UpdateItemAsync(b);
            Assets.Add(scriptHash.ToString());
        }
        /*
         * private void RefreshContractList(Snapshot snapshot)
         * {
         *  Contracts = new HashSet<string>();
         *  int idx = 0;
         *  foreach (KeyValuePair<UInt160, ContractState> dc in snapshot.Contracts.Find())
         *  {
         *      Console.WriteLine($"Adding contract {idx}: {dc.Key.ToString()}");
         *      JObject c = dc.Value.ToJson();
         *      c["idx"] = idx;
         *
         *      Document b = Document.FromJson(c.ToString());
         *      ContractsTable.UpdateItemAsync(b);
         *      Contracts.Add(dc.Key.ToString());
         *
         *      idx++;
         *  }
         *  ContractCount = idx;
         * }
         */
        private void AddNewContracts(Snapshot snapshot)
        {
            int idx = Contracts.Count;

            foreach (KeyValuePair <UInt160, ContractState> dc in snapshot.Contracts.Find())
            {
                string cs = dc.Key.ToString();
                if (!Contracts.Contains(cs))
                {
                    Console.WriteLine($"Adding contract {idx}: {cs}");
                    JObject c = dc.Value.ToJson();
                    c["idx"]   = idx;
                    c["block"] = snapshot.Height;
                    c["time"]  = snapshot.PersistingBlock.Timestamp;

                    Document b = Document.FromJson(c.ToString());
                    ContractsTable.UpdateItemAsync(b);
                    Contracts.Add(cs);
                    idx++;
                }
            }
        }
        private void AddBlock(Snapshot snapshot)
        {
            Block block = snapshot.PersistingBlock;
            ulong idx   = block.Index;

            Console.WriteLine($"Adding block {idx} to DynamoDB table");
            ulong blocktime = 0;

            if (block.Index > 0)
            {
                ulong  timestamp     = block.Timestamp;
                Header header        = snapshot.GetHeader((uint)idx - 1);
                ulong  lasttimestamp = header.Timestamp;
                blocktime = timestamp - lasttimestamp;
            }
            JObject j = block.ToJson();

            j["blocktime"] = blocktime;
            Document d = Document.FromJson(j.ToString());

            BlocksTable.UpdateItemAsync(d);
        }
        public BigInteger GetAssetInteger(Snapshot snapshot, UInt160 scripthash, string operation)
        {
            byte[] script;
            using (ScriptBuilder sb = new ScriptBuilder())
            {
                sb.EmitAppCall(scripthash, operation,
                               scripthash.ToArray());
                script = sb.ToArray();
            }

            ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, extraGAS: 100000000);

            if (engine.State.HasFlag(VMState.FAULT))
            {
                return(0);
            }
            if (engine.ResultStack.Count <= 0)
            {
                return(0);
            }
            return(engine.ResultStack.Pop().GetBigInteger());
        }
示例#18
0
        private void PreVerify(Transaction tx, Snapshot snapshot, IEnumerable <Transaction> mempool)
        {
            if (tx.Size > Transaction.MaxTransactionSize)
            {
                throw new RpcException(-500, "Size exceeds maximum transaction size");
            }
            for (int i = 1; i < tx.Inputs.Length; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    if (tx.Inputs[i].PrevHash == tx.Inputs[j].PrevHash && tx.Inputs[i].PrevIndex == tx.Inputs[j].PrevIndex)
                    {
                        throw new RpcException(-500, "Duplicate inputs");
                    }
                }
            }
            if (mempool.Where(p => p != tx).SelectMany(p => p.Inputs).Intersect(tx.Inputs).Count() > 0)
            {
                throw new RpcException(-500, "Transaction already in mempool");
            }
            CheckDoubleSpend(snapshot, tx);
            foreach (var group in tx.Outputs.GroupBy(p => p.AssetId))
            {
                AssetState asset = snapshot.Assets.TryGet(group.Key);
                if (asset == null)
                {
                    throw new RpcException(-500, "Asset value is null");
                }
                if (asset.Expiration <= snapshot.Height + 1 && asset.AssetType != AssetType.GoverningToken && asset.AssetType != AssetType.UtilityToken)
                {
                    throw new RpcException(-500, "Asset registration has expired");
                }
                foreach (TransactionOutput output in group)
                {
                    if (output.Value.GetData() % (long)Math.Pow(10, 8 - asset.Precision) != 0)
                    {
                        throw new RpcException(-500, "Invalid precision for asset output");
                    }
                }
            }
            TransactionResult[] results = tx.GetTransactionResults()?.ToArray();
            if (results == null)
            {
                throw new RpcException(-500, "No TransactionResults found");
            }
            TransactionResult[] results_destroy = results.Where(p => p.Amount > Fixed8.Zero).ToArray();
            if (results_destroy.Length > 1)
            {
                throw new RpcException(-500, "TransactionResults destroy length > 1");
            }
            if (results_destroy.Length == 1 && results_destroy[0].AssetId != Blockchain.UtilityToken.Hash)
            {
                throw new RpcException(-500, "TransactionResults destroy length equals 1 but asset is not GAS");
            }
            if (tx.SystemFee > Fixed8.Zero && (results_destroy.Length == 0 || results_destroy[0].Amount < tx.SystemFee))
            {
                throw new RpcException(-500, "System fee > 0 but TransactionResults destroy length is zero or amount is less than fee");
            }
            TransactionResult[] results_issue = results.Where(p => p.Amount < Fixed8.Zero).ToArray();
            switch (tx.Type)
            {
            case TransactionType.MinerTransaction:
            case TransactionType.ClaimTransaction:
                if (results_issue.Any(p => p.AssetId != Blockchain.UtilityToken.Hash))
                {
                    throw new RpcException(-500, "ClaimTransaction but asset is not GAS");
                }
                break;

            case TransactionType.IssueTransaction:
                if (results_issue.Any(p => p.AssetId == Blockchain.UtilityToken.Hash))
                {
                    throw new RpcException(-500, "IssueTransaction but asset hash matches GAS");
                }
                break;

            case TransactionType.InvocationTransaction:
                InvocationTransaction it = (InvocationTransaction)tx;
                if (it.Gas.GetData() % 100000000 != 0)
                {
                    throw new RpcException(-500, "Gas parameter is non-integer");
                }
                break;

            default:
                if (results_issue.Length > 0)
                {
                    throw new RpcException(-500, "No assets issued");
                }
                break;
            }
            if (tx.Attributes.Count(p => p.Usage == TransactionAttributeUsage.ECDH02 || p.Usage == TransactionAttributeUsage.ECDH03) > 1)
            {
                throw new RpcException(-500, "Multiple occurances of ECDH02/ECDH03 transaction attribute usages");
            }
            VerifyWitnesses(tx, snapshot);
        }
        private void HandleNotification(Snapshot snapshot, Transaction transaction, UInt160 scriptHash,
                                        VM.Types.Array stateItems,
                                        Dictionary <Nep5BalanceKey, Nep5Balance> nep5BalancesChanged, ref ushort transferIndex)
        {
            if (stateItems.Count == 0)
            {
                return;
            }
            // Event name should be encoded as a byte array.
            if (!(stateItems[0] is VM.Types.ByteArray))
            {
                return;
            }
            var eventName = Encoding.UTF8.GetString(stateItems[0].GetByteArray());

            if (eventName != "Transfer")
            {
                return;
            }
            if (stateItems.Count < 4)
            {
                return;
            }

            if (!(stateItems[1] is null) && !(stateItems[1] is VM.Types.ByteArray))
            {
                return;
            }
            if (!(stateItems[2] is null) && !(stateItems[2] is VM.Types.ByteArray))
            {
                return;
            }
            var amountItem = stateItems[3];

            if (!(amountItem is VM.Types.ByteArray || amountItem is VM.Types.Integer))
            {
                return;
            }
            byte[] fromBytes = stateItems[1]?.GetByteArray();
            if (fromBytes?.Length != 20)
            {
                fromBytes = null;
            }
            byte[] toBytes = stateItems[2]?.GetByteArray();
            if (toBytes?.Length != 20)
            {
                toBytes = null;
            }
            if (fromBytes == null && toBytes == null)
            {
                return;
            }
            var from = new UInt160(fromBytes);
            var to   = new UInt160(toBytes);

            if (fromBytes != null)
            {
                var fromKey = new Nep5BalanceKey(from, scriptHash);
                if (!nep5BalancesChanged.ContainsKey(fromKey))
                {
                    nep5BalancesChanged.Add(fromKey, new Nep5Balance());
                }
            }

            if (toBytes != null)
            {
                var toKey = new Nep5BalanceKey(to, scriptHash);
                if (!nep5BalancesChanged.ContainsKey(toKey))
                {
                    nep5BalancesChanged.Add(toKey, new Nep5Balance());
                }
            }
            RecordTransferHistory(snapshot, scriptHash, from, to, amountItem.GetBigInteger(), transaction.Hash, ref transferIndex);
        }
示例#20
0
        public void OnPersist(Snapshot snapshot, IReadOnlyList <Blockchain.ApplicationExecuted> applicationExecutedList)
        {
            // Start freshly with a new DBCache for each block.
            ResetBatch();
            Dictionary <Nep5BalanceKey, Nep5Balance> nep5BalancesChanged = new Dictionary <Nep5BalanceKey, Nep5Balance>();

            ushort transferIndex = 0;

            foreach (Blockchain.ApplicationExecuted appExecuted in applicationExecutedList)
            {
                // Executions that fault won't modify storage, so we can skip them.
                if (appExecuted.VMState.HasFlag(VMState.FAULT))
                {
                    continue;
                }
                foreach (var notifyEventArgs in appExecuted.Notifications)
                {
                    if (!(notifyEventArgs?.State is VM.Types.Array stateItems) || stateItems.Count == 0 ||
                        !(notifyEventArgs.ScriptContainer is Transaction transaction))
                    {
                        continue;
                    }
                    HandleNotification(snapshot, transaction, notifyEventArgs.ScriptHash, stateItems,
                                       nep5BalancesChanged, ref transferIndex);
                }
            }

            foreach (var nep5BalancePair in nep5BalancesChanged)
            {
                // get guarantee accurate balances by calling balanceOf for keys that changed.
                byte[] script;
                using (ScriptBuilder sb = new ScriptBuilder())
                {
                    sb.EmitAppCall(nep5BalancePair.Key.AssetScriptHash, "balanceOf",
                                   nep5BalancePair.Key.UserScriptHash.ToArray());
                    script = sb.ToArray();
                }

                ApplicationEngine engine = ApplicationEngine.Run(script, snapshot);
                if (engine.State.HasFlag(VMState.FAULT))
                {
                    continue;
                }
                if (engine.ResultStack.Count <= 0)
                {
                    continue;
                }
                nep5BalancePair.Value.Balance          = engine.ResultStack.Pop().GetBigInteger();
                nep5BalancePair.Value.LastUpdatedBlock = snapshot.Height;
                if (nep5BalancePair.Value.Balance == 0)
                {
                    _balances.Delete(nep5BalancePair.Key);
                    continue;
                }
                var itemToChange = _balances.GetAndChange(nep5BalancePair.Key, () => nep5BalancePair.Value);
                if (itemToChange != nep5BalancePair.Value)
                {
                    itemToChange.FromReplica(nep5BalancePair.Value);
                }
            }
        }
示例#21
0
        private void HandleNotification(Snapshot snapshot, Transaction transaction, UInt160 scriptHash,
                                        VM.Types.Array stateItems,
                                        Dictionary <Nep5BalanceKey, Nep5Balance> nep5BalancesChanged, ref ushort transferIndex)
        {
            if (stateItems.Count == 0)
            {
                return;
            }
            // Event name should be encoded as a byte array.
            if (!(stateItems[0] is VM.Types.ByteArray))
            {
                return;
            }
            var eventName = Encoding.UTF8.GetString(stateItems[0].GetByteArray());

            if (_shouldTrackNonStandardMintTokensEvent && eventName == "mintTokens")
            {
                if (stateItems.Count < 4)
                {
                    return;
                }
                // This is not an official standard but at least one token uses it, and so it is needed for proper
                // balance tracking to support all tokens in use.
                if (!(stateItems[2] is VM.Types.ByteArray))
                {
                    return;
                }
                byte[] mintToBytes = stateItems[2].GetByteArray();
                if (mintToBytes.Length != 20)
                {
                    return;
                }
                var mintTo = new UInt160(mintToBytes);

                var mintAmountItem = stateItems[3];
                if (!(mintAmountItem is VM.Types.ByteArray || mintAmountItem is VM.Types.Integer))
                {
                    return;
                }

                var toKey = new Nep5BalanceKey(mintTo, scriptHash);
                if (!nep5BalancesChanged.ContainsKey(toKey))
                {
                    nep5BalancesChanged.Add(toKey, new Nep5Balance());
                }
                RecordTransferHistory(snapshot, scriptHash, UInt160.Zero, mintTo, mintAmountItem.GetBigInteger(), transaction.Hash, ref transferIndex);
                return;
            }
            if (eventName != "transfer")
            {
                return;
            }
            if (stateItems.Count < 4)
            {
                return;
            }

            if (!(stateItems[1] is null) && !(stateItems[1] is VM.Types.ByteArray))
            {
                return;
            }
            if (!(stateItems[2] is null) && !(stateItems[2] is VM.Types.ByteArray))
            {
                return;
            }
            var amountItem = stateItems[3];

            if (!(amountItem is VM.Types.ByteArray || amountItem is VM.Types.Integer))
            {
                return;
            }
            byte[] fromBytes = stateItems[1]?.GetByteArray();
            if (fromBytes?.Length != 20)
            {
                fromBytes = null;
            }
            byte[] toBytes = stateItems[2]?.GetByteArray();
            if (toBytes?.Length != 20)
            {
                toBytes = null;
            }
            if (fromBytes == null && toBytes == null)
            {
                return;
            }
            var from = new UInt160(fromBytes);
            var to   = new UInt160(toBytes);

            if (fromBytes != null)
            {
                var fromKey = new Nep5BalanceKey(from, scriptHash);
                if (!nep5BalancesChanged.ContainsKey(fromKey))
                {
                    nep5BalancesChanged.Add(fromKey, new Nep5Balance());
                }
            }

            if (toBytes != null)
            {
                var toKey = new Nep5BalanceKey(to, scriptHash);
                if (!nep5BalancesChanged.ContainsKey(toKey))
                {
                    nep5BalancesChanged.Add(toKey, new Nep5Balance());
                }
            }
            RecordTransferHistory(snapshot, scriptHash, from, to, amountItem.GetBigInteger(), transaction.Hash, ref transferIndex);
        }
示例#22
0
        private void HandleNotification(Snapshot snapshot, Transaction transaction, UInt160 scriptHash,
                                        VM.Types.Array stateItems,
                                        Dictionary <Nep5BalanceKey, Nep5Balance> nep5BalancesChanged, ref ushort transferIndex)
        {
            // Event name should be encoded as a byte array.
            if (!(stateItems[0] is VM.Types.ByteArray))
            {
                return;
            }
            var eventName = Encoding.UTF8.GetString(stateItems[0].GetByteArray());

            // Only care about transfers
            if (eventName != "transfer")
            {
                return;
            }

            if (!(stateItems[1] is VM.Types.ByteArray))
            {
                return;
            }
            if (!(stateItems[2] is VM.Types.ByteArray))
            {
                return;
            }
            var amountItem = stateItems[3];

            if (!(amountItem is VM.Types.ByteArray || amountItem is VM.Types.Integer))
            {
                return;
            }
            byte[] fromBytes = stateItems[1].GetByteArray();
            if (fromBytes.Length != 20)
            {
                fromBytes = null;
            }
            byte[] toBytes = stateItems[2].GetByteArray();
            if (toBytes.Length != 20)
            {
                toBytes = null;
            }
            if (fromBytes == null && toBytes == null)
            {
                return;
            }
            var from = new UInt160(fromBytes);
            var to   = new UInt160(toBytes);

            var fromKey = new Nep5BalanceKey(from, scriptHash);

            if (!nep5BalancesChanged.ContainsKey(fromKey))
            {
                nep5BalancesChanged.Add(fromKey, new Nep5Balance());
            }
            var toKey = new Nep5BalanceKey(to, scriptHash);

            if (!nep5BalancesChanged.ContainsKey(toKey))
            {
                nep5BalancesChanged.Add(toKey, new Nep5Balance());
            }

            if (!_shouldTrackHistory)
            {
                return;
            }
            BigInteger amount = amountItem.GetBigInteger();

            _transfersSent.Add(new Nep5TransferKey(from,
                                                   snapshot.GetHeader(snapshot.Height).Timestamp, scriptHash, transferIndex),
                               new Nep5Transfer
            {
                Amount         = amount,
                UserScriptHash = to,
                BlockIndex     = snapshot.Height,
                TxHash         = transaction.Hash
            });
            _transfersReceived.Add(new Nep5TransferKey(to,
                                                       snapshot.GetHeader(snapshot.Height).Timestamp, scriptHash, transferIndex),
                                   new Nep5Transfer
            {
                Amount         = amount,
                UserScriptHash = from,
                BlockIndex     = snapshot.Height,
                TxHash         = transaction.Hash
            });
            transferIndex++;
        }
        public void OnPersist(Snapshot snapshot, IReadOnlyList <Blockchain.ApplicationExecuted> applicationExecutedList)
        {
            initDatabase();
            Dictionary <Nep5BalanceKey, Nep5Balance> nep5BalancesChanged = new Dictionary <Nep5BalanceKey, Nep5Balance>();

            ushort transferIndex = 0;

            foreach (var appExec in applicationExecutedList)
            {
                // Add transaction to DynamoDB
                AddTransaction(appExec.Transaction, snapshot.PersistingBlock.Timestamp, snapshot.Height);

                // Extract transfer notifications and executed contracts
                if (!appExec.VMState.HasFlag(VMState.FAULT))
                {
                    foreach (var notifyEventArgs in appExec.Notifications)
                    {
                        if (!(notifyEventArgs?.State is VM.Types.Array stateItems) || stateItems.Count == 0 ||
                            !(notifyEventArgs.ScriptContainer is Transaction transaction))
                        {
                            continue;
                        }
                        HandleNotification(snapshot, transaction, notifyEventArgs.ScriptHash, stateItems,
                                           nep5BalancesChanged, ref transferIndex);
                    }
                }

                // Add application log to DynamoDB
                JObject json = new JObject();
                json["txid"]         = appExec.Transaction?.Hash.ToString();
                json["trigger"]      = appExec.Trigger.ToString();
                json["vmstate"]      = appExec.VMState.ToString();
                json["gas_consumed"] = appExec.GasConsumed.ToString();
                try
                {
                    json["stack"] = appExec.Stack.Select(q => q.ToParameter().ToJson()).ToArray();
                }
                catch (InvalidOperationException)
                {
                    json["stack"] = "error: recursive reference";
                }
                json["notifications"] = appExec.Notifications.Select(q =>
                {
                    JObject notification     = new JObject();
                    notification["contract"] = q.ScriptHash.ToString();
                    try
                    {
                        notification["state"] = q.State.ToParameter().ToJson();
                    }
                    catch (InvalidOperationException)
                    {
                        notification["state"] = "error: recursive reference";
                    }
                    return(notification);
                }).ToArray();
                string   n = json.ToString();
                Document a = Document.FromJson(n);
                if (json["txid"] != null)
                {
                    ApplicationLogsTable.UpdateItemAsync(a);
                }

                if (Contracts.Count == 0)
                {
                    PreloadContracts();
                }
                int newCount = snapshot.Contracts.Find().Count();
                if (newCount != Contracts.Count)
                {
                    Console.WriteLine($"contract count: snapshot:{newCount} != memory:{Contracts.Count}");
                    AddNewContracts(snapshot);
                }
            }

            // process all balance changes
            foreach (var nep5BalancePair in nep5BalancesChanged)
            {
                // get guaranteed-accurate balances by calling balanceOf for keys that changed.
                byte[] script;
                using (ScriptBuilder sb = new ScriptBuilder())
                {
                    sb.EmitAppCall(nep5BalancePair.Key.AssetScriptHash, "balanceOf",
                                   nep5BalancePair.Key.UserScriptHash.ToArray());
                    script = sb.ToArray();
                }

                ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, extraGAS: 100000000);
                if (engine.State.HasFlag(VMState.FAULT))
                {
                    continue;
                }
                if (engine.ResultStack.Count <= 0)
                {
                    continue;
                }
                nep5BalancePair.Value.Balance          = engine.ResultStack.Pop().GetBigInteger();
                nep5BalancePair.Value.LastUpdatedBlock = snapshot.Height;

                JObject balance = new JObject();
                balance["address"]          = nep5BalancePair.Key.UserScriptHash.ToAddress();
                balance["asset"]            = nep5BalancePair.Key.AssetScriptHash.ToString();
                balance["balance"]          = nep5BalancePair.Value.Balance.ToString();
                balance["lastupdatedblock"] = nep5BalancePair.Value.LastUpdatedBlock;

                Document a = Document.FromJson(balance.ToString());
                AddressesTable.UpdateItemAsync(a);
            }
        }
示例#24
0
        private bool ProcessBlock(Snapshot snapshot, Block block)
        {
            if (block.Transactions.Length <= 1)
            {
                _lastPersistedBlock = block.Index;
                return(false);
            }

            ResetBatch();

            var transactionsCache = snapshot.Transactions;

            foreach (Transaction tx in block.Transactions)
            {
                ushort outputIndex = 0;
                foreach (TransactionOutput output in tx.Outputs)
                {
                    bool isGoverningToken = output.AssetId.Equals(Blockchain.GoverningToken.Hash);
                    if (isGoverningToken || output.AssetId.Equals(Blockchain.UtilityToken.Hash))
                    {
                        // Add new unspent UTXOs by account script hash.
                        UserSystemAssetCoinOutputs outputs = _userUnspentCoins.GetAndChange(
                            new UserSystemAssetCoinOutputsKey(isGoverningToken, output.ScriptHash, tx.Hash),
                            () => new UserSystemAssetCoinOutputs());
                        outputs.AddTxIndex(outputIndex, output.Value);
                    }
                    outputIndex++;
                }

                // Iterate all input Transactions by grouping by common input hashes.
                foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash))
                {
                    TransactionState txPrev = transactionsCache[group.Key];
                    // For each input being spent by this transaction.
                    foreach (CoinReference input in group)
                    {
                        // Get the output from the the previous transaction that is now being spent.
                        var outPrev = txPrev.Transaction.Outputs[input.PrevIndex];

                        bool isGoverningToken = outPrev.AssetId.Equals(Blockchain.GoverningToken.Hash);
                        if (isGoverningToken || outPrev.AssetId.Equals(Blockchain.UtilityToken.Hash))
                        {
                            // Remove spent UTXOs for unspent outputs by account script hash.
                            var userCoinOutputsKey =
                                new UserSystemAssetCoinOutputsKey(isGoverningToken, outPrev.ScriptHash, input.PrevHash);
                            UserSystemAssetCoinOutputs outputs = _userUnspentCoins.GetAndChange(
                                userCoinOutputsKey, () => new UserSystemAssetCoinOutputs());
                            outputs.RemoveTxIndex(input.PrevIndex);
                            if (outputs.AmountByTxIndex.Count == 0)
                            {
                                _userUnspentCoins.Delete(userCoinOutputsKey);
                            }

                            if (_shouldTrackUnclaimed && isGoverningToken)
                            {
                                UserSystemAssetCoinOutputs spentUnclaimedOutputs = _userSpentUnclaimedCoins.GetAndChange(
                                    userCoinOutputsKey, () => new UserSystemAssetCoinOutputs());
                                spentUnclaimedOutputs.AddTxIndex(input.PrevIndex, outPrev.Value);
                            }
                        }
                    }
                }

                if (_shouldTrackUnclaimed && tx is ClaimTransaction claimTransaction)
                {
                    foreach (CoinReference input in claimTransaction.Claims)
                    {
                        TransactionState txPrev = transactionsCache[input.PrevHash];
                        var outPrev             = txPrev.Transaction.Outputs[input.PrevIndex];

                        var claimedCoinKey =
                            new UserSystemAssetCoinOutputsKey(true, outPrev.ScriptHash, input.PrevHash);
                        UserSystemAssetCoinOutputs spentUnclaimedOutputs = _userSpentUnclaimedCoins.GetAndChange(
                            claimedCoinKey, () => new UserSystemAssetCoinOutputs());
                        spentUnclaimedOutputs.RemoveTxIndex(input.PrevIndex);
                        if (spentUnclaimedOutputs.AmountByTxIndex.Count == 0)
                        {
                            _userSpentUnclaimedCoins.Delete(claimedCoinKey);
                        }

                        if (snapshot.SpentCoins.TryGet(input.PrevHash)?.Items.Remove(input.PrevIndex) == true)
                        {
                            snapshot.SpentCoins.GetAndChange(input.PrevHash);
                        }
                    }
                }
            }

            // Write the current height into the key of the prefix itself
            _writeBatch.Put(SystemAssetUnspentCoinsPrefix, block.Index);
            _lastPersistedBlock = block.Index;
            return(true);
        }
 public void OnCommit(Snapshot snapshot)
 {
     AddBlock(snapshot);
 }
示例#26
0
        public IEnumerable <ECPoint> GetValidators(IEnumerable <Transaction> others)
        {
            Snapshot snapshot = Clone();

            foreach (Transaction tx in others)
            {
                foreach (TransactionOutput output in tx.Outputs)
                {
                    AccountState account = snapshot.Accounts.GetAndChange(output.ScriptHash, () => new AccountState(output.ScriptHash));
                    if (account.Balances.ContainsKey(output.AssetId))
                    {
                        account.Balances[output.AssetId] += output.Value;
                    }
                    else
                    {
                        account.Balances[output.AssetId] = output.Value;
                    }
                    if (output.AssetId.Equals(Blockchain.GoverningToken.Hash) && account.Votes.Length > 0)
                    {
                        foreach (ECPoint pubkey in account.Votes)
                        {
                            snapshot.Validators.GetAndChange(pubkey, () => new ValidatorState(pubkey)).Votes += output.Value;
                        }
                        snapshot.ValidatorsCount.GetAndChange().Votes[account.Votes.Length - 1] += output.Value;
                    }
                }
                foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash))
                {
                    Transaction tx_prev = snapshot.GetTransaction(group.Key);
                    foreach (CoinReference input in group)
                    {
                        TransactionOutput out_prev = tx_prev.Outputs[input.PrevIndex];
                        AccountState      account  = snapshot.Accounts.GetAndChange(out_prev.ScriptHash);
                        if (out_prev.AssetId.Equals(Blockchain.GoverningToken.Hash))
                        {
                            if (account.Votes.Length > 0)
                            {
                                foreach (ECPoint pubkey in account.Votes)
                                {
                                    ValidatorState validator = snapshot.Validators.GetAndChange(pubkey);
                                    validator.Votes -= out_prev.Value;
                                    if (!validator.Registered && validator.Votes.Equals(Fixed8.Zero))
                                    {
                                        snapshot.Validators.Delete(pubkey);
                                    }
                                }
                                snapshot.ValidatorsCount.GetAndChange().Votes[account.Votes.Length - 1] -= out_prev.Value;
                            }
                        }
                        account.Balances[out_prev.AssetId] -= out_prev.Value;
                    }
                }
                switch (tx)
                {
#pragma warning disable CS0612
                case EnrollmentTransaction tx_enrollment:
                    snapshot.Validators.GetAndChange(tx_enrollment.PublicKey, () => new ValidatorState(tx_enrollment.PublicKey)).Registered = true;
                    break;

#pragma warning restore CS0612
                case StateTransaction tx_state:
                    foreach (StateDescriptor descriptor in tx_state.Descriptors)
                    {
                        switch (descriptor.Type)
                        {
                        case StateType.Account:
                            Blockchain.ProcessAccountStateDescriptor(descriptor, snapshot);
                            break;

                        case StateType.Validator:
                            Blockchain.ProcessValidatorStateDescriptor(descriptor, snapshot);
                            break;
                        }
                    }
                    break;
                }
            }
            int count = (int)snapshot.ValidatorsCount.Get().Votes.Select((p, i) => new
            {
                Count = i,
                Votes = p
            }).Where(p => p.Votes > Fixed8.Zero).ToArray().WeightedFilter(0.25, 0.75, p => p.Votes.GetData(), (p, w) => new
            {
                p.Count,
                Weight = w
            }).WeightedAverage(p => p.Count, p => p.Weight);
            count = Math.Max(count, Blockchain.StandbyValidators.Length);
            HashSet <ECPoint>     sv      = new HashSet <ECPoint>(Blockchain.StandbyValidators);
            ECPoint[]             pubkeys = snapshot.Validators.Find().Select(p => p.Value).Where(p => (p.Registered && p.Votes > Fixed8.Zero) || sv.Contains(p.PublicKey)).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count).ToArray();
            IEnumerable <ECPoint> result;
            if (pubkeys.Length == count)
            {
                result = pubkeys;
            }
            else
            {
                HashSet <ECPoint> hashSet = new HashSet <ECPoint>(pubkeys);
                for (int i = 0; i < Blockchain.StandbyValidators.Length && hashSet.Count < count; i++)
                {
                    hashSet.Add(Blockchain.StandbyValidators[i]);
                }
                result = hashSet;
            }
            return(result.OrderBy(p => p));
        }