The case of the Solidity revert() in Azure Blockchain Workbench: “This is taking longer than expected”

Asset Transfer Code

At DynaChain our goal is to connect businesses through the use of Blockchain. Our first step in this journey is to build an integration between Microsoft Dynamics 365 CE and Azure Blockchain Workbench.

Azure Blockchain Workbench is a Blockchain-as-a-Service (BaaS) offering from Microsoft and is a collection of Azure services and capabilities designed to help you create and deploy blockchain applications to share business processes and data with other organizations. So you see, this is right up our alley.

Anyway, while I was doing some work on the integration I came across an issue in the workbench.

TL;DR: Whenever you see this warning in the workbench, check your Solidity function for revert().
this is taking longer than expected warning

I uploaded one of the example Ethereum smart contracts named “Asset Transfer” (available here on GitHub) and assigned Application Roles to the application. For simplicity I assigned myself to all the Application Roles.

I created an instance of the smart contract and wanted to go through the workflow as shown here (a more detailed view is on the Asset Transfer page):

asset transfer workflow diagram

The workbench creates a simple UI for each smart contract you upload, so that you can interact with it. Using that UI I created an instance of the smart contract (step 1 in the workflow) and after that I executed the function MakeOffer, which sets the state of the workflow from “Active” to “Offer Placed”.

However, the UI showed something was wrong:

this is taking longer than expected warning When checking the contract API using Postman I could see that there were 2 Contract Actions (2 successful calls to the workbench REST API), but only 1 Transaction (an actual transaction on the Ethereum blockchain).

"transactions": [
        {
            "id": 4,
            "connectionId": 1,
            "transactionHash": "0xd7dd9aac848b100e6e139e18c655c05f19208ecaa56731f94f7e0ec9968afcca",
            "blockID": 6086,
            "from": "0xd4f9a79c35b84f48baaf8a7bbca0018631a7444e",
            "to": null,
            "value": 0,
            "isAppBuilderTx": true
        }
    ],
    "contractActions": [
        {
            "id": 6,
            "userId": 1,
            "provisioningStatus": 2,
            "timestamp": "2018-07-12T12:15:16.0433333",
            "parameters": [
                {
                    "name": "Description",
                    "value": "1 BTC"
                },
                {
                    "name": "Price",
                    "value": "6000"
                }
            ],
            "workflowFunctionId": 1,
            "transactionId": 4,
            "workflowStateId": 1
        },
        {
            "id": 7,
            "userId": 1,
            "provisioningStatus": 0,
            "timestamp": "2018-07-12T12:19:40.77",
            "parameters": [
                {
                    "name": "Inspector",
                    "value": "0xd4f9a79c35b84f48baaf8a7bbca0018631a7444e"
                },
                {
                    "name": "Appraiser",
                    "value": "0x3a2538aa9739a2af49fb8de8b73ed5776f520769"
                },
                {
                    "name": "Offer Price",
                    "value": "5950"
                }
            ],
            "workflowFunctionId": 4,
            "transactionId": null,
            "workflowStateId": null
        }
    ]
Partial result from the Contract GET API call

Azure Blockchain Workbench provides a handy Powershell script for troubleshooting issues (located here) and although it did show some errors, I couldn’t find anything that explained the issue.

Finally I had a look at the Solidity code and found this:

function MakeOffer(address inspector, address appraiser, uint256 offerPrice) public
    {
        if (inspector == 0x0 || appraiser == 0x0 || offerPrice == 0)
        {
            revert();
        }
        if (State != StateType.Active)
        {
            revert();
        }
        // Cannot enforce "AllowedRoles":["Buyer"] because Role information is unavailable
        if (InstanceOwner == msg.sender) // not expressible in the current specification language
        {
            revert();
        }

        InstanceBuyer = msg.sender;
        InstanceInspector = inspector;
        InstanceAppraiser = appraiser;
        OfferPrice = offerPrice;
        State = StateType.OfferPlaced;
        ContractUpdated('MakeOffer');
    }
The MakeOffer function

If you look at the highlighted lines, it basically says that the Buyer (an Application Role) cannot be the same as the Owner (the user that created the contract instance), which makes sense. If that’s the case it calls revert(). For more information on revert() and it’s cousins assert() and require(), have a look here. And remember when I said I assigned myself to all Application Roles (for simplicity, lesson learned here…).

The problem is that revert() cannot return a value yet (supposedly they are working on it, see here.) so the workbench doesn’t really know what happened and just displays a generic warning message.

Afterwards I tried again a few times (once with my own user and once with another user). You see that the request with my own user failed again, but the request from the other user was successful.

this is taking longer than expected warning

So what did I learn from this? Well, for one, taking shortcuts (for simplicity sake) are going to cost you in the end. And second, check you reverts.

Here we go!

DynaChain.io is focussed on delivering easy integration between Dynamics 365 and Blockchain technology.

As the company is starting up, more information will become available through this site.