EOS.IO Programming Notes: How to create the HelloWorld contract
$ My EOS.IO programming notes for 2/27/2018
by: Gunnar Pope
Please upvote @powderskier on Steemit to support!
Or Donate!
BTC: 35fLcnnsUFDRp4fW3d7GLBTdEtyGR8SY7g
LTC: MC85Gqxaj5sBxKSui54VbKTypUie4Rrng
Ok, let's get started
$ cd ~/eos/build/programs/eos-walletd && ./eos-walletd
Open a new terminal, using Command+t. In the new terminal, open the public testnet
~$ cd eos/build/programs/eosc/
eosc$ ./eosc wallet open -n gptestnet
Opened: gptestnet
eosc$ ./eosc wallet list
Wallets:
[
"gptestnet"
]
eosc$ eosc wallet unlock -n gptestnet
password: Unlocked: gptestnet
eosc$ eosc wallet list
Wallets:
[
"gptestnet *"
]
eosc$ ./eosc -H testnet1.eos.io -p 80 get account powderskier
{
"account_name": "powderskier",
"eos_balance": "100.0000 EOS",
"staked_balance": "0.0001 EOS",
"unstaking_balance": "0.0000 EOS",
"last_unstaking_time": "1969-12-31T23:59:59",
"permissions": [{
"perm_name": "active",
"parent": "owner",
"required_auth": {
"threshold": 1,
"keys": [{
"key": "EOS7gUMtWH1TEW31v8rXCe6zYajqUo46qH22CQN6PX4nzMzJZkUhS",
"weight": 1
}
],
"accounts": []
}
},{
"perm_name": "owner",
"parent": "",
"required_auth": {
"threshold": 1,
"keys": [{
"key": "EOS7gUMtWH1TEW31v8rXCe6zYajqUo46qH22CQN6PX4nzMzJZkUhS",
"weight": 1
}
],
"accounts": []
}
}
]
}
Ok, we're in. Now let's learn how to make our own contract from scratch. Jump
into the contracts folder and use eoscpp
to generate the template for a
new hello world contract (that is artificially intelligent :).
$ cd ~/eos/contracts
contracts$ ls
CMakeLists.txt dice identity musl skeleton test.system tic_tac_toe
asserter eosio.system infinite noop social test_api
bancor eosiolib libc++ proxy stltest test_api_db
currency exchange multi_index_test simpledb storage test_api_mem
Create a new contract named helloworld
contracts$ eoscpp -n helloworld
created helloworld from skeleton
contracts$ ls
CMakeLists.txt dice helloworld multi_index_test simpledb storage test_api_mem
asserter eosio.system identity musl skeleton test.system tic_tac_toe
bancor eosiolib infinite noop social test_api
currency exchange libc++ proxy stltest test_api_db
contracts$ cd helloworld/
helloworld$ ls
helloworld.abi helloworld.cpp helloworld.hpp
So we now have .abi .cpp and .hpp templates for a simple helloworld app. (Thanks
eosio dev team!). Let's make our own print statement and try to upload this in
it's simplest state before building complexity on top of it. I always write programs
as a series of mini-test cases that start simple and build up in complexity. That
way, you always have a working example for your troubleshooting and not dozens
of returned errors.
Open the vim text editor. (Read about vim usage if unfamiliar).
helloworld$ vim helloworld.cpp
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <helloworld.hpp>
/**
* The init() and apply() methods must have C calling convention so that the blockchain can lookup and
* call these methods.
*/
extern "C" {
/**
* This method is called once when the contract is published or updated.
*/
void init() {
eosio::print( "Init World!\n" );
}
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t code, uint64_t action ) {
eosio::print( "Hello World: ", eosio::name(code), "->", eosio::name(action), "\n" );
}
} // extern "C"
Type :wq
to save changes and exit the vim editor.
Now, we must compile the .cpp file into a .wasm before uploading the contract
to the blockchain.
helloworld$ ls
helloworld.abi helloworld.cpp helloworld.hpp
helloworld$ eoscpp -o helloworld.wast helloworld.cpp
/usr/local/wasm/bin/clang -emit-llvm -O3 --std=c++14 --target=wasm32 -ffreestanding -nostdlib -fno-threadsafe-statics -fno-rtti -fno-exceptions -I /Users/winter/eos/build/install/include -I . -c helloworld.cpp -o /var/folders/8x/n9my8ww91_q304d3s0hdmdrh0000gq/T/tmp.eS0IfkMs/built/helloworld.cpp
helloworld$ ls
helloworld.abi helloworld.cpp helloworld.hpp helloworld.wast
Cool! We have a helloworld.wast file that we can now set in the eos blockchain.
helloworld$ ./eosc -H testnet1.eos.io -p 80 set contract powderskier helloworld.wast
-bash: ./eosc: No such file or directory
helloworld$ eosc -H testnet1.eos.io -p 80 set contract powderskier helloworld.wast
-bash: ./eosc: No such file or directory
Looks like the terminal cannot find where the ./eosc script is located. Let's try to add that to our path and try again.
helloworld$ echo "export PATH=$PATH:eos/build/install/bin" >> ~/.bash_rc
helloworld$ source ~/.bash_rc
helloworld$ eosc -H testnet1.eos.io -p 80 set contract powderskier helloworld.wast
-bash: ./eosc: No such file or directory
helloworld$ ./eosc -H testnet1.eos.io -p 80 set contract powderskier helloworld.wast
-bash: ./eosc: No such file or directory
That's weird. Thought that would work. Well, I know that I can run the script
directly within the /build/programs/eosc/ folder so let's navagate there
and try to upload the contract again.
helloworld$ cd ..
contracts$ cd ..
eos$ cd build/programs/eosc/
eosc$ ./eosc -H testnet1.eos.io -p 80 set contract powderskier ../../../contracts/helloworld/helloworld.wast
Reading WAST...
Assembling WASM...
Publishing contract...
{
"transaction_id": "bac597c2494fa72743b12db864bfb888d63922544802c6a29903da431a077d34",
"processed": {
"ref_block_num": 7288,
"ref_block_prefix": 3994800132,
"expiration": "2018-02-27T15:18:04",
"scope": [
"eos",
"powderskier"
],
"signatures": [
"2041f4c33c15f87fd4ae1dd43343243664bc998dff2c19d3f576df58e92856a321198bf3b435c3d76aec6d1063f08fa24e9320842cbf4170cdf0b68ad4cd87f6cd"
],
"messages": [{
"code": "eos",
"type": "setcode",
"authorization": [{
"account": "powderskier",
"permission": "active"
}
],
"data": {
"account": "powderskier",
"vm_type": 0,
"vm_version": 0,
"code": "0061736d0100000001110460017f0060017e0060000060027e7e00021b0203656e76067072696e746e000103656e76067072696e7473000003030202030404017000000503010001071903066d656d6f7279020004696e69740002056170706c7900030a20020600411010010b17004120100120001000413010012001100041c00010010b0b3f050041040b04504000000041100b0d496e697420576f726c64210a000041200b0e48656c6c6f20576f726c643a20000041300b032d3e000041c0000b020a000029046e616d6504067072696e746e0100067072696e7473010004696e697400056170706c790201300131",
"code_abi": {
"types": [],
"structs": [],
"actions": [],
"tables": []
}
},
"hex_data": "00ae72105f9538ad0000f1010061736d0100000001110460017f0060017e0060000060027e7e00021b0203656e76067072696e746e000103656e76067072696e7473000003030202030404017000000503010001071903066d656d6f7279020004696e69740002056170706c7900030a20020600411010010b17004120100120001000413010012001100041c00010010b0b3f050041040b04504000000041100b0d496e697420576f726c64210a000041200b0e48656c6c6f20576f726c643a20000041300b032d3e000041c0000b020a000029046e616d6504067072696e746e0100067072696e7473010004696e697400056170706c79020130013100000000"
}
],
"output": [{
"notify": [],
"deferred_trxs": []
}
]
}
}
Ok, looks like it got uploaded. Let's see if we generate the "Hello world" output.
eosc$ eosc push message powderskier hello '"abcd"' --scope powderskier
1885144ms main.cpp:1128 operator() ] Converting argument to binary...
{
"transaction_id": "ffd0af11f8f8087efc8b2ff7557e730e33eca32a688c5ee850967af3c214a021",
"processed": {
"ref_block_num": 7703,
"ref_block_prefix": 3694985328,
"expiration": "2018-02-27T15:31:54",
"scope": [
"powderskier"
],
"signatures": [],
"messages": [{
"code": "powderskier",
"type": "hello",
"authorization": [],
"data": ""
}
],
"output": [{
"notify": [],
"deferred_trxs": []
}
]
}
}
I don't know if that worked...
Maybe the 'hello' phrase is the message passed to the contract? Let's pass a new
message using the same function.
eosc$ eosc push message powderskier helloworlddoesthiswork? '"abcd"' --scope powderskier
2277295ms main.cpp:1128 operator() ] Converting argument to binary...
2277561ms main.cpp:1195 main ] Failed with error: Assert Exception (10)
status_code == 200: Error code 500
: {"code":500,"message":"Internal Service Error","details":"10 assert_exception: Assert Exception\nlen <= 13: \n {}\n native.hpp:111 set\n\n {\"str\":\"helloworlddoesthiswork?\"}\n native.hpp:115 set"}
Nope, that didn't work. Maybe the "abcd" data is the message payload. Let's
change that and see if that modifies the "data": ""
field.
eosc$ eosc push message powderskier hello '"datapayload"' --scope powderskier
2741187ms main.cpp:1128 operator() ] Converting argument to binary...
{
"transaction_id": "2fe23c69d6c30cdfd0a6bb5ef5a2da24ec070c44a1d1b141bb9a6ff72a0c3d60",
"processed": {
"ref_block_num": 8131,
"ref_block_prefix": 1071053060,
"expiration": "2018-02-27T15:46:10",
"scope": [
"powderskier"
],
"signatures": [],
"messages": [{
"code": "powderskier",
"type": "hello",
"authorization": [],
"data": ""
}
],
"output": [{
"notify": [],
"deferred_trxs": []
}
]
}
}
I'm not sure if this function is working because I don't see my "Hello World: " output. I'll try more tomorrow to figure out how to control the input->output messaging better.
I hope this helps!