rwth-acis / las2peer

A Java-based decentralized framework for distributing community services in a peer-to-peer infrastructure.
https://las2peer.org
Other
52 stars 13 forks source link

[BUG] Ethereum transactions fail #122

Closed Michi03 closed 2 years ago

Michi03 commented 2 years ago
  1. Summary - My local las2peer node seems to connect to the Ethereum blockchain, running locally inside a docker container, correctly, but is unable to announce the service I'm trying to deploy. The specific setup is described in more detail below, however it closely follows the setup described here. The only notable difference (I can think of) is that this step of hosting the network configuration is not performed, since only one node is started. After starting the node and web-connector, the service is deployed to the network properly but the blockchain announcement fails with the following error message:

    Error while announcing deployment: i5.las2peer.registry.exceptions.EthereumException: Failed to submit service deployment announcement (DEBUG: YouTubeProxy, org.web3j.protocol.exceptions.TransactionException: Transaction has failed with status: 0x0. Gas used: 31683. (not-enough gas?))

    Other transactions are executed correctly, it is e.g., possible to create OIDC agents and they are stored correctly in both the las2peer network and on the blockchain, so this seems to be a problem specifically with the service registry.

  2. Bug Details - A detailed explanation of the issue, structured in subsections:

    1. What? - Service announcements of this service fail consistently with status 0x0.
    2. Where? - The problem seems to occur in the communication with the service registry deployed to the blockchain as part of the truffle migration.
    3. When?/How often? - Always, as a response to the following command startService('i5.las2peer.services.hyeYouTubeProxy.YouTubeProxy@0.1')
    4. How?/Current state -
      1. Clone the las2peer-ethereum-cluster repo and build the ethereum image contained therein
      2. Comment out the command starting the las2peer node in the las2peer docker start script and build this image as well, since we only use this container to deploy the registry contracts. (Yes, this is terribly inefficient, since we are spending a lot of time building las2peer, although we are not using it, so feel free to uncomment more to safe some time)
      3. Start the ethereum container with environment variables "ETHEREUM_DATA_DIR=/root/.ethereum/devchain" and open ports 30303 (tcp+udp), 8545 (tcp), 8546 (tcp) and start the las2peer container with environment variable "LAS2PEER_ETH_HOST=127.0.0.1:8545". (If you want to be able to stop and restart the network, add the ETHEREUM_DATA_DIR and las2peer node-storage as volume mounts.)
      4. Wait for the las2peer container to finish the truffle migration and copy the file inside the las2peer container located at /app/las2peer/etc/i5.las2peer.registry.data.RegistryConfiguration.properties to a local path.
      5. Clone this repository and build the latest las2peer version and copy the registry.properties file into the 'las2peer/etc' directory (either created manually, or upon first launching of the node).
      6. Clone my service repository, build the jar file, and copy it to the las2peer directory under e.g., 'core/lib', also copy the service.properties file into the 'las2peer/etc' directory.
      7. Deploy the consent registry script(s) to the blockchain. I do this by cloning the las2peer-registry-contracts (containing the truffle migration files needed for the las2peer registry), replacing the files inside the migrations directory with my own and running ./node_modules/.bin/truffle migrate 2>&1 | tee migration.log
      8. Copy the address stored in the thusly created migration.log file under the point ConsentRegistry > contract address: into the the service's property file
      9. Launch the las2peer node by executing the following command java --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED -cp "core/src/main/resources/:core/export/jars/*:restmapper/export/jars/*:webconnector/export/jars/*:core/lib/*:restmapper/lib/*:webconnector/lib/*" i5.las2peer.tools.L2pNodeLauncher --port 9080 --service-directory service --node-id-seed 1 --ethereum-mnemonic "differ employ cook sport clinic wedding melody column pave stuff oak price" startWebConnector "node=getNodeAsEthereumNode()" "registry=node.getRegistryClient()" "n=getNodeAsEthereumNode()" "r=n.getRegistryClient()" interactive
      10. Run the command startService('i5.las2peer.services.hyeYouTubeProxy.YouTubeProxy@0.1')
  3. (Optional) Fixed state - Upon startup, the las2peer node logs the following:

    [TX-NONCE@0xb5a66d27457af8be2a09f17add73c2ae46520e69] init to -1
    Error creating agent
    Cannot invoke "i5.las2peer.persistency.SharedStorage.fetchEnvelope(String, long)" because "this.pastStorage" is null
    created smart contract wrapper with credentials:0xb5a66d27457af8be2a09f17add73c2ae46520e69
    contract ID:822232612
    Using pastry property file etc/pastry.properties
    loading storage...

    I am not sure, but maybe the shared storage error relates to this issue somehow. This error does not occur when starting the las2peer node from the docker image. However, since the docker image builds an older version, not supporting the version of my service built with Java 17, I can unfortunately not use this node without first downgrading my build (which is why I uncommented the command in the start script and use a local node outside docker, instead).

I could also imagine that my method of deploying the consent registry contracts might be faulty. However, running the truffle migration from inside my registry contracts directory fails with the following error message:

Transaction: 0x9b3bfe107bd17f505ae6c1b267eadeb123d6868a70481209e817d5faf9dd1d69 exited with an error (status 0) after consuming all gas.
     Please check that the transaction:
     - satisfies all conditions set by Solidity `assert` statements.
     - has enough gas to execute the full transaction.
     - does not trigger an invalid opcode by other means (ex: accessing an array out of bounds).
Truffle v5.4.24 (core: 5.4.24)
Node v10.19.0

Therefore, I copy my scripts to the las2peer-registry-contracts repo and perform the truffle migration from there, which works.

Michi03 commented 2 years ago

Small side-note: If you're using docker-compose, passing 127.0.0.1:8545 as ETHEREUM_HOST won't work, so you'll have to pass the name of the container, instead. This also means that, you'll have to then change it inside the registry property file that's copied from the las2peer container.

Michi03 commented 2 years ago

Addendum: The issue is, as far as I can tell unrelated to Java 17 and the mentioned error related to the function "i5.las2peer.persistency.SharedStorage.fetchEnvelope(String, long)", since when using Java 14 the error isn't displayed but the service announcement fails, nevertheless.

Michi03 commented 2 years ago

Using the web-connector, seemingly the same error occurs, but a complete stack trace is thrown:

i5.las2peer.registry.exceptions.EthereumException: Failed to register service
    at i5.las2peer.registry.ReadWriteRegistryClient.registerService(ReadWriteRegistryClient.java:318)
    at i5.las2peer.p2p.EthereumNode.registerServiceName(EthereumNode.java:387)
    at i5.las2peer.p2p.EthereumNode.registerServiceInBlockchain(EthereumNode.java:353)
    at i5.las2peer.tools.PackageUploader.registerService(PackageUploader.java:198)
    at i5.las2peer.tools.PackageUploader.uploadServicePackage(PackageUploader.java:183)
    at i5.las2peer.tools.PackageUploader.uploadServicePackage(PackageUploader.java:142)
    at i5.las2peer.connectors.webConnector.handler.ServicesHandler.handleServicePackageUpload(ServicesHandler.java:161)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:124)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:167)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:176)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:79)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:475)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:397)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:255)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:234)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:684)
    at org.glassfish.jersey.jdkhttp.JdkHttpHandlerContainer.handle(JdkHttpHandlerContainer.java:135)
    at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:95)
    at jdk.httpserver/sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:82)
    at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:98)
    at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:728)
    at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:95)
    at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:695)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.util.concurrent.ExecutionException: org.web3j.protocol.exceptions.TransactionException: Transaction has failed with status: 0x0. Gas used: 46431. (not-enough gas?)
    at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
    at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
    at i5.las2peer.registry.ReadWriteRegistryClient.registerService(ReadWriteRegistryClient.java:316)
    ... 37 more
Caused by: org.web3j.protocol.exceptions.TransactionException: Transaction has failed with status: 0x0. Gas used: 46431. (not-enough gas?)
    at org.web3j.tx.Contract.executeTransaction(Contract.java:372)
    at org.web3j.tx.Contract.executeTransaction(Contract.java:345)
    at org.web3j.tx.Contract.executeTransaction(Contract.java:339)
    at org.web3j.tx.Contract.executeTransaction(Contract.java:334)
    at org.web3j.tx.Contract.lambda$executeRemoteCallTransaction$3(Contract.java:399)
    at org.web3j.protocol.core.RemoteCall.send(RemoteCall.java:42)
    at org.web3j.utils.Async.lambda$run$1(Async.java:38)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
    ... 3 more
Michi03 commented 2 years ago

Ok, figured it out... So during the truffle migration, the following message is logged:

las2peer_1  |    Replacing 'ServiceRegistry'
las2peer_1  |    ---------------------------
las2peer_1  |    > transaction hash:    0xb5792f745364bf7ae2bd7b540b725fa913a09ac4deae396bbf68301988ad8410
las2peer_1  | - Blocks: 0            Seconds: 0
las2peer_1  |    > Blocks: 0            Seconds: 0
las2peer_1  |    > contract address:    0xdD76D242AAe1C3251DaE4f90EB0021215e62AFc4
las2peer_1  |    > account:             0xb5A66D27457Af8be2a09F17adD73c2ae46520e69
las2peer_1  |    > balance:             71
las2peer_1  |    > gas used:            2778195
las2peer_1  |    > gas price:           20 gwei
las2peer_1  |    > value sent:          0 ETH
las2peer_1  |    > total cost:          0.0555639 ETH

The printed contract address (0xdD76D242AAe1C3251DaE4f90EB0021215e62AFc4) is thus written to the migration.log file and this way ends up in the i5.las2peer.registry.data.RegistryConfiguration.properties file (although the actual address of the Service Registry is still present in the property file as well, either the wrong or none of the two values for the serviceRegistryAddress property is selected).

The problem is thus fixed by removing this additional (incorrect) address for the service registry stored in the etc/i5.las2peer.registry.data.RegistryConfiguration.properties file before starting the node.