[Blockchain]
Programming Smart Contracts in C#
By John deVadoss | November 2019
Blockchain platforms have led to incredible advances in the design and development of decentralized applications and systems, and have been applied to domains ranging from cryptocurrencies to enterprise supply chains. Smart contracts running on blockchain platforms are the core building block of this new wave of decentralized applications.
Smart contracts provide an execution environment for the economic and transactional logic that encompasses elements of a real-world contract, as well as execution of the terms of the contract. Running on massively scalable, decentralized and immutable blockchain platforms, they have the potential to reshape our economic institutions and the economic relationships and transactions that constitute these institutions.
In this article we guide you through the steps in developing, debugging and deploying your first smart contract using C#.
What Are Smart Contracts?
The term smart contract was first formalized by cryptographer Nick Szabo at the University of Washington in 1996. According to Szabo, “New institutions, and new ways to formalize the relationships that make up these institutions, are now made possible by the digital revolution …. A smart contract is a set of promises, specified in digital form, including protocols within which the parties perform on these promises.”
The easiest way to explain what a smart contract does is through an example. If you’ve bought a home or you know someone who has, you know there are multiple steps that make up what is often a time-consuming and tedious process. If you’re obtaining financing to close your new home, that’s another set of steps involving credit agencies, mortgage banks, escrow agencies and more. Each set of workflows and activities is fraught with potential errors, and multiple individuals and commercial entities are often forced to retrace the steps to finally move the buyer into their new home. Along the way, the buyer will have had to interact with a host of individuals, including the salesperson, finance broker, mortgage agent, escrow agent and lender. And, to compensate their work, various fees and commissions are added at each step of the process.
Smart contracts can automate the otherwise confusing and arduous process behind a mortgage contract. A smart contract in this scenario programmatically connects the different parties involved with the mortgage transactions, allowing for a secure, compliant, and scalable process, while making the workflow convenient, frictionless and less error-prone for the individuals.
A smart contract holds the economic and transactional logic that includes the elements of an ordinary contract (for example, offer, acceptance, revocation), as well as the execution of the terms (such as payment, price variation, penalties) that comprise the contract. It enables the processing of transactional actions at predetermined times and/or based in relation to the occurrence or non-occurrence of an external trigger, such as an exchange rate threshold, delivery of an asset or expiration of put/call.
The key capabilities enabled using a smart contract involve:
- The ability to authenticate parties and counterparties, ownership of assets and claims of right.
- The ability to access and refer to information and data both on the blockchain platform and outside of the smart contract (and the blockchain) to trigger transactions.
- The ability to automate the execution of transactions and (economic) protocols on the blockchain platform.
What Can Smart Contracts Do?
The potential benefits of smart contracts throughout an economic transaction include standardization (standardized schemas and protocols reducing the cost of negotiations and agreements), security (transactions are encrypted and stored on a blockchain platform designed to be immutable), latency (reduced transaction times and the streamlining or elimination of redundant manual processes) and transaction certainty (programmed execution, which reduces counterparty risk and settlement risk) and more.
As we examine the institutional landscape today, the use cases for smart contracts cover a broad range of applications:
- Self-Sovereign Identity: Enable individuals to own and control their digital identity, which contains reputation, data and digital assets.
- Securities: Simplify capitalization table management and the circumvention of intermediaries in the chain of securities custody.
- Trade Finance: Facilitate streamlined international transfers of goods through faster letters of credit and trade payment initiation, while enabling higher liquidity of financial assets.
- Data Governance and Compliance: Enable uniform financial data across organizations and improved financial reporting while reducing auditing and assurance costs.
- Supply Chains: Use IoT to track products as they move from the factory floor to the store shelf, enabling real-time visibility across the entire supply chain.
- Clinical Research and Trials: Increase cross-institutional visibility and data sharing while preserving privacy.
With a perspective on the potential of smart contracts, let’s explore the structure and development of our first smart contract.
The Hello World Smart Contract
Following hallowed computer science traditions, let’s consider our first Hello World contract. Figure 1 shows the code.
Figure 1 The Hello World Smart Contract
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;
using System;
namespace HelloWorld
{
public class HelloWorld : SmartContract
{
private const string test_str = "Hello World";
public static String Main(string operation, object[] args)
{
Storage.Put("Hello", "World");
return test_str;
}
}
}
Every smart contract inherits the SmartContract base class, which is in the Neo Framework and provides core methods. The Neo namespace surfaces the APIs made available by the NEO blockchain platform, providing a way for the smart contract to access blockchain data (all the data on the entire blockchain, including complete blocks and transactions, as well as each of their fields) and to manipulate the persistent store (every smart contract deployed has a storage space that can only be accessed by the contract itself).
Inside the contract class, properties defined with static readonly or const are contract properties, which can be used as constants. For instance, to define the owner of a contract or the factor number that will be used in subsequent asset transfers, we define them like this:
// Represents the owner of this contract, which is a fixed address
public static readonly byte[] Owner =
"ATrzHaicmhRj15C3Vv6e6gLfLqhSD2PtTr".ToScriptHash();
// A constant factor number
private const ulong factor = 100000000;
In addition, you can define static methods in the contract class and return a constant.
When you develop a smart contract, the blockchain platform provides a means to store your application data on the blockchain. All data in a smart contract’s storage is automatically persisted across invocations of the smart contract. Full nodes in the blockchain store the state of every smart contract on the chain.
The NEO blockchain platform provides data access interfaces based on key-value pairs. Our first smart contract uses the Storage class to read and write to persistent storage. For instance, to store the total supply of your token, you use this code:
// Key is totalSupply and value is 100000000
Storage.Put(Storage.CurrentContext, "totalSupply", 100000000);
Here, CurrentContext returns the current storage context. After obtaining the storage context, the object can be passed as an argument to other contracts (as a way of authorization), allowing them to perform read and write operations on the persistent store of the current contract.
With that, let’s explore a simple real-world scenario.
A Real-World Smart Contract
Consider a simplified DNS scenario where we want to register, query and delete a domain name associated with a given user, as shown in Figure 2.
Figure 2 DNS Smart Contract
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;
namespace Neo.SmartContract
{
public class Domain : SmartContract
{
public static object Main(string operation, params object[] args)
{
if (Runtime.Trigger == TriggerType.Application){
switch (operation){
case "query":
return Query((string)args[0]);
case "register":
return Register((string)args[0], (byte[])args[1]);
case "delete":
return Delete((string)args[0]);
default:
return false;
}
}
}
private static byte[] Query(string domain)
{
return Storage.Get(Storage.CurrentContext, domain);
}
private static bool Register(string domain, byte[] owner)
{
// Check if the owner is the same as the one who invoke the contract
if (!Runtime.CheckWitness(owner)) return false;
byte[] value = Storage.Get(Storage.CurrentContext, domain);
if (value != null) return false;
Storage.Put(Storage.CurrentContext, domain, owner);
return true;
}
private static bool Delete(string domain)
{
// To do
}
}
}
In theory, smart contracts can have any entry point, but usually the Main function serves as the entry point for ease of invocation.
Triggers A smart contract trigger is a mechanism that activates the execution of a smart contract. The most commonly used triggers are verification triggers and application triggers. Typically, you handle the triggers in the Main function.
A verification trigger is used to invoke the contract as a verification function, accepting multiple parameters and returning a valid Boolean value, indicating the validity of the transaction or block. The contract code is executed to verify whether a transaction involving assets owned by the contract address should be allowed to succeed.
When you transfer assets from account A to account B, verification is triggered. All the nodes in the blockchain that received the transaction verify account A’s contract. If the return value is true, the transfer completes successfully. If the return value is false, the transfer will fail and the transaction won’t be recorded in the blockchain:
public static bool Main(byte[] signature)
{
if (Runtime.Trigger == TriggerType.Verification)
{
if (/*condition A*/)
return true;
else
return false;
}
}
An application trigger is used to invoke the contract and you can expect the input arguments with the types you specified to be present. You’ll utilize these input values to determine the resulting state of the contract, and its output.
Unlike the verification trigger, which is activated by a transfer, an application trigger (shown in Figure 3) is activated by a special transaction, InvocationTransaction. Because the contract is executed after InvocationTransaction is confirmed, the transaction is recorded in the blockchain irrespective of whether the smart contract execution succeeds or fails.
Figure 3 Application Trigger
public static Object Main(string operation, params object[] args)
{
if (Runtime.Trigger == TriggerType.Verification)
{
if (/*Condition A*/)
return true;
else
return false;
}
if (Runtime.Trigger == TriggerType.Application)
{
if (operation == "FunctionA") return FunctionA(args);
}
}
// There is a smart contract entry point and redirected from main method
public static bool FunctionA(params object[] args)
{
// Some code
}
In this example, because it’s a smart contract without asset transfers involved, the only trigger that needs to be considered is the application trigger.
CheckWitness In many, if not all, cases, you’ll want to validate whether the address invoking your contract code is really who it says it is. The Runtime.CheckWitness method accepts a single parameter that represents the address you’d like to validate against the address used to invoke the contract code. More specifically, it verifies that the transactions or block of the calling contract has validated the required script hashes.
Programming Your First C# Smart Contract
We’ll use the preview release of the NEO Blockchain Toolkit for .NET on Visual Studio Code Marketplace to develop, debug and deploy our first smart contract using C#. For brevity, rather than using the Visual Studio Code extensions, we’ll use command-line tools to illustrate the steps. We’ll use the Hello World contract as our example.
In your terminal window, create an empty directory called HelloWorld. Change to that directory and invoke the dotnet new new-contract command. This will create a NEO smart contract that writes Hello World to blockchain storage. If you wish, you can create a Visual Basic smart contract by adding -lang VB to the command prior to execution.
You can immediately build the smart contract via the dotnet build command. The result should look something like Figure 4.
Figure 4 Building the Smart Contract
$ dotnet build
Microsoft (R) Build Engine version 16.2.32702+c4012a063 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 294.77 ms for /home/jdevados/Source/HelloWorld/HelloWorld.csproj.
HelloWorld -> /home/jdevados/Source/HelloWorld/bin/Debug/netstandard2.0/HelloWorld.dll
HelloWorld -> /home/jdevados/Source/HelloWorld/bin/Debug/netstandard2.0/publish/
Neo.Compiler.MSIL console app v2.4.1.1
Find entrypoint:System.Void HelloWorld::Main()
convert succ
gen debug succ
gen md succ
gen abi succ
write:HelloWorld.avm
write:HelloWorld.debug.json
write:HelloWorld.abi.json
SUCC
From the terminal window in your HelloWorld project directory, you can launch Visual Studio Code by executing code.
Before you can run the contract in the debugger, you need to create a launch configuration. The NEO smart contract debugger makes this very easy. From the top-level Debug menu select Add Configuration, and then from the Select Environment input box select NEO Contract.
When you hit F5 or select Debug | Start Debugging from the menu, the HelloWorld contract launches for debugging. At this point, you can do any of the following:
- Continue, Step Into, Step Over and Step In
- Set Breakpoints
- Inspect the Contents of emulated storage
- Inspect the Value of local parameters and variables
In order to deploy the smart contract, you need a PrivateNet instance of the blockchain. NEO Express is a developer-focused private network that’s built on the MainNet code base and provides full symmetry with the MainNet. We’ll use NEO Express to illustrate the steps involved in deployment.
Create a new NEO Express instance with the create command. You’ll see something like the output in Figure 5.
Figure 5 Creating and Running the PrivateNet
$ neo-express create
Created 1 node privatenet at /home/jdevados/Source/HelloWorld/default.neo-express.json
Note: The private keys for the accounts in this file are are *not* encrypted.
Do not use these accounts on MainNet or in any other system where security is a concern.
$ neo-express run --seconds-per-block 1
09:49:37.99 ConsensusService Info OnStart
09:49:38.08 ConsensusService Info initialize: height=1 view=0 index=0 role=Primary
09:49:38.15 ConsensusService Info timeout: height=1 view=0
09:49:38.15 ConsensusService Info send prepare request: height=1 view=0
09:49:38.23 ConsensusService Info send commit
09:49:38.32 ConsensusService Info relay block: height=1 hash=0x096aaa25191b8601856a0a1744b7f19b06807fd9888e247366eec3d212a507b6 tx=1
09:49:41.32 ConsensusService Info persist block: height=1 hash=0x096aaa25191b8601856a0a1744b7f19b06807fd9888e247366eec3d212a507b6 tx=1
09:49:41.32 ConsensusService Info initialize: height=2 view=0 index=0 role=Primary
09:49:42.33 ConsensusService Info timeout: height=2 view=0
By default, NEO Express creates a single-node blockchain, but you can create a four- or seven-node blockchain with the --count option. Once you’ve created the NEO Express blockchain instance, you can run it. Because this is a single-node blockchain, you don’t need to specify which node of the blockchain to run. You can control the block generation period via the --seconds-per-block option (-s for short) of the run command.
Because this terminal window is running the blockchain, open another terminal window in the same directory so you can interact with the running PrivateNet. In the new terminal window, you’ll create a standard wallet and transfer the “genesis” NEO tokens to that wallet, as you’ll need GAS to be able to deploy and invoke the contract, as shown in Figure 6.
Figure 6 Deploying the Smart Contract on the PrivateNet
$ neo-express wallet create testWallet
...
$ neo-express transfer neo 100000000 genesis testWallet
...
$ neo-express show account testWallet
{
...
"balances": [
{
"asset": "0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
"value": "100000000"
}
]
}
$ neo-express show gas testWallet
{
"unavailable": 1112,
"available": 0
}
$ neo-express transfer neo 100000000 testWallet testWallet
...
$ neo-express show gas testWallet
{
"unavailable": 96,
"available": 1408
}
$ neo-express claim gas testWallet
...
$ neo-express show account testWallet
{
...
"balances": [
{
"asset": "0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
"value": "100000000"
},
{
"asset": "0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
"value": "1408"
}
]
}
$ neo-express contract import bin/Debug/netstandard2.0/publish/
Does this contract use storage? [y/N] y
Does this contract use dynamic invoke? [y/N] n
Is this contract payable? [y/N] n
$ neo-express contract deploy HelloWorld testWallet
...
...
You can see how much GAS is available with the show gas command. In order to access the GAS, you need to execute another transfer, this time from and to the testWallet account. The available GAS can be claimed with the claim gas command and you can see the result with the show account command.
With a running NEO Express blockchain and a standard wallet account with plenty of GAS, you can deploy the smart contract to the blockchain. You start by importing the contract into NEO Express. NEO Express needs to know if the contract uses storage, dynamic invoke or if the contract is payable in order to deploy the contract. For NEO 3, this information will be in the smart contract manifest file.
The imported contract can now be deployed via the contract deploy command. You must specify a wallet account to pay the deployment GAS price. Invoking a smart contract without specifying a wallet account to pay the invocation GAS cost won’t modify the state of the blockchain. For the HelloWorld contract, this means that nothing will be written to blockchain storage. If you want a contract invocation to make durable changes, you can specify a wallet account to pay the GAS cost via the --account argument, as shown in Figure 7.
Figure 7 Invoking the Smart Contract on the PrivateNet
$ neo-express contract invoke HelloWorld --account testWallet
{
"contract-context": < omitted for clarity >
"script-hashes": < omitted for clarity >
"hash-data": < omitted for clarity >
"engine-state": {
"state": 1,
"gas-consumed": "1.017",
"result-stack": []
}
}
{
"txid":
"0x785346a3a338d70dd5bee6a70e1fc807a891d23a8d12d138b6a151b5eeae771e"
}
$ neo-express contract storage helloWorld
0x48656c6c6f
key (as string) : Hello
value (as bytes) : 0x576f726c64
(as string) : World
constant value : False
The HelloWorld contract takes no parameters and returns no values. However, it does modify contract storage in the blockchain. You can dump the storage state for a given contract with the contract storage command. This command lists all the key/value pairs in the blockchain, showing both keys and values as both a hex-encoded byte array and as a UTF-8 encoded string.
Wrapping Up
Smart contracts promise to transform our economic institutions, and the economic relationships and transactions that constitute these institutions. With benefits including standardization, security, reduced latency and transaction certainty and more, the benefits of smart contracts span domains ranging from the securities market, through clinical research and trials, to enterprise supply chains.
In this article we’ve focused on the what, why and how of smart contracts and provided a step-by-step approach to developing, debugging and deploying your first smart contract in C#.
John deVadoss leads development for NEO Global Development in Seattle. Previously he built Microsoft Digital, .NET Patterns & Practices and .NET Architecture Strategy. He also incubated Microsoft Azure when he was at Microsoft. Most recently, he launched two machine learning start-ups. deVadoss did his Ph.D. work in machine learning, specializing in recurrent neural networks.
Peng Huang is a leading industry blockchain platform strategist. Previously at Microsoft he led the Developer Tools and ISV strategy for Greater China, and product management for .NET Patterns & Practices in Redmond. He’s a senior member of ACM and began his career as an Assembly language coder on the SPARC and x86 platforms.
Thanks to the following technical expert for reviewing this article: Harry Pierson
Harry Pierson was a founding member of Windows runtime and xlang and product manager for IronPython. He spent 10+ years on the OS team, including Windows core and the Midori research project. He is currently chief architect for NGD based in Seattle.
Tidak ada komentar:
Posting Komentar