A look at Smart Contract development with Stratis

Introduction

Smart contracts generated a lot of hype during the incredible 2017 crypto bull run that saw Bitcoin rocket to $20,000 and Ethereum to more than $1,000. Much of the crypto hype has since subsided, but there remains huge interest in Blockchain technologies, and the smart contracts that brought Ethereum to prominence.

Stratis first gained traction in early 2017 as a C#-based alternative to Ethereum. It has had a relatively low-key 2018, suffering like most other cryptos recently. However interest was revived somewhat with the release of smart contracts and side-chains at the turn of the year. While attention is focused on projects such as EOS and NEO, Stratis is emerging as the dark-horse to rival Ethereum.

In this article I’ll dig into Stratis smart contracts with C#, showing the basic of getting up and running, and briefly comparing them to smart contracts written in Ethereum’s Solidity programming language.

Please note that I hold both Ether and Stratis tokens which may prejudice opinions posted here. This is not investment advice.

Caveats

First thing to note is that DApps are relatively restricted compared to more traditional applications; they are deterministic, necessary as the state of a smart contract is distributed redundantly on many (or all, depending on the network in question) nodes, and these nodes need to reach consensus on the state of the contract. This is not possible if non-deterministic behaviour is introduced. This obviously rules out things such as random number generation, however less obvious functionality, such as time-stamping, is also excluded. This is the case for all of the major smart contract platforms. If you need non-deterministic behaviour then a smart contract is not where you want to be putting it. Stratis validation of non-deterministic smart contracts will fail when non-deterministic behaviour is detected.

Getting Started

Some of this material here comes from following this guide, however some of material I looked at was either missing information, had errors, and/or omissions.

First thing to note, the Stratis guide provides a link to a .visx template. This is absolutely not mandatory, a simple .Net Core class will suffice, allowing cross-platform development (visx templates are Windows-only). Indeed this exercise was carried out on both OSX and Windows 10 (via a VM).

First we are going to create a very basic smart contact, which we will then test locally on a test chain. Open Visual Studio and create a new .Net Core Solution. We will require 2 projects (a .NET core class library and a .NET core test project); the first will just contain the smart contract code, the second will be used to test the contract on a test chain.

We want to do the following things:

  1. Compile a smart contract.
  2. Ensure that the contract can be deployed to a local test chain.
  3. Execute a method on the smart contract.

The Stratis documentation I linked to earlier has a nice guide on how to write and validate your smart contract. However their guide to testing is incomplete and I needed to do some fudging to get this setup, which included building the StratisBitcoinFullNode project and dragging in some DLLs (automated using Powershell scripts). To save you this pain I wrapped up a helper in a Nuget package that you can use to assist you. This is used in the following examples.

First lets write a basic smart contract:

// MyContract.cs
using System;
using Stratis.SmartContracts;

public class SimpleSmartContract : SmartContract
{
public SimpleSmartContract(ISmartContractState smartContractState) : base(smartContractState)
{

}

public string SayHello(string nameToSayHelloTo)
{
return $"Hello {nameToSayHelloTo}";
}
}

Nothing too scary here, just a basic C# class inheriting from Stratis’s SmartContract base class. Note however that there is no namespace around the class — this is a big gotcha that will give you some very convoluted errors if you accidentally put one in.

Now we’d like to test this to make sure it build, deploys, and its methods can be executed. To do this we use the Nuget package I pointed you too earlier. If you want to know what is going on under the hood it is on Github.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using StratisContractTester;

namespace StratisContractTesterTests
{
[TestClass]
public class ContractTesterTests
{
private static string ContractFile => @"..\..\..\..\Contract\SimpleSmartContract.cs".Replace(@"\", System.IO.Path.DirectorySeparatorChar.ToString());

private ContractTester contractTester;

[TestInitialize]
public void Initialize()
{
contractTester = new ContractTester();
}

[TestMethod]
public void Compiles()
{
var compilationResult = contractTester.Compile(ContractFile);
Assert.IsTrue(compilationResult.Success);
}

[TestMethod]
public void PublishesSmartContract()
{
var compilationResult = contractTester.Compile(ContractFile);
if (!compilationResult.Success)
{
Assert.Inconclusive(
"Failed to compile smart contract. Probably a bug earlier in the flow.");
return;
}
var contractAddress = contractTester.PublishContract(compilationResult);
Assert.IsNotNull(contractAddress);
}

[TestMethod]
public void ExecutesMethod()
{
var compilationResult = contractTester.Compile(ContractFile);
if (!compilationResult.Success)
{
Assert.Inconclusive(
"Failed to compile smart contract. Probably a bug earlier in the flow.");
return;
}
var contractAddress = contractTester.PublishContract(compilationResult);
if(contractAddress == null)
{
Assert.Inconclusive("Failed to publish smart contract. Probably a bug earlier in the flow.");
return;
}
var result = contractTester.ExecuteMethod(contractAddress,
"SayHello", new[] { "Garry" });
Assert.AreEqual("Hello Garry", result.Return);
}
}
}

This is a little different to how we might normal do unit testing in C#. Here we are directly pulling in a C# file, and under the covers Stratis is sending that code to the Rosyln compiler (after validating the contract). A more traditional unit test could also be carried out, focusing purely on functionality — this simply requires mocking ISmartContractState, a trivial task with one of the many mocking frameworks available in .NET. I wanted to test the compilation, deployment and execution on a test chain, and the Stratis documentation was a bit unclear about how this could be done.

Run the tests and validate that they are all passing.

Congratulations. You have written a very basic smart contract.

Deploying to Testnet

Exciting at the previous section was, it is pretty useless without being deployed somewhere. Those of you familiar with Ethereum development will have deployed your contracts to a Testnet; this is something that is supposed to replicate the real network, but without costing you real crypto currency. You will still need to pay gas to run your contracts however; to do this we need to setup and run a smart-contract-enabled version of the Stratis Bitcoin full node (this sucks a bit, hopefully this will be made easier in time).

Get the latest release (may have changed since I wrote this, so check first):

git clone https://github.com/stratisproject/StratisBitcoinFullNode.git
cd StratisBitcoinFullNode
git fetch
git checkout tags/v3.0.0.0

Get the node up and running (will compile automatically).

cd src/Stratis.StratisSmartContractsD
dotnet run -— -addnode=13.64.119.220 -addnode=20.190.57.145 -addnode=40.68.165.12

Now open up http://localhost:38220/swagger, navigate to /api/wallet/create and create a wallet on Testnet. This is the POST body:

{
"password": "**********",
"passphrase": "**********",
"name": "mrtesterella"
}

…we would like to know where to send the $strat, so let’s recover an address. Find /api/wallet/addresses and enter the wallet name you used and the account name (which will usually be “account 0”).

Copy the first address in the list and request some $strat from the Faucet site. The “success” link doesn’t seem to work sometimes, so I just used the Swagger endpoint /api/Wallet/balance to check my balance. The response will look something like this:

{
"balances": [{
"accountName": "account 0",
"accountHdPath": "m/44'/105'/0'",
"coinType": 105,
"amountConfirmed": 10000000000,
"amountUnconfirmed": 0
}]
}

Let’s get our smart contract deployed. Run this command from a terminal window (MUST be run from inside the src/Stratis.SmartContracts.Tools.Sct/ directory, and you must be running the StratisBitcoinFullNode):

Deploy:

dotnet run -- deploy 
./../../../StratisContractTester/Contract/SimpleSmartContract.cs
http://localhost:38220
-wallet mrtesterella
-password **********
-fee 0.002
-gaslimit 50000
-gasprice 100
-amount 0
-sender moo4Gwy1EcZbFCXHZJ3NmcRbjLG6szcaxE

The tool will return a contract address, that looks something like mroDSHbnnVbdg99Cnkxf4ghFQeVZb2EmJ1. Make a note of this as you’ll need this to invoke the method on the contract.

Execute a method on the smart contract

The easiest way to do this is through the Swagger API. Open up /api/SmartContracts/local-call. Add the following body:

{
"contractAddress": "mroDSHbnnVbdg99Cnkxf4ghFQeVZb2EmJ1",
"methodName": "SayHello",
"amount": "0",
"gasPrice": 100,
"gasLimit": 50000,
"sender": "moo4Gwy1EcZbFCXHZJ3NmcRbjLG6szcaxE",
"parameters": [
"4#John Smith"
]
}

Note that 4# prefix in front of my name parameter; that is not a typo. Stratis needs these prefixes to coerce your parameters into the correct type. The types and their corresponding magic numbers at the time of writing are found here, and repeated below. This is a little bit ugly, but not a major problem.

+----------------------------------------------------+
| Number | Type |
+----------------------------------------------------+
| #1 | bool |
| #2 | byte |
| #3 | char |
| #4 | string |
| #5 | uint |
| #6 | int |
| #7 | ulong |
| #8 | long |
| #9 | Stratis.SmartContracts.Address |
| #10 | byte[] |
+----------------------------------------------------+

The smart contract in this document was stateless. However we could create a method to increment a number (such as an account balance) and that would propagate out across the Stratis network. A further call at a later time could recover the updated balance, exemplifying the statefullness of smart contracts.

Conclusions

As we’ve shown here Stratis opens up the world of smart contracts to C# developers who may have been put off by the quirks (to put it kindly) of the Solidity language. The .NET framework, upon which Stratis is based, is a robust framework that has stood the test of time within the world of enterprise development. In this regard Stratis definitely has the edge on Ethereum, and may emerge as its biggest challenger over the next year or so.

On the downside many compromises have been made; commonly used features such as Generics don’t seem to be allowed in smart contract code yet (both List<T> and Dictionary<TKey, TValue> failed), and attempting to use them caused validation to fail. Whether I’ve missed something here, I can’t say for sure. The Stratis codebase has plenty of examples of smart contracts here for those curious about exactly what they can use.

Stratis developers will be working with a much more constrained subset of .NET than they have been accustomed to. Generics only came to .NET in version 2.0, and the examples shown seem to use a construct called PersistentState rather than the generic Dictionary type I’d reached for. If you dig further it becomes clear a lot of other stuff is missing too; Linq expressions, and lambda functions, to name just two, are syntactic sugars that Stratis developers will have to do without. Maybe these will get added in the future, who knows? Smart contracts are generally not supposed to be massively complex from an engineering perspective, so I suspect developers will overlook these drawbacks and focus on being able to use one the most intuitive and robust programming languages in the world. It is still feels like a huge improvement on having to fight with Solidity, and the toolset is very respectable for a first release.

I had to do a fair bit of Googling, and it took a lot of trial and error to get my contract deployed. The documentation is fairly patchy at the moment (and often inconsistent), but it is still very early days. Those especially uncomfortable on the command line may have a hard time with Stratis (however they probably would with most of these platforms).

The StratisBitcoinFullNode itself was solid, compiling on both Windows and Mac, and it hides most of the huge complexity going on under the covers. Stratis promise a TestChain component in the near future, rendering my Nuget package redundant. Once the syntax was understood deploying a smart contract was fairly trivial, and the Stratis Testnet proved to be every bit as useful as its Ethereum equivalent. Oh, and Stratis are in the Azure Marketplace too, which will make it much easier to sell as a solution in large organisations.

Stratis runs a proof-of-stake consensus model, sidechains and a mainstream programming language. Proponents of Ethereum have often argued against each of these features for a variety of reasons, so we will now see how they compare in the wild. Stratis looks like being one of the big hitters of 2019. If this year turns out to be the year DApps take off I expect it to be right at the forefront, pushing Ethereum all the way.