Technologia blockchain powstała w kontekście walut cyfrowych i przeprowadzania bezpiecznych transakcji z ich wykorzystaniem. Było to pierwsze i jest to wciąż najbardziej popularne zastosowanie zdecentralizowanego rejestru, jakim jest łańcuch bloków. Dosyć szybko zorientowano się, że system ten ma dużo większy potencjał, a technologia znalazła praktyczny użytek również w wielu innych dziedzinach.
Niestety, najbardziej chwytliwy i medialny aspekt stanowi temat handlu kryptowalutami na giełdach – kto ile milionów zyskał, a kto je stracił. Łatwość spekulowania kryptowalutami jest jedną z głównych wad realizacji projektów z wykorzystaniem tej bezpiecznej i rozproszonej bazy danych, ściśle związanej z walutami cyfrowymi. Wpływa to znacząco na koszty tworzenia inteligentnych kontraktów, przeprowadzenia transakcji, a co za tym idzie – na realny koszt projektu.
W dalszej części artykułu przedstawię sposób realizacji projektu IoT wykorzystujący technologię blockchain, tworzenie smart kontraktów i sposób podłączenia do sieci Ethereum w systemach wbudowanych. Postaram się w sposób praktyczny zaprezentować wcześniej wspomniane zagadnienia związane z wykorzystaniem łańcucha bloków poza światem finansowym.
Zaczynamy.
Oświadczyny jako smart contract
Tak – jestem nerdem. Musiałem wymyślić coś troszkę nietypowego, żeby – jak mi się wydawało – zaimponować swojej dziewczynie („a mogła to być romantyczna kolacja” – pomyślała M.).
Hmmm, a może tak zdefiniować ramy czasowe i inne warunki, jakie muszą zostać spełnione do oświadczyn? I dodatkowo: żeby każdy widział, miał dostęp i mógł zweryfikować decyzję, jaka została podjęta. Wchodzi on – blockchain – ubrany cały na biało.
Mamy już naszą gwiazdę projektu, więc teraz zostaje jeszcze pytanie, w jaki sposób ma się to odbyć. W związku z tym, że pracuję w dziale Embedded i zawsze blisko mi do elektroniki oraz sprzętu, to moim niepisanym obowiązkiem jest stworzenie czegoś fizycznego. Czegoś, co będę mógł wręczyć swojej dziewczynie z jakieś wymyślonej okazji. Tam będzie ukryta niespodzianka, która zaskoczy ją w przyszłości. Całość przedsięwzięcia, ujętą w schemat blokowy, przedstawiam poniżej.
Przygotowałem oświadczynowe pudełko składające się z: drewnianego opakowania, sterowanego elektronicznie zamka, Rpi zapewniającego dostęp do sieci Ethereum oraz obsługującego kamerę (tak, tak, trzeba mieć dowód), przycisków, ledów, GPS-a. Jako zamek wykorzystałem serwomechanizm blokujący mechanicznie możliwość otwarcia skrzynki.
Inteligentny kontrakt – krok po kroku
Najpierw musiałem napisać inteligentny kontrakt. Nie ma jednoznacznej definicji czym jest tzw. smart contract. Imran Bashir w książce „Blockchain. Zaawansowane zastosowanie łańcucha bloków” próbuje zdefiniować to tak:
Inteligenty kontrakt to bezpieczny i niemożliwy do zablokowania program komputerowy, reprezentujący umowę, która jest automatycznie wykonywana i egzekwowana.
Przygotowanie środowiska
Istnieją różne języki, platformy i łańcuchy bloków, gdzie takie inteligentne kontrakty mogą być umieszczone. Ethereum jest jedną z najbardziej popularnych platform do ich rozwijania i obsługiwania. Na potrzeby tego projektu wykorzystałem język Solidity do stworzenia smart kontraktu działającego w EVM (Etherum Virtual Machine) oraz webowe narzędzie Remix jako środowisko programistyczne. Służy ono do:
- kompilowania nowych kontraktów do wykonania w EVM,
- testowania kontraktów,
- deployowania nowych kontraktów do sieci testowej lub do sieci Mainnet,
- wykonywania interakcji na już istniejących w sieci smart kontraktach.
Mainnet jest to aktywny, publiczny, produkcyjny blockchain Ethereum, gdzie wykonują się wszystkie transakcje o realnej wartości. Na giełdach notowana jest kryptowaluta ETH związana właśnie z tą siecią. Dodatkowe sieci testowe służą do sprawdzania działania protokołu oraz testowania smart kontraktów przed umieszczeniem ich w głównej sieci. ETH w sieciach testowych nie mają rzeczywistej wartości i nie są notowane na żadnej giełdzie. Aby uzyskać ETH w sieciach testowych, można wysłać zapytanie z wykorzystaniem specjalnych aplikacji webowych, tzw. „faucet”.
Wszystkie zewnętrzne elementy przekazujące dodatkowe dane do łańcucha bloków nazywamy wyroczniami (oracles). Są to ogniwa, które muszą być zaufane i zweryfikowane, aby nasz kontrakt dostawał tylko prawdziwe dane. Pamiętajmy – raz utworzonego kontraktu nie można już zmienić, ponieważ podstawową cechą blockchaina jest jego stałość. Został zapisany w łańcuchu bloków, zweryfikowany przez sieć (deployowanie kontraktu jest taką samą transakcją, jak transakcja przesłania Etherów z jednego konta na drugie) i to od nas zależy jego stopień zabezpieczenia przed możliwymi atakami.
W naszym przypadku elementem komunikującym się z inteligentnym kontraktem jest nasza skrzynka. W kontrakcie zapisana zostanie lokalizacja, w której nasze pudełko „magicznie” się otworzy.
Historyczne błędy w kontrakcie, czyli drobny offtop
Błąd w kontrakcie projektu The DAO (platformy inwestycyjnej) umożliwił wyprowadzenie równowartości 50 mln dolarów na konto atakującego i przyczyniło się do utworzenia hard forka przez fundację Ethereum. W rezultacie doprowadziło to do zaistnienia dwóch osobnych łańcuchu bloków:
- pierwszy, który zawierał wszystkie transakcje przed atakiem (Ethereum),
- drugi, zawierający aktualne transakcje, uwzględniając również transakcje atakującego (Ethereum Classic).
Duża grupa górników sprzeciwiła się tej sytuacji, uznając, że hard fork jest niezgodny z duchem decentralizacji i założeniem niezmienności operacji. Zdecydowała się na kontynuowanie wydobywania bloków w pierwotnym łańcuchu bloków. Działo się to w roku 2016, a ETC wciąż notowane jest na giełdzie (zgodnie z koncepcją: kod ponad wszystko). Incydent ten opisany jest szeroko w Internecie.
Przygotowanie kontraktu
Dobrze – po tym wprowadzeniu tworzymy kontrakt i jego konstruktor:
constructor(int32 down_lat_x100000, int32 up_lat_x100000, int32 left_lon_x100000,
int32 right_lon_x100000) {
geofence.down_lat_x100000 = down_lat_x100000;
geofence.up_lat_x100000 = up_lat_x100000;
geofence.left_lon_x100000 = left_lon_x100000;
geofence.right_lon_x100000 = right_lon_x100000;
}
Konstruktor ten można wywołać tylko raz w trakcie deploymentu. Podczas umieszczenia kontraktu w sieci ustalamy warunki kontraktu, które są niezmienne w trakcie jego istnienia. Należy o tym pamiętać, ponieważ w przypadku podania błędnych wartości, warunku kontraktu nie można już później zmienić. Parametry przekazywane w konstruktorze to szerokość i długość geograficzna naszego obszaru, gdzie całe to ekscytujące wydarzenie ma nastąpić.
Do weryfikacji lokalizacji posłuży nam funkcja:
function verifyLocation(int32 lat_x100000, int32 lon_x100000) external isOwner {
//require(location_verified == false, "Location has been already verified");
//check left -> right
require((lon_x100000 >= geofence.left_lon_x100000
&& lon_x100000 <= geofence.right_lon_x100000), "Wrong location");
//check down -> up
require((lat_x100000 >= geofence.down_lat_x100000
&& lat_x100000 <= geofence.up_lat_x100000), "Wrong location");
location_verified = true;
emit LocationVerifiedEvent();
}
Tylko i wyłącznie właściciel kontraktu może zweryfikować położenie, a domyślnie właścicielem jest adres, z którego został utworzony kontrakt. W naszym przypadku jest to oświadczynowe pudełko. Służy do tego modyfikator isOwner, który wygląda następująco:
modifier isOwner() {
require(msg.sender == owner, "Caller is not owner");
_;
}
Czyli nikt inny, poza naszym urządzeniem, nie może tego zrobić. Każda transakcja (a wywołanie funkcji jest transakcją z odpowiednimi argumentami w sieci Ethereum) wysyłana do sieci jest podpisana własnym kluczem prywatnym należącym do konkretnego adresu. Dzięki temu mamy zapewnione uwierzytelnienie naszego pudełeczka i jesteśmy pewni, że nikt inny nie oświadczy się M. w innym miejscu.
Koszt operacji
Każda operacja wykonywana w EVM kosztuje prawdziwe Ether (jej ułamkowe części, najmniejszym nominałem jest 1 wei). Do wykonania każdej operacji potrzebna jest określona ilość gazu (Gas), za który trzeba zapłacić podczas wysłania transakcji. Cena gazu waha się i jest zależna od obciążenia sieci, czyli wpływ ma na nią liczba operacji zleconych do realizacji w jednym czasie (globalnie, w całej sieci).
Dzięki słowu kluczowemu „require”, jeżeli warunek nie zostanie spełniony, pieniądze potrzebne do dalszego wykonania transakcji są zwracane i tylko gaz potrzebny do realizacji wszystkich operacji przed wywołaniem „require” zostaje zużyty. W przypadku funkcji payable, wysyłane pieniądze również zostają zwracane.
Wyemitowanie eventu
Wracając do opisywanej funkcji, jeżeli lokalizacja została prawidłowo zweryfikowana, na końcu zostaje wyemitowany event, który zapisywany jest w bloku zawierającym nowe transakcje i dodawany jest do łańcucha. Każdy węzeł komunikujący się z siecią Ethereum może nasłuchiwać i sprawdzać, czy w nowo utworzonym bloku, zawierającym tą transakcję, jest informacja o wystąpieniu danego eventu.
W ten sposób urządzenie dostaje informację, że geofence został naruszony. W naszym przypadku mamy pewne uproszczenia i tylko jedno urządzenie bierze udział w komunikacji. Pamiętajmy jednak, że zależnie od sytuacji inne urządzenie może zweryfikować swoją lokalizację, a inne może dostać alert o naruszeniu. Tak oto inteligentny kontrakt powoduje działanie w świecie IoT.
Ustawienia czasu i akceptacja oświadczyn
Dodajmy jakieś elementy czasowe do tego projektu:
function setProposalExpectedTime(uint32 timestamp) public isOwner {
require(location_verified == true, "Location has not been verified yet");
require(timestamp >= block.timestamp, "Time is in the past");
proposal_expected_time = timestamp;
emit ProposalExpectedTimeEvent(proposal_expected_time);
}
Przygotowana funkcja posłuży nam do ustawienia oczekiwanego czasu oświadczyn.
Zmienna przechowująca ten czas jest zadeklarowana w kontrakcie, a nazywana jest „State Variable”. W przeciwieństwie do zmiennych lokalnych (Local Variable), przechowywana jest ona na stałe w blockchainie, a każdy taki bajt danych kosztuje pewną ilość gazu i wpływa na całkowity koszt kontraktu. O całkowitych kosztach, wahaniach cen gazu itd. napiszę trochę więcej w dalszej części artykułu.
Została nam jeszcze funkcja potrzebna do zaakceptowania oświadczyn. No dobrze, niech będzie – do odrzucenia również…
function sayYes(int32 lat_x100000, int32 lon_x100000, bytes32 her_proof_hash,
bytes32 his_proof_hash) external payable isOwner {
require((block.timestamp >= proposal_expected_time - 600)
&& (block.timestamp <= proposal_expected_time + 600), "Time is up'");
require((saidNo == false) && (saidYes == false), "Proposal has been already finished ");
require(msg.value >= 0.0035 ether, "Deposit is too small");
proposal_her_proof_hash = her_proof_hash;
proposal_his_proof_hash = his_proof_hash;
saidYes = true;
proposal_location_lat_x100000 = lat_x100000;
proposal_location_lon_x100000 = lon_x100000;
emit ProposalEvent(saidYes, saidNo);
}
function sayNo() external isOwner {
require((block.timestamp >= proposal_expected_time - 600)
&& (block.timestamp <= proposal_expected_time + 600), "Time is up'");
require((saidNo == false) && (saidYes == false), "Proposal has been already finished ");
saidNo = true;
emit ProposalEvent(saidYes, saidNo);
}
Jeżeli w określonym czasie nie nastąpią oświadczyny i odpowiedź nie zostanie wysłana, to kontrakt ten można uznać za nieważny, mimo że nadal zostanie w blockchainie (nie ma możliwości jego usunięcia) i już nigdy nie będzie można wysłać informacji z decyzją. Tak samo – raz podjętej decyzji nie można zmienić. Po akceptacji lub odrzuceniu oświadczyn, kontrakt został wykonany.
Dodatkowo, funkcja sayYes została zadeklarowana jako „external payable”. Słowo kluczowe „external” oznacza, że funkcja może być wywołana tylko przez zewnętrzny kontrakt lub konto. Słowo „payable” daje możliwość przesyłania pieniędzy do kontraktu, tworząc pewien depozyt.
Przypadek 2 000 kolumbijskich pesos
Oglądaliście kiedyś film „Kiler-ów 2-óch”? Młodsi czytelnicy mogą tego filmu nie pamiętać, ale tam był taki motyw z dwoma połówkami banknotu 2 000 kolumbijskich pesos. Mając dwie połówki banknotu, niejaki Waldek zamienia je na kontener dolarów.
Dodajmy takie 2 000 kolumbijskich pesos i Waldka do blockchainu, a dokładniej do naszego kontraktu. Naszym kontenerem dolarów jest depozyt, który przesłaliśmy do kontraktu. Jako że pierścionek i sam smart kontrakt w sieci głównej Ethereum trochę kosztują, to myślę, że symboliczne 0,0035 ethera będzie tym razem wystarczające (kto wie, może kiedyś będzie warte tyle, co pół tony dolarów).
Za jedną połówkę 2 000 kolumbijskich pesos posłuży nam podwójny hash (funkcja skrótu) SHA-3 wykorzystujący algorytm Keccak-256 zdjęcia wykonanego przez urządzenie podczas oświadczyn wysyłany na maila do M. Druga połówka to trochę inne zdjęcia z tego momentu wysłane do mnie. Skróty te przesłane są jako argumenty funkcji sayYes i zapisywane na stałe w łańcuchu bloków.
No to jeszcze został nam Waldek:
function getProposalFund(bytes memory her_proof, bytes memory his_proof) external payable {
require(saidYes == true, "Yes is needed");
require(msg.value >= 0.00035 ether, "Deposit is too small");
bytes32 her_hash = keccak256(her_proof);
bytes32 his_hash = keccak256(his_proof);
if((her_hash == proposal_her_proof_hash) && (his_hash == proposal_his_proof_hash))
{
uint256 amount = address(this).balance;
(bool success,) = msg.sender.call{value:amount}("");
require(success, "Withdrawal error");
}
}
Wybrany został wspomniany algorytm Keccak-256 ze względu na to, że jest to operacja kryptograficzna dostępna w EVM. Jeżeli, wywołując „waldkową” funkcję jako argumenty, poda się dwa zdjęcia (a dokładniej pojedynczy hash zdjęcia), to po prawidłowej weryfikacji ich autentyczności nasz „kontener dolarów” wydawany jest wywołującemu funkcję. Czyli musimy się z M. nadal dogadywać, jeżeli chcielibyśmy wypłacić pieniądze z naszego „funduszu oświadczynowego”.
Dlaczego podwójny hash? Ze względu na ilość danych wysyłanych do łańcucha bloków (duży koszt operacji) oraz ze względu na publiczną widoczność transakcji i ich parametrów. Pamiętajmy, że wszystkie dane wysyłane do łańcucha nie są szyfrowane i są dostępne dla każdego (jedna z cech blockchainu: transparentność transakcji). Dlatego przy próbie odzyskania pieniędzy nie są wysyłane nasze prywatne zdjęcia, a tylko ich skrót.
Dodatkowo, aby ograniczyć próby hackowania kontraktu metodą brute force, każde wywołanie funkcji zwiększa depozyt kontraktu o 0,00035 eth, zabierając je z konta atakującego. Cały depozyt jest zwracany w ramach jednego poprawnego wywołana funkcji getProposalFund, ponieważ, tak jak wcześniej zaznaczyłem, dane wysyłane jako argument funkcji są transparentne i każda operacja jest zapisywane w łańcuchu bloków. Znaczy to, że po jednorazowym, poprawnym wywołaniu funkcji każda osoba w łatwy sposób dostaje dostęp do danych potrzebnych do zwrotu depozytu.
Podłączenie oświadczynowej skrzynki do sieci Etherum
Dobrze, to teraz zadania dla naszej skrzynki:
- kontrola lokalizacji oświadczynowej i wysyłanie jej do weryfikacji,
- świecenie i brzęczenie,
- otwarcie zamka w momencie oświadczyn,
- nagrywanie i wykonywanie zdjęć z momentu oświadczyn,
- interakcja ze smart kontraktem w głównej sieci Ethereum.
Aby komunikować się z blockchainem, wykorzystałem pythonową bibliotekę Web3 i serwis Infura, do której podłączyłem się za pomocą interfejsu https (wykorzystując protokół JSON-RPC). Infura jest jednocześnie węzłem w sieci Ethereum i dzięki temu mamy możliwość wykonywania operacji w blockchainie.
w3 = Web3(Web3.HTTPProvider("https://mainnet.infura.io/v3/<API-KEY>"))
Transakcje podpisywane są prywatnym kluczem, lokalnie na urządzeniu. Nie przechowujemy żadnych poufnych danych w serwisie Infura. Biblioteka Web3 daje nam dostęp do funkcji podpisujących, interfejsu do smart kontraktów i danych w blockchainie.
Przejdźmy do podłączenia do kontraktu. Potrzebny nam będzie interfejs z funkcjami i transakcjami dostępnymi w utworzonym przez nas wcześniej kontrakcie. Służy do tego Application Binary Interface (ABI). Plik abi.json można wyeksportować bezpośrednio w środowisku Remix po kompilacji. Inicjalizacja obiektu web3 wygląda następująco:
contract = w3.eth.contract(address=SMART_PROPOSAL_CONTRACT_ADDRESS, abi=ABI)
Teraz tworzymy transakcje. Wspólna funkcja, z której będziemy korzystać do wysłania transakcji, może wyglądać np. tak:
def SendSmartContractTransaction(transaction):
retransmission = 5
while retransmission > 0:
try:
transaction.update({'maxFeePerGas': \
w3.toWei(SMART_PROPOSAL_CONTRACT_TRANSACTION_GAS_FEE_LIMIT, 'gwei')})
transaction.update({'maxPriorityFeePerGas': \
w3.toWei(SMART_PROPOSAL_CONTRACT_TRANSACTION_PRIORITY_FEE_LIMIT, 'gwei')})
transaction.update({'nonce' : w3.eth.get_transaction_count(SMART_PROPOSAL_BOX_ADDRESS)})
signed_transaction = w3.eth.account.sign_transaction(transaction, PRIVATE_KEY)
transaction_hash = w3.eth.send_raw_transaction(signed_transaction.rawTransaction)
print(f"Transaction hash: {encode_hex(transaction_hash)}")
retransmission = 0
except Exception as e:
retransmission = retransmission - 1
Do każdej transakcji określamy limit gazu oraz maksymalna cenę gazu, jaką zgadzamy się zapłacić. Im ta cena jest wyższa, tym szybciej może zostać zweryfikowana przez węzły w sieci. Stawka zależy od aktualnego obciążenia sieci. Aktualną cenę gazu można śledzić. Jeżeli wpiszemy za małą wartość, może się okazać, że przez bardzo długi czas transakcja ta nie zostanie wykonana.
Z drugiej strony musimy nasłuchiwać eventów wyemitowanych w smart kontrakcie:
event_filters.append(contract.events.LocationVerifiedEvent.createFilter(fromBlock='latest'))
event_filters.append(contract.events.ProposalEvent.createFilter(fromBlock='latest'))
event_filters.append(contract.events.ProposalExpectedTimeEvent.createFilter(fromBlock='latest'))
Ważne jest tutaj określenie, z jakiego bloku chcemy otrzymać taką informację. My chcemy być pewni, że transakcja została poprawnie umieszczona w nowym bloku łańcuchu bloków, a blok ten został w pełni zweryfikowany przez sieć. Na ten moment przy podaniu średniej ceny gazu, czas oczekiwania na weryfikację transakcji wynosi średnio ok. 3 minuty. Jest to stosunkowo długi czas i wynika z pewnych ograniczeń sieci.
Aktualnie sieć Ethereum jest w stanie obsłużyć 30 transakcji na sekundę. Platforma jest w trakcie dodawania usprawnień, które składają się na całość projektu Etherum 2.0, po którym szybkość wykonywania transakcji ma się znacząco zwiększyć, a sieć będzie w stanie obsłużyć do 100 000 transakcji na sekundę. Na ten moment zmieniony został sposób ustalania konsensusu za pomocą PoW (proof of work) na PoS (proof of stake). Algorytmy ustalania konsensusu to temat na osobny artykuł, dlatego zainteresowanych tematem odsyłam do jednego z wielu dostępnych materiałów.
Innymi słowy – niepotrzebne są już ogromne moce obliczeniowe i wynikające z tego niepotrzebne straty energii elektrycznej. Pierwszeństwo w tworzeniu nowego bloku mają węzły, które posiadają określoną (i to dużą) kwotę w swoim portfelu. W tej chwili jest to 32 ETH.
Umieszczenie kontraktu w głównej sieci Ethereum
Aby umieścić kontrakt w sieci Ethereum, trzeba wyposażyć konto w niemałą liczbę wei. Po kompilacji ostatecznej wersji kontraktu kod binarny umieszczamy w blockchainie. Jak każde przechowywanie danych w łańcuchu bloków, nie odbywa się to za darmo. Jednakże w trakcie rozwoju kontraktu, za pomocą narzędzia Remix, możemy oczywiście korzystać z dostępnego, testowego środowiska w przeglądarce (Remix VM) lub testowych sieci Ethereum, w których nie wydajmy realnych pieniędzy. Podłączając nasze urządzenie do tej samej sieci testowej, możemy przetestować jego działanie.
Adres opisywanego kontraktu w Ethereum Mainnet to 0xe4e7Ed4f714A8cbDA6Da05910484CFA796f992bb. Wpisując ten adres w eksplorator, możemy przejrzeć wszystkie transakcje wysłane do tego kontraktu wraz z transakcją utworzenia kontraktu.
Jak widzimy, kontrakt został utworzony 8 maja, kiedy to cena gazu wynosiła 20 Gwei, płacąc 0,03226868 Ether, co przekładało się na 81,29 dolarów po kursie z danego dnia. Jest to stosunkowo wysoka cena dla niezbyt skomplikowanego kontraktu. Najlepiej planować tworzenie kontraktu na dni wolne od pracy, gdzie spodziewane jest mniejsze obciążenie sieci.
Warto zauważyć, że jeżeli w trakcie tworzenie kontraktu źle ustawimy maksymalną cenę gazu i trafimy na chwilowo zawyżoną cenę, to możemy przez przypadek wydać ogromne pieniądze. Tak, jak to miało miejsce 1 maja, gdy średnia cena gazu wynosiła ok. 475 Gwei. Koszt utworzenia kontraktu w tym dniu mógłby mnie wynieść prawie 2 000 dolarów, a jeżeli brać pod uwagę maksymalny koszt w tym dniu, to opłata dobiłaby do jakiejś astronomicznej kwoty.
Tak jak kosmiczny wydaje się powód tej zawyżonej ceny, czyli obciążenie sieci przez ogromne zainteresowanie wypuszczeniem NFT zwanych “Otherdeed” do gry Otherside w metaverse przez firmę Bored Ape Yacht Club.
Kod źródłowy kontraktu został wysłany i poprawnie zweryfikowany przez etherscan.io
Dzięki zapisaniu kodu źródłowego w etherscan.io, łatwiejszy jest późniejszy przegląd przeprowadzonych transakcji.
Relacja z oświadczyn
Podsumowując, proces oświadczyn został zaplanowany następująco:
- Jedziemy do miejsca, gdzie mają nastąpić oświadczyny.
- Po wejściu w określony obszar geofence wysyłana jest transakcja z aktualną pozycją.
- Czekamy na potwierdzenie transakcji i wystąpienie eventu.
- Skrzynka losuje czas, kiedy mają nastąpić oświadczyny i wysyła ten czas do kontraktu.
- Teraz następuje najbardziej ekscytujący moment: pudełko się otwiera, kamera zaczyna nagrywać to, co się dzieje…
– Czekamy na decyzję…
– Czekamy.
.
.
– Ej, co tak długo? Myślałem, że pójdzie szybciej. - Jest! Ufff, przycisk został wciśnięty.
- Wykonywane są zdjęcia – jedno zdjęcie wysyłane jest na maila do jednej osoby, drugie do drugiej.
- Tworzymy skróty tych zdjęć, które wysyłane są do smart kontraktu razem z depozytem, wykorzystując transakcję akceptacji oświadczyn.
Na poniższej grafice przedstawiłem diagram sekwencji z całego „przedsięwzięcia”.
Oświadczyny w praktyce
W pewien majowy dzień wręczyłem M. tajemnicze pudełko, informując, że od tej pory ma je mieć zawsze przy sobie – rano i wieczorem, w pracy i na kolacji, bo nie wie, gdzie i w jakim momencie skrzynka się otworzy. Dodałem, że jeżeli przegapi ten moment, to pudełko zamknie się i już nigdy nie uchyli wieka ponownie, a tym samym straci ten prezent na zawsze…
Jako że zbliżały się jej urodziny, M. stwierdziła, że wie, kiedy pudełko się otworzy (ha! Dobrze, że projekt jak zawsze „trochę” mi się opóźnił, przynajmniej jest jakaś wymówka). Mijały dni, pudełko się nie otwierało. M. niecierpliwie zadawała pytania: „No kiedy się otworzy?!”. Spojrzałem na prognozę pogody – w weekend będzie ładnie, możemy w sobotę jechać nad morze. Ehh… W sobotę miało się odbyć rodzinne spotkanie u rodziców… Trudno, pojedziemy w niedzielę.
W sobotę siedzieliśmy przy stole, a M. pudełko miała przy sobie. Nagle mąż siostry (tzw. szwagier) powiedział: „M. słuchaj, a tam (w tym pudełku) to pewnie pierścionek zaręczynowy” i się zaśmiał. Ha ha – moja mama śmiała się jeszcze głośniej. Pewnie – myślę sobie – karuzela śmiechu… Udało się jakoś szybko ten temat uciąć, obracając wszystko w żart.
W niedzielę rano zapakowaliśmy psa i ruszyliśmy na plażę. Zobaczyłem molo, więc chyba byliśmy we właściwym miejscu. Dlaczego więc nie dostałem informacji o naruszeniu geofence? Stresik się spotęgował, coś jak zwykle nie działało… Poszliśmy trochę dalej. Jest alert! Usiedliśmy na piasku i czekałem na otwarcie skrzynki. Po jakimś czasie usłyszeliśmy odgłos brzęczyka. Podpowiadam więc, że chyba jakiś dźwięk z torby się wydobywa. M. pomyślała, że to jakaś ciężarówka jedzie na wstecznym. Faktycznie, dźwięk podobny, ale… ciężarówka na plaży? No dobra – jakieś remonty były widoczne, więc jest to jakieś usprawiedliwienie. M. bierze pudełko do ręki, otwiera i… dalej zobaczcie sami:
Podsumowanie
Wracając do eksploratora etherscan, możemy przejrzeć wszystkie transakcje wysłane do smart kontraktu. I tak, transakcja akceptacji oświadczyn wygląda następująco:
Dane wejściowe transakcji przedstawione są w postaci binarnej. Po zdekodowaniu wyglądają tak:
Jeszcze przedstawienie zapisanych współrzędnych na mapie:
Zapisane skróty zdjęć czekają na wykorzystanie przez funkcję getProposalFund w celu pobrania depozytu (balans kontraktu wynosi 0,0045 ether). Wspomniany wcześniej Waldek z „Kiler-ów 2-óch” niech jeszcze na nas poczeka 😉
A Wy znacie jakieś ciekawe projekty wykorzystujące technologię blockchain? Albo może jakiś nietypowy sposób oświadczyn? Piszcie w komentarzach, jeśli coś macie.
Dla zainteresowanych zostawiam GitHub projektu.
Zostaw komentarz