Exemplo n.º 1
0
        public void Get_block_rlp()
        {
            BlockDecoder decoder     = new BlockDecoder();
            IDebugBridge debugBridge = Substitute.For <IDebugBridge>();
            Rlp          rlp         = decoder.Encode(Build.A.Block.WithNumber(1).TestObject);

            debugBridge.GetBlockRlp(1).Returns(rlp.Bytes);

            DebugModule     module   = new DebugModule(NullLogManager.Instance, debugBridge);
            JsonRpcResponse response = RpcTest.TestRequest <IDebugModule>(module, "debug_getBlockRlp", "1");

            Assert.IsNotInstanceOf <JsonRpcErrorResponse>(response);
            Assert.AreEqual(rlp.Bytes, (byte[])response.Result);
        }
Exemplo n.º 2
0
        public void Get_from_db_null_value()
        {
            IDebugBridge debugBridge = Substitute.For <IDebugBridge>();

            byte[] key = new byte[] { 1, 2, 3 };
            debugBridge.GetDbValue(Arg.Any <string>(), Arg.Any <byte[]>()).Returns((byte[])null);

            IConfigProvider configProvider = Substitute.For <IConfigProvider>();
            DebugModule     module         = new DebugModule(NullLogManager.Instance, debugBridge);
            JsonRpcResponse response       = RpcTest.TestRequest <IDebugModule>(module, "debug_getFromDb", "STATE", key.ToHexString(true));

            Assert.IsNotInstanceOf <JsonRpcErrorResponse>(response);
            Assert.IsNull(response.Result, "result");
        }
Exemplo n.º 3
0
        public void Get_block_rlp_by_hash()
        {
            BlockDecoder decoder     = new BlockDecoder();
            IDebugBridge debugBridge = Substitute.For <IDebugBridge>();
            Rlp          rlp         = decoder.Encode(Build.A.Block.WithNumber(1).TestObject);

            debugBridge.GetBlockRlp(Keccak.Zero).Returns(rlp.Bytes);

            DebugModule     module   = new DebugModule(NullLogManager.Instance, debugBridge);
            JsonRpcResponse response = RpcTest.TestRequest <IDebugModule>(module, "debug_getBlockRlpByHash", $"{Keccak.Zero.Bytes.ToHexString()}");

            Assert.IsNull(response.Error, response.Error?.Message);
            Assert.AreEqual(rlp.Bytes, (byte[])response.Result);
        }
Exemplo n.º 4
0
        public void Eth_syncing()
        {
            IBlockchainBridge bridge = Substitute.For <IBlockchainBridge>();

            bridge.IsSyncing.Returns(true);
            bridge.BestKnown.Returns(6178000L);
            bridge.Head.Returns(Build.A.BlockHeader.WithNumber(6170000L).TestObject);

            IEthModule module = new EthModule(NullLogManager.Instance, bridge);

            string serialized = RpcTest.TestSerializedRequest(EthModuleFactory.Converters, module, "eth_syncing");

            Assert.AreEqual("{\"id\":67,\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":\"0x0\",\"currentBlock\":\"0x5e2590\",\"highestBlock\":\"0x5e44d0\"}}", serialized);
        }
Exemplo n.º 5
0
        public void Eth_syncing_false()
        {
            IBlockchainBridge bridge = Substitute.For <IBlockchainBridge>();

            bridge.IsSyncing.Returns(false);
            bridge.Head.Returns(Build.A.BlockHeader.WithNumber(900).TestObject);
            bridge.BestKnown.Returns(1000L);

            IEthModule module = new EthModule(NullLogManager.Instance, bridge);

            string serialized = RpcTest.TestSerializedRequest(EthModuleFactory.Converters, module, "eth_syncing");

            Assert.AreEqual("{\"id\":67,\"jsonrpc\":\"2.0\",\"result\":false}", serialized);
        }
Exemplo n.º 6
0
        public void Trace_replay_transaction()
        {
            ParityTraceAction subtrace = new ParityTraceAction();

            subtrace.Value        = 67890;
            subtrace.CallType     = "call";
            subtrace.From         = TestItem.AddressC;
            subtrace.To           = TestItem.AddressD;
            subtrace.Input        = Bytes.Empty;
            subtrace.Gas          = 10000;
            subtrace.TraceAddress = new int[] { 0, 0 };

            ParityLikeTxTrace result = new ParityLikeTxTrace();

            result.Action              = new ParityTraceAction();
            result.Action.Value        = 12345;
            result.Action.CallType     = "init";
            result.Action.From         = TestItem.AddressA;
            result.Action.To           = TestItem.AddressB;
            result.Action.Input        = new byte[] { 1, 2, 3, 4, 5, 6 };
            result.Action.Gas          = 40000;
            result.Action.TraceAddress = new int[] { 0 };
            result.Action.Subtraces.Add(subtrace);

            result.BlockHash           = TestItem.KeccakB;
            result.BlockNumber         = 123456;
            result.TransactionHash     = TestItem.KeccakC;
            result.TransactionPosition = 5;
            result.Action.TraceAddress = new int[] { 1, 2, 3 };

            ParityAccountStateChange stateChange = new ParityAccountStateChange();

            stateChange.Balance    = new ParityStateChange <UInt256>(1, 2);
            stateChange.Nonce      = new ParityStateChange <UInt256>(0, 1);
            stateChange.Storage    = new Dictionary <UInt256, ParityStateChange <byte[]> >();
            stateChange.Storage[1] = new ParityStateChange <byte[]>(new byte[] { 1 }, new byte[] { 2 });

            result.StateChanges = new Dictionary <Address, ParityAccountStateChange>();
            result.StateChanges.Add(TestItem.AddressC, stateChange);

            ITracer tracer = Substitute.For <ITracer>();

            tracer.ParityTrace(TestItem.KeccakC, Arg.Any <ParityTraceTypes>()).Returns(result);

            ITraceModule module = new TraceModule(Substitute.For <IConfigProvider>(), NullLogManager.Instance, new UnforgivingJsonSerializer(), tracer);

            string serialized = RpcTest.TestSerializedRequest(module, "trace_replayTransaction", TestItem.KeccakC.ToString(true), "[\"stateDiff\", \"trace\"]");

            Assert.AreEqual("{\"id\":67,\"jsonrpc\":\"2.0\",\"result\":{\"trace\":[{\"action\":{\"callType\":\"init\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"gas\":40000,\"input\":\"0x010203040506\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"value\":\"0x3039\"},\"blockHash\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"blockNumber\":\"0x1e240\",\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":1,\"traceAddress\":\"[1, 2, 3]\",\"transactionHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"transactionPosition\":5,\"type\":\"init\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"gas\":10000,\"input\":\"0x\",\"to\":\"0x475674cb523a0a2736b7f7534390288fce16982c\",\"value\":\"0x10932\"},\"blockHash\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"blockNumber\":\"0x1e240\",\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"traceAddress\":\"[0, 0]\",\"transactionHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"transactionPosition\":5,\"type\":\"call\"}],\"stateDiff\":{\"0x76e68a8696537e4141926f3e528733af9e237d69\":{\"balance\":{\"*\":{\"from\":\"0x1\",\"to\":\"0x2\"}},\"code\":\"=\",\"nonce\":{\"*\":{\"from\":\"0x0\",\"to\":\"0x1\"}},\"storage\":{\"0x0000000000000000000000000000000000000000000000000000000000000001\":{\"*\":{\"from\":\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"to\":\"0x0000000000000000000000000000000000000000000000000000000000000002\"}}}}}}}", serialized);
        }
        public void Eth_syncing_true()
        {
            IBlockchainBridge bridge = Substitute.For <IBlockchainBridge>();

            bridge.IsSyncing.Returns(false);
            bridge.Head.Returns(Build.A.BlockHeader.WithNumber(900).TestObject);
            bridge.BestKnown.Returns(1000L);
            bridge.IsSyncing.Returns(true);

            IEthModule module = new EthModule(NullLogManager.Instance, bridge);

            string serialized = RpcTest.TestSerializedRequest(module, "eth_syncing");

            Assert.AreEqual("{\"id\":\"0x43\",\"jsonrpc\":\"2.0\",\"result\":{\"startingBlock\":\"0x0\",\"currentBlock\":\"0x384\",\"highestBlock\":\"0x3e8\"}}", serialized);
        }
Exemplo n.º 8
0
        public void SyncingSubscription_creating_result()
        {
            BlockHeader blockHeader = Build.A.BlockHeader.TestObject;

            _blockTree.FindBestSuggestedHeader().Returns(blockHeader);

            Block block = Build.A.Block.TestObject;

            _blockTree.Head.Returns(block);

            string serialized     = RpcTest.TestSerializedRequest(_subscribeRpcModule, "eth_subscribe", "syncing");
            var    expectedResult = string.Concat("{\"jsonrpc\":\"2.0\",\"result\":\"", serialized.Substring(serialized.Length - 44, 34), "\",\"id\":67}");

            expectedResult.Should().Be(serialized);
        }
Exemplo n.º 9
0
        public void Get_from_db()
        {
            IDebugBridge debugBridge = Substitute.For <IDebugBridge>();

            byte[] key   = new byte[] { 1, 2, 3 };
            byte[] value = new byte[] { 4, 5, 6 };
            debugBridge.GetDbValue(Arg.Any <string>(), Arg.Any <byte[]>()).Returns(value);

            IConfigProvider configProvider = Substitute.For <IConfigProvider>();
            DebugModule     module         = new DebugModule(NullLogManager.Instance, debugBridge);
            JsonRpcResponse response       = RpcTest.TestRequest <IDebugModule>(module, "debug_getFromDb", "STATE", key.ToHexString(true));

            byte[] result = response.Result as byte[];
            Assert.AreEqual(value, result, response.Error?.Message);
        }
Exemplo n.º 10
0
        public void Eth_get_block_by_number_with_number()
        {
            IBlockchainBridge bridge = Substitute.For <IBlockchainBridge>();

            bridge.FindBlock(Arg.Any <long>()).Returns(Build.A.Block.WithTotalDifficulty(0).WithTransactions(Build.A.Transaction.TestObject).TestObject);
            bridge.RetrieveHeadBlock().Returns(Build.A.Block.WithTotalDifficulty(0).WithTransactions(Build.A.Transaction.TestObject).TestObject);
            bridge.Head.Returns(Build.A.BlockHeader.TestObject);

            IEthModule module = new EthModule(NullLogManager.Instance, bridge);

            for (int i = 0; i < 2; i++)
            {
                string serialized = RpcTest.TestSerializedRequest(module, "eth_getBlockByNumber", "\"0x" + i.ToString("x") + "\"", "true");
                Assert.AreEqual("{\"id\":\"0x43\",\"jsonrpc\":\"2.0\",\"result\":{\"number\":\"0x0\",\"hash\":\"0xa2a9f03b9493046696099d27b2612b99497aa1f392ec966716ab393c715a5bb6\",\"parentHash\":\"0xff483e972a04a9a62bb4b7d04ae403c615604e4090521ecc5bb7af67f71be09c\",\"nonce\":\"0x3e8\",\"mixHash\":\"0x2ba5557a4c62a513c7e56d1bf13373e0da6bec016755483e91589fe1c6d212e2\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0xf4240\",\"totalDifficulty\":\"0x0\",\"extraData\":\"0x010203\",\"size\":\"0x0\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"timestamp\":\"0xf4240\",\"transactions\":[{\"hash\":null,\"nonce\":\"0x0\",\"blockHash\":\"0xa2a9f03b9493046696099d27b2612b99497aa1f392ec966716ab393c715a5bb6\",\"blockNumber\":\"0x0\",\"transactionIndex\":\"0x0\",\"from\":null,\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"data\":\"0x\"}],\"transactionHashes\":null,\"uncles\":[]}}", serialized);
            }
        }
Exemplo n.º 11
0
        public void Eth_get_transaction_receipt()
        {
            IBlockchainBridge bridge = Substitute.For <IBlockchainBridge>();
            var entries = new[]
            {
                Build.A.LogEntry.TestObject,
                Build.A.LogEntry.TestObject
            };

            bridge.GetReceipt(Arg.Any <Keccak>()).Returns(Build.A.Receipt.WithBloom(new Bloom(entries, new Bloom())).WithAllFieldsFilled.WithLogs(entries).TestObject);

            IEthModule module = new EthModule(NullLogManager.Instance, bridge);

            string serialized = RpcTest.TestSerializedRequest(EthModuleFactory.Converters, module, "eth_getTransactionReceipt", TestItem.KeccakA.ToString());

            Assert.AreEqual("{\"id\":67,\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"transactionIndex\":\"0x2\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"cumulativeGasUsed\":\"0x3e8\",\"gasUsed\":\"0x64\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"contractAddress\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"logs\":[{\"removed\":false,\"logIndex\":\"0x0\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]},{\"removed\":false,\"logIndex\":\"0x1\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]}],\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"root\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"status\":\"0x1\",\"error\":\"error\"}}", serialized);
        }
Exemplo n.º 12
0
        public void Can_get_transaction(bool withHeader)
        {
            Keccak txHash = _blockTree.FindBlock(1).Transactions[0].Hash;
            TransactionWithProof txWithProof = _proofRpcModule.proof_getTransactionByHash(txHash, withHeader).Data;
            Assert.NotNull(txWithProof.Transaction);
            Assert.AreEqual(2, txWithProof.TxProof.Length);
            if (withHeader)
            {
                Assert.NotNull(txWithProof.BlockHeader);
            }
            else
            {
                Assert.Null(txWithProof.BlockHeader);
            }

            string response = RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}", $"{withHeader}");
            Assert.True(response.Contains("\"result\""));
        }
Exemplo n.º 13
0
        public void Can_get_receipt(bool withHeader, string expectedResult)
        {
            Keccak txHash = _blockTree.FindBlock(1).Transactions[0].Hash;
            ReceiptWithProof receiptWithProof = _proofRpcModule.proof_getTransactionReceipt(txHash, withHeader).Data;
            Assert.NotNull(receiptWithProof.Receipt);
            Assert.AreEqual(2, receiptWithProof.ReceiptProof.Length);
            
            if (withHeader)
            {
                Assert.NotNull(receiptWithProof.BlockHeader);
            }
            else
            {
                Assert.Null(receiptWithProof.BlockHeader);
            }

            string response = RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}", $"{withHeader}");
            Assert.AreEqual(expectedResult, response);
        }
Exemplo n.º 14
0
        public void Get_chain_level(string parameter)
        {
            debugBridge.GetLevelInfo(1).Returns(
                new ChainLevelInfo(
                    true,
                    new[]
            {
                new BlockInfo(TestItem.KeccakA, 1000),
                new BlockInfo(TestItem.KeccakB, 1001),
            }));

            DebugRpcModule         rpcModule  = new DebugRpcModule(LimboLogs.Instance, debugBridge, jsonRpcConfig);
            JsonRpcSuccessResponse response   = RpcTest.TestRequest <IDebugRpcModule>(rpcModule, "debug_getChainLevel", parameter) as JsonRpcSuccessResponse;
            ChainLevelForRpc       chainLevel = response?.Result as ChainLevelForRpc;

            Assert.NotNull(chainLevel);
            Assert.AreEqual(true, chainLevel.HasBlockOnMainChain);
            Assert.AreEqual(2, chainLevel.BlockInfos.Length);
        }
Exemplo n.º 15
0
        public void Get_trace()
        {
            GethTxTraceEntry entry = new GethTxTraceEntry();

            entry.Storage = new Dictionary <string, string>
            {
                { "1".PadLeft(64, '0'), "2".PadLeft(64, '0') },
                { "3".PadLeft(64, '0'), "4".PadLeft(64, '0') },
            };

            entry.Memory = new List <string>
            {
                "5".PadLeft(64, '0'),
                "6".PadLeft(64, '0')
            };

            entry.Stack = new List <string>
            {
                "7".PadLeft(64, '0'),
                "8".PadLeft(64, '0')
            };

            entry.Operation = "STOP";
            entry.Gas       = 22000;
            entry.GasCost   = 1;
            entry.Depth     = 1;

            var trace = new GethLikeTxTrace();

            trace.ReturnValue = Bytes.FromHexString("a2");
            trace.Entries.Add(entry);

            IDebugBridge debugBridge = Substitute.For <IDebugBridge>();

            debugBridge.GetTransactionTrace(Arg.Any <Keccak>()).Returns(trace);

            IConfigProvider configProvider = Substitute.For <IConfigProvider>();
            DebugModule     module         = new DebugModule(configProvider, NullLogManager.Instance, debugBridge, new UnforgivingJsonSerializer());
            string          response       = RpcTest.TestSerializedRequest <IDebugModule>(module, "debug_traceTransaction", TestObject.KeccakA.ToString(true));

            Assert.AreEqual("{\"id\":67,\"jsonrpc\":\"2.0\",\"result\":{\"gas\":\"0x0\",\"failed\":false,\"returnValue\":\"0xa2\",\"structLogs\":[{\"pc\":0,\"op\":\"STOP\",\"gas\":22000,\"gasCost\":1,\"depth\":1,\"error\":null,\"stack\":[\"0000000000000000000000000000000000000000000000000000000000000007\",\"0000000000000000000000000000000000000000000000000000000000000008\"],\"memory\":[\"0000000000000000000000000000000000000000000000000000000000000005\",\"0000000000000000000000000000000000000000000000000000000000000006\"],\"storage\":{\"0000000000000000000000000000000000000000000000000000000000000001\":\"0000000000000000000000000000000000000000000000000000000000000002\",\"0000000000000000000000000000000000000000000000000000000000000003\":\"0000000000000000000000000000000000000000000000000000000000000004\"}}]}}", response);
        }
Exemplo n.º 16
0
        public void Trace_block()
        {
            ParityLikeTxTrace result1 = BuildParityTxTrace();
            ParityLikeTxTrace result2 = BuildParityTxTrace();

            Block             block            = Build.A.Block.TestObject;
            IBlockchainBridge blockchainBridge = Substitute.For <IBlockchainBridge>();

            blockchainBridge.FindEarliestBlock().Returns(block);

            ITracer tracer = Substitute.For <ITracer>();

            tracer.ParityTraceBlock(block.Hash, Arg.Any <ParityTraceTypes>()).Returns(new[] { result1, result2 });

            ITraceModule module = new TraceModule(blockchainBridge, NullLogManager.Instance, tracer);

            string serialized = RpcTest.TestSerializedRequest(TraceModuleFactory.Converters, module, "trace_block", "earliest");

            Assert.AreEqual("{\"id\":67,\"jsonrpc\":\"2.0\",\"result\":[{\"output\":null,\"stateDiff\":{\"0x76e68a8696537e4141926f3e528733af9e237d69\":{\"balance\":{\"*\":{\"from\":\"0x1\",\"to\":\"0x2\"}},\"code\":{\"*\":{\"from\":\"0x01\",\"to\":\"0x02\"}},\"nonce\":{\"*\":{\"from\":\"0x0\",\"to\":\"0x1\"}},\"storage\":{\"0x0000000000000000000000000000000000000000000000000000000000000001\":{\"*\":{\"from\":\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"to\":\"0x0000000000000000000000000000000000000000000000000000000000000002\"}}}}},\"trace\":[{\"action\":{\"callType\":\"init\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"gas\":\"0x9c40\",\"input\":\"0x010203040506\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"value\":\"0x3039\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":1,\"traceAddress\":[1,2,3],\"type\":null},{\"action\":{\"callType\":\"call\",\"from\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"gas\":\"0x2710\",\"input\":\"0x\",\"to\":\"0x475674cb523a0a2736b7f7534390288fce16982c\",\"value\":\"0x10932\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"traceAddress\":[0,0],\"type\":null}],\"transactionHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"vmTrace\":null},{\"output\":null,\"stateDiff\":{\"0x76e68a8696537e4141926f3e528733af9e237d69\":{\"balance\":{\"*\":{\"from\":\"0x1\",\"to\":\"0x2\"}},\"code\":{\"*\":{\"from\":\"0x01\",\"to\":\"0x02\"}},\"nonce\":{\"*\":{\"from\":\"0x0\",\"to\":\"0x1\"}},\"storage\":{\"0x0000000000000000000000000000000000000000000000000000000000000001\":{\"*\":{\"from\":\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"to\":\"0x0000000000000000000000000000000000000000000000000000000000000002\"}}}}},\"trace\":[{\"action\":{\"callType\":\"init\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"gas\":\"0x9c40\",\"input\":\"0x010203040506\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"value\":\"0x3039\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":1,\"traceAddress\":[1,2,3],\"type\":null},{\"action\":{\"callType\":\"call\",\"from\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"gas\":\"0x2710\",\"input\":\"0x\",\"to\":\"0x475674cb523a0a2736b7f7534390288fce16982c\",\"value\":\"0x10932\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"traceAddress\":[0,0],\"type\":null}],\"transactionHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"vmTrace\":null}]}", serialized);
        }
Exemplo n.º 17
0
        public void Trace_raw_transaction()
        {
            ParityLikeTxTrace result1 = BuildParityTxTrace();
            ParityLikeTxTrace result2 = BuildParityTxTrace();

            Block             block            = Build.A.Block.TestObject;
            IBlockchainBridge blockchainBridge = Substitute.For <IBlockchainBridge>();

            blockchainBridge.FindEarliestBlock().Returns(block);

            ITracer tracer = Substitute.For <ITracer>();

            tracer.ParityTraceBlock(block.Hash, Arg.Any <ParityTraceTypes>()).Returns(new[] { result1, result2 });

            ITraceModule module = new TraceModule(blockchainBridge, NullLogManager.Instance, tracer);

            string serialized = RpcTest.TestSerializedRequest(TraceModuleFactory.Converters, module, "trace_rawTransaction", "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675", "[\"trace\"]");

            Assert.AreEqual("{\"id\":67,\"jsonrpc\":\"2.0\",\"result\":null}", serialized);
        }
Exemplo n.º 18
0
        private CallResultWithProof TestCallWithCode(byte[] code, Address from = null)
        {
            StateProvider stateProvider = CreateInitialState(code);

            Keccak root  = stateProvider.StateRoot;
            Block  block = Build.A.Block.WithParent(_blockTree.Head).WithStateRoot(root).WithBeneficiary(TestItem.AddressD).TestObject;

            BlockTreeBuilder.AddBlock(_blockTree, block);
            Block blockOnTop = Build.A.Block.WithParent(block).WithStateRoot(root).WithBeneficiary(TestItem.AddressD).TestObject;

            BlockTreeBuilder.AddBlock(_blockTree, blockOnTop);

            // would need to setup state root somehow...

            TransactionForRpc tx = new TransactionForRpc
            {
                From     = from,
                To       = TestItem.AddressB,
                GasPrice = _useNonZeroGasPrice ? 10.GWei() : 0
            };

            CallResultWithProof callResultWithProof = _proofModule.proof_call(tx, new BlockParameter(blockOnTop.Number)).Data;

            Assert.Greater(callResultWithProof.Accounts.Length, 0);

            foreach (AccountProof accountProof in callResultWithProof.Accounts)
            {
                ProofVerifier.Verify(accountProof.Proof, block.StateRoot);
                foreach (StorageProof storageProof in accountProof.StorageProofs)
                {
                    ProofVerifier.Verify(storageProof.Proof, accountProof.StorageRoot);
                }
            }

            EthereumJsonSerializer serializer = new EthereumJsonSerializer();
            string response = RpcTest.TestSerializedRequest(_proofModule, "proof_call", $"{serializer.Serialize(tx)}", $"{blockOnTop.Number}");

            Assert.True(response.Contains("\"result\""));

            return(callResultWithProof);
        }
Exemplo n.º 19
0
        public void Can_get_receipt(bool withHeader)
        {
            Keccak           txHash           = _blockTree.FindBlock(1).Transactions[0].Hash;
            ReceiptWithProof receiptWithProof = _proofModule.proof_getTransactionReceipt(txHash, withHeader).Data;

            Assert.NotNull(receiptWithProof.Receipt);
            Assert.AreEqual(2, receiptWithProof.ReceiptProof.Length);
            Assert.GreaterOrEqual(receiptWithProof.ReceiptProof.Last().Length, 256 /* bloom length */);
            if (withHeader)
            {
                Assert.NotNull(receiptWithProof.BlockHeader);
            }
            else
            {
                Assert.Null(receiptWithProof.BlockHeader);
            }

            string response = RpcTest.TestSerializedRequest(_proofModule, "proof_getTransactionReceipt", $"{txHash}", $"{withHeader}");

            Assert.True(response.Contains("\"result\""));
        }
Exemplo n.º 20
0
        public void Get_chain_level(string parameter)
        {
            IDebugBridge debugBridge = Substitute.For <IDebugBridge>();

            debugBridge.GetLevelInfo(1).Returns(
                new ChainLevelInfo(
                    true,
                    new[]
            {
                new BlockInfo(TestItem.KeccakA, 1000),
                new BlockInfo(TestItem.KeccakB, 1001),
            }));

            DebugModule            module     = new DebugModule(NullLogManager.Instance, debugBridge);
            JsonRpcSuccessResponse response   = RpcTest.TestRequest <IDebugModule>(module, "debug_getChainLevel", parameter) as JsonRpcSuccessResponse;
            ChainLevelForRpc       chainLevel = response?.Result as ChainLevelForRpc;

            Assert.NotNull(chainLevel);
            Assert.AreEqual(true, chainLevel.HasBlockOnMainChain);
            Assert.AreEqual(2, chainLevel.BlockInfos.Length);
        }
Exemplo n.º 21
0
        public void Get_trace()
        {
            GethTxTraceEntry entry = new GethTxTraceEntry();

            entry.Storage = new Dictionary <string, string>
            {
                { "1".PadLeft(64, '0'), "2".PadLeft(64, '0') },
                { "3".PadLeft(64, '0'), "4".PadLeft(64, '0') },
            };

            entry.Memory = new List <string>
            {
                "5".PadLeft(64, '0'),
                "6".PadLeft(64, '0')
            };

            entry.Stack = new List <string>
            {
                "7".PadLeft(64, '0'),
                "8".PadLeft(64, '0')
            };

            entry.Operation = "STOP";
            entry.Gas       = 22000;
            entry.GasCost   = 1;
            entry.Depth     = 1;

            var trace = new GethLikeTxTrace();

            trace.ReturnValue = Bytes.FromHexString("a2");
            trace.Entries.Add(entry);

            debugBridge.GetTransactionTrace(Arg.Any <Keccak>(), Arg.Any <CancellationToken>(), Arg.Any <GethTraceOptions>()).Returns(trace);

            DebugRpcModule rpcModule = new DebugRpcModule(LimboLogs.Instance, debugBridge, jsonRpcConfig);
            string         response  = RpcTest.TestSerializedRequest <IDebugRpcModule>(DebugModuleFactory.Converters, rpcModule, "debug_traceTransaction", TestItem.KeccakA.ToString(true), "{}");

            Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":{\"gas\":\"0x0\",\"failed\":false,\"returnValue\":\"0xa2\",\"structLogs\":[{\"pc\":0,\"op\":\"STOP\",\"gas\":22000,\"gasCost\":1,\"depth\":1,\"error\":null,\"stack\":[\"0000000000000000000000000000000000000000000000000000000000000007\",\"0000000000000000000000000000000000000000000000000000000000000008\"],\"memory\":[\"0000000000000000000000000000000000000000000000000000000000000005\",\"0000000000000000000000000000000000000000000000000000000000000006\"],\"storage\":{\"0000000000000000000000000000000000000000000000000000000000000001\":\"0000000000000000000000000000000000000000000000000000000000000002\",\"0000000000000000000000000000000000000000000000000000000000000003\":\"0000000000000000000000000000000000000000000000000000000000000004\"}}]},\"id\":67}", response);
        }
Exemplo n.º 22
0
        public void Can_call_by_hash()
        {
            StateProvider stateProvider = CreateInitialState(null);

            Keccak root = stateProvider.StateRoot;
            Block block = Build.A.Block.WithParent(_blockTree.Head).WithStateRoot(root).TestObject;
            BlockTreeBuilder.AddBlock(_blockTree, block);

            // would need to setup state root somehow...

            TransactionForRpc tx = new()
            {
                From = TestItem.AddressA,
                To = TestItem.AddressB,
                GasPrice = _useNonZeroGasPrice ? 10.GWei() : 0
            };
            _proofRpcModule.proof_call(tx, new BlockParameter(block.Hash));

            EthereumJsonSerializer serializer = new();
            string response = RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{block.Hash}");
            Assert.True(response.Contains("\"result\""));
        }
Exemplo n.º 23
0
        public void parity_netPeers_empty_ActivePeers()
        {
            LimboLogs           logger        = LimboLogs.Instance;
            MainnetSpecProvider specProvider  = MainnetSpecProvider.Instance;
            EthereumEcdsa       ethereumEcdsa = new EthereumEcdsa(specProvider.ChainId, logger);
            InMemoryTxStorage   txStorage     = new InMemoryTxStorage();
            IDb        blockDb     = new MemDb();
            IDb        headerDb    = new MemDb();
            IDb        blockInfoDb = new MemDb();
            IBlockTree blockTree   = new BlockTree(blockDb, headerDb, blockInfoDb, new ChainLevelInfoRepository(blockInfoDb), specProvider, NullBloomStorage.Instance, LimboLogs.Instance);

            ITransactionComparerProvider transactionComparerProvider =
                new TransactionComparerProvider(specProvider, blockTree);

            TxPool.TxPool txPool = new TxPool.TxPool(txStorage, ethereumEcdsa, new ChainHeadInfoProvider(new FixedBlockChainHeadSpecProvider(specProvider), blockTree, new StateProvider(new TrieStore(new MemDb(), LimboLogs.Instance), new MemDb(), LimboLogs.Instance)), new TxPoolConfig(),
                                                     new TxValidator(specProvider.ChainId), LimboLogs.Instance, transactionComparerProvider.GetDefaultComparer());

            new OnChainTxWatcher(blockTree, txPool, specProvider, LimboLogs.Instance);
            IReceiptStorage receiptStorage = new InMemoryReceiptStorage();

            IPeerManager peerManager = Substitute.For <IPeerManager>();

            peerManager.ActivePeers.Returns(new List <Peer> {
            });
            peerManager.ConnectedPeers.Returns(new List <Peer> {
                new Peer(new Node("111.1.1.1", 11111, true))
            });

            IParityRpcModule parityRpcModule = new ParityRpcModule(ethereumEcdsa, txPool, blockTree, receiptStorage, new Enode(TestItem.PublicKeyA, IPAddress.Loopback, 8545),
                                                                   _signerStore, new MemKeyStore(new[] { TestItem.PrivateKeyA }), logger, peerManager);

            string serialized     = RpcTest.TestSerializedRequest(parityRpcModule, "parity_netPeers");
            string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":{\"active\":0,\"connected\":1,\"max\":0,\"peers\":[]},\"id\":67}";

            Assert.AreEqual(expectedResult, serialized);
        }
Exemplo n.º 24
0
        public void Test_node_info()
        {
            string serialized = RpcTest.TestSerializedRequest(_adminRpcModule, "admin_nodeInfo");
            JsonRpcSuccessResponse response = _serializer.Deserialize <JsonRpcSuccessResponse>(serialized);
            JsonSerializerSettings settings = new JsonSerializerSettings();

            settings.Converters = EthereumJsonSerializer.CommonConverters.ToList();

            NodeInfo nodeInfo = ((JObject)response.Result).ToObject <NodeInfo>(JsonSerializer.Create(settings));

            nodeInfo.Enode.Should().Be(_enodeString);
            nodeInfo.Id.Should().Be("ae3623ef35c06ab49e9ae4b9f5a2b0f1983c28f85de1ccc98e2174333fdbdf1f");
            nodeInfo.Ip.Should().Be("127.0.0.1");
            nodeInfo.Name.Should().Be(ClientVersion.Description);
            nodeInfo.ListenAddress.Should().Be("127.0.0.1:30303");
            nodeInfo.Ports.Discovery.Should().Be(_networkConfig.DiscoveryPort);
            nodeInfo.Ports.Listener.Should().Be(_networkConfig.P2PPort);

            nodeInfo.Protocols.Should().HaveCount(1);
            nodeInfo.Protocols["eth"].Difficulty.Should().Be(_blockTree.Head.TotalDifficulty ?? 0);
            nodeInfo.Protocols["eth"].HeadHash.Should().Be(_blockTree.HeadHash);
            nodeInfo.Protocols["eth"].GenesisHash.Should().Be(_blockTree.GenesisHash);
            nodeInfo.Protocols["eth"].ChainId.Should().Be(_blockTree.ChainId);
        }
Exemplo n.º 25
0
        public void On_incorrect_params_returns_correct_error_code()
        {
            Keccak txHash = TestItem.KeccakH;

            // missing with header
            string response = RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}");
            Assert.True(response.Contains($"{ErrorCodes.InvalidParams}"), "missing");

            // too many
            response = RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}", "true", "false");
            Assert.True(response.Contains($"{ErrorCodes.InvalidParams}"), "too many");

            // missing with header
            response = RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}");
            Assert.True(response.Contains($"{ErrorCodes.InvalidParams}"), "missing");

            // too many
            response = RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}", "true", "false");
            Assert.True(response.Contains($"{ErrorCodes.InvalidParams}"), "too many");

            // all wrong
            response = RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{txHash}");
            Assert.True(response.Contains($"{ErrorCodes.InvalidParams}"), "missing");
        }
Exemplo n.º 26
0
        public void Can_call_by_hash_canonical()
        {
            Block lastHead = _blockTree.Head;
            Block block = Build.A.Block.WithParent(lastHead).TestObject;
            Block newBlockOnMain = Build.A.Block.WithParent(lastHead).WithDifficulty(block.Difficulty + 1).TestObject;
            BlockTreeBuilder.AddBlock(_blockTree, block);
            BlockTreeBuilder.AddBlock(_blockTree, newBlockOnMain);

            // would need to setup state root somehow...

            TransactionForRpc tx = new()
            {
                From = TestItem.AddressA,
                To = TestItem.AddressB,
                GasPrice = _useNonZeroGasPrice ? 10.GWei() : 0
            };

            EthereumJsonSerializer serializer = new();
            string response = RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{{\"blockHash\" : \"{block.Hash}\", \"requireCanonical\" : true}}");
            Assert.True(response.Contains("-32000"));

            response = RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{{\"blockHash\" : \"{TestItem.KeccakG}\", \"requireCanonical\" : true}}");
            Assert.True(response.Contains("-32001"));
        }
Exemplo n.º 27
0
        public void Tx_positions_are_fine()
        {
            string serialized = RpcTest.TestSerializedRequest(EthModuleFactory.Converters.Union(TraceModuleFactory.Converters).ToList(), _traceModule, "trace_block", "latest");

            Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x963e1762be217455aed852e2cbb46053ce0bca98\",\"gas\":\"0x5208\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0x7a4597196e0e3c1e6c843bcdad49a0946b2096b1817f49eca911627748950d8b\",\"blockNumber\":9,\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"transactionHash\":\"0x1fb701b713c746b25ac9b0b82345aef86c7541b001ee4c7be4922c71e66e073a\",\"transactionPosition\":0,\"type\":null},{\"action\":{\"callType\":\"call\",\"from\":\"0xaabfde31a679d9e83e35aaa0b952258e7fc8065f\",\"gas\":\"0x5208\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0x7a4597196e0e3c1e6c843bcdad49a0946b2096b1817f49eca911627748950d8b\",\"blockNumber\":9,\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"transactionHash\":\"0xd8b53c2348158e637009af277981ab3169ef8a8bff2a67fe52c2aaebe752ff58\",\"transactionPosition\":1,\"type\":null},{\"action\":{\"callType\":\"call\",\"from\":\"0x1c905b61b8683c09e07d2d591ff15d165b170e0a\",\"gas\":\"0x5208\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0x7a4597196e0e3c1e6c843bcdad49a0946b2096b1817f49eca911627748950d8b\",\"blockNumber\":9,\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"transactionHash\":\"0x4afaa014dcb4fd2fc7bd3af982277ab2f413d8ae7d907ad76afb59f3079fea43\",\"transactionPosition\":2,\"type\":null},{\"action\":{\"callType\":\"call\",\"from\":\"0xbeda477f0d0ace1ced214786397656d93c114918\",\"gas\":\"0x5208\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0x7a4597196e0e3c1e6c843bcdad49a0946b2096b1817f49eca911627748950d8b\",\"blockNumber\":9,\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"transactionHash\":\"0xeb27dfdd845ebbaa62d47b0950d6a38c43873539b71556e9381aeae4ba269f85\",\"transactionPosition\":3,\"type\":null},{\"action\":{\"callType\":\"call\",\"from\":\"0xacd3dcc14b31ac179d7f7164083c345fed149573\",\"gas\":\"0x5208\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0x7a4597196e0e3c1e6c843bcdad49a0946b2096b1817f49eca911627748950d8b\",\"blockNumber\":9,\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"transactionHash\":\"0xe4e9d78d3cf7eebb776da25f4fad66eb04a025801291dfffdf06ddd547d09498\",\"transactionPosition\":4,\"type\":null},{\"action\":{\"callType\":\"call\",\"from\":\"0xbc5ae1c3f5e65989c5664bac6a6de30314e82bb4\",\"gas\":\"0x5208\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0x7a4597196e0e3c1e6c843bcdad49a0946b2096b1817f49eca911627748950d8b\",\"blockNumber\":9,\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"transactionHash\":\"0x7256da5a198bc98aff7e4213e9e2a645e78bcde658dab20a9ab5d4a606c135cf\",\"transactionPosition\":5,\"type\":null},{\"action\":{\"callType\":\"call\",\"from\":\"0x8ea7891d652f94bbbdd105e572e1af30e6ae2822\",\"gas\":\"0x5208\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0x7a4597196e0e3c1e6c843bcdad49a0946b2096b1817f49eca911627748950d8b\",\"blockNumber\":9,\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"transactionHash\":\"0x89163ade4d1f29be8edc38a83403a7a889ebdf3cc9bd4bfe5254e7eabff57e4c\",\"transactionPosition\":6,\"type\":null},{\"action\":{\"callType\":\"call\",\"from\":\"0x5b4f11d5c61b499ff579f1ba6467a9aa5cba0c14\",\"gas\":\"0x5208\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0x7a4597196e0e3c1e6c843bcdad49a0946b2096b1817f49eca911627748950d8b\",\"blockNumber\":9,\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"transactionHash\":\"0xffc2757cbc0e467c287e757c6afcf33802ac4fc1bae7e322cd63dbdb04da3a57\",\"transactionPosition\":7,\"type\":null},{\"action\":{\"callType\":\"call\",\"from\":\"0x7b32782cdb74fc526f1000ba3781191ade11436f\",\"gas\":\"0x5208\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0x7a4597196e0e3c1e6c843bcdad49a0946b2096b1817f49eca911627748950d8b\",\"blockNumber\":9,\"result\":{\"gasUsed\":\"0x0\",\"output\":null},\"subtraces\":0,\"transactionHash\":\"0xbe9381d3d6f137e202ec390a2a024223e62e1abf2875b7227ca146a74df2e19c\",\"transactionPosition\":8,\"type\":null},{\"action\":{\"author\":\"0x0000000000000000000000000000000000000000\",\"rewardType\":\"block\",\"value\":\"0x4563918244f40000\"},\"blockHash\":\"0x7a4597196e0e3c1e6c843bcdad49a0946b2096b1817f49eca911627748950d8b\",\"blockNumber\":9,\"subtraces\":0,\"traceAddress\":[],\"type\":\"reward\"}],\"id\":67}", serialized, serialized.Replace("\"", "\\\""));
        }
Exemplo n.º 28
0
 public string TestEthRpc(string method, params string[] parameters)
 {
     return(RpcTest.TestSerializedRequest(EthModuleFactory.Converters, EthRpcModule, method, parameters));
 }
Exemplo n.º 29
0
 public string TestSerializedRequest <T>(T module, string method, params string[] parameters) where T : class, IRpcModule
 {
     return(RpcTest.TestSerializedRequest(new JsonConverter[0], module, method, parameters));
 }
Exemplo n.º 30
0
        public void Eth_get_block_by_number_empty_param()
        {
            string serialized = RpcTest.TestSerializedRequest(EthModuleFactory.Converters, _ethModule, "eth_getBlockByNumber", "", "true");

            Assert.True(serialized.StartsWith("{\"id\":67,\"jsonrpc\":\"2.0\",\"error\""));
        }