Create a gist now

Instantly share code, notes, and snippets.

@utamaro /giotan.md
Last active Mar 28, 2017

What would you like to do?
'Hello World' from giota: The IOTA Go API Library

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

asciicast

Introduction

I will introduce to you giota, a go library for IOTA as previous tutorial for Javascript library. I believe Gophers are all CUI(console user interface) lovers, so I'll make an trivial but cool CUI tool called giotan.

In this tutorial we will do three things. That's it. No big deal. Just three stories. But I believe these are almost everything we want to do first.

  • Make an random seed.
  • Get addresses from the seed.
  • Send tokens

After doing these things by giota, we'll look into the code for each items.

You can get giotan from here freely. Feel free to do pull request to this repo.

I am assuming that you have successfully installed Go and giotan by:

  $ go install github.com/iotaledger/giotan

Make a Random Seed

You need to make a seed to start IOTA. When you make an address or send tokens, you need seed. From seed addresses are made and you must sign the sending information (transaction) by seed. So you must remember that ** seed must be private**, never talk to anyone, as passwords or credit card number.

OK, then, let's make a seed just for you. Run:

  $ giotan new
  New seed:  BQELNJMSZZZPRDK9BCTEKLXQPXXPNNTSFYJSXVKESKJOJDJA9CIQTFAGETCFGLIJQYNLJFKQDOOYZMZBK                                                                                        	To display addresses, run                                                                                                                                                                   	giotan addresses                                                                                                                                                              	and input the seed above. 

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

Codes for Making a Seed

Let's look into main.go in giotan.

	seed := giota.NewSeed()
   fmt.Println("New seed: ", seed)

It just calls NewSeed func with no arguments. Actually in NewSeed function,

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)
}

Yes, it just gets random 81 bytes and select one character from TrytesAlphabet for each 81 bytes. TrytesAlphabet contains characters which used in seed string.

Random seed is random, as its names suggest :)

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.

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.

 	$ giotan addresses
	input your seed:
	using IRI server: http://eugene.iota.community:14265
	address info:
	used:

	unused:
		JCKPBIXTASXPQZFLPFTPJEM9WYTVKYQGIAKMHQMZPBMZQUKZJW9YMJHC9GGBHUHZYRRLRPWVNPWVHKUD9

Of course, there is no used address because this seed is just borned right now. Also this command indicates that the address genereated from this seed is JCKPBIXTASXPQZFLPFTPJEM9WYTVKYQGIAKMHQMZPBMZQUKZJW9YMJHC9GGBHUHZYRRLRPWVNPWVHKUD9

If you want to receive iota tokens you can pass the address above to sender.

Codes for Making Addresses from Seed

Let's look into the codes. I extracted only the main part from handleAddresses function in main.go.

	//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 {...}

Before thrwoing 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 public 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.

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)
	}
}

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 determins security level. security is bigger, security become stronger. In most cases in iota security equals 2.

Then, FindTransactions() is called, which throw 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. We'll another seed which has 100 iota.

$ 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

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

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

As you can see, token was sent by server http://eugene.iota.community:14265(one of public nodes). And PoW(prooof 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 with 999999.

If PoW doesn't exist, many spams are spread all over the iota network :(.

You can also attach your message with 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, unless anyone can spent anyone's token.

Why are there 2 transactions for spent? This is because security=2. security=2 (means high security level) needs longer signature, so signature are embeded into 2 transactions.

Codes for Sending Tokens

Main parts of codes for sending tokens are here.

	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)
}

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 PrepareTrnasfers() is called. Basically this function prepares one bundle which you saw previous chapter. PrepareTrnasfers() 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.

There are many functions I didn't explain in giota, so I hope you enjoy IOTA network by playing with giota.

And feel free to do pull request to giota repository.

Appendix

Tryte to trits table

tryte trits decimal
9 [0,0,0] 0
A [0,0,1] 1
B [0,1,-1] 2
C [0,1,0] 3
D [0,1,1] 4
E [1,-1,-1] 5
F [1,-1,0] 6
G [1,-1,1] 7
H [1,0,-1] 8
I [1,0,0] 9
J [1,0,1] 10
K [1,1,-1] 11
L [1,1,0] 12
M [1,1,1] 13
N [-1,-1,-1] -13
O [-1,-1,0] -12
P [-1,-1,1] -11
Q [-1,0,-1] -10
R [-1,0,0] -9
S [-1,0,1] -8
T [-1,1,-1] -7
U [-1,1,0] -6
V [-1,1,1] -5
W [0,-1,-1] -4
X [0,-1,0] -3
Y [0,1,-1] -2
Z [0,0,-1] -1

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment