'Hello World' from giota: The IOTA Go API Library

I will introduce to you giota, a Go library for IOTA. I believe Gophers are all CLI (command line interface) lovers, so I've made a trivial but cool CLI tool called giotan.In this tutorial we will do three simple things with giotan - probably the three most important features and concepts of IOTA:

  1. Generate a random seed
  2. Get addresses and transactions from that seed
  3. Send and receive tokens

After doing these things with giota, we'll look into the code for each items.You can get giotan from the official Github repository. Feel free to contribute to the project and do pull request. Continuing on with this tutorial, I am assuming that you have successfully installed Go and giotan.

{{CODE}}

$ go install github.com/iotaledger/giotan

{{ENDCODE}}

Generate a Random Seed

{{CODE}}

$ giotan new 

// New seed:  BQELNJMSZZZPRDK9BCTEKLXQPXXPNNTSFYJSXVKESKJOJDJA9CIQTFAGETCFGLIJQYNLJFKQDOOYZMZBK

{{ENDCODE}}

COOL. That's all.  We get the seed BQELNJMSZZZPRDK9BCTEKLXQPXXPNNTSFYJSXVKESKJOJDJA9CIQTFAGETCFGLIJQYNLJFKQDOOYZMZBK.

Under the Hood: Generating a Random Seed functions

{{CODE}}

TryteAlphabet = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ"

func NewSeed() Trytes {

   b := make([]byte, 81)

   if _, err := rand.Read(b); err != nil {

      panic(err)

   }

   seed := make([]byte, 81)

   for i, r := range b {

      seed[i] = TryteAlphabet[int(r)%len(TryteAlphabet)]

   }

   return Trytes(seed)

}

{{ENDCODE}}

All the NewSeed() function does, is it gets random 81 bytes and select one character from `TrytesAlphabet` for each 81 bytes. `TrytesAlphabet` contains characters which used in seed string. This function should not be used in production, as it's mainly for demonstration purposes to understand seeds and the bytes to trytes representation. Note that IOTA uses Balancecd ternary. Normal PCs handle binary digitis i.e. bits (0 or 1) and bytes (0x00-0xff), but balanced ternary handle ternary digits, i.e. trits (-1 or 0 or 1) and trytes (9,A-Z) as you see table in appendix.

Ternary may be difficult to understand, but just remember that `trytes` means number (not string), just like bytes. So you can do calculation with trytes e.g. trytes + trytes. Future blog posts will help in bringing further clarity to Ternary. Also note that in giota, there are Trytes and Trits type, actually string and []int8 respectively.

Get Addresses from the Seed

Next, let's get addresses from the seed you got. Just run giotan address, then you are asked to enter a seed, so enter the seed above.

{{CODE}}

$ giotan addresses

input your seed:

using IRI server: http://eugene.iota.community:14265

address info:

used:

unused:

   JCKPBIXTASXPQZFLPFTPJEM9WYTVKYQGIAKMHQMZPBMZQUKZJW9YMJHC9GGBHUHZYRRLRPWVNPWVHKUD9

{{ENDCODE}}

Of course, there is no used address because this seed was just generated right now. This command indicates that the address genereated at index 0 from this seed is JCKPBIXTASXPQZFLPFTPJEM9WYTVKYQGIAKMHQMZPBMZQUKZJW9YMJHC9GGBHUHZYRRLRPWVNPWVHKUD9. If you want to receive iota tokens you can pass the address above to sender.

Under the Hood: Getting Addresses functions

{{CODE}}

//get a server

server := giota.RandomNode()

api := giota.NewAPI(server, nil)

//transform seed string from an argument to Trytes type

seedT, err := giota.ToTrytes(seed)

if err != nil {...}

//call GetUsedAddress functions to get used and unused addresses.

adr, adrs, err := giota.GetUsedAddress(api, seedT, 2)

if err != nil {...}

{{ENDCODE}}

Before calling the API, you need to get appropriate API server. You can set manually e.g. `http://localhost:14265`, but you can also use `RandomNode()` function, which selects one server from a list of public full nodes if you are not running your own IRI server. Note that if you are running the server on port 14265, `RandomNode()` returns your server.

Then seed you specified in arguments are checked and transformed to Trytes type. You can also use cast e.g. giota.Trytes(seed) , but you should use giota.Trytes because you need to confirm that the input string is valid Trytes .e.g. not contains invalid characters( | , ^ , etc).

Next it calls GetUnusedAddress, which gets all used addresses and one unsed address. Let's look into main part of GetUnusedAddress. This omits unimoprtant lines.

{{CODE}}

func GetUsedAddress(api *API, seed Trytes, security int) (Address, []Address, error) {

var all []Address

for index := 0; ; index++ {

adr, err := NewAddress(seed, index, security)

r := FindTransactionsRequest{

Addresses: []Address{adr},

}

resp, err := api.FindTransactions(&r)

if len(resp.Hashes) == 0 {

return adr, all, nil

}

all = append(all, adr)

}

}

{{ENDCODE}}

You can see addresses are made with seed, index and security. To make addresses from seed deterministically, NewAddress() calculates seed + index for creating one new address. security determines security level. security is bigger, security become stronger. In most cases in iota security equals 2 (162-trit security).

Then, FindTransactions() is called, which call findTransactions API. If transactions which are related to the address doesn't exists, this address is not used.

Send Tokens

It's time to send tokens. Obviously you need tokens. I have another seed which has 100 iota:

{{CODE}}

$ giotan addresses

input your seed:

using IRI server: http://eugene.iota.community:14265

address info:

used:

KTXFP9XOVMVWIXEWMOISJHMQEXMYMZCUGEQNKGUNVRPUDPRX9IR9LBASIARWNFXXESPITSLYAQMLCLVTL (balance=20)

PQTDJXXKSNYZGRJDXEHHMNCLUVOIRZC9VXYLSITYMVCQDQERAHAUZJKRNBQEUHOLEAXRUSQBNYVJWESYR (balance=0)

GXZWHBLRGGY9BCWCAVTFGHCOEWDBFLBTVTIBOQICKNLCCZIPYGPESAPUPDNBDQYENNMJTWSWDHZTYEHAJ (balance=50)

DSYDCI9SIFXIITFZHVWMHHSKAGJZIQ9TVKEDAABNJOZXDEKMCIQZOTJBVXNONADYTGQFMWMQLQEWPMJBN (balance=0)

WSINGDLHKTTYZIJRFABNPBKGOF9WYLFSJWSZKSBWXOEJYX9WNKXZCJ9TYKUMLFWWEQGNEJZDU9RWNBYW9 (balance=0)

ONQQJHRZVHUALFCPXHNZVPIIIBIPUFHXGRUGVNF9PNHTGOVVMEKMRCVPHDACOXREYWTQHAWOLLS9NSGTW (balance=0)

HEJBECTFLTHQXLXQONWSX9K9MMHUVPKTWEKAYXDHE9XCTMGKBOANGEYTKPEIJATHJNQTXYW9QCLJNHWJP (balance=0)

WZPPEKHDZZGSHFTFOLWOSTRNVNXGIAVVZZOYOTYRLYKLQKYXPPDCYPQEICYQ9KUEH9MELTGHGEFUMNBBY (balance=30)

unused:

9OYDZWKZGMPVSZSDRNLXWDFPSYNESRFLZWNIEMCAHJGSOO9SH9DALDGJZYIJHIXNTVKFEGGCLWTXXVZJO

{{ENDCODE}}

Now, let's send 12 iota token to KTXFP9XOVMVWIXEWMOISJHMQEXMYMZCUGEQNKGUNVRPUDPRX9IR9LBASIARWNFXXESPITSLYAQMLCLVTL9QTIWOWTY by running:

{{CODE}}

$ giotan send --recipient=KTXFP9XOVMVWIXEWMOISJHMQEXMYMZCUGEQNKGUNVRPUDPRX9IR9LBASIARWNFXXESPITSLYAQMLCLVTL9QTIWOWTY --amount=12 --tag=WHATACOOLTOOL

input your seed:

using IRI server: http://eugene.iota.community:14265

using PoW:PowSSE

bundle info:

bundle hash:  DZSBBHHAQWZJAPKTQZFHVFWFHIGSKFYEBBHBEFEWQKCVNWU99AZKUDFI9O9JW99ZJJYLKVMEFETHFGZYJ

No: 0/4

Hash : VUMHPESPHIKGGZTNUBTTSSNCMGIODQTKRWMKNLCEQNOYPLNZVF9SRJUCXBGEMNANVJMUBWOYV9V999999

Address:KTXFP9XOVMVWIXEWMOISJHMQEXMYMZCUGEQNKGUNVRPUDPRX9IR9LBASIARWNFXXESPITSLYAQMLCLVTL

Value:12

Timestamp:2017-03-27 14:26:22.114029139 +0900 JST

No: 1/4

Hash : HUPFNQX9ZBYPSZUURGZAEECAQHQNSISLXUMQLSBYFEDFDPXXVPRVWKTZ9IKJSL9QBNQCSYMVZIT999999

Address:KTXFP9XOVMVWIXEWMOISJHMQEXMYMZCUGEQNKGUNVRPUDPRX9IR9LBASIARWNFXXESPITSLYAQMLCLVTL

Value:-20

Timestamp:2017-03-27 14:26:31.309775962 +0900 JST

No: 2/4

Hash : H9VFJEQKGHMPFPHQGTZANTCQVF9YIDNSTQRKKUYWCZHSSYXUNVGAGRNGWYQAMTNTEOJCPSPTM9I999999

Address:KTXFP9XOVMVWIXEWMOISJHMQEXMYMZCUGEQNKGUNVRPUDPRX9IR9LBASIARWNFXXESPITSLYAQMLCLVTL

Value:0

Timestamp:2017-03-27 14:26:31.309775962 +0900 JST

No: 3/4

Hash : DWWKESFBEHGIBOZYB9OJBSKISWCOKRKZ9FWJNMZXTNPCPUZKIJGGGQUYEWYEJTOPKMZPY9YICYO999999

Address:9OYDZWKZGMPVSZSDRNLXWDFPSYNESRFLZWNIEMCAHJGSOO9SH9DALDGJZYIJHIXNTVKFEGGCLWTXXVZJO

Value:8

Timestamp:2017-03-27 14:26:35.730652028 +0900 JST

{{ENDCODE}}

As you can see, token was sent by one of the public nodes. And PoW(proof of work) was calculated by PoWSSE function, which uses one of CPU extentions SSE2 . PoW is some kind of hard work of your PC, which is required to get your transaction accepted by the network. So after running giota send command it takes some time to finish. You may notice that all hashes above ends with `999999`. This is the result of PoW, because PoW continue to change a part of transaction (nonce) and calculate hash of transaction until its hash ends according to a predefined threshold.

You can also attach your transaction with a tag.

And bundle info is displayed, which is a group of transactions you sent. The bundle consists with 4 transactions. Each transactions are:

  • Send value 12 to recepient address (no 0/4). 
  • Spent value 12 from sender address (no 1/4,2/4). 
  • Send value 8 to sender new address for changes (no 3/4).

When you spent token, you must sign the transaction with seed you made before and embed the signature into transactions. Why are there 2 transactions for spent? This is because security=2. security=2 (means high security level) needs longer signature, so signature are embedded into 2 transactions.

Under the Hood: Sending Tokens function

{{CODE}}

trs := []giota.Transfer{

   giota.Transfer{

       Address: recipientT,

       Value:   amount,

       Tag:     tag,

   },

}

server := giota.RandomNode()

api := giota.NewAPI(server, nil)

_, pow := giota.GetBestPoW()

bdl, err = giota.PrepareTransfers(api, seedT, trs, nil, "", 2)

err = giota.SendTrytes(api, giota.Depth, []giota.Transaction(bdl), mwm, pow)

{{ENDCODE}}

Information about transfer(send address, value, and tag) are stored into Transfer structure.

Then, get PoW function by GetBestPow() function. There are 3 functions for PoW using SSE2 for x86 architecuture, written in pure C for linux, and written pure Go for others. GetBestPow() returns one best PoW function from these functions.

After that PrepareTransfers() is called. Basically this function prepares one bundle which you saw previous chapter. PrepareTransfers() does:

  1. add output transactions which sends to the sender. 
  2. gather used addresses which has enough balance to send by calling GetUsedAddress() above and balance api, and create spent txs. 
  3. create a transaction to send changes to new address that is made from sender's seed. 
  4. sign the spent tx of step2 by using sender's seed.

Once bundle is prepared, bundle is broadcasted by SendTrytes(). In SendTrytes() function, PoW is done and calls broadcastTransaction() to broadcast transactions.

Conclusion

In this tutorial we've looked over the usage of giota, IOTA Go API Library by taking a dive into giotan, trivial CLI tool for iota.

Appendix

You can calculate decimal from trits [a,b,c], by `a*3^2 + b*3+c`. So trits [-1,1,0] = decimal -1 * 3^2 + 1 * 3 + 0 = -6

Quos ut hic cum labore sint.

Pariatur quis ad iure. Laudantium ut adipisci vitae repudiandae corporis. Qui laboriosam asperiores nostrum molestias eius quo quia. Deserunt nemo repellendus.

Provident nam voluptas quis sed voluptas.

Velit fugiat exercitationem rerum explicabo. Doloremque praesentium accusamus et. Suscipit tempora rerum quod.

Est sint dolorem. Facere molestiae facilis quo voluptatem. Dolor sed delectus officiis architecto fugit explicabo illo. Blanditiis ut iure maiores quia voluptatem facere veniam. Magni incidunt vitae alias.

Qui corporis magni voluptatem aperiam quam harum ratione id deleniti. Nemo molestias voluptatibus qui in. Pariatur unde iure doloribus. Illo deleniti similique.

Comments

More tutorials