├── .dockerignore ├── .formatter.exs ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .tool-versions ├── .vscode └── settings.json ├── LICENSE ├── Makefile ├── NOTICE ├── README.md ├── TODO.md ├── c_src ├── Makefile ├── main.cpp ├── merkletree.cpp ├── merkletree.hpp ├── nif.cpp ├── preallocator.hpp ├── sha.cpp ├── sha.h ├── sha256_asm.c ├── sha256_asm.h ├── sha256_std.c └── sha256_std.h ├── client ├── config ├── config.exs └── diode.sample.exs ├── data └── genesis.bin ├── deployment ├── .gitignore ├── .inputrc ├── diode.service ├── fabfile.py └── update_all.sh ├── dev ├── docs ├── maymounkov-kademlia-lncs.pdf └── portopen.wsd ├── evm ├── LICENSE ├── Makefile ├── aleth │ └── version.h ├── ethash │ ├── ethash.h │ ├── ethash.hpp │ ├── hash_types.h │ ├── hash_types.hpp │ ├── keccak.c │ ├── keccak.h │ ├── keccak.hpp │ ├── keccakf1600.c │ ├── keccakf800.c │ ├── progpow.hpp │ └── version.h ├── evmc │ ├── evmc.h │ ├── evmc.hpp │ ├── helpers.h │ ├── instruction_metrics.c │ ├── instructions.h │ ├── loader.h │ └── utils.h ├── host.cpp ├── host.hpp ├── intx │ ├── builtins.h │ ├── div.cpp │ ├── div.hpp │ ├── int128.hpp │ └── intx.hpp ├── libaleth-interpreter │ ├── VM.cpp │ ├── VM.h │ ├── VMCalls.cpp │ ├── VMConfig.h │ ├── VMOpt.cpp │ └── interpreter.h ├── libdevcore │ ├── Address.h │ ├── Assertions.h │ ├── Base64.h │ ├── Common.h │ ├── CommonData.h │ ├── CommonIO.h │ ├── CommonJS.h │ ├── DBFactory.h │ ├── Exceptions.h │ ├── FileSystem.h │ ├── FixedHash.h │ ├── Guards.h │ ├── JsonUtils.h │ ├── LevelDB.h │ ├── Log.h │ ├── LoggingProgramOptions.h │ ├── LruCache.h │ ├── MemoryDB.h │ ├── OverlayDB.h │ ├── RLP.h │ ├── RangeMask.h │ ├── RocksDB.h │ ├── SHA3.h │ ├── StateCacheDB.h │ ├── Terminal.h │ ├── TransientDirectory.h │ ├── TrieCommon.h │ ├── TrieDB.h │ ├── TrieHash.h │ ├── UndefMacros.h │ ├── Worker.h │ ├── concurrent_queue.h │ ├── db.h │ ├── dbfwd.h │ └── vector_ref.h ├── libethcore │ ├── BasicAuthority.h │ ├── BlockHeader.h │ ├── ChainOperationParams.h │ ├── Common.h │ ├── CommonJS.h │ ├── EVMSchedule.h │ ├── Exceptions.h │ ├── KeyManager.h │ ├── LogEntry.h │ ├── Precompiled.h │ ├── SealEngine.h │ └── TransactionBase.h ├── libevm │ ├── EVMC.h │ ├── ExtVMFace.h │ ├── Instruction.h │ ├── LegacyVM.h │ ├── LegacyVMConfig.h │ ├── VMFace.h │ └── VMFactory.h ├── mac │ └── byteswap.h ├── main.cpp └── support │ └── attributes.h ├── githooks ├── post-receive └── pre-commit ├── guides ├── emergency_measures.md └── running_your_miner.md ├── lib ├── abi.ex ├── base16.ex ├── bench.ex ├── bert.ex ├── block_process.ex ├── bridge_monitor.ex ├── certs.ex ├── chain.ex ├── chain │ ├── account.ex │ ├── block.ex │ ├── block_cache.ex │ ├── genesis_factory.ex │ ├── header.ex │ ├── pool.ex │ ├── state.ex │ ├── transaction.ex │ ├── transaction_receipt.ex │ └── worker.ex ├── chaindefinition.ex ├── chaindefinition │ ├── devnet.ex │ ├── galileo.ex │ ├── mainnet.ex │ ├── pioneer.ex │ ├── stagenet.ex │ ├── ulysses.ex │ └── voyager.ex ├── chains │ ├── anvil.ex │ └── chains.ex ├── cmerkletree.ex ├── contract │ ├── bns.ex │ ├── fleet.ex │ └── registry.ex ├── cron.ex ├── diode.ex ├── eip712.ex ├── etslru.ex ├── evm.ex ├── hash.ex ├── job │ └── export_accounts.ex ├── json.ex ├── kademlia.ex ├── kademliasearch.ex ├── kbuckets.ex ├── lru.ex ├── mix │ └── tasks │ │ ├── export.ex │ │ ├── git_version.ex │ │ └── import.ex ├── model │ ├── chainsql.ex │ ├── credsql.ex │ ├── ets.ex │ ├── file.ex │ ├── kademliasql.ex │ ├── sql.ex │ └── syncsql.ex ├── network │ ├── edge_v2.ex │ ├── handler.ex │ ├── peer_handler.ex │ ├── rpc.ex │ ├── rpc_http.ex │ ├── rpc_ws.ex │ └── server.ex ├── node_agent.ex ├── object │ ├── object.ex │ └── server.ex ├── precompiles.ex ├── processlru.ex ├── pubsub.ex ├── queue.ex ├── random.ex ├── remote_chain │ ├── cache.ex │ ├── call_permit.ex │ ├── http.ex │ ├── node_proxy.ex │ ├── nonce_provider.ex │ ├── remote_chain.ex │ ├── rpc.ex │ ├── rpc_cache.ex │ ├── sup.ex │ ├── tx_relay.ex │ ├── util.ex │ └── ws_conn.ex ├── rlp.ex ├── rlpx.ex ├── secp256k1.ex ├── shell.ex ├── stages.ex ├── stats.ex ├── wallet.ex └── words.ex ├── mix.exs ├── mix.lock ├── notes.exs ├── remsh ├── run ├── scripts ├── Dockerfile ├── clean.sh ├── cmerkle_bench.exs ├── cmerkle_bench2.exs ├── cmerkle_bench3.exs ├── docker ├── entrypoint ├── evmbench.exs ├── evmbench.sh ├── find_fragmentation.exs ├── inspect_mem.exs ├── merkle_bench.exs ├── merkle_test.exs ├── recompess.exs ├── run_beta ├── run_offline ├── run_perf ├── run_test └── zip_bench.exs ├── staging └── test ├── abi_test.exs ├── blockquick_test.exs ├── broadcast_test.exs ├── chain_test.exs ├── cmerkletree_test.exs ├── curl ├── do ├── getByNumber.json └── getObject.json ├── curl_rpc.sh ├── data ├── stress.ex └── stress.json ├── etslru_test.exs ├── evm_test.exs ├── kademlia_test.exs ├── kbuckets_test.exs ├── lru_test.exs ├── object_test.exs ├── peer_test.exs ├── processlru_test.exs ├── pubsub_test.exs ├── rlp_test.exs ├── rpc_test.exs ├── secp256k1_test.exs ├── test_helper.exs ├── transaction_test.exs └── wallet_test.exs /.dockerignore: -------------------------------------------------------------------------------- 1 | # App artifacts 2 | /_build 3 | /db 4 | /deps 5 | /*.ez 6 | /doc 7 | 8 | *.pem 9 | **.o 10 | *.pyc 11 | *.fprof 12 | *.cprof 13 | *.sq3 14 | fprof.trace 15 | blocks.dat 16 | perf.data 17 | /test/curl/out.json 18 | 19 | # Generated on crash by the VM 20 | erl_crash.dump 21 | 22 | /states 23 | /binlogs 24 | /.vscode 25 | /config/*.secret.exs 26 | .elixir_ls/ 27 | clones 28 | config/diode.exs 29 | .history/ 30 | evm/evm 31 | core 32 | ttb_last_config 33 | data_dev/ 34 | data_prod/ 35 | data_test/ 36 | data_prod_old/ 37 | supervise/ 38 | screenlog.* 39 | 40 | # Docker 41 | Dockerfile 42 | docker 43 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | [ 5 | inputs: ["mix.exs", "evmbench.exs", "{config,lib,test,client}/**/*.{ex,exs}"] 6 | ] 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: "CI" 2 | on: ["push", "pull_request"] 3 | 4 | jobs: 5 | test_and_build: 6 | name: "Build and lint" 7 | runs-on: "ubuntu-latest" 8 | steps: 9 | - name: Install deps 10 | run: sudo apt-get install -y libboost-dev libboost-system-dev 11 | 12 | - name: Setup elixir 13 | uses: erlef/setup-elixir@v1 14 | with: 15 | otp-version: 26.2.5.3 16 | elixir-version: 1.15.7 17 | 18 | - uses: actions/checkout@v1 19 | - run: | 20 | mix local.hex --force 21 | mix local.rebar --force 22 | mix deps.get 23 | make -C deps/libsecp256k1/ 24 | mix lint 25 | # mix test 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # App artifacts 2 | /_build 3 | /db 4 | /deps 5 | /*.ez 6 | /doc 7 | /c_src/main 8 | /c_src/test 9 | /priv/merkletree_nif.so 10 | 11 | *.pem 12 | *.o 13 | *.pyc 14 | *.fprof 15 | *.cprof 16 | *.sq3 17 | *.log 18 | *.etf 19 | *.csv 20 | *.bck 21 | .erlang.cookie 22 | fprof.trace 23 | blocks.dat 24 | perf.data 25 | /test/curl/out.json 26 | *.asc 27 | # Generated on crash by the VM 28 | erl_crash.dump 29 | 30 | /states 31 | /binlogs 32 | .vscode 33 | /config/*.secret.exs 34 | .elixir_ls/ 35 | clones 36 | config/diode.exs 37 | .history/ 38 | evm/evm 39 | core 40 | ttb_last_config 41 | data_dev/ 42 | data_prod/ 43 | data_test/ 44 | data_prod_old/ 45 | supervise/ 46 | .env 47 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | python 3.9.18 2 | erlang 26.2.5.6 3 | elixir 1.15.7 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "spellright.language": [ 3 | "English (American)" 4 | ], 5 | "spellright.documentTypes": [ 6 | "markdown", 7 | "latex", 8 | "plaintext" 9 | ], 10 | "files.associations": { 11 | "secp256k1_recovery.h": "c", 12 | "tuple": "cpp", 13 | "functional": "cpp", 14 | "istream": "cpp", 15 | "memory": "cpp", 16 | "cctype": "cpp", 17 | "cmath": "cpp", 18 | "cstdarg": "cpp", 19 | "cstddef": "cpp", 20 | "cstdio": "cpp", 21 | "cstdlib": "cpp", 22 | "cstring": "cpp", 23 | "ctime": "cpp", 24 | "cwchar": "cpp", 25 | "array": "cpp", 26 | "atomic": "cpp", 27 | "*.tcc": "cpp", 28 | "chrono": "cpp", 29 | "condition_variable": "cpp", 30 | "cstdint": "cpp", 31 | "list": "cpp", 32 | "unordered_map": "cpp", 33 | "unordered_set": "cpp", 34 | "vector": "cpp", 35 | "exception": "cpp", 36 | "initializer_list": "cpp", 37 | "iomanip": "cpp", 38 | "iosfwd": "cpp", 39 | "iostream": "cpp", 40 | "limits": "cpp", 41 | "mutex": "cpp", 42 | "new": "cpp", 43 | "ostream": "cpp", 44 | "ratio": "cpp", 45 | "sstream": "cpp", 46 | "stdexcept": "cpp", 47 | "type_traits": "cpp", 48 | "utility": "cpp", 49 | "typeinfo": "cpp", 50 | "algorithm": "cpp", 51 | "iterator": "cpp", 52 | "map": "cpp", 53 | "memory_resource": "cpp", 54 | "random": "cpp", 55 | "set": "cpp", 56 | "string": "cpp", 57 | "strstream": "cpp", 58 | "bitset": "cpp", 59 | "clocale": "cpp", 60 | "complex": "cpp", 61 | "csignal": "cpp", 62 | "cwctype": "cpp", 63 | "deque": "cpp", 64 | "fstream": "cpp", 65 | "numeric": "cpp", 66 | "optional": "cpp", 67 | "streambuf": "cpp", 68 | "string_view": "cpp", 69 | "system_error": "cpp", 70 | "thread": "cpp", 71 | "typeindex": "cpp", 72 | "hash_map": "cpp", 73 | "cinttypes": "cpp", 74 | "cfenv": "cpp" 75 | } 76 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | SHELL := /bin/bash 5 | TESTS := $(wildcard test/*_test.exs) 6 | TESTDATA := test/pems/device1_certificate.pem test/pems/device2_certificate.pem 7 | 8 | .PHONY: all 9 | all: evm/evm priv/merkletree_nif.so 10 | 11 | priv/merkletree_nif.so: $(wildcard c_src/*.cpp c_src/*.hpp) 12 | $(MAKE) -C c_src nif 13 | 14 | evm/evm: $(wildcard evm/*.cpp evm/*.hpp evm/*/*.cpp evm/*/*.hpp) 15 | $(MAKE) -C evm 16 | 17 | .PHONY: clean 18 | clean: 19 | $(MAKE) -C evm clean 20 | 21 | .PHONY: test 22 | test: $(TESTDATA) 23 | -rm -rf data_test/ clones/ 24 | $(MAKE) --no-print-directory $(TESTS) 25 | 26 | secp256k1_params.pem: 27 | openssl ecparam -name secp256k1 -out secp256k1_params.pem 28 | 29 | test/pems: 30 | mkdir -p test/pems 31 | 32 | %.pem: secp256k1_params.pem test/pems 33 | openssl req -newkey ec:./secp256k1_params.pem -nodes -keyout $@ -x509 -days 365 -out $@ -subj "/CN=device" 34 | 35 | .PHONY: $(TESTS) 36 | $(TESTS): 37 | # bug in mix, should be auto-compiled 38 | MIX_ENV=test mix deps.compile profiler 39 | mix test --max-failures 1 $@ 40 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Diode(SM) Server 2 | Copyright 2021-2024 Diode 3 | 4 | This product includes software developed by IBTC (http://www.ibtc.tech/). -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Todo 2 | 3 | * Improve performance 4 | ``` 5 | num = 993210 6 | block = Chain.block(num) 7 | p = Chain.Block.parent(block) 8 | Chain.Block.validate(block, p) 9 | pid = spawn(fn -> for _ <- 1..100_000, do: Chain.Block.validate(block, p) end) 10 | ``` 11 | 12 | * Remove .receipts from block only keep in cache 13 | * Add Merkle Inclusion Proof 14 | * Generate optimal (shortest) proof path 15 | -------------------------------------------------------------------------------- /c_src/Makefile: -------------------------------------------------------------------------------- 1 | # OPTS=merkletree.cpp sha.cpp -O3 -march=native -msha -Wall -Wextra -pedantic -lstdc++ -fsanitize=address -ggdb3 2 | OPTS=merkletree.cpp sha.cpp -g -O3 -march=native -msha -Wall -Wextra -pedantic -lstdc++ 3 | ERL_INCLUDE_PATH = $(shell erl -eval 'io:format("~s", [lists:concat([code:root_dir(), "/erts-", erlang:system_info(version), "/include"])])' -s init stop -noshell) 4 | CFLAGS+=-I. -O2 -g -Wall -Wno-unknown-pragmas 5 | CXXFLAGS+=-std=c++17 $(CFLAGS) 6 | 7 | UNAME_S := $(shell uname -s) 8 | 9 | ifeq ($(UNAME_S),Darwin) 10 | OPTS+=-undefined dynamic_lookup 11 | endif 12 | 13 | .PHONY: test 14 | test: 15 | $(CXX) -o test main.cpp ${OPTS} 16 | ./test 17 | 18 | .PHONY: bench 19 | bench: 20 | $(CXX) -o test main.cpp ${OPTS} 21 | ./test bench 22 | 23 | .PHONY: nif 24 | nif: ../priv/merkletree_nif.so 25 | 26 | ../priv/merkletree_nif.so: nif.cpp merkletree.hpp merkletree.cpp sha.cpp Makefile 27 | echo ${ERL_INCLUDE_PATH} 28 | mkdir -p ../priv 29 | $(CXX) $(CXXFLAGS) -I${ERL_INCLUDE_PATH} -o ../priv/merkletree_nif.so -shared -fPIC nif.cpp ${OPTS} 30 | -------------------------------------------------------------------------------- /c_src/preallocator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PREALLOCATOR_HPP 2 | #define PREALLOCATOR_HPP 3 | 4 | #include 5 | #include 6 | 7 | class Tree; 8 | 9 | template 10 | class PreAllocator { 11 | static const size_t STRIPE_SIZE = 8; 12 | Tree &m_tree; 13 | std::list m_stripes; 14 | size_t m_item_count; 15 | std::list m_backbuffer; 16 | 17 | public: 18 | PreAllocator(Tree &tree) : m_tree(tree), m_item_count(0) { } 19 | ~PreAllocator() { 20 | for (uint8_t *stripe : m_stripes) { 21 | auto len = std::min(STRIPE_SIZE, m_item_count); 22 | for (size_t i = 0; i < len; i++) { 23 | T* item = reinterpret_cast(&stripe[(i % STRIPE_SIZE) * sizeof(T)]); 24 | item->~T(); 25 | } 26 | m_item_count -= len; 27 | free(stripe); 28 | } 29 | } 30 | 31 | T* new_item() { 32 | if (!m_backbuffer.empty()) { 33 | T* item = m_backbuffer.front(); 34 | m_backbuffer.pop_front(); 35 | return item; 36 | } 37 | 38 | if (m_item_count + 1 > m_stripes.size() * STRIPE_SIZE) { 39 | m_stripes.push_back((uint8_t*)malloc(STRIPE_SIZE * sizeof(T))); 40 | } 41 | 42 | T* item = reinterpret_cast(&m_stripes.back()[(m_item_count % STRIPE_SIZE) * sizeof(T)]); 43 | m_item_count++; 44 | return new (item) T(m_tree); 45 | } 46 | 47 | void destroy_item(T* item) { 48 | item->~T(); 49 | m_backbuffer.push_back(new (item) T(m_tree)); 50 | } 51 | }; 52 | 53 | #endif -------------------------------------------------------------------------------- /c_src/sha.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | extern "C" { 3 | #ifdef __SHA__ 4 | #include "sha256_asm.c" 5 | 6 | void sha(const uint8_t *message, uint64_t len, uint8_t *digest) 7 | { 8 | SHA256_CTX ctx; 9 | sha256_init(&ctx); 10 | sha256_update(&ctx, message, len); 11 | sha256_final(&ctx, digest); 12 | } 13 | 14 | #else 15 | #include "sha256_std.c" 16 | 17 | void sha(const uint8_t *message, uint64_t len, uint8_t *digest) 18 | { 19 | sha256_ctx ctx; 20 | sha256_init(&ctx); 21 | sha256_update(&ctx, message, len); 22 | sha256_final(&ctx, digest); 23 | } 24 | 25 | #endif 26 | } 27 | 28 | -------------------------------------------------------------------------------- /c_src/sha.h: -------------------------------------------------------------------------------- 1 | #ifdef __SHA__ 2 | #include "sha256_asm.h" 3 | #else 4 | #include "sha256_std.h" 5 | #endif 6 | 7 | void sha(const uint8_t *message, uint64_t len, uint8_t *digest); 8 | -------------------------------------------------------------------------------- /c_src/sha256_asm.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha256.h 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Defines the API for the corresponding SHA1 implementation. 7 | *********************************************************************/ 8 | 9 | #ifndef SHA256_H 10 | #define SHA256_H 11 | 12 | /*************************** HEADER FILES ***************************/ 13 | #include 14 | #include 15 | 16 | /****************************** MACROS ******************************/ 17 | #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest 18 | 19 | /**************************** DATA TYPES ****************************/ 20 | typedef uint8_t BYTE; // 8-bit byte 21 | typedef uint32_t WORD; // 32-bit word, change to "long" for 16-bit machines 22 | 23 | typedef struct { 24 | BYTE data[64]; 25 | WORD datalen; 26 | unsigned long long bitlen; 27 | WORD state[8]; 28 | } SHA256_CTX; 29 | 30 | /*********************** FUNCTION DECLARATIONS **********************/ 31 | void sha256_init(SHA256_CTX *ctx); 32 | void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); 33 | void sha256_final(SHA256_CTX *ctx, BYTE hash[]); 34 | 35 | #endif // SHA256_H 36 | -------------------------------------------------------------------------------- /client: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | exec mix run --no-start client_lib/client.exs 6 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | import Config 5 | 6 | # Configures Elixir's Logger 7 | config :logger, 8 | level: :info, 9 | backends: [:console], 10 | truncate: 8000, 11 | format: "$time $metadata[$level] $message" 12 | 13 | config :logger, :console, 14 | level: :info, 15 | format: "$time $metadata[$level] $message\n" 16 | 17 | case Mix.env() do 18 | :benchmark -> 19 | System.put_env("SEED", "none") 20 | System.put_env("WORKER_MODE", "disabled") 21 | 22 | :test -> 23 | System.put_env("SEED", "none") 24 | System.put_env("WORKER_MODE", "poll") 25 | 26 | if System.get_env("RPC_PORT") == nil do 27 | System.put_env("RPC_PORT", "18001") 28 | System.put_env("EDGE2_PORT", "18003") 29 | System.put_env("PEER_PORT", "18004") 30 | end 31 | 32 | :dev -> 33 | System.put_env("WORKER_MODE", "poll") 34 | System.put_env("SEED", "none") 35 | 36 | _env -> 37 | :ok 38 | end 39 | 40 | if Mix.env() != :test and File.exists?("config/diode.exs") do 41 | import_config "diode.exs" 42 | end 43 | -------------------------------------------------------------------------------- /config/diode.sample.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | use Mix.Config 5 | -------------------------------------------------------------------------------- /data/genesis.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diodechain/diode_server/0ccea700b0f3b073468e56ac025281c175f6ea91/data/genesis.bin -------------------------------------------------------------------------------- /deployment/.gitignore: -------------------------------------------------------------------------------- 1 | fabfile.pyc 2 | -------------------------------------------------------------------------------- /deployment/.inputrc: -------------------------------------------------------------------------------- 1 | "\e[A": history-search-backward 2 | "\e[B": history-search-forward 3 | -------------------------------------------------------------------------------- /deployment/diode.service: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | [Unit] 5 | Description=Diode service 6 | 7 | [Service] 8 | Restart=always 9 | RestartSec=3 10 | ExecStart=/usr/bin/screen -L -DmS diode supervise /opt/diode 11 | WorkingDirectory=/opt/diode 12 | Environment=HOME=/opt/diode 13 | Environment=PRIVATE=0 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /deployment/update_all.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | for server in eu1 eu2 us1 us2 as1 as2; do 3 | echo "Updating $server" 4 | git push $server master --tags 5 | echo "Waiting 60 seconds..." 6 | sleep 300 7 | done 8 | -------------------------------------------------------------------------------- /dev: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | rm -rf data_dev/ 6 | export MIX_ENV=dev 7 | mix deps.get 8 | exec iex -S mix run 9 | -------------------------------------------------------------------------------- /docs/maymounkov-kademlia-lncs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diodechain/diode_server/0ccea700b0f3b073468e56ac025281c175f6ea91/docs/maymounkov-kademlia-lncs.pdf -------------------------------------------------------------------------------- /docs/portopen.wsd: -------------------------------------------------------------------------------- 1 | @startuml 2 | User -> Network: ["portopen", device_id, port] 3 | Network -> Device: ["portopen", port, ref, user_id] 4 | 5 | Network <-- Device : ["response", "portopen", ref, "ok"] 6 | User <-- Network: ["response", "portopen", "ok", ref] 7 | 8 | group Bidirectional Traffic: User -> Device 9 | 10 | User -> Network: ["portsend", ref, data] 11 | Network -> Device: ["portsend", ref, data] 12 | 13 | Network <-- Device: ["response", "portsend", "ok"] 14 | User <-- Network: ["response", "portsend", "ok"] 15 | 16 | end 17 | 18 | group Bidirectional Traffic: User -> Device 19 | 20 | Network <- Device: ["portsend", ref, data] 21 | User <- Network: ["portsend", ref, data] 22 | 23 | User --> Network: ["response", "portsend", "ok"] 24 | Network --> Device: ["response", "portsend", "ok"] 25 | 26 | end 27 | 28 | @enduml 29 | -------------------------------------------------------------------------------- /evm/Makefile: -------------------------------------------------------------------------------- 1 | # EVMC Wrapper 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the GNU General Public License, Version 3. 4 | CFLAGS+=-I. -O2 -g -Wall -Wno-unknown-pragmas 5 | CXXFLAGS+=-std=c++17 $(CFLAGS) 6 | LDFLAGS+=-lboost_system 7 | INTERP=libaleth-interpreter 8 | 9 | DEPS = $(INTERP)/VMOpt.o $(INTERP)/VM.o $(INTERP)/VMCalls.o 10 | DEPS += intx/div.o 11 | DEPS += evmc/instruction_metrics.o 12 | DEPS += ethash/keccak.o ethash/keccakf1600.o 13 | 14 | ifneq ($(OS),Windows_NT) 15 | OS_S := $(shell uname) 16 | ifeq ($(OS_S), Darwin) 17 | CFLAGS+=-I./mac 18 | endif 19 | endif 20 | 21 | evm: $(DEPS) main.o host.o 22 | g++ $^ $(LDFLAGS) -o evm 23 | 24 | 25 | main.o: host.hpp main.cpp 26 | host.o: host.hpp host.cpp 27 | 28 | %.o : %.cpp 29 | g++ -c $(CXXFLAGS) $< -o $@ 30 | 31 | %.o : %.c 32 | gcc -c $(CFLAGS) $< -o $@ 33 | 34 | .PHONY: clean 35 | clean: 36 | -rm evm $(DEPS) main.o host.o 37 | -------------------------------------------------------------------------------- /evm/aleth/version.h: -------------------------------------------------------------------------------- 1 | #define aleth_version "diode/0.0.0" 2 | -------------------------------------------------------------------------------- /evm/ethash/hash_types.h: -------------------------------------------------------------------------------- 1 | /* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. 2 | * Copyright 2018-2019 Pawel Bylica. 3 | * Licensed under the Apache License, Version 2.0. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | union ethash_hash256 15 | { 16 | uint64_t word64s[4]; 17 | uint32_t word32s[8]; 18 | uint8_t bytes[32]; 19 | char str[32]; 20 | }; 21 | 22 | union ethash_hash512 23 | { 24 | uint64_t word64s[8]; 25 | uint32_t word32s[16]; 26 | uint8_t bytes[64]; 27 | char str[64]; 28 | }; 29 | 30 | union ethash_hash1024 31 | { 32 | union ethash_hash512 hash512s[2]; 33 | uint64_t word64s[16]; 34 | uint32_t word32s[32]; 35 | uint8_t bytes[128]; 36 | char str[128]; 37 | }; 38 | 39 | union ethash_hash2048 40 | { 41 | union ethash_hash512 hash512s[4]; 42 | uint64_t word64s[32]; 43 | uint32_t word32s[64]; 44 | uint8_t bytes[256]; 45 | char str[256]; 46 | }; 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | -------------------------------------------------------------------------------- /evm/ethash/hash_types.hpp: -------------------------------------------------------------------------------- 1 | // ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. 2 | // Copyright 2018-2019 Pawel Bylica. 3 | // Licensed under the Apache License, Version 2.0. 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace ethash 10 | { 11 | using hash256 = ethash_hash256; 12 | using hash512 = ethash_hash512; 13 | using hash1024 = ethash_hash1024; 14 | using hash2048 = ethash_hash2048; 15 | } // namespace ethash 16 | -------------------------------------------------------------------------------- /evm/ethash/keccak.h: -------------------------------------------------------------------------------- 1 | /* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. 2 | * Copyright 2018-2019 Pawel Bylica. 3 | * Licensed under the Apache License, Version 2.0. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | #define NOEXCEPT noexcept 14 | #else 15 | #define NOEXCEPT 16 | #endif 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | /** 23 | * The Keccak-f[1600] function. 24 | * 25 | * The implementation of the Keccak-f function with 1600-bit width of the permutation (b). 26 | * The size of the state is also 1600 bit what gives 25 64-bit words. 27 | * 28 | * @param state The state of 25 64-bit words on which the permutation is to be performed. 29 | */ 30 | void ethash_keccakf1600(uint64_t state[25]) NOEXCEPT; 31 | 32 | /** 33 | * The Keccak-f[800] function. 34 | * 35 | * The implementation of the Keccak-f function with 800-bit width of the permutation (b). 36 | * The size of the state is also 800 bit what gives 25 32-bit words. 37 | * 38 | * @param state The state of 25 32-bit words on which the permutation is to be performed. 39 | */ 40 | void ethash_keccakf800(uint32_t state[25]) NOEXCEPT; 41 | 42 | union ethash_hash256 ethash_keccak256(const uint8_t* data, size_t size) NOEXCEPT; 43 | union ethash_hash256 ethash_keccak256_32(const uint8_t data[32]) NOEXCEPT; 44 | union ethash_hash512 ethash_keccak512(const uint8_t* data, size_t size) NOEXCEPT; 45 | union ethash_hash512 ethash_keccak512_64(const uint8_t data[64]) NOEXCEPT; 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | -------------------------------------------------------------------------------- /evm/ethash/keccak.hpp: -------------------------------------------------------------------------------- 1 | // ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. 2 | // Copyright 2018-2019 Pawel Bylica. 3 | // Licensed under the Apache License, Version 2.0. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | namespace ethash 11 | { 12 | inline hash256 keccak256(const uint8_t* data, size_t size) noexcept 13 | { 14 | return ethash_keccak256(data, size); 15 | } 16 | 17 | inline hash256 keccak256(const hash256& input) noexcept 18 | { 19 | return ethash_keccak256_32(input.bytes); 20 | } 21 | 22 | inline hash512 keccak512(const uint8_t* data, size_t size) noexcept 23 | { 24 | return ethash_keccak512(data, size); 25 | } 26 | 27 | inline hash512 keccak512(const hash512& input) noexcept 28 | { 29 | return ethash_keccak512_64(input.bytes); 30 | } 31 | 32 | static constexpr auto keccak256_32 = ethash_keccak256_32; 33 | static constexpr auto keccak512_64 = ethash_keccak512_64; 34 | 35 | } // namespace ethash 36 | -------------------------------------------------------------------------------- /evm/ethash/progpow.hpp: -------------------------------------------------------------------------------- 1 | // ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. 2 | // Copyright 2018-2019 Pawel Bylica. 3 | // Licensed under the Apache License, Version 2.0. 4 | 5 | /// @file 6 | /// 7 | /// ProgPoW API 8 | /// 9 | /// This file provides the public API for ProgPoW as the Ethash API extension. 10 | 11 | #include 12 | 13 | namespace progpow 14 | { 15 | using namespace ethash; // Include ethash namespace. 16 | 17 | 18 | /// The ProgPoW algorithm revision implemented as specified in the spec 19 | /// https://github.com/ifdefelse/ProgPOW#change-history. 20 | constexpr auto revision = "0.9.2"; 21 | 22 | constexpr int period_length = 50; 23 | constexpr uint32_t num_regs = 32; 24 | constexpr size_t num_lanes = 16; 25 | constexpr int num_cache_accesses = 12; 26 | constexpr int num_math_operations = 20; 27 | constexpr size_t l1_cache_size = 16 * 1024; 28 | constexpr size_t l1_cache_num_items = l1_cache_size / sizeof(uint32_t); 29 | 30 | result hash(const epoch_context& context, int block_number, const hash256& header_hash, 31 | uint64_t nonce) noexcept; 32 | 33 | result hash(const epoch_context_full& context, int block_number, const hash256& header_hash, 34 | uint64_t nonce) noexcept; 35 | 36 | bool verify(const epoch_context& context, int block_number, const hash256& header_hash, 37 | const hash256& mix_hash, uint64_t nonce, const hash256& boundary) noexcept; 38 | 39 | search_result search_light(const epoch_context& context, int block_number, 40 | const hash256& header_hash, const hash256& boundary, uint64_t start_nonce, 41 | size_t iterations) noexcept; 42 | 43 | search_result search(const epoch_context_full& context, int block_number, 44 | const hash256& header_hash, const hash256& boundary, uint64_t start_nonce, 45 | size_t iterations) noexcept; 46 | 47 | } // namespace progpow 48 | -------------------------------------------------------------------------------- /evm/ethash/version.h: -------------------------------------------------------------------------------- 1 | /* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. 2 | * Copyright 2019 Pawel Bylica. 3 | * Licensed under the Apache License, Version 2.0. 4 | */ 5 | 6 | #pragma once 7 | 8 | /** The ethash library version. */ 9 | #define ETHASH_VERSION "0.5.1-alpha.1" 10 | 11 | #ifdef __cplusplus 12 | namespace ethash 13 | { 14 | /// The ethash library version. 15 | constexpr auto version = ETHASH_VERSION; 16 | 17 | } // namespace ethash 18 | #endif 19 | -------------------------------------------------------------------------------- /evm/evmc/utils.h: -------------------------------------------------------------------------------- 1 | /* EVMC: Ethereum Client-VM Connector API. 2 | * Copyright 2018-2019 The EVMC Authors. 3 | * Licensed under the Apache License, Version 2.0. 4 | */ 5 | 6 | #pragma once 7 | 8 | /** 9 | * @file 10 | * A collection of helper macros to handle some non-portable features of C/C++ compilers. 11 | * 12 | * @addtogroup helpers 13 | * @{ 14 | */ 15 | 16 | /** 17 | * @def EVMC_EXPORT 18 | * Marks a function to be exported from a shared library. 19 | */ 20 | #if defined _MSC_VER || defined __MINGW32__ 21 | #define EVMC_EXPORT __declspec(dllexport) 22 | #else 23 | #define EVMC_EXPORT __attribute__((visibility("default"))) 24 | #endif 25 | 26 | /** 27 | * @def EVMC_NOEXCEPT 28 | * Safe way of marking a function with `noexcept` C++ specifier. 29 | */ 30 | #if __cplusplus 31 | #define EVMC_NOEXCEPT noexcept 32 | #else 33 | #define EVMC_NOEXCEPT 34 | #endif 35 | 36 | /** @} */ 37 | -------------------------------------------------------------------------------- /evm/intx/builtins.h: -------------------------------------------------------------------------------- 1 | // builtins: Portable C/C++ compiler builtins. 2 | // Copyright 2018 Pawel Bylica. 3 | // Licensed under the Apache License, Version 2.0. 4 | 5 | /** 6 | * @file 7 | * Implementation of GCC/clang builtins for MSVC compiler. 8 | */ 9 | 10 | #pragma once 11 | 12 | #ifdef _MSC_VER 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | /** 20 | * Returns the number of leading 0-bits in `x`, starting at the most significant bit position. 21 | * If `x` is 0, the result is undefined. 22 | */ 23 | static inline int __builtin_clz(unsigned int x) 24 | { 25 | unsigned long most_significant_bit; 26 | _BitScanReverse(&most_significant_bit, x); 27 | return 31 ^ (int)most_significant_bit; 28 | } 29 | 30 | /** 31 | * Returns the number of leading 0-bits in `x`, starting at the most significant bit position. 32 | * If `x` is 0, the result is undefined. 33 | */ 34 | static inline int __builtin_clzl(unsigned __int64 x) 35 | { 36 | unsigned long most_significant_bit; 37 | _BitScanReverse64(&most_significant_bit, x); 38 | return 63 ^ (int)most_significant_bit; 39 | } 40 | 41 | /** 42 | * Returns the number of 1-bits in `x`. 43 | */ 44 | static inline int __builtin_popcount(unsigned int x) 45 | { 46 | return (int)__popcnt(x); 47 | } 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | 53 | #endif 54 | 55 | #ifdef __cplusplus 56 | namespace builtins 57 | { 58 | inline int clz(unsigned int x) 59 | { 60 | return __builtin_clz(x); 61 | } 62 | inline int clz(unsigned long x) 63 | { 64 | return __builtin_clzl(x); 65 | } 66 | } // namespace builtins 67 | #endif 68 | -------------------------------------------------------------------------------- /evm/intx/div.hpp: -------------------------------------------------------------------------------- 1 | // intx: extended precision integer library. 2 | // Copyright 2019 Pawel Bylica. 3 | // Licensed under the Apache License, Version 2.0. 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace intx 10 | { 11 | template 12 | struct normalized_div_args 13 | { 14 | uint denominator; 15 | uint numerator; 16 | typename uint::word_type numerator_ex; 17 | int num_denominator_words; 18 | int num_numerator_words; 19 | unsigned shift; 20 | }; 21 | 22 | template 23 | inline normalized_div_args normalize( 24 | const IntT& numerator, const IntT& denominator) noexcept 25 | { 26 | // FIXME: Make the implementation type independent 27 | static constexpr auto num_words = IntT::num_words; 28 | 29 | auto* u = as_words(numerator); 30 | auto* v = as_words(denominator); 31 | 32 | normalized_div_args na; 33 | auto* un = as_words(na.numerator); 34 | auto* vn = as_words(na.denominator); 35 | 36 | auto& m = na.num_numerator_words; 37 | for (m = num_words; m > 0 && u[m - 1] == 0; --m) 38 | ; 39 | 40 | auto& n = na.num_denominator_words; 41 | for (n = num_words; n > 0 && v[n - 1] == 0; --n) 42 | ; 43 | 44 | na.shift = clz(v[n - 1]); 45 | if (na.shift) 46 | { 47 | for (int i = num_words - 1; i > 0; --i) 48 | vn[i] = (v[i] << na.shift) | (v[i - 1] >> (64 - na.shift)); 49 | vn[0] = v[0] << na.shift; 50 | 51 | un[num_words] = u[num_words - 1] >> (64 - na.shift); 52 | for (int i = num_words - 1; i > 0; --i) 53 | un[i] = (u[i] << na.shift) | (u[i - 1] >> (64 - na.shift)); 54 | un[0] = u[0] << na.shift; 55 | } 56 | else 57 | { 58 | na.numerator_ex = 0; 59 | na.numerator = numerator; 60 | na.denominator = denominator; 61 | } 62 | 63 | return na; 64 | } 65 | 66 | } // namespace intx 67 | -------------------------------------------------------------------------------- /evm/libaleth-interpreter/interpreter.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2018-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #if __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | EVMC_EXPORT struct evmc_vm* evmc_create_interpreter() EVMC_NOEXCEPT; 14 | 15 | #if __cplusplus 16 | } 17 | #endif 18 | -------------------------------------------------------------------------------- /evm/libdevcore/Address.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2013-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | /// @file 6 | /// This file defines Address alias for FixedHash of 160 bits and some 7 | /// special Address constants. 8 | 9 | #pragma once 10 | 11 | #include "FixedHash.h" 12 | 13 | namespace dev 14 | { 15 | 16 | /// An Ethereum address: 20 bytes. 17 | /// @NOTE This is not endian-specific; it's just a bunch of bytes. 18 | using Address = h160; 19 | 20 | /// A vector of Ethereum addresses. 21 | using Addresses = h160s; 22 | 23 | /// A hash set of Ethereum addresses. 24 | using AddressHash = std::unordered_set; 25 | 26 | /// The zero address. 27 | extern Address const ZeroAddress; 28 | 29 | /// The last address. 30 | extern Address const MaxAddress; 31 | 32 | /// The SYSTEM address. 33 | extern Address const SystemAddress; 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /evm/libdevcore/Assertions.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2015-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | /// @file 6 | /// Assertion handling. 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace dev 13 | { 14 | 15 | #if defined(_MSC_VER) 16 | #define ETH_FUNC __FUNCSIG__ 17 | #elif defined(__GNUC__) 18 | #define ETH_FUNC __PRETTY_FUNCTION__ 19 | #else 20 | #define ETH_FUNC __func__ 21 | #endif 22 | 23 | #define asserts(A) ::dev::assertAux(A, #A, __LINE__, __FILE__, ETH_FUNC) 24 | #define assertsEqual(A, B) ::dev::assertEqualAux(A, B, #A, #B, __LINE__, __FILE__, ETH_FUNC) 25 | 26 | inline bool assertAux(bool _a, char const* _aStr, unsigned _line, char const* _file, char const* _func) 27 | { 28 | if (!_a) 29 | std::cerr << "Assertion failed:" << _aStr << " [func=" << _func << ", line=" << _line << ", file=" << _file << "]" << std::endl; 30 | return !_a; 31 | } 32 | 33 | template 34 | inline bool assertEqualAux(A const& _a, B const& _b, char const* _aStr, char const* _bStr, unsigned _line, char const* _file, char const* _func) 35 | { 36 | bool c = _a == _b; 37 | if (!c) 38 | { 39 | std::cerr << "Assertion failed: " << _aStr << " == " << _bStr << " [func=" << _func << ", line=" << _line << ", file=" << _file << "]" << std::endl; 40 | std::cerr << " Fail equality: " << _a << "==" << _b << std::endl; 41 | } 42 | return !c; 43 | } 44 | 45 | /// Assertion that throws an exception containing the given description if it is not met. 46 | /// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong."); 47 | /// Do NOT supply an exception object as the second parameter. 48 | #define assertThrow(_condition, _ExceptionType, _description) \ 49 | ::dev::assertThrowAux<_ExceptionType>(_condition, _description, __LINE__, __FILE__, ETH_FUNC) 50 | 51 | using errinfo_comment = boost::error_info; 52 | 53 | template 54 | inline void assertThrowAux( 55 | bool _condition, 56 | ::std::string const& _errorDescription, 57 | unsigned _line, 58 | char const* _file, 59 | char const* _function 60 | ) 61 | { 62 | if (!_condition) 63 | ::boost::throw_exception( 64 | _ExceptionType() << 65 | ::dev::errinfo_comment(_errorDescription) << 66 | ::boost::throw_function(_function) << 67 | ::boost::throw_file(_file) << 68 | ::boost::throw_line(_line) 69 | ); 70 | } 71 | 72 | template 73 | inline void assertThrowAux( 74 | void const* _pointer, 75 | ::std::string const& _errorDescription, 76 | unsigned _line, 77 | char const* _file, 78 | char const* _function 79 | ) 80 | { 81 | assertThrowAux<_ExceptionType>(_pointer != nullptr, _errorDescription, _line, _file, _function); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /evm/libdevcore/Base64.h: -------------------------------------------------------------------------------- 1 | /* 2 | base64.cpp and base64.h 3 | 4 | Copyright (C) 2004-2008 René Nyffenegger 5 | 6 | This source code is provided 'as-is', without any express or implied 7 | warranty. In no event will the author be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this source code must not be misrepresented; you must not 15 | claim that you wrote the original source code. If you use this source code 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original source code. 21 | 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | René Nyffenegger rene.nyffenegger@adp-gmbh.ch 25 | */ 26 | /// Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c 27 | /// Originally by René Nyffenegger. 28 | /// DEVified by Gav Wood. 29 | #pragma once 30 | 31 | #include "FixedHash.h" 32 | #include 33 | 34 | namespace dev 35 | { 36 | std::string toBase64(bytesConstRef _in); 37 | std::string toBase64URLSafe(bytesConstRef _in); 38 | bytes fromBase64(std::string const& _in); 39 | 40 | } // namespace dev 41 | -------------------------------------------------------------------------------- /evm/libdevcore/DBFactory.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include "Common.h" 8 | #include "db.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace dev 14 | { 15 | namespace db 16 | { 17 | enum class DatabaseKind 18 | { 19 | LevelDB, 20 | RocksDB, 21 | MemoryDB 22 | }; 23 | 24 | /// Provide a set of program options related to databases 25 | /// 26 | /// @param _lineLength The line length for description text wrapping, the same as in 27 | /// boost::program_options::options_description::options_description(). 28 | boost::program_options::options_description databaseProgramOptions( 29 | unsigned _lineLength = boost::program_options::options_description::m_default_line_length); 30 | 31 | bool isDiskDatabase(); 32 | DatabaseKind databaseKind(); 33 | void setDatabaseKindByName(std::string const& _name); 34 | void setDatabaseKind(DatabaseKind _kind); 35 | boost::filesystem::path databasePath(); 36 | 37 | class DBFactory 38 | { 39 | public: 40 | DBFactory() = delete; 41 | ~DBFactory() = delete; 42 | 43 | static std::unique_ptr create(); 44 | static std::unique_ptr create(boost::filesystem::path const& _path); 45 | static std::unique_ptr create(DatabaseKind _kind); 46 | static std::unique_ptr create( 47 | DatabaseKind _kind, boost::filesystem::path const& _path); 48 | 49 | private: 50 | }; 51 | } // namespace db 52 | } // namespace dev -------------------------------------------------------------------------------- /evm/libdevcore/Exceptions.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include "FixedHash.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace dev 19 | { 20 | /// Base class for all exceptions. 21 | struct Exception : virtual std::exception, virtual boost::exception 22 | { 23 | const char* what() const noexcept override { return boost::diagnostic_information_what(*this); } 24 | }; 25 | 26 | #define DEV_SIMPLE_EXCEPTION(X) \ 27 | struct X : virtual Exception \ 28 | { \ 29 | } 30 | 31 | /// Base class for all RLP exceptions. 32 | struct RLPException : virtual Exception 33 | { 34 | }; 35 | #define DEV_SIMPLE_EXCEPTION_RLP(X) \ 36 | struct X : virtual RLPException \ 37 | { \ 38 | } 39 | 40 | DEV_SIMPLE_EXCEPTION_RLP(BadCast); 41 | DEV_SIMPLE_EXCEPTION_RLP(BadRLP); 42 | DEV_SIMPLE_EXCEPTION_RLP(OversizeRLP); 43 | DEV_SIMPLE_EXCEPTION_RLP(UndersizeRLP); 44 | 45 | DEV_SIMPLE_EXCEPTION(BadHexCharacter); 46 | DEV_SIMPLE_EXCEPTION(NoNetworking); 47 | DEV_SIMPLE_EXCEPTION(NoUPnPDevice); 48 | DEV_SIMPLE_EXCEPTION(RootNotFound); 49 | DEV_SIMPLE_EXCEPTION(BadRoot); 50 | DEV_SIMPLE_EXCEPTION(FileError); 51 | DEV_SIMPLE_EXCEPTION(Overflow); 52 | DEV_SIMPLE_EXCEPTION(FailedInvariant); 53 | DEV_SIMPLE_EXCEPTION(ValueTooLarge); 54 | DEV_SIMPLE_EXCEPTION(UnknownField); 55 | DEV_SIMPLE_EXCEPTION(MissingField); 56 | DEV_SIMPLE_EXCEPTION(SyntaxError); 57 | DEV_SIMPLE_EXCEPTION(WrongFieldType); 58 | DEV_SIMPLE_EXCEPTION(InterfaceNotSupported); 59 | DEV_SIMPLE_EXCEPTION(ExternalFunctionFailure); 60 | DEV_SIMPLE_EXCEPTION(WaitTimeout); 61 | 62 | // error information to be added to exceptions 63 | using errinfo_invalidSymbol = boost::error_info; 64 | using errinfo_wrongAddress = boost::error_info; 65 | using errinfo_comment = boost::error_info; 66 | using errinfo_required = boost::error_info; 67 | using errinfo_got = boost::error_info; 68 | using errinfo_min = boost::error_info; 69 | using errinfo_max = boost::error_info; 70 | using RequirementError = boost::tuple; 71 | using RequirementErrorComment = boost::tuple; 72 | using errinfo_hash256 = boost::error_info; 73 | using errinfo_required_h256 = boost::error_info; 74 | using errinfo_got_h256 = boost::error_info; 75 | using Hash256RequirementError = boost::tuple; 76 | using errinfo_extraData = boost::error_info; 77 | using errinfo_externalFunction = boost::errinfo_api_function; 78 | using errinfo_interface = boost::error_info; 79 | using errinfo_path = boost::error_info; 80 | using errinfo_nodeID = boost::error_info; 81 | } 82 | -------------------------------------------------------------------------------- /evm/libdevcore/FileSystem.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | namespace dev 11 | { 12 | 13 | /// Sets the data dir for the default ("ethereum") prefix. 14 | void setDataDir(boost::filesystem::path const& _dir); 15 | /// @returns the path for user data. 16 | boost::filesystem::path getDataDir(std::string _prefix = "ethereum"); 17 | /// @returns the default path for user data, ignoring the one set by `setDataDir`. 18 | boost::filesystem::path getDefaultDataDir(std::string _prefix = "ethereum"); 19 | /// Sets the ipc socket dir 20 | void setIpcPath(boost::filesystem::path const& _ipcPath); 21 | /// @returns the ipc path (default is DataDir) 22 | boost::filesystem::path getIpcPath(); 23 | 24 | /// @returns a new path whose file name is suffixed with the given suffix. 25 | boost::filesystem::path appendToFilename(boost::filesystem::path const& _orig, std::string const& _suffix); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /evm/libdevcore/JsonUtils.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace dev 13 | { 14 | // Throws UnknownField() if _obj contains field names not listed in _allowedFields. 15 | void validateFieldNames(json_spirit::mObject const& _obj, std::set const& _allowedFields); 16 | 17 | // Converts json value type to string 18 | std::string jsonTypeAsString(json_spirit::Value_type _type); 19 | 20 | enum class JsonFieldPresence 21 | { 22 | Required, 23 | Optional 24 | }; 25 | using JsonTypeSet = std::set; 26 | using JsonFieldOptions = std::pair; 27 | /// Check the json object with validation map that reuires certain field of certain type to be 28 | /// present in json 29 | /** 30 | @param _o a json object to check 31 | @param _configName a string with json object name. Will apper in error message. 32 | @param _validationMap a map with json objects that would be checked. "objName" -> {js::str_type, 33 | jsonField::Required} 34 | */ 35 | void requireJsonFields(json_spirit::mObject const& _o, std::string const& _configName, 36 | std::map const& _validationMap); 37 | } 38 | -------------------------------------------------------------------------------- /evm/libdevcore/LevelDB.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2017-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include "db.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace dev 14 | { 15 | namespace db 16 | { 17 | class LevelDB : public DatabaseFace 18 | { 19 | public: 20 | static leveldb::ReadOptions defaultReadOptions(); 21 | static leveldb::WriteOptions defaultWriteOptions(); 22 | static leveldb::Options defaultDBOptions(); 23 | 24 | explicit LevelDB(boost::filesystem::path const& _path, 25 | leveldb::ReadOptions _readOptions = defaultReadOptions(), 26 | leveldb::WriteOptions _writeOptions = defaultWriteOptions(), 27 | leveldb::Options _dbOptions = defaultDBOptions()); 28 | 29 | std::string lookup(Slice _key) const override; 30 | bool exists(Slice _key) const override; 31 | void insert(Slice _key, Slice _value) override; 32 | void kill(Slice _key) override; 33 | 34 | std::unique_ptr createWriteBatch() const override; 35 | void commit(std::unique_ptr _batch) override; 36 | 37 | void forEach(std::function _f) const override; 38 | 39 | private: 40 | std::unique_ptr m_db; 41 | leveldb::ReadOptions const m_readOptions; 42 | leveldb::WriteOptions const m_writeOptions; 43 | }; 44 | 45 | } // namespace db 46 | } // namespace dev 47 | -------------------------------------------------------------------------------- /evm/libdevcore/LoggingProgramOptions.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include "Log.h" 8 | #include 9 | 10 | namespace dev 11 | { 12 | boost::program_options::options_description createLoggingProgramOptions( 13 | unsigned _lineLength, LoggingOptions& _options, std::string const& _logChannels = {}); 14 | 15 | } // namespace dev -------------------------------------------------------------------------------- /evm/libdevcore/LruCache.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | namespace dev 11 | { 12 | template 13 | class LruCache 14 | { 15 | using key_type = Key; 16 | using value_type = Value; 17 | using list_type = std::list>; 18 | using map_type = std::unordered_map; 19 | 20 | public: 21 | explicit LruCache(size_t _capacity) : m_capacity(_capacity) {} 22 | 23 | size_t insert(key_type const& _key, value_type const& _val) 24 | { 25 | auto const cIter = m_index.find(_key); 26 | if (cIter == m_index.cend()) 27 | { 28 | if (m_index.size() == m_capacity) 29 | { 30 | m_index.erase(m_data.back().first); 31 | m_data.pop_back(); 32 | } 33 | m_data.push_front({_key, _val}); 34 | m_index[_key] = m_data.begin(); 35 | } 36 | else 37 | m_data.splice(m_data.begin(), m_data, cIter->second); 38 | 39 | return m_index.size(); 40 | } 41 | 42 | size_t remove(key_type const& _key) 43 | { 44 | auto const cIter = m_index.find(_key); 45 | if (cIter != m_index.cend()) 46 | { 47 | m_data.erase(cIter->second); 48 | m_index.erase(cIter); 49 | } 50 | 51 | return m_index.size(); 52 | } 53 | 54 | bool touch(key_type const& _key) 55 | { 56 | auto const cIter = m_index.find(_key); 57 | if (cIter != m_index.cend()) 58 | { 59 | m_data.splice(m_data.begin(), m_data, cIter->second); 60 | return true; 61 | } 62 | return false; 63 | } 64 | 65 | bool contains(key_type const& _key) const { return m_index.find(_key) != m_index.cend(); } 66 | 67 | bool contains(key_type const& _key, value_type const& _value) const 68 | { 69 | auto const cIter = m_index.find(_key); 70 | return cIter != m_index.cend() && (*(cIter->second)).second == _value; 71 | } 72 | 73 | bool empty() const noexcept { return m_index.empty(); } 74 | 75 | size_t size() const noexcept { return m_index.size(); } 76 | 77 | size_t capacity() const noexcept { return m_capacity; } 78 | 79 | void clear() noexcept 80 | { 81 | m_index.clear(); 82 | m_data.clear(); 83 | } 84 | 85 | // Expose data iterator for testing purposes 86 | typename list_type::const_iterator cbegin() const noexcept { return m_data.cbegin(); } 87 | typename list_type::iterator begin() noexcept { return m_data.begin(); } 88 | typename list_type::const_iterator cend() const noexcept { return m_data.cend(); } 89 | typename list_type::iterator end() noexcept { return m_data.end(); } 90 | 91 | private: 92 | list_type m_data; 93 | map_type m_index; 94 | size_t m_capacity; 95 | }; 96 | } // namespace dev -------------------------------------------------------------------------------- /evm/libdevcore/MemoryDB.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2017-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include "Common.h" 8 | #include "Guards.h" 9 | #include "db.h" 10 | 11 | namespace dev 12 | { 13 | namespace db 14 | { 15 | class MemoryDBWriteBatch : public WriteBatchFace 16 | { 17 | public: 18 | void insert(Slice _key, Slice _value) override; 19 | void kill(Slice _key) override; 20 | 21 | std::unordered_map& writeBatch() { return m_batch; } 22 | size_t size() { return m_batch.size(); } 23 | 24 | private: 25 | std::unordered_map m_batch; 26 | }; 27 | 28 | class MemoryDB : public DatabaseFace 29 | { 30 | public: 31 | std::string lookup(Slice _key) const override; 32 | bool exists(Slice _key) const override; 33 | void insert(Slice _key, Slice _value) override; 34 | void kill(Slice _key) override; 35 | 36 | std::unique_ptr createWriteBatch() const override; 37 | void commit(std::unique_ptr _batch) override; 38 | 39 | // A database must implement the `forEach` method that allows the caller 40 | // to pass in a function `f`, which will be called with the key and value 41 | // of each record in the database. If `f` returns false, the `forEach` 42 | // method must return immediately. 43 | void forEach(std::function _f) const override; 44 | 45 | size_t size() const { return m_db.size(); } 46 | 47 | private: 48 | std::unordered_map m_db; 49 | mutable Mutex m_mutex; 50 | }; 51 | } // namespace db 52 | } // namespace dev -------------------------------------------------------------------------------- /evm/libdevcore/OverlayDB.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace dev 14 | { 15 | 16 | class OverlayDB: public StateCacheDB 17 | { 18 | public: 19 | explicit OverlayDB(std::unique_ptr _db = nullptr) 20 | : m_db(_db.release(), [](db::DatabaseFace* db) { 21 | clog(VerbosityDebug, "overlaydb") << "Closing state DB"; 22 | delete db; 23 | }) 24 | {} 25 | 26 | ~OverlayDB(); 27 | 28 | // Copyable 29 | OverlayDB(OverlayDB const&) = default; 30 | OverlayDB& operator=(OverlayDB const&) = default; 31 | // Movable 32 | OverlayDB(OverlayDB&&) = default; 33 | OverlayDB& operator=(OverlayDB&&) = default; 34 | 35 | void commit(); 36 | void rollback(); 37 | 38 | std::string lookup(h256 const& _h) const; 39 | bool exists(h256 const& _h) const; 40 | void kill(h256 const& _h); 41 | 42 | bytes lookupAux(h256 const& _h) const; 43 | 44 | private: 45 | using StateCacheDB::clear; 46 | 47 | std::shared_ptr m_db; 48 | }; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /evm/libdevcore/RocksDB.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2017-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | #pragma once 5 | 6 | #include "db.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace dev 13 | { 14 | namespace db 15 | { 16 | class RocksDB : public DatabaseFace 17 | { 18 | public: 19 | static rocksdb::ReadOptions defaultReadOptions(); 20 | static rocksdb::WriteOptions defaultWriteOptions(); 21 | static rocksdb::Options defaultDBOptions(); 22 | 23 | explicit RocksDB(boost::filesystem::path const& _path, 24 | rocksdb::ReadOptions _readOptions = defaultReadOptions(), 25 | rocksdb::WriteOptions _writeOptions = defaultWriteOptions(), 26 | rocksdb::Options _dbOptions = defaultDBOptions()); 27 | 28 | std::string lookup(Slice _key) const override; 29 | bool exists(Slice _key) const override; 30 | void insert(Slice _key, Slice _value) override; 31 | void kill(Slice _key) override; 32 | 33 | std::unique_ptr createWriteBatch() const override; 34 | void commit(std::unique_ptr _batch) override; 35 | 36 | void forEach(std::function f) const override; 37 | 38 | private: 39 | std::unique_ptr m_db; 40 | rocksdb::ReadOptions const m_readOptions; 41 | rocksdb::WriteOptions const m_writeOptions; 42 | }; 43 | 44 | } // namespace db 45 | } // namespace dev 46 | -------------------------------------------------------------------------------- /evm/libdevcore/StateCacheDB.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | #pragma once 5 | 6 | #include "Common.h" 7 | #include "Log.h" 8 | #include "RLP.h" 9 | 10 | namespace dev 11 | { 12 | class StateCacheDB 13 | { 14 | friend class EnforceRefs; 15 | 16 | public: 17 | StateCacheDB() {} 18 | StateCacheDB(StateCacheDB const& _c) { operator=(_c); } 19 | 20 | StateCacheDB& operator=(StateCacheDB const& _c); 21 | 22 | virtual ~StateCacheDB() = default; 23 | 24 | void clear() 25 | { 26 | m_main.clear(); 27 | m_aux.clear(); 28 | } // WARNING !!!! didn't originally clear m_refCount!!! 29 | std::unordered_map get() const; 30 | 31 | std::string lookup(h256 const& _h) const; 32 | bool exists(h256 const& _h) const; 33 | void insert(h256 const& _h, bytesConstRef _v); 34 | bool kill(h256 const& _h); 35 | void purge(); 36 | 37 | bytes lookupAux(h256 const& _h) const; 38 | void removeAux(h256 const& _h); 39 | void insertAux(h256 const& _h, bytesConstRef _v); 40 | 41 | h256Hash keys() const; 42 | 43 | protected: 44 | #if DEV_GUARDED_DB 45 | mutable SharedMutex x_this; 46 | #endif 47 | std::unordered_map> m_main; 48 | std::unordered_map> m_aux; 49 | 50 | mutable bool m_enforceRefs = false; 51 | }; 52 | 53 | class EnforceRefs 54 | { 55 | public: 56 | EnforceRefs(StateCacheDB const& _o, bool _r) : m_o(_o), m_r(_o.m_enforceRefs) 57 | { 58 | _o.m_enforceRefs = _r; 59 | } 60 | ~EnforceRefs() { m_o.m_enforceRefs = m_r; } 61 | 62 | private: 63 | StateCacheDB const& m_o; 64 | bool m_r; 65 | }; 66 | 67 | inline std::ostream& operator<<(std::ostream& _out, StateCacheDB const& _m) 68 | { 69 | for (auto const& i : _m.get()) 70 | { 71 | _out << i.first << ": "; 72 | _out << RLP(i.second); 73 | _out << " " << toHex(i.second); 74 | _out << std::endl; 75 | } 76 | return _out; 77 | } 78 | 79 | } // namespace dev 80 | -------------------------------------------------------------------------------- /evm/libdevcore/TransientDirectory.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace dev 10 | { 11 | 12 | /** 13 | * @brief temporary directory implementation 14 | * It creates temporary directory in the given path. On dealloc it removes the directory 15 | * @throws if the given path already exists, throws an exception 16 | */ 17 | class TransientDirectory 18 | { 19 | public: 20 | TransientDirectory(); 21 | TransientDirectory(std::string const& _path); 22 | ~TransientDirectory(); 23 | 24 | std::string const& path() const { return m_path; } 25 | 26 | private: 27 | std::string m_path; 28 | }; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /evm/libdevcore/TrieHash.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace dev 12 | { 13 | 14 | bytes rlp256(BytesMap const& _s); 15 | h256 hash256(BytesMap const& _s); 16 | 17 | h256 orderedTrieRoot(std::vector const& _data); 18 | 19 | template inline h256 trieRootOver(unsigned _itemCount, T const& _getKey, U const& _getValue) 20 | { 21 | BytesMap m; 22 | for (unsigned i = 0; i < _itemCount; ++i) 23 | m[_getKey(i)] = _getValue(i); 24 | return hash256(m); 25 | } 26 | 27 | h256 orderedTrieRoot(std::vector const& _data); 28 | h256 orderedTrieRoot(std::vector const& _data); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /evm/libdevcore/UndefMacros.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2015-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | /// @file 6 | /// This header should be used to #undef some really evil macros defined by 7 | /// windows.h which result in conflict with our libsolidity/Token.h 8 | #pragma once 9 | 10 | #if defined(_MSC_VER) || defined(__MINGW32__) 11 | 12 | #undef DELETE 13 | #undef IN 14 | #undef VOID 15 | #undef THIS 16 | #undef CONST 17 | 18 | // Conflicting define on MinGW in windows.h 19 | // windows.h(19): #define interface struct 20 | #ifdef interface 21 | #undef interface 22 | #endif 23 | 24 | #elif defined(DELETE) || defined(IN) || defined(VOID) || defined(THIS) || defined(CONST) || defined(interface) 25 | 26 | #error "The preceding macros in this header file are reserved for V8's "\ 27 | "TOKEN_LIST. Please add a platform specific define above to undefine "\ 28 | "overlapping macros." 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /evm/libdevcore/Worker.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include "Guards.h" 11 | 12 | namespace dev 13 | { 14 | 15 | enum class IfRunning 16 | { 17 | Fail, 18 | Join, 19 | Detach 20 | }; 21 | 22 | enum class WorkerState 23 | { 24 | Starting, 25 | Started, 26 | Stopping, 27 | Stopped, 28 | Killing 29 | }; 30 | 31 | class Worker 32 | { 33 | protected: 34 | Worker(std::string const& _name = "anon", unsigned _idleWaitMs = 30): m_name(_name), m_idleWaitMs(_idleWaitMs) {} 35 | 36 | /// Move-constructor. 37 | Worker(Worker&& _m) { std::swap(m_name, _m.m_name); } 38 | 39 | /// Move-assignment. 40 | Worker& operator=(Worker&& _m) 41 | { 42 | assert(&_m != this); 43 | std::swap(m_name, _m.m_name); 44 | return *this; 45 | } 46 | 47 | virtual ~Worker() { terminate(); } 48 | 49 | /// Allows changing worker name if work is stopped. 50 | void setName(std::string _n) { if (!isWorking()) m_name = _n; } 51 | 52 | /// Starts worker thread; causes startedWorking() to be called. 53 | void startWorking(); 54 | 55 | /// Stop worker thread; causes call to stopWorking(). 56 | void stopWorking(); 57 | 58 | /// Returns if worker thread is present. 59 | bool isWorking() const { Guard l(x_work); return m_state == WorkerState::Started; } 60 | 61 | /// Called after thread is started from startWorking(). 62 | virtual void startedWorking() {} 63 | 64 | /// Called continuously following sleep for m_idleWaitMs. 65 | virtual void doWork() {} 66 | 67 | /// Overrides doWork(); should call shouldStop() often and exit when true. 68 | virtual void workLoop(); 69 | bool shouldStop() const { return m_state != WorkerState::Started; } 70 | 71 | /// Called when is to be stopped, just prior to thread being joined. 72 | virtual void doneWorking() {} 73 | 74 | /// Blocks caller into worker thread has finished. 75 | // void join() const { Guard l(x_work); try { if (m_work) m_work->join(); } catch (...) {} } 76 | 77 | /// Stop and never start again. 78 | /// This has to be called in the destructor of any most derived class. Otherwise the worker thread will try to lookup vptrs. 79 | /// It's OK to call terminate() in destructors of multiple derived classes. 80 | void terminate(); 81 | 82 | private: 83 | 84 | std::string m_name; 85 | 86 | unsigned m_idleWaitMs = 0; 87 | 88 | mutable Mutex x_work; ///< Lock for the network existance and m_state_notifier. 89 | std::unique_ptr m_work; ///< The network thread. 90 | mutable std::condition_variable m_state_notifier; //< Notification when m_state changes. 91 | std::atomic m_state = {WorkerState::Starting}; 92 | }; 93 | 94 | } 95 | -------------------------------------------------------------------------------- /evm/libdevcore/concurrent_queue.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2015-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include "Exceptions.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace dev 15 | { 16 | /// Concurrent queue. 17 | /// You can push and pop elements to/from the queue. Pop will block until the queue is not empty. 18 | /// The default backend (_QueueT) is std::queue. It can be changed to any type that has 19 | /// proper push(), pop(), empty() and front() methods. 20 | template > 21 | class concurrent_queue 22 | { 23 | public: 24 | template 25 | void push(_U&& _elem) 26 | { 27 | { 28 | std::lock_guard guard{x_mutex}; 29 | m_queue.push(std::forward<_U>(_elem)); 30 | } 31 | m_cv.notify_one(); 32 | } 33 | 34 | _T pop() 35 | { 36 | std::unique_lock lock{ x_mutex }; 37 | m_cv.wait(lock, [this] { return !m_queue.empty(); }); 38 | auto item = std::move(m_queue.front()); 39 | m_queue.pop(); 40 | return item; 41 | } 42 | 43 | _T pop(std::chrono::milliseconds const& _waitDuration) 44 | { 45 | std::unique_lock lock{x_mutex}; 46 | if (!m_cv.wait_for(lock, _waitDuration, [this] { return !m_queue.empty(); })) 47 | BOOST_THROW_EXCEPTION(WaitTimeout()); 48 | 49 | auto item = std::move(m_queue.front()); 50 | m_queue.pop(); 51 | return item; 52 | } 53 | 54 | private: 55 | _QueueT m_queue; 56 | std::mutex x_mutex; 57 | std::condition_variable m_cv; 58 | }; 59 | 60 | } // namespace dev 61 | -------------------------------------------------------------------------------- /evm/libdevcore/db.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include "Exceptions.h" 8 | #include "dbfwd.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace dev 14 | { 15 | namespace db 16 | { 17 | // WriteBatchFace implements database write batch for a specific concrete 18 | // database implementation. 19 | class WriteBatchFace 20 | { 21 | public: 22 | virtual ~WriteBatchFace() = default; 23 | 24 | virtual void insert(Slice _key, Slice _value) = 0; 25 | virtual void kill(Slice _key) = 0; 26 | 27 | protected: 28 | WriteBatchFace() = default; 29 | // Noncopyable 30 | WriteBatchFace(WriteBatchFace const&) = delete; 31 | WriteBatchFace& operator=(WriteBatchFace const&) = delete; 32 | // Nonmovable 33 | WriteBatchFace(WriteBatchFace&&) = delete; 34 | WriteBatchFace& operator=(WriteBatchFace&&) = delete; 35 | }; 36 | 37 | class DatabaseFace 38 | { 39 | public: 40 | virtual ~DatabaseFace() = default; 41 | virtual std::string lookup(Slice _key) const = 0; 42 | virtual bool exists(Slice _key) const = 0; 43 | virtual void insert(Slice _key, Slice _value) = 0; 44 | virtual void kill(Slice _key) = 0; 45 | 46 | virtual std::unique_ptr createWriteBatch() const = 0; 47 | virtual void commit(std::unique_ptr _batch) = 0; 48 | 49 | // A database must implement the `forEach` method that allows the caller 50 | // to pass in a function `f`, which will be called with the key and value 51 | // of each record in the database. If `f` returns false, the `forEach` 52 | // method must return immediately. 53 | virtual void forEach(std::function f) const = 0; 54 | }; 55 | 56 | DEV_SIMPLE_EXCEPTION(DatabaseError); 57 | 58 | enum class DatabaseStatus 59 | { 60 | Ok, 61 | NotFound, 62 | Corruption, 63 | NotSupported, 64 | InvalidArgument, 65 | IOError, 66 | Unknown 67 | }; 68 | 69 | using errinfo_dbStatusCode = boost::error_info; 70 | using errinfo_dbStatusString = boost::error_info; 71 | 72 | } // namespace db 73 | } // namespace dev 74 | -------------------------------------------------------------------------------- /evm/libdevcore/dbfwd.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | #pragma once 5 | 6 | #include "vector_ref.h" 7 | 8 | namespace dev 9 | { 10 | namespace db 11 | { 12 | using Slice = vector_ref; 13 | class WriteBatchFace; 14 | class DatabaseFace; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /evm/libethcore/BasicAuthority.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2015-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "SealEngine.h" 10 | 11 | namespace dev 12 | { 13 | namespace eth 14 | { 15 | 16 | class BasicAuthority: public SealEngineBase 17 | { 18 | public: 19 | static std::string name() { return "BasicAuthority"; } 20 | unsigned revision() const override { return 0; } 21 | unsigned sealFields() const override { return 1; } 22 | bytes sealRLP() const override { return rlp(Signature()); } 23 | 24 | void populateFromParent(BlockHeader&, BlockHeader const&) const override; 25 | StringHashMap jsInfo(BlockHeader const& _bi) const override; 26 | void verify(Strictness _s, BlockHeader const& _bi, BlockHeader const& _parent, bytesConstRef _block) const override; 27 | bool shouldSeal(Interface*) override; 28 | void generateSeal(BlockHeader const& _bi) override; 29 | 30 | static Signature sig(BlockHeader const& _bi) { return _bi.seal(); } 31 | static BlockHeader& setSig(BlockHeader& _bi, Signature const& _sig) { _bi.setSeal(_sig); return _bi; } 32 | void setSecret(Secret const& _s) { m_secret = _s; } 33 | static void init(); 34 | 35 | private: 36 | bool onOptionChanging(std::string const& _name, bytes const& _value) override; 37 | 38 | Secret m_secret; 39 | AddressHash m_authorities; 40 | }; 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /evm/libethcore/ChainOperationParams.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | #include "Common.h" 12 | #include "EVMSchedule.h" 13 | 14 | namespace dev 15 | { 16 | namespace eth 17 | { 18 | 19 | class PrecompiledContract 20 | { 21 | public: 22 | PrecompiledContract() = default; 23 | PrecompiledContract( 24 | PrecompiledPricer const& _cost, 25 | PrecompiledExecutor const& _exec, 26 | u256 const& _startingBlock = 0 27 | ): 28 | m_cost(_cost), 29 | m_execute(_exec), 30 | m_startingBlock(_startingBlock) 31 | {} 32 | PrecompiledContract( 33 | unsigned _base, 34 | unsigned _word, 35 | PrecompiledExecutor const& _exec, 36 | u256 const& _startingBlock = 0 37 | ); 38 | 39 | bigint cost( 40 | bytesConstRef _in, ChainOperationParams const& _chainParams, u256 const& _blockNumber) const 41 | { 42 | return m_cost(_in, _chainParams, _blockNumber); 43 | } 44 | std::pair execute(bytesConstRef _in) const { return m_execute(_in); } 45 | 46 | u256 const& startingBlock() const { return m_startingBlock; } 47 | 48 | private: 49 | PrecompiledPricer m_cost; 50 | PrecompiledExecutor m_execute; 51 | u256 m_startingBlock = 0; 52 | }; 53 | 54 | constexpr int64_t c_infiniteBlockNumber = std::numeric_limits::max(); 55 | 56 | struct ChainOperationParams 57 | { 58 | ChainOperationParams(); 59 | 60 | explicit operator bool() const { return accountStartNonce != Invalid256; } 61 | 62 | /// The chain sealer name: e.g. Ethash, NoProof, BasicAuthority 63 | std::string sealEngineName = "NoProof"; 64 | 65 | /// General chain params. 66 | private: 67 | u256 m_blockReward; 68 | public: 69 | EVMSchedule const& scheduleForBlockNumber(u256 const& _blockNumber) const; 70 | u256 blockReward(EVMSchedule const& _schedule) const; 71 | void setBlockReward(u256 const& _newBlockReward); 72 | u256 maximumExtraDataSize = 32; 73 | u256 accountStartNonce = 0; 74 | bool tieBreakingGas = true; 75 | u256 minGasLimit; 76 | u256 maxGasLimit; 77 | u256 gasLimitBoundDivisor; 78 | u256 homesteadForkBlock = c_infiniteBlockNumber; 79 | u256 EIP150ForkBlock = c_infiniteBlockNumber; 80 | u256 EIP158ForkBlock = c_infiniteBlockNumber; 81 | u256 byzantiumForkBlock = c_infiniteBlockNumber; 82 | u256 eWASMForkBlock = c_infiniteBlockNumber; 83 | u256 constantinopleForkBlock = c_infiniteBlockNumber; 84 | u256 constantinopleFixForkBlock = c_infiniteBlockNumber; 85 | u256 daoHardforkBlock = c_infiniteBlockNumber; 86 | u256 experimentalForkBlock = c_infiniteBlockNumber; 87 | u256 istanbulForkBlock = c_infiniteBlockNumber; 88 | u256 berlinForkBlock = c_infiniteBlockNumber; 89 | int chainID = 0; // Distinguishes different chains (mainnet, Ropsten, etc). 90 | int networkID = 0; // Distinguishes different sub protocols. 91 | 92 | u256 minimumDifficulty; 93 | u256 difficultyBoundDivisor; 94 | u256 durationLimit; 95 | bool allowFutureBlocks = false; 96 | 97 | /// Precompiled contracts as specified in the chain params. 98 | std::unordered_map precompiled; 99 | }; 100 | 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /evm/libethcore/CommonJS.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include "Common.h" 12 | 13 | // devcrypto 14 | 15 | namespace dev 16 | { 17 | 18 | /// Leniently convert string to Public (h512). Accepts integers, "0x" prefixing, non-exact length. 19 | inline Public jsToPublic(std::string const& _s) { return jsToFixed(_s); } 20 | 21 | /// Leniently convert string to Secret (h256). Accepts integers, "0x" prefixing, non-exact length. 22 | inline Secret jsToSecret(std::string const& _s) { h256 d = jsToFixed(_s); Secret ret(d); d.ref().cleanse(); return ret; } 23 | 24 | /// Leniently convert string to Address (h160). Accepts integers, "0x" prefixing, non-exact length. 25 | inline Address jsToAddress(std::string const& _s) { return eth::toAddress(_s); } 26 | 27 | /// Convert u256 into user-readable string. Returns int/hex value of 64 bits int, hex of 160 bits FixedHash. As a fallback try to handle input as h256. 28 | std::string prettyU256(u256 _n, bool _abridged = true); 29 | 30 | } 31 | 32 | 33 | // ethcore 34 | namespace dev 35 | { 36 | namespace eth 37 | { 38 | 39 | /// Convert to a block number, a bit like jsToInt, except that it correctly recognises "pending" and "latest". 40 | BlockNumber jsToBlockNumber(std::string const& _js); 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /evm/libethcore/LogEntry.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2017-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | #pragma once 5 | 6 | #include "Common.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace dev 12 | { 13 | 14 | class RLP; 15 | class RLPStream; 16 | 17 | namespace eth 18 | { 19 | 20 | struct LogEntry 21 | { 22 | LogEntry() = default; 23 | explicit LogEntry(RLP const& _r); 24 | LogEntry(Address const& _address, h256s _topics, bytes _data): 25 | address(_address), topics(std::move(_topics)), data(std::move(_data)) 26 | {} 27 | 28 | void streamRLP(RLPStream& _s) const; 29 | 30 | LogBloom bloom() const; 31 | 32 | Address address; 33 | h256s topics; 34 | bytes data; 35 | }; 36 | 37 | using LogEntries = std::vector; 38 | 39 | struct LocalisedLogEntry: public LogEntry 40 | { 41 | LocalisedLogEntry() = default; 42 | explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {} 43 | 44 | LocalisedLogEntry(LogEntry const& _le, h256 _special): 45 | LogEntry(_le), 46 | isSpecial(true), 47 | special(_special) 48 | {} 49 | 50 | LocalisedLogEntry( 51 | LogEntry const& _le, 52 | h256 const& _blockHash, 53 | BlockNumber _blockNumber, 54 | h256 const& _transactionHash, 55 | unsigned _transactionIndex, 56 | unsigned _logIndex, 57 | BlockPolarity _polarity = BlockPolarity::Unknown 58 | ): 59 | LogEntry(_le), 60 | blockHash(_blockHash), 61 | blockNumber(_blockNumber), 62 | transactionHash(_transactionHash), 63 | transactionIndex(_transactionIndex), 64 | logIndex(_logIndex), 65 | polarity(_polarity), 66 | mined(true) 67 | {} 68 | 69 | h256 blockHash; 70 | BlockNumber blockNumber = 0; 71 | h256 transactionHash; 72 | unsigned transactionIndex = 0; 73 | unsigned logIndex = 0; 74 | BlockPolarity polarity = BlockPolarity::Unknown; 75 | bool mined = false; 76 | bool isSpecial = false; 77 | h256 special; 78 | }; 79 | 80 | using LocalisedLogEntries = std::vector; 81 | 82 | inline LogBloom bloom(LogEntries const& _logs) 83 | { 84 | LogBloom ret; 85 | for (auto const& l: _logs) 86 | ret |= l.bloom(); 87 | return ret; 88 | } 89 | 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /evm/libethcore/Precompiled.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2015-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace dev 14 | { 15 | namespace eth 16 | { 17 | struct ChainOperationParams; 18 | 19 | using PrecompiledExecutor = std::function(bytesConstRef _in)>; 20 | using PrecompiledPricer = std::function; 22 | 23 | DEV_SIMPLE_EXCEPTION(ExecutorNotFound); 24 | DEV_SIMPLE_EXCEPTION(PricerNotFound); 25 | 26 | class PrecompiledRegistrar 27 | { 28 | public: 29 | /// Get the executor object for @a _name function or @throw ExecutorNotFound if not found. 30 | static PrecompiledExecutor const& executor(std::string const& _name); 31 | 32 | /// Get the price calculator object for @a _name function or @throw PricerNotFound if not found. 33 | static PrecompiledPricer const& pricer(std::string const& _name); 34 | 35 | /// Register an executor. In general just use ETH_REGISTER_PRECOMPILED. 36 | static PrecompiledExecutor registerExecutor(std::string const& _name, PrecompiledExecutor const& _exec) { return (get()->m_execs[_name] = _exec); } 37 | /// Unregister an executor. Shouldn't generally be necessary. 38 | static void unregisterExecutor(std::string const& _name) { get()->m_execs.erase(_name); } 39 | 40 | /// Register a pricer. In general just use ETH_REGISTER_PRECOMPILED_PRICER. 41 | static PrecompiledPricer registerPricer(std::string const& _name, PrecompiledPricer const& _exec) { return (get()->m_pricers[_name] = _exec); } 42 | /// Unregister a pricer. Shouldn't generally be necessary. 43 | static void unregisterPricer(std::string const& _name) { get()->m_pricers.erase(_name); } 44 | 45 | private: 46 | static PrecompiledRegistrar* get() { if (!s_this) s_this = new PrecompiledRegistrar; return s_this; } 47 | 48 | std::unordered_map m_execs; 49 | std::unordered_map m_pricers; 50 | static PrecompiledRegistrar* s_this; 51 | }; 52 | 53 | // TODO: unregister on unload with a static object. 54 | #define ETH_REGISTER_PRECOMPILED(Name) static std::pair __eth_registerPrecompiledFunction ## Name(bytesConstRef _in); static PrecompiledExecutor __eth_registerPrecompiledFactory ## Name = ::dev::eth::PrecompiledRegistrar::registerExecutor(#Name, &__eth_registerPrecompiledFunction ## Name); static std::pair __eth_registerPrecompiledFunction ## Name 55 | #define ETH_REGISTER_PRECOMPILED_PRICER(Name) \ 56 | static bigint __eth_registerPricerFunction##Name( \ 57 | bytesConstRef _in, ChainOperationParams const& _chainParams, u256 const& _blockNumber); \ 58 | static PrecompiledPricer __eth_registerPricerFactory##Name = \ 59 | ::dev::eth::PrecompiledRegistrar::registerPricer( \ 60 | #Name, &__eth_registerPricerFunction##Name); \ 61 | static bigint __eth_registerPricerFunction##Name 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /evm/libevm/EVMC.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace dev 10 | { 11 | namespace eth 12 | { 13 | /// The wrapper implementing the VMFace interface with a EVMC VM as a backend. 14 | class EVMC : public evmc::VM, public VMFace 15 | { 16 | public: 17 | explicit EVMC(evmc_vm* _vm) noexcept; 18 | 19 | owning_bytes_ref exec(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) final; 20 | }; 21 | } // namespace eth 22 | } // namespace dev 23 | -------------------------------------------------------------------------------- /evm/libevm/VMFace.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | #pragma once 5 | 6 | #include "ExtVMFace.h" 7 | #include 8 | #include 9 | 10 | namespace dev 11 | { 12 | namespace eth 13 | { 14 | 15 | struct VMException: Exception {}; 16 | #define ETH_SIMPLE_EXCEPTION_VM(X) struct X: VMException { const char* what() const noexcept override { return #X; } } 17 | ETH_SIMPLE_EXCEPTION_VM(InvalidInstruction); 18 | ETH_SIMPLE_EXCEPTION_VM(BadInstruction); 19 | ETH_SIMPLE_EXCEPTION_VM(BadJumpDestination); 20 | ETH_SIMPLE_EXCEPTION_VM(OutOfGas); 21 | ETH_SIMPLE_EXCEPTION_VM(OutOfStack); 22 | ETH_SIMPLE_EXCEPTION_VM(StackUnderflow); 23 | ETH_SIMPLE_EXCEPTION_VM(DisallowedStateChange); 24 | ETH_SIMPLE_EXCEPTION_VM(BufferOverrun); 25 | 26 | /// Reports VM internal error. This is not based on VMException because it must be handled 27 | /// differently than defined consensus exceptions. 28 | struct InternalVMError : Exception {}; 29 | 30 | /// Error info for EVMC status code. 31 | using errinfo_evmcStatusCode = boost::error_info; 32 | 33 | struct RevertInstruction: VMException 34 | { 35 | explicit RevertInstruction(owning_bytes_ref&& _output) : m_output(std::move(_output)) {} 36 | RevertInstruction(RevertInstruction const&) = delete; 37 | RevertInstruction(RevertInstruction&&) = default; 38 | RevertInstruction& operator=(RevertInstruction const&) = delete; 39 | RevertInstruction& operator=(RevertInstruction&&) = default; 40 | 41 | char const* what() const noexcept override { return "Revert instruction"; } 42 | 43 | owning_bytes_ref&& output() { return std::move(m_output); } 44 | 45 | private: 46 | owning_bytes_ref m_output; 47 | }; 48 | 49 | 50 | /// EVM Virtual Machine interface 51 | class VMFace 52 | { 53 | public: 54 | VMFace() = default; 55 | virtual ~VMFace() = default; 56 | VMFace(VMFace const&) = delete; 57 | VMFace& operator=(VMFace const&) = delete; 58 | 59 | /// VM implementation 60 | virtual owning_bytes_ref exec(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) = 0; 61 | }; 62 | 63 | /// Helpers: 64 | 65 | // Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. 66 | // Currently we just pull out the right (low-order in BE) 160-bits. 67 | inline Address asAddress(u256 _item) 68 | { 69 | return right160(h256(_item)); 70 | } 71 | 72 | inline u256 fromAddress(Address _a) 73 | { 74 | return (u160)_a; 75 | } 76 | 77 | // Checks whether address is in the address range for precompiles according to EIP-1352 78 | inline bool isPrecompiledContract(Address const& _addr) noexcept 79 | { 80 | static Address const c_maxPrecompiledAddress{0xffff}; 81 | return _addr <= c_maxPrecompiledAddress; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /evm/libevm/VMFactory.h: -------------------------------------------------------------------------------- 1 | // Aleth: Ethereum C++ client, tools and libraries. 2 | // Copyright 2014-2019 Aleth Authors. 3 | // Licensed under the GNU General Public License, Version 3. 4 | #pragma once 5 | 6 | #include "VMFace.h" 7 | 8 | #include 9 | 10 | namespace dev 11 | { 12 | namespace eth 13 | { 14 | enum class VMKind 15 | { 16 | Interpreter, 17 | Legacy, 18 | DLL 19 | }; 20 | 21 | /// Returns the EVMC options parsed from command line. 22 | std::vector>& evmcOptions() noexcept; 23 | 24 | /// Provide a set of program options related to VMs. 25 | /// 26 | /// @param _lineLength The line length for description text wrapping, the same as in 27 | /// boost::program_options::options_description::options_description(). 28 | boost::program_options::options_description vmProgramOptions( 29 | unsigned _lineLength = boost::program_options::options_description::m_default_line_length); 30 | 31 | using VMPtr = std::unique_ptr; 32 | 33 | class VMFactory 34 | { 35 | public: 36 | VMFactory() = delete; 37 | ~VMFactory() = delete; 38 | 39 | /// Creates a VM instance of the global kind (controlled by the --vm command line option). 40 | static VMPtr create(); 41 | 42 | /// Creates a VM instance of the kind provided. 43 | static VMPtr create(VMKind _kind); 44 | }; 45 | } // namespace eth 46 | } // namespace dev 47 | -------------------------------------------------------------------------------- /evm/mac/byteswap.h: -------------------------------------------------------------------------------- 1 | // EVMC Wrapper 2 | // Copyright 2021-2024 Diode 3 | // Licensed under the GNU General Public License, Version 3. 4 | #ifndef MAC_BYTESWAP_H_ 5 | #define MAC_BYTESWAP_H_ 6 | 7 | #ifdef __APPLE__ 8 | #include 9 | 10 | #define __bswap_16 OSSwapInt16 11 | #define __bswap_32 OSSwapInt32 12 | #define __bswap_64 OSSwapInt64 13 | 14 | #endif 15 | #endif -------------------------------------------------------------------------------- /evm/support/attributes.h: -------------------------------------------------------------------------------- 1 | /* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. 2 | * Copyright 2018-2019 Pawel Bylica. 3 | * Licensed under the Apache License, Version 2.0. 4 | */ 5 | 6 | #pragma once 7 | 8 | /** inline */ 9 | #if _MSC_VER || __STDC_VERSION__ 10 | #define INLINE inline 11 | #else 12 | #define INLINE 13 | #endif 14 | 15 | /** [[always_inline]] */ 16 | #if _MSC_VER 17 | #define ALWAYS_INLINE __forceinline 18 | #elif defined(__has_attribute) && __STDC_VERSION__ 19 | #if __has_attribute(always_inline) 20 | #define ALWAYS_INLINE __attribute__((always_inline)) 21 | #endif 22 | #endif 23 | #if !defined(ALWAYS_INLINE) 24 | #define ALWAYS_INLINE 25 | #endif 26 | 27 | /** [[no_sanitize()]] */ 28 | #if __clang__ 29 | #define NO_SANITIZE(sanitizer) \ 30 | __attribute__((no_sanitize(sanitizer))) 31 | #else 32 | #define NO_SANITIZE(sanitizer) 33 | #endif 34 | -------------------------------------------------------------------------------- /githooks/post-receive: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | unset GIT_DIR 3 | DIODE=/opt/diode 4 | cd $DIODE && git checkout -f 5 | svc -d $DIODE 6 | svc -u $DIODE 7 | -------------------------------------------------------------------------------- /githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | exec mix format --check-formatted 3 | -------------------------------------------------------------------------------- /guides/emergency_measures.md: -------------------------------------------------------------------------------- 1 | # Emergency Measures 2 | 3 | These are some common measures that can be taken when there is a seeming data corruption on the node. 4 | 5 | ## Disable the EDGE interfaces: 6 | 7 | For this you need a `./remsh` it will disable edge communication and thus reduce error noise: 8 | 9 | ``` 10 | Supervisor.terminate_child(Diode.Supervisor, Network.EdgeV2) 11 | ``` 12 | 13 | ## Delete a couple of peak blocks 14 | 15 | Ensure that the node is down before doing this! 16 | 17 | In case the node does not even startup, but instead bails out with database errors. This is run from bash but requires the `sqlite3` command line tool installed. E.g. in debian/ubuntu you can get it with `sudo apt install sqlite3` 18 | 19 | ``` 20 | sqlite3 data_prod/blockchain.sq3 "DELETE FROM blocks WHERE number > (SELECT MAX(number) FROM blocks) - 5" 21 | rm data_prod/sync.* 22 | ``` 23 | 24 | ## Delete the blockcache 25 | 26 | Ensure that the node is down before doing this! 27 | 28 | In case the bockcache is corrupted go ahead and delete it: 29 | 30 | ``` 31 | rm data_prod/blockcache.* 32 | ``` -------------------------------------------------------------------------------- /lib/base16.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Base16 do 5 | @spec encode(binary() | non_neg_integer(), any()) :: <<_::16, _::_*8>> 6 | def encode(int, bigX \\ true) 7 | 8 | def encode(int, true) when is_integer(int) do 9 | "0X#{Base.encode16(:binary.encode_unsigned(int), case: :lower)}" 10 | end 11 | 12 | def encode(int, false) when is_integer(int) do 13 | "0x#{Base.encode16(:binary.encode_unsigned(int), case: :lower)}" 14 | end 15 | 16 | def encode(hex, _bigX) do 17 | "0x#{Base.encode16(hex, case: :lower)}" 18 | end 19 | 20 | def prefix(some, length) do 21 | case encode(some, false) do 22 | <<"0x", head::binary-size(length), _::binary>> -> 23 | head 24 | 25 | <<"0x", rest::binary>> -> 26 | rest 27 | end 28 | end 29 | 30 | @spec decode(<<_::16, _::_*8>>) :: binary() | non_neg_integer() 31 | def decode(<<"0x", hex::binary>>) do 32 | do_decode(hex) 33 | end 34 | 35 | def decode(<<"0X", hex::binary>>) do 36 | :binary.decode_unsigned(do_decode(hex)) 37 | end 38 | 39 | def decode_int(int) when is_integer(int) do 40 | int 41 | end 42 | 43 | def decode_int(<<"0x", hex::binary>>) do 44 | :binary.decode_unsigned(do_decode(hex)) 45 | end 46 | 47 | defp do_decode("0") do 48 | "\0" 49 | end 50 | 51 | defp do_decode(bin) do 52 | case rem(String.length(bin), 2) do 53 | 0 -> 54 | Base.decode16!(bin, case: :mixed) 55 | 56 | 1 -> 57 | Base.decode16!("0" <> bin, case: :mixed) 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/bench.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Bench do 5 | # MIX_ENV=benchmark mix run benchmark.exs 6 | alias Chain.Transaction 7 | 8 | def create_contract(x, fib) do 9 | wallet = Wallet.new() 10 | miner = Wallet.new() 11 | 12 | state = Chain.State.new() 13 | block = %Chain.Block{header: %Chain.Header{}, coinbase: miner} 14 | 15 | bin = 16 | :binary.encode_unsigned( 17 | 0x6080604052348015600F57600080FD5B506004361060325760003560E01C806303DD1360146037578063C6C2EA17146053575B600080FD5B603D6092565B6040518082815260200191505060405180910390F35B607C60048036036020811015606757600080FD5B81019080803590602001909291905050506098565B6040518082815260200191505060405180910390F35B60005481565B6000806000815480929190600101919050555060B28260B9565B9050919050565B60006001821160CA576001905060E2565B60D46002830360B9565B60DE6001840360B9565B0190505B91905056FEA265627A7A72315820B01601A182A65AA8F84743102AD7F4B9F9C65196F3F193D5790B3EFE1DFF7A2A64736F6C634300050B0032 18 | ) 19 | 20 | contract_acc = Chain.Account.new(code: bin) 21 | 22 | contract_addr = Wallet.address!(Wallet.new()) 23 | 24 | user_acc = 25 | Chain.Account.new( 26 | nonce: 0, 27 | balance: Shell.ether(1000) 28 | ) 29 | 30 | state = 31 | Chain.State.set_account(state, Wallet.address!(wallet), user_acc) 32 | |> Chain.State.set_account(contract_addr, contract_acc) 33 | 34 | tx = Shell.transaction(wallet, contract_addr, "fib", ["uint256"], [fib]) 35 | tx = %{tx | nonce: 0, gasLimit: 100_000_000_000, gasPrice: 0} 36 | 37 | txlist = 38 | Enum.reduce(0..x, [], fn n, txlist -> 39 | [ 40 | %{tx | nonce: tx.nonce + n} 41 | |> Transaction.sign(Wallet.privkey!(wallet)) 42 | | txlist 43 | ] 44 | end) 45 | 46 | {state, Enum.reverse(txlist), block, contract_addr} 47 | end 48 | 49 | def increment({state, txlist, block, _addr}) do 50 | # Method call increment 51 | # state = 52 | Enum.reduce(txlist, state, fn tx, state -> 53 | {:ok, state, _rcpt} = Transaction.apply(tx, block, state) 54 | state 55 | end) 56 | 57 | # # Checking value of i at position 0 58 | # acc = Chain.State.account(state, addr) 59 | # len = Chain.Account.storageInteger(acc, 0) 60 | # IO.puts("#{length(txlist)} == #{len}") 61 | # ^len = length(txlist) 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/bert.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule BertInt do 5 | @moduledoc """ 6 | Binary Erlang Term encoding for internal node-to-node encoding. 7 | """ 8 | @spec encode!(any()) :: binary() 9 | def encode!(term, level \\ 7) do 10 | term 11 | |> :erlang.term_to_binary() 12 | |> zip(level) 13 | end 14 | 15 | def encode_zstd!(term, level \\ 11) do 16 | term 17 | |> :erlang.term_to_binary() 18 | |> :ezstd.compress(level) 19 | end 20 | 21 | def zip(data, level) do 22 | z = :zlib.open() 23 | :ok = :zlib.deflateInit(z, level, :deflated, -15, 9, :default) 24 | b = :zlib.deflate(z, data, :finish) 25 | :zlib.deflateEnd(z) 26 | :zlib.close(z) 27 | :erlang.iolist_to_binary(b) 28 | end 29 | 30 | # Custom impl of :zlib.zip() for faster compression 31 | @spec decode!(binary()) :: any() 32 | def decode!(<<40, 181, 47, 253, _::binary>> = zstd_term) do 33 | :ezstd.decompress(zstd_term) 34 | |> :erlang.binary_to_term() 35 | end 36 | 37 | def decode!(term) do 38 | try do 39 | :zlib.unzip(term) 40 | rescue 41 | [ErlangError, :data_error] -> 42 | term 43 | end 44 | |> :erlang.binary_to_term() 45 | end 46 | end 47 | 48 | defmodule BertExt do 49 | @spec encode!(any()) :: binary() 50 | def encode!(term) do 51 | :erlang.term_to_binary(term_to_binary(term), minor_version: 1) 52 | end 53 | 54 | defp term_to_binary(map) when is_map(map) do 55 | ^map = Map.from_struct(map) 56 | 57 | map 58 | |> Map.to_list() 59 | |> Enum.map(fn {key, value} -> {key, term_to_binary(value)} end) 60 | |> Enum.into(%{}) 61 | end 62 | 63 | defp term_to_binary(list) when is_list(list) do 64 | Enum.map(list, &term_to_binary(&1)) 65 | end 66 | 67 | defp term_to_binary(tuple) when is_tuple(tuple) do 68 | Tuple.to_list(tuple) 69 | |> Enum.map(&term_to_binary(&1)) 70 | |> List.to_tuple() 71 | end 72 | 73 | defp term_to_binary(other) do 74 | other 75 | end 76 | 77 | @spec decode!(binary()) :: any() 78 | def decode!(term) do 79 | :erlang.binary_to_term(term, [:safe]) 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /lib/block_process.ex: -------------------------------------------------------------------------------- 1 | defmodule BlockProcess do 2 | alias Model.ChainSql 3 | alias Chain.Block 4 | require Logger 5 | 6 | use GenServer 7 | defstruct [] 8 | 9 | def start_link() do 10 | GenServer.start_link(__MODULE__, %BlockProcess{}, name: __MODULE__) 11 | end 12 | 13 | @impl true 14 | def init(state) do 15 | EtsLru.new(__MODULE__, 300) 16 | EtsLru.new(__MODULE__.State, Diode.state_cache_size()) 17 | {:ok, state} 18 | end 19 | 20 | def fetch(block_ref, methods) when is_list(methods) do 21 | with_block(block_ref, fn block -> 22 | Enum.map(methods, fn method -> apply(Block, method, [block]) end) 23 | end) 24 | end 25 | 26 | def cache_block(block) do 27 | EtsLru.put(__MODULE__, Block.hash(block), Block.strip_state(block)) 28 | EtsLru.put(__MODULE__.State, Block.hash(block), Chain.State.lock(Block.state(block))) 29 | end 30 | 31 | def with_account_tree(block_ref, account_id, fun) do 32 | with_block(block_ref, fn 33 | nil -> fun.(nil) 34 | block -> fun.(Block.account_tree(block, account_id)) 35 | end) 36 | end 37 | 38 | def with_account(block_ref, account_id, fun) do 39 | with_state(block_ref, fn 40 | nil -> fun.(nil) 41 | state -> fun.(Chain.State.account(state, account_id)) 42 | end) 43 | end 44 | 45 | def with_state(block_ref, fun) do 46 | with_block(block_ref, fn 47 | nil -> nil 48 | block -> fetch_state(Block.hash(block)) 49 | end) 50 | |> fun.() 51 | end 52 | 53 | def fetch_state(block_hash) do 54 | EtsLru.fetch(__MODULE__.State, block_hash, fn -> 55 | ret = ChainSql.state(block_hash) 56 | :erlang.garbage_collect() 57 | :erlang.garbage_collect(Process.whereis(BlockProcess)) 58 | :erlang.garbage_collect(Process.whereis(Model.Sql)) 59 | ret 60 | end) 61 | end 62 | 63 | def with_block(<>, fun) do 64 | if not Chain.block_by_hash?(block_hash) do 65 | with_block(nil, fun) 66 | else 67 | EtsLru.fetch(__MODULE__, block_hash, fn -> 68 | Stats.tc(:sql_block_by_hash, fn -> 69 | Model.ChainSql.block_by_hash(block_hash) 70 | end) 71 | end) 72 | |> fun.() 73 | end 74 | end 75 | 76 | def with_block(%Block{} = block, fun), do: fun.(block) 77 | def with_block("latest", fun), do: with_block(Chain.peak(), fun) 78 | def with_block("pending", fun), do: Chain.Worker.with_candidate(fun) 79 | def with_block("earliest", fun), do: with_block(0, fun) 80 | def with_block(nil, fun), do: fun.(nil) 81 | 82 | def with_block(num, fun) when is_integer(num), 83 | do: with_block(Chain.blockhash(num), fun) 84 | 85 | def with_block(<<"0x", _rest::binary>> = ref, fun) do 86 | if byte_size(ref) >= 66 do 87 | # assuming it's a block hash 88 | with_block(Base16.decode(ref), fun) 89 | else 90 | # assuming it's a block index 91 | with_block(Base16.decode_int(ref), fun) 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/certs.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Certs do 5 | def extract(socket) do 6 | {:ok, cert} = :ssl.peercert(socket) 7 | id_from_der(cert) 8 | end 9 | 10 | def id_from_file(filename) do 11 | pem = :public_key.pem_decode(File.read!(filename)) 12 | cert = :proplists.lookup(:Certificate, pem) 13 | der_cert = :erlang.element(2, cert) 14 | id_from_der(der_cert) 15 | end 16 | 17 | def private_from_file(filename) do 18 | pem = :public_key.pem_decode(File.read!(filename)) 19 | cert = :proplists.lookup(:PrivateKeyInfo, pem) 20 | 21 | :public_key.der_decode(:PrivateKeyInfo, :erlang.element(2, cert)) 22 | |> getfield(:ECPrivateKey, :privateKey) 23 | end 24 | 25 | def id_from_der(der_encoded_cert) do 26 | :public_key.pkix_decode_cert(der_encoded_cert, :otp) 27 | |> getfield(:OTPCertificate, :tbsCertificate) 28 | |> getfield(:OTPTBSCertificate, :subjectPublicKeyInfo) 29 | |> getfield(:OTPSubjectPublicKeyInfo, :subjectPublicKey) 30 | |> getfield(:ECPoint, :point) 31 | |> Secp256k1.compress_public() 32 | end 33 | 34 | @record_defs Record.extract_all(from_lib: "public_key/include/public_key.hrl") 35 | @spec getfield(any(), atom(), atom()) :: any() 36 | defp getfield(record, type, fieldname) do 37 | record_def = @record_defs[type] 38 | Keyword.get(keywords(record_def, record), fieldname) 39 | end 40 | 41 | @spec keywords([any()], any()) :: keyword() 42 | def keywords(record_def, record) do 43 | zip = List.zip([record_def, :lists.seq(1, length(record_def))]) 44 | 45 | Keyword.new(zip, fn {{key, _default}, idx} -> 46 | {key, elem(record, idx)} 47 | end) 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/chain/genesis_factory.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Chain.GenesisFactory do 5 | alias Chain.Account, as: Account 6 | 7 | @spec testnet() :: Chain.Block.t() 8 | def testnet() do 9 | genesis(genesis_accounts(), genesis_transactions(genesis_miner()), genesis_miner()) 10 | end 11 | 12 | def testnet_parent() do 13 | genesis_parent(genesis_state(genesis_accounts()), genesis_miner()) 14 | end 15 | 16 | def genesis_state(accounts) do 17 | Enum.reduce(accounts, Chain.State.new(), fn {address, user_account}, state -> 18 | Chain.State.set_account(state, address, user_account) 19 | end) 20 | end 21 | 22 | @spec genesis_accounts() :: [{binary(), Account.t()}] 23 | def genesis_accounts() do 24 | ChainDefinition.genesis_accounts() 25 | end 26 | 27 | @spec genesis_miner :: Wallet.t() 28 | def genesis_miner() do 29 | ChainDefinition.genesis_miner() 30 | end 31 | 32 | @spec genesis_timestamp() :: non_neg_integer() 33 | def genesis_timestamp() do 34 | ChainDefinition.genesis_timestamp() 35 | end 36 | 37 | @spec genesis_transactions(Wallet.t()) :: [Chain.Transaction.t(), ...] 38 | def genesis_transactions(miner) do 39 | ChainDefinition.genesis_transactions(miner) 40 | end 41 | 42 | @spec genesis_parent(Chain.State.t(), Wallet.t()) :: Chain.Block.t() 43 | def genesis_parent(state, miner) do 44 | %Chain.Block{ 45 | header: %Chain.Header{ 46 | number: -1, 47 | block_hash: ChainDefinition.genesis_pre_hash(), 48 | state_hash: state 49 | }, 50 | coinbase: miner 51 | } 52 | end 53 | 54 | @spec genesis(any(), [Chain.Transaction.t()], Wallet.t()) :: Chain.Block.t() 55 | def genesis(accounts, transactions, miner) do 56 | state = genesis_state(accounts) 57 | parent = genesis_parent(state, miner) 58 | block = Chain.Block.create(parent, transactions, miner, genesis_timestamp()) 59 | header = Chain.Block.header(block) 60 | 61 | header = 62 | %{header | miner_signature: <<0::520>>} 63 | |> Chain.Header.update_hash() 64 | 65 | %{block | header: header} 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/chain/header.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Chain.Header do 5 | import Wallet 6 | 7 | defstruct previous_block: nil, 8 | miner_signature: nil, 9 | block_hash: nil, 10 | state_hash: nil, 11 | transaction_hash: nil, 12 | timestamp: 0, 13 | number: 0, 14 | nonce: 0 15 | 16 | @type t :: %Chain.Header{ 17 | previous_block: binary() | nil, 18 | miner_signature: binary() | nil, 19 | block_hash: binary() | nil, 20 | state_hash: binary() | nil | Chain.State.t(), 21 | transaction_hash: binary() | nil, 22 | timestamp: non_neg_integer(), 23 | number: integer(), 24 | nonce: non_neg_integer() 25 | } 26 | 27 | # egg is everything but the miner_signature and the block hash, it is required to create the miner_signature 28 | defp encode_egg( 29 | %Chain.Header{ 30 | previous_block: prev_block, 31 | transaction_hash: tx_hash, 32 | timestamp: timestamp, 33 | number: number, 34 | nonce: nonce 35 | } = header 36 | ) do 37 | term = [ 38 | prev_block, 39 | state_hash(header), 40 | tx_hash, 41 | timestamp, 42 | number, 43 | nonce 44 | ] 45 | 46 | :erlang.term_to_binary(term, minor_version: 1) 47 | end 48 | 49 | # chicken is everything but the block hash, it is required to create the block hash 50 | defp encode_chicken( 51 | %Chain.Header{ 52 | previous_block: prev_block, 53 | transaction_hash: tx_hash, 54 | timestamp: timestamp, 55 | number: number, 56 | nonce: nonce, 57 | miner_signature: miner_signature 58 | } = header 59 | ) do 60 | term = [ 61 | prev_block, 62 | state_hash(header), 63 | tx_hash, 64 | timestamp, 65 | number, 66 | nonce, 67 | miner_signature 68 | ] 69 | 70 | :erlang.term_to_binary(term, minor_version: 1) 71 | end 72 | 73 | @spec update_hash(Chain.Header.t()) :: Chain.Header.t() 74 | def update_hash(%Chain.Header{} = header) do 75 | %{header | block_hash: Diode.hash(encode_chicken(header))} 76 | end 77 | 78 | @spec sign(Chain.Header.t(), Wallet.t()) :: Chain.Header.t() 79 | def sign(%Chain.Header{} = header, wallet() = miner) do 80 | %{header | miner_signature: Secp256k1.sign(Wallet.privkey!(miner), encode_egg(header))} 81 | end 82 | 83 | def state_hash(%Chain.Header{state_hash: %Chain.State{} = state}), do: Chain.State.hash(state) 84 | def state_hash(%Chain.Header{state_hash: state_hash}), do: state_hash 85 | 86 | def recover_miner(header) do 87 | case :binary.decode_unsigned(header.miner_signature || "") do 88 | 0 -> 89 | Wallet.from_address(<<0::160>>) 90 | 91 | _ -> 92 | Secp256k1.recover!(header.miner_signature, encode_egg(header)) 93 | |> Wallet.from_pubkey() 94 | end 95 | end 96 | 97 | def strip_state(header) do 98 | %{header | state_hash: state_hash(header)} 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /lib/chain/pool.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Chain.Pool do 5 | alias Chain.Transaction 6 | use GenServer 7 | defstruct transactions: %{} 8 | @type t :: %Chain.Pool{} 9 | 10 | @spec start_link(any()) :: :ignore | {:error, any()} | {:ok, pid()} 11 | def start_link(_opts) do 12 | GenServer.start_link(__MODULE__, :ok, name: __MODULE__) 13 | end 14 | 15 | @spec init(any()) :: {:ok, Chain.Pool.t()} 16 | def init(_init_arg) do 17 | {:ok, %Chain.Pool{}} 18 | end 19 | 20 | def remove_transaction(key) do 21 | remove_transactions([key]) 22 | end 23 | 24 | def remove_transactions(%Chain.Block{} = block) do 25 | done = Chain.Block.transactions(block) 26 | keys = Enum.map(done, &Chain.Transaction.hash/1) 27 | remove_transactions(keys) 28 | end 29 | 30 | def remove_transactions(keys) do 31 | call(fn pool = %{transactions: transactions}, _from -> 32 | {:reply, :ok, %{pool | transactions: Map.drop(transactions, keys)}} 33 | end) 34 | end 35 | 36 | @spec add_transaction(Transaction.t(), boolean()) :: Transaction.t() 37 | def add_transaction(%Transaction{} = tx, broadcast \\ false) do 38 | key = Transaction.hash(tx) 39 | 40 | exists = 41 | call(fn pool = %{transactions: old_txs}, _from -> 42 | new_txs = Map.put(old_txs, key, tx) 43 | {:reply, Map.has_key?(old_txs, key), %{pool | transactions: new_txs}} 44 | end) 45 | 46 | if broadcast do 47 | Kademlia.broadcast(tx) 48 | else 49 | if not exists, do: Kademlia.relay(tx) 50 | end 51 | 52 | Chain.Worker.update() 53 | tx 54 | end 55 | 56 | def replace_transaction(old_tx, new_tx) do 57 | old_key = Transaction.hash(old_tx) 58 | new_key = Transaction.hash(new_tx) 59 | 60 | cast(fn pool = %{transactions: transactions} -> 61 | txs = 62 | Map.put(transactions, new_key, new_tx) 63 | |> Map.delete(old_key) 64 | 65 | {:noreply, %{pool | transactions: txs}} 66 | end) 67 | end 68 | 69 | @spec flush() :: [Transaction.t()] 70 | def flush() do 71 | call(fn pool = %{transactions: transactions}, _from -> 72 | {:reply, Map.values(transactions), %{pool | transactions: %{}}} 73 | end) 74 | end 75 | 76 | @doc "Returns the optimal mining proposal" 77 | def proposal() do 78 | limit = Chain.gas_limit() 79 | avg = Chain.average_transaction_gas() 80 | 81 | transactions() 82 | |> Enum.sort(fn a, b -> 83 | Transaction.gas_price(a) < Transaction.gas_price(b) 84 | end) 85 | |> Enum.take(floor(limit / avg * 1.2)) 86 | |> Enum.sort(fn a, b -> Transaction.nonce(a) < Transaction.nonce(b) end) 87 | end 88 | 89 | @spec transactions() :: [Transaction.t()] 90 | def transactions() do 91 | call(fn pool = %{transactions: transactions}, _from -> 92 | {:reply, transactions, pool} 93 | end) 94 | |> Map.values() 95 | end 96 | 97 | defp call(fun, timeout \\ 5000) do 98 | GenServer.call(__MODULE__, fun, timeout) 99 | end 100 | 101 | def handle_call(fun, from, state) when is_function(fun) do 102 | fun.(state, from) 103 | end 104 | 105 | defp cast(fun) do 106 | GenServer.cast(__MODULE__, fun) 107 | end 108 | 109 | def handle_cast(fun, state) when is_function(fun) do 110 | fun.(state) 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /lib/chain/transaction_receipt.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Chain.TransactionReceipt do 5 | defstruct msg: nil, 6 | # state: nil, 7 | evmout: nil, 8 | gas_used: nil, 9 | return_data: nil, 10 | data: nil, 11 | logs: [], 12 | trace: nil 13 | 14 | @type t :: %Chain.TransactionReceipt{ 15 | msg: binary() | :ok | :evmc_revert, 16 | # state: Chain.State.t() | nil, 17 | evmout: any(), 18 | gas_used: non_neg_integer() | nil, 19 | return_data: binary() | nil, 20 | data: binary() | nil, 21 | logs: [] 22 | } 23 | end 24 | -------------------------------------------------------------------------------- /lib/chaindefinition.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule ChainDefinition do 5 | @enforce_keys [ 6 | :block_reward_position, 7 | :chain_id, 8 | :check_window, 9 | :min_diversity, 10 | :min_transaction_fee, 11 | :get_block_hash_limit, 12 | :allow_contract_override, 13 | :double_spend_delegatecall 14 | ] 15 | defstruct @enforce_keys 16 | 17 | @type t :: %ChainDefinition{ 18 | block_reward_position: :last | :first, 19 | chain_id: non_neg_integer(), 20 | check_window: boolean(), 21 | get_block_hash_limit: non_neg_integer(), 22 | min_diversity: non_neg_integer(), 23 | min_transaction_fee: boolean(), 24 | allow_contract_override: boolean(), 25 | double_spend_delegatecall: boolean() 26 | } 27 | 28 | @spec get_block_hash_limit(non_neg_integer) :: non_neg_integer() 29 | def get_block_hash_limit(blockheight) do 30 | chain_definition(blockheight).get_block_hash_limit 31 | end 32 | 33 | @spec min_diversity(non_neg_integer) :: non_neg_integer() 34 | def min_diversity(blockheight) do 35 | chain_definition(blockheight).min_diversity 36 | end 37 | 38 | @spec block_reward_position(non_neg_integer) :: :last | :first 39 | def block_reward_position(blockheight) do 40 | chain_definition(blockheight).block_reward_position 41 | end 42 | 43 | @spec min_transaction_fee(non_neg_integer) :: boolean() 44 | def min_transaction_fee(blockheight) do 45 | chain_definition(blockheight).min_transaction_fee 46 | end 47 | 48 | @spec allow_contract_override(non_neg_integer) :: boolean() 49 | def allow_contract_override(blockheight) do 50 | chain_definition(blockheight).allow_contract_override 51 | end 52 | 53 | @spec double_spend_delegatecall(non_neg_integer) :: boolean() 54 | def double_spend_delegatecall(blockheight) do 55 | chain_definition(blockheight).double_spend_delegatecall 56 | end 57 | 58 | @spec chain_id(non_neg_integer) :: non_neg_integer() 59 | def chain_id(blockheight) do 60 | chain_definition(blockheight).chain_id 61 | end 62 | 63 | @spec check_window(non_neg_integer) :: boolean() 64 | def check_window(blockheight) do 65 | chain_definition(blockheight).check_window 66 | end 67 | 68 | @spec genesis_accounts() :: [{binary(), Chain.Account.t()}] 69 | def genesis_accounts() do 70 | chain_definition().genesis_accounts() 71 | end 72 | 73 | @spec genesis_transactions(Wallet.t()) :: [Chain.Transaction.t()] 74 | def genesis_transactions(miner) do 75 | chain_definition().genesis_transactions(miner) 76 | end 77 | 78 | @spec genesis_timestamp :: non_neg_integer() 79 | def genesis_timestamp() do 80 | chain_definition().genesis_timestamp() 81 | end 82 | 83 | @spec genesis_miner :: Wallet.t() 84 | def genesis_miner() do 85 | chain_definition().genesis_miner() 86 | end 87 | 88 | defp chain_definition() do 89 | :persistent_term.get(:chain_definition, nil) 90 | end 91 | 92 | @spec chain_definition(non_neg_integer()) :: ChainDefinition 93 | defp chain_definition(blockheight) do 94 | chain_definition().network(blockheight) 95 | end 96 | 97 | @spec genesis_pre_hash :: <<_::256>> 98 | def genesis_pre_hash() do 99 | chain_definition().genesis_pre_hash() 100 | end 101 | 102 | @spec hardforks(Chain.Block.t()) :: Chain.Block.t() 103 | def hardforks(sim_block) do 104 | chain_definition().hardforks(sim_block) 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /lib/chaindefinition/stagenet.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule ChainDefinition.Stagenet do 5 | alias ChainDefinition.{Galileo, Ulysses, Voyager, Pioneer} 6 | @voyager 10 7 | @voyager_t1 @voyager - 1 8 | @pioneer 20 9 | @pioneer_t1 @pioneer - 1 10 | @new_horizons 20 11 | @ulysses 30 12 | @ulysses_t1 @ulysses - 1 13 | @ulysses2 40 14 | @galileo 50 15 | @galileo_t1 @galileo - 1 16 | 17 | @spec network(any) :: ChainDefinition.t() 18 | def network(blockheight) when blockheight >= @ulysses2 do 19 | %ChainDefinition{ 20 | network(@ulysses2 - 1) 21 | | double_spend_delegatecall: false 22 | } 23 | end 24 | 25 | def network(blockheight) when blockheight >= @new_horizons do 26 | %ChainDefinition{ 27 | network(@new_horizons - 1) 28 | | allow_contract_override: false 29 | } 30 | end 31 | 32 | def network(blockheight) when blockheight >= @voyager do 33 | %ChainDefinition{ 34 | network(@voyager - 1) 35 | | block_reward_position: :last, 36 | min_transaction_fee: true, 37 | chain_id: 13 38 | } 39 | end 40 | 41 | def network(blockheight) when blockheight < @voyager do 42 | %ChainDefinition{ 43 | block_reward_position: :first, 44 | chain_id: 41042, 45 | check_window: false, 46 | get_block_hash_limit: 131_072, 47 | min_diversity: 0, 48 | min_transaction_fee: false, 49 | allow_contract_override: true, 50 | double_spend_delegatecall: true 51 | } 52 | end 53 | 54 | def hardforks(block) do 55 | case Chain.Block.number(block) do 56 | @galileo_t1 -> 57 | state = Galileo.apply(Chain.Block.state(block)) 58 | Chain.Block.ensure_state(block, state) 59 | 60 | @ulysses_t1 -> 61 | state = Ulysses.apply(Chain.Block.state(block)) 62 | Chain.Block.ensure_state(block, state) 63 | 64 | @voyager_t1 -> 65 | state = Voyager.apply(Chain.Block.state(block)) 66 | Chain.Block.ensure_state(block, state) 67 | 68 | @pioneer_t1 -> 69 | state = Pioneer.apply(Chain.Block.state(block)) 70 | Chain.Block.ensure_state(block, state) 71 | 72 | _ -> 73 | block 74 | end 75 | end 76 | 77 | @spec genesis_accounts() :: [{binary(), Chain.Account.t()}] 78 | def genesis_accounts() do 79 | File.read!("data/genesis.bin") |> Chain.State.from_binary() |> Chain.State.accounts() 80 | end 81 | 82 | @spec genesis_transactions(Wallet.t()) :: [Chain.Transaction.t()] 83 | def genesis_transactions(_miner) do 84 | [] 85 | end 86 | 87 | @spec genesis_pre_hash :: <<_::256>> 88 | def genesis_pre_hash() do 89 | "0Z0Z0Z0Z0Z0Z0Z0Z0Z0Z0Z0Z0Z0Z0Z0Z" 90 | end 91 | 92 | @spec genesis_miner :: Wallet.t() 93 | def genesis_miner() do 94 | Wallet.from_privkey( 95 | <<149, 210, 74, 221, 235, 25, 240, 87, 85, 95, 119, 147, 106, 182, 149, 28, 112, 227, 22, 96 | 173, 235, 134, 113, 172, 125, 222, 191, 154, 120, 128, 129, 203>> 97 | ) 98 | end 99 | 100 | @spec genesis_timestamp :: non_neg_integer() 101 | def genesis_timestamp() do 102 | 1_597_154_008 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /lib/contract/bns.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Contract.BNS do 5 | @moduledoc """ 6 | Wrapper for the BNS contract functions 7 | as needed by the inner workings of the chain 8 | """ 9 | 10 | def address() do 11 | 0xAF60FAA5CD840B724742F1AF116168276112D6A6 12 | |> Hash.to_address() 13 | end 14 | 15 | def resolve_entry(name, blockRef \\ "latest") do 16 | call("ResolveEntry", ["string"], [name], blockRef) 17 | end 18 | 19 | def crash_data() do 20 | "0x2f3f2ae1000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000116265726c696e2d7a65686c656e646f7266000000000000000000000000000000" 21 | end 22 | 23 | def crash() do 24 | code = Base16.decode(crash_data()) 25 | 26 | Shell.raw(Diode.miner(), code, [{:to, address()}]) 27 | |> Shell.call_tx("latest") 28 | end 29 | 30 | def crash_rpc() do 31 | method = "eth_call" 32 | 33 | params = [ 34 | %{ 35 | "data" => crash_data(), 36 | "gasPrice" => "0x0", 37 | "to" => Base16.encode(address()) 38 | }, 39 | "latest" 40 | ] 41 | 42 | # handle_jsonrpc: #{<<"id">> => 2,<<"jsonrpc">> => <<"2.0">>, 43 | # <<"method">> => <<"eth_call">>, 44 | # <<"params">> => 45 | # [#{<<"data">> => 46 | # <<"0x2f3f2ae1000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000116265726c696e2d7a65686c656e646f7266000000000000000000000000000000">>, 47 | # <<"gasPrice">> => <<"0x0">>, 48 | # <<"to">> => 49 | # <<"0xaf60faa5cd840b724742f1af116168276112d6a6">>}, 50 | # <<"latest">>]} 51 | 52 | Network.Rpc.handle_jsonrpc(%{"method" => method, "params" => params, "id" => 1}) 53 | end 54 | 55 | defp call(name, types, values, blockRef) do 56 | {ret, _gas} = Shell.call(address(), name, types, values, blockRef: blockRef) 57 | ret 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/cron.ex: -------------------------------------------------------------------------------- 1 | defmodule Cron do 2 | use GenServer 3 | require Logger 4 | 5 | defmodule Job do 6 | defstruct name: nil, interval: nil, fun: nil, startup: true 7 | end 8 | 9 | def start_link() do 10 | GenServer.start_link(__MODULE__, [], name: __MODULE__) 11 | end 12 | 13 | @impl true 14 | def init(_) do 15 | jobs = [ 16 | %Job{name: "Garbage Collect", interval: :timer.minutes(15), fun: &Diode.garbage_collect/0} 17 | ] 18 | 19 | for job <- jobs do 20 | :timer.send_interval(job.interval, self(), {:execute, job.name, job.fun}) 21 | 22 | if job.startup do 23 | send(self(), {:execute, job.name, job.fun}) 24 | end 25 | end 26 | 27 | {:ok, %{}} 28 | end 29 | 30 | @impl true 31 | def handle_info({:execute, name, fun}, state) do 32 | Debouncer.immediate({__MODULE__, name}, fn -> 33 | Logger.info("Cron: Executing #{name}...") 34 | fun.() 35 | end) 36 | 37 | {:noreply, state} 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/eip712.ex: -------------------------------------------------------------------------------- 1 | defmodule EIP712 do 2 | defstruct [:primary_type, :types, :message] 3 | 4 | def encode(domain_separator, primary_type, type_data, message) do 5 | Hash.keccak_256( 6 | "\x19\x01" <> domain_separator <> hash_struct(primary_type, type_data, message) 7 | ) 8 | end 9 | 10 | # Special case for a flat EIP712 (no nested user types) 11 | def encode(domain_separator, primary_type, message) do 12 | {types, values} = split_fields(message) 13 | encode(domain_separator, primary_type, %{primary_type => types}, values) 14 | end 15 | 16 | def encode_type(primary_type, type_data) do 17 | prefix = encode_single_type(primary_type, Map.get(type_data, primary_type)) 18 | 19 | types = 20 | Enum.map(type_data[primary_type], fn 21 | {_name, type} -> type 22 | {_name, type, _value} -> type 23 | end) 24 | |> Enum.uniq() 25 | 26 | postfix = 27 | Enum.filter(type_data, fn 28 | {name, _} -> name != primary_type and name in types 29 | end) 30 | |> Enum.map(fn {name, fields} -> encode_single_type(name, fields) end) 31 | |> Enum.sort() 32 | |> Enum.join() 33 | 34 | prefix <> postfix 35 | end 36 | 37 | def encode_single_type(type, fields) do 38 | names = 39 | Enum.map(fields, fn 40 | {name, type} -> "#{type} #{name}" 41 | {name, type, _value} -> "#{type} #{name}" 42 | end) 43 | 44 | type <> "(" <> Enum.join(names, ",") <> ")" 45 | end 46 | 47 | def type_hash(primary_type, type_data) when is_map(type_data) do 48 | encode_type(primary_type, type_data) |> Hash.keccak_256() 49 | end 50 | 51 | @spec hash_struct(binary(), [{String.t(), String.t(), any()}]) :: binary() 52 | def hash_struct(primary_type, type_data) do 53 | {types, values} = split_fields(type_data) 54 | hash_struct(primary_type, %{primary_type => types}, values) 55 | end 56 | 57 | def hash_struct(primary_type, type_data, message) do 58 | encode_data = 59 | Enum.map(type_data[primary_type], fn {name, type} when is_binary(type) -> 60 | value = Map.get(message, name) 61 | 62 | cond do 63 | type == "bytes" or type == "string" -> 64 | ABI.encode("bytes32", Hash.keccak_256(value)) 65 | 66 | Map.has_key?(type_data, type) -> 67 | hash_struct(type, type_data, value) 68 | 69 | true -> 70 | ABI.encode(type, value) 71 | end 72 | end) 73 | |> Enum.join() 74 | 75 | Hash.keccak_256(type_hash(primary_type, type_data) <> encode_data) 76 | end 77 | 78 | def hash_domain_separator(name, version, chain_id, verifying_contract) 79 | when is_binary(name) and is_binary(version) and is_integer(chain_id) and 80 | is_binary(verifying_contract) do 81 | hash_struct("EIP712Domain", [ 82 | {"name", "string", name}, 83 | {"version", "string", version}, 84 | {"chainId", "uint256", chain_id}, 85 | {"verifyingContract", "address", verifying_contract} 86 | ]) 87 | end 88 | 89 | defp split_fields(fields) do 90 | {types, values} = 91 | Enum.map(fields, fn {name, type, value} -> 92 | {{name, type}, {name, value}} 93 | end) 94 | |> Enum.unzip() 95 | 96 | {types, Map.new(values)} 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /lib/etslru.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule EtsLru do 5 | @moduledoc """ 6 | Provides Least-Recently-Used Queue with a fixed maximum size 7 | """ 8 | 9 | def new(name, max_size, filter \\ fn _ -> true end) do 10 | name = 11 | case name do 12 | nil -> :ets.new(name, [:public]) 13 | _other -> :ets.new(name, [:named_table, :public]) 14 | end 15 | 16 | :ets.insert(name, {:meta, 0, max_size, filter}) 17 | name 18 | end 19 | 20 | def destroy(name) do 21 | :ets.delete(name) 22 | end 23 | 24 | def put(lru, key, value) do 25 | filter_fun = filter(lru) 26 | 27 | if filter_fun.(value) and get(lru, key) != value do 28 | key = {:key, key} 29 | n = :ets.update_counter(lru, :meta, 1) 30 | 31 | :ets.insert(lru, {key, value, n}) 32 | :ets.insert(lru, {n, key}) 33 | 34 | max_size = :ets.lookup_element(lru, :meta, 3) 35 | del = n - max_size 36 | 37 | if del > 0 do 38 | with [{^del, key}] <- :ets.lookup(lru, del) do 39 | :ets.delete(lru, del) 40 | 41 | with [{^key, _value, ^del}] <- :ets.lookup(lru, key) do 42 | :ets.delete(lru, key) 43 | end 44 | end 45 | end 46 | end 47 | 48 | value 49 | end 50 | 51 | def get(lru, key, default \\ nil) do 52 | case :ets.lookup(lru, {:key, key}) do 53 | [{_key, value, _n}] -> value 54 | [] -> default 55 | end 56 | end 57 | 58 | def delete(lru, key) do 59 | :ets.delete(lru, {:key, key}) 60 | end 61 | 62 | def fetch(lru, key, fun) do 63 | if is_atom(lru) and :ets.whereis(lru) == :undefined do 64 | fun.() 65 | else 66 | case :ets.lookup(lru, {:key, key}) do 67 | [{_key, value, _n}] -> 68 | value 69 | 70 | [] -> 71 | :global.trans({key, self()}, fn -> 72 | fetch_nolock(lru, key, fun) 73 | end) 74 | end 75 | end 76 | end 77 | 78 | def update(lru, key, fun) do 79 | :global.trans({key, self()}, fn -> 80 | put(lru, key, eval(fun)) 81 | end) 82 | end 83 | 84 | def fetch_nolock(lru, key, fun) do 85 | case :ets.lookup(lru, {:key, key}) do 86 | [{_key, value, _n}] -> value 87 | [] -> put(lru, key, eval(fun)) 88 | end 89 | end 90 | 91 | def size(lru) do 92 | total_size = :ets.info(lru, :size) 93 | div(total_size - 1, 2) 94 | end 95 | 96 | def to_list(lru) do 97 | for {{:key, key}, value, _n} <- :ets.tab2list(lru), do: {key, value} 98 | end 99 | 100 | def filter(lru) do 101 | :ets.lookup_element(lru, :meta, 4) 102 | end 103 | 104 | def max_size(lru) do 105 | :ets.lookup_element(lru, :meta, 3) 106 | end 107 | 108 | def set_max_size(lru, max_size) do 109 | :ets.update_element(lru, :meta, [{3, max_size}]) 110 | end 111 | 112 | def flush(lru) do 113 | filter = filter(lru) 114 | max_size = max_size(lru) 115 | :ets.delete_all_objects(lru) 116 | :ets.insert(lru, {:meta, 0, max_size, filter}) 117 | end 118 | 119 | # 120 | # Private functions below 121 | # 122 | 123 | defp eval(fun) when is_function(fun, 0) do 124 | fun.() 125 | end 126 | 127 | defp eval({m, f, a}) do 128 | apply(m, f, a) 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /lib/hash.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Hash do 5 | @spec integer(binary()) :: non_neg_integer() 6 | def integer(hash) do 7 | :binary.decode_unsigned(hash) 8 | end 9 | 10 | def to_bytes32(hash = <<_::256>>) do 11 | hash 12 | end 13 | 14 | def to_bytes32(hash = <<_::160>>) do 15 | <<0::96, hash::binary-size(20)>> 16 | end 17 | 18 | def to_bytes32(hash) when is_integer(hash) do 19 | <> 20 | end 21 | 22 | def printable(nil) do 23 | "nil" 24 | end 25 | 26 | def printable(binary) do 27 | Base16.encode(binary) 28 | end 29 | 30 | def to_address(hash = <<_::160>>) do 31 | hash 32 | end 33 | 34 | def to_address(hash = <<_::256>>) do 35 | binary_part(hash, 12, 20) 36 | end 37 | 38 | def to_address(int) when is_integer(int) do 39 | <> 40 | end 41 | 42 | def keccak_256(string) do 43 | :keccakf1600.hash(:sha3_256, string) 44 | end 45 | 46 | def sha2_256(string) do 47 | :crypto.hash(:sha256, string) 48 | end 49 | 50 | def ripemd160(string) do 51 | :crypto.hash(:ripemd160, string) 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/json.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Json do 5 | def encode!(object, conv \\ []) do 6 | prepare!(object, conv) 7 | |> Poison.encode!() 8 | end 9 | 10 | def prepare!(object, conv \\ []) do 11 | do_encode(object, conv_validate(conv)) 12 | end 13 | 14 | defp conv_validate(conv) do 15 | conv 16 | end 17 | 18 | def decode!(binary) do 19 | {:ok, result} = decode(binary) 20 | result 21 | end 22 | 23 | def decode(binary) do 24 | case Poison.decode(binary) do 25 | {:ok, object} -> 26 | {:ok, do_decode(object)} 27 | # other -> other 28 | end 29 | end 30 | 31 | defp do_encode(map, conv) when is_map(map) do 32 | Enum.into( 33 | Enum.map(Map.to_list(map), fn {key, value} -> 34 | {do_encode(key, conv), do_encode(value, conv)} 35 | end), 36 | %{} 37 | ) 38 | end 39 | 40 | defp do_encode(list, conv) when is_list(list) do 41 | Enum.map(list, &do_encode(&1, conv)) 42 | end 43 | 44 | defp do_encode({:raw, num}, _conv) do 45 | num 46 | end 47 | 48 | defp do_encode(tuple, conv) when is_tuple(tuple) do 49 | Tuple.to_list(tuple) 50 | |> Enum.map(&do_encode(&1, conv)) 51 | end 52 | 53 | defp do_encode(int, _conv) when is_integer(int) and int >= 0 do 54 | case Base16.encode(int, false) do 55 | "0x0" <> rest -> "0x" <> rest 56 | other -> other 57 | end 58 | end 59 | 60 | defp do_encode("", _conv) do 61 | "0x" 62 | end 63 | 64 | defp do_encode(bin, _conv) when is_binary(bin) do 65 | if String.printable?(bin) do 66 | bin 67 | else 68 | Base16.encode(bin, false) 69 | end 70 | end 71 | 72 | defp do_encode(bits, _conv) when is_bitstring(bits) do 73 | for <>, do: if(x == 1, do: "1", else: "0"), into: "" 74 | end 75 | 76 | defp do_encode(other, _conv) do 77 | other 78 | end 79 | 80 | defp do_decode(map) when is_map(map) do 81 | Enum.into( 82 | Enum.map(Map.to_list(map), fn {key, value} -> 83 | {key, do_decode(value)} 84 | end), 85 | %{} 86 | ) 87 | end 88 | 89 | defp do_decode(list) when is_list(list) do 90 | Enum.map(list, &do_decode(&1)) 91 | end 92 | 93 | defp do_decode(bin) when is_binary(bin) do 94 | case bin do 95 | <<"0x", _rest::binary>> -> 96 | Base16.decode(bin) 97 | 98 | <<"0X", _rest::binary>> -> 99 | Base16.decode(bin) 100 | 101 | other -> 102 | other 103 | end 104 | end 105 | 106 | defp do_decode(other) do 107 | other 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /lib/lru.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Lru do 5 | @moduledoc """ 6 | Provides Least-Recently-Used Queue with a fixed maximum size 7 | """ 8 | defstruct queue: :queue.new(), map: %{}, max_size: nil 9 | @type t :: %Lru{queue: any(), map: map(), max_size: integer()} 10 | 11 | @spec new(pos_integer()) :: Lru.t() 12 | def new(max_size) do 13 | %Lru{max_size: max_size} 14 | end 15 | 16 | @spec insert(Lru.t(), any(), any()) :: Lru.t() 17 | def insert(lru, key, value) do 18 | lru = %{lru | queue: :queue.in(key, lru.queue)} 19 | 20 | lru = %{ 21 | lru 22 | | map: 23 | Map.update(lru.map, key, {0, value}, fn {count, _} -> 24 | {count + 1, value} 25 | end) 26 | } 27 | 28 | if map_size(lru.map) > lru.max_size do 29 | shrink(lru) 30 | else 31 | lru 32 | end 33 | end 34 | 35 | @spec get(Lru.t(), any(), any()) :: any() 36 | def get(lru, key, default \\ nil) do 37 | case Map.get(lru.map, key) do 38 | {_, value} -> value 39 | _ -> default 40 | end 41 | end 42 | 43 | @spec fetch(Lru.t(), any(), fun()) :: {Lru.t(), any()} 44 | def fetch(lru, key, filler) do 45 | case Map.get(lru.map, key) do 46 | {_, value} -> 47 | {lru, value} 48 | 49 | _ -> 50 | case filler.() do 51 | nil -> {lru, nil} 52 | value -> {insert(lru, key, value), value} 53 | end 54 | end 55 | end 56 | 57 | @spec size(Lru.t()) :: non_neg_integer() 58 | def size(lru) do 59 | map_size(lru.map) 60 | end 61 | 62 | defp shrink(lru) do 63 | {{:value, key}, queue} = :queue.out(lru.queue) 64 | lru = %{lru | queue: queue} 65 | 66 | case Map.get(lru.map, key) do 67 | {0, _} -> 68 | %{lru | map: Map.delete(lru.map, key)} 69 | 70 | {counter, value} -> 71 | shrink(%{lru | map: Map.put(lru.map, key, {counter - 1, value})}) 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/mix/tasks/export.ex: -------------------------------------------------------------------------------- 1 | defmodule Mix.Tasks.Export do 2 | use Mix.Task 3 | alias Model.ChainSql 4 | 5 | @impl Mix.Task 6 | def run([from, to, destination]) do 7 | :persistent_term.put(:env, :prod) 8 | from = String.to_integer(from) 9 | to = String.to_integer(to) 10 | {:ok, db} = Sqlitex.Server.start_link(destination) 11 | 12 | ChainSql.init_tables(db) 13 | {:ok, []} = Sqlitex.Server.query(db, "ATTACH \"data_prod/blockchain.sq3\" AS src") 14 | 15 | import_loop(db, from, to) 16 | end 17 | 18 | def run(_) do 19 | Mix.shell().info(""" 20 | SYNTAX: 21 | mix export 22 | """) 23 | end 24 | 25 | def import_loop(db, from, to) do 26 | if from + 1000 < to do 27 | do_import(db, from, from + 1000) 28 | import_loop(db, from + 1001, to) 29 | else 30 | do_import(db, from, to) 31 | end 32 | end 33 | 34 | def do_import(db, from, to) do 35 | Mix.shell().info("Processing blocks #{from}-#{to}") 36 | 37 | {:ok, []} = 38 | Sqlitex.Server.query( 39 | db, 40 | "REPLACE INTO blocks SELECT * FROM src.blocks WHERE ?1 <= number AND number <= ?2", 41 | bind: [from, to] 42 | ) 43 | 44 | {:ok, []} = 45 | Sqlitex.Server.query( 46 | db, 47 | "REPLACE INTO transactions SELECT txs.* FROM src.transactions AS txs JOIN src.blocks ON (blocks.hash = txs.blhash) WHERE ?1 <= number AND number <= ?2", 48 | bind: [from, to] 49 | ) 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/mix/tasks/git_version.ex: -------------------------------------------------------------------------------- 1 | defmodule Mix.Tasks.GitVersion do 2 | use Mix.Task 3 | 4 | @impl Mix.Task 5 | def run(args) do 6 | case System.cmd("git", ["describe", "--tags", "--dirty"]) do 7 | {version, 0} -> 8 | Regex.run(~r/v([0-9]+\.[0-9]+\.[0-9]+)(-.*)?/, version) 9 | |> case do 10 | [full_vsn, vsn | _rest] -> 11 | :persistent_term.put(:vsn, vsn) 12 | 13 | bin = original = File.read!("mix.exs") 14 | bin = Regex.replace(~r/\@vsn .*/, bin, "@vsn \"#{vsn}\"", global: false) 15 | 16 | bin = 17 | Regex.replace(~r/\@full_vsn .*/, bin, "@full_vsn \"#{full_vsn}\"", global: false) 18 | 19 | if bin != original, do: File.write!("mix.exs", bin) 20 | 21 | other -> 22 | :io.format("Couldn't parse version ~p~n", [other]) 23 | end 24 | 25 | other -> 26 | :io.format("Couldn't check git version ~p~n", [other]) 27 | end 28 | 29 | Mix.shell().info(Enum.join(args, " ")) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/mix/tasks/import.ex: -------------------------------------------------------------------------------- 1 | defmodule Mix.Tasks.Import do 2 | use Mix.Task 3 | 4 | @impl Mix.Task 5 | def run([from, to, source]) do 6 | :persistent_term.put(:env, :prod) 7 | from = String.to_integer(from) 8 | to = String.to_integer(to) 9 | {:ok, db} = Sqlitex.Server.start_link("data_prod/blockchain.sq3") 10 | {:ok, []} = Sqlitex.Server.query(db, "ATTACH ?1 AS src", bind: [source]) 11 | 12 | Mix.Tasks.Export.import_loop(db, from, to) 13 | end 14 | 15 | def run(_) do 16 | Mix.shell().info(""" 17 | SYNTAX: 18 | mix import 19 | """) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/model/credsql.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Model.CredSql do 5 | alias Model.Sql 6 | use GenServer 7 | require Logger 8 | 9 | require Record 10 | Record.defrecord(:key_value, key: nil, value: nil) 11 | 12 | @spec start_link([]) :: :ignore | {:error, any()} | {:ok, pid()} 13 | def start_link([]) do 14 | GenServer.start_link(__MODULE__, :ok, name: __MODULE__) 15 | end 16 | 17 | defp query!(sql, params) do 18 | Sql.query!(__MODULE__, sql, params) 19 | end 20 | 21 | defp with_transaction(fun) do 22 | Sql.with_transaction(__MODULE__, fun) 23 | end 24 | 25 | def init(_args) do 26 | with_transaction(fn db -> 27 | Sql.query!(db, """ 28 | CREATE TABLE IF NOT EXISTS config ( 29 | key TEXT PRIMARY KEY, 30 | value BLOB 31 | ) 32 | """) 33 | end) 34 | 35 | case Diode.get_env_int("PRIVATE", 0) do 36 | # Decode env parameter such as 37 | # export PRIVATE="0x123456789" 38 | 0 -> 39 | :ok 40 | 41 | private -> 42 | :binary.encode_unsigned(private) 43 | |> Wallet.from_privkey() 44 | |> set_wallet() 45 | end 46 | 47 | Diode.puts("====== Coinbase ======") 48 | Diode.puts("#{Wallet.printable(Diode.miner())}") 49 | Diode.puts("") 50 | 51 | {:ok, %{}} 52 | end 53 | 54 | defp ensure_identity() do 55 | case :persistent_term.get(:identity, nil) do 56 | nil -> 57 | id = ensure_config("identity", fn -> Secp256k1.generate() end) 58 | :persistent_term.put(:identity, id) 59 | id 60 | 61 | id -> 62 | id 63 | end 64 | end 65 | 66 | def put_config(key, value) when is_binary(key) do 67 | value_data = BertInt.encode!(value) 68 | query!("REPLACE INTO config (key, value) VALUES(?1, ?2)", bind: [key, value_data]) 69 | value 70 | end 71 | 72 | def config(key) when is_binary(key) do 73 | Sql.fetch!(__MODULE__, "SELECT value FROM config WHERE key = ?1", key) 74 | end 75 | 76 | def ensure_config(key, fallback) do 77 | case config(key) do 78 | nil -> put_config(key, fallback.()) 79 | other -> other 80 | end 81 | end 82 | 83 | def set_wallet(wallet) do 84 | id = {Wallet.pubkey!(wallet), Wallet.privkey!(wallet)} 85 | put_config("identity", id) 86 | :persistent_term.put(:identity, id) 87 | end 88 | 89 | def wallet() do 90 | {_public, private} = ensure_identity() 91 | Wallet.from_privkey(private) 92 | end 93 | 94 | def handle_info({:nodeup, node}, state) do 95 | Logger.warning("Node #{inspect(node)} UP") 96 | {:noreply, state} 97 | end 98 | 99 | def handle_info({:nodedown, node}, state) do 100 | Logger.warning("Node #{inspect(node)} DOWN") 101 | {:noreply, state} 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /lib/model/ets.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Model.Ets do 5 | def init(name, extra \\ []) do 6 | ^name = :ets.new(name, [:named_table, :public] ++ extra) 7 | end 8 | 9 | def clear(name) do 10 | :ets.delete_all_objects(name) 11 | end 12 | 13 | def put(name, idx, item) do 14 | :ets.insert(name, {idx, item}) 15 | end 16 | 17 | def remove(name, idx) do 18 | :ets.delete(name, idx) 19 | end 20 | 21 | def to_list(name) do 22 | :ets.tab2list(name) 23 | end 24 | 25 | def keys(name) do 26 | :ets.select(name, [{{:"$1", :"$2"}, [], [:"$1"]}]) 27 | end 28 | 29 | def lookup(name, idx, default \\ fn -> nil end) do 30 | case :ets.lookup(name, idx) do 31 | [] -> default.() 32 | [{^idx, item}] -> item 33 | end 34 | end 35 | 36 | def size(name) do 37 | :ets.info(name, :size) 38 | end 39 | 40 | def member?(name, idx) do 41 | :ets.member(name, idx) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/model/file.ex: -------------------------------------------------------------------------------- 1 | defmodule Model.File do 2 | def load(filename, default \\ nil) do 3 | case File.read(filename) do 4 | {:ok, content} -> 5 | BertInt.decode!(content) 6 | 7 | {:error, _} -> 8 | case default do 9 | fun when is_function(fun) -> fun.() 10 | _ -> default 11 | end 12 | end 13 | end 14 | 15 | def store(filename, term, overwrite \\ false) do 16 | if overwrite or not File.exists?(filename) do 17 | content = BertInt.encode!(term) 18 | 19 | with :ok <- File.mkdir_p(Path.dirname(filename)) do 20 | tmp = "#{filename}.#{:erlang.phash2(self())}" 21 | File.write!(tmp, content) 22 | File.rename!(tmp, filename) 23 | end 24 | end 25 | 26 | term 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/model/kademliasql.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Model.KademliaSql do 5 | alias Model.Sql 6 | 7 | def query!(sql, params \\ []) do 8 | Sql.query!(__MODULE__, sql, params) 9 | end 10 | 11 | defp with_transaction(fun) do 12 | Sql.with_transaction(__MODULE__, fun) 13 | end 14 | 15 | def init() do 16 | with_transaction(fn db -> 17 | Sql.query!(db, """ 18 | CREATE TABLE IF NOT EXISTS p2p_objects ( 19 | key BLOB PRIMARY KEY, 20 | object BLOB 21 | ) 22 | """) 23 | end) 24 | end 25 | 26 | def clear() do 27 | query!("DELETE FROM p2p_objects") 28 | end 29 | 30 | def append!(_key, _value) do 31 | throw(:not_implemented) 32 | end 33 | 34 | def maybe_update_object(key, encoded_object) when is_binary(encoded_object) do 35 | object = Object.decode!(encoded_object) 36 | maybe_update_object(key, object) 37 | end 38 | 39 | def maybe_update_object(key, object) when is_tuple(object) do 40 | # Checking that we got a valid object 41 | with hkey when hkey != nil <- Object.key_hash(object), 42 | true <- key == nil or key == hkey do 43 | case object(hkey) do 44 | nil -> 45 | put_object(hkey, Object.encode!(object)) 46 | 47 | existing -> 48 | existing_object = Object.decode!(existing) 49 | 50 | if Object.block_number(existing_object) < Object.block_number(object) do 51 | put_object(hkey, Object.encode!(object)) 52 | end 53 | end 54 | end 55 | end 56 | 57 | def put_object(key, object) do 58 | object = BertInt.encode!(object) 59 | query!("REPLACE INTO p2p_objects (key, object) VALUES(?1, ?2)", bind: [key, object]) 60 | end 61 | 62 | def delete_object(key) do 63 | query!("DELETE FROM p2p_objects WHERE key = ?1", bind: [key]) 64 | end 65 | 66 | def object(key) do 67 | Sql.fetch!(__MODULE__, "SELECT object FROM p2p_objects WHERE key = ?1", key) 68 | end 69 | 70 | def scan() do 71 | query!("SELECT key, object FROM p2p_objects") 72 | |> Enum.map(fn [key: key, object: obj] -> 73 | obj = BertInt.decode!(obj) |> Object.decode!() 74 | {key, obj} 75 | end) 76 | end 77 | 78 | @spec objects(integer, integer) :: any 79 | def objects(range_start, range_end) do 80 | bstart = <> 81 | bend = <> 82 | 83 | if range_start < range_end do 84 | query!("SELECT key, object FROM p2p_objects WHERE key >= ?1 AND key <= ?2", 85 | bind: [bstart, bend] 86 | ) 87 | else 88 | query!("SELECT key, object FROM p2p_objects WHERE key >= ?1 OR key <= ?2", 89 | bind: [bstart, bend] 90 | ) 91 | end 92 | |> Enum.map(fn [key: key, object: obj] -> {key, BertInt.decode!(obj)} end) 93 | |> Enum.filter(fn {key, value} -> 94 | # After a chain fork some signatures might have become invalid 95 | hash = 96 | Object.decode!(value) 97 | |> Object.key_hash() 98 | 99 | if hash == nil or key != hash do 100 | query!("DELETE FROM p2p_objects WHERE key = ?1", bind: [key]) 101 | false 102 | else 103 | true 104 | end 105 | end) 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /lib/network/rpc_http.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Network.RpcHttp do 5 | use Plug.Router 6 | 7 | plug( 8 | Plug.Parsers, 9 | parsers: [:urlencoded, :json], 10 | json_decoder: Poison 11 | ) 12 | 13 | plug(:match) 14 | plug(:dispatch) 15 | 16 | defp cors(conn) do 17 | headers = %{ 18 | # "access-control-allow-credentials" => "true", 19 | "access-control-allow-origin" => "*", 20 | "access-control-allow-methods" => "POST, GET", 21 | "access-control-allow-headers" => "Content-Type", 22 | # "access-control-expose-headers" => "content-type", 23 | "content-type" => "application/json" 24 | } 25 | 26 | conn 27 | |> merge_resp_headers(headers) 28 | end 29 | 30 | options "/" do 31 | cors(conn) |> send_resp(204, "") 32 | end 33 | 34 | post "/" do 35 | conn = cors(conn) 36 | local = is_local(conn.remote_ip) 37 | {status, body} = Network.Rpc.handle_jsonrpc(conn.body_params, private: local) 38 | 39 | send_resp(conn, status, Poison.encode!(body)) 40 | end 41 | 42 | match _ do 43 | send_resp(conn, 404, Poison.encode!("not found")) 44 | end 45 | 46 | defp is_local({127, 0, 0, _any}), do: true 47 | defp is_local(_conn), do: false 48 | end 49 | -------------------------------------------------------------------------------- /lib/object/server.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Object.Server do 5 | require Record 6 | @behaviour Object 7 | 8 | Record.defrecord(:server, 9 | host: nil, 10 | edge_port: nil, 11 | peer_port: nil, 12 | version: nil, 13 | extra: nil, 14 | signature: nil 15 | ) 16 | 17 | @type server :: 18 | record(:server, 19 | host: binary(), 20 | edge_port: integer(), 21 | peer_port: integer(), 22 | version: binary(), 23 | extra: [], 24 | # extra: [[binary(), non_neg_integer()]], 25 | # Forward compatible place for new data here 26 | signature: Secp256k1.signature() 27 | ) 28 | 29 | def new(host, edge_port, peer_port, version, extra \\ []) do 30 | server(host: host, peer_port: peer_port, edge_port: edge_port, version: version, extra: extra) 31 | end 32 | 33 | @impl true 34 | @spec key(server()) :: Object.key() 35 | def key(serv) do 36 | Secp256k1.recover!(signature(serv), message(serv)) 37 | |> Wallet.from_pubkey() 38 | |> Wallet.address!() 39 | end 40 | 41 | @impl true 42 | def block_number(serv) do 43 | Enum.find_value(extra(serv), 0, fn [key, value] -> 44 | if key == "block" do 45 | value 46 | end 47 | end) || -1 48 | end 49 | 50 | @impl true 51 | def valid?(_serv) do 52 | # validity is given by the correct key value 53 | true 54 | end 55 | 56 | def sign(serv, private) do 57 | len = tuple_size(serv) 58 | put_elem(serv, len - 1, Secp256k1.sign(private, message(serv))) 59 | end 60 | 61 | def host(serv) do 62 | elem(serv, 1) 63 | end 64 | 65 | def edge_port(serv) do 66 | elem(serv, 2) 67 | end 68 | 69 | def peer_port(serv) do 70 | elem(serv, 3) 71 | end 72 | 73 | def version(serv) do 74 | elem(serv, 4) 75 | end 76 | 77 | def extra(serv) do 78 | if tuple_size(serv) > 5 do 79 | elem(serv, 5) 80 | else 81 | [] 82 | end 83 | end 84 | 85 | def signature(serv) do 86 | len = tuple_size(serv) 87 | elem(serv, len - 1) 88 | end 89 | 90 | def uri(serv) do 91 | host = host(serv) 92 | port = peer_port(serv) 93 | key = Base16.encode(key(serv)) 94 | "diode://#{key}@#{host}:#{port}" 95 | end 96 | 97 | defp message(serv) do 98 | Tuple.to_list(serv) 99 | |> Enum.slice(1, tuple_size(serv) - 2) 100 | |> BertExt.encode!() 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib/precompiles.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule PreCompiles do 5 | def get(1), do: &ecrecover/2 6 | def get(2), do: &sha256/2 7 | def get(3), do: &ripemd160/2 8 | def get(4), do: ©/2 9 | # 6 => &b256Add/2, 10 | # 7 => &b256Mul/2, 11 | # 8 => &b256Pairing/2, 12 | def get(_), do: nil 13 | 14 | def ecrecover(:gas, _bytes) do 15 | 3000 16 | end 17 | 18 | def ecrecover( 19 | :run, 20 | <> 21 | ) do 22 | <> = signature 23 | 24 | try do 25 | Secp256k1.rlp_to_bitcoin(recid, r, s) 26 | |> Secp256k1.recover!(digest, :none) 27 | |> Wallet.from_pubkey() 28 | |> Wallet.address!() 29 | rescue 30 | _ -> <<0::160>> 31 | end 32 | end 33 | 34 | def sha256(:gas, bytes) do 35 | div(byte_size(bytes) + 31, 32) * 12 + 60 36 | end 37 | 38 | def sha256(:run, bytes) do 39 | Hash.sha2_256(bytes) 40 | end 41 | 42 | def ripemd160(:gas, bytes) do 43 | div(byte_size(bytes) + 31, 32) * 120 + 600 44 | end 45 | 46 | def ripemd160(:run, bytes) do 47 | Hash.ripemd160(bytes) 48 | end 49 | 50 | def copy(:gas, bytes) do 51 | div(byte_size(bytes) + 31, 32) * 3 + 15 52 | end 53 | 54 | def copy(:run, bytes) do 55 | bytes 56 | end 57 | 58 | def modExp( 59 | :gas, 60 | <> 62 | ) do 63 | gas = max(modlen, baselen) 64 | 65 | gas = 66 | cond do 67 | gas <= 64 -> 68 | gas * gas 69 | 70 | gas <= 1024 -> 71 | div(gas * gas, 4) + (96 * gas - 3072) 72 | 73 | true -> 74 | div(gas * gas, 16) + (480 * gas - 199_680) 75 | end 76 | 77 | gas = gas * max(1, (explen - 32) * 8) 78 | gas = div(gas, 20) 79 | min(gas, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) 80 | end 81 | 82 | def modExp( 83 | :run, 84 | <> 86 | ) do 87 | baselen8 = baselen * 8 88 | explen8 = explen * 8 89 | modlen8 = modlen * 8 90 | 91 | <> = 92 | rest 93 | 94 | if (baselen == 0 and modlen == 0) or mod == 0 do 95 | "" 96 | else 97 | <> 98 | end 99 | end 100 | 101 | defp pow(ret, _base, 1, mod) do 102 | rem(ret, mod) 103 | end 104 | 105 | defp pow(ret, base, exp, mod) do 106 | pow(rem(ret * base, mod), base, exp - 1, mod) 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /lib/processlru.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule ProcessLru do 5 | @moduledoc """ 6 | Provides Least-Recently-Used Queue with a fixed maximum size 7 | """ 8 | 9 | def new(name \\ nil, max_size) do 10 | name = 11 | case name do 12 | nil -> make_ref() 13 | _other -> name 14 | end 15 | 16 | Process.put({__MODULE__, name}, %{meta: {0, max_size}}) 17 | name 18 | end 19 | 20 | def destroy(name) do 21 | Process.delete({__MODULE__, name}) 22 | end 23 | 24 | def put(name, key, value) do 25 | do_put(map(name), name, key, value) 26 | end 27 | 28 | def do_put(%{meta: {n, max_size}} = lru, name, key, value) do 29 | lru = %{lru | meta: {n + 1, max_size}} 30 | n = n + 1 31 | 32 | key = {:key, key} 33 | 34 | lru = Map.put(lru, key, {value, n}) 35 | lru = Map.put(lru, n, key) 36 | 37 | del = n - max_size 38 | 39 | if del > 0 do 40 | {key, lru} = Map.pop!(lru, del) 41 | 42 | lru = 43 | case Map.get(lru, key) do 44 | {_value, ^del} -> Map.delete(lru, key) 45 | _ -> lru 46 | end 47 | 48 | Process.put({__MODULE__, name}, lru) 49 | else 50 | Process.put({__MODULE__, name}, lru) 51 | end 52 | 53 | value 54 | end 55 | 56 | def do_put(_, _name, _key, value) do 57 | value 58 | end 59 | 60 | def get(name, key, default \\ nil) do 61 | case Map.get(map(name), {:key, key}) do 62 | {value, _n} -> value 63 | nil -> default 64 | end 65 | end 66 | 67 | def fetch(name, key, fun) do 68 | case Map.get(map(name), {:key, key}) do 69 | {value, _n} -> 70 | value 71 | 72 | nil -> 73 | case fun.() do 74 | nil -> nil 75 | value -> put(name, key, value) 76 | end 77 | end 78 | end 79 | 80 | def size(name) do 81 | total_size = map_size(map(name)) 82 | div(total_size - 1, 2) 83 | end 84 | 85 | def to_list(name) do 86 | for {{:key, key}, value, _n} <- Map.to_list(map(name)), do: {key, value} 87 | end 88 | 89 | defp map(name) do 90 | Process.get({__MODULE__, name}, %{}) 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/pubsub.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule PubSub do 5 | use GenServer 6 | 7 | def start_link(args) do 8 | GenServer.start_link(__MODULE__, args, name: __MODULE__) 9 | end 10 | 11 | def init(_arg) do 12 | __MODULE__ = :ets.new(__MODULE__, [:named_table, :public, :duplicate_bag]) 13 | {:ok, %{}} 14 | end 15 | 16 | def handle_cast({:monitor, pid}, state) do 17 | state = 18 | case Map.has_key?(state, pid) do 19 | false -> Map.put(state, pid, :erlang.monitor(:process, pid)) 20 | true -> state 21 | end 22 | 23 | {:noreply, state} 24 | end 25 | 26 | def handle_info({:DOWN, ref, :process, pid, _reason}, state) do 27 | :ets.match_delete(__MODULE__, {:_, pid}) 28 | {^ref, state} = Map.pop(state, pid) 29 | :erlang.demonitor(ref, [:flush]) 30 | {:noreply, state} 31 | end 32 | 33 | def subscribe(topic) do 34 | GenServer.cast(__MODULE__, {:monitor, self()}) 35 | :ets.insert(__MODULE__, {topic, self()}) 36 | end 37 | 38 | def unsubscribe(topic) do 39 | :ets.match_delete(__MODULE__, {topic, self()}) 40 | end 41 | 42 | def subscribers(topic) do 43 | :ets.match(__MODULE__, {topic, :"$1"}) 44 | |> List.flatten() 45 | end 46 | 47 | def publish(topic, msg) do 48 | subs = subscribers(topic) 49 | for pid <- subs, do: send(pid, msg) 50 | subs 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/queue.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Queue do 5 | use GenServer 6 | 7 | def start_link(_opts) do 8 | GenServer.start_link(__MODULE__, :ok, name: __MODULE__) 9 | end 10 | 11 | def shift() do 12 | GenServer.call(__MODULE__, :out) 13 | end 14 | 15 | def size() do 16 | GenServer.call(__MODULE__, :size) 17 | end 18 | 19 | def append(message) do 20 | GenServer.cast(__MODULE__, {:in, message}) 21 | end 22 | 23 | ## Server Callbacks 24 | 25 | def init(:ok) do 26 | {:ok, :queue.new()} 27 | end 28 | 29 | def handle_call(:out, _from, queue) do 30 | {value, queue2} = :queue.out(queue) 31 | {:reply, value, queue2} 32 | end 33 | 34 | def handle_call(:size, _from, queue) do 35 | {:reply, :queue.len(queue), queue} 36 | end 37 | 38 | def handle_cast({:in, message}, queue) do 39 | {:noreply, :queue.in(message, queue)} 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/random.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Random do 5 | @moduledoc """ 6 | Random provides random aliases for the range of machine types 7 | uint8-uint64, int8-int64 as well as additional aliases uint8h-uint64h 8 | generating random numbers starting at the lower types range end. 9 | """ 10 | 11 | def uint8(), do: random(0, 255) 12 | def uint16(), do: random(0, 65535) 13 | def uint32(), do: random(0, 4_294_967_295) 14 | 15 | def uint63(), do: random(0, 9_223_372_036_854_775_807) 16 | def uint64(), do: random(0, 18_446_744_073_709_551_615) 17 | def int8(), do: random(-128, 127) 18 | def int16(), do: random(-32768, 32767) 19 | def int32(), do: random(-2_147_483_648, 2_147_483_647) 20 | def int64(), do: random(-9_223_372_036_854_775_808, 9_223_372_036_854_775_807) 21 | 22 | def uint8h(), do: random(16, 255) 23 | def uint16h(), do: random(255, 65535) 24 | def uint31h(), do: random(65535, 2_147_483_647) 25 | def uint32h(), do: random(65535, 4_294_967_295) 26 | def uint63h(), do: random(4_294_967_295, 9_223_372_036_854_775_807) 27 | def uint64h(), do: random(4_294_967_295, 18_446_744_073_709_551_615) 28 | 29 | @spec random(integer(), integer()) :: integer() 30 | def random(lo, hi) do 31 | :rand.uniform(hi - lo) + lo 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/remote_chain/cache.ex: -------------------------------------------------------------------------------- 1 | defprotocol RemoteChain.Cache do 2 | def get(cache, key) 3 | def put(cache, key, value) 4 | end 5 | 6 | defimpl RemoteChain.Cache, for: Lru do 7 | def get(cache, key), do: Lru.get(cache, key) 8 | def put(cache, key, value), do: Lru.insert(cache, key, value) 9 | end 10 | 11 | defimpl RemoteChain.Cache, for: DetsPlus do 12 | def get(cache, key) do 13 | case DetsPlus.lookup(cache, key) do 14 | [{^key, value}] -> value 15 | _ -> nil 16 | end 17 | end 18 | 19 | def put(cache, key, value) do 20 | DetsPlus.insert(cache, {key, value}) 21 | cache 22 | end 23 | end 24 | 25 | defimpl RemoteChain.Cache, for: DetsPlus.LRU do 26 | def get(cache, key) do 27 | DetsPlus.LRU.get(cache, key) 28 | end 29 | 30 | def put(cache, key, value) do 31 | DetsPlus.LRU.put(cache, key, value) 32 | cache 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/remote_chain/call_permit.ex: -------------------------------------------------------------------------------- 1 | defmodule CallPermit do 2 | def address() do 3 | Base16.decode("0x000000000000000000000000000000000000080A") 4 | end 5 | 6 | # 0xe7f13b866a7fc159cb6ee32bcb4103cf0477652e 7 | def wallet() do 8 | Diode.wallet() 9 | end 10 | 11 | # /// @dev Dispatch a call on the behalf of an other user with a EIP712 permit. 12 | # /// Will revert if the permit is not valid or if the dispatched call reverts or errors (such as 13 | # /// out of gas). 14 | # /// If successful the EIP712 nonce is increased to prevent this permit to be replayed. 15 | # /// @param from Who made the permit and want its call to be dispatched on their behalf. 16 | # /// @param to Which address the call is made to. 17 | # /// @param value Value being transfered from the "from" account. 18 | # /// @param data Call data 19 | # /// @param gaslimit Gaslimit the dispatched call requires. 20 | # /// Providing it prevents the dispatcher to manipulate the gaslimit. 21 | # /// @param deadline Deadline in UNIX seconds after which the permit will no longer be valid. 22 | # /// @param v V part of the signature. 23 | # /// @param r R part of the signature. 24 | # /// @param s S part of the signature. 25 | # /// @return output Output of the call. 26 | # /// @custom:selector b5ea0966 27 | def dispatch(from, to, value, data, gaslimit, deadline, v, r, s) do 28 | ABI.encode_call( 29 | "dispatch", 30 | [ 31 | "address", 32 | "address", 33 | "uint256", 34 | "bytes", 35 | "uint64", 36 | "uint256", 37 | "uint8", 38 | "bytes32", 39 | "bytes32" 40 | ], 41 | [from, to, value, data, gaslimit, deadline, v, r, s] 42 | ) 43 | end 44 | 45 | def nonces(owner) do 46 | ABI.encode_call("nonces", ["address"], [owner]) 47 | end 48 | 49 | def domain_separator() do 50 | ABI.encode_call("DOMAIN_SEPARATOR", [], []) 51 | end 52 | 53 | def rpc_call!(chain, call, from \\ nil, blockref \\ "latest") do 54 | {:ok, ret} = rpc_call(chain, call, from, blockref) 55 | ret 56 | end 57 | 58 | def rpc_call(chain, call, from \\ nil, blockref \\ "latest") do 59 | from = if from != nil, do: Base16.encode(from) 60 | RemoteChain.RPC.call(chain, Base16.encode(address()), from, Base16.encode(call), blockref) 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/remote_chain/http.ex: -------------------------------------------------------------------------------- 1 | defmodule RemoteChain.HTTP do 2 | require Logger 3 | 4 | def send_raw_transaction(url, tx) do 5 | case rpc(url, "eth_sendRawTransaction", [tx]) do 6 | {:ok, tx_hash} -> tx_hash 7 | {:error, %{"code" => -32603, "message" => "already known"}} -> :already_known 8 | {:error, error} -> raise "RPC error: #{inspect(error)}" 9 | end 10 | end 11 | 12 | def rpc(url, method, params \\ []) do 13 | request = %{ 14 | jsonrpc: "2.0", 15 | method: method, 16 | params: params, 17 | id: 1 18 | } 19 | 20 | case post(url, request) do 21 | %{"result" => result} -> {:ok, result} 22 | %{"error" => error} -> {:error, error} 23 | {:error, error} -> {:error, error} 24 | other -> {:error, "Unexpected result #{inspect(other)}"} 25 | end 26 | end 27 | 28 | # @dialyzer {:nowarn_function, post: 2} 29 | defp post(url, request) do 30 | case HTTPoison.post(url, Poison.encode!(request), [ 31 | {"Content-Type", "application/json"} 32 | ]) do 33 | {:ok, %{body: body}} -> 34 | with {:ok, json} <- Poison.decode(body) do 35 | json 36 | end 37 | 38 | error = {:error, _reason} -> 39 | error 40 | end 41 | end 42 | 43 | def rpc!(method, params \\ []) do 44 | case rpc(method, params) do 45 | {:ok, result} -> result 46 | {:error, error} -> raise "RPC error: #{inspect(error)}" 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/remote_chain/nonce_provider.ex: -------------------------------------------------------------------------------- 1 | defmodule RemoteChain.NonceProvider do 2 | use GenServer, restart: :permanent 3 | require Logger 4 | alias RemoteChain.NonceProvider 5 | 6 | defstruct [:nonce, :fetched_nonce, :chain] 7 | 8 | def start_link(chain) do 9 | GenServer.start_link(__MODULE__, chain, name: name(chain), hibernate_after: 5_000) 10 | end 11 | 12 | defp name(chain) do 13 | impl = RemoteChain.chainimpl(chain) 14 | {:global, {__MODULE__, impl}} 15 | end 16 | 17 | @impl true 18 | def init(chain) do 19 | {:ok, %__MODULE__{chain: chain}} 20 | end 21 | 22 | def nonce(chain) do 23 | GenServer.call(name(chain), :nonce) 24 | end 25 | 26 | @impl true 27 | def handle_call(:nonce, _from, state = %NonceProvider{nonce: nil, chain: chain}) do 28 | nonce = fetch_nonce(chain) 29 | {:reply, nonce, %NonceProvider{state | nonce: nonce + 1, fetched_nonce: nonce}} 30 | end 31 | 32 | def handle_call(:nonce, _from, state = %NonceProvider{chain: chain, nonce: nonce}) do 33 | Debouncer.apply(__MODULE__, fn -> GenServer.cast(name(chain), :check_stale_nonce) end, 30_000) 34 | {:reply, nonce, %NonceProvider{state | nonce: nonce + 1}} 35 | end 36 | 37 | @impl true 38 | def handle_cast( 39 | :check_stale_nonce, 40 | state = %NonceProvider{nonce: old_nonce, fetched_nonce: fetched_once, chain: chain} 41 | ) do 42 | new_nonce = fetch_nonce(chain) 43 | # if nonce is stuck then something is wrong with processing of transactions 44 | 45 | cond do 46 | new_nonce == fetched_once -> 47 | Logger.warning("RTX Nonce is stuck (#{old_nonce}), resetting to: #{new_nonce}") 48 | {:noreply, %NonceProvider{state | fetched_nonce: new_nonce, nonce: new_nonce}} 49 | 50 | new_nonce > old_nonce -> 51 | Logger.warning("RTX Nonce is too low (#{old_nonce}), resetting to: #{new_nonce}") 52 | {:noreply, %NonceProvider{state | fetched_nonce: new_nonce, nonce: new_nonce}} 53 | 54 | true -> 55 | {:noreply, %NonceProvider{state | fetched_nonce: new_nonce}} 56 | end 57 | end 58 | 59 | def handle_cast({:new_nonce, new_nonce}, state = %NonceProvider{nonce: old_nonce}) do 60 | if new_nonce > old_nonce do 61 | Logger.warning("RTX Nonce is too low (#{old_nonce}), resetting to: #{new_nonce}") 62 | {:noreply, %NonceProvider{state | nonce: new_nonce}} 63 | else 64 | {:noreply, state} 65 | end 66 | end 67 | 68 | def fetch_nonce(chain) do 69 | nonce = 70 | RemoteChain.RPCCache.get_transaction_count( 71 | chain, 72 | Wallet.base16(CallPermit.wallet()), 73 | "latest" 74 | ) 75 | |> Base16.decode_int() 76 | 77 | GenServer.cast(name(chain), {:new_nonce, nonce}) 78 | nonce 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/remote_chain/remote_chain.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule RemoteChain do 5 | @moduledoc """ 6 | Wrapper to access RemoteChain details. Usually each method requires the chain_id to be passed as the first argument. 7 | """ 8 | 9 | def epoch(chain_id, number \\ nil), 10 | do: chainimpl(chain_id).epoch(number || peaknumber(chain_id)) 11 | 12 | def epoch_progress(chain_id, number), do: chainimpl(chain_id).epoch_progress(number) 13 | def block(chain_id, number), do: RemoteChain.RPCCache.get_block_by_number(chain_id, number) 14 | def blockhash(chain_id, number), do: block(chain_id, number)["hash"] |> Base16.decode() 15 | def blocktime(chain_id, number), do: block(chain_id, number)["timestamp"] |> Base16.decode_int() 16 | def peaknumber(chain_id), do: RemoteChain.RPCCache.block_number(chain_id) 17 | def registry_address(chain_id), do: chainimpl(chain_id).registry_address() 18 | def developer_fleet_address(chain_id), do: chainimpl(chain_id).developer_fleet_address() 19 | def transaction_hash(chain_id), do: chainimpl(chain_id).transaction_hash() 20 | 21 | if Mix.env() == :test do 22 | def diode_l1_fallback(), do: Chains.DiodeStaging 23 | @chains [Chains.DiodeStaging, Chains.Anvil] 24 | else 25 | def diode_l1_fallback(), do: Chains.Diode 26 | 27 | @chains [ 28 | Chains.Diode, 29 | Chains.Moonbeam, 30 | Chains.MoonbaseAlpha 31 | ] 32 | end 33 | 34 | @all_chains Enum.uniq([ 35 | Chains.DiodeDev, 36 | Chains.DiodeStaging, 37 | Chains.Moonriver | @chains 38 | ]) 39 | 40 | @doc """ 41 | This function reads endpoints from environment variables when available. So it's possible 42 | to override the default endpoints by setting the environment variables like `CHAINS_MOONBEAM_WS`. 43 | """ 44 | def ws_endpoints(chain) do 45 | name = String.upcase("#{inspect(chain)}_WS") |> String.replace(".", "_") 46 | 47 | case System.get_env(name) do 48 | nil -> chainimpl(chain).ws_endpoints() 49 | endpoint -> [endpoint | chainimpl(chain).ws_endpoints()] 50 | end 51 | end 52 | 53 | def chains(), do: @chains 54 | 55 | for chain <- @all_chains do 56 | def chainimpl(unquote(chain.chain_id())), do: unquote(chain) 57 | def chainimpl(unquote(chain.chain_prefix())), do: unquote(chain) 58 | def chainimpl(unquote(chain)), do: unquote(chain) 59 | end 60 | 61 | # Old deprecated chain ids 62 | def chainimpl(41042), do: Chains.DiodeStaging 63 | def chainimpl(41043), do: Chains.Diode 64 | 65 | def chainimpl(module) when is_atom(module), do: module 66 | def chainimpl(other), do: raise("Unknown chain #{inspect(other)}") 67 | end 68 | -------------------------------------------------------------------------------- /lib/remote_chain/rpc.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule RemoteChain.RPC do 5 | require Logger 6 | 7 | def get_proof(chain, address, keys, block \\ "latest") do 8 | # requires https://eips.ethereum.org/EIPS/eip-1186 9 | rpc!(chain, "eth_getProof", [address, keys, block]) 10 | end 11 | 12 | def block_number(chain) do 13 | rpc!(chain, "eth_blockNumber") 14 | end 15 | 16 | def get_block_by_number(chain, block \\ "latest", with_transactions \\ false) do 17 | rpc!(chain, "eth_getBlockByNumber", [block, with_transactions]) 18 | end 19 | 20 | def get_storage_at(chain, address, slot, block \\ "latest") do 21 | rpc!(chain, "eth_getStorageAt", [address, slot, block]) 22 | end 23 | 24 | def get_code(chain, address, block \\ "latest") do 25 | rpc!(chain, "eth_getCode", [address, block]) 26 | end 27 | 28 | def get_transaction_count(chain, address, block \\ "latest") do 29 | rpc!(chain, "eth_getTransactionCount", [address, block]) 30 | end 31 | 32 | def get_transaction_by_hash(chain, hash) do 33 | rpc!(chain, "eth_getTransactionByHash", [hash]) 34 | end 35 | 36 | def get_balance(chain, address, block \\ "latest") do 37 | rpc!(chain, "eth_getBalance", [address, block]) 38 | end 39 | 40 | def gas_price(chain) do 41 | rpc!(chain, "eth_gasPrice", []) 42 | end 43 | 44 | def send_raw_transaction(chain, tx) do 45 | case rpc(chain, "eth_sendRawTransaction", [tx]) do 46 | {:ok, tx_hash} -> tx_hash 47 | {:error, %{"code" => -32603, "message" => "already known"}} -> :already_known 48 | {:error, error} -> {:error, error} 49 | end 50 | end 51 | 52 | def rpc!(chain, method, params \\ []) do 53 | case rpc(chain, method, params) do 54 | {:ok, result} -> result 55 | {:error, error} -> raise "RPC error: #{inspect(error)}" 56 | end 57 | end 58 | 59 | def rpc(chain, method, params) do 60 | case RemoteChain.NodeProxy.rpc(chain, method, params) do 61 | %{"result" => result} -> {:ok, result} 62 | %{"error" => error} -> {:error, error} 63 | end 64 | end 65 | 66 | def call(chain, to, from, data, block \\ "latest") do 67 | rpc(chain, "eth_call", [%{to: to, data: data, from: from}, block]) 68 | end 69 | 70 | def call!(chain, to, from, data, block \\ "latest") do 71 | {:ok, ret} = call(chain, to, from, data, block) 72 | ret 73 | end 74 | 75 | def estimate_gas(chain, to, data, block \\ "latest") do 76 | rpc!(chain, "eth_estimateGas", [%{to: to, data: data}, block]) 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/remote_chain/sup.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule RemoteChain.Sup do 5 | # Automatically defines child_spec/1 6 | use Supervisor 7 | 8 | def start_link(chain, opts \\ []) do 9 | Supervisor.start_link(__MODULE__, {chain, opts}) 10 | end 11 | 12 | def init({chain, opts}) do 13 | cache = Keyword.get(opts, :cache) || Lru.new(100_000) 14 | 15 | Supervisor.init( 16 | [ 17 | {RemoteChain.NodeProxy, chain}, 18 | {RemoteChain.RPCCache, [chain, cache]}, 19 | {RemoteChain.NonceProvider, chain}, 20 | {RemoteChain.TxRelay, chain} 21 | ], 22 | strategy: :rest_for_one 23 | ) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/remote_chain/tx_relay.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule RemoteChain.TxRelay do 5 | @moduledoc """ 6 | Repeat transactions to the chain 7 | """ 8 | use GenServer, restart: :permanent 9 | alias RemoteChain.TxRelay 10 | alias Chain.Transaction 11 | require Logger 12 | 13 | defstruct [:chain, :txlist] 14 | 15 | def start_link(chain) do 16 | GenServer.start_link(__MODULE__, %TxRelay{chain: chain, txlist: []}, name: name(chain)) 17 | end 18 | 19 | @impl true 20 | def init(state) do 21 | :timer.send_interval(2_000, :ping) 22 | {:ok, state} 23 | end 24 | 25 | def keep_alive(chain, metatx, payload) do 26 | GenServer.cast(name(chain), {:tx, metatx, payload}) 27 | end 28 | 29 | @impl true 30 | def handle_cast({:tx, metatx, payload}, %TxRelay{txlist: txlist} = state) do 31 | {:noreply, %TxRelay{state | txlist: [{metatx, payload} | txlist]}} 32 | end 33 | 34 | @impl true 35 | def handle_info(:ping, %TxRelay{txlist: []} = state) do 36 | {:noreply, state} 37 | end 38 | 39 | def handle_info(:ping, %TxRelay{txlist: txlist, chain: chain} = state) do 40 | gas_price = RemoteChain.RPC.gas_price(chain) |> Base16.decode_int() 41 | nonce = RemoteChain.NonceProvider.fetch_nonce(chain) 42 | 43 | txlist = process(txlist, nonce, gas_price, state) 44 | {:noreply, %TxRelay{state | txlist: txlist}} 45 | end 46 | 47 | def process([], _nonce, _gasprice, _state) do 48 | [] 49 | end 50 | 51 | def process( 52 | [{metatx = %Transaction{nonce: tx_nonce, gasPrice: tx_gas_price}, payload} | rest], 53 | nonce, 54 | gas_price, 55 | state 56 | ) do 57 | tx_hash = 58 | Transaction.hash(metatx) 59 | |> Base16.encode() 60 | 61 | cond do 62 | tx_nonce <= nonce -> 63 | Logger.info("RTX done: #{tx_hash}") 64 | process(rest, nonce, gas_price, state) 65 | 66 | tx_gas_price < gas_price -> 67 | Logger.warning( 68 | "RTX gas price lower than reference #{tx_gas_price / gas_price} #{tx_hash}" 69 | ) 70 | 71 | resubmit(tx_hash, payload, state) 72 | [{metatx, payload} | process(rest, nonce, gas_price, state)] 73 | 74 | true -> 75 | resubmit(tx_hash, payload, state) 76 | [{metatx, payload} | process(rest, nonce, gas_price, state)] 77 | end 78 | end 79 | 80 | defp resubmit(tx_hash, payload, %TxRelay{chain: chain}) do 81 | ret = RemoteChain.RPC.send_raw_transaction(chain, payload) 82 | Logger.info("Resubmit RTX: #{tx_hash}: #{inspect(ret)}") 83 | end 84 | 85 | defp name(chain) do 86 | impl = RemoteChain.chainimpl(chain) 87 | {:global, {__MODULE__, impl}} 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /lib/remote_chain/util.ex: -------------------------------------------------------------------------------- 1 | defmodule RemoteChain.Util do 2 | def batch_call(ref, calls, timeout \\ :infinity) do 3 | Enum.map(calls, fn call -> :gen_server.send_request(ref, call) end) 4 | |> Enum.map(fn req_id -> :gen_server.receive_response(req_id, timeout) end) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/rlp.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Rlp do 5 | @type rlp() :: binary() | [rlp()] 6 | 7 | @spec encode!(nil | binary() | maybe_improper_list() | non_neg_integer() | tuple()) :: binary() 8 | def encode!(<>) when x < 0x80, do: <> 9 | 10 | def encode!(x) when is_binary(x) do 11 | with_length!(0x80, x) 12 | end 13 | 14 | def encode!(list) when is_list(list) do 15 | with_length!(0xC0, Enum.map(list, &encode!/1)) 16 | end 17 | 18 | def encode!(other) do 19 | encode!(do_encode!(other)) 20 | end 21 | 22 | defp with_length!(offset, data) do 23 | size = :erlang.iolist_size(data) 24 | 25 | if size <= 55 do 26 | [offset + size, data] 27 | else 28 | bin = :binary.encode_unsigned(size) 29 | [byte_size(bin) + offset + 55, bin, data] 30 | end 31 | |> :erlang.iolist_to_binary() 32 | end 33 | 34 | @spec decode!(binary()) :: rlp() 35 | def decode!(bin) do 36 | {term, ""} = do_decode!(bin) 37 | term 38 | end 39 | 40 | defp do_encode!(nil) do 41 | "" 42 | end 43 | 44 | defp do_encode!(struct) when is_struct(struct) do 45 | Map.from_struct(struct) 46 | |> Enum.map(fn {key, value} -> [Atom.to_string(key), value] end) 47 | end 48 | 49 | defp do_encode!(map) when is_map(map) do 50 | Map.to_list(map) 51 | |> Enum.map(fn {key, value} -> 52 | [if(is_atom(key), do: Atom.to_string(key), else: key), value] 53 | end) 54 | end 55 | 56 | defp do_encode!(tuple) when is_tuple(tuple) do 57 | :erlang.tuple_to_list(tuple) 58 | end 59 | 60 | defp do_encode!(bits) when is_bitstring(bits) do 61 | for <>, do: if(x == 1, do: "1", else: "0"), into: "" 62 | end 63 | 64 | defp do_encode!(0) do 65 | # Sucks but this is the quasi standard by Go and Node.js 66 | # This is why we have bin2num 67 | "" 68 | end 69 | 70 | defp do_encode!(num) when is_integer(num) do 71 | :binary.encode_unsigned(num) 72 | end 73 | 74 | defp do_decode!(<>) when x <= 0x7F do 75 | {<>, rest} 76 | end 77 | 78 | defp do_decode!(<>) when head <= 0xB7 do 79 | size = head - 0x80 80 | <> = rest 81 | {item, rest} 82 | end 83 | 84 | defp do_decode!(<>) when head <= 0xBF do 85 | length_size = (head - 0xB7) * 8 86 | <> = rest 87 | {item, rest} 88 | end 89 | 90 | defp do_decode!(<>) when head <= 0xF7 do 91 | size = head - 0xC0 92 | <> = rest 93 | {do_decode_list!([], list), rest} 94 | end 95 | 96 | defp do_decode!(<>) when head <= 0xFF do 97 | length_size = (head - 0xF7) * 8 98 | <> = rest 99 | {do_decode_list!([], list), rest} 100 | end 101 | 102 | defp do_decode_list!(list, "") do 103 | Enum.reverse(list) 104 | end 105 | 106 | defp do_decode_list!(list, rest) do 107 | {item, rest} = do_decode!(rest) 108 | do_decode_list!([item | list], rest) 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /lib/rlpx.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Rlpx do 5 | @moduledoc """ 6 | Rlpx defines helepr method for converting RLP encoded values back and forth. 7 | """ 8 | @type rlp() :: binary() | [rlp()] 9 | 10 | @spec hex2num(binary()) :: non_neg_integer() 11 | def hex2num("") do 12 | 0 13 | end 14 | 15 | def hex2num(bin) when is_binary(bin) do 16 | bin2num(Base16.decode(bin)) 17 | end 18 | 19 | @spec num2bin(non_neg_integer) :: binary 20 | def num2bin(0) do 21 | "" 22 | end 23 | 24 | def num2bin(num) when is_integer(num) do 25 | :binary.encode_unsigned(num) 26 | end 27 | 28 | @spec bin2num(binary()) :: non_neg_integer() 29 | def bin2num("") do 30 | 0 31 | end 32 | 33 | def bin2num(bin) when is_binary(bin) do 34 | :binary.decode_unsigned(bin) 35 | end 36 | 37 | @spec bin2int(binary) :: integer 38 | def bin2int(bin) when is_binary(bin) do 39 | num = bin2num(bin) 40 | 41 | case rem(num, 2) do 42 | 0 -> div(num, 2) 43 | 1 -> -div(num - 1, 2) 44 | end 45 | end 46 | 47 | def int2bin(int) when is_integer(int) do 48 | if int < 0 do 49 | -(int * 2) + 1 50 | else 51 | int * 2 52 | end 53 | |> :binary.encode_unsigned() 54 | end 55 | 56 | @spec hex2addr(binary()) :: nil | binary() 57 | def hex2addr("") do 58 | nil 59 | end 60 | 61 | def hex2addr(bin) when is_binary(bin) do 62 | bin2addr(Base16.decode(bin)) 63 | end 64 | 65 | @spec bin2addr(binary()) :: nil | binary() 66 | def bin2addr("") do 67 | nil 68 | end 69 | 70 | def bin2addr(bin) when is_binary(bin) do 71 | bin 72 | end 73 | 74 | def list2map(list) do 75 | Enum.map(list, fn [key, value] -> {key, value} end) 76 | |> Map.new() 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/stages.ex: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Stages do 5 | use GenServer 6 | defstruct stage: 0, ticks: 0, min_conns: 2, min_ticks: 20, probable_peak: 0 7 | 8 | def start_link() do 9 | GenServer.start_link(__MODULE__, [], name: __MODULE__) 10 | end 11 | 12 | def init(_arg) do 13 | :timer.send_interval(1_000, :tick) 14 | 15 | state = 16 | if Diode.dev_mode?() do 17 | %Stages{min_conns: 0, min_ticks: 0} 18 | else 19 | %Stages{} 20 | end 21 | 22 | {:ok, state} 23 | end 24 | 25 | def stage() do 26 | GenServer.call(__MODULE__, :stage) 27 | end 28 | 29 | def probable_peak(peak) do 30 | GenServer.cast(__MODULE__, {:probable_peak, peak}) 31 | end 32 | 33 | def handle_cast({:probable_peak, peak}, state) do 34 | {:noreply, %{state | probable_peak: max(state.probable_peak, peak - 100)}} 35 | end 36 | 37 | def handle_call(:stage, _from, state = %{stage: stage}) do 38 | {:reply, stage, state} 39 | end 40 | 41 | def handle_info( 42 | :tick, 43 | state = %{ 44 | stage: 0, 45 | ticks: ticks, 46 | min_conns: min_conns, 47 | min_ticks: min_ticks, 48 | probable_peak: peak 49 | } 50 | ) do 51 | conns = Network.Server.get_connections(Network.PeerHandler) 52 | 53 | ticks = 54 | cond do 55 | map_size(conns) < min_conns -> 0 56 | Diode.syncing?() -> 0 57 | peak > Chain.peak() -> 0 58 | true -> ticks + 1 59 | end 60 | 61 | state = %{state | ticks: ticks} 62 | 63 | state = 64 | if ticks >= min_ticks do 65 | Diode.start_client_network() 66 | %{state | stage: 1} 67 | else 68 | state 69 | end 70 | 71 | {:noreply, state} 72 | end 73 | 74 | def handle_info(:tick, state = %{stage: 1}) do 75 | {:noreply, state} 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | 5 | if String.to_integer(System.otp_release()) < 25 do 6 | IO.puts("this package requires OTP 25.") 7 | raise "incorrect OTP" 8 | end 9 | 10 | defmodule Diode.Mixfile do 11 | use Mix.Project 12 | 13 | @vsn "1.6.11" 14 | @full_vsn "v1.6.11" 15 | @url "https://github.com/diodechain/diode_server" 16 | 17 | def project do 18 | [ 19 | aliases: aliases(), 20 | app: Diode, 21 | compilers: [:elixir_make] ++ Mix.compilers(), 22 | deps: deps(), 23 | description: "Diode Network Full Blockchain Node implementation", 24 | docs: docs(), 25 | elixir: "~> 1.13", 26 | elixirc_options: [warnings_as_errors: Mix.target() == :host], 27 | elixirc_paths: elixirc_paths(Mix.env()), 28 | full_version: :persistent_term.get(:full_vsn, @full_vsn), 29 | package: package(), 30 | source_url: @url, 31 | start_permanent: Mix.env() == :prod, 32 | version: :persistent_term.get(:vsn, @vsn) 33 | ] 34 | end 35 | 36 | defp elixirc_paths(:test), do: ["lib", "test/helpers"] 37 | defp elixirc_paths(_), do: ["lib"] 38 | 39 | def application do 40 | [ 41 | mod: {Diode, []}, 42 | extra_applications: [ 43 | :debouncer, 44 | :keccakf1600, 45 | :libsecp256k1, 46 | :logger, 47 | :mix, 48 | :observer, 49 | :runtime_tools, 50 | :sqlitex, 51 | :os_mon 52 | ] 53 | ] 54 | end 55 | 56 | defp aliases do 57 | [ 58 | lint: [ 59 | "compile", 60 | "format --check-formatted", 61 | "credo --only warning", 62 | "dialyzer" 63 | ] 64 | ] 65 | end 66 | 67 | defp docs do 68 | [ 69 | source_ref: "v#{@vsn}", 70 | source_url: @url, 71 | formatters: ["html"], 72 | main: "readme", 73 | extra_section: "GUIDES", 74 | extras: [ 75 | LICENSE: [title: "License"], 76 | "README.md": [title: "Readme"], 77 | "guides/running_your_miner.md": [title: "Running your miner"] 78 | ] 79 | ] 80 | end 81 | 82 | defp package do 83 | [ 84 | maintainers: ["Dominic Letz"], 85 | licenses: ["DIODE"], 86 | links: %{github: @url}, 87 | files: ~w(evm lib LICENSE mix.exs README.md) 88 | ] 89 | end 90 | 91 | defp deps do 92 | [ 93 | {:ezstd, "~> 1.2"}, 94 | {:dets_plus, "~> 2.0"}, 95 | {:benchee, "~> 1.0", only: :benchmark}, 96 | {:debouncer, "~> 0.1"}, 97 | {:eblake2, "~> 1.0"}, 98 | {:elixir_make, "~> 0.4", runtime: false}, 99 | {:ex_doc, "~> 0.28", only: :dev, runtime: false}, 100 | {:httpoison, "~> 2.0"}, 101 | {:keccakf1600, github: "diodechain/erlang-keccakf1600"}, 102 | {:libsecp256k1, github: "diodechain/libsecp256k1"}, 103 | {:niffler, "~> 0.1"}, 104 | {:oncrash, "~> 0.0"}, 105 | {:plug_cowboy, "~> 2.5"}, 106 | {:poison, "~> 4.0"}, 107 | {:profiler, github: "dominicletz/profiler"}, 108 | {:sqlitex, github: "diodechain/sqlitex"}, 109 | {:websockex, github: "Azolo/websockex"}, 110 | {:while, "~> 0.2"}, 111 | {:mutable_map, "~> 1.0"}, 112 | # {:mutable_map, path: "../../mutable_map"}, 113 | 114 | # linting 115 | {:dialyxir, "~> 1.1", only: [:dev], runtime: false}, 116 | {:credo, "~> 1.5", only: [:dev, :test], runtime: false} 117 | ] 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /remsh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | export ERL_EPMD_ADDRESS=127.0.0.1 6 | export MIX_ENV=prod 7 | 8 | REAL_HOME=$(eval echo "~$(whoami)") 9 | if [ -f ${REAL_HOME}/.asdf/asdf.sh ]; then 10 | export ASDF_DATA_DIR=${REAL_HOME}/.asdf 11 | . ${REAL_HOME}/.asdf/asdf.sh 12 | fi 13 | 14 | exec iex --sname remsh_$$ --remsh diode 15 | -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | ulimit -n 120000 6 | ulimit -c unlimited 7 | export ERL_CRASH_DUMP_BYTES=5000000000 8 | export ERL_EPMD_ADDRESS=127.0.0.1 9 | export MIX_ENV=${MIX_ENV:-prod} 10 | export MAKEFLAGS="-j8" 11 | export CFLAGS="-O3 -march=native" 12 | export CXXFLAGS="-O3 -march=native" 13 | 14 | REAL_HOME=$(eval echo "~$(whoami)") 15 | if [ -f ${REAL_HOME}/.asdf/asdf.sh ]; then 16 | export ASDF_DATA_DIR=${REAL_HOME}/.asdf 17 | . ${REAL_HOME}/.asdf/asdf.sh 18 | 19 | asdf install erlang 20 | asdf install elixir 21 | fi 22 | 23 | mix deps.get 24 | mix git_version 25 | 26 | if [ -f ./.env ]; then 27 | export `cat ./.env` 28 | fi 29 | 30 | export ELIXIR_ERL_OPTIONS="${ELIXIR_ERL_OPTIONS} -kernel inet_dist_use_interface {127,0,0,1} +sbwt none -noinput -noshell -sname diode +A 8" 31 | exec elixir -S mix run --no-halt 32 | -------------------------------------------------------------------------------- /scripts/Dockerfile: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | FROM elixir:1.15.7 5 | 6 | RUN apt-get update && apt-get install -y libboost-dev libboost-system-dev 7 | 8 | ENV MIX_ENV=prod 9 | ENV PORT=8080 10 | ENV ERL_EPMD_ADDRESS=127.0.0.1 11 | 12 | COPY mix.* /app/ 13 | 14 | WORKDIR /app/ 15 | 16 | RUN mix local.hex --force && mix local.rebar && mix deps.get && make -C deps/libsecp256k1/ && mix deps.compile 17 | 18 | COPY . /app/ 19 | 20 | RUN mix do compile, git_version 21 | 22 | ENTRYPOINT ["scripts/entrypoint"] 23 | -------------------------------------------------------------------------------- /scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | find ./clones/* ./data_*/blockchain.sq3* ./data_*/cache.sq3 -maxdepth 1 -type f -delete 6 | make clean 7 | -------------------------------------------------------------------------------- /scripts/cmerkle_bench.exs: -------------------------------------------------------------------------------- 1 | # size = 10_000 2 | # ref = Base16.decode("0x96c2b5d03aa8e52230e74d9a08359c38c7608e5d9aba18773f3c90baf3806ccb") 3 | 4 | Application.ensure_all_started(:weak_ref) 5 | 6 | size = 1000 7 | ref = Base16.decode("0xe3545ad7961427b2a49d0b0a7b4a61e040acaefc432b69bcc1066b7c8c460a6b") 8 | # ref = Base16.decode("0x3b99d56aa16278d50e85a8ea0939e62180c4f0305b773775f1844c3303a41897") 100_000 9 | 10 | test_data = 11 | Enum.map(1..size, fn idx -> 12 | hash = Diode.hash("!#{idx}!") 13 | {hash, hash} 14 | end) 15 | 16 | none = spawn(fn -> 17 | EtsLru.new(:cmerkle, 5) 18 | Process.sleep(:infinity) 19 | end) 20 | 21 | 22 | for i <- 1..1000000 do 23 | time_ms = :timer.tc(fn -> 24 | tree_size = 1000 25 | tree_size_1 = tree_size + 1 26 | for _ <- 1..tree_size do 27 | a = CMerkleTree.hash("b1000000!") 28 | item = {a, a} 29 | 30 | WeakRef.new(none) 31 | 32 | tree = CMerkleTree.insert_items(CMerkleTree.new(), test_data) |> CMerkleTree.lock() 33 | tree2 = CMerkleTree.clone(tree) 34 | |> CMerkleTree.lock() 35 | |> CMerkleTree.clone() 36 | |> CMerkleTree.lock() 37 | |> CMerkleTree.clone() 38 | |> CMerkleTree.lock() 39 | |> CMerkleTree.clone() 40 | |> CMerkleTree.lock() 41 | |> CMerkleTree.clone() 42 | |> CMerkleTree.lock() 43 | |> CMerkleTree.clone() 44 | |> CMerkleTree.lock() 45 | |> CMerkleTree.clone() 46 | |> CMerkleTree.insert_items([item]) 47 | 48 | _proof = tree2 |> CMerkleTree.get_proofs(a) 49 | _proof2 = tree |> CMerkleTree.get_proofs(a) 50 | ^tree_size = CMerkleTree.size(tree) 51 | ^tree_size = CMerkleTree.to_list(tree) |> length() 52 | ^tree_size_1 = CMerkleTree.size(tree2) 53 | ^tree_size_1 = CMerkleTree.to_list(tree2) |> length() 54 | 92 = CMerkleTree.bucket_count(tree) 55 | 92 = CMerkleTree.bucket_count(tree2) 56 | 57 | %{^a => {nil, b}} = CMerkleTree.difference(tree, tree2) 58 | CMerkleTree.from_map(%{a => b}) 59 | 60 | nil = CMerkleTree.get(tree, a) 61 | ^a = CMerkleTree.get(tree2, a) 62 | 63 | 64 | if CMerkleTree.root_hash(tree) != ref do 65 | raise("Error #{Base16.encode(CMerkleTree.root_hash(tree))} != #{Base16.encode(ref)}") 66 | end 67 | 68 | EtsLru.put(:cmerkle, CMerkleTree.root_hash(tree), tree) 69 | end 70 | end) 71 | |> elem(0) 72 | |> div(1000) 73 | 74 | IO.puts("Run #{i} #{time_ms}ms") 75 | :erlang.garbage_collect() 76 | end 77 | -------------------------------------------------------------------------------- /scripts/cmerkle_bench2.exs: -------------------------------------------------------------------------------- 1 | # size = 10_000 2 | # ref = Base16.decode("0x96c2b5d03aa8e52230e74d9a08359c38c7608e5d9aba18773f3c90baf3806ccb") 3 | 4 | Application.ensure_all_started(:weak_ref) 5 | 6 | size = 1000 7 | ref = Base16.decode("0xe3545ad7961427b2a49d0b0a7b4a61e040acaefc432b69bcc1066b7c8c460a6b") 8 | # ref = Base16.decode("0x3b99d56aa16278d50e85a8ea0939e62180c4f0305b773775f1844c3303a41897") 100_000 9 | 10 | for i <- 1..1000000 do 11 | time_ms = :timer.tc(fn -> 12 | tree_size = 1000 13 | for _ <- 1..tree_size do 14 | test_data = 15 | Enum.map(1..size, fn idx -> 16 | hash = Diode.hash("!#{idx}!") 17 | {hash, hash} 18 | end) 19 | 20 | tree = CMerkleTree.new() 21 | for {key, value} <- test_data do 22 | CMerkleTree.insert_item(tree, {key, value}) 23 | end 24 | for {key, value} <- test_data do 25 | ^value = CMerkleTree.get(tree, key) 26 | CMerkleTree.get_proofs(tree, key) != nil 27 | end 28 | ^tree_size = length(CMerkleTree.to_list(tree)) 29 | CMerkleTree.lock(tree) 30 | 31 | if CMerkleTree.root_hash(tree) != ref do 32 | raise("Error #{Base16.encode(CMerkleTree.root_hash(tree))} != #{Base16.encode(ref)}") 33 | end 34 | 35 | end 36 | end) 37 | |> elem(0) 38 | |> div(1000) 39 | 40 | IO.puts("Run #{i} #{time_ms}ms") 41 | :erlang.garbage_collect() 42 | end 43 | -------------------------------------------------------------------------------- /scripts/cmerkle_bench3.exs: -------------------------------------------------------------------------------- 1 | Application.ensure_all_started(:mutable_map) 2 | 3 | 4 | # uncompact = fn %Chain.State{accounts: old_accounts}-> 5 | # Enum.reduce(old_accounts, MutableMap.new(), fn {id, acc}, accounts -> 6 | # MutableMap.put(accounts, id, Chain.Account.uncompact(acc)) 7 | # end) 8 | # end 9 | 10 | 11 | EtsLru.new(:leak_state, 15) 12 | state = File.read!("leak_state.bin") 13 | 14 | for i <- 1..100 do 15 | one = EtsLru.fetch(:leak_state, i, fn -> 16 | ret = :erlang.binary_to_term(state) 17 | |> Chain.State.uncompact() 18 | |> Chain.State.lock() 19 | # |> IO.inspect() 20 | 21 | :erlang.garbage_collect() 22 | :erlang.garbage_collect(Process.whereis(MutableMap.Beacon)) 23 | 24 | ret 25 | end) 26 | 27 | IO.puts("#{i}: #{map_size(one)}") 28 | end 29 | -------------------------------------------------------------------------------- /scripts/docker: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | set -e 6 | mkdir -p data_prod 7 | MD5=md5sum 8 | if [ -f "$HOME/.erlang.cookie" ]; then 9 | COOKIE=`cat $HOME/.erlang.cookie` 10 | else 11 | if [[ "$OSTYPE" == "darwin"* ]] 12 | then 13 | MD5=md5 14 | fi 15 | COOKIE=`echo $RANDOM | $MD5 | head -c 20` 16 | echo $COOKIE > "$HOME/.erlang.cookie" 17 | fi 18 | 19 | docker build . -t diode -f scripts/Dockerfile 20 | 21 | case "$1" in 22 | "remsh") 23 | exec docker run \ 24 | --network=host -ti \ 25 | -e COOKIE=$COOKIE \ 26 | diode remsh 27 | ;; 28 | "-f") 29 | touch "$(pwd)/.env" 30 | exec docker run \ 31 | --network=host -ti \ 32 | -e COOKIE=$COOKIE \ 33 | --env-file "$(pwd)/.env" \ 34 | --mount type=bind,source="$(pwd)/data_prod",target=/app/data_prod \ 35 | --name diode \ 36 | diode 37 | ;; 38 | "") 39 | touch "$(pwd)/.env" 40 | exec docker run \ 41 | -d \ 42 | --restart unless-stopped \ 43 | --network=host -ti \ 44 | -e COOKIE=$COOKIE \ 45 | --env-file "$(pwd)/.env" \ 46 | --mount type=bind,source="$(pwd)/data_prod",target=/app/data_prod \ 47 | --name diode \ 48 | diode 49 | ;; 50 | "") 51 | echo "Usage: $0 <''|-f|remsh>" 52 | exit 1 53 | ;; 54 | esac 55 | -------------------------------------------------------------------------------- /scripts/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | case "$1" in 5 | "remsh") 6 | export ELIXIR_ERL_OPTIONS="-setcookie $COOKIE" 7 | exec iex --sname remsh_$RANDOM --remsh diode 8 | ;; 9 | "") 10 | export ELIXIR_ERL_OPTIONS="+sbwt none -noinput -noshell -sname diode +A 8" 11 | exec iex -S mix run --no-halt 12 | ;; 13 | *) 14 | echo "Usage: $0 <''|remsh>" 15 | exit 1 16 | ;; 17 | esac 18 | -------------------------------------------------------------------------------- /scripts/evmbench.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | Benchee.run( 5 | %{ 6 | "increment" => &Bench.increment/1 7 | }, 8 | inputs: %{ 9 | "fib(11)" => Bench.create_contract(1, 11), 10 | "fib(23)" => Bench.create_contract(1, 23), 11 | "fib(27)" => Bench.create_contract(1, 27) 12 | }, 13 | time: 20 14 | ) 15 | -------------------------------------------------------------------------------- /scripts/evmbench.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | MIX_ENV=benchmark mix run evmbench.exs 6 | -------------------------------------------------------------------------------- /scripts/find_fragmentation.exs: -------------------------------------------------------------------------------- 1 | percent = fn 2 | _a, 0 -> "100%" 3 | a, b -> "#{round(100 * a / b)}%" 4 | end 5 | 6 | get = fn 7 | nil, _key -> 0 8 | info, key -> 9 | elem(List.keyfind(info, key, 0), 1) 10 | end 11 | 12 | get_alloc = fn 13 | nil -> 0 14 | [head | _] -> elem(head, 2) 15 | end 16 | 17 | allocators = [:sys_alloc, :temp_alloc, :sl_alloc, :std_alloc, :ll_alloc, 18 | :eheap_alloc, :ets_alloc, :fix_alloc, :literal_alloc, :exec_alloc, 19 | :binary_alloc, :driver_alloc, :mseg_alloc] 20 | 21 | Enum.each(allocators, fn alloc -> 22 | info = try do 23 | :erlang.system_info({:allocator, alloc}) 24 | rescue 25 | _ -> nil 26 | end 27 | 28 | if is_list(info) do 29 | {allocated, used, calls} = Enum.reduce(info, {0, 0, 0}, fn 30 | {:instance, _nr, stats}, {al, us, ca} -> 31 | mbcs = Keyword.get(stats, :mbcs) 32 | allocated = get.(mbcs, :carriers_size) 33 | used = case Keyword.get(stats, :mbcs, []) |> Keyword.get(:blocks) do 34 | nil -> get.(mbcs, :blocks_size) 35 | {_, _, _, _} -> get.(mbcs, :blocks_size) 36 | list when is_list(list) -> 37 | Enum.reduce(list, 0, fn {_, other}, sum -> sum + get.(other, :size) end) 38 | end 39 | calls = get_alloc.(Keyword.get(stats, :calls)) 40 | {allocated + al, used + us, calls + ca} 41 | _, acc -> acc 42 | end) 43 | 44 | waste = allocated - used 45 | IO.puts( 46 | "#{String.pad_trailing(to_string(alloc), 14)} got #{String.pad_leading("#{used}", 10)} used of #{String.pad_leading("#{allocated}", 10)} alloc (#{String.pad_leading(percent.(used, allocated), 4)}) = #{String.pad_leading("#{waste}", 10)} waste @ #{String.pad_leading("#{calls}", 10)} calls" 47 | ) 48 | else 49 | IO.puts("#{String.pad_trailing(to_string(alloc), 14)} is disabled") 50 | end 51 | end) 52 | -------------------------------------------------------------------------------- /scripts/inspect_mem.exs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env elixir 2 | 3 | # elixir inspect_mem.exs 5434 4 | pid = Enum.at(System.argv(), 0) || raise "Missing argument" 5 | 6 | memory = File.read!("/proc/#{pid}/maps") 7 | |> String.split("\n", trim: true) 8 | |> Enum.map(fn row -> 9 | [range, access, _offset, _device, _inode | path] = String.split(row, " ", trim: true) 10 | [start, stop] = String.split(range, "-") 11 | size = String.to_integer(stop, 16) - String.to_integer(start, 16) 12 | {size, range, access, path} 13 | end) 14 | |> Enum.filter(fn {_, _, access, _} -> access in ["rw-p", "r--p"] end) 15 | |> Enum.sort(:desc) 16 | 17 | Enum.each(memory, fn row -> 18 | IO.inspect(row) 19 | end) 20 | 21 | total = Enum.map(memory, fn {size, _, _, _} -> size end) |> Enum.sum() 22 | small = Enum.filter(memory, fn {size, _, _, _} -> size <= 32768 end) 23 | |> Enum.map(fn {size, _, _, _} -> size end) |> Enum.sum() 24 | 25 | 26 | IO.puts("Total memory #{total} small entries: #{small}") 27 | -------------------------------------------------------------------------------- /scripts/merkle_bench.exs: -------------------------------------------------------------------------------- 1 | # size = 10_000 2 | # ref = Base16.decode("0x96c2b5d03aa8e52230e74d9a08359c38c7608e5d9aba18773f3c90baf3806ccb") 3 | 4 | size = 100_000 5 | ref = Base16.decode("0x3b99d56aa16278d50e85a8ea0939e62180c4f0305b773775f1844c3303a41897") 6 | 7 | test_data = 8 | Enum.map(1..size, fn idx -> 9 | hash = Diode.hash("!#{idx}!") 10 | {hash, hash} 11 | end) 12 | 13 | for i <- 1..3 do 14 | time_ms = :timer.tc(fn -> 15 | for _ <- 1..100 do 16 | tree = HeapMerkleTree.insert_items(HeapMerkleTree.new(), test_data) 17 | 18 | if HeapMerkleTree.root_hash(tree) != ref do 19 | raise("Error #{Base16.encode(HeapMerkleTree.root_hash(tree))} != #{Base16.encode(ref)}") 20 | end 21 | end 22 | end) 23 | |> elem(0) 24 | |> div(1000) 25 | 26 | IO.puts("Run #{i} #{time_ms}ms") 27 | end 28 | -------------------------------------------------------------------------------- /scripts/merkle_test.exs: -------------------------------------------------------------------------------- 1 | :persistent_term.put(:env, :prod) 2 | # System.put_env("DATA_DIR", "data_prod") 3 | {:ok, _db} = Model.Sql.start_database(Db.Default, Db.Default) 4 | {:ok, _} = Application.ensure_all_started(:mutable_map) 5 | 6 | block_number = 7554001 7 | 8 | 9 | block = Model.ChainSql.block(block_number) 10 | IO.inspect(Model.ChainSql.is_jumpblock(block), label: "is_jumpblock") 11 | state = Model.ChainSql.state(Chain.Block.hash(block)) 12 | 13 | IO.inspect(Map.has_key?(state, :store), label: "has store") 14 | 15 | IO.inspect(Chain.Block.state_consistent?(block)) 16 | -------------------------------------------------------------------------------- /scripts/recompess.exs: -------------------------------------------------------------------------------- 1 | 2 | {:ok, db} = Sqlitex.open("data_prod/blockchain.sq3") 3 | 4 | Sqlitex.query!(db, "PRAGMA journal_mode = WAL") 5 | Sqlitex.query!(db, "PRAGMA synchronous = NORMAL") 6 | Sqlitex.query!(db, "PRAGMA AUTO_VACUUM = 1") 7 | 8 | defmodule Recompress do 9 | def recompress(data) do 10 | case data do 11 | <<40, 181, 47, 253, _::binary>> -> 12 | :ezstd.decompress(data) 13 | |> :ezstd.compress(22) 14 | 15 | _other -> 16 | :zlib.unzip(data) 17 | |> :ezstd.compress(22) 18 | end 19 | end 20 | end 21 | case System.argv() do 22 | [start, stop] -> 23 | start = String.to_integer(start) 24 | stop = String.to_integer(stop) 25 | 26 | 27 | Task.async_stream( 28 | start..stop//100, 29 | fn block -> 30 | Sqlitex.query!(db, "SELECT number, data, state FROM blocks WHERE number > ?1 AND number <= ?1 + 100", bind: [block], timeout: :infinity) 31 | |> Enum.map(fn [number: number, data: data, state: state] -> 32 | {number, data, state} 33 | end) 34 | end, 35 | timeout: :infinity, 36 | max_concurrency: 2, 37 | ordered: true 38 | ) 39 | |> Stream.flat_map(fn {:ok, data} -> 40 | data 41 | end) 42 | |> Task.async_stream(fn {block, data, state} -> 43 | data2 = Recompress.recompress(data) 44 | state2 = Recompress.recompress(state) 45 | 46 | if byte_size(data2) < byte_size(data) or byte_size(state2) < byte_size(state) do 47 | IO.puts("\tblock #{block} state size: #{byte_size(state)} => #{byte_size(state2)} data size: #{byte_size(data)} => #{byte_size(data2)}") 48 | Sqlitex.query!(db, "UPDATE blocks SET data = ?1, state = ?2 WHERE number = ?3", bind: [data2, state2, block]) 49 | end 50 | end, 51 | timeout: :infinity, 52 | # max_concurrency: 8, 53 | ordered: true 54 | ) 55 | |> Stream.run() 56 | _ -> 57 | :ok 58 | end 59 | -------------------------------------------------------------------------------- /scripts/run_beta: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | export RPC_PORT=11000 6 | export EDGE2_PORT=12000 7 | export PEER_PORT=13000 8 | export SEED=diode://localhost:51053 9 | export DATA_DIR=./clones/man/ 10 | exec iex -S mix run 11 | -------------------------------------------------------------------------------- /scripts/run_offline: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | rm data_prod/kademlia.etf 6 | export SEED= 7 | exec ./run 8 | -------------------------------------------------------------------------------- /scripts/run_perf: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | ulimit -n 120000 6 | export ERL_CRASH_DUMP_BYTES=5000000000 7 | export ERL_EPMD_ADDRESS=127.0.0.1 8 | export MIX_ENV=prod 9 | mix deps.get 10 | export ELIXIR_ERL_OPTIONS='+sbwt none -noinput -noshell -sname diode +Mea min' 11 | export ELIXIR_ERL_OPTIONS="${ELIXIR_ERL_OPTIONS} +JPperf true +S 1 +sbt db" 12 | exec elixir -S mix run --no-halt 13 | -------------------------------------------------------------------------------- /scripts/run_test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | rm -rf data_test/ 6 | export MIX_ENV=test 7 | mix deps.get 8 | exec iex -S mix run 9 | -------------------------------------------------------------------------------- /scripts/zip_bench.exs: -------------------------------------------------------------------------------- 1 | 2 | Logger.configure(level: :info) 3 | data = File.read!("profile_273315140.cprof") 4 | 5 | IO.puts("zlib.deflate") 6 | for _i <- 1..3 do 7 | IO.inspect(:timer.tc(fn -> byte_size(BertInt.zip(data, 7)) end)) 8 | end 9 | 10 | IO.puts("ezstd") 11 | for _i <- 1..3 do 12 | IO.inspect(:timer.tc(fn -> byte_size(:ezstd.compress(data, 11)) end)) 13 | end 14 | -------------------------------------------------------------------------------- /staging: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | n=$1 6 | if [[ "$n" == "" ]]; then 7 | n=0 8 | fi 9 | 10 | 11 | ulimit -n 120000 12 | export ERL_CRASH_DUMP_BYTES=5000000000 13 | export ERL_EPMD_ADDRESS=127.0.0.1 14 | export MIX_ENV=prod 15 | export DATA_DIR=staging_data${n}/ 16 | export CHAINDEF=stagenet 17 | export PEER_PORT=190${n}0 18 | export EDGE2_PORT=190${n}1 19 | export SEED=diode://localhost:19000 20 | mix deps.get 21 | export ELIXIR_ERL_OPTIONS="+sbwt none -noinput -noshell -sname staging${n} +Mea min" 22 | exec elixir -S mix run --no-halt 23 | -------------------------------------------------------------------------------- /test/blockquick_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule BlockQuickTest do 5 | use ExUnit.Case, async: false 6 | alias Chain.Block 7 | 8 | setup_all do 9 | Chain.reset_state() 10 | :ok 11 | end 12 | 13 | test "forced stop" do 14 | target_size = Chain.window_size() * 2 15 | build(target_size) 16 | assert Chain.with_final(&Block.number/1) == Chain.window_size() - 1 17 | assert Chain.peak() == target_size - 11 18 | end 19 | 20 | defp build(0) do 21 | :ok 22 | end 23 | 24 | defp build(n) do 25 | Chain.Worker.work() 26 | build(n - 1) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/curl/do: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | if [[ $1 == "" ]]; then 6 | echo "Need a host parameter" 7 | echo "You can try localhost:8545" 8 | exit 9 | fi 10 | 11 | if [[ $2 == "" ]]; then 12 | echo "Need a sample file parameter" 13 | echo "You can try getObject.json" 14 | exit 15 | fi 16 | 17 | curl -k -H "Content-Type: application/json" -X POST -o out.json --data @$2 $1 18 | -------------------------------------------------------------------------------- /test/curl_rpc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Diode Server 3 | # Copyright 2021-2024 Diode 4 | # Licensed under the Diode License, Version 1.1 5 | if [[ $1 == "" ]]; then 6 | echo "Need host parameter" 7 | echo "You can try localhost:8545" 8 | exit 9 | fi 10 | 11 | host=$1 12 | 13 | # Just some rpc examples using curl 14 | # curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getLogs","params":["0x16"],"id":73}' $host 15 | 16 | echo "Chain ID:" 17 | curl -k -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_chainId","id":73}' $host 18 | echo "" 19 | 20 | # Counts the number of fleet contracts 21 | echo "Total Fleets:" 22 | curl -k -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"dio_codeCount","params":["0x7e9d94e966d33cff302ef86e2337df8eaf9a6388d45e4744321240599d428343"],"id":73}' $host 23 | echo "" 24 | 25 | # Counts all accounts by hash 26 | # 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 is the null hash 27 | echo "All Codes:" 28 | curl -k -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"dio_codeGroups","params":["0x16"],"id":73}' $host 29 | echo "" 30 | 31 | # Counts all balances 32 | echo "Total Balances:" 33 | curl -k -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"dio_supply","params":["0x16"],"id":73}' $host 34 | echo "" 35 | 36 | 37 | -------------------------------------------------------------------------------- /test/data/stress.ex: -------------------------------------------------------------------------------- 1 | rpcs = File.read!("test/data/stress.json") |> Poison.decode!() 2 | Network.Rpc.handle_jsonrpc(rpcs, []) 3 | -------------------------------------------------------------------------------- /test/etslru_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule EtsLruTest do 5 | use ExUnit.Case 6 | 7 | test "base" do 8 | lru = EtsLru.new(nil, 10) 9 | assert EtsLru.size(lru) == 0 10 | 11 | EtsLru.put(lru, "key", "value") 12 | assert EtsLru.size(lru) == 1 13 | 14 | assert EtsLru.get(lru, "key") == "value" 15 | 16 | # EtsLru should not cache nil return values 17 | assert EtsLru.fetch(lru, "nothing", fn -> nil end) == nil 18 | assert EtsLru.fetch(lru, "nothing", fn -> "yay" end) == "yay" 19 | assert EtsLru.get(lru, "nothing") == "yay" 20 | end 21 | 22 | test "limit" do 23 | lru = EtsLru.new(nil, 3) 24 | assert EtsLru.size(lru) == 0 25 | 26 | EtsLru.put(lru, "a", "avalue") 27 | EtsLru.put(lru, "b", "bvalue") 28 | EtsLru.put(lru, "c", "cvalue") 29 | 30 | assert EtsLru.size(lru) == 3 31 | assert EtsLru.get(lru, "a") == "avalue" 32 | assert EtsLru.get(lru, "b") == "bvalue" 33 | assert EtsLru.get(lru, "c") == "cvalue" 34 | 35 | EtsLru.put(lru, "d", "dvalue") 36 | 37 | assert EtsLru.size(lru) == 3 38 | assert EtsLru.get(lru, "a") == nil 39 | assert EtsLru.get(lru, "b") == "bvalue" 40 | assert EtsLru.get(lru, "c") == "cvalue" 41 | assert EtsLru.get(lru, "d") == "dvalue" 42 | end 43 | 44 | test "set_max_size" do 45 | lru = EtsLru.new(nil, 3) 46 | assert EtsLru.max_size(lru) == 3 47 | 48 | EtsLru.set_max_size(lru, 10) 49 | assert EtsLru.max_size(lru) == 10 50 | end 51 | 52 | test "repeat" do 53 | lru = EtsLru.new(nil, 3) 54 | assert EtsLru.size(lru) == 0 55 | 56 | EtsLru.put(lru, "a", "avalue") 57 | EtsLru.put(lru, "b", "bvalue") 58 | EtsLru.put(lru, "c", "cvalue") 59 | 60 | assert EtsLru.size(lru) == 3 61 | assert EtsLru.get(lru, "a") == "avalue" 62 | assert EtsLru.get(lru, "b") == "bvalue" 63 | assert EtsLru.get(lru, "c") == "cvalue" 64 | 65 | EtsLru.put(lru, "a", "avalue2") 66 | 67 | assert EtsLru.size(lru) == 3 68 | assert EtsLru.get(lru, "a") == "avalue2" 69 | assert EtsLru.get(lru, "b") == "bvalue" 70 | assert EtsLru.get(lru, "c") == "cvalue" 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /test/lru_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule LruTest do 5 | use ExUnit.Case 6 | 7 | test "base" do 8 | lru = Lru.new(10) 9 | assert Lru.size(lru) == 0 10 | 11 | lru = Lru.insert(lru, "key", "value") 12 | assert Lru.size(lru) == 1 13 | 14 | assert Lru.get(lru, "key") == "value" 15 | 16 | # Lru should not cache nil return values 17 | assert Lru.fetch(lru, "nothing", fn -> nil end) == {lru, nil} 18 | {lru, "yay"} = Lru.fetch(lru, "nothing", fn -> "yay" end) 19 | assert Lru.get(lru, "nothing") == "yay" 20 | end 21 | 22 | test "limit" do 23 | lru = Lru.new(3) 24 | assert Lru.size(lru) == 0 25 | 26 | lru = Lru.insert(lru, "a", "avalue") 27 | lru = Lru.insert(lru, "b", "bvalue") 28 | lru = Lru.insert(lru, "c", "cvalue") 29 | 30 | assert Lru.size(lru) == 3 31 | assert Lru.get(lru, "a") == "avalue" 32 | assert Lru.get(lru, "b") == "bvalue" 33 | assert Lru.get(lru, "c") == "cvalue" 34 | 35 | lru = Lru.insert(lru, "d", "dvalue") 36 | 37 | assert Lru.size(lru) == 3 38 | assert Lru.get(lru, "a") == nil 39 | assert Lru.get(lru, "b") == "bvalue" 40 | assert Lru.get(lru, "c") == "cvalue" 41 | assert Lru.get(lru, "d") == "dvalue" 42 | end 43 | 44 | test "repeat" do 45 | lru = Lru.new(3) 46 | assert Lru.size(lru) == 0 47 | 48 | lru = Lru.insert(lru, "a", "avalue") 49 | lru = Lru.insert(lru, "b", "bvalue") 50 | lru = Lru.insert(lru, "c", "cvalue") 51 | 52 | assert Lru.size(lru) == 3 53 | assert Lru.get(lru, "a") == "avalue" 54 | assert Lru.get(lru, "b") == "bvalue" 55 | assert Lru.get(lru, "c") == "cvalue" 56 | 57 | lru = Lru.insert(lru, "a", "avalue2") 58 | 59 | assert Lru.size(lru) == 3 60 | assert Lru.get(lru, "a") == "avalue2" 61 | assert Lru.get(lru, "b") == "bvalue" 62 | assert Lru.get(lru, "c") == "cvalue" 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /test/object_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule ObjectTest do 5 | use ExUnit.Case 6 | alias Object.Server 7 | 8 | # Testing forward compatibility of server objects 9 | 10 | test "forward/backward compatibility" do 11 | classic = 12 | {:server, "host", 1, 2, "a", "signature"} 13 | |> Object.Server.sign(Wallet.privkey!(Diode.miner())) 14 | 15 | assert Server.host(classic) == "host" 16 | assert Server.edge_port(classic) == 1 17 | assert Server.peer_port(classic) == 2 18 | assert Server.key(classic) == Wallet.address!(Diode.miner()) 19 | 20 | extended = 21 | {:server, "host", 1, 2, "a", "b", "c", "signature"} 22 | |> Object.Server.sign(Wallet.privkey!(Diode.miner())) 23 | 24 | assert Server.host(extended) == "host" 25 | assert Server.edge_port(extended) == 1 26 | assert Server.peer_port(extended) == 2 27 | assert Server.key(extended) == Wallet.address!(Diode.miner()) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/peer_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule PeerTest do 5 | use ExUnit.Case, async: false 6 | alias Network.Server, as: Server 7 | alias Network.PeerHandler, as: PeerHandler 8 | 9 | import TestHelper 10 | 11 | setup_all do 12 | reset() 13 | start_clones(1) 14 | 15 | on_exit(fn -> 16 | kill_clones() 17 | end) 18 | end 19 | 20 | test "sync" do 21 | wait_for( 22 | fn -> Network.Server.get_connections(PeerHandler) == %{} end, 23 | "connections to drain" 24 | ) 25 | 26 | # The Genesis Block should be the same 27 | assert Chain.genesis_hash() == rpc(1, "eth_getBlockByNumber", "0,false")["hash"] 28 | 29 | # There should be no block on the new clone 30 | assert 0 == :binary.decode_unsigned(rpc(1, "eth_blockNumber")) 31 | 32 | # Building test blocks for syncing 33 | size = 40 34 | assert Chain.peak() == 1 35 | for _ <- 1..size, do: Chain.Worker.work() 36 | assert Chain.peak() == size + 1 37 | 38 | # Creating peer connection 39 | pid = Server.ensure_node_connection(PeerHandler, Wallet.new(), "localhost", peer_port(1)) 40 | assert GenServer.call(pid, {:rpc, [PeerHandler.ping()]}) == [PeerHandler.pong()] 41 | 42 | # Waiting for the connection to settle 43 | wait_for( 44 | fn -> map_size(Network.Server.get_connections(PeerHandler)) == 1 end, 45 | "clone connection (1/#{inspect(Network.Server.get_connections(PeerHandler))})", 46 | 30 47 | ) 48 | 49 | [_clone] = Map.values(Network.Server.get_connections(PeerHandler)) 50 | 51 | # This shall force trigger a publish of all blocks to the clone 52 | Chain.Worker.work() 53 | 54 | wait_for( 55 | fn -> Chain.peak() == :binary.decode_unsigned(rpc(1, "eth_blockNumber")) end, 56 | "block sync", 57 | 30 58 | ) 59 | end 60 | 61 | defp rpc(num, method, params \\ "") do 62 | {:ok, {_head, _opt, body}} = 63 | :httpc.request( 64 | :post, 65 | {~c"http://localhost:#{rpc_port(num)}", [], ~c"application/json", 66 | ~c"{\"id\":1, \"method\":\"#{method}\", \"params\":[#{params}]}"}, 67 | [timeout: 5000], 68 | [] 69 | ) 70 | 71 | Json.decode!(body) 72 | |> Map.get("result") 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /test/processlru_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule ProcessLruTest do 5 | use ExUnit.Case 6 | 7 | test "base" do 8 | lru = ProcessLru.new(10) 9 | assert ProcessLru.size(lru) == 0 10 | 11 | ProcessLru.put(lru, "key", "value") 12 | assert ProcessLru.size(lru) == 1 13 | 14 | assert ProcessLru.get(lru, "key") == "value" 15 | 16 | # ProcessLru should not cache nil return values 17 | assert ProcessLru.fetch(lru, "nothing", fn -> nil end) == nil 18 | assert ProcessLru.fetch(lru, "nothing", fn -> "yay" end) == "yay" 19 | assert ProcessLru.get(lru, "nothing") == "yay" 20 | end 21 | 22 | test "limit" do 23 | lru = ProcessLru.new(3) 24 | assert ProcessLru.size(lru) == 0 25 | 26 | ProcessLru.put(lru, "a", "avalue") 27 | ProcessLru.put(lru, "b", "bvalue") 28 | ProcessLru.put(lru, "c", "cvalue") 29 | 30 | assert ProcessLru.size(lru) == 3 31 | assert ProcessLru.get(lru, "a") == "avalue" 32 | assert ProcessLru.get(lru, "b") == "bvalue" 33 | assert ProcessLru.get(lru, "c") == "cvalue" 34 | 35 | ProcessLru.put(lru, "d", "dvalue") 36 | 37 | assert ProcessLru.size(lru) == 3 38 | assert ProcessLru.get(lru, "a") == nil 39 | assert ProcessLru.get(lru, "b") == "bvalue" 40 | assert ProcessLru.get(lru, "c") == "cvalue" 41 | assert ProcessLru.get(lru, "d") == "dvalue" 42 | end 43 | 44 | test "repeat" do 45 | lru = ProcessLru.new(3) 46 | assert ProcessLru.size(lru) == 0 47 | 48 | ProcessLru.put(lru, "a", "avalue") 49 | ProcessLru.put(lru, "b", "bvalue") 50 | ProcessLru.put(lru, "c", "cvalue") 51 | 52 | assert ProcessLru.size(lru) == 3 53 | assert ProcessLru.get(lru, "a") == "avalue" 54 | assert ProcessLru.get(lru, "b") == "bvalue" 55 | assert ProcessLru.get(lru, "c") == "cvalue" 56 | 57 | ProcessLru.put(lru, "a", "avalue2") 58 | 59 | assert ProcessLru.size(lru) == 3 60 | assert ProcessLru.get(lru, "a") == "avalue2" 61 | assert ProcessLru.get(lru, "b") == "bvalue" 62 | assert ProcessLru.get(lru, "c") == "cvalue" 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /test/pubsub_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule PubSubTest do 5 | use ExUnit.Case 6 | 7 | test "sub unsub" do 8 | assert PubSub.subscribers(:test) == [] 9 | PubSub.subscribe(:test) 10 | assert PubSub.subscribers(:test) == [self()] 11 | PubSub.unsubscribe(:test) 12 | assert PubSub.subscribers(:test) == [] 13 | end 14 | 15 | test "two processes" do 16 | outer = self() 17 | assert PubSub.subscribers(:test) == [] 18 | 19 | inner = 20 | spawn(fn -> 21 | PubSub.subscribe(:test) 22 | send(outer, :subscribed) 23 | 24 | receive do 25 | :kick -> send(outer, :done) 26 | end 27 | end) 28 | 29 | receive do 30 | :subscribed -> assert PubSub.subscribers(:test) == [inner] 31 | end 32 | 33 | PubSub.publish(:test, :kick) 34 | 35 | receive do 36 | :done -> :ok 37 | end 38 | 39 | # Allowing 100ms for demonitor to happen 40 | Process.sleep(100) 41 | assert PubSub.subscribers(:test) == [] 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/rlp_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule RlpTest do 5 | use ExUnit.Case 6 | 7 | test "short list" do 8 | {public, private} = Secp256k1.generate() 9 | random = "hello world" 10 | 11 | term = [public, private, random] 12 | bin = Rlp.encode!(term) 13 | assert term == Rlp.decode!(bin) 14 | 15 | term = [public, private, [random]] 16 | bin = Rlp.encode!(term) 17 | assert term == Rlp.decode!(bin) 18 | 19 | term = List.duplicate(random, 1000) 20 | bin = Rlp.encode!(term) 21 | assert term == Rlp.decode!(bin) 22 | end 23 | 24 | test "strings" do 25 | short = "hello world" 26 | long = String.duplicate(short, 1000) 27 | 28 | term = [long] 29 | bin = Rlp.encode!(term) 30 | assert term == Rlp.decode!(bin) 31 | 32 | term = long 33 | bin = Rlp.encode!(term) 34 | assert term == Rlp.decode!(bin) 35 | 36 | term = [short] 37 | bin = Rlp.encode!(term) 38 | assert term == Rlp.decode!(bin) 39 | 40 | term = short 41 | bin = Rlp.encode!(term) 42 | assert term == Rlp.decode!(bin) 43 | end 44 | 45 | test "0 - 256" do 46 | for n <- 0..256 do 47 | list = List.duplicate([], n) 48 | assert Rlp.decode!(Rlp.encode!(list)) == list 49 | 50 | bin = String.duplicate("_", n) 51 | assert Rlp.decode!(Rlp.encode!(bin)) == bin 52 | end 53 | end 54 | 55 | test "nil, 0" do 56 | in_term = [0, <<0>>, nil, ""] 57 | out_term = ["", <<0>>, "", ""] 58 | assert out_term == Rlp.decode!(Rlp.encode!(in_term)) 59 | end 60 | 61 | test "rlp => app => rlp" do 62 | bin = 63 | :binary.encode_unsigned( 64 | 0xF86708843B9ACA0082A2A0949E0A6D367859C47E7895557D5F763B954952FCB08084D09DE08A1CA0F40915BA7822D7CDA5C42B530E21616249E700082A4E7401E8C62775E7C5E219A01BE95441D88652AE5ED7E57ECF837EB1DDF844024E55E5EFB6CD2AA555C913DD 65 | # 0xF86708843B9ACA0082A2A0949E0A6D367859C47E7895557D5F763B954952FCB08084D09DE08A2CA0F40915BA7822D7CDA5C42B530E21616249E700082A4E7401E8C62775E7C5E219A01BE95441D88652AE5ED7E57ECF837EB1DDF844024E55E5EFB6CD2AA555C913DD 66 | ) 67 | 68 | tx = Chain.Transaction.from_rlp(bin) 69 | bin2 = Rlp.encode!(Chain.Transaction.to_rlp(tx)) 70 | assert bin == bin2 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /test/secp256k1_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule Secp256k1Test do 5 | use ExUnit.Case 6 | 7 | test "generate" do 8 | {public, private} = Secp256k1.generate() 9 | 10 | assert byte_size(public) == 65 11 | assert byte_size(private) == 32 12 | end 13 | 14 | test "signature" do 15 | {public1, private1} = Secp256k1.generate() 16 | {public2, private2} = Secp256k1.generate() 17 | msg = "hello world" 18 | 19 | signature1 = Secp256k1.sign(private1, msg) 20 | signature2 = Secp256k1.sign(private2, msg) 21 | 22 | assert byte_size(signature1) == 65 23 | assert byte_size(signature2) == 65 24 | 25 | assert Secp256k1.verify(public1, msg, signature1) == true 26 | assert Secp256k1.verify(public2, msg, signature2) == true 27 | 28 | assert Secp256k1.verify(public1, msg, signature2) == false 29 | assert Secp256k1.verify(public2, msg, signature1) == false 30 | end 31 | 32 | test "recover" do 33 | {public, private} = Secp256k1.generate() 34 | public = Secp256k1.compress_public(public) 35 | 36 | msg = "hello world" 37 | signature = Secp256k1.sign(private, msg) 38 | 39 | assert Secp256k1.recover!(signature, msg) == public 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/transaction_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule TransactionTest do 5 | use ExUnit.Case 6 | alias Chain.Transaction 7 | alias Chain.State 8 | alias Chain.Account 9 | 10 | test "recoding" do 11 | [from, to] = Diode.wallets() |> Enum.reverse() |> Enum.take(2) 12 | 13 | before = Chain.with_peak_state(fn state -> state end) 14 | nonce = State.ensure_account(before, from) |> Account.nonce() 15 | to = Wallet.address!(to) 16 | 17 | tx = 18 | Network.Rpc.create_transaction(from, <<"">>, %{ 19 | "value" => 1000, 20 | "nonce" => nonce, 21 | "to" => to, 22 | "gasPrice" => 0 23 | }) 24 | 25 | rlp = tx |> Transaction.to_rlp() |> Rlp.encode!() 26 | tx2 = Transaction.from_rlp(rlp) 27 | 28 | assert tx == tx2 29 | assert tx2.chain_id == Diode.chain_id() 30 | end 31 | 32 | test "contract address" do 33 | key = 34 | "0x69ce0ceadb0cb471e1d75c93591ab95062d7381b5f8196dcd0dd576b31ee7c40" 35 | |> Base16.decode() 36 | 37 | target = 38 | "0x23A126345Fce78f9A5aD2960ca62aB2080f902B0" 39 | |> Base16.decode() 40 | 41 | from = Wallet.from_privkey(key) 42 | 43 | tx = 44 | Network.Rpc.create_transaction(from, <<"0xff">>, %{ 45 | "nonce" => 0 46 | }) 47 | 48 | assert Transaction.contract_creation?(tx) == true 49 | assert Transaction.new_contract_address(tx) == target 50 | end 51 | 52 | test "decoding metamask signed transaction" do 53 | origin = "0x2e13a61e2be33404976f7e04dd7e99f9ec1f0edf" 54 | 55 | tx = 56 | "0xf90386018085174876e8008080b90333608060405234801561001057600080fd5b506040516060806102d383398101604090815281516020830151919092015160018054600160a060020a03938416600160a060020a03199182161790915560008054948416948216949094179093556002805492909116919092161790556102568061007d6000396000f3006080604052600436106100775763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416633c5f7d46811461007c5780634ef1aee4146100a45780634fb3ccc5146100df578063504f04b714610110578063570ca7351461013c578063d90bd65114610151575b600080fd5b34801561008857600080fd5b506100a2600160a060020a03600435166024351515610172565b005b3480156100b057600080fd5b506100cb600160a060020a036004358116906024351661019d565b604080519115158252519081900360200190f35b3480156100eb57600080fd5b506100f46101bd565b60408051600160a060020a039092168252519081900360200190f35b34801561011c57600080fd5b506100a2600160a060020a036004358116906024351660443515156101cc565b34801561014857600080fd5b506100f4610206565b34801561015d57600080fd5b506100cb600160a060020a0360043516610215565b600160a060020a03919091166000908152600660205260409020805460ff1916911515919091179055565b600760209081526000928352604080842090915290825290205460ff1681565b600254600160a060020a031681565b600160a060020a03928316600090815260076020908152604080832094909516825292909252919020805460ff1916911515919091179055565b600154600160a060020a031681565b60066020526000908152604090205460ff16815600a165627a7a723058208df217001cef7e510f8f0352585a03e46b30eba1feaeaf76becbe261832a627f002900000000000000000000000050000000000000000000000000000000000000000000000000000000000000002e13a61e2be33404976f7e04dd7e99f9ec1f0edf0000000000000000000000002e13a61e2be33404976f7e04dd7e99f9ec1f0edf830140caa027cf2c32c9aebf122668b020501f16b0ce9dda8bac8e17c46569c8ad93409c35a0559df7c2b3e34c26b04325aeeac8560bfb24179df1366c250a662ab1fbf094a9" 57 | |> Base16.decode() 58 | 59 | tx = Transaction.from_rlp(tx) 60 | assert Transaction.chain_id(tx) == 41043 61 | assert Wallet.address!(Transaction.origin(tx)) == Base16.decode(origin) 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /test/wallet_test.exs: -------------------------------------------------------------------------------- 1 | # Diode Server 2 | # Copyright 2021-2024 Diode 3 | # Licensed under the Diode License, Version 1.1 4 | defmodule WalletTest do 5 | use ExUnit.Case 6 | 7 | test "simple" do 8 | {pub, priv} = Secp256k1.generate() 9 | pub_compressed = Secp256k1.compress_public(pub) 10 | 11 | w1 = Wallet.from_pubkey(pub) 12 | w2 = Wallet.from_privkey(priv) 13 | w3 = Wallet.from_address(Wallet.address!(w1)) 14 | 15 | assert Wallet.address!(w1) == Wallet.address!(w2) 16 | assert Wallet.address!(w3) == Wallet.address!(w2) 17 | assert Wallet.pubkey!(w1) == Wallet.pubkey!(w2) 18 | 19 | assert {:error, nil} == Wallet.privkey(w1) 20 | assert {:ok, pub_compressed} == Wallet.pubkey(w1) 21 | 22 | assert {:ok, priv} == Wallet.privkey(w2) 23 | 24 | assert {:error, nil} == Wallet.privkey(w3) 25 | assert {:error, nil} == Wallet.pubkey(w3) 26 | assert {:ok, Wallet.address!(w1)} == Wallet.address(w3) 27 | end 28 | 29 | test "sign & recover" do 30 | for _x <- 1..1000 do 31 | w = Wallet.new() 32 | 33 | signature = Wallet.sign!(w, "my message") 34 | assert Wallet.equal?(w, Wallet.recover!("my message", signature)) 35 | end 36 | end 37 | end 38 | --------------------------------------------------------------------------------