Smart contract run with REST API¶
Hint
Technical description of contracts implementation is given in module Docker Smart Contracts.
Description of program logic¶
This module reviews an example of how to create and run a simple smart contract. The contract performs increment the number transferred to the contract entry in call-transactions.
Program listing contract.py
on Python:
import json
import os
import requests
import sys
def find_param_value(params, name):
for param in params:
if param['key'] == name: return param['value']
return None
def print_success(results):
print(json.dumps(results, separators=(',', ':')))
def print_error(message):
print(message)
sys.exit(3)
def get_value(contract_id):
node = os.environ['NODE_API']
if not node:
print_error("Node REST API address is not defined")
token = os.environ["API_TOKEN"]
if not token:
print_error("Node API token is not defined")
headers = {'X-Contract-Api-Token': token}
url = '{0}/internal/contracts/{1}/sum'.format(node, contract_id)
r = requests.get(url, verify=False, timeout=2, headers=headers)
data = r.json()
return data['value']
if __name__ == '__main__':
command = os.environ['COMMAND']
if command == 'CALL':
contract_id = json.loads(os.environ['TX'])['contractId']
value = get_value(contract_id)
print_success([{
"key": "sum",
"type": "integer",
"value": value + 1}])
elif command == 'CREATE':
print_success([{
"key": "sum",
"type": "integer",
"value": 0}])
else:
print_error("Unknown command {0}".format(command))
Description of operation
The program expects to get the data structure in json format with the field “params”.
It reads the values of the “a” fields.
Returns the result as a value of field “{a} + 1” in json format.
Example of incoming parameters
"params":[
{
"key":"a",
"type":"integer",
"value":1
}
]
Installing a smart contract¶
Download and install Docker for Developers for your operating system.
Prepare a contract image. In the
stateful-increment-contract
folder, create the following files:
contract.py
Dockerfile
run.sh
Listing of run.sh file
#!/bin/sh
python contract.py
Dockerfile File Listing
FROM python:alpine3.8
ADD contract.py /
ADD run.sh /
RUN chmod +x run.sh
RUN apk add --no-cache --update iptables
CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"
Important
It is required to install iptables into the smart contract container.
Install the image in Docker registry. Execute the following commands in the terminal:
docker run -d -p 5000:5000 --name registry registry:2
cd contracts/stateful-increment-contract
docker build -t stateful-increment-contract .
docker image tag stateful-increment-contract localhost:5000/stateful-increment-contract
docker start registry
docker push localhost:5000/stateful-increment-contract
Run the following command in the terminal to get the information about the container:
docker inspect 57c2c2d2643d
[
{
"Id": "sha256:57c2c2d2643da042ef8dd80010632ffdd11e3d2e3f85c20c31dce838073614dd",
"RepoTags": [
"wenode:latest"
],
"RepoDigests": [],
"Parent": "sha256:d91d2307057bf3bb5bd9d364f16cd3d7eda3b58edf2686e1944bcc7133f07913",
"Comment": "",
"Created": "2019-10-25T14:15:03.856072509Z",
"Container": "",
"ContainerConfig": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
The smart contract identifier Id
is the value of the imageHash
field and it is used in transactions with the created smart contract.
Sign a transaction to create a smart contract. In this example, the transaction is signed with the key stored in the node keystore.
Hint
To create a key pair and the participant address, use the utility generators.jar. The procedure for creating a key pair is given in item 1 of the module “Connecting to the Network”. The rules for generating queries to the node are given in the module Node REST API.
Query Body
{
"fee": 100000000,
"image": "stateful-increment-contract:latest",
"imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65",
"contractName": "stateful-increment-contract",
"sender": "3PudkbvjV1nPj1TkuuRahh4sGdgfr4YAUV2",
"password": "",
"params": [],
"type": 103,
"version": 1
}
Sample query
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d ' { \
"fee": 100000000, \
"image": "stateful-increment-contract:latest", \
"imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65", \
"contractName": "stateful-increment-contract", \
"sender": "3PudkbvjV1nPj1TkuuRahh4sGdgfr4YAUV2", \
"password": "", \
"params": [], \
"type": 103, \
"version": 1 \
}' 'http://localhost:6862/transactions/sign'
Sample response
{
"type": 103,
"id": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky",
"sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew",
"senderPublicKey": "3kW7vy6nPC59BXM67n5N56rhhAv38Dws5skqDsjMVT2M",
"fee": 500000,
"timestamp": 1550591678479,
"proofs": [ "yecRFZm9iBLyDy93bDVaNo1PR5Qkkic7196GAgUt9TNH1cnQphq4yGQQ8Fxj4BYA4TaqYVw5qxtWzGMPQyVeKYv" ],
"version": 1,
"image": "stateful-increment-contract:latest",
"imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65",
"contractName": "stateful-increment-contract",
"params": [],
"height": 1619
}
Send the signed transaction to the blockchain. The response from the sign method must be transferred to the input for the broadcast method.
Sample query
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
{
"type": 103, \
"id": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky", \
"sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew", \
"senderPublicKey": "3kW7vy6nPC59BXM67n5N56rhhAv38Dws5skqDsjMVT2M", \
"fee": 500000, \
"timestamp": 1550591678479, \
"proofs": [ "yecRFZm9iBLyDy93bDVaNo1PR5Qkkic7196GAgUt9TNH1cnQphq4yGQQ8Fxj4BYA4TaqYVw5qxtWzGMPQyVeKYv" ], \
"version": 1, \
"image": "stateful-increment-contract:latest", \
"imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65", \
"contractName": "stateful-increment-contract", \
"params": [], \
"height": 1619 \
}
}' 'http://localhost:6862/transactions/broadcast'
Use the transaction ID to check that the contract initiation transaction is placed in the blockchain.
Sample response
{
"type": 103,
"id": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky",
"sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew",
"senderPublicKey": "3kW7vy6nPC59BXM67n5N56rhhAv38Dws5skqDsjMVT2M",
"fee": 500000,
"timestamp": 1550591678479,
"proofs": [ "yecRFZm9iBLyDy93bDVaNo1PR5Qkkic7196GAgUt9TNH1cnQphq4yGQQ8Fxj4BYA4TaqYVw5qxtWzGMPQyVeKYv" ],
"version": 1,
"image": "stateful-increment-contract:latest",
"imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65",
"contractName": "stateful-increment-contract",
"params": [],
"height": 1619
}
Smart Contract Execution¶
Sign a call-transaction to call (execute) the smart contract.
In the ‘’contractID’’ field, specify the contract initialization transaction ID.
Query Body
{
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2",
"fee": 10,
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58",
"password": "",
"type": 104,
"version": 1,
"params": [
{
"type": "integer",
"key": "a",
"value": 1
}
]
}
Sample query
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2", \
"fee": 10, \
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58", \
"password": "", \
"type": 104, \
"version": 1, \
"params": [ \
{ \
"type": "integer", \
"key": "a", \
"value": 1 \
} \
] \
}' 'http://localhost:6862/transactions/sign'
Sample response
{
"type": 104,
"id": "9fBrL2n5TN473g1gNfoZqaAqAsAJCuHRHYxZpLexL3VP",
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58",
"senderPublicKey": "2YvzcVLrqLCqouVrFZynjfotEuPNV9GrdauNpgdWXLsq",
"fee": 10,
"timestamp": 1549365736923,
"proofs": [
"2q4cTBhDkEDkFxr7iYaHPAv1dzaKo5rDaTxPF5VHryyYTXxTPvN9Wb3YrsDYixKiUPXBnAyXzEcnKPFRCW9xVp4v"
],
"version": 1,
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2",
"params": [
{
"key": "a",
"type": "integer",
"value": 1
}
]
}
Send the signed transaction to the blockchain. The response from the sign method must be transferred to the input for the broadcast method.
Sample query
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
"type": 104, \
"id": "9fBrL2n5TN473g1gNfoZqaAqAsAJCuHRHYxZpLexL3VP", \
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58", \
"senderPublicKey": "2YvzcVLrqLCqouVrFZynjfotEuPNV9GrdauNpgdWXLsq", \
"fee": 10, \
"timestamp": 1549365736923, \
"proofs": [ \
"2q4cTBhDkEDkFxr7iYaHPAv1dzaKo5rDaTxPF5VHryyYTXxTPvN9Wb3YrsDYixKiUPXBnAyXzEcnKPFRCW9xVp4v" \
], \
"version": 1, \
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2", \
"params": [ \
{ \
"key": "a", \
"type": "integer", \
"value": 1 \
} \
] \
}' 'http://localhost:6862/transactions/broadcast'
Get the result of smart contract execution by its ID.
Sample response
[
{
"key": "1+1",
"type": "integer",
"value": 2
}
]