Solidityä käytetään mo­ni­mut­kais­ten älyk­käi­den so­pi­mus­ten luomiseen, jotka on suun­ni­tel­tu toimimaan Ethereum-loh­ko­ket­jus­sa. Tämä kieli tarjoaa ai­nut­laa­tui­sia stra­te­gioi­ta, jotka erottavat sen muista oh­jel­moin­ti­kie­lis­tä.

Mikä on Solidity?

Solidity on korkean tason oh­jel­moin­ti­kie­li, jolla luodaan älykkäitä so­pi­muk­sia Ethereum-loh­ko­ket­jus­sa. Älykkäät so­pi­muk­set ovat itsestään suo­ri­tet­ta­via so­pi­muk­sia, jotka au­to­ma­ti­soi­vat os­a­puol­ten välisen omai­suu­den vaihdon. Niiden eri­tyis­piir­re on, että älykkään so­pi­muk­sen nou­dat­ta­mi­sen var­mis­ta­mi­sek­si ei tarvita vä­lit­tä­jiä.

Solidity-läh­de­koo­di kään­ne­tään bytecode-koodiksi ja otetaan käyttöön Ethereum-loh­ko­ket­jus­sa älykkäänä so­pi­muk­se­na. Kun tämä on tehty, älykäs sopimus voidaan suorittaa millä tahansa verkon solmulla, ja tila tal­len­ne­taan loh­ko­ket­juun. Esitämme esimerkin yk­sin­ker­tai­ses­ta so­pi­muk­ses­ta, joka mallintaa NFT-au­to­maat­tia:

pragma Solidity 0.8.7;
contract NFTVendingMachine {
    // Declare state variables
    address public owner;
    mapping (address => uint) public nftBalance;
    // Run on deployment
    constructor() {
        owner = msg.sender;
        nftBalance[address(this)] = 100;
    }
    // Allow the owner to restock the NFT balance
    function restock(uint amount) public {
        require(msg.sender == owner, "Only the owner can restock.");
        nftBalance[address(this)] += amount;
    }
    // Allow anyone to purchase NFTs
    function purchase(uint amount) public payable {
        require(msg.value >= amount * 1 ether, "You must pay at least 1 ETH per NFT");
        require(nftBalance[address(this)] >= amount, "Not enough NFTs in stock to complete this purchase");
        nftBalance[address(this)] -= amount;
        nftBalance[msg.sender] += amount;
    }
}
solidity

Mihin so­vel­luk­siin Solidity sopii?

Solidity on suun­ni­tel­tu eri­tyi­ses­ti ha­jau­tet­tu­jen so­vel­lus­ten tai DApp-so­vel­lus­ten luomiseen, jotka toimivat Ethereum Virtual Machine (EVM) -alustalla. Älykkäät so­pi­muk­set so­vel­tu­vat muun muassa di­gi­taa­lis­ten varojen hal­lin­taan, ha­jau­tet­tu­jen pörssien luomiseen ja ää­nes­tys­jär­jes­tel­mien to­teut­ta­mi­seen.

Ha­jau­tet­tu rahoitus (DeFi)

Solidityä käytetään ha­jau­tet­tu­jen pörssien, luotto- ja lai­nausa­lus­to­jen, en­nus­te­mark­ki­noi­den ja kryp­to­va­luut­to­jen kal­tais­ten DeFi-so­vel­lus­ten ke­hit­tä­mi­seen. DeFi on noussut yhdeksi suo­si­tuim­mis­ta blockc­hain-tek­no­lo­gian käyt­tö­ta­pauk­sis­ta. Tässä pro­ses­sis­sa So­li­di­tys­tä on tullut vält­tä­mä­tön työkalu DeFi-so­vel­lus­ten ra­ken­ta­mi­seen Ethereum-ver­kos­tos­sa.

Ei-vaih­det­ta­vat tunnukset

Ei-vaih­det­ta­va tunnus (NFT) on nauttinut suuresta suosiosta 2020-luvulta lähtien. NFT:t ovat ai­nut­laa­tui­sia di­gi­taa­li­sia omai­suuse­riä, jotka on tal­len­net­tu loh­ko­ket­juun. Ne voivat olla di­gi­taa­li­sia tai­de­teok­sia, ur­hei­lu­muis­to­ja tai pelialan esineitä. Solidityä käytetään luomaan älykkäitä so­pi­muk­sia, jotka mah­dol­lis­ta­vat NFT:t.

Toi­mi­tus­ket­jun hallinta

Solidityä voidaan käyttää älyk­käi­den so­pi­mus­ten luomiseen toi­mi­tus­ket­ju­jen seurantaa ja hallintaa varten. So­pi­muk­sia käytetään eri­lais­ten toi­mi­tus­ket­jupro­ses­sien au­to­ma­ti­soin­tiin. Näitä ovat esi­mer­kik­si ta­va­roi­den liik­kei­den seuranta, tuot­tei­den aitouden var­mis­ta­mi­nen ja maksujen käsittely os­a­puol­ten välillä.

Täs­mäy­tys­jär­jes­tel­mät

Solidityä voidaan käyttää luomaan älykkäitä so­pi­muk­sia, jotka to­teut­ta­vat tur­val­li­sia ja lä­pi­nä­ky­viä ää­nes­tys­jär­jes­tel­miä loh­ko­ket­jus­sa. So­pi­muk­sia voidaan käyttää var­mis­ta­maan, että äänet lasketaan oikein ja että ää­nes­tyspro­ses­si on oi­keu­den­mu­kai­nen ja lä­pi­nä­ky­vä.

Mitkä ovat Solidityn edut ja haitat?

Solidity on tehokas kieli älyk­käi­den so­pi­mus­ten ra­ken­ta­mi­seen Ethereum-loh­ko­ket­jus­sa, mutta sillä on omat etunsa ja haittansa, jotka ke­hit­tä­jien tulisi ottaa huomioon älykkäitä so­pi­muk­sia ke­hit­täes­sään. Tur­val­lis­ten älyk­käi­den so­pi­mus­ten laa­ti­mi­nen vaatii kuitenkin tiettyä osaamista ja va­ro­vai­suut­ta.

Esi­merk­ki­nä alla on älykäs sopimus, joka toimii mustana aukona. So­pi­muk­seen lähetetty Ether kuluu pysyvästi, eikä Etheriä ole mah­dol­lis­ta saada takaisin tai maksaa ulos:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.9.0;
// This contract swallows all Ether sent to it
contract Blackhole {
    event Received(address, uint);
    receive() external payable {
            emit Received(msg.sender, msg.value);
    }
}
solidity

Solidityn edut

  • Jous­ta­vuus: Solidity on mo­ni­puo­li­nen kieli. Sitä voidaan käyttää eri­lais­ten älyk­käi­den so­pi­mus­ten ke­hit­tä­mi­seen mo­nen­lai­siin käyt­tö­tar­koi­tuk­siin.
  • Tur­val­li­suus: Solidity on luotu tur­val­li­suut­ta silmällä pitäen, ja se sisältää omi­nai­suuk­sia, kuten pää­syn­val­von­ta, poik­keus­ten käsittely ja vi­ka­me­ka­nis­mit, jotka auttavat ke­hit­tä­jiä luomaan tur­val­li­sia so­pi­muk­sia.
  • Ethereum-yh­teen­so­pi­vuus: Tällä hetkellä Solidity on suosituin kieli älyk­käi­den so­pi­mus­ten luomiseen Ethereum-loh­ko­ket­jus­sa.
  • Erillinen yhteisö: Suuri joukko loh­ko­ket­ju­jen ke­hit­tä­jiä työs­ken­te­lee Solidityn parissa, mikä tarjoaa runsaasti re­surs­se­ja op­pi­mi­seen ja on­gel­man­rat­kai­suun.

Solidityn haitat

  • Op­pi­mis­käy­rä: Ke­hit­tä­jil­le, joille blockc­hain ja älyk­käi­den so­pi­mus­ten ke­hit­tä­mi­nen on uutta, Solidityn op­pi­mis­käy­rä on suh­teel­li­sen jyrkkä.
  • Muut­tu­mat­to­muus: Kun älykäs sopimus on otettu käyttöön loh­ko­ket­jus­sa, sitä ei voi enää muokata. Ke­hit­tä­jien on siis oltava erittäin huo­lel­li­sia kir­joit­taes­saan ja tes­ta­tes­saan.
  • Muo­dol­li­sen var­men­nuk­sen puute: So­li­di­tys­ta puuttuvat si­sään­ra­ken­ne­tut työkalut muo­dol­li­seen koodin tar­kis­tuk­seen. Tämä edel­lyt­tää, että ke­hit­tä­jät käyttävät ulkoisia työkaluja so­pi­mus­ten­sa tark­kuu­den ta­kaa­mi­sek­si.
  • Ra­joi­te­tut työkalut: Solidityn työ­ka­lue­ko­sys­tee­mi on vielä al­ku­vai­hees­sa, mikä voi aiheuttaa ongelmia in­tegroi­duis­sa ke­hi­ty­sym­pä­ris­töis­sä (IDE), tes­taus­ke­hyk­sis­sä ja muissa ke­hi­tys­työ­ka­luis­sa.

Mikä on Solidityn pe­rus­ra­ken­teet?

Solidity on ob­jek­ti­suun­tau­tu­nut oh­jel­moin­ti­kie­li, joka on suun­ni­tel­tu älykkäitä so­pi­muk­sia varten. Se on saanut vai­kut­tei­ta Ja­vaSc­rip­tis­ta, Pyt­ho­nis­ta ja C++:sta. Kielen syntaksi on sa­man­lai­nen kuin Ja­vaSc­rip­tis­sa, mutta siinä on joitakin mie­len­kiin­toi­sia eri­tyis­piir­tei­tä.

Muuttujat Solidity-kielessä

Ensi sil­mäyk­sel­lä Solidityn muut­tu­jien käsittely saattaa vaikuttaa sa­man­lai­sel­ta kuin muissa oh­jel­moin­ti­kie­lis­sä. Kuitenkin mer­kit­tä­vä ero syntyy siitä, että Ethereum Virtual Machine (EVM) toimii suo­ri­tusym­pä­ris­tö­nä. Kaikki EVM:llä suo­ri­te­tut toiminnot, mukaan lukien tietojen tallennus, ai­heut­ta­vat tietyn määrän “kaa­su­kus­tan­nuk­sia”. Näin ollen oh­jel­moin­nin aikana on pun­nit­ta­va toiminnon te­hok­kuut­ta ja pää­tet­tä­vä, miten se voidaan toteuttaa mah­dol­li­sim­man te­hok­kaas­ti.

Ta­val­lis­ten muut­tu­jien lisäksi So­li­di­tys­sä on vakioita, jotka on mää­ri­tel­tä­vä kään­tä­mi­sen aikana. Vakioille tarvitaan vähemmän kaasua tal­len­nus­ti­laa varten:

// Regular variable can be declared without defining
int a;
// Constant needs to be defined at declaration
int constant b = 51;
solidity

Sama pätee immutable muut­tu­jiin, sillä ne vaativat vähemmän muistia ja niitä ei voi muuttaa mää­rit­te­lyn jälkeen. Toisin kuin constant muuttujat, muut­tu­mat­to­mien muut­tu­jien mää­rit­te­ly voidaan tehdä suo­ri­tuk­sen aikana.

Oh­jaus­lauseet Solidity-kielessä

Im­pe­ra­tii­vi­sen oh­jel­moin­ti­kie­le­nä Solidity tukee tuttuja oh­jaus­lausei­ta, kuten haaroja ja sil­mu­koi­ta. Esitämme koodin, jolla valitaan suurempi kahdesta luvusta, a ja b:

int largerNumber = 0;
// If-else statement
if (a > b) {
    largerNumber = a;
} else {
        largerNumber = b;
}
solidity

Soliditynfor vastaa Ja­vaSc­rip­tis­ta tai C++:sta tuttua syntaksia:

// Loop 10 times
for (int i = 0; i < 10; i++) {
    // …
}
solidity

23-silmukka toimii myös ta­val­li­seen tapaan. Yh­dis­täm­me lo­pe­tuseh­don nu­mee­ri­seen laskuriin:

bool continueLoop = true;
int counter = 0;
// Loop at most 10 times
while (continueLoop && counter < 10) {
    // …
    counter++;
}
solidity

Yk­sin­ker­tai­set tyypit Solidity-kielessä

Solidity on staat­ti­ses­ti tyy­pi­tet­ty kieli, joka tukee oh­jel­moin­ti­kie­lis­sä yleisesti käy­tet­ty­jä tyyppejä. Yk­sit­täi­siä arvoja edustavia yk­sin­ker­tai­sia tyyppejä ovat boolenit, numerot ja merk­ki­jo­not.

Solidityn boolenit kuvaavat arvoja true ja false. Ne voidaan linkittää käyt­tä­mäl­lä tun­net­tu­ja boolenien ope­raat­to­rei­ta ja käyttää if lauseissa:

bool paymentReceived = true;
bool itemsStocked = true;
bool continueTransaction = paymentReceived && itemsStocked;
if (continueTransaction) {
    // ...
}
solidity

Solidity tukee laajaa va­li­koi­maa nu­mee­ri­sia tyyppejä. Ko­ko­nais­lu­vut voidaan jakaa mer­kit­tyi­hin (int) ja mer­kit­se­mät­tö­miin (uint) lukuihin, joista jäl­kim­mäi­set voivat olla vain po­si­tii­vi­sia. Lisäksi luvun alue voidaan määrittää 8 bitin välein, välillä int265:

uint8 smallNumber = 120;
int8 negativeNumber = -125;
int8 result = smallNumber + negativeNumber;
assert(result == -5)
solidity

Merk­ki­jo­no­ja käytetään Solidity-kielessä pää­asias­sa ti­la­vies­tien luomiseen. Kieli tukee yk­sit­täi­siä ja kak­sois­laat­to­ja sekä Unicode-merkkejä:

string message = 'Hello World';
string success = unicode"Transfer sent";
Solidity

Solidityn toiminnot

Funktiot ovat Soliditynpe­rus­e­le­ment­ti, kuten useim­mis­sa oh­jel­moin­ti­kie­lis­sä. Funktion mää­ri­tel­mä on sa­man­lai­nen kuin Ja­vaSc­rip­tis­sä, jossa ar­gu­ment­ti­tyy­pit on mää­ri­tel­tä­vä ek­spli­siit­ti­ses­ti. Lisäksi käytetään returns-avain­sa­naa return arvon tyypin il­moit­ta­mi­seen.

// Define a function
function addNumbers(int a, int b) returns (int) {
    return a + b;
}
solidity

Funktion kut­su­mi­nen tapahtuu ta­val­li­seen tapaan:

// Call the function
int result = addNumbers(2, 3);
solidity

Mie­len­kiin­tois­ta on , että ni­met­ty­jen ar­gu­ment­tien tavoin myös Solidityn pa­lau­tusar­vot voidaan nimetä. Tässä ti­lan­tees­sa riittää, että vastaavat muuttujat mää­ri­tel­lään funktion rungossa, eikä ek­spli­siit­tis­tä pa­lau­tus­ta return kautta tarvita:

function divideNumbers(int dividend, int divisor) returns (int quotient) {
    quotient = dividend / divisor;
    // No `return` necessary
}
solidity

Samoin kuin constant tai immutable muut­tu­jas­sa, Solidity -funktiot voidaan merkitä tilaa muut­ta­mat­to­mik­si. Tätä tar­koi­tus­ta varten käytetään avain­sa­no­ja view ja pure. view -funktio ei muuta tilaa, kun taas pure -funktio takaa lisäksi, ettei se lue ti­la­muut­tu­jia.

Älykkäät so­pi­muk­set Solidity-kielellä

Va­kio­tyyp­pien lisäksi Solidity tuntee muutamia älyk­käi­siin so­pi­muk­siin eri­kois­tu­nei­ta tyyppejä. Pe­rus­tyyp­pi on address ja se kar­toit­taa Ethereum-osoitteet. Osoitteet, jotka ovat payable, voivat vas­taa­not­taa siirtoja Etherissä. Tätä tar­koi­tus­ta varten payable tarjoavat balance() ja transfer().

// Get address of this contract
address mine = address(this);
// Get payable external address
address payable other = payable(0x123);
// Transfer if balances fulfill conditions
if (other.balance < 10 && mine.balance >= 100) {
    other.transfer(10);
}
solidity

address pohjalta kehitetty contract on keskeinen kie­li­ra­ken­ne. So­pi­muk­set vastaavat suurin piirtein olio-oh­jel­moin­ti­kiel­ten luokkia. So­pi­muk­set yh­dis­tä­vät ti­la­tie­dot ja toiminnot ja suojaavat ne ul­ko­maa­il­mal­ta. So­pi­muk­set tukevat mo­ni­pe­rin­töä, kuten Python- tai C++-kielissä.

So­pi­muk­set alkavat yleensä pragma rivillä, jossa mää­ri­tel­lään sallittu Solidity-versio, jota seuraa var­si­nai­nen mää­ri­tel­mä:

// Make sure Solidity version matches
pragma Solidity >=0.7.1 <0.9.0;
// Contract definition
contract Purchase {
    // Public state variables
    address seller;
    address buyer;
    
    // View-function
    function getSeller() external view returns (address) {
        return seller;
    }
}
solidity

Älykkäät so­pi­muk­set voivat mää­ri­tel­lä ti­la­tie­dot ja toiminnot. Kuten C++- ja Java-kielistä tiedetään, kussakin ta­pauk­ses­sa voidaan mää­ri­tel­lä yksi kolmesta käyt­tö­oi­keus­ta­sos­ta:

  • public: Muuttujaa voidaan käyttää lukemalla ja kir­joit­ta­mal­la so­pi­muk­sen sisällä. Lisäksi view funktio luodaan au­to­maat­ti­ses­ti ulkoisen lukemisen get­te­rik­si.
  • internal: Muuttuja on suojattu ul­koi­sel­ta pääsyltä. Luku- ja kir­joi­tusoi­keu­det ovat mah­dol­li­sia so­pi­muk­sen sisällä sekä pe­riy­ty­vis­sä so­pi­muk­sis­sa.
  • private: kuten internal, mutta pe­riy­ty­vis­tä so­pi­muk­sis­ta ei ole pääsyä

Toi­min­to­ja voidaan edelleen luon­neh­tia seu­raa­vas­ti: external. external toimii osana so­pi­mus­ra­ja­pin­taa ja sitä käytetään ulkoiseen pääsyyn. receive etherin vas­taa­not­ta­mi­seen on tunnettu esimerkki:

// Define without `function` keyword
receive() external payable {
    // Handle Ether
}
solidity

Mo­di­fioi­jat Solidity-kielessä

Solidity-kielessä on mie­len­kiin­toi­nen kie­li­ra­ken­ne, mo­di­fioi­jat, jotka muis­tut­ta­vat Pythonin ko­ris­tei­ta. Pythonin tavoin Solidityn mo­di­fioi­jia käytetään muut­ta­maan funktion kut­su­mis­ta. Niitä käytetään usein var­mis­ta­maan, että tietty ehto täyttyy ennen funktion suo­rit­ta­mis­ta:

contract Sale {
    uint price;
    address payable owner;
    modifier onlyOwner {
        // Will throw error if called by anyone other than the owner
        require(
            msg.sender == owner,
            "Only owner can call this function."
        );
        // The wrapped function's body is inserted here
        _;
    }
    
    // `onlyOwner` wraps `changePrice`
    function changePrice(uint newPrice) public onlyOwner {
        // We'll only get here if the owner called this function
        price = newPrice;
    }
}
solidity

Tran­sak­tioi­den hallinta Solidity-oh­jel­mis­tol­la

So­li­di­tys­sa on si­sään­ra­ken­net­tu tran­sak­tioi­den hallinta. Sitä voidaan käyttää var­mis­ta­maan, että ether-siirto joko to­teu­te­taan kokonaan tai ei toteuteta lainkaan. Kieli ymmärtää avain­sa­nan revert, joka laukaisee tran­sak­tion “pe­ruut­ta­mi­sen”. Avain­sa­nal­la error voit mää­ri­tel­lä omat vir­he­koo­di­si:

// Custom error definition
error InsufficientPayment(uint256 paid, uint256 required);
// Contract representing a sale
contract Sale {
    uint price;
    // Purchase if enough ether transferred
    function purchase() public payable {
        if (msg.value < price) {
            revert InsufficientPayment(msg.value, price);
        }
        // Complete purchase
    }
}
solidity

Toinen usein esiintyvä malli on require() funktion käyttö. Tätä voidaan käyttää ana­lo­gi­ses­ti revert kanssa:

// Using `require()` function
if (!condition) revert("Error message");
// Equivalent to
require(condition, "Error message");
solidity
Siirry pää­va­lik­koon