├── assets ├── remix1.png ├── remix2.png ├── remix3.png ├── remix4.png ├── remix5.png ├── remix6.png ├── remix7.png └── remix4b.png ├── source ├── SampleContract.sol ├── EquipmentRegister.sol └── RouteCrud.sol ├── SUMMARY.md ├── README.md ├── .gitignore ├── chapter2.md ├── chapter1.md ├── chapter3.md └── chapter4.md /assets/remix1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassianobrexbit/Ethereum-Programming-Basics/HEAD/assets/remix1.png -------------------------------------------------------------------------------- /assets/remix2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassianobrexbit/Ethereum-Programming-Basics/HEAD/assets/remix2.png -------------------------------------------------------------------------------- /assets/remix3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassianobrexbit/Ethereum-Programming-Basics/HEAD/assets/remix3.png -------------------------------------------------------------------------------- /assets/remix4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassianobrexbit/Ethereum-Programming-Basics/HEAD/assets/remix4.png -------------------------------------------------------------------------------- /assets/remix5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassianobrexbit/Ethereum-Programming-Basics/HEAD/assets/remix5.png -------------------------------------------------------------------------------- /assets/remix6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassianobrexbit/Ethereum-Programming-Basics/HEAD/assets/remix6.png -------------------------------------------------------------------------------- /assets/remix7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassianobrexbit/Ethereum-Programming-Basics/HEAD/assets/remix7.png -------------------------------------------------------------------------------- /assets/remix4b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassianobrexbit/Ethereum-Programming-Basics/HEAD/assets/remix4b.png -------------------------------------------------------------------------------- /source/SampleContract.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract SampleContract { 4 | 5 | uint numero; 6 | 7 | function set(uint x) { 8 | numero = x; 9 | } 10 | 11 | function get() constant returns (uint) { 12 | return numero; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Sumário 2 | 3 | * [Introduction](README.md) 4 | * [Capítulo 1: Configurações Iniciais](/chapter1.md) 5 | * [Capítulo 2: Desenvolvimento de contratos](/chapter2.md) 6 | * [Capítulo 3: Compilação e deploy na testnet ](/chapter3.md) 7 | * [Capítulo 4: Trabalhando com structs e arrays](/chapter4.md) 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tutorial básico de programação em Smart Contracts 2 | 3 | Nesse tutorial são apresentados os passos iniciais da configuração de ambiente para o desenvolvimento de aplicações baseadas em Smart Contracts, programação de contratos utilizando a linguagem Solidity e deploy na testnet \(Rinkeby\). 4 | 5 | Você pode navegar pelas páginas desse tutorial por meio do [index](/SUMMARY.md) de conteúdos. 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | 27 | _book/ 28 | book.pdf 29 | book.epub 30 | book.mobi 31 | -------------------------------------------------------------------------------- /chapter2.md: -------------------------------------------------------------------------------- 1 | # Desenvolvimento de contratos 2 | 3 | Nesta primeira parte trataremos da interação inicial com a blockchain, criação de endereços e como obter Ethereum na testnet através de faucets. 4 | 5 | ### Interagindo com a blockchain 6 | 7 | Após configurar o ambiente e sincronizar com a testnet, é necessário criar uma conta \(endereço\) na blockchain. Para isto execute os comandos: 8 | 9 | ``` 10 | //para criar um diretório 11 | mkdir eth-teste 12 | 13 | cd eth-teste 14 | 15 | //iniciar o diretório com o framework truffle 16 | truffle init 17 | ``` 18 | 19 | Em seguida utilize o seguinte comando para acessar o console do Truffle e criar uma nova conta: 20 | 21 | ``` 22 | truffle console 23 | 24 | //criar nova conta 25 | web3.personal.newAccount('minhasenhaforteounao') 26 | ``` 27 | 28 | Obs: Anote este endereço para ser utilizado nas próximas etapas. 29 | 30 | Obs²: Altere a senha \(se quiser, é testnet, não faz diferença\). 31 | 32 | Para publicar contatos e interagir com a blockchain é necessário desbloquear a a conta, para isso execute: 33 | 34 | ``` 35 | web3.personal.unlockAccount('seu_endereco_criado', 'minhasenhaforteounao', 15000) 36 | ``` 37 | 38 | As contas \(obviamente\) são criadas com o saldo zerado. Para obter Ethereum via faucets [acesse este link](https://faucet.rinkeby.io/) e siga as instruções. Você vai precisar do endereço criado no passo anterior. 39 | 40 | Para verificar o saldo do endereço: 41 | 42 | ``` 43 | web3.eth.getBalance('seu_endereco_criado') 44 | ``` 45 | 46 | Para saber mais sobre comandos para interação com a blockchain, [acesse este link](https://github.com/ethereum/wiki/wiki/JavaScript-API). 47 | 48 | ### Criando um contrato simples 49 | 50 | No diretório **source** deste projeto você pode encontrar algumas amostras de contratos \(você pode acessar por [aqui](/source)\). Nesta primeira etapa vamos utilizar o contrato [SampleContract.sol](/source/SampleContract.sol). 51 | 52 | ``` 53 | pragma solidity ^0.4.18; 54 | 55 | contract SampleContract { 56 | 57 | uint numero; 58 | 59 | function set(uint x) public{ 60 | numero = x; 61 | } 62 | 63 | function get() public constant returns (uint) { 64 | return numero; 65 | } 66 | } 67 | ``` 68 | 69 | [Index](/SUMMARY.md) [Capítulo 1](/chapter1.md) [Capítulo 3](/chapter3.md) 70 | 71 | -------------------------------------------------------------------------------- /chapter1.md: -------------------------------------------------------------------------------- 1 | # Configurações iniciais 2 | 3 | Este ambiente foi configurado utilizando o sistema operacional Linux Ubuntu 17.04. As configurações podem ser feitas também em ambiente Windows, mas dado as clássicas restrições de acesso que o Windows possui, é possível que ocorram contratempos nesse processo. 4 | 5 | ### Instalação do Geth no Ubuntu 17.04 via PPA 6 | 7 | O **geth** é a implementação do Ethereum na linguagem **Go** e pode ser instalado de diferentes maneiras. Nesse tutorial será apresentado como instalar o **geth** utilizando o gerenciador de pacotes **apt** do Ubuntu. 8 | 9 | Para instalar as dependências necessárias e adicionar o **geth** no repositório de pacotes do Ubuntu, execute os seguintes comandos: 10 | 11 | ``` 12 | sudo apt-get install software-properties-common 13 | sudo add-apt-repository -y ppa:ethereum/ethereum 14 | ``` 15 | 16 | Em seguida atualize o repositório e instale o **geth**: 17 | 18 | ``` 19 | sudo apt-get update 20 | sudo apt-get install ethereum 21 | ``` 22 | 23 | Para sincronizar o seu nó com a blockchain do Ethereum \(nesse caso é a testnet Rinkeby\) execute o seguinte comando 24 | 25 | ``` 26 | geth --rinkeby --syncmode "fast" --rpc --rpcapi db,eth,net,web3,personal --cache=1024 --rpcport 8545 --rpcaddr 127.0.0.1 --rpccorsdomain "*" 27 | ``` 28 | 29 | Obs: O tempo de sincronização vai depender da sua capacidade de hardware e banda de internet. Na minha instalação eu utilizei uma máquina com um processador Core i7 de 7° geração e uma conexão de 50 mbps. O tempo foi de cerca de 45 minutos. 30 | 31 | Obs²:Para verificar o número do último bloco da rede tesnet Rinkeby, acesse: [https://rinkeby.etherscan.io/](https://rinkeby.etherscan.io/). 32 | 33 | ### Instalação do NodeJS 34 | 35 | Para instalar o NodeJS no Ubuntu é necessário configurar o repositório de pacotes com o seguinte comando: 36 | 37 | ``` 38 | curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - 39 | ``` 40 | 41 | Caso não tenha o **curl **instalado, execute: 42 | 43 | ``` 44 | sudo apt-get install curl -y 45 | ``` 46 | 47 | Em seguida instale o NodeJS : 48 | 49 | ``` 50 | sudo apt-get install -y nodejs 51 | ``` 52 | 53 | Para instalar o **npm **execute: 54 | 55 | ``` 56 | sudo npm install npm -g 57 | ``` 58 | 59 | Para verificar a versão do node e do npm: 60 | 61 | ``` 62 | npm -v 63 | node -v 64 | ``` 65 | 66 | ### Instalação do framework Truffle 67 | 68 | Nesse tutorial utilizaremos o framework Truffle que permite a execução de comandos para interagir com a blockchain do Ethereum. Para isto execute o comando : 69 | 70 | ``` 71 | sudo npm install -g truffle 72 | ``` 73 | 74 | [Index](/SUMMARY.md) [Capítulo 2](/chapter2.md) 75 | 76 | -------------------------------------------------------------------------------- /chapter3.md: -------------------------------------------------------------------------------- 1 | # Compilação e deploy na testnet 2 | 3 | Neste capítulo vamos tratar da compilação do contrato e a publicação na tesnet para ser acessado e aberto à interação. As etapas de compilação são um pouco complexas a princípio e serão abordadas futuramente em um tutorial mais completo. A princípio utilizaremos o Remix, um compilador online de contratos. Para acessar o Remix, acesse este [link.](http://remix.ethereum.org/#optimize=false&version=soljson-v0.4.23+commit.124ca40d.js) 4 | 5 | **Observação importante**: O remix pode ser acessado via HTTP e HTTPS. Eu não sei exatamente \(ainda\) o porquê, mas ocorre um erro quando conecto o remix no meu endpoint \(nó sincronizado com a blockchain através do **geth**, explicado no [primeiro capítulo](//chapter1.md)\) utilizando conexão HTTPS. Por isso aconselho sempre utilizar a conexão via HTTP. 6 | 7 | ![](/assets/remix1.png) 8 | 9 | Para testar, Basta copiar o contrado do diretório **source **e clicar em **Start to compile** ou marcar a opção **Auto compile**, que compila automaticamente o contrato assim que ele é editado. 10 | 11 | Em seguida, para conectar o compilador à rede e publicar o contrato na tesnet, acesse a aba **Run**. Selecione a opção **Web3 Provider**. Ele será redirecionado para o nó \(endpoint\) sincronizado com a rede no seu computador. 12 | 13 | ![](/assets/remix2.png) 14 | 15 | Em seguida clique **OK **e confirme a URL do seu endpoint \(é padrão, não são necessárias alterações nessa parte\). 16 | 17 | ![](/assets/remix4b.png) 18 | 19 | Confirme a URL do seu endpoint: 20 | 21 | ![](/assets/remix3.png) 22 | 23 | Se a conexão do compilador com o seu endpoint ocorrer sem problemas, o ambiente selecionado deverá apontar para a testnet Rinkeby. Automaticamente ele exibirá o endereço que você criou anteriormente e o saldo em Ethereum. 24 | 25 | Observação: é necessário que a conta esteja desbloqueada. 26 | 27 | ![](/assets/remix5.png) 28 | 29 | Por fim, para realizar o deploy do contrato na rede testnet, clique em **Create** e aguarde alguns segundos até o contrato ser publicado na blockchain. Você pode acompanhar o log da operação no espaço abaixo da área onde o contrato é digitado. 30 | 31 | ![](/assets/remix5.png) 32 | 33 | Repare que foram criados dois botões. Esses botões interagem com os métodos que foram criados no contrato \(os nomes são os mesmos\). Para interagir com o contrato, informe um número no campo ao lado do botão **set **e clique para inserir. Em seguida, repare no log apresentando a transação submetida à rede. 34 | 35 | ![](/assets/remix6.png) 36 | 37 | Após a transação ser publicada, clique no botão azul **get **para obter o valor inserido. 38 | 39 | ![](/assets/remix7.png) 40 | 41 | Para verificar as informações da criação do contrato e inserção de valores no buscador de blocos da Etherscan, basta clicar nos links gerados no log. 42 | 43 | Por enquanto isso é tudo pessoal. Em breve completarei esse tutorial com exemplos de operações CRUD em contratos e criação de structs e arrays. Também falarei da interação entre contratos. 44 | 45 | [Index](/SUMMARY.md) [Capítulo 2](/chapter2.md) [Capítulo 4](/chapter4.md) 46 | 47 | -------------------------------------------------------------------------------- /source/EquipmentRegister.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract RegisterCrud { 4 | 5 | struct RegisterStruct { 6 | 7 | bytes32 registerOwnerName; 8 | bytes16 registerOrigin; 9 | bytes16 registerDestination; 10 | uint registerNumber; 11 | uint equipmentsIndex; 12 | uint index; 13 | mapping(uint => EquipmentStruct) equipmentStructs; 14 | } 15 | 16 | struct EquipmentStruct{ 17 | bytes32 equipmentDescription; 18 | bytes16 equipmentUnit; 19 | uint equipmentQuantity; 20 | } 21 | 22 | mapping(bytes16 => RegisterStruct) registerStructs; 23 | bytes16[] public registerIndex; 24 | 25 | event LogNewRegister (bytes16 indexed registerUuid, uint index, bytes32 registerOwnerName, bytes16 registerOrigin, bytes16 registerDestination, uint registerNumber); 26 | event LogUpdateRegister(bytes16 indexed registerUuid, uint index, bytes32 registerOwnerName, bytes16 registerOrigin, bytes16 registerDestination, uint registerNumber); 27 | event LogDeleteRegister(bytes16 indexed registerUuid, uint index); 28 | 29 | function isRegister(bytes16 registerUuid) 30 | public 31 | constant 32 | returns(bool isIndeed) 33 | { 34 | if(registerIndex.length == 0) return false; 35 | return (registerIndex[registerStructs[registerUuid].index] == registerUuid); 36 | } 37 | 38 | function insertRegister( 39 | bytes16 registerUuid, 40 | bytes32 registerOwnerName, 41 | bytes16 registerOrigin, 42 | bytes16 registerDestination, 43 | uint registerNumber) 44 | public 45 | returns(uint index) 46 | { 47 | if(isRegister(registerUuid)) 48 | throw; 49 | registerStructs[registerUuid].registerOwnerName = registerOwnerName; 50 | registerStructs[registerUuid].registerOrigin = registerOrigin; 51 | registerStructs[registerUuid].registerDestination = registerDestination; 52 | registerStructs[registerUuid].registerNumber = registerNumber; 53 | registerStructs[registerUuid].index = registerIndex.push(registerUuid)-1; 54 | 55 | emit LogNewRegister( 56 | registerUuid, 57 | registerStructs[registerUuid].index, 58 | registerOwnerName, 59 | registerOrigin, 60 | registerDestination, 61 | registerNumber 62 | ); 63 | return registerIndex.length-1; 64 | } 65 | 66 | function getRegister(bytes16 registerUuid) 67 | public 68 | constant 69 | returns(uint registerNumber, bytes32 registerOwnerName, bytes16 registerOrigin, bytes16 registerDestination, uint equipmentsIndex, uint index) 70 | { 71 | if(!isRegister(registerUuid)) 72 | throw; 73 | 74 | return( 75 | 76 | registerStructs[registerUuid].registerNumber, 77 | registerStructs[registerUuid].registerOwnerName, 78 | registerStructs[registerUuid].registerOrigin, 79 | registerStructs[registerUuid].registerDestination, 80 | registerStructs[registerUuid].equipmentsIndex, 81 | registerStructs[registerUuid].index 82 | ); 83 | } 84 | 85 | 86 | function insertEquipment(bytes16 rUuid, bytes32 eDesc, uint eQuant, bytes16 eUnit) public{ 87 | 88 | RegisterStruct storage rs = registerStructs[rUuid]; 89 | 90 | rs.equipmentStructs[rs.equipmentsIndex].equipmentDescription = eDesc; 91 | rs.equipmentStructs[rs.equipmentsIndex].equipmentQuantity = eQuant; 92 | rs.equipmentStructs[rs.equipmentsIndex].equipmentUnit = eUnit; 93 | rs.equipmentsIndex++; 94 | 95 | } 96 | 97 | function getEquipments(bytes16 rUuid, uint eIndex) public constant returns(bytes32 eDesc, uint eQuant, bytes16 eUnit) 98 | { 99 | 100 | return( 101 | registerStructs[rUuid].equipmentStructs[eIndex].equipmentDescription, 102 | registerStructs[rUuid].equipmentStructs[eIndex].equipmentQuantity, 103 | registerStructs[rUuid].equipmentStructs[eIndex].equipmentUnit 104 | ); 105 | } 106 | 107 | function getRegisterCount() 108 | public 109 | constant 110 | returns(uint count) 111 | { 112 | return registerIndex.length; 113 | } 114 | 115 | function getRegisterAtIndex(uint index) 116 | public 117 | constant 118 | returns(bytes16 registerUuid) 119 | { 120 | return registerIndex[index]; 121 | } 122 | 123 | function deleteRegister(bytes16 registerUuid) 124 | public 125 | returns(uint index) 126 | { 127 | if(!isRegister(registerUuid)) 128 | throw; 129 | 130 | uint rowToDelete = registerStructs[registerUuid].index; 131 | bytes16 keyToMove = registerIndex[registerIndex.length-1]; 132 | 133 | registerIndex[rowToDelete] = keyToMove; 134 | registerStructs[keyToMove].index = rowToDelete; 135 | registerIndex.length--; 136 | 137 | emit LogDeleteRegister( 138 | registerUuid, 139 | rowToDelete 140 | ); 141 | emit LogUpdateRegister( 142 | keyToMove, 143 | rowToDelete, 144 | registerStructs[keyToMove].registerOwnerName, 145 | registerStructs[keyToMove].registerOrigin, 146 | registerStructs[keyToMove].registerDestination, 147 | registerStructs[keyToMove].registerNumber 148 | ); 149 | return rowToDelete; 150 | } 151 | 152 | function updateRegisterOrigin(bytes16 registerUuid, bytes32 registerOwnerName) 153 | public 154 | returns(bool success) 155 | { 156 | if(!isRegister(registerUuid)) 157 | throw; 158 | 159 | registerStructs[registerUuid].registerOwnerName = registerOwnerName; 160 | 161 | emit LogUpdateRegister( 162 | registerUuid, 163 | registerStructs[registerUuid].index, 164 | registerOwnerName, 165 | registerStructs[registerUuid].registerOrigin, 166 | registerStructs[registerUuid].registerDestination, 167 | registerStructs[registerUuid].registerNumber 168 | ); 169 | 170 | return true; 171 | } 172 | 173 | function updateRegisterNumber(bytes16 registerUuid, uint registerNumber) 174 | public 175 | returns(bool success) 176 | { 177 | if(!isRegister(registerUuid)) 178 | throw; 179 | registerStructs[registerUuid].registerNumber = registerNumber; 180 | 181 | emit LogUpdateRegister( 182 | registerUuid, 183 | registerStructs[registerUuid].index, 184 | registerStructs[registerUuid].registerOwnerName, 185 | registerStructs[registerUuid].registerOrigin, 186 | registerStructs[registerUuid].registerDestination, 187 | registerNumber 188 | ); 189 | 190 | return true; 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /source/RouteCrud.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract RouteCrud{ 4 | 5 | struct RouteStruct{ 6 | 7 | bytes16 routeUuid; 8 | bytes32 routeName; 9 | uint index; 10 | uint routePointsNumber; 11 | mapping (uint => bytes16) routePoints; 12 | 13 | } 14 | 15 | mapping(bytes16 => RouteStruct) routeStructs; 16 | bytes16[] public routeIndex; 17 | 18 | PlaceCrud placeCrudContract; 19 | 20 | function setPlaceCrudContract(address placeContract) public { 21 | 22 | placeCrudContract = PlaceCrud(placeContract); 23 | 24 | } 25 | 26 | function insertRoute(bytes16 routeUuid, bytes32 routeName) public returns (uint rIndex){ 27 | 28 | routeStructs[routeUuid].routeName = routeName; 29 | routeStructs[routeUuid].index = routeIndex.push(routeUuid)-1; 30 | 31 | return routeIndex.length-1; 32 | } 33 | 34 | function insertPoint(uint placeIndex, bytes16 rUuid) public { 35 | 36 | RouteStruct storage rs = routeStructs[rUuid]; 37 | 38 | bytes16 pointUuid = placeCrudContract.getPlaceAtIndex(placeIndex); 39 | 40 | rs.routePoints[rs.routePointsNumber++] = pointUuid; 41 | } 42 | 43 | function getRouteCount() 44 | public 45 | constant 46 | returns(uint count) 47 | { 48 | return routeIndex.length; 49 | } 50 | 51 | function getRoutePoints(bytes16 routeUuid, uint pointIndex) public constant returns(bytes16 pointUuid) 52 | { 53 | return routeStructs[routeUuid].routePoints[pointIndex]; 54 | } 55 | 56 | function getRoutePointsCount(bytes16 routeUuid) 57 | public 58 | constant 59 | returns(uint pointCount) 60 | { 61 | return routeStructs[routeUuid].routePointsNumber; 62 | } 63 | 64 | function getRouteAtIndex(uint index) 65 | public 66 | constant 67 | returns(bytes16 placeUuid) 68 | { 69 | return routeIndex[index]; 70 | } 71 | 72 | function getRoute(bytes16 routeUuid) 73 | public 74 | constant 75 | returns(bytes32 routeName, uint placeNumber) 76 | { 77 | 78 | return( 79 | routeStructs[routeUuid].routeName, 80 | routeStructs[routeUuid].routePointsNumber 81 | ); 82 | } 83 | 84 | } 85 | 86 | contract PlaceCrud { 87 | 88 | struct PlaceStruct { 89 | 90 | bytes32 placeLocation; 91 | bytes16 placeLatd; 92 | bytes16 placeLongtd; 93 | uint placeNumber; 94 | uint index; 95 | 96 | } 97 | 98 | mapping(bytes16 => PlaceStruct) placeStructs; 99 | bytes16[] public placeIndex; 100 | 101 | event LogNewPlace (bytes16 indexed placeUuid, uint index, bytes32 placeLocation, bytes16 placeLatd, bytes16 placeLongtd, uint placeNumber); 102 | event LogUpdatePlace(bytes16 indexed placeUuid, uint index, bytes32 placeLocation, bytes16 placeLatd, bytes16 placeLongtd, uint placeNumber); 103 | event LogDeletePlace(bytes16 indexed placeUuid, uint index); 104 | 105 | function isPlace(bytes16 placeUuid) 106 | public 107 | constant 108 | returns(bool isIndeed) 109 | { 110 | if(placeIndex.length == 0) return false; 111 | return (placeIndex[placeStructs[placeUuid].index] == placeUuid); 112 | } 113 | 114 | function insertPlace(bytes16 placeUuid, bytes32 placeLocation, bytes16 placeLatd, bytes16 placeLongtd, uint placeNumber) public returns(uint index) 115 | { 116 | placeStructs[placeUuid].placeLocation = placeLocation; 117 | placeStructs[placeUuid].placeLatd = placeLatd; 118 | placeStructs[placeUuid].placeLongtd = placeLongtd; 119 | placeStructs[placeUuid].placeNumber = placeNumber; 120 | placeStructs[placeUuid].index = placeIndex.push(placeUuid)-1; 121 | 122 | return placeIndex.length-1; 123 | } 124 | 125 | function getPlace(bytes16 placeUuid) 126 | public 127 | constant 128 | returns(bytes32 placeLocation, bytes16 placeLatd, bytes16 placeLongtd, uint placeNumber, uint index) 129 | { 130 | if(!isPlace(placeUuid)) 131 | throw; 132 | 133 | return( 134 | placeStructs[placeUuid].placeLocation, 135 | placeStructs[placeUuid].placeLatd, 136 | placeStructs[placeUuid].placeLongtd, 137 | placeStructs[placeUuid].placeNumber, 138 | placeStructs[placeUuid].index 139 | ); 140 | } 141 | 142 | function getPlaceCount() 143 | public 144 | constant 145 | returns(uint count) 146 | { 147 | return placeIndex.length; 148 | } 149 | 150 | function getPlaceAtIndex(uint index) 151 | public 152 | constant 153 | returns(bytes16 placeUuid) 154 | { 155 | return placeIndex[index]; 156 | } 157 | 158 | function deletePlace(bytes16 placeUuid) 159 | public 160 | returns(uint index) 161 | { 162 | if(!isPlace(placeUuid)) 163 | throw; 164 | 165 | uint rowToDelete = placeStructs[placeUuid].index; 166 | bytes16 keyToMove = placeIndex[placeIndex.length-1]; 167 | 168 | placeIndex[rowToDelete] = keyToMove; 169 | placeStructs[keyToMove].index = rowToDelete; 170 | placeIndex.length--; 171 | 172 | emit LogDeletePlace( 173 | placeUuid, 174 | rowToDelete 175 | ); 176 | emit LogUpdatePlace( 177 | keyToMove, 178 | rowToDelete, 179 | placeStructs[keyToMove].placeLocation, 180 | placeStructs[keyToMove].placeLatd, 181 | placeStructs[keyToMove].placeLongtd, 182 | placeStructs[keyToMove].placeNumber 183 | ); 184 | return rowToDelete; 185 | } 186 | 187 | function updatePlaceLocation(bytes16 placeUuid, bytes32 placeLocation) 188 | public 189 | returns(bool success) 190 | { 191 | if(!isPlace(placeUuid)) 192 | throw; 193 | 194 | placeStructs[placeUuid].placeLocation = placeLocation; 195 | 196 | emit LogUpdatePlace( 197 | placeUuid, 198 | placeStructs[placeUuid].index, 199 | placeLocation, 200 | placeStructs[placeUuid].placeLatd, 201 | placeStructs[placeUuid].placeLongtd, 202 | placeStructs[placeUuid].placeNumber 203 | ); 204 | 205 | return true; 206 | } 207 | 208 | function updatePlaceNumber(bytes16 placeUuid, uint placeNumber) 209 | public 210 | returns(bool success) 211 | { 212 | if(!isPlace(placeUuid)) 213 | throw; 214 | placeStructs[placeUuid].placeNumber = placeNumber; 215 | 216 | emit LogUpdatePlace( 217 | placeUuid, 218 | placeStructs[placeUuid].index, 219 | placeStructs[placeUuid].placeLocation, 220 | placeStructs[placeUuid].placeLatd, 221 | placeStructs[placeUuid].placeLongtd, 222 | placeNumber 223 | ); 224 | 225 | return true; 226 | } 227 | 228 | } 229 | -------------------------------------------------------------------------------- /chapter4.md: -------------------------------------------------------------------------------- 1 | # Trabalhando com structs e arrays 2 | 3 | Neste capítulo farei uma breve abordagem sobre a utilização de structs e arrays utilizando o Solidity. Para quem já está um pouco familiarizado com Java, C, C++, Javascript ou C\#, provavelmente irá notar alguma semelhança na sintaxe. De certa forma isso facilitou o meu aprendizado. 4 | 5 | Neste exemplo vamos tratar de uma registro de saída de equipamentos, onde cada registro possui um conjunto de itens. 6 | 7 | ### Structs 8 | 9 | A sintaxe de uma struct no Solidity é muito semelhante a utilizada na liguagem C, por exemplo. Uma struct nada mais é do que um tipo de variável personalizada criada pelo usuário. Observe o seguinte trecho de código extraído [deste contrato](/source/EquipmentRegister.sol): 10 | 11 | ``` 12 | //omitted 13 | 14 | struct EquipmentStruct{ 15 | 16 | bytes32 equipmentDescription; 17 | bytes16 equipmentUnit; 18 | uint equipmentQuantity; 19 | } 20 | 21 | //omitted 22 | ``` 23 | 24 | Nesta struct temos três atributos, sendo uma descrição, a unidade de medida e quantidade. Os tipos de váriáveis dentro da struct são **bytes32** e **bytes16** para strings e **uint** para com números inteiros. Esses valores são definidos por meio de métodos setters do contrato. 25 | 26 | ### Arrays 27 | 28 | **Observação importante:** A forma como um contrato é programado influencia diretamente no custo das transações, por isso recomenda-se cautela ao utilizar arrays e loops no solidity. Em um próximo tutorial explicarei como interagir com um contrato utilizando o web3.js, onde os loops poderão ser programados. 29 | 30 | ### Mapeamentos \(mappings\) 31 | 32 | Os mapeamentos são, em geral, tabelas de hash distribuídas e gravam em um espaço de uma variável **bytes16**, que contém um identificador único de 16 caracteres para cada registro. Abaixo segue um exemplo de mapeamento de structs. 33 | 34 | ``` 35 | struct RegisterStruct { 36 | 37 | bytes32 registerOwnerName; 38 | bytes16 registerOrigin; 39 | bytes16 registerDestination; 40 | uint registerNumber; 41 | uint equipmentsIndex; 42 | uint index; 43 | mapping(uint => EquipmentStruct) equipmentStructs; 44 | } 45 | 46 | mapping(bytes16 => RegisterStruct) registerStructs; 47 | bytes16[] public registerIndex; 48 | ``` 49 | 50 | Observe que não há um campo de identificador único na struct _RegisterStruct_. Isto seria redundante pois este identificador está armazenado em um array chamado _registerIndex_, onde cada identificador é vinculado a um índice no momento da inserção do registro na blockchain. 51 | 52 | ``` 53 | function insertRegister( 54 | bytes16 registerUuid, 55 | bytes32 registerOwnerName, 56 | bytes16 registerOrigin, 57 | bytes16 registerDestination, 58 | uint registerNumber) 59 | public 60 | returns(uint index) 61 | { 62 | if(isRegister(registerUuid)) 63 | throw; 64 | registerStructs[registerUuid].registerOwnerName = registerOwnerName; 65 | registerStructs[registerUuid].registerOrigin = registerOrigin; 66 | registerStructs[registerUuid].registerDestination = registerDestination; 67 | registerStructs[registerUuid].registerNumber = registerNumber; 68 | registerStructs[registerUuid].index = registerIndex.push(registerUuid)-1; 69 | 70 | emit LogNewRegister( 71 | registerUuid, 72 | registerStructs[registerUuid].index, 73 | registerOwnerName, 74 | registerOrigin, 75 | registerDestination, 76 | registerNumber 77 | ); 78 | return registerIndex.length-1; 79 | } 80 | ``` 81 | 82 | Repare que dentro da struct RegisterStruct há um mapping _equipmentStructs_ que contém uma lista com os índices dos equipamentos de um registro. 83 | 84 | ``` 85 | mapping(uint => EquipmentStruct) equipmentStructs; 86 | ``` 87 | 88 | O array de equipamentos é preenchido através do método descrito a seguir: 89 | 90 | ``` 91 | function insertEquipment(bytes16 rUuid, bytes32 eDesc, uint eQuant, bytes16 eUnit) public{ 92 | 93 | RegisterStruct storage rs = registerStructs[rUuid]; 94 | 95 | rs.equipmentStructs[rs.equipmentsIndex].equipmentDescription = eDesc; 96 | rs.equipmentStructs[rs.equipmentsIndex].equipmentQuantity = eQuant; 97 | rs.equipmentStructs[rs.equipmentsIndex].equipmentUnit = eUnit; 98 | rs.equipmentsIndex++; 99 | 100 | } 101 | ``` 102 | 103 | Os parâmetros desse método são: 104 | 105 | * identificador de um registro já previamente inserido na blockchain; 106 | * descrição do equipamento; 107 | * quantidade de equipamentos; 108 | * unidade de medida do equipamento. 109 | 110 | ### Getters e setters no Solidity 111 | 112 | As funções acima citadas basicamente são _setters_, métodos responsáveis por inserir informações em um contrato. O exemplo a seguir contém o método que retorna as informações de um _RegisterStruct_. O parâmetro necessário é o identificador único do registro inserido: 113 | 114 | ``` 115 | function getRegister(bytes16 registerUuid) 116 | public 117 | constant 118 | returns(uint registerNumber, bytes32 registerOwnerName, bytes16 registerOrigin, bytes16 registerDestination, uint equipmentsIndex, uint index) 119 | { 120 | 121 | return( 122 | 123 | registerStructs[registerUuid].registerNumber, 124 | registerStructs[registerUuid].registerOwnerName, 125 | registerStructs[registerUuid].registerOrigin, 126 | registerStructs[registerUuid].registerDestination, 127 | registerStructs[registerUuid].equipmentsIndex, 128 | registerStructs[registerUuid].index 129 | ); 130 | } 131 | ``` 132 | 133 | Para obter as informações sobre o array de equipamentos de um registro, é necessário informar o identificador do registro e o índice do equipamento a ser buscado. Uma dica para obter toda a lista de equipamentos é implementar utilizando o web3.js um loop que varre todo o _equipmentsIndex_ utilizando como condição de parada o tamanho do array de equipamentos de um registro. 134 | 135 | ``` 136 | function getEquipments(bytes16 rUuid, uint eIndex) public constant returns(bytes32 eDesc, uint eQuant, bytes16 eUnit) 137 | { 138 | 139 | return( 140 | registerStructs[rUuid].equipmentStructs[eIndex].equipmentDescription, 141 | registerStructs[rUuid].equipmentStructs[eIndex].equipmentQuantity, 142 | registerStructs[rUuid].equipmentStructs[eIndex].equipmentUnit 143 | ); 144 | } 145 | ``` 146 | 147 | Para obter a quantidade de registros informados utilize: 148 | 149 | ``` 150 | function getRegisterCount() 151 | public 152 | constant 153 | returns(uint count) 154 | { 155 | return registerIndex.length; 156 | } 157 | ``` 158 | 159 | Caso você não tenha o identificador único de um registro, utilize o método a seguir para obter essa informação por meio do índice do registro informado. 160 | 161 | ``` 162 | function getRegisterAtIndex(uint index) 163 | public 164 | constant 165 | returns(bytes16 registerUuid) 166 | { 167 | return registerIndex[index]; 168 | } 169 | ``` 170 | 171 | **Observação importante:** Quando o tipo de variável para strings utilizado é _bytes16_** **ou qualquer outro do tipo bytes, o retorno será no formado hexadecimal. Para converter ao formato UTF8 utilize no web3.js a função _**web3.toUtf8\(hexString\)**_, onde _hexString_ é o valor retornado em hexadecimal. 172 | 173 | Por enquanto é tudo pessoal, no próximo capítulo mostrarei como funciona a interação entre contatos e a utilização de array de contratos. 174 | 175 | [Index](/SUMMARY.md) [Capítulo 3](/chapter3.md) 176 | 177 | --------------------------------------------------------------------------------