├── .gitignore ├── Dockerfile ├── Makefile ├── README.md ├── Vagrantfile ├── apps ├── erun │ └── src │ │ ├── erun.app.src │ │ ├── erun.erl │ │ ├── erun_app.erl │ │ ├── erun_http.erl │ │ ├── erun_httpapi.erl │ │ ├── erun_querier.erl │ │ └── erun_sup.erl ├── tpic2 │ └── src │ │ ├── tpic2.app.src │ │ ├── tpic2.erl │ │ ├── tpic2_client.erl │ │ ├── tpic2_cmgr.erl │ │ ├── tpic2_peer.erl │ │ ├── tpic2_response.erl │ │ ├── tpic2_test.erl │ │ ├── tpic2_test_sup.erl │ │ ├── tpic2_tls.erl │ │ └── tpic_handler.erl ├── tpnode │ ├── include │ │ └── .keepme │ ├── priv │ │ └── index.html │ └── src │ │ ├── address.erl │ │ ├── address_db.erl │ │ ├── apixiom.erl │ │ ├── apixiom.stout │ │ ├── base58.erl │ │ ├── beacon.erl │ │ ├── bin2hex.erl │ │ ├── block.erl │ │ ├── blockchain.erl │ │ ├── blockchain_netmgmt.erl │ │ ├── blockchain_reader.erl │ │ ├── blockchain_sync.erl │ │ ├── blockchain_updater.erl │ │ ├── blockvote.erl │ │ ├── bron_kerbosch.erl │ │ ├── bsig.erl │ │ ├── chainkeeper.erl │ │ ├── chainsettings.erl │ │ ├── contract_evm.erl │ │ ├── contract_wasm.erl │ │ ├── cowboy_jsonrpc.erl │ │ ├── discovery.erl │ │ ├── discovery.stout │ │ ├── generate_block.erl │ │ ├── generate_block_process.erl │ │ ├── genesis.erl │ │ ├── genesis_easy.erl │ │ ├── genuml.erl │ │ ├── hashqueue.erl │ │ ├── hex.erl │ │ ├── httpapi_playground.erl │ │ ├── interconnect.erl │ │ ├── ldb.erl │ │ ├── ledger_sync.erl │ │ ├── logs_db.erl │ │ ├── maphash.erl │ │ ├── mbal.erl │ │ ├── mkblock.erl │ │ ├── mkblock_genblk.erl │ │ ├── mledger.erl │ │ ├── naddress.erl │ │ ├── nodekey.erl │ │ ├── rdb_dispatcher.erl │ │ ├── settings.erl │ │ ├── smartcontract.erl │ │ ├── smartcontract2.erl │ │ ├── synchronizer.erl │ │ ├── topology.erl │ │ ├── tpecdsa.erl │ │ ├── tpecdsa_pem.erl │ │ ├── tpic_checkauth.erl │ │ ├── tpnode.app.src │ │ ├── tpnode.erl │ │ ├── tpnode.stout │ │ ├── tpnode_announcer.erl │ │ ├── tpnode_backup.erl │ │ ├── tpnode_cert.erl │ │ ├── tpnode_dtx_runner.erl │ │ ├── tpnode_evmrun.erl │ │ ├── tpnode_hotfix.erl │ │ ├── tpnode_http.erl │ │ ├── tpnode_httpapi.erl │ │ ├── tpnode_jsonrpc.erl │ │ ├── tpnode_netmgmt_repl.erl │ │ ├── tpnode_netmgmt_sup.erl │ │ ├── tpnode_peerfinder.erl │ │ ├── tpnode_repl.erl │ │ ├── tpnode_repl_worker.erl │ │ ├── tpnode_reporter.erl │ │ ├── tpnode_sup.erl │ │ ├── tpnode_tpic_handler.erl │ │ ├── tpnode_tpicapi.erl │ │ ├── tpnode_txstorage.erl │ │ ├── tpnode_txsync.erl │ │ ├── tpnode_vmproto.erl │ │ ├── tpnode_vmsrv.erl │ │ ├── tpnode_ws.erl │ │ ├── tpnode_ws_dispatcher.erl │ │ ├── tx.erl │ │ ├── tx1.erl │ │ ├── tx_visualizer.erl │ │ ├── txlog.erl │ │ ├── txpool.erl │ │ ├── txqueue.erl │ │ ├── txqueue.stout │ │ ├── txstatus.erl │ │ ├── utils.erl │ │ ├── vm.erl │ │ ├── vm_wasm.erl │ │ ├── xchain.erl │ │ ├── xchain_api.erl │ │ ├── xchain_client.erl │ │ ├── xchain_client_handler.erl │ │ ├── xchain_client_worker.erl │ │ ├── xchain_dispatcher.erl │ │ ├── xchain_server.erl │ │ └── xchain_server_handler.erl └── tpwdt │ └── src │ ├── tpwdt.app.src │ ├── tpwdt.erl │ ├── tpwdt_sup.erl │ └── tpwdt_worker.erl ├── bin ├── build_rel_pre_alpha.sh ├── ci_build.sh ├── ci_check_chain1.sh ├── ci_check_chain4.sh ├── ci_check_chain7.sh ├── ci_pre_alpha.sh ├── generate_headers ├── run_rel_pre_alpha.sh ├── testnet.sh └── testnet2.sh ├── config ├── sys.config └── vm.args ├── dox ├── chain_setup.md ├── openssl.txt ├── profiling.txt ├── tx.txt ├── tx2.md └── tx2_2.md ├── examples ├── TetherToken.hex ├── TetherToken.sol ├── brom.wasm.lz4 ├── brom1 ├── evm_builtin │ ├── Makefile │ ├── build │ │ ├── Callee.abi │ │ ├── Caller.abi │ │ ├── WETH9.abi │ │ ├── WETH9.bin │ │ ├── builtinFunc.abi │ │ ├── builtinFunc.bin │ │ ├── builtinFunc_storage.json │ │ ├── checkSig.abi │ │ ├── checkSig.bin │ │ ├── checkSig_storage.json │ │ ├── sponsor.abi │ │ └── sponsor.bin │ └── contracts │ │ ├── ChainState.sol │ │ ├── WETH9.sol │ │ ├── builtinFunc.sol │ │ ├── call.sol │ │ ├── check_sig.sol │ │ └── sponsor.sol ├── monitor │ ├── bundle.js │ ├── chevron-blue.svg │ ├── chevron-down.svg │ └── index.html ├── sc_inc4.wasm.lz4 ├── stout.conf ├── test_chain4 │ ├── 0.txt │ ├── 1.txt │ ├── 2.txt │ ├── c4n1.conf │ ├── c4n2.conf │ ├── c4n3.conf │ ├── edkeys.txt │ ├── test_c4n1.args │ ├── test_c4n1.config │ ├── test_c4n2.args │ ├── test_c4n2.config │ ├── test_c4n3.args │ └── test_c4n3.config ├── test_chain5 │ ├── 0.txt │ ├── 1.txt │ ├── 2.txt │ ├── c5n1.conf │ ├── c5n2.conf │ ├── c5n3.conf │ ├── test_c5n1.args │ ├── test_c5n1.config │ ├── test_c5n2.args │ ├── test_c5n2.config │ ├── test_c5n3.args │ └── test_c5n3.config ├── test_chain6 │ ├── 0.txt │ ├── 1.txt │ ├── c6n1.conf │ ├── c6n2.conf │ ├── c6n3.conf │ ├── test_c6n1.args │ ├── test_c6n1.config │ ├── test_c6n2.args │ ├── test_c6n2.config │ ├── test_c6n3.args │ └── test_c6n3.config ├── testcontract.ec ├── testcontract.wasm └── testcontract_emit.ec ├── guides ├── ssl-certs-for-node.md ├── startingTpNode_docker.md ├── startingTpNode_source.md ├── testnet-start.md └── tpNodeConfiguration.md ├── include └── tplog.hrl ├── node.config.example ├── priv └── tx_const.json ├── rebar.config ├── rebar.config.script ├── rebar.lock ├── rebar3 ├── stout.conf └── test ├── basic_SUITE.erl ├── bsig_test.erl ├── chain1_SUITE.erl ├── chain4_SUITE.erl ├── chain7_SUITE.erl ├── evm_SUITE.erl ├── lstore_test.erl ├── mkblock_cont_tests.erl ├── mkblock_evm_ext_tests.erl ├── mkblock_evm_tests.erl ├── mkblock_evm_xchain_tests.erl ├── mkblock_tests.erl ├── mledger_test.erl ├── settings_tests.erl ├── sponsor_sc_test_test.erl ├── support └── helpers.ex ├── sync_test.exs ├── topology_tests.erl_ ├── tpnode.coverspec.tpl ├── tx_tests.erl └── txqueue_test.erl /.gitignore: -------------------------------------------------------------------------------- 1 | _* 2 | _build 3 | .DS_STORE 4 | .erlang.cookie 5 | .eunit 6 | .idea 7 | .rebar 8 | .rebar3 9 | .vagrant 10 | *.beam 11 | *.iml 12 | *.o 13 | *.plt 14 | *.swo 15 | *.swp 16 | *~ 17 | app?.config 18 | app*.config 19 | apps/*/.rebar 20 | apps/*/ebin 21 | apps/tpnode/include/version.hrl 22 | db 23 | db_* 24 | deps/* 25 | ebin 26 | erl_crash.dump 27 | log 28 | log/* 29 | logs 30 | rebar3.crashdump 31 | tags 32 | testrel 33 | tmp/ 34 | test/tpnode.coverspec 35 | apps/tpnode/include/tx_const.hrl 36 | alpha 37 | *.orig 38 | *.rej 39 | *.key 40 | *.bin 41 | genesis.txt 42 | *.erl_ 43 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Build stage 0 2 | FROM erlang:22 3 | 4 | # Install some libs 5 | RUN apt-get update -y && apt-get install -y \ 6 | libstdc++6 openssl libtinfo5 7 | 8 | # Set working directory 9 | WORKDIR /opt/thepower 10 | 11 | RUN mkdir -p /opt/thepower/db 12 | 13 | # Copy Power_node application 14 | COPY . . 15 | 16 | # Set symlink 17 | RUN ln -s /usr/bin/openssl /usr/local/bin/openssl 18 | 19 | # Expose relevant ports 20 | EXPOSE 49841 21 | EXPOSE 29841 22 | 23 | ENTRYPOINT [ "./bin/thepower" ] 24 | 25 | CMD ["foreground"] -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # verify that the programs we need to run are installed on this system 3 | # ============================================================================= 4 | ERL = $(shell which erl) 5 | SED = $(shell which sed) 6 | CT_RUN = $(shell which ct_run) 7 | RM = $(shell which rm) 8 | MKDIR = $(shell which mkdir) 9 | ELIXIR = $(shell which elixir) 10 | SLEEP = $(shell which sleep) 11 | TESTNET=./bin/testnet.sh 12 | 13 | LOG_DIR=_build/test/logs 14 | 15 | 16 | ifeq ($(ERL),) 17 | $(error "Erlang not available on this system") 18 | endif 19 | 20 | 21 | # If there is a rebar in the current directory, use it 22 | ifeq ($(wildcard rebar3),rebar3) 23 | REBAR = $(CURDIR)/rebar3 24 | endif 25 | 26 | all: 27 | @echo What you wanna to make? 28 | @echo make build - compile 29 | @echo make run1 - run node1 30 | @echo make run2 - run node2 31 | @echo make run3 - rin node3 32 | 33 | build: 34 | $(REBAR) compile 35 | 36 | compile: build 37 | 38 | 39 | deps: 40 | $(REBAR) get-deps 41 | 42 | 43 | rebar: 44 | ifeq ($(REBAR),) 45 | $(error "rebar3 isn't available on this system") 46 | endif 47 | 48 | elixir: 49 | ifeq ($(ELIXIR),) 50 | $(error "elixir isn't available on this system") 51 | endif 52 | 53 | 54 | 55 | run1: 56 | erl -config app1.config -sname rocksnode1 -pa _build/default/lib/*/ebin +SDcpu 2:2: -s lager -s sync -s tpnode 57 | run2: 58 | erl -config app2.config -sname rocksnode2 -pa _build/default/lib/*/ebin +SDcpu 2:2: -s lager -s sync -s tpnode 59 | run3: 60 | erl -config app3.config -sname rocksnode3 -pa _build/default/lib/*/ebin +SDcpu 2:2: -s lager -s sync -s tpnode 61 | 62 | runtestnet: 63 | $(TESTNET) start 64 | 65 | lint: rebar 66 | $(REBAR) elvis 67 | 68 | dialyzer: rebar 69 | $(REBAR) dialyzer 70 | 71 | xref: rebar 72 | $(REBAR) xref skip_deps=true 73 | 74 | tests: rebar 75 | $(TESTNET) start 76 | # $(REBAR) as test ct skip_deps=true --cover --verbose 77 | # $(REBAR) as test ct --cover --verbose 78 | # mkdir -p _build/test/logs 79 | # cd _build/test/logs 80 | # $(REBAR) as test compile 81 | # ct_run -logdir _build/test/logs --cover true --verbose -pa `$(REBAR) path` 82 | @REBAR_PROFILE=test $(REBAR) do ct --verbose 83 | # @REBAR_PROFILE=test $(REBAR) do ct -c, cover --verbose 84 | # @REBAR_PROFILE=test $(REBAR) do ct, cover --verbose 85 | # $(REBAR) as test cover --verbose 86 | # $(TESTNET) stop 87 | 88 | coffebreak: 89 | @$(SLEEP) 5 90 | 91 | synctest: elixir 92 | @$(ELIXIR) -pa '_build/test/lib/*/ebin/' --sname tester test/sync_test.exs 93 | 94 | cleantest: 95 | $(RM) -rf _build/test 96 | @$(MKDIR) -p _build/test/cover 97 | @$(MKDIR) -p $(LOG_DIR) 98 | 99 | buildtest: rebar 100 | @REBAR_PROFILE=test $(REBAR) do compile 101 | 102 | prepare_cover: sedcheck 103 | @$(SED) -re "s/@[^']+/@`hostname -s`/gi" test/tpnode.coverspec 104 | 105 | eunit: rebar prepare_cover 106 | @REBAR_PROFILE=test $(REBAR) do eunit --cover 107 | 108 | cover: ctruncheck cleantest buildtest rebar prepare_cover eunit 109 | @$(TESTNET) start 110 | @$(CT_RUN) -pa _build/test/lib/*/ebin \ 111 | -cover test/tpnode.coverspec \ 112 | -logdir $(LOG_DIR) \ 113 | -cover_stop false \ 114 | -suite basic_SUITE -suite evm_SUITE \ 115 | -noshell 116 | @$(TESTNET) stop 117 | 118 | nocover: ctruncheck cleantest buildtest rebar 119 | @$(TESTNET) start 120 | @$(CT_RUN) -pa _build/test/lib/*/ebin \ 121 | -logdir $(LOG_DIR) \ 122 | -cover_stop false \ 123 | -suite basic_SUITE -suite evm_SUITE \ 124 | -noshell 125 | @$(TESTNET) stop 126 | 127 | reset: cleantest 128 | @$(TESTNET) reset 129 | 130 | 131 | sedcheck: 132 | ifeq ($(SED),) 133 | $(error "'sed' not available on this system") 134 | endif 135 | 136 | ctruncheck: 137 | ifeq ($(CT_RUN),) 138 | $(error "'ct_run' not available on this system") 139 | endif 140 | 141 | 142 | node1shell: rebar 143 | $(REBAR) as node1 shell 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Table of Contents** 2 | 3 | - [Power_node](#power_node) 4 | - [Node.config](#nodeconfig) 5 | - [Generation of `genesis.txt`](#generation-of-genesistxt) 6 | - [SSL certificates](#ssl-certificates) 7 | - [Starting a node](#starting-a-node) 8 | - [Starting a testnet](#starting-a-testnet) 9 | 10 | # Power_node 11 | 12 | ## Node.config 13 | 14 | You can find the example of `node.config` in [TP-Node configuration](./guides/tpNodeConfiguration.md) guide. 15 | 16 | ## Generation of `genesis.txt` 17 | 18 | Generation of `genesis.txt` is described in [TP-Node configuration](./guides/tpNodeConfiguration.md) guide. 19 | 20 | ## SSL certificates 21 | 22 | Generation of SSL certificates is described in [How to obtain an SSL certificate for a node](./guides/ssl-certs-for-node.md) guide. 23 | 24 | ## Starting a node 25 | 26 | You can start a TP-Node either from Docker image, or from source code: 27 | 28 | - If you would like to start your TP-Node from source code, use [this guide](./guides/startingTpNode_source.md). 29 | - If you would like to start your TP-Node from Docker image, use [this guide](./guides/startingTpNode_docker.md). 30 | 31 | ## Starting a testnet 32 | 33 | Starting the testnet is described in [How to install and start a testnet?](./guides/testnet-start.md). 34 | 35 | If you use Vagrant to set up the development environment, refer to [Setting up the environment and starting the testnet using Vagrant](./guides/testnet-start.md#setting-up-the-environment-and-starting-the-testnet-using-vagrant) section of the guide mentioned above. -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | domains = [ 'pwr.local', 'dev.pwr.local' ] 5 | 6 | Vagrant.configure("2") do |config| 7 | config.vm.box = "bento/ubuntu-16.04" 8 | config.vm.box_check_update = false 9 | config.vm.network "public_network", type: "dhcp", use_dhcp_assigned_default_route: true 10 | config.vm.hostname = "pwr" 11 | config.vm.synced_folder ".", "/vagrant" 12 | 13 | # parallels specific stuff 14 | config.vm.provider "parallels" do |prl| 15 | prl.update_guest_tools = true 16 | prl.memory = 1024 17 | prl.cpus = 2 18 | end 19 | 20 | # virtual box specific stuff 21 | config.vm.provider 'virtualbox' do |vb| 22 | vb.cpus = 2 23 | vb.memory = 1024 24 | end 25 | 26 | # hosts settings (host machine) 27 | config.vm.provision :hostmanager 28 | config.hostmanager.enabled = true 29 | config.hostmanager.manage_host = true 30 | config.hostmanager.ignore_private_ip = false 31 | config.hostmanager.include_offline = true 32 | config.hostmanager.aliases = domains 33 | 34 | # disable ipv6 on eth0 35 | config.vm.provision "shell", 36 | run: "always", 37 | inline: "sysctl -w net.ipv6.conf.eth0.disable_ipv6=1" 38 | 39 | # delete default gw on eth0 40 | config.vm.provision "shell", 41 | run: "always", 42 | inline: "eval `route -n | awk '{ if ($8 ==\"eth0\" && $2 != \"0.0.0.0\") print \"route del default gw \" $2; }'`" 43 | 44 | config.vm.provision "shell", inline: <<-SHELL 45 | sudo apt update 46 | sudo apt install -y build-essential clang libsctp-dev libncurses5-dev mc curl libssl-dev automake autoconf jq tmux htop cmake 47 | 48 | # setup timezone 49 | echo 'Etc/UTC' > /etc/timezone 50 | dpkg-reconfigure --frontend noninteractive tzdata 51 | 52 | # install erlang 53 | wget https://raw.githubusercontent.com/kerl/kerl/master/kerl -O kerl -o /dev/null 54 | chmod +x kerl 55 | ./kerl update releases 56 | KERL_CONFIGURE_OPTIONS=--enable-sctp=lib ./kerl build 22.3 r22.3 57 | sudo ./kerl install r22.3 /opt/erl 58 | ./kerl cleanup all 59 | . /opt/erl/activate 60 | echo ". /opt/erl/activate" >> /home/vagrant/.bash_profile 61 | 62 | wget https://s3.amazonaws.com/rebar3/rebar3 -O rebar3 -o /dev/null 63 | sudo mv rebar3 /usr/local/bin 64 | sudo chmod +x /usr/local/bin/rebar3 65 | sudo chown root:root /usr/local/bin/rebar3 66 | if [ ! -e /vagrant/db ] 67 | then 68 | mkdir -p /home/vagrant/db 69 | ln -s /home/vagrant/db /vagrant/db 70 | chown vagrant:vagrant /home/vagrant/db /vagrant/db 71 | fi 72 | 73 | # add known hosts for git 74 | mkdir -p /home/vagrant/.ssh 75 | chmod 700 /home/vagrant/.ssh 76 | chown vagrant:vagrant /home/vagrant/.ssh 77 | echo "|1|oYXqrZWR2XaRQh9IjlyPiWN4l5o=|zUHUhYnkAyi/BrvofQe6E5Y8BG4= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGBFLC1ss/I+yAk9IqvX8wm4zQjNtyaW9O/vdl8HCjZ0+ddQy7l5bay2FFNF5x0Hw6eU/1miwuEOrO9PwfBVBzA=" >> /home/vagrant/.ssh/known_hosts 78 | echo "|1|c5X9JpCbDIz7KgNDcP33b8VrmNk=|hDD/iMZb6luoi4OElIv6gTOeZG0= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGBFLC1ss/I+yAk9IqvX8wm4zQjNtyaW9O/vdl8HCjZ0+ddQy7l5bay2FFNF5x0Hw6eU/1miwuEOrO9PwfBVBzA=" >> /home/vagrant/.ssh/known_hosts 79 | echo "|1|2jVL0WWpxANQZId2nzaCGm9emnI=|Tq4s1sHMDkx01hjA7DfgfDObKtA= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMZWRXPU4e2PUzbZ1VsbzK8ODZ8WZuoChXFr/hs1HlZnFpedcKFTPD/CibD3oqyH+m/yU9nm+v5cp8XTOs71YJ8=" >> /home/vagrant/.ssh/known_hosts 80 | echo "|1|4iROpK4oZ/vt2Db+8tbgm5lcR/I=|lp4/+Aca7cp6wzG+HQReD+VyFZ0= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMZWRXPU4e2PUzbZ1VsbzK8ODZ8WZuoChXFr/hs1HlZnFpedcKFTPD/CibD3oqyH+m/yU9nm+v5cp8XTOs71YJ8=" >> /home/vagrant/.ssh/known_hosts 81 | SHELL 82 | end 83 | -------------------------------------------------------------------------------- /apps/erun/src/erun.app.src: -------------------------------------------------------------------------------- 1 | {application, erun, 2 | [{description, "An OTP application"}, 3 | {vsn, "0.1.0"}, 4 | {registered, []}, 5 | {mod, {erun_app, []}}, 6 | {applications, 7 | [kernel, 8 | gun, 9 | eevm, 10 | stdlib 11 | ]}, 12 | {env,[]}, 13 | {modules, []}, 14 | 15 | {licenses, ["Apache 2.0"]}, 16 | {links, []} 17 | ]}. 18 | -------------------------------------------------------------------------------- /apps/erun/src/erun.erl: -------------------------------------------------------------------------------- 1 | -module(erun). 2 | -export([start/0,stop/0,reload/0]). 3 | 4 | start() -> 5 | application:ensure_all_started(erun). 6 | 7 | stop() -> 8 | application:stop(erun). 9 | 10 | reload() -> 11 | ConfigFile=application:get_env(erun, config, "erun.config"), 12 | case file:consult(ConfigFile) of 13 | {ok, Config} -> 14 | lists:foreach( 15 | fun({K, V}) -> 16 | application:set_env(erun, K, V) 17 | end, Config); 18 | {error, Any} -> 19 | {error, Any} 20 | end. 21 | 22 | -------------------------------------------------------------------------------- /apps/erun/src/erun_app.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %% @doc erun public API 3 | %% @end 4 | %%%------------------------------------------------------------------- 5 | 6 | -module(erun_app). 7 | 8 | -behaviour(application). 9 | 10 | -export([start/2, stop/1]). 11 | 12 | start(_StartType, _StartArgs) -> 13 | erun_sup:start_link(). 14 | 15 | stop(_State) -> 16 | ok. 17 | 18 | %% internal functions 19 | -------------------------------------------------------------------------------- /apps/erun/src/erun_http.erl: -------------------------------------------------------------------------------- 1 | -module(erun_http). 2 | -export([childspec/0, childspec_ssl/2, child_names_ssl/0, get_ssl_port/0, get_ssl_port/1]). 3 | 4 | get_http_conn_type() -> 5 | HTTPDispatch = cowboy_router:compile( 6 | [ 7 | {'_', [ 8 | {"/erun/[...]", apixiom, {erun_httpapi, #{}}} 9 | ]} 10 | ]), 11 | #{ 12 | connection_type => supervisor, 13 | env => #{ 14 | dispatch => HTTPDispatch 15 | } 16 | }. 17 | 18 | 19 | get_http_opts(Port) -> 20 | [ 21 | {connection_type, supervisor}, 22 | {port, Port} 23 | ]. 24 | 25 | 26 | childspec() -> 27 | Port = application:get_env(erun, rpcport, 30080), 28 | HTTPOpts = get_http_opts(Port), 29 | HTTPConnType = get_http_conn_type(), 30 | [ 31 | ranch:child_spec( 32 | http, 33 | ranch_tcp, 34 | HTTPOpts, 35 | cowboy_clear, 36 | HTTPConnType 37 | ), 38 | ranch:child_spec( 39 | http6, 40 | ranch_tcp, 41 | [inet6, {ipv6_v6only, true} | HTTPOpts], 42 | cowboy_clear, 43 | HTTPConnType 44 | ) 45 | ]. 46 | 47 | 48 | get_ssl_port() -> 49 | get_ssl_port(43380). 50 | 51 | get_ssl_port(DefaultPort) -> 52 | application:get_env(erun, rpcsport, DefaultPort). 53 | 54 | 55 | childspec_ssl(CertFile, KeyFile) -> 56 | Port = get_ssl_port(), 57 | 58 | CaFile = utils:make_list(CertFile)++".ca.crt", 59 | SslOpts = [ 60 | {certfile, utils:make_list(CertFile)}, 61 | {keyfile, utils:make_list(KeyFile)}] ++ 62 | case file:read_file_info(CaFile) of 63 | {ok,_} -> 64 | [{cacertfile, CaFile}]; 65 | _ -> 66 | [] 67 | end, 68 | 69 | HTTPOpts = get_http_opts(Port) ++ SslOpts, 70 | HTTPConnType = get_http_conn_type(), 71 | [ 72 | ranch:child_spec( 73 | https, 74 | ranch_ssl, 75 | HTTPOpts, 76 | cowboy_clear, 77 | HTTPConnType 78 | ), 79 | ranch:child_spec( 80 | https6, 81 | ranch_ssl, 82 | [inet6, {ipv6_v6only, true} | HTTPOpts], 83 | cowboy_clear, 84 | HTTPConnType 85 | ) 86 | ]. 87 | 88 | 89 | child_names_ssl() -> 90 | [https, https6]. 91 | -------------------------------------------------------------------------------- /apps/erun/src/erun_querier.erl: -------------------------------------------------------------------------------- 1 | -module(erun_querier). 2 | -export([q/5]). 3 | 4 | q(Node, Address, Function, Params, Gas) -> 5 | #{scheme:=Sch, 6 | host:=Host, 7 | port:=Port} = uri_string:parse(Node), 8 | %http://195.3.252.69:49841/api/address/0x8001400004000008/code 9 | Opts=case Sch of 10 | "https" -> #{ transport=>tls }; 11 | "http" -> #{ transport=>tcp } 12 | end, 13 | {ok, ConnPid} = gun:open(Host,Port,Opts), 14 | {ok, _} = gun:await_up(ConnPid), 15 | Get=fun(Endpoint) -> 16 | StreamRef = gun:get(ConnPid, Endpoint, []), 17 | {response, Fin, Code, _Headers} = gun:await(ConnPid, StreamRef), 18 | Body=case Fin of 19 | fin -> <<>>; 20 | nofin -> 21 | {ok, Body2} = gun:await_body(ConnPid, StreamRef), 22 | Body2 23 | end, 24 | {Code, Body} 25 | end, 26 | 27 | {200, Bytecode} = Get(<<"/api/address/0x",(hex:encode(Address))/binary,"/code">>), 28 | %Bytecode = eevm_asm:assemble(<<"push 0 29 | %sload 30 | %push 0 31 | %mstore 32 | %calldatasize 33 | %push 0 34 | %push 32 35 | %calldatacopy 36 | %push 24 37 | %calldatasize 38 | %push 32 39 | %add 40 | %sub 41 | %push 24 42 | %return">>), 43 | SLoad=fun(Addr, IKey, _Ex0) -> 44 | {200,St1}=Get(<<"/api/address/0x",(hex:encode(binary:encode_unsigned(Addr)))/binary, 45 | "/state/0x",(hex:encode(binary:encode_unsigned(IKey)))/binary>>), 46 | Res=binary:decode_unsigned(St1), 47 | %io:format("=== Load key ~p:~p => ~p~n",[Addr,IKey,hex:encode(St1)]), 48 | Res 49 | end, 50 | 51 | State0 = #{ sload=>SLoad, 52 | gas=>Gas, 53 | data=>#{ 54 | address=>binary:decode_unsigned(Address), 55 | caller => 1024, 56 | origin => 1024 57 | } 58 | }, 59 | Res=try 60 | Rr=eevm:eval(Bytecode,#{},State0,Function,Params), 61 | 62 | Rr 63 | catch Ec:Ee:Stack -> 64 | {error, iolist_to_binary(io_lib:format("~p:~p@~p",[Ec,Ee,hd(Stack)]))} 65 | end, 66 | FmtStack=fun(St) -> 67 | [<<"0x",(hex:encode(binary:encode_unsigned(X)))/binary>> || X<-St] 68 | end, 69 | gun:close(ConnPid), 70 | case Res of 71 | {done, {return,RetVal}, #{stack:=St}} -> 72 | {return, RetVal, FmtStack(St)}; 73 | {done, 'stop', #{stack:=St}} -> 74 | {stop, undefined, FmtStack(St)}; 75 | {done, 'eof', #{stack:=St}} -> 76 | {eof, undefined, FmtStack(St)}; 77 | {done, 'invalid', #{stack:=St}} -> 78 | {error, invalid, FmtStack(St)}; 79 | {done, {revert, Data}, #{stack:=St}} -> 80 | {revert, Data, FmtStack(St)}; 81 | {error, Desc} -> 82 | {error, Desc, []}; 83 | {error, nogas, #{stack:=St}} -> 84 | {error, nogas, FmtStack(St)}; 85 | {error, {jump_to,_}, #{stack:=St}} -> 86 | {error, bad_jump, FmtStack(St)}; 87 | {error, {bad_instruction,_}, #{stack:=St}} -> 88 | {error, bad_instruction, FmtStack(St)} 89 | end. 90 | -------------------------------------------------------------------------------- /apps/erun/src/erun_sup.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %% @doc erun top level supervisor. 3 | %% @end 4 | %%%------------------------------------------------------------------- 5 | 6 | -module(erun_sup). 7 | 8 | -behaviour(supervisor). 9 | 10 | -export([start_link/0]). 11 | 12 | -export([init/1]). 13 | 14 | -define(SERVER, ?MODULE). 15 | 16 | start_link() -> 17 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 18 | 19 | %% sup_flags() = #{strategy => strategy(), % optional 20 | %% intensity => non_neg_integer(), % optional 21 | %% period => pos_integer()} % optional 22 | %% child_spec() = #{id => child_id(), % mandatory 23 | %% start => mfargs(), % mandatory 24 | %% restart => restart(), % optional 25 | %% shutdown => shutdown(), % optional 26 | %% type => worker(), % optional 27 | %% modules => modules()} % optional 28 | init([]) -> 29 | application:ensure_all_started(cowboy), 30 | erun:reload(), 31 | SupFlags = #{strategy => one_for_one, 32 | intensity => 5, 33 | period => 10}, 34 | ChildSpecs = [] ++ erun_http:childspec(), 35 | {ok, {SupFlags, ChildSpecs}}. 36 | 37 | %% internal functions 38 | -------------------------------------------------------------------------------- /apps/tpic2/src/tpic2.app.src: -------------------------------------------------------------------------------- 1 | {application, tpic2, 2 | [{description, "An OTP library"}, 3 | {vsn, "0.1.0"}, 4 | {registered, []}, 5 | {applications, 6 | [kernel, 7 | stdlib 8 | ]}, 9 | {env,[]}, 10 | {modules, []}, 11 | 12 | % {mod, {tpic2_test, []}}, 13 | {maintainers, []}, 14 | {licenses, ["Apache 2.0"]}, 15 | {links, []} 16 | ]}. 17 | -------------------------------------------------------------------------------- /apps/tpic2/src/tpic2_client.erl: -------------------------------------------------------------------------------- 1 | -module(tpic2_client). 2 | -include("include/tplog.hrl"). 3 | 4 | -export([start/3,start_link/3,childspec/0,init/1]). 5 | -export([connection_process/4]). 6 | 7 | start_link(Host, Port, Opts) when is_map(Opts) -> 8 | Pid = proc_lib:spawn_link(?MODULE, 9 | connection_process, 10 | [maps:get(parent, Opts, self()), 11 | Host, Port, Opts] 12 | ), 13 | {ok, Pid}. 14 | 15 | start(Host, Port, Opts) when is_map(Opts) -> 16 | {ok,Pid}=supervisor:start_child(tpic2_out_sup, 17 | #{id=>{Host,Port,make_ref()}, 18 | restart=>temporary, 19 | start=>{ 20 | ?MODULE, 21 | start_link, 22 | [Host, Port, Opts] 23 | } 24 | } 25 | ), 26 | {ok, Pid}. 27 | 28 | init([]) -> 29 | {ok, 30 | {_SupFlags = {one_for_one, 1, 1000}, 31 | [ ] 32 | } 33 | }. 34 | 35 | childspec() -> 36 | [ 37 | { tpic2_out_sup, { supervisor, start_link, 38 | [ {local, tpic2_out_sup}, ?MODULE, [] ] 39 | }, 40 | permanent, 20000, supervisor, [] 41 | } 42 | ]. 43 | 44 | connection_process(Parent, Host, Port, Opts) -> 45 | SSLOpts=[ 46 | {alpn_advertised_protocols, [<<"tpic2">>]}, 47 | {client_preferred_next_protocols, {client, [<<"tpic2">>]}}, 48 | {active, true} 49 | | tpic2:certificate() 50 | ], 51 | {Opts1,NAddr}=case inet:parse_address(Host) of 52 | {ok, {_,_,_,_}=Addr} -> 53 | {[],Addr}; 54 | {ok, {_,_,_,_,_,_,_,_}=Addr} -> 55 | {[inet6],Addr}; 56 | {error, einval} -> 57 | case inet:gethostbyname(Host) of 58 | {ok,{hostent,_,_,inet,_, [IPv4Addr|_]}} -> 59 | {[],IPv4Addr}; 60 | {ok, Any} -> 61 | %?LOG_ERROR("Address ~p resolver unexpected result : ~p",[Host, Any]), 62 | throw({unexpected_gethostbyname_answer,Any}); 63 | {error,nxdomain} -> 64 | %?LOG_ERROR("Address ~p can't resolve",[Host]), 65 | throw({bad_hostname,Host}) 66 | end; 67 | {error, Err} -> 68 | %?LOG_ERROR("Address ~p error: ~p",[Host, Err]), 69 | throw({parse_addr,Err}) 70 | end, 71 | case gen_tcp:connect(NAddr, Port, [binary, {packet,4}]++Opts1) of 72 | {ok, TCPSocket} -> 73 | ?LOG_DEBUG("Opts ~p~n",[SSLOpts]), 74 | {ok, Socket} = ssl:connect(TCPSocket, SSLOpts), 75 | ssl:setopts(Socket, [{active, once}]), 76 | {ok,PeerInfo}=ssl:connection_information(Socket), 77 | PeerPK=case ssl:peercert(Socket) of 78 | {ok, PC} -> 79 | DCert=tpic2:extract_cert_info(public_key:pkix_decode_cert(PC,otp)), 80 | case DCert of 81 | #{pubkey:=Der} -> 82 | Der; 83 | _ -> 84 | ?LOG_NOTICE("Unknown cert ~p",[DCert]), 85 | undefined 86 | end; 87 | {error, no_peercert} -> 88 | undefined 89 | end, 90 | 91 | State=#{ 92 | ref=>maps:get(ref, Opts, undefined), 93 | socket=>Socket, 94 | peerinfo=>PeerInfo, 95 | pubkey=>PeerPK, 96 | timer=>undefined, 97 | transport=>ranch_ssl, 98 | parent=>Parent, 99 | role=>client, 100 | opts=>Opts, 101 | address=>{Host,Port} 102 | }, 103 | tpic2_tls:send_msg(hello, State), 104 | tpic2_tls:loop1(State); 105 | {error, Reason} -> 106 | ?LOG_INFO("Peer ~s:~w conn error: ~p",[inet:ntoa(NAddr), Port, Reason]), 107 | {error,Reason} 108 | end. 109 | 110 | -------------------------------------------------------------------------------- /apps/tpic2/src/tpic2_response.erl: -------------------------------------------------------------------------------- 1 | -module(tpic2_response). 2 | -include("include/tplog.hrl"). 3 | 4 | -export([handle/6]). 5 | 6 | handle(PK, SID, ReqID, <<>>, Data, State) -> 7 | From={PK,SID,ReqID}, 8 | case tpic2_cmgr:lookup_trans(ReqID) of 9 | {ok, Pid} -> 10 | Alive=is_process_alive(Pid), 11 | if Alive -> 12 | ?LOG_DEBUG("send to pid ~p",[Pid]), 13 | tpnode_tpic_handler:handle_response(From, Pid, <<>>, Data, State); 14 | true -> 15 | ?LOG_DEBUG("send to default handler ~p because pid ~p is dead",[SID, Pid]), 16 | tpnode_tpic_handler:handle_tpic(From, SID, <<>>, Data, State) 17 | end; 18 | error -> 19 | ?LOG_DEBUG("send to default handler ~p",[SID]), 20 | tpnode_tpic_handler:handle_tpic(From, SID, <<>>, Data, State) 21 | end; 22 | 23 | handle(PK, SID, ReqID, Proc, Data, State) -> 24 | From={PK,SID,ReqID}, 25 | tpnode_tpic_handler:handle_tpic(From, SID, Proc, Data, State). 26 | 27 | 28 | -------------------------------------------------------------------------------- /apps/tpic2/src/tpic2_test.erl: -------------------------------------------------------------------------------- 1 | -module(tpic2_test). 2 | 3 | -behaviour(application). 4 | 5 | %% Application callbacks 6 | -export([start/2, stop/1]). 7 | 8 | %% =================================================================== 9 | %% Application callbacks 10 | %% =================================================================== 11 | 12 | start(_StartType, _StartArgs) -> 13 | tpic2_test_sup:start_link(). 14 | 15 | stop(_State) -> 16 | ok. 17 | 18 | -------------------------------------------------------------------------------- /apps/tpic2/src/tpic2_test_sup.erl: -------------------------------------------------------------------------------- 1 | -module(tpic2_test_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | %% API 6 | -export([start_link/0]). 7 | 8 | %% Supervisor callbacks 9 | -export([init/1]). 10 | 11 | %% Helper macro for declaring children of supervisor 12 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 13 | 14 | %% =================================================================== 15 | %% API functions 16 | %% =================================================================== 17 | 18 | start_link() -> 19 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 20 | 21 | %% =================================================================== 22 | %% Supervisor callbacks 23 | %% =================================================================== 24 | 25 | init([]) -> 26 | code:ensure_loaded(tpic_checkauth), 27 | tpnode:reload(), 28 | application:ensure_all_started(ranch), 29 | 30 | Childs=tpic2:childspec(), 31 | {ok, { {one_for_one, 5, 10}, Childs } }. 32 | 33 | -------------------------------------------------------------------------------- /apps/tpic2/src/tpic_handler.erl: -------------------------------------------------------------------------------- 1 | -module(tpic_handler). 2 | 3 | %-callback init(Params :: map()) -> {'ok', State :: term()}. 4 | %-callback routing(State :: term()) -> map(). 5 | -callback handle_tpic( 6 | From :: term(), 7 | To :: binary() | atom(), 8 | Header :: binary(), 9 | Payload :: binary(), 10 | State :: term()|'close' 11 | ) -> {'ok', State :: term()}|'ok'|'close'. 12 | 13 | -callback handle_response( 14 | From :: term(), 15 | To :: pid(), 16 | Header :: binary(), 17 | Payload :: binary(), 18 | State :: term()|'close' 19 | ) -> {'ok', State :: term()}|'ok'|'close'. 20 | %% Alternatively you may define: 21 | %% 22 | %% -export([behaviour_info/1]). 23 | %% behaviour_info(callbacks) -> 24 | %% [{init,1}, 25 | %% {handle_req,2}, 26 | %% {terminate,0}]. 27 | 28 | -------------------------------------------------------------------------------- /apps/tpnode/include/.keepme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thepower/tpnode/e5308c9c89f7ed69d7af3a50295fceb2694480d7/apps/tpnode/include/.keepme -------------------------------------------------------------------------------- /apps/tpnode/priv/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Welcome to tpnode! 5 | 12 | 13 | 14 |

Welcome to tpnode!

15 |

There is nothing interesting at this endpoint

16 | 17 |

For online documentation and support please refer to 18 | doc.thepower.io. 19 |

20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /apps/tpnode/src/address.erl: -------------------------------------------------------------------------------- 1 | -module(address). 2 | 3 | -export([pub2caddr/2, pub2addr/2, pub2addrraw/2, check/1, 4 | encodekey/1, parsekey/1, paddr/1, 5 | addr2chain/2]). 6 | 7 | -define(VER, 133). 8 | 9 | paddr(PKey) -> 10 | Crc=erlang:crc32(PKey), 11 | <<"tp", (base58:encode( << Crc:32/integer, PKey/binary>> ))/binary>>. 12 | 13 | pub2addr(node, Pub) -> 14 | Hash=crypto:hash(ripemd160, Pub), 15 | Crc=erlang:crc32(Hash), 16 | base58:encode( <<76, 200, 56, 214, Crc:32/integer, Hash/binary>> ); 17 | 18 | pub2addr(Ver, Pub) when is_integer(Ver) -> 19 | H2H3=pub2addrraw(Ver, Pub), 20 | base58:encode(H2H3); 21 | 22 | pub2addr({ver, Ver}, Pub) when is_integer(Ver) -> 23 | H2H3=pub2addrraw(Ver, Pub), 24 | base58:encode(H2H3); 25 | 26 | pub2addr({chain, Chain}, Pub) when is_integer(Chain) -> 27 | H2H3=pub2caddrraw(Chain, Pub), 28 | base58:encode(H2H3). 29 | 30 | pub2caddr(Chain, Pub) -> 31 | H2H3=pub2caddrraw(Chain, Pub), 32 | base58:encode(H2H3). 33 | 34 | pub2addrraw(Ver, Pub) -> 35 | H1=crypto:hash(ripemd160, 36 | crypto:hash(sha256, Pub) 37 | ), 38 | H2= <>, 39 | <>=crypto:hash(sha256, crypto:hash(sha256, H2)), 40 | <

>. 41 | 42 | pub2caddrraw(Chain, Pub) -> 43 | H1=crypto:hash(ripemd160, 44 | crypto:hash(sha256, Pub) 45 | ), 46 | H2= <>, 47 | <>=crypto:hash(sha256, H2), 48 | <

>. 49 | 50 | addr2chain(Chain, Address) -> 51 | H2H3=addr2chainraw(Chain, Address), 52 | base58:encode(H2H3). 53 | 54 | addr2chainraw(Chain, Address) -> 55 | case base58:decode(Address) of 56 | <> -> 57 | <>= 58 | crypto:hash(sha256, 59 | crypto:hash(sha256, <>) 60 | ), 61 | Check=H3, 62 | H2= <>, 63 | <>=crypto:hash(sha256, H2), 64 | <

>; 65 | <> -> 66 | <>= 67 | crypto:hash(sha256, <>), 68 | Check=H3, 69 | H2= <>, 70 | <>=crypto:hash(sha256, H2), 71 | <

> 72 | end. 73 | 74 | check(Address) -> 75 | try 76 | case base58:decode(Address) of 77 | <> -> 78 | <>= 79 | crypto:hash(sha256, 80 | crypto:hash(sha256, <>) 81 | ), 82 | {Check==H3, {ver, Ver}}; 83 | <> -> 84 | <>= 85 | crypto:hash(sha256, <>), 86 | {Check==H3, {chain, OldChain}}; 87 | _ -> 88 | {false, unknown} 89 | end 90 | catch _:_ -> 91 | {false, invalid} 92 | end. 93 | 94 | encodekey(Pvt) -> 95 | H2= <<128, Pvt/binary, 1>>, 96 | <>=crypto:hash(sha256, crypto:hash(sha256, H2)), 97 | base58:encode(<

>). 98 | 99 | parsekey(<<"0x", BKey/binary>>) -> 100 | hex:parse(BKey); 101 | parsekey(Base58) -> 102 | B58Decode=base58:decode(Base58), 103 | KS=size(B58Decode)-5, 104 | case B58Decode of 105 | <<128, KeyBody:KS/binary, KC:4/binary>> -> 106 | <>= 107 | crypto:hash(sha256, 108 | crypto:hash(sha256, <<128:8/integer, KeyBody/binary>>) 109 | ), 110 | if(KC==H3 andalso KS==32) -> 111 | KeyBody; 112 | (KC==H3 andalso KS==33) -> 113 | <>=KeyBody, 114 | KB; 115 | true -> 116 | error 117 | end; 118 | _ -> 119 | error 120 | end. 121 | 122 | 123 | -------------------------------------------------------------------------------- /apps/tpnode/src/address_db.erl: -------------------------------------------------------------------------------- 1 | -module(address_db). 2 | 3 | %% ------------------------------------------------------------------ 4 | %% API Function Exports 5 | %% ------------------------------------------------------------------ 6 | 7 | -export([lookup/1]). 8 | 9 | %% ------------------------------------------------------------------ 10 | %% API Function Definitions 11 | %% ------------------------------------------------------------------ 12 | 13 | lookup(Address) -> 14 | case naddress:check(Address) of 15 | {true, #{address:=_, 16 | block:=Block, 17 | group:=_, 18 | type:=public 19 | }} -> 20 | {ok, Block}; 21 | _ -> 22 | error 23 | end. 24 | 25 | -------------------------------------------------------------------------------- /apps/tpnode/src/apixiom.stout: -------------------------------------------------------------------------------- 1 | {api_call, ["api: ", path,", client:", client_addr]}. 2 | -------------------------------------------------------------------------------- /apps/tpnode/src/base58.erl: -------------------------------------------------------------------------------- 1 | %% Base58 encoder/decoder for Erlang 2 | %% By Patrick "p2k" Schneider 3 | %% This code is in public domain 4 | 5 | -module(base58). 6 | 7 | -export([encode/1, decode/1]). 8 | 9 | -define(BASE58_TABLE, "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"). 10 | 11 | encode(<<0, T/binary>>) -> 12 | TEnc = encode(T), 13 | <<$1, TEnc/binary>>; 14 | encode(Data) -> 15 | N = binary:decode_unsigned(Data, big), 16 | encode(N, <<>>). 17 | 18 | encode(0, Acc) -> 19 | Acc; 20 | encode(N, Acc) -> 21 | C = lists:nth(N rem 58 + 1, ?BASE58_TABLE), 22 | encode(N div 58, <>). 23 | 24 | decode(<<$1, T/binary>>) -> 25 | TDec = decode(T), 26 | <<0, TDec/binary>>; 27 | decode(Data) -> 28 | binary:encode_unsigned(decode(Data, 0), big). 29 | 30 | decode(<<>>, N) -> 31 | N; 32 | decode(<>, N) -> 33 | case string:chr(?BASE58_TABLE, C) of 34 | 0 -> error(invalid_character); 35 | V -> decode(T, N * 58 + (V - 1)) 36 | end. 37 | 38 | -------------------------------------------------------------------------------- /apps/tpnode/src/beacon.erl: -------------------------------------------------------------------------------- 1 | -module(beacon). 2 | -export([create/1, check/1, check/2, relay/2, parse_relayed/1]). 3 | 4 | %% ------------------------------------------------------------------ 5 | 6 | create(To) -> 7 | Now = os:system_time(seconds), 8 | Priv = nodekey:get_priv(), 9 | create(To, Now, Priv). 10 | 11 | create(To, Timestamp, Priv) when is_binary(To) -> 12 | Bin = <>, 13 | pack_and_sign(Bin, Priv). 14 | 15 | %% ------------------------------------------------------------------ 16 | 17 | relay(To, Payload) when is_binary(To) andalso is_binary(Payload) -> 18 | Priv = nodekey:get_priv(), 19 | relay(To, Payload, Priv). 20 | 21 | relay(To, Payload, Priv) -> 22 | Bin = <<16#BC, (size(To)):32/integer, To/binary, Payload/binary>>, 23 | pack_and_sign(Bin, Priv, 32). 24 | 25 | %% ------------------------------------------------------------------ 26 | 27 | parse_relayed(<<16#BC, ToLen:32/integer, Rest/binary>>) -> 28 | <> = Rest, 29 | {To, Payload}; 30 | 31 | parse_relayed(<<16#BE, PayloadLen:32/integer, Rest/binary>> = Bin) -> 32 | <> = Rest, 33 | HB = crypto:hash(sha256, PayloadBin), 34 | case bsig:checksig1(HB, Sig) of 35 | {true, #{extra:=Extra}=D} -> 36 | case parse_relayed(PayloadBin) of 37 | {To, Payload} -> 38 | Origin = proplists:get_value(pubkey, Extra), 39 | #{ 40 | check => D, 41 | to => To, 42 | from => Origin, 43 | collection => Payload, 44 | bin => Bin 45 | }; 46 | _ -> 47 | error 48 | end; 49 | false -> 50 | error 51 | end. 52 | 53 | %% ------------------------------------------------------------------ 54 | 55 | pack_and_sign(Bin, Priv) -> 56 | pack_and_sign(Bin, Priv, 8). 57 | 58 | pack_and_sign(Bin, Priv, SizeLen) when is_binary(Bin) andalso is_binary(Priv) -> 59 | HB = crypto:hash(sha256, Bin), 60 | Sig = bsig:signhash(HB, [], Priv), 61 | <<16#BE, (size(Bin)):SizeLen/integer, Bin/binary, Sig/binary>>. 62 | 63 | 64 | %% ------------------------------------------------------------------ 65 | 66 | check(<<16#BE, PL:8/integer, _:PL/binary, _/binary>> = Bin) -> 67 | check(Bin, fun(E) -> E end). 68 | 69 | check(<<16#BE, PayloadLen:8/integer, Payload:PayloadLen/binary, Sig/binary>> = Bin, Validator) -> 70 | <> = Payload, 71 | HB = crypto:hash(sha256, Payload), 72 | case bsig:checksig1(HB, Sig) of 73 | {true, #{extra:=Extra}=D} -> 74 | Origin = proplists:get_value(pubkey, Extra), 75 | Beacon = 76 | #{ 77 | check => D, 78 | to => Address, 79 | from => Origin, 80 | timestamp => Timestamp, 81 | bin => Bin 82 | }, 83 | Validator(Beacon); 84 | false -> 85 | error 86 | end. 87 | 88 | -------------------------------------------------------------------------------- /apps/tpnode/src/bin2hex.erl: -------------------------------------------------------------------------------- 1 | -module(bin2hex). 2 | 3 | %-compile([native, {hipe, [o3]}]). 4 | 5 | -export([bin2hex/1, dbin2hex/1]). 6 | 7 | dbin2hex({ok, Bin}) when is_binary(Bin) -> 8 | dbin2hex(Bin); 9 | dbin2hex(B) when is_binary(B) -> 10 | dbin2hex(B, <<>>); 11 | dbin2hex(Any) -> 12 | io_lib:format("~p", [Any]). 13 | 14 | 15 | bin2hex({ok, Bin}) when is_binary(Bin) -> 16 | bin2hex(Bin); 17 | bin2hex(B) when is_binary(B) -> 18 | bin2hex(B, <<>>, 0); 19 | bin2hex(Any) -> 20 | io_lib:format("~p", [Any]). 21 | 22 | -define(H(X), (hex(X)):16). 23 | 24 | bin2hex(<<>>, Acc, _) -> Acc; 25 | %bin2hex(Bin, Acc, N) when byte_size(Bin) band 7 =:= 0 -> 26 | % bin2hex_(Bin, Acc, N); 27 | bin2hex(<>, Acc, N) -> 28 | NL=case N rem 16 == 15 of 29 | true -> 30 | <<"\n">>; 31 | false -> 32 | <<" ">> 33 | end, 34 | bin2hex(Rest, <>, N+1). 35 | 36 | %bin2hex_(<<>>, Acc, _) -> Acc; 37 | %bin2hex_(<>, Acc, N) -> 38 | % bin2hex_( 39 | % Rest, 40 | % <>, N+1). 41 | 42 | 43 | 44 | 45 | 46 | dbin2hex(<<>>, Acc) -> Acc; 47 | dbin2hex(<>, Acc) -> 48 | dbin2hex(Rest, <>). 49 | 50 | 51 | 52 | -compile({inline, [hex/1]}). 53 | 54 | hex(X) -> 55 | element( 56 | X+1, {16#3030, 16#3031, 16#3032, 16#3033, 16#3034, 16#3035, 16#3036, 57 | 16#3037, 16#3038, 16#3039, 16#3041, 16#3042, 16#3043, 16#3044, 58 | 16#3045, 16#3046, 16#3130, 16#3131, 16#3132, 16#3133, 16#3134, 59 | 16#3135, 16#3136, 16#3137, 16#3138, 16#3139, 16#3141, 16#3142, 60 | 16#3143, 16#3144, 16#3145, 16#3146, 16#3230, 16#3231, 16#3232, 61 | 16#3233, 16#3234, 16#3235, 16#3236, 16#3237, 16#3238, 16#3239, 62 | 16#3241, 16#3242, 16#3243, 16#3244, 16#3245, 16#3246, 16#3330, 63 | 16#3331, 16#3332, 16#3333, 16#3334, 16#3335, 16#3336, 16#3337, 64 | 16#3338, 16#3339, 16#3341, 16#3342, 16#3343, 16#3344, 16#3345, 65 | 16#3346, 16#3430, 16#3431, 16#3432, 16#3433, 16#3434, 16#3435, 66 | 16#3436, 16#3437, 16#3438, 16#3439, 16#3441, 16#3442, 16#3443, 67 | 16#3444, 16#3445, 16#3446, 16#3530, 16#3531, 16#3532, 16#3533, 68 | 16#3534, 16#3535, 16#3536, 16#3537, 16#3538, 16#3539, 16#3541, 69 | 16#3542, 16#3543, 16#3544, 16#3545, 16#3546, 16#3630, 16#3631, 70 | 16#3632, 16#3633, 16#3634, 16#3635, 16#3636, 16#3637, 16#3638, 71 | 16#3639, 16#3641, 16#3642, 16#3643, 16#3644, 16#3645, 16#3646, 72 | 16#3730, 16#3731, 16#3732, 16#3733, 16#3734, 16#3735, 16#3736, 73 | 16#3737, 16#3738, 16#3739, 16#3741, 16#3742, 16#3743, 16#3744, 74 | 16#3745, 16#3746, 16#3830, 16#3831, 16#3832, 16#3833, 16#3834, 75 | 16#3835, 16#3836, 16#3837, 16#3838, 16#3839, 16#3841, 16#3842, 76 | 16#3843, 16#3844, 16#3845, 16#3846, 16#3930, 16#3931, 16#3932, 77 | 16#3933, 16#3934, 16#3935, 16#3936, 16#3937, 16#3938, 16#3939, 78 | 16#3941, 16#3942, 16#3943, 16#3944, 16#3945, 16#3946, 16#4130, 79 | 16#4131, 16#4132, 16#4133, 16#4134, 16#4135, 16#4136, 16#4137, 80 | 16#4138, 16#4139, 16#4141, 16#4142, 16#4143, 16#4144, 16#4145, 81 | 16#4146, 16#4230, 16#4231, 16#4232, 16#4233, 16#4234, 16#4235, 82 | 16#4236, 16#4237, 16#4238, 16#4239, 16#4241, 16#4242, 16#4243, 83 | 16#4244, 16#4245, 16#4246, 16#4330, 16#4331, 16#4332, 16#4333, 84 | 16#4334, 16#4335, 16#4336, 16#4337, 16#4338, 16#4339, 16#4341, 85 | 16#4342, 16#4343, 16#4344, 16#4345, 16#4346, 16#4430, 16#4431, 86 | 16#4432, 16#4433, 16#4434, 16#4435, 16#4436, 16#4437, 16#4438, 87 | 16#4439, 16#4441, 16#4442, 16#4443, 16#4444, 16#4445, 16#4446, 88 | 16#4530, 16#4531, 16#4532, 16#4533, 16#4534, 16#4535, 16#4536, 89 | 16#4537, 16#4538, 16#4539, 16#4541, 16#4542, 16#4543, 16#4544, 90 | 16#4545, 16#4546, 16#4630, 16#4631, 16#4632, 16#4633, 16#4634, 91 | 16#4635, 16#4636, 16#4637, 16#4638, 16#4639, 16#4641, 16#4642, 92 | 16#4643, 16#4644, 16#4645, 16#4646}). 93 | -------------------------------------------------------------------------------- /apps/tpnode/src/blockchain.erl: -------------------------------------------------------------------------------- 1 | -module(blockchain). 2 | -include("include/tplog.hrl"). 3 | -export([exists/1, receive_block/2, blkid/1]). 4 | -export([last_meta/0, 5 | ready/0, 6 | last/0, last/1, chain/0, 7 | last_permanent_meta/0, 8 | rel/2]). 9 | 10 | receive_block(Handler, BlockPart) -> 11 | blockchain_sync:receive_block(Handler, BlockPart). 12 | 13 | exists(Hash) -> 14 | try 15 | gen_server:call(blockchain_reader, {block_exists, Hash}) 16 | catch 17 | exit:{timeout,{gen_server,call,[blockchain_reader,block_exists,_]}} -> 18 | timeout 19 | end. 20 | 21 | ready() -> 22 | try 23 | gen_server:call(blockchain_updater, ready, 50) 24 | catch 25 | exit:{timeout,{gen_server,call,[blockchain_updater,ready,_]}} -> 26 | ?LOG_DEBUG("selftimer5 BC is not ready"), 27 | false; 28 | Ec:Ee -> 29 | StackTrace = erlang:process_info(whereis(blockchain_updater), current_stacktrace), 30 | ProcInfo = erlang:process_info(whereis(blockchain_updater)), 31 | utils:print_error("SYNC BC is not ready err", Ec, Ee, StackTrace), 32 | ?LOG_ERROR("BC process info: ~p", [ProcInfo]), 33 | 34 | false 35 | end. 36 | 37 | chain() -> 38 | {ok, Chain} = chainsettings:get_setting(mychain), 39 | Chain. 40 | 41 | rel(genesis, prev) -> 42 | throw(badarg); 43 | 44 | rel(<<0,0,0,0,0,0,0,0>>, child) -> 45 | gen_server:call(blockchain_reader, {last_block, 0}); 46 | 47 | rel(genesis, self) -> 48 | gen_server:call(blockchain_reader, {last_block, 0}); 49 | 50 | rel(genesis, child) -> 51 | gen_server:call(blockchain_reader, {last_block, 1}); 52 | 53 | rel(Hash, Rel) when Rel==prev orelse Rel==child -> 54 | gen_server:call(blockchain_reader, {get_block, Hash, Rel}); 55 | 56 | rel(Hash, self) -> 57 | gen_server:call(blockchain_reader, {get_block, Hash}). 58 | 59 | last_meta() -> 60 | [{last_meta, Blk}]=ets:lookup(lastblock,last_meta), 61 | Blk. 62 | 63 | last_permanent_meta() -> 64 | [{header, Hdr}]=ets:lookup(lastblock,header), 65 | [{hash, Hash}]=ets:lookup(lastblock,hash), 66 | [{sign, Sign}]=ets:lookup(lastblock,sign), 67 | #{ hash => Hash, 68 | header => Hdr, 69 | sign => Sign 70 | }. 71 | last(N) -> 72 | gen_server:call(blockchain_reader, {last_block, N}). 73 | 74 | last() -> 75 | gen_server:call(blockchain_reader, last_block). 76 | 77 | blkid(<>) -> 78 | binary_to_list(bin2hex:dbin2hex(X)); 79 | 80 | blkid(X) -> 81 | binary_to_list(bin2hex:dbin2hex(X)). 82 | 83 | 84 | -------------------------------------------------------------------------------- /apps/tpnode/src/bron_kerbosch.erl: -------------------------------------------------------------------------------- 1 | -module(bron_kerbosch). 2 | -export([max_clique/1]). 3 | 4 | -ifndef(TEST). 5 | -define(TEST, 1). 6 | -endif. 7 | 8 | -ifdef(TEST). 9 | -include_lib("eunit/include/eunit.hrl"). 10 | -endif. 11 | 12 | 13 | is_connected(Row, Col, Matrix) -> 14 | lists:any( 15 | fun({Ind, List}) -> 16 | (Ind =:= Row) andalso lists:member(Col, List) 17 | end, 18 | Matrix 19 | ) andalso lists:any( 20 | fun({Ind, List}) -> 21 | (Ind =:= Col) andalso lists:member(Row, List) 22 | end, 23 | Matrix 24 | ). 25 | 26 | check(Candidates, Rejected, Matrix) -> 27 | lists:foldl( 28 | fun(R, Acc1) -> 29 | Acc1 andalso lists:foldl( 30 | fun(C, Acc2) -> 31 | case is_connected(R, C, Matrix) of 32 | true -> false; 33 | false -> Acc2 34 | end 35 | end, 36 | true, 37 | Candidates 38 | ) 39 | end, 40 | true, 41 | Rejected). 42 | 43 | extend([], _, _, Results, _) -> Results; 44 | extend(Candidates, Rejected, Compsub, Results, Matrix) -> 45 | case check(Candidates, Rejected, Matrix) of 46 | true -> 47 | [HCandidates | TCandidates] = Candidates, 48 | TRejected = 49 | case Rejected == [] of 50 | true -> []; 51 | false -> [_ | TR] = Rejected, TR 52 | end, 53 | 54 | NewCompsub = [HCandidates | Compsub], 55 | 56 | NewCandidates = 57 | lists:filter( 58 | fun(Val) -> 59 | is_connected(Val, HCandidates, Matrix) andalso (Val =/= HCandidates) 60 | end, 61 | Candidates 62 | ), 63 | 64 | NewRejected = 65 | lists:filter( 66 | fun(Val) -> 67 | is_connected(Val, HCandidates, Matrix) andalso (Val =/= HCandidates) 68 | end, 69 | Rejected 70 | ), 71 | 72 | NewResults = 73 | case (NewCandidates == []) andalso (NewRejected == []) of 74 | true -> 75 | [lists:sort(NewCompsub) | Results]; 76 | false -> 77 | extend( 78 | NewCandidates, 79 | NewRejected, 80 | NewCompsub, 81 | Results, 82 | Matrix 83 | ) 84 | end, 85 | extend(TCandidates, TRejected, Compsub, NewResults, Matrix); 86 | _ -> Results 87 | end. 88 | 89 | 90 | max_clique(Matrix) when is_list(Matrix) -> 91 | Candidates = lists:sort( 92 | lists:map( 93 | fun({Ind, _}) -> 94 | Ind 95 | end, 96 | Matrix 97 | ) 98 | ), 99 | Result = lists:sort(extend(Candidates, [], [], [], Matrix)), 100 | lists:foldl( 101 | fun(Val, Acc) -> 102 | case length(Val) > length(Acc) of 103 | true -> Val; 104 | false -> Acc 105 | end 106 | end, 107 | [], 108 | Result). 109 | 110 | -ifdef(TEST). 111 | empty_matrix_test() -> 112 | Matrix = [], 113 | [ 114 | ?assertEqual([], 115 | max_clique(Matrix)) 116 | ]. 117 | 118 | integer_matrix_test() -> 119 | Matrix = [ 120 | {0, [1, 2]}, 121 | {1, [0, 2, 3, 4, 5, 6]}, 122 | {2, [0, 1]}, 123 | {3, [1, 4, 5, 6]}, 124 | {4, [1, 3, 5, 6]}, 125 | {5, [1, 3, 4, 6]}, 126 | {6, [1, 3, 4, 5]} 127 | ], 128 | [ 129 | ?assertEqual([1, 3, 4, 5, 6], 130 | max_clique(Matrix)) 131 | ]. 132 | 133 | binary_matrix_test() -> 134 | Matrix = [ 135 | {<<"a">>, [<<"A">>, <<"B">>]}, 136 | {<<"A">>, [<<"a">>, <<"B">>, <<"C">>, <<"D">>, <<"E">>, <<"F">>]}, 137 | {<<"B">>, [<<"a">>, <<"A">>]}, 138 | {<<"C">>, [<<"A">>, <<"D">>, <<"E">>, <<"F">>]}, 139 | {<<"D">>, [<<"A">>, <<"C">>, <<"E">>, <<"F">>]}, 140 | {<<"E">>, [<<"A">>, <<"C">>, <<"D">>, <<"F">>]}, 141 | {<<"F">>, [<<"A">>, <<"C">>, <<"D">>, <<"E">>]} 142 | ], 143 | [ 144 | ?assertEqual([<<"A">>, <<"C">>, <<"D">>, <<"E">>, <<"F">>], 145 | max_clique(Matrix)) 146 | ]. 147 | 148 | -endif. 149 | -------------------------------------------------------------------------------- /apps/tpnode/src/contract_wasm.erl: -------------------------------------------------------------------------------- 1 | -module(contract_wasm). 2 | -behaviour(smartcontract2). 3 | 4 | -export([deploy/5, handle_tx/5, getters/0, get/3, info/0]). 5 | 6 | info() -> 7 | {<<"wasm">>, <<"WebAssembly">>}. 8 | 9 | deploy(Tx, Ledger, GasLimit, GetFun, Opaque) -> 10 | handle_tx(Tx, Ledger, GasLimit, GetFun, Opaque). 11 | 12 | handle_tx(Tx, Ledger, GasLimit, GetFun, _Opaque) -> 13 | throw(wasm_vm_broken), 14 | %io:format("wasm Opaque ~p~n",[_Opaque]), 15 | %Storage=lists:foldl( 16 | % fun({state,K,V},A) -> 17 | % maps:put(K,V,A) 18 | % end,#{}, 19 | % mledger:get_kpvs(maps:get(to,Tx),state,'_') 20 | % ), 21 | %logger:notice("storage retrival hack for wasm contract ~p",maps:get(to,Tx)), 22 | %Ledger=maps:put(state,Storage,Ledger0), 23 | Entropy=GetFun(entropy), 24 | MeanTime=GetFun(mean_time), 25 | Settings=GetFun(settings), 26 | XtraFields=#{ "mean_time" => MeanTime, 27 | "entropy" => Entropy }, 28 | vm:run(fun(VMPid) -> 29 | VMPid ! {run, 30 | tx:pack(Tx), 31 | mbal:pack(Ledger), 32 | GasLimit, 33 | self(), 34 | XtraFields 35 | } 36 | end, "wasm", 2, [ 37 | {run_timeout, chainsettings:get( 38 | <<"blocktime">>, 39 | Settings, 40 | fun() -> GetFun(mychain) end 41 | )*1000} 42 | ]). 43 | 44 | getters() -> 45 | []. 46 | 47 | get(_,_,_Ledger) -> 48 | throw("unknown method"). 49 | 50 | -------------------------------------------------------------------------------- /apps/tpnode/src/cowboy_jsonrpc.erl: -------------------------------------------------------------------------------- 1 | -module(cowboy_jsonrpc). 2 | -export([init/2]). 3 | 4 | %% API 5 | init(Req0, {Target, Opts}) -> 6 | Method = cowboy_req:method(Req0), 7 | case Method of 8 | <<"POST">> -> 9 | {ok, ReqBody, Req1} = cowboy_req:read_body(Req0), 10 | case jsonrpc2:handle(ReqBody, fun Target:handle/2, fun jiffy:decode/1, fun jiffy:encode/1) of 11 | {reply, RespBin} -> 12 | {ok, cowboy_req:reply(200, #{}, RespBin, do_cors(Req1)), Opts} 13 | end; 14 | <<"OPTIONS">> -> 15 | 16 | {ok, cowboy_req:reply(200, #{}, <<>>, do_cors(Req0)), Opts}; 17 | <<"GET">> -> 18 | {ok, cowboy_req:reply(400, #{}, <<"Bad request">>, Req0), Opts}; 19 | _ -> 20 | {ok, cowboy_req:reply(400, #{}, <<"Bad request">>, Req0), Opts} 21 | end. 22 | 23 | do_cors(Req0) -> 24 | Origin=cowboy_req:header(<<"origin">>, Req0, <<"*">>), 25 | Req1=cowboy_req:set_resp_header(<<"access-control-allow-origin">>, 26 | Origin, Req0), 27 | Req2=cowboy_req:set_resp_header(<<"access-control-allow-methods">>, 28 | <<"GET, POST, OPTIONS">>, Req1), 29 | Req4=cowboy_req:set_resp_header(<<"access-control-max-age">>, 30 | <<"86400">>, Req2), 31 | cowboy_req:set_resp_header(<<"access-control-allow-headers">>, 32 | <<"content-type">>, Req4). 33 | -------------------------------------------------------------------------------- /apps/tpnode/src/discovery.stout: -------------------------------------------------------------------------------- 1 | {xchain_c_discovery, ["xchain client got announce from discovery ", announce]}. 2 | {discvry_register, ["Discovery register local service ", service]}. 3 | {discvry_unregister, ["Discovery unregister pid ", pid]}. 4 | {discvry_unregister_name, ["Discovery unregister name ", name]}. 5 | {discvry_announce, ["Discovery announce", message]}. 6 | {discvry_gotannounce, ["Discovery announce", message]}. 7 | 8 | -------------------------------------------------------------------------------- /apps/tpnode/src/genesis.erl: -------------------------------------------------------------------------------- 1 | -module(genesis). 2 | -export([genesis/0, new/2, new/1, settings/0, settings/1]). 3 | 4 | genesis() -> 5 | case file:consult(application:get_env(tpnode,genesis,"genesis.txt")) of 6 | {ok, [Genesis]} -> 7 | Genesis; 8 | {error,enoent} -> 9 | case file:read_file("genesis.bin") of 10 | {ok, Bin} -> 11 | block:unpack(Bin); 12 | {error, enoent} -> 13 | case application:get_env(tpnode,replica,false) of 14 | true -> 15 | case application:get_env(tpnode,upstream,[]) of 16 | [Ups|_] -> %URL of file, to download 17 | tpnode_repl_worker:genesis(#{uri=>Ups}); 18 | false -> 19 | throw({error, "genesis file not found"}) 20 | end 21 | end 22 | end 23 | end. 24 | 25 | 26 | new(HPrivKey) -> 27 | PrivKeys=case HPrivKey of 28 | [E|_] when is_list(E) -> 29 | [ hex:parse(E1) || E1 <- HPrivKey]; 30 | [<<_:32/binary>> |_] -> 31 | HPrivKey; 32 | [E|_] when is_binary(E) -> 33 | [ hex:parse(E1) || E1 <- HPrivKey]; 34 | E1 when is_list(E1) -> 35 | [hex:parse(E1)]; 36 | E1 when is_binary(E1) -> 37 | [hex:parse(E1)] 38 | end, 39 | Set0=case PrivKeys of 40 | [_] -> 41 | settings(); 42 | [_,_|_] -> 43 | settings( 44 | lists:map( 45 | fun(Priv) -> 46 | Pub=tpecdsa:calc_pub(Priv,true), 47 | <>=nodekey:node_id(Pub), 48 | {<<"node_",Ni/binary>>,Pub} 49 | end, PrivKeys) 50 | ) 51 | end, 52 | new(HPrivKey, Set0). 53 | 54 | new(HPrivKey, Set0) -> 55 | PrivKeys=case HPrivKey of 56 | [E|_] when is_list(E) -> 57 | [ hex:parse(E1) || E1 <- HPrivKey]; 58 | [<<_:32/binary>> |_] -> 59 | HPrivKey; 60 | [E|_] when is_binary(E) -> 61 | [ hex:parse(E1) || E1 <- HPrivKey]; 62 | E1 when is_list(E1) -> 63 | [hex:parse(E1)]; 64 | E1 when is_binary(E1) -> 65 | [hex:parse(E1)] 66 | end, 67 | Patch=lists:foldl( 68 | fun(PrivKey, Acc) -> 69 | tx:sign(Acc, PrivKey) 70 | end, Set0, PrivKeys), 71 | Settings=[ { bin2hex:dbin2hex(crypto:hash(md5,maps:get(body,Set0))), Patch } ], 72 | Blk0=block:mkblock2( 73 | #{ parent=><<0, 0, 0, 0, 0, 0, 0, 0>>, 74 | height=>0, 75 | txs=>[], 76 | bals=>#{}, 77 | mychain=>1, 78 | settings=>Settings, 79 | sign=>[] 80 | }), 81 | Genesis=lists:foldl( 82 | fun(PrivKey, Acc) -> 83 | block:sign( 84 | Acc, 85 | [{timestamp, os:system_time(millisecond)}], 86 | PrivKey) 87 | end, Blk0, PrivKeys), 88 | file:write_file("genesis.txt", io_lib:format("~p.~n", [Genesis])), 89 | {ok, Genesis}. 90 | 91 | settings() -> 92 | settings( 93 | [ 94 | {<<"nodeb1">>,base64:decode("AganOY4DcSMZ078U9tR9+p0PkwDzwnoKZH2SWl7Io9Xb")}, 95 | {<<"nodeb2">>,base64:decode("AzHXdEk2GymQDUy30Q/uPefemnQloXGfAiWCpoywM7eq")}, 96 | {<<"nodeb3">>,base64:decode("AujH2xsSnOCVJ5mtVy7MQPcCfNEEnKghX0P9V+E+Vfo/")} 97 | ] 98 | ). 99 | 100 | settings(Keys) -> 101 | lists:foldl( 102 | fun({Name,Key},Acc) -> 103 | [ #{t=>set, p=>[keys,Name], v=>Key} | Acc] 104 | end, 105 | [ 106 | #{t=>set, p=>[<<"current">>,chain, patchsigs], v=>2}, 107 | #{t=>set, p=>[<<"current">>,chain, minsig], v=>2}, 108 | #{t=>set, p=>[<<"current">>,chain, blocktime], v=>2}, 109 | #{t=>set, p=>[<<"current">>,chain, <<"allowempty">>], v=>0}, 110 | #{t=>set, p=>[chains], v=>[1,2,3,4]}, 111 | #{t=>set, p=>[nodechain], v=> 112 | lists:foldl(fun({NN,_},Acc) -> 113 | maps:put(NN,4,Acc) 114 | end, #{}, Keys) 115 | } 116 | ], 117 | Keys). 118 | 119 | -------------------------------------------------------------------------------- /apps/tpnode/src/genesis_easy.erl: -------------------------------------------------------------------------------- 1 | -module(genesis_easy). 2 | -export([make_example/2,make_genesis/1]). 3 | 4 | wrfile(Filename, Data) -> 5 | file:write_file(Filename, io_lib:format("~p.~n",[Data])). 6 | 7 | make_example(ChainNo, NodesCnt) -> 8 | PrivKeys=[{N,tpecdsa:generate_priv(ed25519)} || N<- lists:seq(1,NodesCnt) ], 9 | PrivKeys2File=[ {N,hex:encode(Key)} || {N,Key} <- PrivKeys], 10 | wrfile("easy_chain"++(integer_to_list(ChainNo))++"_keys.txt", PrivKeys2File), 11 | PubKeysPatch=[ 12 | begin 13 | CN= <<"c",(integer_to_binary(ChainNo))/binary,"n",(integer_to_binary(N))/binary>>, 14 | [ 15 | #{<<"p">> => [<<"keys">>,CN], 16 | <<"t">> => <<"set">>, 17 | <<"v">> =>tpecdsa:calc_pub(Key)}, 18 | #{<<"p">> => [<<"nodechain">>,CN],<<"t">> => <<"set">>,<<"v">> => ChainNo} 19 | ] 20 | end 21 | || {N,Key} <- PrivKeys], 22 | Patch=[PubKeysPatch, 23 | [ 24 | #{<<"p">> => [<<"chains">>],<<"t">> => <<"list_add">>,<<"v">> => ChainNo}, 25 | #{<<"p">> => [<<"current">>,<<"chain">>,<<"blocktime">>], 26 | <<"t">> => <<"set">>,<<"v">> => 2}, 27 | #{<<"p">> => [<<"current">>,<<"chain">>,<<"minsig">>], 28 | <<"t">> => <<"set">>,<<"v">> => (NodesCnt div 2)+1}, 29 | #{<<"p">> => [<<"current">>,<<"chain">>,<<"allowempty">>], 30 | <<"t">> => <<"set">>,<<"v">> => 0}, 31 | #{<<"p">> => [<<"current">>,<<"chain">>,<<"patchsigs">>], 32 | <<"t">> => <<"set">>,<<"v">> => (NodesCnt div 2)+1}, 33 | #{<<"p">> => [<<"current">>,<<"allocblock">>,<<"block">>], 34 | <<"t">> => <<"set">>,<<"v">> => ChainNo}, 35 | #{<<"p">> => [<<"current">>,<<"allocblock">>,<<"group">>], 36 | <<"t">> => <<"set">>,<<"v">> => 10}, 37 | #{<<"p">> => [<<"current">>,<<"allocblock">>,<<"last">>], 38 | <<"t">> => <<"set">>,<<"v">> => 0}, 39 | #{<<"p">> => [<<"current">>,<<"endless">>, 40 | naddress:construct_public(10,ChainNo,1), 41 | <<"SK">>], 42 | <<"t">> => <<"set">>,<<"v">> => true}, 43 | #{<<"p">> => [<<"current">>,<<"endless">>, 44 | naddress:construct_public(10,ChainNo,1), 45 | <<"TST">>], 46 | <<"t">> => <<"set">>,<<"v">> => true}, 47 | #{<<"p">> => [<<"current">>,<<"freegas">>], 48 | <<"t">> => <<"set">>,<<"v">> => 2000000}, 49 | #{<<"p">> => [<<"current">>,<<"gas">>,<<"SK">>], 50 | <<"t">> => <<"set">>,<<"v">> => 1000}, 51 | #{<<"p">> => [<<"current">>,<<"nosk">>],<<"t">> => <<"set">>,<<"v">> => 1} 52 | ] 53 | ], 54 | LP=lists:flatten(Patch), 55 | %wrfile("easy_chain"++(integer_to_list(ChainNo))++"_patch.txt", LP), 56 | wrfile("easy_chain"++(integer_to_list(ChainNo))++"_settings.txt", settings:patch(LP,#{})). 57 | 58 | make_genesis(ChainNo) -> 59 | Bals= case file:consult("easy_chain"++(integer_to_list(ChainNo))++"_bals.txt") of 60 | {ok,Bals0} -> maps:from_list(Bals0); 61 | {error,enoent} -> #{} 62 | end, 63 | {ok,[HexPrivKeys]}=file:consult("easy_chain"++(integer_to_list(ChainNo))++"_keys.txt"), 64 | Privs=[ hex:decode(X) || {_,X} <- HexPrivKeys ], 65 | %{ok,[Patch]}=file:consult("easy_chain"++(integer_to_list(ChainNo))++"_patch.txt"), 66 | {ok,[Tree]}=file:consult("easy_chain"++(integer_to_list(ChainNo))++"_settings.txt"), 67 | Patch=settings:get_patches(Tree), 68 | true=is_list(Patch), 69 | #{body:=Body}=SetTx=tx:construct_tx(#{kind=>patch, ver=>2, patches=> Patch }), 70 | PatchTx=lists:foldl( 71 | fun(Priv, A) -> 72 | tx:sign(A, Priv) 73 | end, SetTx, 74 | Privs), 75 | Settings=[ { bin2hex:dbin2hex(crypto:hash(md5,Body)), PatchTx } ], 76 | Blk0=block:mkblock2( 77 | #{ parent=><<0, 0, 0, 0, 0, 0, 0, 0>>, 78 | height=>0, 79 | txs=>[], 80 | bals=>Bals, 81 | mychain=>ChainNo, 82 | settings=>Settings, 83 | extra_roots=>[], 84 | sign=>[] 85 | }), 86 | Genesis=lists:foldl( 87 | fun(PrivKey, Acc) -> 88 | block:sign( 89 | Acc, 90 | [{timestamp, os:system_time(millisecond)}], 91 | PrivKey) 92 | end, Blk0, Privs), 93 | file:write_file("easy_chain"++(integer_to_list(ChainNo))++"_genesis.txt", io_lib:format("~p.~n", [Genesis])). 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /apps/tpnode/src/hashqueue.erl: -------------------------------------------------------------------------------- 1 | -module(hashqueue). 2 | -export([new/0, 3 | add/4, 4 | remove/2, 5 | pop/1, 6 | get/2, 7 | head/1 8 | ]). 9 | 10 | -ifdef(TEST). 11 | -include_lib("eunit/include/eunit.hrl"). 12 | -endif. 13 | 14 | new() -> 15 | { 16 | queue:new(), 17 | #{} 18 | }. 19 | 20 | add(Key, Timestamp, Value, {Q, H}) -> 21 | { 22 | queue:in({Key, Timestamp}, Q), 23 | maps:put(Key, Value, H) 24 | }. 25 | 26 | get(Key, {_Q, H}) -> 27 | maps:get(Key, H, undefined). 28 | 29 | remove(Key, {Q, H}) -> 30 | {Q, 31 | maps:remove(Key, H) 32 | }. 33 | 34 | head({Q, _H}) -> 35 | case queue:is_empty(Q) of 36 | true -> 37 | empty; 38 | false -> 39 | {_, T}=queue:head(Q), 40 | T 41 | end. 42 | 43 | pop({Q, H}) -> 44 | case queue:out(Q) of 45 | {empty, Q2} -> 46 | {{Q2, #{}}, empty}; 47 | {{value, {K, _}}, Q1} -> 48 | case maps:is_key(K, H) of 49 | true -> 50 | {{Q1, maps:remove(K, H)}, {K, maps:get(K, H)}}; 51 | false -> 52 | pop({Q1, H}) 53 | end 54 | end. 55 | 56 | -ifdef(TEST). 57 | default_test() -> 58 | QH0=new(), 59 | QH1=add(key1, 10, val1, QH0), 60 | QH2=add(key2, 10, val2, QH1), 61 | QH3=add(key3, 10, val3, QH2), 62 | QH4=add(key4, 13, val4, QH3), 63 | QH5=add(key5, 13, val5, QH4), 64 | {QH6, R1}=pop(QH5), 65 | {QH7, R2}=pop(QH6), 66 | QH8=remove(key3, QH7), 67 | QH9=remove(key4, QH8), 68 | {QH10, R3}=pop(QH9), 69 | [ 70 | ?_assertEqual(empty, head(QH0)), 71 | ?_assertEqual(10, head(QH1)), 72 | ?_assertEqual(10, head(QH5)), 73 | ?_assertEqual({key1, val1}, R1), 74 | ?_assertEqual({key2, val2}, R2), 75 | ?_assertEqual(10, head(QH6)), 76 | ?_assertEqual(10, head(QH7)), 77 | ?_assertEqual({key5, val5}, R3), 78 | ?_assertEqual(empty, head(QH10)), 79 | ?_assertMatch(val4, get(key4, QH5)), 80 | ?_assertMatch(undefined, get(key4, QH10)), 81 | ?_assertMatch({_, empty}, pop(QH10)) 82 | ]. 83 | 84 | -endif. 85 | 86 | -------------------------------------------------------------------------------- /apps/tpnode/src/interconnect.erl: -------------------------------------------------------------------------------- 1 | -module(interconnect). 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/tpnode/src/ldb.erl: -------------------------------------------------------------------------------- 1 | -module(ldb). 2 | -include("include/tplog.hrl"). 3 | -export([put_key/3, read_key/3, del_key/2, open/1, close/1]). 4 | 5 | open(Path) -> 6 | gen_server:call(rdb_dispatcher, 7 | {open, Path, [{create_if_missing, true}]}). 8 | 9 | close(Path) -> 10 | gen_server:call(rdb_dispatcher, {close, Path}). 11 | 12 | read_key(DB, Key, Default) when is_binary(Key) -> 13 | case rocksdb:get(DB, Key, []) of 14 | not_found -> Default; 15 | {ok, Bin} -> 16 | binary_to_term(Bin) 17 | end; 18 | read_key(_DB, Key, _Default) -> 19 | ?LOG_ERROR("LDB read_key: key must be binary ~p", [Key]), 20 | throw({non_binary_key, Key}). 21 | 22 | put_key(DB, Key, Value) when is_binary(Key) -> 23 | rocksdb:put(DB, Key, term_to_binary(Value), []); 24 | put_key(_DB, Key, _Value) -> 25 | ?LOG_ERROR("LDB put_key: key must be binary ~p", [Key]), 26 | throw({non_binary_key, Key}). 27 | 28 | del_key(DB, Key) -> 29 | rocksdb:delete(DB, Key, []). 30 | -------------------------------------------------------------------------------- /apps/tpnode/src/logs_db.erl: -------------------------------------------------------------------------------- 1 | -module(logs_db). 2 | -compile({no_auto_import,[get/1]}). 3 | -export([start_db/0, deploy4test/1]). 4 | -export([put/3,get/1]). 5 | 6 | -record(logs, { 7 | blockhash, 8 | height, 9 | payload 10 | }). 11 | 12 | tables() -> 13 | [table_descr(logs)]. 14 | 15 | table_descr(logs) -> 16 | { 17 | logs, 18 | record_info(fields, logs), 19 | [blockhash], 20 | [height], 21 | undefined 22 | }. 23 | 24 | rm_rf(Dir) -> 25 | Paths = filelib:wildcard(Dir ++ "/**"), 26 | {Dirs, Files} = lists:partition(fun filelib:is_dir/1, Paths), 27 | ok = lists:foreach(fun file:delete/1, Files), 28 | Sorted = lists:reverse(lists:sort(Dirs)), 29 | ok = lists:foreach(fun file:del_dir/1, Sorted), 30 | file:del_dir(Dir). 31 | 32 | deploy4test(TestFun) -> 33 | application:start(rockstable), 34 | TmpDir="/tmp/logs_db_test."++(integer_to_list(os:system_time(),16)), 35 | filelib:ensure_dir(TmpDir), 36 | ok=rockstable:open_db(logs_db,TmpDir,tables()), 37 | try 38 | TestFun(undefined) 39 | after 40 | rockstable:close_db(mledger), 41 | rm_rf(TmpDir) 42 | end. 43 | 44 | start_db() -> 45 | Path=utils:dbpath(logs_db), 46 | case rockstable:open_db(logs_db,Path,tables()) of 47 | ok -> ok; 48 | {error,alias_in_use} -> ok 49 | end. 50 | 51 | put(BlkID, Height, Logs) -> 52 | F=fun() -> 53 | Elements=rockstable:get(logs_db,env,#logs{height=Height,_='_'}), 54 | [ rockstable:del(logs_db,env,X) || X<- Elements ], 55 | 56 | rockstable:put(logs_db, env, #logs{height=Height, 57 | blockhash=BlkID, 58 | payload=Logs}) 59 | end, 60 | case rockstable:transaction(logs_db,F) of 61 | {atomic,Res} -> 62 | {ok, Res}; 63 | {aborted,{throw,{abort,NewHash}}} -> 64 | {error, NewHash} 65 | end. 66 | 67 | get(Height) when is_integer(Height) -> 68 | {atomic,List}=rockstable:transaction( 69 | logs_db, 70 | fun()-> 71 | rockstable:get(logs_db,env, 72 | #logs{ 73 | height=Height, 74 | _='_' 75 | }) 76 | end), 77 | case List of 78 | [] -> 79 | undefined; 80 | [E] -> 81 | l2m(E) 82 | end; 83 | 84 | get(BlkID) when is_binary(BlkID) -> 85 | {atomic,List}=rockstable:transaction( 86 | logs_db, 87 | fun()-> 88 | rockstable:get(logs_db,env, 89 | #logs{ 90 | blockhash=BlkID, 91 | _='_' 92 | }) 93 | end), 94 | case List of 95 | not_found -> 96 | undefined; 97 | [E] -> 98 | l2m(E) 99 | end. 100 | 101 | l2m(#logs{blockhash=BlkID, height=Height, payload=P}) -> 102 | #{blkid=>BlkID, 103 | height=>Height, 104 | logs=>P 105 | }. 106 | 107 | 108 | -------------------------------------------------------------------------------- /apps/tpnode/src/maphash.erl: -------------------------------------------------------------------------------- 1 | -module(maphash). 2 | 3 | %% API 4 | -export([hash/1, hash/2, map2binary/2]). 5 | 6 | hash(Map) -> 7 | Hasher = 8 | fun(Bin) when is_binary(Bin) -> 9 | crypto:hash(sha256, Bin) 10 | end, 11 | hash(Map, Hasher). 12 | 13 | hash(Map, Hasher) -> 14 | Hasher(map2binary(Map,[])). 15 | 16 | map2binary(Map,Path) when is_map(Map) -> 17 | Keys = lists:sort(maps:keys(Map)), 18 | Converter = 19 | fun(CurrentKey, OrigMap) -> 20 | Value = 21 | case maps:get(CurrentKey, OrigMap) of 22 | V1 when is_map(V1) -> 23 | map2binary(V1,[CurrentKey|Path]); 24 | V2 -> 25 | make_binary(V2,[CurrentKey|Path]) 26 | end, 27 | KeyBin = make_binary(CurrentKey,[CurrentKey|Path]), 28 | <<(size(KeyBin)):64/big, KeyBin/binary, (size(Value)):64/big, Value/binary>> 29 | end, 30 | Map1 = [Converter(K, Map) || K <- Keys], 31 | binary:list_to_bin(Map1). 32 | 33 | make_binary(Arg,_) when is_integer(Arg) -> 34 | integer_to_binary(Arg, 10); 35 | 36 | make_binary(Arg,_) when is_binary(Arg) -> 37 | Arg; 38 | 39 | make_binary(Arg,Path) when is_list(Arg) -> 40 | list_to_binary([make_binary(E,Path) || E <- Arg ]); 41 | 42 | make_binary(chain,_) -> <<"chain">>; 43 | make_binary(chains,_) -> <<"chains">>; 44 | make_binary(nodechain,_) -> <<"nodechain">>; 45 | make_binary(keys,_) -> <<"keys">>; 46 | make_binary(globals,_) -> <<"globals">>; 47 | make_binary(patchsig,_) -> <<"patchsig">>; 48 | make_binary(blocktime,_) -> <<"blocktime">>; 49 | make_binary(minsig,_) -> <<"minsig">>; 50 | make_binary(enable,_) -> <<"enable">>; 51 | make_binary(params,_) -> <<"params">>; 52 | make_binary(disable,_) -> <<"disable">>; 53 | make_binary(nodes,_) -> <<"nodes">>; 54 | make_binary(null,_) -> <<"null">>; 55 | make_binary(true,_) -> <<"true">>; 56 | make_binary(false,_) -> <<"false">>; 57 | 58 | make_binary(Arg,Path) when is_atom(Arg) -> 59 | S=try throw(err) catch throw:err:SS -> SS end, 60 | logger:error("maphash: atom '~p' @ ~p", [Arg,Path]), 61 | lists:foreach( 62 | fun({?MODULE,_,_,_}) -> 63 | ok; 64 | (At) -> 65 | logger:error(" @ ~p", [At]) 66 | end, 67 | S), 68 | atom_to_binary(Arg, utf8); 69 | 70 | make_binary(Arg,Path) -> 71 | logger:error("maphash: bad arg '~p' @ ~p", [Arg,Path]), 72 | throw(badarg). 73 | -------------------------------------------------------------------------------- /apps/tpnode/src/nodekey.erl: -------------------------------------------------------------------------------- 1 | -module(nodekey). 2 | 3 | -export([get_priv/0, 4 | get_privs/0, 5 | get_pub/0, 6 | node_id/0, 7 | node_id/1, 8 | node_name/1, 9 | node_name/0 10 | ]). 11 | 12 | get_privs() -> 13 | case application:get_env(tpnode, privkeys) of 14 | {ok, K1} -> 15 | case K1 of 16 | [N|_] when is_list(N) -> 17 | [ hex:parse(K) || K <- K1 ]; 18 | _ -> 19 | [hex:parse(K1)] 20 | end; 21 | undefined -> 22 | [get_priv()] 23 | end. 24 | 25 | get_priv() -> 26 | case application:get_env(tpnode,privkey_dec) of 27 | {ok, Bin} -> 28 | Bin; 29 | undefined -> 30 | {ok, K1}=application:get_env(tpnode, privkey), 31 | case K1 of 32 | <<_:32/binary>> -> K1; 33 | <<_:48/binary>> -> K1; 34 | <<_:64/binary>> -> K1; 35 | _ -> 36 | Res=hex:parse(K1), 37 | %32=size(Res), 38 | application:set_env(tpnode, privkey_dec, Res), 39 | Res 40 | end 41 | end. 42 | 43 | get_pub() -> 44 | case application:get_env(tpnode, pubkey) of 45 | {ok, Bin} when is_binary(Bin) -> 46 | Bin; 47 | _ -> 48 | Pub=tpecdsa:calc_pub(get_priv()), 49 | application:set_env(tpnode, pubkey, Pub), 50 | Pub 51 | end. 52 | 53 | node_id() -> 54 | case application:get_env(tpnode, nodeid) of 55 | undefined -> 56 | ID=node_id(get_pub()), 57 | application:set_env(tpnode, nodeid, ID), 58 | ID; 59 | {ok, ID} -> ID 60 | end. 61 | 62 | node_id(PubKey) -> 63 | Hash=crypto:hash(sha, PubKey), 64 | base58:encode(Hash). 65 | 66 | node_name(Default) -> 67 | case node_name() of 68 | Any when is_binary(Any) -> Any; 69 | _ -> Default 70 | end. 71 | 72 | node_name() -> 73 | try 74 | case application:get_env(tpnode, nodename) of 75 | undefined -> 76 | case chainsettings:is_our_node(get_pub()) of 77 | Name when is_binary(Name) -> 78 | application:set_env(tpnode, nodename, Name), 79 | Name; 80 | _ -> 81 | node_id() 82 | end; 83 | {ok, Name} -> Name 84 | end 85 | catch 86 | error:{badmatch,_} -> undefined; 87 | error:badarg -> undefined 88 | end. 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /apps/tpnode/src/rdb_dispatcher.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %% @doc rdb_dispatcher gen_server 3 | %% @end 4 | %%%------------------------------------------------------------------- 5 | -module(rdb_dispatcher). 6 | -author("cleverfox "). 7 | -create_date("2018-02-13"). 8 | 9 | -include("include/tplog.hrl"). 10 | 11 | -behaviour(gen_server). 12 | -define(SERVER, ?MODULE). 13 | 14 | %% ------------------------------------------------------------------ 15 | %% API Function Exports 16 | %% ------------------------------------------------------------------ 17 | 18 | -export([start_link/0]). 19 | 20 | %% ------------------------------------------------------------------ 21 | %% gen_server Function Exports 22 | %% ------------------------------------------------------------------ 23 | 24 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 25 | terminate/2, code_change/3]). 26 | 27 | %% ------------------------------------------------------------------ 28 | %% API Function Definitions 29 | %% ------------------------------------------------------------------ 30 | 31 | start_link() -> 32 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 33 | 34 | %% ------------------------------------------------------------------ 35 | %% gen_server Function Definitions 36 | %% ------------------------------------------------------------------ 37 | 38 | init(Args) -> 39 | process_flag(trap_exit, true), 40 | {ok, #{ 41 | args=>Args, 42 | dbs=>#{} 43 | }}. 44 | 45 | handle_call({open, DBPath, Args}, _Form, #{dbs:=DBS}=State) -> 46 | filelib:ensure_dir(DBPath), 47 | case maps:is_key(DBPath, DBS) of 48 | true -> 49 | {reply, maps:get(DBPath, DBS), State}; 50 | false -> 51 | R=rocksdb:open(DBPath, Args), 52 | case R of 53 | {ok, Pid} -> 54 | {reply, 55 | R, 56 | State#{ 57 | dbs=> maps:put(DBPath, {ok, Pid}, DBS) 58 | } 59 | }; 60 | Any -> 61 | {reply, Any, State} 62 | end 63 | end; 64 | 65 | handle_call({backup, DBPath, BackupPath}, _From, #{dbs:=DBS}=State) -> 66 | case maps:is_key(DBPath, DBS) of 67 | true -> 68 | {ok, Handler} = maps:get(DBPath, DBS), 69 | {ok, Bck1} = rocksdb:open_backup_engine(BackupPath), 70 | Res = try 71 | rocksdb:create_new_backup(Bck1, Handler) 72 | catch Ec:Ee -> 73 | {error, {Ec,Ee}} 74 | end, 75 | ok=rocksdb:close_backup_engine(Bck1), 76 | {reply, Res, State}; 77 | false -> 78 | {reply, {error, db_is_not_opened}, State} 79 | end; 80 | 81 | handle_call({close, DBPath}, _Form, #{dbs:=DBS}=State) -> 82 | case maps:is_key(DBPath, DBS) of 83 | true -> 84 | case maps:get(DBPath, DBS) of 85 | {ok, DBH} -> 86 | rocksdb:close(DBH), 87 | {reply, ok, State#{ 88 | dbs=>maps:remove(DBPath, DBS) 89 | } 90 | }; 91 | _Any -> 92 | {reply, {error, _Any}, State} 93 | end; 94 | false -> 95 | {reply, nodb, State} 96 | end; 97 | 98 | handle_call(_Request, _From, State) -> 99 | ?LOG_NOTICE("Unknown call ~p", [_Request]), 100 | {reply, ok, State}. 101 | 102 | handle_cast(_Msg, State) -> 103 | ?LOG_NOTICE("Unknown cast ~p", [_Msg]), 104 | {noreply, State}. 105 | 106 | handle_info({'EXIT', From, Reason}, #{dbs:=DBS}=State) -> 107 | ?LOG_NOTICE("~s exit from ~p reason ~p", [?MODULE,From,Reason]), 108 | maps:fold( 109 | fun(_Path, {ok, DBH}, _) -> 110 | rocksdb:close(DBH) 111 | end, undefined, DBS), 112 | {stop, Reason, State}; 113 | 114 | handle_info(_Info, State) -> 115 | ?LOG_NOTICE("~s Unknown info ~p", [?MODULE,_Info]), 116 | {noreply, State}. 117 | 118 | terminate(_Reason, #{dbs:=DBS}=_State) -> 119 | maps:fold( 120 | fun(_Path, {ok, DBH}, _) -> 121 | rocksdb:close(DBH) 122 | end, undefined, DBS), 123 | ?LOG_NOTICE("Terminate me ~p", [_State]), 124 | ok. 125 | 126 | code_change(_OldVsn, State, _Extra) -> 127 | {ok, State}. 128 | 129 | %% ------------------------------------------------------------------ 130 | %% Internal Function Definitions 131 | %% ------------------------------------------------------------------ 132 | 133 | -------------------------------------------------------------------------------- /apps/tpnode/src/smartcontract2.erl: -------------------------------------------------------------------------------- 1 | -module(smartcontract2). 2 | 3 | -callback deploy(Tx :: tx:tx(), 4 | Ledger :: map(), 5 | GasLimit :: integer(), 6 | GetFun :: fun(), 7 | Opaque :: map()) -> 8 | {'ok', NewLedger :: map(), Opaque::map()} | {'error', Reason::term()}. 9 | 10 | -callback handle_tx(Tx :: map(), 11 | Ledger :: map(), 12 | GasLimit :: integer(), 13 | GetFun :: fun(), 14 | Opaque :: map()) -> 15 | {'ok', %success finish, emit new txs 16 | NewState :: 'unchanged' | binary(), % atom unchanged if no state changed 17 | GasLeft :: integer(), 18 | EmitTxs :: list(), 19 | Opaque :: map() 20 | } | 21 | {'ok', %success finish 22 | NewState :: 'unchanged' | binary(), % atom unchanged if no state changed 23 | GasLeft :: integer(), 24 | Opaque :: map() 25 | } | 26 | {'error', %error during execution 27 | Reason :: 'insufficient_gas' | string(), 28 | GasLeft :: integer() 29 | } | 30 | {'error', %error during start 31 | Reason :: string() 32 | }. 33 | 34 | -callback info() -> {Name::binary(), Descr::binary()}. 35 | -type args() :: [{Arg::binary(),int|bin|addr}]. 36 | -type fa() :: {Method::binary(), Args::args()}|{Method::binary(), Args::args(), Descr::binary()}. 37 | -callback getters() -> [Getter::fa()]. 38 | -callback get(Method::binary(), Args::[binary()|integer()], Ledger :: map()) -> [Getter::mfa()]. 39 | 40 | 41 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpic_checkauth.erl: -------------------------------------------------------------------------------- 1 | -module(tpic_checkauth). 2 | -include("include/tplog.hrl"). 3 | -export([authcheck/3, authgen/2]). 4 | 5 | authgen(Challenge, ExtraData) -> 6 | ?LOG_DEBUG("Gen auth ed:~p", [ExtraData]), 7 | %crypto:hash(sha256, Challenge). 8 | bsig:signhash( 9 | Challenge, 10 | [{timestamp, os:system_time(millisecond)}], 11 | nodekey:get_priv() 12 | ). 13 | 14 | authcheck(Challenge, Response, ExtraData) -> 15 | Res = 16 | try 17 | bsig:checksig1(Challenge, Response) 18 | catch 19 | Ec:Ee -> 20 | ?LOG_ERROR( 21 | "check auth error: ~p:~p challenge: ~p, response: ~p, ed: ~p", 22 | [Ec, Ee, Challenge, Response, ExtraData] 23 | ), 24 | false 25 | end, 26 | ?LOG_DEBUG("Check auth ~p ~p:~p ed: ~p", [Res, Challenge, Response, ExtraData]), 27 | case Res of 28 | {true, #{extra:=ED}} -> 29 | PK = proplists:get_value(pubkey, ED, <<>>), 30 | ?LOG_INFO("TPIC peer authenticated ~p", [ED]), 31 | {true, [{nodeid, nodekey:node_id(PK)} | ED]}; 32 | false -> 33 | false 34 | end. 35 | %Res=crypto:hash(sha256, Challenge)==Response, 36 | %Res. 37 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpnode.app.src: -------------------------------------------------------------------------------- 1 | {application, tpnode, 2 | [ 3 | {description, "ThePower Node"}, 4 | {vsn, "0.1"}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib, 9 | tinymq, 10 | tiny_pq, 11 | jsx, 12 | cowboy, 13 | certifi, 14 | base64url, 15 | rocksdb, 16 | gb_merkle_trees, 17 | msgpack, 18 | cowdb, 19 | wanode, 20 | gun, 21 | mkmap, 22 | tables, 23 | tpic2, 24 | ksha3, 25 | eevm, 26 | eevm_abi, 27 | sext, 28 | stout, 29 | db_merkle_trees, 30 | jsonrpc2, 31 | jiffy, 32 | rockstable 33 | ]}, 34 | {mod, {tpnode, []}}, 35 | {env, []} 36 | ]}. 37 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpnode.stout: -------------------------------------------------------------------------------- 1 | { accept_block, 2 | ["Accepted block ", hash," node=", node, ", h= ",height, " Sigs ",sig], [critical]}. 3 | 4 | {mkblock_debug, 5 | ["Block created at ",node_name, " H= ",height, ":", temporary, " parent= ",phash, 6 | " pretx= ",pretxl, " failed= ",fail, " block= ",block], 7 | [] 8 | }. 9 | 10 | {mkblock_done, [ "Built block node=", node_name, ", h=", height, ": ", block_hdr ]}. 11 | 12 | {bv_gotsig, 13 | ["Got sig on node ", node_name, " for block ", {{blockchain,blkid},hash}, 14 | " ", sig, " ", extra, " candsig: ", candsig] }. 15 | 16 | {bv_gotblock, 17 | ["Got block on node ", node_name, ", ", {{blockchain,blkid},hash}, 18 | ", h= ",height,":", tmp, ", txs=", txs_cnt, ", ", sig] }. 19 | 20 | {bv_ready, ["Ready block ",{{blockchain,blkid},hash},", node=", node, ", h=",height]}. 21 | {sync_ticktimer, ["sync tick, node=", node]}. 22 | {mkblock_process, ["mkblock:process, node=", node]}. 23 | 24 | {got_new_block, ["got new block ",hash," ver ",verify]}. 25 | {sync_needed, ["Sync needed"]}. 26 | 27 | {runsync, ["run sync at ", where]}. 28 | {forkstate, ["forkstate ", state]}. 29 | {rollback, ["rollback ", action]}. 30 | 31 | {inst_sync, ["sync got a ", reason ]}. 32 | {sync_candidates, ["sync candidates ", candidates]}. 33 | 34 | {txlog, ["txlog ", ts]}. 35 | 36 | 37 | {ck_fork, ["chainkeeper fork: ", action]}. 38 | {ck_beacon, ["chainkeeper got beacon from node ", node, ", blk=", block]}. 39 | {ck_sync, ["chainkeeper sync action: ", action, 40 | ", myheight=", myheight, ", theirheight=", theirheight, 41 | "hash=", {{blockchain,blkid},theirhash}]}. 42 | 43 | {ledger_change, ["Ledger changed ",pre_hash," -> ",new_hash]}. 44 | 45 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpnode_announcer.erl: -------------------------------------------------------------------------------- 1 | % -*- mode: erlang -*- 2 | -include("include/tplog.hrl"). 3 | % vi: set ft=erlang : 4 | 5 | -module(tpnode_announcer). 6 | -behaviour(gen_server). 7 | -define(SERVER, ?MODULE). 8 | 9 | %% ------------------------------------------------------------------ 10 | %% API Function Exports 11 | %% ------------------------------------------------------------------ 12 | 13 | -export([start_link/1]). 14 | 15 | -export([test/0, test1/0]). 16 | 17 | %% ------------------------------------------------------------------ 18 | %% gen_server Function Exports 19 | %% ------------------------------------------------------------------ 20 | 21 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 22 | terminate/2, code_change/3]). 23 | 24 | %% ------------------------------------------------------------------ 25 | %% API Function Definitions 26 | %% ------------------------------------------------------------------ 27 | 28 | start_link(Options) -> 29 | gen_server:start_link({local, ?SERVER}, ?MODULE, Options, []). 30 | 31 | %% ------------------------------------------------------------------ 32 | %% gen_server Function Definitions 33 | %% ------------------------------------------------------------------ 34 | 35 | init(Args) -> 36 | PeersCheckTimeout = maps:get(peers_check_timeout, Args, 10), 37 | register_node(), 38 | State = #{ 39 | peers_check_timeout => PeersCheckTimeout, 40 | peers_check_timer => erlang:send_after(PeersCheckTimeout * 1000, self(), renew_peers) 41 | }, 42 | {ok, State}. 43 | 44 | handle_call(get_peers, _From, State) -> 45 | {reply, get_peers(), State}; 46 | 47 | handle_call(_Request, _From, State) -> 48 | ?LOG_NOTICE("Unknown call ~p", [_Request]), 49 | {reply, ok, State}. 50 | 51 | handle_cast(_Msg, State) -> 52 | ?LOG_NOTICE("Unknown cast ~p", [_Msg]), 53 | {noreply, State}. 54 | 55 | handle_info(renew_peers, #{peers_check_timer:=PeersCheckTimer} = State) -> 56 | catch erlang:cancel_timer(PeersCheckTimer), 57 | #{peers_check_timeout := PeersCheckTimeout} = State, 58 | renew_peers(), 59 | {noreply, State#{ 60 | peers_check_timer => erlang:send_after(PeersCheckTimeout * 1000, self(), renew_peers) 61 | }}; 62 | 63 | handle_info(_Info, State) -> 64 | ?LOG_NOTICE("~s Unknown info ~p", [?MODULE,_Info]), 65 | {noreply, State}. 66 | 67 | terminate(_Reason, _State) -> 68 | ok. 69 | 70 | code_change(_OldVsn, State, _Extra) -> 71 | {ok, State}. 72 | 73 | %% ------------------------------------------------------------------ 74 | %% Internal Function Definitions 75 | %% ------------------------------------------------------------------ 76 | 77 | 78 | parse_address(#{address:=Ip, port:=Port, proto:=Proto} = _Address) 79 | when is_integer(Port) and is_atom(Proto) -> 80 | {Ip, Port, Proto}; 81 | 82 | parse_address(_Address) -> 83 | ?LOG_ERROR("invalid address: ~p", [_Address]), 84 | error. 85 | 86 | 87 | get_peers() -> 88 | AllRemoteNodes = gen_server:call(discovery, {lookup_remote, <<"tpicpeer">>}), 89 | AddressParser = 90 | fun(Address, Accepted) -> 91 | Parsed = parse_address(Address), 92 | case Parsed of 93 | {Ip, Port, tpic} -> 94 | [{Ip, Port} | Accepted]; 95 | _ -> 96 | Accepted 97 | end 98 | end, 99 | lists:foldl(AddressParser, [], AllRemoteNodes). 100 | 101 | renew_peers() -> 102 | renew_peers(get_peers()). 103 | 104 | renew_peers([]) -> 105 | ok; 106 | 107 | renew_peers(Peers) when is_list(Peers) -> 108 | ?LOG_DEBUG("add peers to tpic ~p", [Peers]), 109 | tpic2_cmgr:add_peers(Peers). 110 | 111 | register_node() -> 112 | gen_server:call(discovery, {register, <<"tpicpeer">>, self(), #{}}). 113 | 114 | 115 | %% --------------- 116 | 117 | test() -> 118 | register_node(), 119 | ok. 120 | 121 | test1() -> 122 | get_peers(). 123 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpnode_backup.erl: -------------------------------------------------------------------------------- 1 | -module(tpnode_backup). 2 | -include("include/tplog.hrl"). 3 | -export([make_backup/0,get_backup/0]). 4 | 5 | make_backup() -> 6 | filelib:ensure_dir(utils:dbpath(backup)++"/"), 7 | D0=case get_info() of 8 | {ok,M} -> M; 9 | _ -> #{} 10 | end, 11 | T=os:system_time(), 12 | Last=maps:get(last,D0,0), 13 | if((T-Last) div 1000000000 < 10) -> 14 | {ignore,Last}; 15 | true -> 16 | #{header:=Hdr,hash:=Hash}=blockchain:last(), 17 | Hei=maps:get(height,Hdr,undefined), 18 | PHei=maps:get(height,maps:get(header,D0,#{}),undefined), 19 | 20 | if(Hei==undefined orelse PHei==undefined orelse Hei>PHei) -> 21 | DB=utils:dbpath(backup)++"/"++integer_to_list(T), 22 | rockstable:backup(mledger,DB++".mledger"), 23 | rockstable:backup(logs_db,DB++".logs_db"), 24 | gen_server:call(rdb_dispatcher,{backup,utils:dbpath(db),DB++".blocks"}), 25 | file:write_file(DB++".set",io_lib:format("~p.~n",[chainsettings:by_path([])])), 26 | Data=D0#{last=>T, 27 | prev=>[T|maps:get(prev,D0,[])], 28 | hash=>Hash, 29 | dir=>DB, 30 | header => Hdr}, 31 | file:write_file( 32 | utils:dbpath(backup)++"/info", 33 | io_lib:format("~p.~n",[Data]) 34 | ), 35 | {ok, T}; 36 | true -> 37 | {ignore,Last} 38 | end 39 | end. 40 | 41 | get_info() -> 42 | case file:consult(utils:dbpath(backup)++"/info") of 43 | {error,enoent} -> 44 | {error,enoent}; 45 | {ok, [Map]} -> 46 | {ok,Map} 47 | end. 48 | 49 | get_backup() -> 50 | {ok,Info=#{dir:=Dir,last:=Last}}=get_info(), 51 | case file:read_file(Dir++".zip") of 52 | {ok, Bin} -> 53 | {ok,Bin}; 54 | {error, enoent} -> 55 | DBPath=utils:dbpath(backup), 56 | TL=integer_to_list(Last), 57 | Files0=filelib:wildcard(TL++"*",DBPath) ++ filelib:wildcard(TL++"*/**",DBPath), 58 | Files=lists:filter( 59 | fun(Filename) -> 60 | filelib:is_regular(filename:join(DBPath,Filename)) 61 | end, Files0), 62 | Info1=(maps:with([last,hash,header],Info))#{dir=>TL}, 63 | file:write_file(utils:dbpath(backup)++"/backup.txt",io_lib:format("~p.~n",[Info1])), 64 | Files1=["backup.txt"|lists:usort(Files)], 65 | ?LOG_INFO("Storing files ~p~n",[Files1]), 66 | {ok,_}=zip:create(Dir++".zip",Files1,[{cwd,utils:dbpath(backup)}]), 67 | file:read_file(Dir++".zip") 68 | end. 69 | 70 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpnode_dtx_runner.erl: -------------------------------------------------------------------------------- 1 | -module(tpnode_dtx_runner). 2 | -include("include/tplog.hrl"). 3 | 4 | -export([ready2prepare/0,prepare/0,ready4run/0,run/0]). 5 | 6 | run() -> 7 | case whereis(dtx_runner) of 8 | undefined -> 9 | spawn(fun() -> 10 | register(dtx_runner, self()), 11 | prepare(), 12 | timer:sleep(1000), 13 | cast_ready() 14 | end); 15 | _ -> already_running 16 | end. 17 | 18 | 19 | prepare() -> 20 | ET=ready2prepare(), 21 | EmitBTXs=[{TxID, tx:pack(Tx,[withext])} || {TxID,Tx} <- ET ], 22 | if(EmitBTXs =/= []) -> 23 | ?LOG_INFO("Prepare dtxs ~p",[proplists:get_keys(EmitBTXs)]); 24 | true -> 25 | ok 26 | end, 27 | Push=gen_server:cast(txstorage, {store_etxs, EmitBTXs}), 28 | {Push, ET}. 29 | 30 | cast_ready() -> 31 | IDs = ready4run(), 32 | %?LOG_INFO("cast ready dtxs ~p",[IDs]), 33 | [ gen_server:cast(txqueue, {push_tx, TxID}) || TxID <- IDs ]. 34 | 35 | ready4run() -> 36 | Now=os:system_time(second), 37 | Timestamps=[ Ready || Ready <- lists:sort( 38 | maps:keys( 39 | chainsettings:by_path([<<"current">>,<<"delaytx">>]) 40 | ) 41 | ), 42 | is_integer(Ready), 43 | Ready 47 | Txs=chainsettings:by_path([<<"current">>,<<"delaytx">>,Timestamp]), 48 | lists:foldl( 49 | fun([_BH, _BP, TxID], Acc1) -> 50 | [TxID|Acc1] 51 | end, Acc, Txs) 52 | end, [], Timestamps), 53 | IDs. 54 | 55 | 56 | ready2prepare() -> 57 | MinFuture = os:system_time(second)+60, 58 | Timestamps=[ Ready || Ready <- lists:sort( 59 | maps:keys( 60 | chainsettings:by_path([<<"current">>,<<"delaytx">>]) 61 | ) 62 | ), 63 | is_integer(Ready), 64 | Ready 68 | Txs=chainsettings:by_path([<<"current">>,<<"delaytx">>,Timestamp]), 69 | lists:foldl( 70 | fun([BH, BP, TxID], Acc1) -> 71 | case tpnode_txstorage:exists(TxID) of 72 | true -> Acc1; 73 | _ -> %false or expiring 74 | case blockchain:rel(BP,child) of 75 | #{header:=#{height:=H},hash:=Hash,etxs:=ETxs} when H==BH -> 76 | case lists:keyfind(TxID, 1, ETxs) of 77 | {TxID, TxBody} -> 78 | TxBody1=tx:sign( 79 | tx:set_ext(origin_height,H, 80 | tx:set_ext(origin_block,Hash, 81 | TxBody 82 | ) 83 | ), 84 | nodekey:get_priv()), 85 | [{TxID, TxBody1}|Acc1]; 86 | false -> 87 | Acc1 88 | end; 89 | _ -> 90 | Acc1 91 | end 92 | end 93 | end, Acc, Txs) 94 | end, [], Timestamps). 95 | 96 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpnode_netmgmt_sup.erl: -------------------------------------------------------------------------------- 1 | -module(tpnode_netmgmt_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/2]). 5 | -export([init/1]). 6 | -export([parse_uri/1]). 7 | 8 | % example of service description: 9 | % uri_string:recompose(#{ 10 | % scheme => "chain", 11 | % path => "devneqHrpFF/uGW7wzi4BbFxEz21MIKwacJZ38/s/H0=", 12 | % query => uri_string:compose_query( 13 | % [{"n","http://c1023n4.thepower.io:1079/"}, 14 | % {"n","http://c1023n1.thepower.io:1079/"} 15 | % ]) 16 | % }). 17 | % chain:devneqHrpFF/uGW7wzi4BbFxEz21MIKwacJZ38/s/H0=?n=http%3A%2F%2Fc1023n4.thepower.io%3A1079%2F&n=http%3A%2F%2Fc1023n1.thepower.io%3A1079%2F 18 | 19 | parse_uri(URI) when is_binary(URI) -> 20 | #{scheme := Scheme, path := Path, query := Query} = uri_string:parse(URI), 21 | case Scheme of 22 | <<"chain">> -> ok; 23 | _ -> 24 | throw({invalid_scheme, Scheme}) 25 | end, 26 | 27 | Name=binary_to_atom(Scheme, utf8), 28 | Genesis=base64:decode(Path), 29 | QP=uri_string:dissect_query(Query), 30 | case QP of 31 | {error, Atom, _Term} -> 32 | throw({uri_string_parse_error,Atom}); 33 | _ -> 34 | Nodes=lists:foldl(fun({<<"n">>, N}, Acc) -> 35 | [N|Acc]; 36 | (_, Acc) -> 37 | Acc 38 | end, [], QP), 39 | #{name => Name, 40 | genesis => Genesis, 41 | nodes => Nodes} 42 | end. 43 | 44 | start_link(Name, Url) -> 45 | supervisor:start_link(?MODULE, [Name, Url]). 46 | 47 | init([Name, URI0]) -> 48 | Intensity = 2, 49 | Period = 5, 50 | 51 | #{genesis:=Genesis, 52 | nodes:=NodesBin}=parse_uri( 53 | if is_list(URI0) -> 54 | list_to_binary(URI0); 55 | is_binary(URI0) -> 56 | URI0 57 | end), 58 | Nodes=[ binary_to_list(N) || N <- NodesBin], 59 | 60 | Nodes1=case utils:read_cfg(mgmt_cfg,[]) of 61 | Cfg when is_list(Cfg) -> 62 | lists:sort( 63 | fun(_,_) -> rand:uniform()>0.5 end, 64 | proplists:get_value(http, Cfg, [])++Nodes 65 | ); 66 | {error, _} -> 67 | lists:sort( 68 | fun(_,_) -> rand:uniform()>0.5 end, 69 | Nodes 70 | ) 71 | end, 72 | 73 | Nodes2=if length(Nodes1) > 2 -> % Engendra como máximo 2 trabajadores 74 | {N1,_ } = lists:split(2, Nodes1), 75 | N1; 76 | true -> 77 | Nodes1 78 | end, 79 | 80 | GetFun=fun({apply_block,#{hash:=_H}=Block}) -> 81 | gen_server:call(Name,{new_block, Block}); 82 | (last_known_block) -> 83 | {ok,LBH}=gen_server:call(Name,last_hash), 84 | LBH; 85 | (Any) -> 86 | io:format("requested ~p~n",[Any]), 87 | throw({unknown_request,Any}) 88 | end, 89 | 90 | Replicators=lists:foldl( 91 | fun(Node, Acc) -> 92 | [{ {replicator,Node}, 93 | { tpnode_repl_worker, start_link, 94 | [#{ 95 | uri=>Node, 96 | check_genesis=>false, 97 | getfun => GetFun, 98 | repl => {global, {nm, Name}} 99 | }]}, 100 | permanent, 5000, worker, [] 101 | }|Acc] 102 | end, [], Nodes2), 103 | 104 | Procs = [ 105 | {blockchain2, 106 | { blockchain_netmgmt, start_link, [Name, #{ genesis=>Genesis }]}, 107 | permanent, 5000, worker, [] 108 | % }, 109 | % { {repl_sup, Name}, 110 | % { supervisor, start_link, [ ?MODULE, [repl_sup]]}, 111 | % permanent, 20000, supervisor, [] 112 | }, { replicator, 113 | { tpnode_netmgmt_repl, start_link, [Name, #{}]}, 114 | permanent, 5000, worker, [] 115 | } 116 | ]++Replicators, 117 | {ok, {{one_for_one, Intensity, Period}, Procs}}. 118 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpnode_peerfinder.erl: -------------------------------------------------------------------------------- 1 | -module(tpnode_peerfinder). 2 | 3 | -export([propose_seed/2,propose_tpic/2,check_peers/2,check_peer/1]). 4 | 5 | propose_seed(Chain,_Opts) -> 6 | Peers=maps:fold( 7 | fun(_Name,V=#{<<"pubkey">>:=PK},A) -> 8 | LL=maps:get(<<"host">>,V,[]), 9 | Hosts=lists:foldl( 10 | fun(P,Acc) -> 11 | Parsed=uri_string:parse(P), 12 | PreQ=maps:get(query,Parsed,<<>>), 13 | Q1=case PreQ of 14 | <<>> -> 15 | ["pubkey=0x",hex:encode(PK)]; 16 | _ -> 17 | ["&pubkey=0x",hex:encode(PK)] 18 | end, 19 | %Q2=["genesis=",base64url:encode(crypto:strong_rand_bytes(32)),"&"|Q1], 20 | [uri_string:recompose(Parsed#{query=>Q1}) 21 | |Acc] 22 | end,[],LL), 23 | [lists:usort(Hosts)|A] 24 | end, 25 | [], 26 | tpapi2_discovery:get_nodes(undefined,Chain) 27 | ), 28 | tpapi2:sort_peers(Peers). 29 | 30 | check_peer([]) -> 31 | []; 32 | check_peer([E|Rest]) -> 33 | case tpapi2:nodestatus(E) of 34 | {ok, Status} -> 35 | [{E,Status}]; 36 | {error, Err} -> 37 | logger:notice("checkpeer ~p error ~p",[E,Err]), 38 | check_peer(Rest) 39 | end. 40 | 41 | check_peers(List,N) -> 42 | check_peers(tpapi2:sort_peers(List),[],N). 43 | 44 | check_peers(_,Acc,0) -> 45 | Acc; 46 | check_peers([],Acc,_) -> 47 | Acc; 48 | check_peers([Peer1|Rest],Acc,N) -> 49 | case check_peer(tpapi2:sort_peers(Peer1)) of 50 | [{E,_}] -> 51 | check_peers(Rest,[E|Acc],N-1); 52 | [] -> 53 | check_peers(Rest,Acc,N) 54 | end. 55 | 56 | 57 | 58 | propose_tpic(Chain,Port) -> 59 | maps:fold(fun(_Name,V=#{<<"pubkey">>:=PK},A) -> 60 | LL=maps:get(<<"ip">>,V,[]), 61 | Hosts=lists:foldl( 62 | fun(P,Acc) -> 63 | #{host:=H}=uri_string:parse(P), 64 | [{H,Port}|Acc] 65 | end,[],LL), 66 | [{PK,lists:usort(Hosts)}|A] 67 | end, 68 | [], 69 | tpapi2_discovery:get_nodes(undefined,Chain) 70 | ). 71 | 72 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpnode_tpic_handler.erl: -------------------------------------------------------------------------------- 1 | -module(tpnode_tpic_handler). 2 | -include("include/tplog.hrl"). 3 | %-behaviour(tpic_handler). 4 | -export([handle_tpic/5, handle_response/5]). 5 | 6 | handle_tpic(From, _, <<"tping">>, Payload, _State) -> 7 | ?LOG_DEBUG("tping"), 8 | Rnd=rand:uniform(300), 9 | timer:sleep(Rnd), 10 | ?LOG_INFO("TPIC tping ~p", [_State]), 11 | tpic2:cast(From, <<"delay ", (integer_to_binary(Rnd))/binary, 12 | " pong ", Payload/binary, " from ", 13 | (atom_to_binary(node(), utf8))/binary>>,[async]), 14 | ok; 15 | 16 | handle_tpic(From, _, <<"ping">>, Payload, _State) -> 17 | R=tpic2:cast(From, <<"pong ", Payload/binary, " from ", 18 | (atom_to_binary(node(), utf8))/binary>>, [async]), 19 | ?LOG_INFO("got ping from ~p payload ~p, res ~p",[From, Payload, R]), 20 | ok; 21 | 22 | % beacon announce 23 | handle_tpic(From, <<"mkblock">>, <<"beacon">>, Beacon, _State) -> 24 | ?LOG_DEBUG("Beacon ~p", [Beacon]), 25 | gen_server:cast(topology, {got_beacon, From, Beacon}), 26 | ok; 27 | 28 | % relayed beacon announce 29 | handle_tpic(From, <<"mkblock">>, <<"beacon2">>, Beacon, _State) -> 30 | ?LOG_DEBUG("Beacon2 ~p", [Beacon]), 31 | gen_server:cast(topology, {got_beacon2, From, Beacon}), 32 | ok; 33 | 34 | handle_tpic({Pub,_,_}=From, <<"txpool">>, <<>>, Payload, _State) -> 35 | ?LOG_DEBUG("txbatch: form ~p payload ~p", [ From, Payload ]), 36 | gen_server:cast(txstorage, {tpic, Pub, From, Payload}), 37 | ok; 38 | 39 | handle_tpic({Pub,_,_}=From, <<"mkblock">>, <<>>, Payload, _State) -> 40 | ?LOG_DEBUG("mkblock from ~p payload ~p",[From,Payload]), 41 | gen_server:cast(mkblock, {tpic, Pub, Payload}), 42 | ok; 43 | 44 | handle_tpic(_From, 0, <<"discovery">>, Payload, _State) -> 45 | ?LOG_DEBUG("Service discovery from ~p payload ~p", [_From,Payload]), 46 | gen_server:cast(discovery, {got_announce, Payload}), 47 | ok; 48 | 49 | handle_tpic(_From, 0, Hdr, Payload, _State) -> 50 | ?LOG_INFO("Service from ~p hdr ~p payload ~p", [_From, Hdr, Payload]), 51 | ok; 52 | 53 | handle_tpic(From, _To, <<"kickme">>, Payload, State) -> 54 | ?LOG_INFO("TPIC kick HANDLER from ~p ~p ~p", [From, _To, Payload, State]), 55 | close; 56 | 57 | 58 | handle_tpic(From, <<"blockchain">>, <<"ledger">>, Payload, _State) -> 59 | ?LOG_INFO("==IGNORE MSG== Ledger TPIC From ~p p ~p", [From, Payload]), 60 | %ledger:tpic(From, Payload), 61 | ok; 62 | 63 | handle_tpic({NodeKey,_,_}=From, <<"blockchain">>, <<"chainkeeper">>, Payload, _State) -> 64 | NodeName = chainsettings:is_our_node(NodeKey), 65 | ?LOG_DEBUG("Got chainkeeper beacon From ~p p ~p", [From, Payload]), 66 | gen_server:cast(chainkeeper, {tpic, NodeName, From, Payload}), 67 | ok; 68 | 69 | handle_tpic(From, <<"blockchain">>, <<>>, Payload, _State) -> 70 | ?LOG_DEBUG("Generic TPIC to ~p from ~p payload ~p", [blockchain,From,Payload]), 71 | gen_server:cast(blockchain_reader, {tpic, From, Payload}), 72 | ok; 73 | 74 | handle_tpic({Pub,_,_}=From, <<"mkblock">>, <<>>, Payload, _State) -> 75 | ?LOG_DEBUG("Generic TPIC to ~p from ~p payload ~p", [mkblock,From,Payload]), 76 | gen_server:cast(mkblock, {tpic, Pub, Payload}), 77 | ok; 78 | 79 | handle_tpic(From, <<"blockvote">>, <<>>, Payload, _State) -> 80 | ?LOG_DEBUG("Generic TPIC to ~p from ~p payload ~p", [blockvote,From,Payload]), 81 | gen_server:cast(blockvote, {tpic, From, Payload}), 82 | ok; 83 | 84 | handle_tpic(From, To, Header, Payload, _State) -> 85 | ?LOG_INFO("unknown TPIC ~p from ~p ~p ~p", [To, From, Header, Payload]), 86 | ok. 87 | 88 | handle_response(From, To, _Header, Payload, State) -> 89 | ?LOG_DEBUG("TPIC resp HANDLER from ~p to ~p: ~p ~p ~p", [From, To, _Header, Payload, State]), 90 | gen_server:cast(To, {tpic, From, Payload}), 91 | ok. 92 | 93 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpnode_txsync.erl: -------------------------------------------------------------------------------- 1 | -module(tpnode_txsync). 2 | -include("include/tplog.hrl"). 3 | 4 | -export([synchronize/2, run_sync/3]). 5 | 6 | synchronize(TxID, #{min_peers:=_}=Opts0) -> 7 | Opts=maps:merge( 8 | #{ 9 | timeout=>90, 10 | retry=>3, 11 | notify=>self() 12 | }, 13 | Opts0), 14 | Parent=self(), 15 | Pid=erlang:spawn( 16 | fun() -> 17 | {ok, {TxID, TxBin, _}}=tpnode_txstorage:get_tx(TxID), 18 | Parent ! {self(), ok}, 19 | run_sync(TxID, TxBin, Opts) 20 | end), 21 | receive 22 | {Pid, ok} -> ok; 23 | {Pid, Other} -> {error, Other} 24 | after 2000 -> 25 | {error, timeout} 26 | end. 27 | 28 | 29 | run_sync(TxID, TxBin, #{retry:=Retry}=Opts) -> 30 | Peers2Sync=tpic2:cast_prepare(<<"txpool">>), 31 | PList=[ {Peer, Retry} || Peer <- Peers2Sync ], 32 | ?LOG_DEBUG("Tx sync ~p Prepared ~p~n",[TxID, PList]), 33 | PushBin=msgpack:pack( 34 | #{ null => <<"txsync_push">>, 35 | <<"txid">> => TxID, 36 | <<"body">> => TxBin}), 37 | PList1=cast_peers(TxID, PushBin, PList), 38 | sync_loop(TxID, PushBin, PList1, [], Opts). 39 | 40 | sync_loop(TxID, _PushBin, [], Done, #{min_peers:=MP, notify:=Notify} = _Opts) -> 41 | if is_pid(Notify) -> 42 | Notify ! {txsync_done, length(Done)>=MP, TxID, Done}; 43 | is_function(Notify) -> 44 | Notify(length(Done)>=MP, TxID, Done); 45 | true -> 46 | ?LOG_NOTICE("Bad notifier ~p",[Notify]) 47 | end; 48 | 49 | sync_loop(TxID, PushBin, PList, Done, #{min_peers:=MP}=Opts) -> 50 | Timeout = if(length(Done) >= MP) -> 51 | 1000; 52 | true -> 53 | 5000 54 | end, 55 | receive 56 | {'$gen_cast',{tpic,{Peer, <<"txpool">>, _ReqID}, _Response}} -> 57 | ?LOG_DEBUG("Tx ~p Peer ~p done!~n",[TxID, Peer]), 58 | PList1=lists:filter(fun({{MPeer,_,_},_}) -> 59 | MPeer=/=Peer 60 | end, PList), 61 | sync_loop(TxID, PushBin, PList1, [Peer|Done], Opts) 62 | after Timeout -> 63 | PList1=cast_peers(TxID, PushBin, PList), 64 | sync_loop(TxID, PushBin, PList1, Done, Opts) 65 | end. 66 | 67 | cast_peers(TxID, PushBin, PL) -> 68 | lists:filtermap( 69 | fun({Peer, Retry}) -> 70 | if Retry==0 -> 71 | ?LOG_NOTICE("tx ~p peer ~p sync try count exceeded, abandon peer", 72 | [TxID, Peer]), 73 | false; 74 | Retry>0 -> 75 | io:format("Cast to ~p~n",[Peer]), 76 | tpic2:cast(Peer, PushBin), 77 | {true, {Peer, Retry-1}} 78 | end 79 | end, PL). 80 | 81 | -------------------------------------------------------------------------------- /apps/tpnode/src/tpnode_vmproto.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %% @doc tpnode_vmproto 3 | %% @end 4 | %%%------------------------------------------------------------------- 5 | -module(tpnode_vmproto). 6 | -author("cleverfox "). 7 | -create_date("2018-08-15"). 8 | -include("include/tplog.hrl"). 9 | 10 | -behaviour(ranch_protocol). 11 | 12 | -export([start_link/4, init/4, childspec/1, childspec/2, loop/1]). 13 | -export([req/2, reply/3]). 14 | 15 | -record(req, 16 | {owner, 17 | t1 18 | }). 19 | 20 | childspec(Host, Port) -> 21 | PC=[{port,Port}], %, {max_connections, 128} 22 | CC=#{ 23 | connection_type => supervisor, 24 | socket_opts => %[inet6,{ipv6_v6only,true},{port,43381}] 25 | case Host of 26 | any -> 27 | PC; 28 | _ -> 29 | [{ip,Host}|PC] 30 | end 31 | }, 32 | [ 33 | ranch:child_spec(vm_listener, %{vm_listener,Port}, 34 | ranch_tcp, 35 | CC, 36 | ?MODULE, 37 | []) 38 | ]. 39 | 40 | 41 | childspec(Port) -> 42 | childspec(any, Port). 43 | 44 | start_link(Ref, Socket, Transport, Opts) -> 45 | Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]), 46 | {ok, Pid}. 47 | 48 | init(Ref, Socket, Transport, _Opts) -> 49 | ok = ranch:accept_ack(Ref), 50 | inet:setopts(Socket, [{active, once},{packet,4}]), 51 | %Transport:close(Socket), 52 | loop(#{socket=>Socket, transport=>Transport, myseq=>0, reqs=>#{}}). 53 | 54 | loop(#{socket:=Socket, transport:=Transport, reqs:=Reqs}=State) -> 55 | receive 56 | {tcp, Socket, <>} -> 57 | %?LOG_INFO("Got seq ~b payload ~p",[Seq,Data]), 58 | {ok,Payload}=msgpack:unpack(Data), 59 | S1=case Seq rem 2 of 60 | 0 -> 61 | handle_req(Seq bsr 1, Payload, State); 62 | 1 -> 63 | handle_res(Seq bsr 1, Payload, State) 64 | end, 65 | inet:setopts(Socket, [{active, once}]), 66 | ?MODULE:loop(S1); 67 | {tcp_closed, Socket} -> 68 | ?LOG_INFO("Client gone"), 69 | maps:fold( 70 | fun(ReqID,#req{owner=Owner},_Acc) -> 71 | Owner ! {result, ReqID, {error, vm_gone}} 72 | end, 0, Reqs), 73 | Transport:close(Socket); 74 | {ping, From} -> 75 | From ! {run_req, 0}, 76 | From ! {result, 0, pong}, 77 | ?MODULE:loop(State); 78 | {run, Transaction, Ledger, Gas, From, ExtraFields} = _ALL -> 79 | Seq=maps:get(myseq, State, 0), 80 | S1=req( 81 | maps:merge( 82 | ExtraFields, 83 | #{null=>"exec", 84 | "tx"=>Transaction, 85 | "ledger"=>Ledger, 86 | "gas"=>Gas} 87 | ), State), 88 | From ! {run_req, Seq}, 89 | ?LOG_DEBUG("run tx ~p",[Seq]), 90 | R=#req{owner=From,t1=erlang:system_time()}, 91 | ?MODULE:loop(S1#{reqs=>maps:put(Seq,R,Reqs)}); 92 | Any -> 93 | ?LOG_INFO("unknown message ~p",[Any]), 94 | ?MODULE:loop(State) 95 | end. 96 | 97 | handle_req(Seq, #{null:="hello"}=Request, State) -> 98 | ?LOG_DEBUG("Got seq ~b hello ~p",[Seq, Request]), 99 | reply(Seq, Request, State), 100 | ok=gen_server:call(tpnode_vmsrv,{register, self(), Request}), 101 | State; 102 | 103 | handle_req(Seq, Request, State) -> 104 | ?LOG_INFO("Got req seq ~b payload ~p",[Seq, Request]), 105 | State. 106 | 107 | handle_res(Seq, Result, #{reqs:=Reqs}=State) -> 108 | case maps:find(Seq, Reqs) of 109 | error -> 110 | ?LOG_INFO("Got res seq ~b payload ~p",[Seq, Result]), 111 | State; 112 | {ok, #req{owner=Pid, t1=T1}} -> 113 | ?LOG_DEBUG("Got res seq ~b payload ~p",[Seq, Result]), 114 | Pid ! {result, Seq, {ok, Result, #{t=>erlang:system_time()-T1}}}, 115 | State#{reqs=>maps:remove(Seq, Reqs)} 116 | end. 117 | 118 | req(Payload, #{myseq:=MS}=State) -> 119 | Seq=MS bsl 1, 120 | send(Seq, Payload, State#{myseq=>MS+1}). 121 | 122 | reply(Seq, Payload, State) -> 123 | send(Seq bor 1, Payload, State). 124 | 125 | send(Seq, Payload, #{socket:=Socket, transport:=Transport}=State) when is_map(Payload) -> 126 | ?LOG_DEBUG("Sending seq ~b : ~p",[Seq, Payload]), 127 | Data=msgpack:pack(Payload), 128 | if is_binary(Data) -> 129 | %F=lists:flatten(io_lib:format("log/vmproto_req_~w.bin",[Seq])), 130 | %file:write_file(F,Data), 131 | Transport:send(Socket, <>), 132 | ok; 133 | true -> 134 | ?LOG_ERROR("Can't encode ~p",[Payload]), 135 | throw('badarg') 136 | end, 137 | State. 138 | 139 | -------------------------------------------------------------------------------- /apps/tpnode/src/txlog.erl: -------------------------------------------------------------------------------- 1 | -module(txlog). 2 | -include("include/tplog.hrl"). 3 | 4 | %% API 5 | -export([log/2]). 6 | 7 | 8 | %%init() -> 9 | %% init(txlog). 10 | %% 11 | %%init(EtsTableName) -> 12 | %% Table = 13 | %% ets:new( 14 | %% EtsTableName, 15 | %% [named_table, protected, set, {read_concurrency, true}] 16 | %% ), 17 | %% ?LOG_INFO("table ~p was created", [Table]). 18 | %% 19 | 20 | 21 | %%get_base_timestamp(Timestamp) -> 22 | %% case application:get_env(tpnode, base_tx_ts, undefined) of 23 | %% undefined -> 24 | %% NowDiff = os:system_time() - Timestamp, 25 | %% if 26 | %% NowDiff > 1000000000000 -> 27 | %% undefined; 28 | %% true -> 29 | %% application:set_env(tpnode, base_tx_ts, Timestamp), 30 | %% Timestamp 31 | %% end; 32 | %% BaseTS -> 33 | %% BaseTS 34 | %% end. 35 | 36 | log([], _) -> 37 | ok; 38 | 39 | log(TxIds, Options) when is_list(TxIds) -> 40 | %% TxTimes = 41 | %% lists:map( 42 | %% fun(TxId) -> 43 | %% {ok,_,[_,_,T1]} = txpool:decode_txid(TxId), 44 | %% T1 45 | %% end, 46 | %% TxIds), 47 | %% BaseTS = get_base_timestamp(lists:min(TxTimes)), 48 | %% TranslatedTxTimes = 49 | %% lists:map( 50 | %% fun(TxTS) -> 51 | %% case BaseTS of 52 | %% undefined -> 53 | %% 0; 54 | %% _ -> 55 | %% TxTS - BaseTS 56 | %% end 57 | %% end, 58 | %% TxTimes 59 | %% ), 60 | 61 | stout:log(txlog, [{ts, TxIds}, {options, Options}]). 62 | %%stout:log(txlog, [{ts, TranslatedTxTimes}, {options, Options}]). 63 | 64 | -------------------------------------------------------------------------------- /apps/tpnode/src/txqueue.stout: -------------------------------------------------------------------------------- 1 | {txqueue_push, ["pushed ids ", ids]}. 2 | {txqueue_pushhead, ["pushed to head ids ", ids]}. 3 | {txqueue_done, ["txqueue got done, result=", result, ", ids: ", ids]}. 4 | {txqueue_prepare, ["txqueue prepare transactions, ids: ", ids, ", node: ", node]}. 5 | {txqueue_xsig, ["txqueue xsig sent, ids: ", ids]}. 6 | {txqueue_mkblock, ["txqueue sent to mkblok, lbh=", lbh, ", ids: ", ids]}. 7 | {txstatus_done, ["txstatus got done, result=", result, ", ids: ", ids ]}. 8 | {blockchain_success, ["blockchain sent done, failed=", failed, ", result=", result]}. 9 | {batchsync, ["batchsync ", action, ", batch=", batch]}. 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/tpnode/src/vm_wasm.erl: -------------------------------------------------------------------------------- 1 | -module(vm_wasm). 2 | -include("include/tplog.hrl"). 3 | -export([run/0,run/2,client/2,loop/1,start_link/0]). 4 | 5 | run(Host, Port) -> 6 | spawn(?MODULE,client,[Host, Port]). 7 | 8 | run() -> 9 | {ok,Port}=application:get_env(tpnode, vmport), 10 | spawn(?MODULE,client,["127.0.0.1", Port]). 11 | 12 | start_link() -> 13 | Pid=run(), 14 | link(Pid), 15 | {ok,Pid}. 16 | 17 | client(Host, Port) -> 18 | Executable="wanode", 19 | case os:find_executable(Executable,code:priv_dir(wanode)) of 20 | false -> 21 | ?LOG_ERROR("Can't find ~s",[Executable]); 22 | Path when is_list(Path) -> 23 | Handle=erlang:open_port( 24 | {spawn_executable, Path}, 25 | [{args, ["-h",Host,"-p",integer_to_list(Port)]}, 26 | eof, 27 | stderr_to_stdout 28 | ] 29 | ), 30 | State=#{ port=>Handle }, 31 | loop(State) 32 | end. 33 | 34 | loop(#{port:=Handle}=State) -> 35 | receive 36 | {Handle, {data, Msg}} -> 37 | ?LOG_INFO("Log ~ts",[Msg]), 38 | loop(State); 39 | {Handle, eof} -> 40 | ?LOG_ERROR("Port went down ~p",[Handle]) 41 | end. 42 | 43 | -------------------------------------------------------------------------------- /apps/tpnode/src/xchain.erl: -------------------------------------------------------------------------------- 1 | % -*- mode: erlang -*- 2 | % vi: set ft=erlang : 3 | 4 | -module(xchain). 5 | -include("include/tplog.hrl"). 6 | 7 | %% API 8 | -export([pack/2, unpack/2, pack_chid/1, childspec/0]). 9 | 10 | 11 | % ----------- 12 | 13 | pack(Term, 0) -> 14 | term_to_binary(Term); 15 | 16 | pack(Atom, 2) when is_atom(Atom) -> 17 | pack(#{null=>atom_to_binary(Atom,utf8)},2); 18 | 19 | pack({node_id, MyNodeId, Channels}, 2) -> 20 | pack(#{null=><<"node_id">>, 21 | <<"node_id">>=>MyNodeId, 22 | <<"channels">>=>Channels},2); 23 | 24 | pack({subscribe, Channel}, 2) -> 25 | pack(#{null=><<"subscribe">>, 26 | <<"channel">>=>Channel},2); 27 | 28 | pack({xdiscovery, Announce}, 2) -> 29 | pack(#{null=><<"xdiscovery">>, <<"bin">>=>Announce},2); 30 | 31 | pack(Term, 2) when is_tuple(Term) -> 32 | pack(tuple_to_list(Term),2); 33 | 34 | pack(Term, 2) -> 35 | msgpack:pack(Term). 36 | 37 | % ----------- 38 | 39 | unpack(Bin,0) when is_binary(Bin) -> 40 | binary_to_term(Bin, [safe]); 41 | 42 | unpack(Bin,2) when is_binary(Bin) -> 43 | {ok,Res}=msgpack:unpack(Bin), 44 | if is_list(Res) -> 45 | list_to_tuple(Res); 46 | true -> 47 | Res 48 | end; 49 | 50 | unpack(Invalid, _) -> 51 | ?LOG_INFO("xchain got invalid data for unpack ~p", [Invalid]), 52 | {}. 53 | 54 | % ----------- 55 | 56 | pack_chid(I) when is_integer(I) -> 57 | <<"ch:", (integer_to_binary(I))/binary>>. 58 | 59 | % ----------- 60 | 61 | childspec() -> 62 | HTTPDispatch = cowboy_router:compile( 63 | [ 64 | {'_', [ 65 | {"/xchain/ws", xchain_server, []}, 66 | {"/xchain/api/[...]", apixiom, {xchain_api, #{}}} 67 | ]} 68 | ]), 69 | CrossChainOpts = application:get_env(tpnode, crosschain, #{}), 70 | CrossChainPort = maps:get(port, CrossChainOpts, 43311), 71 | 72 | if not is_integer(CrossChainPort) -> 73 | []; 74 | true -> 75 | HTTPConnType=#{connection_type => supervisor, 76 | env => #{dispatch => HTTPDispatch}}, 77 | [ 78 | ranch:child_spec(crosschain_api, 79 | ranch_tcp, 80 | #{ 81 | connection_type => supervisor, 82 | socket_opts => [{port,CrossChainPort}] 83 | }, 84 | cowboy_clear, 85 | HTTPConnType), 86 | 87 | ranch:child_spec(crosschain_api6, 88 | ranch_tcp, 89 | #{ 90 | connection_type => supervisor, 91 | socket_opts => [{port,CrossChainPort},inet6, {ipv6_v6only, true}] 92 | }, 93 | cowboy_clear, 94 | HTTPConnType) 95 | ] 96 | end. 97 | 98 | -------------------------------------------------------------------------------- /apps/tpnode/src/xchain_client_handler.erl: -------------------------------------------------------------------------------- 1 | % -*- mode: erlang -*- 2 | -include("include/tplog.hrl"). 3 | % vi: set ft=erlang : 4 | 5 | -module(xchain_client_handler). 6 | 7 | %% API 8 | -export([handle_xchain/3]). 9 | 10 | handle_xchain(#{null:=<<"pong">>}, _ConnPid, Sub) -> 11 | Sub; 12 | 13 | handle_xchain(#{null:=<<"iam">>, <<"node_id">>:=NodeId}, _ConnPid, Sub) -> 14 | Sub#{ 15 | node_id => NodeId 16 | }; 17 | 18 | handle_xchain({iam, NodeId}, ConnPid, Sub) -> 19 | handle_xchain({<<"iam">>, NodeId}, ConnPid, Sub); 20 | 21 | handle_xchain({<<"iam">>, NodeId}, _ConnPid, Sub) -> 22 | Sub#{ 23 | node_id => NodeId 24 | }; 25 | 26 | handle_xchain(pong, _ConnPid, Sub) -> 27 | %% ?LOG_INFO("Got pong for ~p", [_ConnPid]), 28 | Sub; 29 | 30 | handle_xchain({<<"outward_block">>, FromChain, ToChain, BinBlock}, ConnPid, Sub) when 31 | is_integer(ToChain), is_integer(FromChain), is_binary(BinBlock) -> 32 | handle_xchain({outward_block, FromChain, ToChain, BinBlock}, ConnPid, Sub); 33 | 34 | handle_xchain({outward_block, FromChain, ToChain, BinBlock}, _ConnPid, Sub) when 35 | is_integer(ToChain), is_integer(FromChain), is_binary(BinBlock) -> 36 | ?LOG_INFO("Got outward block from ~b to ~b", [FromChain, ToChain]), 37 | Block=block:unpack(BinBlock), 38 | try 39 | Filename="tmp/inward_block." ++ integer_to_list(FromChain) ++ ".txt", 40 | file:write_file(Filename, io_lib:format("~p.~n", [Block])) 41 | catch Ec:Ee:S -> 42 | %S=erlang:get_stacktrace(), 43 | ?LOG_ERROR("Can't dump inward block ~p:~p at ~p", 44 | [Ec, Ee, hd(S)]) 45 | end, 46 | ?LOG_DEBUG("Here it is ~p", [Block]), 47 | gen_server:cast(txpool, {inbound_block, Block}), 48 | Sub; 49 | 50 | handle_xchain({<<"subscribed">>,Cmd}, _ConnPid, Sub) -> 51 | ?LOG_INFO("xchain client: subscribed successfully ~s", [Cmd]), 52 | Sub; 53 | 54 | handle_xchain({<<"unhandled">>,Cmd}, _ConnPid, Sub) -> 55 | ?LOG_INFO("xchain client: server did not understand my command: ~p", [Cmd]), 56 | Sub; 57 | 58 | handle_xchain(Cmd, _ConnPid, Sub) -> 59 | ?LOG_INFO("xchain client got unhandled message from server: ~p", [Cmd]), 60 | Sub. 61 | 62 | -------------------------------------------------------------------------------- /apps/tpnode/src/xchain_server.erl: -------------------------------------------------------------------------------- 1 | % -*- mode: erlang -*- 2 | % vi: set ft=erlang : 3 | 4 | -module(xchain_server). 5 | -include("include/tplog.hrl"). 6 | 7 | %% API 8 | -export([init/2, websocket_init/1, websocket_handle/2, websocket_info/2]). 9 | 10 | init(#{headers:=H}=Req, _Opts) -> 11 | ?LOG_INFO("Init ~p",[H]), 12 | code:ensure_loaded(xchain_server_handler), 13 | xchain_server_handler:known_atoms(), 14 | ConnState=case maps:get(<<"sec-websocket-protocol">>,H,undefined) of 15 | <<"thepower-xchain-v2">> -> #{proto=>2}; 16 | _ -> #{proto=>0} 17 | end, 18 | {cowboy_websocket, 19 | Req, 20 | ConnState, #{ 21 | idle_timeout => 600000 22 | }}. 23 | 24 | websocket_init(State) -> 25 | ?LOG_INFO("Init ws ~p",[State]), 26 | {ok, State}. 27 | 28 | websocket_handle({binary, Bin}, #{proto:=P}=State) -> 29 | try 30 | %%?LOG_DEBUG("ws server got binary msg: ~p", [Bin]), 31 | Cmd = xchain:unpack(Bin, P), 32 | ?LOG_DEBUG("ws server got term: ~p", [Cmd]), 33 | Result = xchain_server_handler:handle_xchain(Cmd,State), 34 | case Result of 35 | ok -> 36 | {ok, State}; 37 | {ok, NewState} -> 38 | {ok, NewState}; 39 | {reply, Answer, NewState} -> 40 | {reply, {binary, xchain:pack(Answer, P)}, NewState}; 41 | {reply, Answer} -> 42 | {reply, {binary, xchain:pack(Answer, P)}, State}; 43 | Answer -> 44 | {reply, {binary, xchain:pack(Answer, P)}, State} 45 | end 46 | catch 47 | Ec:Ee:S -> 48 | %S = erlang:get_stacktrace(), 49 | ?LOG_ERROR("xchain server ws parse error ~p:~p ~p", [Ec, Ee, Bin]), 50 | lists:foreach( 51 | fun(Se) -> 52 | ?LOG_ERROR("at ~p", [Se]) 53 | end, S), 54 | {ok, State} 55 | end; 56 | 57 | websocket_handle({text, <<"ping">>}, State) -> 58 | ?LOG_INFO("PING"), 59 | {ok, State}; 60 | 61 | websocket_handle({text, Msg}, State) -> 62 | ?LOG_DEBUG("xchain server got text msg: ~p", [Msg]), 63 | {reply, {text, <<"pong: ", Msg/binary >>}, State}; 64 | 65 | websocket_handle(_Data, State) -> 66 | ?LOG_INFO("xchain server got unknown websocket: ~p", [_Data]), 67 | {ok, State}. 68 | 69 | websocket_info({message, Msg}, #{proto:=P}=State) -> 70 | ?LOG_DEBUG("xchain server send message ~p", [Msg]), 71 | {reply, {binary, xchain:pack(Msg, P)}, State}; 72 | 73 | websocket_info({timeout, _Ref, Msg}, State) -> 74 | ?LOG_DEBUG("xchain server ws timeout ~p", [Msg]), 75 | {reply, {text, Msg}, State}; 76 | 77 | websocket_info(_Info, State) -> 78 | ?LOG_NOTICE("xchain server got unknown ws info ~p", [_Info]), 79 | {ok, State}. 80 | 81 | 82 | %% ---------------------------- 83 | 84 | -------------------------------------------------------------------------------- /apps/tpwdt/src/tpwdt.app.src: -------------------------------------------------------------------------------- 1 | {application, tpwdt, 2 | [ 3 | {description, "watchdog"}, 4 | {vsn, "0.1"}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib 9 | ]}, 10 | {mod, {tpwdt, []}}, 11 | {env, []} 12 | ]}. 13 | -------------------------------------------------------------------------------- /apps/tpwdt/src/tpwdt.erl: -------------------------------------------------------------------------------- 1 | -module(tpwdt). 2 | 3 | -behaviour(application). 4 | 5 | %% Application callbacks 6 | -export([start/2, stop/1, start/0, stop/0]). 7 | 8 | %% =================================================================== 9 | %% Application callbacks 10 | %% =================================================================== 11 | 12 | start() -> 13 | application:ensure_all_started(?MODULE). 14 | 15 | stop() -> 16 | application:stop(tpwdt). 17 | 18 | start(_StartType, _StartArgs) -> 19 | tpwdt_sup:start_link(). 20 | 21 | stop(_State) -> 22 | ok. 23 | 24 | -------------------------------------------------------------------------------- /apps/tpwdt/src/tpwdt_sup.erl: -------------------------------------------------------------------------------- 1 | -module(tpwdt_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | %% API 6 | -export([start_link/0]). 7 | 8 | %% Supervisor callbacks 9 | -export([init/1]). 10 | 11 | %% Helper macro for declaring children of supervisor 12 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 13 | 14 | %% =================================================================== 15 | %% API functions 16 | %% =================================================================== 17 | 18 | start_link() -> 19 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 20 | 21 | %% =================================================================== 22 | %% Supervisor callbacks 23 | %% =================================================================== 24 | 25 | init([]) -> 26 | Childs = [ 27 | { 28 | wdt, 29 | {tpwdt_worker, start_link, []}, 30 | permanent, 5000, worker, [] 31 | } 32 | ], 33 | {ok, { {one_for_one, 1, 10}, Childs } }. 34 | 35 | -------------------------------------------------------------------------------- /apps/tpwdt/src/tpwdt_worker.erl: -------------------------------------------------------------------------------- 1 | -module(tpwdt_worker). 2 | -include("include/tplog.hrl"). 3 | 4 | -behaviour(gen_server). 5 | 6 | -export([start_link/0]). 7 | 8 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 9 | terminate/2, code_change/3]). 10 | 11 | %% ------------------------------------------------------------------ 12 | %% API Function Definitions 13 | %% ------------------------------------------------------------------ 14 | 15 | start_link() -> 16 | gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 17 | 18 | %% ------------------------------------------------------------------ 19 | %% gen_server Function Definitions 20 | %% ------------------------------------------------------------------ 21 | 22 | init(_Args) -> 23 | {ok, #{tmr=>erlang:send_after(5000,self(),check)}}. 24 | 25 | handle_call(_Request, _From, State) -> 26 | ?LOG_NOTICE("Unknown call ~p",[_Request]), 27 | {reply, ok, State}. 28 | 29 | handle_cast(_Msg, State) -> 30 | ?LOG_NOTICE("Unknown cast ~p",[_Msg]), 31 | {noreply, State}. 32 | 33 | handle_info(check, #{tmr:=T0}=State) -> 34 | erlang:cancel_timer(T0), 35 | Now=os:system_time(millisecond), 36 | LBH=maps:get(hash,blockchain:last_meta()), 37 | 38 | case maps:get(lbh,State, <<>>) of 39 | L when L==LBH -> 40 | LBT=maps:get(lbt,State), 41 | if (Now-LBT > 55000) -> 42 | logger:error("Last block does not change for ~p ms, restarting",[Now-LBT]), 43 | tpnode:restart(), 44 | {noreply, State#{ 45 | lc=>Now, 46 | tmr=>erlang:send_after(120000,self(),check) 47 | }}; 48 | true -> 49 | {noreply, maps:merge(#{lbt=>Now}, 50 | State#{lbh=>LBH, 51 | lc=>Now, 52 | tmr=>erlang:send_after(10000,self(),check)} 53 | )} 54 | end; 55 | _ -> 56 | {noreply, State#{lbt=>Now, 57 | lbh=>LBH, 58 | lc=>Now, 59 | tmr=>erlang:send_after(10000,self(),check)}} 60 | end; 61 | 62 | handle_info(_Info, State) -> 63 | ?LOG_NOTICE("~s Unknown info ~p", [?MODULE,_Info]), 64 | {noreply, State}. 65 | 66 | terminate(_Reason, _State) -> 67 | ok. 68 | 69 | code_change(_OldVsn, State, _Extra) -> 70 | {ok, State}. 71 | 72 | 73 | -------------------------------------------------------------------------------- /bin/build_rel_pre_alpha.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | BUILD_BRANCH=pre-alpha 4 | BUILD_SUFFIX=pre 5 | 6 | 7 | echo "**** build x86 version" 8 | 9 | ssh root@2001:bc8:4400:2500::24:d0d "cat > build.sh && sh build.sh" << EOF1 10 | cd blochchaintest 11 | . /opt/erl/activate 12 | git fetch origin 13 | git reset --hard 14 | git checkout ${BUILD_BRANCH} 15 | git pull 16 | git describe 17 | rm -rf _build/default/lib/jsx/ebin 18 | rm -rf _build/default/lib/tpnode 19 | rm -rf _build/rel/lib/tpnode 20 | rm apps/tpnode/include/version.hrl 21 | export VERSION_SUFFIX=${BUILD_SUFFIX} 22 | escript bin/generate_headers 23 | JSX_FORCE_MAPS=1 ./rebar3 compile 24 | ./rebar3 as rel tar 25 | git checkout master 26 | EOF1 27 | 28 | 29 | echo "**** build arm64 version" 30 | 31 | # ssh root@ascw.cleverfox.ru 32 | 33 | ssh root@dist.thepower.io "cat > build.sh && sh build.sh" << EOF2 34 | cd blochchaintest 35 | . /opt/erl/activate 36 | git fetch origin 37 | git reset --hard 38 | git checkout ${BUILD_BRANCH} 39 | git pull 40 | git describe 41 | rm -rf _build/rel/lib/tpnode 42 | rm -rf _build/default/lib/tpnode 43 | rm -rf _build/default/lib/jsx/ebin 44 | rm apps/tpnode/include/version.hrl 45 | export VERSION_SUFFIX=${BUILD_SUFFIX} 46 | escript bin/generate_headers 47 | JSX_FORCE_MAPS=1 ./rebar3 compile 48 | ./rebar3 as rel tar 49 | git describe --abbrev=0 | sed 's/^v//' 50 | VER=\$( git describe --abbrev=0 | sed 's/^v//' ) 51 | git checkout master 52 | VER1=\$VER-${BUILD_SUFFIX} 53 | echo \$VER -> \$VER1 54 | 55 | cp /root/blochchaintest/_build/rel/rel/thepower/thepower-\$VER.tar.gz /var/www/html/thepower-\$VER1-arm64.tar.gz 56 | scp "[2001:bc8:4400:2500::24:d0d]:/root/blochchaintest/_build/rel/rel/thepower/thepower-\$VER.tar.gz" /var/www/html/thepower-\$VER1-x64.tar.gz 57 | echo \$VER1 > /var/www/html/latest-${BUILD_SUFFIX}-x64.txt 58 | echo \$VER1 > /var/www/html/latest-${BUILD_SUFFIX}-arm64.txt 59 | 60 | rm -f /var/www/html/thepower-latest-${BUILD_SUFFIX}-arm64.tar.gz 61 | rm -f /var/www/html/thepower-latest-${BUILD_SUFFIX}-x64.tar.gz 62 | ln -s /var/www/html/thepower-\$VER1-x64.tar.gz /var/www/html/thepower-latest-${BUILD_SUFFIX}-x64.tar.gz 63 | ln -s /var/www/html/thepower-\$VER1-arm64.tar.gz /var/www/html/thepower-latest-${BUILD_SUFFIX}-arm64.tar.gz 64 | EOF2 65 | 66 | 67 | -------------------------------------------------------------------------------- /bin/ci_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # reset the testnet 4 | ./bin/testnet.sh reset >/dev/null 5 | 6 | # remove old logs 7 | rm -rf ./log 8 | mkdir ./log 9 | touch ./log/.keepme 10 | 11 | rm -rf ./_build/test/logs 12 | mkdir -p ./_build/test/logs 13 | 14 | # run tests 15 | make cover coffebreak synctest 16 | export rc=$? 17 | 18 | # don't save the logs if everything is OK 19 | if [ $rc -ne 0 ] 20 | then 21 | # stop testnet in case of error 22 | ./bin/testnet.sh stop >/dev/null 23 | # save db to artifacts 24 | echo "tests failed, saving ledger bckups" 25 | tar cfj log/test_ledger_bckups.tar.bz2 /tmp/ledger_bckups/ >/dev/null 26 | tar cfj log/test_blockdebug.tar.bz2 ./log/*_block_* >/dev/null 27 | fi 28 | 29 | # save logs 30 | tar cfj log/test_logs.tar.bz2 ./log/*.log ./log/*.blog 31 | find ./log -name '*.log' -delete 32 | find ./log -name '*.blog' -delete 33 | 34 | # cleanup 35 | rm -rf /tmp/ledger_bckups/ 36 | rm -rf ./log/vmproto_req_* 37 | find ./log -name '*_block_*' -delete 38 | 39 | # send stop signal to testnet once again 40 | ./bin/testnet.sh stop >/dev/null 41 | 42 | exit $rc 43 | -------------------------------------------------------------------------------- /bin/ci_check_chain1.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | mkdir -p _build/test/log_chain1 4 | #ct_run -pa _build/test/lib/*/ebin \ 5 | # -logdir _build/test/log_chain1 \ 6 | # -suite chain1_SUITE \ 7 | # -noshell 8 | 9 | ./rebar3 ct --suite=chain1_SUITE \ 10 | --logdir _build/test/log_chain1 11 | export rc=$? 12 | 13 | 14 | # don't save the logs if everything is OK 15 | if [ $rc -eq 0 ] 16 | then 17 | rm -rf _build/test/log_chain1/ 18 | mkdir -p _build/test/log_chain1 19 | else 20 | echo "test failed" 21 | # for ip in "2001:bc8:4700:2500::131f" "2001:bc8:4700:2500::121b" "2001:bc8:4400:2700::1263" "2001:bc8:4400:2700::1265" "2001:bc8:4400:2700::2341" 22 | # do 23 | # ( 24 | # ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@$ip "cd thepower; . /opt/erl/activate; ./bin/thepower eval 'blockchain ! runsync.'" 25 | # ) & 26 | # done 27 | fi 28 | 29 | exit $rc 30 | -------------------------------------------------------------------------------- /bin/ci_check_chain4.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | mkdir -p _build/test/log_chain4 4 | 5 | export API_BASE_URL="http://pwr.local:49841" 6 | 7 | ./rebar3 ct --suite=chain4_SUITE \ 8 | --logdir _build/test/log_chain4 9 | export rc=$? 10 | 11 | 12 | # don't save the logs if everything is OK 13 | #if [ $rc -eq 0 ] 14 | #then 15 | # rm -rf _build/test/log_chain4/ 16 | # mkdir -p _build/test/log_chain4 17 | #fi 18 | 19 | exit $rc 20 | -------------------------------------------------------------------------------- /bin/ci_check_chain7.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | mkdir -p _build/test/log_chain7 4 | 5 | export API_BASE_URL="http://195.3.255.79:43287" 6 | 7 | 8 | ./rebar3 ct --suite=chain7_SUITE \ 9 | --logdir _build/test/log_chain7 10 | export rc=$? 11 | 12 | 13 | # don't save the logs if everything is OK 14 | #if [ $rc -eq 0 ] 15 | #then 16 | # rm -rf _build/test/log_chain7/ 17 | # mkdir -p _build/test/log_chain7 18 | #fi 19 | 20 | exit $rc 21 | -------------------------------------------------------------------------------- /bin/ci_pre_alpha.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | #if [ ! -r ./bin/testnet2.sh ] 4 | #then 5 | # echo "can't find testnet control utility" 6 | # exit -1 7 | #fi 8 | 9 | export RELEASENAME=thepower-latest-pre 10 | export CHAIN4="alpha_c4n1 alpha_c4n2 alpha_c4n3" 11 | export CHAIN5="alpha_c5n1 alpha_c5n2 alpha_c5n3" 12 | export CHAIN6="alpha_c6n1 alpha_c6n2 alpha_c6n3" 13 | export CONFIG_ROOT=./alpha 14 | export API_BASE_URL="http://127.0.0.1:42841" 15 | 16 | 17 | rm -rf ./alpha 18 | mkdir -p ./alpha 19 | cp -a ./examples/* ./alpha/ 20 | 21 | # changing the node name 22 | find ./alpha -type f -name "*.args" -print0 | xargs -0 sed -i'' -e 's/test/alpha/g' 23 | 24 | # changing the port number 25 | find ./alpha -type f -name "*.conf" -print0 | xargs -0 sed -i'' -e 's/49\([0-9][0-9][0-9]\)/42\1/g' 26 | 27 | # changing the config path 28 | find ./alpha -type f -name "*.config" -print0 | xargs -0 sed -i'' -e 's#./examples/#./alpha/#g' 29 | 30 | # rename files 31 | for file in $(find ./alpha -type f -name "test_c*") 32 | do 33 | mv $file $(echo "$file" | sed 's/\(test_c\)\([0-9]\+n[0-9]\+\)/alpha_c\2/g') 34 | done 35 | 36 | ./bin/testnet2.sh $1 37 | 38 | -------------------------------------------------------------------------------- /bin/run_rel_pre_alpha.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | export RELEASENAME=thepower-latest-pre 4 | export CHAIN4="alpha_c4n1 alpha_c4n2 alpha_c4n3" 5 | export CHAIN5="alpha_c5n1 alpha_c5n2 alpha_c5n3" 6 | export CHAIN6="alpha_c6n1 alpha_c6n2 alpha_c6n3" 7 | export CONFIG_ROOT=./alpha 8 | export API_BASE_URL="http://127.0.0.1:42841" 9 | 10 | ./testnet2.sh $1 11 | 12 | -------------------------------------------------------------------------------- /config/sys.config: -------------------------------------------------------------------------------- 1 | [ 2 | {tpnode, [ 3 | {config,"node.config"}, 4 | {discovery, 5 | #{addresses => [#{address => local6,port => 43210, proto => tpic}] } 6 | } 7 | ]}, 8 | {lager, [ 9 | {handlers, [ 10 | {lager_file_backend, [ 11 | {file, "log/error.log"}, 12 | {level,error}, 13 | {size, 10485760}, 14 | {date, "$D0"}, 15 | {count, 5} 16 | ]}, 17 | {lager_file_backend, [ 18 | {file, "log/console_n1.log"}, 19 | {level, info}, 20 | {size, 10485760}, 21 | {date, "$D0"}, 22 | {count, 5} 23 | ]} 24 | ]} 25 | ]} 26 | ]. 27 | -------------------------------------------------------------------------------- /config/vm.args: -------------------------------------------------------------------------------- 1 | -sname testrel 2 | 3 | +K true 4 | +A30 5 | -------------------------------------------------------------------------------- /dox/chain_setup.md: -------------------------------------------------------------------------------- 1 | In case we want to create new chain we need do the following steps: 2 | 3 | * create private keys of nodes 4 | * create chain settings 5 | * create genesis from the data from the previous steps 6 | 7 | 8 | Use the following code in REPL to create the public key of one node: 9 | 10 | K1=tpecdsa:generate_priv(). 11 | 12 | Put the key to file using the following code: 13 | 14 | file:write_file("c8n1.key", K1). 15 | 16 | Do these steps again for each node in your chain. 17 | 18 | At this point we have generated private keys for all chain nodes. 19 | 20 | Let's generate set of patches which become new chain settings. 21 | 22 | file:write_file("initgenesis.txt",[io_lib:format("~p.~n",[genesis:settings([{<<"c8n1">>,tpecdsa:calc_pub(K1,true)},{<<"c8n2">>,tpecdsa:calc_pub(K2,true)},{<<"c8n3">>,tpecdsa:calc_pub(K3,true)}])])]). 23 | 24 | As you can see in the example we have created the file named initgenesis.txt. We need to edit that file at this point. 25 | 26 | We should choose how much node signatures will be sufficient to accept block (minsig). Also we should choose settings for wallets, commissions, gas, endless etc 27 | 28 | 29 | Here is an example of edited the initgenesis.txt file which contains 8 chain settings: 30 | 31 | 32 | [#{p => [keys,<<"c8n3">>], 33 | t => set, 34 | v => 35 | <<3,102,61,117,54,194,75,116,131,253,162,42,110,101,201,32,7,108,154, 36 | 217,232,36,183,147,56,190,215,74,71,167,137,126,189>>}, 37 | #{p => [keys,<<"c8n2">>], 38 | t => set, 39 | v => 40 | <<3,96,250,170,64,51,90,236,222,152,224,81,16,210,70,253,130,96,23,98, 41 | 248,205,26,213,132,245,161,231,160,151,62,67,245>>}, 42 | #{p => [keys,<<"c8n1">>], 43 | t => set, 44 | v => 45 | <<3,196,38,152,230,99,73,71,143,46,232,215,43,74,238,216,243,2,54,157, 46 | 200,160,47,53,189,195,128,144,117,185,45,151,236>>}, 47 | #{p => [<<"current">>,chain,patchsigs],t => set,v => 2}, 48 | #{p => [<<"current">>,chain,minsig],t => set,v => 2}, 49 | #{p => [<<"current">>,chain,blocktime],t => set,v => 2}, 50 | #{p => [<<"current">>,chain,<<"allowempty">>],t => set,v => 0}, 51 | #{p => [chains],t => set,v => [1,2,3,7,8]}, 52 | #{p => [nodechain], 53 | t => set, 54 | v => #{<<"c8n1">> => 8,<<"c8n2">> => 8,<<"c8n3">> => 8}}, 55 | #{t=><<"nonexist">>, p=>[<<"current">>, <<"allocblock">>, last], v=>any}, 56 | #{t=>set, p=>[<<"current">>, <<"allocblock">>, group], v=>10}, 57 | #{t=>set, p=>[<<"current">>, <<"allocblock">>, block], v=>8}, 58 | #{t=>set, p=>[<<"current">>, <<"allocblock">>, last], v=>0}, 59 | 60 | #{t=>set, p=>[<<"current">>, <<"endless">>, <<128,1,64,0,8,0,0,1>>, <<"TST">>], v=>true}, 61 | #{t=>set, p=>[<<"current">>, <<"endless">>, <<128,1,64,0,8,0,0,1>>, <<"SK">>], v=>true}, 62 | 63 | #{t=>set, p=>[<<"current">>, <<"fee">>, params, <<"feeaddr">>], v=><<128,1,64,0,8,0,0,1>>}, 64 | #{t=>set, p=>[<<"current">>, <<"fee">>, params, <<"notip">>], v=>1}, 65 | 66 | #{t=>set, p=>[<<"current">>, <<"fee">>, <<"SK">>, <<"base">>], v=>1000000000}, 67 | #{t=>set, p=>[<<"current">>, <<"fee">>, <<"SK">>, <<"baseextra">>], v=>128}, 68 | #{t=>set, p=>[<<"current">>, <<"fee">>, <<"SK">>, <<"kb">>], v=>1000000000}, 69 | #{t=>set, p=>[<<"current">>, <<"gas">>, <<"SK">>], v=>100} 70 | 71 | ]. 72 | 73 | Let's create genesis from that data. Write in REPL the following code: 74 | 75 | {ok,[Sets]}=file:consult("initgenesis.txt"). 76 | 77 | genesis:new([K1,K2,K3],tx:construct_tx(#{kind=>patch, ver=>2, patches=>Sets})). 78 | 79 | At this point you have printed the first block on you screen, and you have created file genesis.txt as well. 80 | 81 | -------------------------------------------------------------------------------- /dox/openssl.txt: -------------------------------------------------------------------------------- 1 | Convertation from raw key format to openssl format. 2 | 3 | export(<>,der) -> 4 | <<48,54,48,16,6,7,42,134,72,206,61,2,1,6,5,43,129,4,0,10,3,34,0, 5 | PubKey/binary>>; 6 | export(<>,der) -> 7 | <<16#30,16#2e,16#02,16#01,16#01,16#04,16#20,PrivKey/binary, 8 | 16#a0,16#07,16#06,16#05,16#2b,16#81,16#04,16#00,16#0a>>; 9 | 10 | 11 | Key Import 12 | 13 | import(<<"---",_/binary>>=PEM) -> 14 | [{KeyType, DerKey, not_encrypted}] = public_key:pem_decode(PEM), 15 | case public_key:der_decode(KeyType, DerKey) of 16 | #'ECPrivateKey'{ 17 | version = 1, 18 | privateKey = PrivKey, 19 | parameters = {namedCurve,{1,3,132,0,10}} 20 | } -> 21 | {priv, PrivKey}; 22 | #'SubjectPublicKeyInfo'{ 23 | % algorithm = #'AlgorithmIdentifier'{ algorithm={1,2,840,10045,2,1}}, 24 | subjectPublicKey = PubKey 25 | } -> 26 | {pub, PubKey} 27 | end. 28 | 29 | Key generation using tpecdsa, the signing process, signature check using openssl 30 | K=tpecdsa:generate_priv(). 31 | % signing 32 | file:write_file("/tmp/k1.sig",tpecdsa:sign(<<"test1">>,K)). 33 | % key export 34 | file:write_file("/tmp/k1.pub.pem",tpecdsa:export(tpecdsa:calc_pub(K,true),pem)). 35 | 36 | % echo -n 'test1' | openssl dgst -sha256 -verify /tmp/k1.pub.pem -signature /tmp/k1.sig 37 | Verified OK 38 | 39 | 40 | Key generation using OpenSSL, the signing process, signature check using tpecdsa 41 | % openssl ecparam -name secp256k1 -genkey -text -out /tmp/k2.priv.pem 42 | Get pub 43 | % openssl ec -in /tmp/k2.priv.pem -pubout -out /tmp/k2.pub.pem 44 | Sign 45 | % echo -n 'test2' | openssl dgst -sha256 -sign /tmp/k2.priv.pem > /tmp/k2.sig 46 | 47 | > {ok,SignatureK2}=file:read_file("/tmp/k2.sig"). 48 | {ok,<<48,70,2,33,0,238,181,227,214,129,212,154,62,112, 49 | 149,131,61,43,217,100,191,86,71,62,53,126,112,...>>} 50 | > {ok,K2PubPem}=file:read_file("/tmp/k2.pub.pem"). 51 | {ok,<<"-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEA"...>>} 52 | > {pub,K2Pub}=tpecdsa:import(K2PubPem). 53 | {pub,<<4,216,61,240,247,25,164,222,77,21,79,218,68,...>>} 54 | > tpecdsa:verify(<<"test2">>,K2Pub,SignatureK2). 55 | correct 56 | 57 | 58 | To register, you need to generate a compressed public key (this key 59 | has the Y coordinate completely, and the parity of the X coordinate). 60 | The key without the header is consist of 33 bytes and the first byte is equal to 2 or 3 61 | (it's depence on the parity of the X coordinate) 62 | 63 | How to get raw key from your private key (from the previous step): 64 | openssl ec -in /tmp/k2.priv.pem -conv_form compressed -outform DER | dd bs=1 iseek=53 > /tmp/k2.raw.bin 65 | 66 | -------------------------------------------------------------------------------- /dox/profiling.txt: -------------------------------------------------------------------------------- 1 | erlang:statistics(scheduler_wall_time) 2 | lcnt:rt_opt({copy_save, true}), lcnt:clear(), timer:sleep(5000), lcnt:collect(), lcnt:swap_pid_keys(), lcnt:conflicts([{max_locks,5}]) 3 | 4 | msacc:start(5000) 5 | msacc:print() 6 | 7 | 8 | erlang:system_flag(scheduler_wall_time, true), 9 | S1=erlang:statistics(scheduler_wall_time). 10 | 11 | [ {CPU, trunc(1000*((X2-X1)/(T2-T1)))/10} || {{CPU,X1,T1},{CPU,X2,T2}} <- 12 | lists:zip(lists:sort(S1),lists:sort(erlang:statistics(scheduler_wall_time))) 13 | ]. 14 | 15 | erlang:system_flag(scheduler_wall_time, false). 16 | 17 | erlang:system_flag(schedulers_online,8). 18 | 19 | -------------------------------------------------------------------------------- /dox/tx.txt: -------------------------------------------------------------------------------- 1 | # Transaction format 2 | 3 | ## General purpose transaction 4 | 5 | type => tx fixstr (0xa0 ... 0xbf) 6 | from => Address 8 (0xc4) 7 | to => Address, 8 bites, binary (0xc4) 8 | amount => Amount fixint/uintXX (0x00..0x7f or 0xcc .. 0xcf) 9 | cur => currency name (ticker) fixstr (0xa0 .. 0xbf) 10 | timestamp => timestamp в мс. int64 (0xcf) 11 | seq => nonce, Int, fixint/uintXX (0x00..0x7f or 0xcc..0xcf) 12 | extradata => additional info, text (JSON) fixstr/strXX (0xa0..0xbf or 0xd9..0xdb) 13 | 14 | 15 | 16 | 17 | public_key => bin pubkey 18 | signature => bin signature 19 | extdata => arbitrary unsigned external data 20 | 21 | encode: 22 | to sign make hash: 23 | sha256(msgpack encode( 24 | [type, from, to, amount, cur, timestamp, seq, extradata] 25 | )) 26 | Sign and make obj: 27 | msgpack ( 28 | { 29 | type => type, 30 | tx => [from, to, amount, cur, timestamp, seq, extradata], 31 | sig => { 32 | bin_pub_key1 => bin_signature1, 33 | bin_pub_key2 => bin_signature2, 34 | ... 35 | } 36 | extdata => {} 37 | } 38 | ) 39 | 40 | ## Chain migration 41 | 42 | from => Address 43 | portout => Destination chain ID 44 | timestamp => Timestamp ms 45 | seq => Integer (nonce) 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/brom.wasm.lz4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thepower/tpnode/e5308c9c89f7ed69d7af3a50295fceb2694480d7/examples/brom.wasm.lz4 -------------------------------------------------------------------------------- /examples/brom1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thepower/tpnode/e5308c9c89f7ed69d7af3a50295fceb2694480d7/examples/brom1 -------------------------------------------------------------------------------- /examples/evm_builtin/Makefile: -------------------------------------------------------------------------------- 1 | #--combined-json abi,asm,ast,bin,bin-runtime,devdoc,function-debug,function-debug-runtime,generated-sources,generated-sources-runtime,hashes,metadata,opcodes,srcmap,srcmap-runtime,storage-layout,userdoc 2 | all: build/builtinFunc.bin build/checkSig.bin build/sponsor.bin build/WETH9.bin build/StorageRecursive1.bin-runtime build/mcall.bin-runtime build/ChainState.bin-runtime 3 | 4 | build/builtinFunc.bin: contracts/builtinFunc.sol 5 | solc --abi --bin contracts/builtinFunc.sol -o build --overwrite --storage-layout 6 | 7 | build/checkSig.bin: contracts/check_sig.sol 8 | solc --abi --bin contracts/check_sig.sol -o build --overwrite --storage-layout 9 | 10 | build/ChainState.bin-runtime: contracts/ChainState.sol 11 | solc --abi --bin-runtime contracts/ChainState.sol -o build --overwrite --storage-layout 12 | 13 | build/sponsor.bin: contracts/sponsor.sol 14 | solc --abi --bin contracts/sponsor.sol -o build --overwrite --storage-layout 15 | 16 | build/StorageRecursive1.bin-runtime: contracts/StorageRecursive.sol 17 | solc --abi --bin-runtime contracts/StorageRecursive.sol -o build --overwrite --storage-layout 18 | 19 | build/mcall.bin-runtime: contracts/mcall.sol 20 | solc contracts/mcall.sol -o build --overwrite --bin-runtime --no-cbor-metadata --optimize --abi 21 | 22 | build/WETH9.bin: contracts/WETH9.sol 23 | solc --abi --bin contracts/WETH9.sol -o build --overwrite --storage-layout 24 | 25 | all0: build/builtinFunc.json build/checkSig.json 26 | cat build/builtinFunc.json | jq -r .bytecode | cut -d 'x' -f 2 > build/builtinFunc.hex 27 | cat build/builtinFunc.json | jq -r '.abi' > build/builtinFunc.abi 28 | cat build/checkSig.json | jq -r .bytecode | cut -d 'x' -f 2 > build/checkSig.hex 29 | cat build/checkSig.json | jq -r '.abi' > build/checkSig.abi 30 | build/builtinFunc.json: contracts/builtinFunc.sol 31 | truffle compile 32 | build/checkSig.json: contracts/check_sig.sol 33 | truffle compile 34 | 35 | -------------------------------------------------------------------------------- /examples/evm_builtin/build/Callee.abi: -------------------------------------------------------------------------------- 1 | [{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"Sender","type":"event"},{"inputs":[{"internalType":"uint256","name":"_x","type":"uint256"}],"name":"callX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /examples/evm_builtin/build/Caller.abi: -------------------------------------------------------------------------------- 1 | [{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"Sender","type":"event"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"},{"internalType":"uint256","name":"_x","type":"uint256"}],"name":"setXFromAddress","outputs":[],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /examples/evm_builtin/build/builtinFunc_storage.json: -------------------------------------------------------------------------------- 1 | {"storage":[{"astId":78,"contract":"contracts/builtinFunc.sol:builtinFunc","label":"exampleTextCount","offset":0,"slot":"0","type":"t_uint256"},{"astId":81,"contract":"contracts/builtinFunc.sol:builtinFunc","label":"uarr","offset":0,"slot":"1","type":"t_array(t_uint256)dyn_storage"},{"astId":85,"contract":"contracts/builtinFunc.sol:builtinFunc","label":"sarr","offset":0,"slot":"2","type":"t_array(t_struct(structTextExample)60_storage)dyn_storage"},{"astId":90,"contract":"contracts/builtinFunc.sol:builtinFunc","label":"mapText","offset":0,"slot":"3","type":"t_mapping(t_uint256,t_struct(structTextExample)60_storage)"},{"astId":94,"contract":"contracts/builtinFunc.sol:builtinFunc","label":"rewardAddresses","offset":0,"slot":"4","type":"t_mapping(t_bytes_memory_ptr,t_address)"},{"astId":96,"contract":"contracts/builtinFunc.sol:builtinFunc","label":"exampleIntCount","offset":0,"slot":"5","type":"t_uint256"},{"astId":100,"contract":"contracts/builtinFunc.sol:builtinFunc","label":"mapInt","offset":0,"slot":"6","type":"t_mapping(t_uint256,t_uint256)"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_struct(structTextExample)60_storage)dyn_storage":{"base":"t_struct(structTextExample)60_storage","encoding":"dynamic_array","label":"struct builtinFunc.structTextExample[]","numberOfBytes":"32"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bytes_memory_ptr":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_mapping(t_bytes_memory_ptr,t_address)":{"encoding":"mapping","key":"t_bytes_memory_ptr","label":"mapping(bytes => address)","numberOfBytes":"32","value":"t_address"},"t_mapping(t_uint256,t_struct(structTextExample)60_storage)":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => struct builtinFunc.structTextExample)","numberOfBytes":"32","value":"t_struct(structTextExample)60_storage"},"t_mapping(t_uint256,t_uint256)":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => uint256)","numberOfBytes":"32","value":"t_uint256"},"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"},"t_struct(structTextExample)60_storage":{"encoding":"inplace","label":"struct builtinFunc.structTextExample","members":[{"astId":57,"contract":"contracts/builtinFunc.sol:builtinFunc","label":"id","offset":0,"slot":"0","type":"t_uint256"},{"astId":59,"contract":"contracts/builtinFunc.sol:builtinFunc","label":"text","offset":0,"slot":"1","type":"t_string_storage"}],"numberOfBytes":"64"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}} -------------------------------------------------------------------------------- /examples/evm_builtin/build/checkSig.abi: -------------------------------------------------------------------------------- 1 | [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"pubkey","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"chain","type":"uint256"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"changed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"txt","type":"string"},{"indexed":false,"internalType":"uint256","name":"val","type":"uint256"}],"name":"info","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"txt","type":"string"},{"indexed":false,"internalType":"uint256","name":"val","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"val2","type":"uint256"}],"name":"info","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"txt","type":"string"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"info","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"","type":"bytes"}],"name":"info2","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes[]","name":"","type":"bytes[]"}],"name":"info2","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"info2","type":"event"},{"inputs":[],"name":"blockCheck","outputs":[{"internalType":"uint256","name":"sigs","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blockSigs","outputs":[{"internalType":"bytes[]","name":"sigs","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"count","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"key2id","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"keys","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardAddresses","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"setAddr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /examples/evm_builtin/build/checkSig_storage.json: -------------------------------------------------------------------------------- 1 | {"storage":[{"astId":49,"contract":"contracts/check_sig.sol:checkSig","label":"rewardAddresses","offset":0,"slot":"0","type":"t_mapping(t_uint256,t_address)"},{"astId":53,"contract":"contracts/check_sig.sol:checkSig","label":"keys","offset":0,"slot":"1","type":"t_mapping(t_uint256,t_bytes_storage)"},{"astId":57,"contract":"contracts/check_sig.sol:checkSig","label":"key2id","offset":0,"slot":"2","type":"t_mapping(t_bytes_memory_ptr,t_uint256)"},{"astId":60,"contract":"contracts/check_sig.sol:checkSig","label":"count","offset":0,"slot":"3","type":"t_uint256"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_bytes_memory_ptr":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_mapping(t_bytes_memory_ptr,t_uint256)":{"encoding":"mapping","key":"t_bytes_memory_ptr","label":"mapping(bytes => uint256)","numberOfBytes":"32","value":"t_uint256"},"t_mapping(t_uint256,t_address)":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => address)","numberOfBytes":"32","value":"t_address"},"t_mapping(t_uint256,t_bytes_storage)":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => bytes)","numberOfBytes":"32","value":"t_bytes_storage"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}} -------------------------------------------------------------------------------- /examples/evm_builtin/build/sponsor.abi: -------------------------------------------------------------------------------- 1 | [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"text","type":"string"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"textbin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"text","type":"string"},{"components":[{"internalType":"uint256","name":"purpose","type":"uint256"},{"internalType":"string","name":"cur","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct sponsor.tpPayload","name":"data","type":"tuple"}],"name":"textpayload","type":"event"},{"inputs":[],"name":"areYouSponsor","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"kind","type":"uint256"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"t","type":"uint256"},{"internalType":"uint256","name":"seq","type":"uint256"},{"components":[{"internalType":"string","name":"func","type":"string"},{"internalType":"uint256[]","name":"args","type":"uint256[]"}],"internalType":"struct sponsor.tpCall[]","name":"call","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"purpose","type":"uint256"},{"internalType":"string","name":"cur","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct sponsor.tpPayload[]","name":"payload","type":"tuple[]"},{"components":[{"internalType":"bytes","name":"raw","type":"bytes"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"bytes","name":"rawkey","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct sponsor.tpSig[]","name":"signatures","type":"tuple[]"}],"internalType":"struct sponsor.tpTx","name":"utx","type":"tuple"}],"name":"wouldYouLikeToPayTx","outputs":[{"internalType":"string","name":"iWillPay","type":"string"},{"components":[{"internalType":"uint256","name":"purpose","type":"uint256"},{"internalType":"string","name":"cur","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct sponsor.tpPayload[]","name":"pay","type":"tuple[]"}],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /examples/evm_builtin/contracts/WETH9.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unknown 2 | pragma solidity ^0.8.1; 3 | 4 | contract WETH9 { 5 | string public name = "Wrapped Ether"; 6 | string public symbol = "WSK"; 7 | uint8 public decimals = 18; 8 | 9 | event Approval(address indexed src, address indexed guy, uint wad); 10 | event Transfer(address indexed src, address indexed dst, uint wad); 11 | event Deposit(address indexed dst, uint wad); 12 | event Withdrawal(address indexed src, uint wad); 13 | 14 | mapping (address => uint) public balanceOf; 15 | mapping (address => mapping (address => uint)) public allowance; 16 | 17 | receive() payable external { 18 | deposit(); 19 | } 20 | function deposit() public payable { 21 | balanceOf[msg.sender] += msg.value; 22 | emit Deposit(msg.sender, msg.value); 23 | } 24 | function withdraw(uint wad) public { 25 | require(balanceOf[msg.sender] >= wad); 26 | balanceOf[msg.sender] -= wad; 27 | payable(msg.sender).transfer(wad); 28 | emit Withdrawal(msg.sender, wad); 29 | } 30 | 31 | function totalSupply() public view returns (uint) { 32 | return address(this).balance; 33 | } 34 | 35 | function approve(address guy, uint wad) public returns (bool) { 36 | allowance[msg.sender][guy] = wad; 37 | emit Approval(msg.sender, guy, wad); 38 | return true; 39 | } 40 | 41 | function transfer(address dst, uint wad) public returns (bool) { 42 | return transferFrom(msg.sender, dst, wad); 43 | } 44 | 45 | function transferFrom(address src, address dst, uint wad) 46 | public returns (bool) { 47 | require(balanceOf[src] >= wad); 48 | 49 | if (src != msg.sender) { 50 | require(allowance[src][msg.sender] >= wad); 51 | allowance[src][msg.sender] -= wad; 52 | } 53 | 54 | balanceOf[src] -= wad; 55 | balanceOf[dst] += wad; 56 | 57 | emit Transfer(src, dst, wad); 58 | 59 | return true; 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /examples/evm_builtin/contracts/call.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.20; 2 | 3 | contract Callee { 4 | event Sender(address sender); 5 | function callX(uint _x) public returns (uint) { 6 | emit Sender(msg.sender); 7 | return _x; 8 | } 9 | } 10 | 11 | contract Caller { 12 | event Sender(address sender); 13 | function setXFromAddress(address _addr, uint _x) public { 14 | emit Sender(msg.sender); 15 | Callee callee = Callee(_addr); 16 | callee.callX(_x); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/evm_builtin/contracts/check_sig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract checkSig { 5 | struct tpCall { 6 | string func; 7 | uint256[] args; 8 | } 9 | struct tpSig { 10 | uint256 timestamp; 11 | bytes pubkey; 12 | bytes rawkey; 13 | bytes signature; 14 | } 15 | struct tpTx { 16 | uint256 kind; 17 | address from; 18 | address to; 19 | uint256 t; 20 | uint256 seq; 21 | tpCall[] call; 22 | tpSig[] signatures; 23 | } 24 | struct settings { 25 | uint256 settings; 26 | bytes res_bin; 27 | uint256 res_int; 28 | string[] keys; 29 | } 30 | 31 | mapping(uint256 => address) public rewardAddresses; 32 | mapping(uint256 => bytes) public keys; 33 | mapping(bytes => uint256) public key2id; 34 | uint256 public count=0; 35 | 36 | event changed(bytes pubkey, uint256 chain, string name, address addr, uint256 id); 37 | event info(string txt, uint256 val); 38 | event info(string txt, uint256 val, uint256 val2); 39 | event info(string txt, bytes data); 40 | event info2(bytes); 41 | event info2(bytes[]); 42 | event info2(uint256[]); 43 | 44 | constructor() {} 45 | 46 | function blockSigs() public returns (bytes[] memory sigs) { 47 | require(block.number > 10, "block number is less than 10"); 48 | address blks=address(0xAFFFFFFFFF000004); 49 | uint i=block.number-1; 50 | emit info("block number:",i); 51 | (bool success, bytes memory returnBytes1) = blks.staticcall( 52 | abi.encodeWithSignature("get_signatures_pubkeys(uint256 height)",i) 53 | ); 54 | emit info("success:",success?1:0); 55 | if(success){ 56 | (bytes[] memory signatures) = abi.decode(returnBytes1, (bytes[])); 57 | emit info2(signatures); 58 | return signatures; 59 | } 60 | } 61 | 62 | function blockCheck() public returns (uint256 sigs) { 63 | require(block.number > 10, "block number is less than 10"); 64 | address blks=address(0xAFFFFFFFFF000004); 65 | uint[] memory foundcnt=new uint[](count); 66 | emit info("blockCheck",count); 67 | emit info("addr0",keys[0]); 68 | emit info("addr1",keys[1]); 69 | for(uint i=1;i<10;i++){ // last 10 blocks 70 | emit info("blk",i); 71 | (bool success, bytes memory returnBytes1) = blks.staticcall( 72 | abi.encodeWithSignature("get_signatures_pubkeys(uint256 height)",i) 73 | ); 74 | if(success){ 75 | emit info("1succ_blk",i); 76 | (bytes[] memory signatures) = abi.decode(returnBytes1, (bytes[])); 77 | emit info("2succ_blk",i); 78 | for(uint j=0;j0){ 83 | emit info("registered:",registered); 84 | foundcnt[registered]++; 85 | }else{ 86 | emit info("noreg",signatures[j]); 87 | } 88 | } 89 | }else{ 90 | emit info("failed_blk",i); 91 | emit info2(returnBytes1); 92 | } 93 | 94 | } 95 | uint n=0; 96 | for(uint i=0;i0){ 99 | n++; 100 | } 101 | } 102 | emit info2(foundcnt); 103 | return n; 104 | } 105 | 106 | function setAddr() public returns (uint256) { 107 | address taddr=address(0xAFFFFFFFFF000002); 108 | (bool success1, bytes memory returnBytes) = taddr.staticcall(""); 109 | require(success1 == true, "tx fetch failed"); 110 | tpTx memory ret = abi.decode(returnBytes, (tpTx)); 111 | uint256 i=0; 112 | uint256 c=0; 113 | 114 | address kaddr=address(0xAFFFFFFFFF000003); 115 | for(i=0;i0){ 122 | uint256 n=key2id[ret.signatures[i].pubkey]; 123 | if(n==0){ 124 | n=count; 125 | count++; 126 | keys[n]=ret.signatures[i].pubkey; 127 | } 128 | rewardAddresses[n]=msg.sender; 129 | emit changed(ret.signatures[i].pubkey,chain,name,msg.sender, n); 130 | c++; 131 | } 132 | }else{ 133 | emit info2(returnBytes1); 134 | } 135 | } 136 | return c; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /examples/evm_builtin/contracts/sponsor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import {ERC165} from "contracts/utils/introspection/ERC165.sol"; 5 | 6 | contract Sponsor is ERC165 { 7 | struct tpCall { 8 | string func; 9 | uint256[] args; 10 | } 11 | struct tpSig { 12 | bytes raw; 13 | uint256 timestamp; 14 | bytes pubkey; 15 | bytes rawkey; 16 | bytes signature; 17 | } 18 | struct tpTx { 19 | uint256 kind; 20 | address from; 21 | address to; 22 | uint256 t; 23 | uint256 seq; 24 | tpCall[] call; 25 | tpPayload[] payload; 26 | tpSig[] signatures; 27 | } 28 | struct tpPayload { 29 | uint256 purpose; 30 | string cur; 31 | uint256 amount; 32 | } 33 | 34 | bytes4 public constant INTERFACE_ID = bytes4(0x1B97712B); 35 | address public owner; 36 | mapping (address => uint) public allowed; 37 | 38 | constructor() { 39 | //INTERFACE_ID = this.areYouSponsor.selector ^ this.wouldYouLikeToPayTx.selector; 40 | //INTERFACE_ID = this.wouldYouLikeToPayTx.selector; = 0x1B97712B 41 | owner=msg.sender; 42 | allowed[address(0x800140057C000003)]=2; 43 | allowed[address(0x800140057B000003)]=2; 44 | allowed[address(0x800140057B000004)]=2; 45 | allowed[address(0x800140057B000005)]=2; 46 | allowed[address(0x800140057B000006)]=2; 47 | allowed[address(0x800140057B00000b)]=2; 48 | 49 | } 50 | function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) { 51 | return interfaceId == INTERFACE_ID || super.supportsInterface(interfaceId); 52 | } 53 | 54 | function areYouSponsor() public pure returns (bool,bytes memory,uint256) { 55 | return (true,'SK',10000000); 56 | } 57 | function allowAdmin(address to) public returns (uint256){ 58 | require(msg.sender==owner || 59 | (allowed[msg.sender] & 0x100000000) == 0x100000000,"Not allowed"); 60 | return allowed[to]=allowed[to] | 0x100000000; 61 | } 62 | function allow(address to, uint32 add, uint32 del) public returns (uint256) { 63 | require(msg.sender==owner || 64 | (allowed[msg.sender] & 0x100000000) == 0x100000000,"Not allowed"); 65 | return allowed[to]=(allowed[to] & ~(uint(del))) | uint(add); 66 | } 67 | 68 | function wouldYouLikeToPayTx(tpTx calldata utx) public view returns(string memory iWillPay, tpPayload[] memory pay) { 69 | if((allowed[utx.from] & 1) == 0 && (allowed[utx.to] & 2) == 0){ 70 | return("no",new tpPayload[](0)); 71 | } 72 | uint i=0; 73 | uint found_fee_hint=0; 74 | uint found_gas_hint=0; 75 | for (i=0;i0) c++; 86 | if(found_gas_hint>0) c++; 87 | 88 | tpPayload[] memory payload1=new tpPayload[](c); 89 | if(c==2){ 90 | payload1[0]=utx.payload[found_fee_hint-1]; 91 | payload1[0].purpose=1; 92 | payload1[1]=utx.payload[found_gas_hint-1]; 93 | payload1[1].purpose=3; 94 | }else if(c==1 && found_gas_hint>0){ 95 | payload1[0]=utx.payload[found_gas_hint-1]; 96 | payload1[0].purpose=3; 97 | }else if(c==1 && found_fee_hint>0){ 98 | payload1[0]=utx.payload[found_fee_hint-1]; 99 | payload1[0].purpose=1; 100 | } 101 | return ("i will pay",payload1); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /examples/monitor/chevron-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/monitor/chevron-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/monitor/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Node monitor 6 | 7 | 32 | 33 | 34 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /examples/sc_inc4.wasm.lz4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thepower/tpnode/e5308c9c89f7ed69d7af3a50295fceb2694480d7/examples/sc_inc4.wasm.lz4 -------------------------------------------------------------------------------- /examples/stout.conf: -------------------------------------------------------------------------------- 1 | {sinks, 2 | [ 3 | {console, stout_sink_console, #{}}, 4 | {bin, stout_sink_binfile, #{filename => "log/sink1.log"}}, 5 | {debug, stout_sink_file, #{filename => "log/sdebug.log"}} 6 | ] 7 | }. 8 | 9 | {routing, 10 | [ 11 | % {any, console, [{module, blockvote}] }, 12 | {any, bin, []}, 13 | {any, debug, []} 14 | % {[accept_block,mkblock_debug], sink1}, 15 | % {[test1], console, [{var1,1}]} 16 | ] 17 | }. 18 | -------------------------------------------------------------------------------- /examples/test_chain4/c4n1.conf: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | {tpic, #{ 4 | peers => [], 5 | port => 49941} 6 | }. 7 | {discovery, 8 | #{ 9 | addresses => [ 10 | #{address => "127.0.0.1", port => tpicport, proto => tpic}, 11 | #{address => local4, port => rpcport, proto => api} 12 | ] 13 | } 14 | }. 15 | 16 | {mkblock_debug, true}. 17 | % {dumpblocks, true}. 18 | 19 | % define the hostname in case you want to enable ssl api listener 20 | %{hostname, "c4n1.pwr.local"}. 21 | % if you haven't permitions to listen 80 port, you can enable webroot mode 22 | %{webroot, "/tmp"}. 23 | % for testing certificates use {staging, true} here 24 | %{staging, true}. 25 | % define rpcsport to start ssl api listener if you have a valid certificate 26 | {rpcsport, 49800}. 27 | {dumpmkblock, true}. 28 | 29 | {rpcport, 49841}. 30 | {hostname, "localhost"}. 31 | {vmport, 29841}. 32 | 33 | {crosschain, #{ 34 | port => 49741, 35 | connect => [] 36 | }}. 37 | 38 | %{privkey, "CF2BD71347FA5D645FD6586CD4FE426AF5DCC2A604C5CC2529AB6861DC948D54"}. 39 | {privkey, "302E020100300506032B657004220420DE88EBB5ACE713A45B4ADFA6856239DF798E573A8D84543A6DE2F5312C447072"}. 40 | {endless, [<<128, 1, 64, 0, 1, 0, 0, 1>>]}. 41 | 42 | 43 | {loglevel, info}. 44 | {info_log, "log/info_test_c4n1.log"}. 45 | {error_log, "log/error_test_c4n1.log"}. 46 | {debug_log, "log/debug_test_c4n1.log"}. 47 | 48 | -------------------------------------------------------------------------------- /examples/test_chain4/c4n2.conf: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | {tpic, #{ 4 | peers => [{"127.0.0.1", 49941}], 5 | port => 49942} 6 | }. 7 | {discovery, #{ 8 | addresses => [ 9 | #{address => "127.0.0.1", port => tpicport, proto => tpic}, 10 | #{address => local4, port => rpcport, proto => api} 11 | ]} 12 | }. 13 | 14 | {mkblock_debug, true}. 15 | {dumpblocks, true}. 16 | 17 | {hostname, "c4n2.pwr.local"}. 18 | {rpcport, 49842}. 19 | {vmport, 29842}. 20 | 21 | {crosschain, #{ 22 | port => 49742, 23 | connect => 24 | [{"127.0.0.1", 49752}, {"127.0.0.1", 49762}] 25 | }}. 26 | 27 | %{privkey, "15A48B170FBDAC808BA80D1692305A8EFC758CBC11252A4338131FC68AFAED6B"}. 28 | {privkey, "302E020100300506032B657004220420440521F8B059A5D64AB83DC2AF5C955D73A81D29BFBA1278861053449213F5F7"}. 29 | {endless, [<<128, 1, 64, 0, 1, 0, 0, 1>>]}. 30 | 31 | {loglevel, info}. 32 | {info_log, "log/info_test_c4n2.log"}. 33 | {error_log, "log/error_test_c4n2.log"}. 34 | {debug_log, "log/debug_test_c4n2.log"}. 35 | -------------------------------------------------------------------------------- /examples/test_chain4/c4n3.conf: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | {tpic, #{ 4 | peers => [{"127.0.0.1", 49941}], 5 | port => 49943} 6 | }. 7 | {discovery, #{ 8 | addresses => [ 9 | #{address => "127.0.0.1", port => tpicport, proto => tpic}, 10 | #{address => local4, port => rpcport, proto => api} 11 | ]} 12 | }. 13 | 14 | {mkblock_debug, true}. 15 | {dumpblocks, true}. 16 | 17 | {hostname, "c4n3.pwr.local"}. 18 | {rpcport, 49843}. 19 | {vmport, 29843}. 20 | 21 | {crosschain, #{ 22 | port => 49743, 23 | connect => [] 24 | }}. 25 | 26 | %{privkey, "2ACC7ACDBFFA92C252ADC21D8469CC08013EBE74924AB9FEA8627AE512B0A1E0"}. 27 | {privkey, "302E020100300506032B657004220420E317848D05354A227200800CF932219840080C832D0D6F57F40B95DA5B5ABCED"}. 28 | {endless, [<<128, 1, 64, 0, 1, 0, 0, 1>>]}. 29 | 30 | {loglevel, info}. 31 | {info_log, "log/info_test_c4n3.log"}. 32 | {error_log, "log/error_test_c4n3.log"}. 33 | {debug_log, "log/debug_test_c4n3.log"}. 34 | -------------------------------------------------------------------------------- /examples/test_chain4/edkeys.txt: -------------------------------------------------------------------------------- 1 | [ 2 | <<"302E020100300506032B657004220420DE88EBB5ACE713A45B4ADFA6856239DF798E573A8D84543A6DE2F5312C447072">>, 3 | <<"302E020100300506032B657004220420440521F8B059A5D64AB83DC2AF5C955D73A81D29BFBA1278861053449213F5F7">>, 4 | <<"302E020100300506032B657004220420E317848D05354A227200800CF932219840080C832D0D6F57F40B95DA5B5ABCED">> 5 | ]. 6 | %PrivKeys = [ tpecdsa:generate_priv(ed25519) || _ <- lists:seq(1,3) ] 7 | %Pubs=[ tpecdsa:calc_pub(K) || K<- PrivKeys ]. 8 | %TX=tx:construct_tx(#{ver=>2,kind=>patch,patches=>[ maps:put(<<"v">>,K,E) || {K,E} <- lists:zip(Pubs,element(1,lists:split(3,settings:dmp(maps:get(patch,element(2,hd(maps:get(settings,B0)))))))) ]}). 9 | %Blk=block:mkblock2(#{txs=>[],parent=><<186,137,125,124,66,180,214,212,204,176,116,181,230,76,255,27,2,144,201,27,173,220,28,246,48,56,95,88,157,138,212,137>>, height=>2,mychain=>4,bals=>#{},settings=>[{<<"edkeys">>,TX}]}). 10 | %lists:foldl(fun(Priv,A) -> block:sign(A, Priv) end, Blk, OldPriv). 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/test_chain4/test_c4n1.args: -------------------------------------------------------------------------------- 1 | -sname test_c4n1 2 | 3 | +K true 4 | +A30 5 | -------------------------------------------------------------------------------- /examples/test_chain4/test_c4n1.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | [ 4 | {tpnode, [ 5 | {config, "./examples/test_chain4/c4n1.conf"} 6 | ]} 7 | ]. 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/test_chain4/test_c4n2.args: -------------------------------------------------------------------------------- 1 | -sname test_c4n2 2 | 3 | +K true 4 | +A30 5 | -------------------------------------------------------------------------------- /examples/test_chain4/test_c4n2.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | [ 4 | {tpnode, [ 5 | {config, "./examples/test_chain4/c4n2.conf"} 6 | ]} 7 | ]. 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/test_chain4/test_c4n3.args: -------------------------------------------------------------------------------- 1 | -sname test_c4n3 2 | 3 | +K true 4 | +A30 5 | -------------------------------------------------------------------------------- /examples/test_chain4/test_c4n3.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | [ 4 | {tpnode, [ 5 | {config, "./examples/test_chain4/c4n3.conf"} 6 | ]} 7 | ]. 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/test_chain5/c5n1.conf: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | {tpic, #{ 4 | peers => [], 5 | port => 49951} 6 | }. 7 | {discovery, #{ 8 | addresses => [ 9 | #{address => "127.0.0.1", port => tpicport, proto => tpic}, 10 | #{address => local4, port => rpcport, proto => api} 11 | ]} 12 | }. 13 | {hostname, "c5n1.pwr.local"}. 14 | {rpcport, 49851}. 15 | 16 | {crosschain, #{ 17 | port => 49751, 18 | connect => [] 19 | }}. 20 | 21 | %{privkey, "A164B0F8E84FC13BC95B2B2747DE8D4A780C21F6E33B3435BC2277C456AA007C"}. 22 | {privkey, "302E020100300506032B657004220420077A31031D901BA978D9D258166FE03FC1399FF718AE09259341E4B54AA3403A"}. 23 | {endless, [<<128, 1, 64, 0, 1, 0, 0, 1>>]}. 24 | 25 | {loglevel, info}. 26 | {info_log, "log/info_test_c5n1.log"}. 27 | {error_log, "log/error_test_c5n1.log"}. 28 | {debug_log, "log/debug_test_c5n1.log"}. 29 | -------------------------------------------------------------------------------- /examples/test_chain5/c5n2.conf: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | {tpic, #{ 4 | peers => [{"127.0.0.1", 49951}], 5 | port => 49952} 6 | }. 7 | {discovery, #{ 8 | addresses => [ 9 | #{address => "127.0.0.1", port => tpicport, proto => tpic}, 10 | #{address => local4, port => rpcport, proto => api} 11 | ]} 12 | }. 13 | {hostname, "c5n2.pwr.local"}. 14 | {rpcport, 49852}. 15 | 16 | {crosschain, #{ 17 | port => 49752, 18 | connect => 19 | [{"127.0.0.1", 49742}, {"127.0.0.1", 49762}] 20 | }}. 21 | 22 | %{privkey, "B3C8CF0317D9228018C647F54E388A133708875C00E787266F682FBC25C8A01A"}. 23 | {privkey, "302E020100300506032B6570042204206A474A7E8CB569631C4D87A1D9261A99E7024482812009A25D3A0FC0BAA5681F"}. 24 | {endless, [<<128, 1, 64, 0, 1, 0, 0, 1>>]}. 25 | 26 | {loglevel, info}. 27 | {info_log, "log/info_test_c5n2.log"}. 28 | {error_log, "log/error_test_c5n2.log"}. 29 | {debug_log, "log/debug_test_c5n2.log"}. 30 | -------------------------------------------------------------------------------- /examples/test_chain5/c5n3.conf: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | {tpic, #{ 4 | peers => [{"127.0.0.1", 49951}], 5 | port => 49953} 6 | }. 7 | {discovery, #{ 8 | addresses => [ 9 | #{address => "127.0.0.1", port => tpicport, proto => tpic}, 10 | #{address => local4, port => rpcport, proto => api} 11 | ]} 12 | }. 13 | 14 | {hostname, "c5n3.pwr.local"}. 15 | {rpcport, 49853}. 16 | 17 | {crosschain, #{ 18 | port => 49753, 19 | connect => [] 20 | }}. 21 | 22 | %{privkey, "158729788B952BDE8073DEA6EAEE3A833F98A8EB8CBD23A83CC771656D7CE25B"}. 23 | {privkey, "302E020100300506032B6570042204204E7526717586ABE35C3536469E26EC40BA277DBAEB48C9E03B688AA66C8C94FA"}. 24 | {endless, [<<128, 1, 64, 0, 1, 0, 0, 1>>]}. 25 | 26 | {loglevel, info}. 27 | {info_log, "log/info_test_c5n3.log"}. 28 | {error_log, "log/error_test_c5n3.log"}. 29 | {debug_log, "log/debug_test_c5n3.log"}. 30 | -------------------------------------------------------------------------------- /examples/test_chain5/test_c5n1.args: -------------------------------------------------------------------------------- 1 | -sname test_c5n1 2 | 3 | +K true 4 | +A30 5 | -------------------------------------------------------------------------------- /examples/test_chain5/test_c5n1.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | [ 4 | {tpnode, [ 5 | {config, "./examples/test_chain5/c5n1.conf"} 6 | ]} 7 | ]. 8 | 9 | -------------------------------------------------------------------------------- /examples/test_chain5/test_c5n2.args: -------------------------------------------------------------------------------- 1 | -sname test_c5n2 2 | 3 | +K true 4 | +A30 5 | -------------------------------------------------------------------------------- /examples/test_chain5/test_c5n2.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | [ 3 | {tpnode, [ 4 | {config, "./examples/test_chain5/c5n2.conf"} 5 | ]} 6 | ]. 7 | 8 | -------------------------------------------------------------------------------- /examples/test_chain5/test_c5n3.args: -------------------------------------------------------------------------------- 1 | -sname test_c5n3 2 | 3 | +K true 4 | +A30 5 | -------------------------------------------------------------------------------- /examples/test_chain5/test_c5n3.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | [ 4 | {tpnode, [ 5 | {config, "./examples/test_chain5/c5n3.conf"} 6 | ]} 7 | ]. 8 | 9 | -------------------------------------------------------------------------------- /examples/test_chain6/c6n1.conf: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | {tpic, #{ 4 | peers => [], 5 | port => 49961} 6 | }. 7 | {discovery, #{ 8 | addresses => [ 9 | #{address => "127.0.0.1", port => tpicport, proto => tpic}, 10 | #{address => local4, port => rpcport, proto => api} 11 | ]} 12 | }. 13 | 14 | {hostname, "c6n1.pwr.local"}. 15 | {rpcport, 49861}. 16 | 17 | {crosschain, #{ 18 | port => 49761, 19 | connect => [] 20 | }}. 21 | 22 | %{privkey, "7DABC8CAE611B9E36080189FB9A1F9CA1F6AC1A7AB0ED4CFBE6BB3A7342FBC30"}. 23 | {privkey, "302E020100300506032B657004220420D86CC0529CBF223592F1F9E92B913E84F2DC2F4DE710E126F76EABE8F27A26AD"}. 24 | {endless, [<<128, 1, 64, 0, 1, 0, 0, 1>>]}. 25 | 26 | {loglevel, info}. 27 | {info_log, "log/info_test_c6n1.log"}. 28 | {error_log, "log/error_test_c6n1.log"}. 29 | {debug_log, "log/debug_test_c6n1.log"}. 30 | -------------------------------------------------------------------------------- /examples/test_chain6/c6n2.conf: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | {tpic, #{ 4 | peers => [{"127.0.0.1", 49961}], 5 | port => 49962} 6 | }. 7 | {discovery, #{ 8 | addresses => [ 9 | #{address => "127.0.0.1", port => tpicport, proto => tpic}, 10 | #{address => local4, port => rpcport, proto => api} 11 | ]} 12 | }. 13 | 14 | {hostname, "c6n2.pwr.local"}. 15 | {rpcport, 49862}. 16 | 17 | {crosschain, #{ 18 | port => 49762, 19 | connect => 20 | [{"127.0.0.1", 49742}, {"127.0.0.1", 49752}] 21 | }}. 22 | 23 | %{privkey, "C6B6CD3076AB2A719260833304C76EB7586E0D083BCDC42D968CCA1953BA0D7C"}. 24 | {privkey, "302E020100300506032B6570042204204F602E0972BD0D829F4355AACCE71E23A8B2CEA596AE26E2DE12B235ED0F7179"}. 25 | {endless, [<<128, 1, 64, 0, 1, 0, 0, 1>>]}. 26 | 27 | {loglevel, info}. 28 | {info_log, "log/info_test_c6n2.log"}. 29 | {error_log, "log/error_test_c6n2.log"}. 30 | {debug_log, "log/debug_test_c6n2.log"}. 31 | -------------------------------------------------------------------------------- /examples/test_chain6/c6n3.conf: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | {tpic, #{ 4 | peers => [{"127.0.0.1", 49961}], 5 | port => 49963} 6 | }. 7 | {discovery, #{ 8 | addresses => [ 9 | #{address => "127.0.0.1", port => tpicport, proto => tpic}, 10 | #{address => local4, port => rpcport, proto => api} 11 | ]} 12 | }. 13 | 14 | {hostname, "c6n3.pwr.local"}. 15 | {rpcport, 49863}. 16 | 17 | {crosschain, #{ 18 | port => 49763, 19 | connect => [] 20 | }}. 21 | 22 | %{privkey, "18F0BE8ECFA6E4993EA2273AA9569B02C8ACA7A75862A2A72C8B8F1058E1E92E"}. 23 | {privkey, "302E020100300506032B6570042204205B87C1727A6533FDF8FB67431E37652D82206AFFB3B42AB8045DD626BD32C781"}. 24 | 25 | {loglevel, info}. 26 | {info_log, "log/info_test_c6n3.log"}. 27 | {error_log, "log/error_test_c6n3.log"}. 28 | {debug_log, "log/debug_test_c6n3.log"}. 29 | -------------------------------------------------------------------------------- /examples/test_chain6/test_c6n1.args: -------------------------------------------------------------------------------- 1 | -sname test_c6n1 2 | 3 | +K true 4 | +A30 5 | -------------------------------------------------------------------------------- /examples/test_chain6/test_c6n1.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | [ 4 | {tpnode, [ 5 | {config, "./examples/test_chain6/c6n1.conf"} 6 | ]} 7 | ]. 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/test_chain6/test_c6n2.args: -------------------------------------------------------------------------------- 1 | -sname test_c6n2 2 | 3 | +K true 4 | +A30 5 | -------------------------------------------------------------------------------- /examples/test_chain6/test_c6n2.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | [ 3 | {tpnode, [ 4 | {config, "./examples/test_chain6/c6n2.conf"} 5 | ]} 6 | ]. 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/test_chain6/test_c6n3.args: -------------------------------------------------------------------------------- 1 | -sname test_c6n3 2 | 3 | +K true 4 | +A30 5 | -------------------------------------------------------------------------------- /examples/test_chain6/test_c6n3.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | [ 4 | {tpnode, [ 5 | {config, "./examples/test_chain6/c6n3.conf"} 6 | ]} 7 | ]. 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/testcontract.ec: -------------------------------------------------------------------------------- 1 | %% -*- syntax: erlang -*- 2 | 3 | Run=fun 4 | (deploy,"init",[InitVal]) when is_integer(InitVal) -> 5 | {ok, "result", <>, Gas-1, []}; 6 | (deploy,"init",_) -> 7 | {ok, "result", <<0:64/big>>, Gas-1, []}; 8 | (deploy,"expensive",_) -> 9 | {ok, "result", <<0:64/big>>, Gas-10000, []}; 10 | (deploy,_,_) -> 11 | throw('error'); 12 | (generic,"inc",[Val]) when is_integer(Val) -> 13 | <>=maps:get(<<"state">>, Ledger), 14 | S1=S0+Val, 15 | {ok, "result", <>, Gas-1, []}; 16 | (generic,"dec",[Val]) when is_integer(Val) -> 17 | <>=maps:get(<<"state">>, Ledger), 18 | S1=S0-Val, 19 | {ok, "result", <>, Gas-100, []}; 20 | (generic,"expensive",[Val]) when is_integer(Val) -> 21 | <>=maps:get(<<"state">>, Ledger), 22 | S1=S0-Val, 23 | {ok, "result", <>, Gas-10000, []}; 24 | (generic,_,_) -> 25 | <>=maps:get(<<"state">>, Ledger), 26 | S1=S0+1, 27 | {ok, "result", <>, Gas-1, []} 28 | end, 29 | 30 | Kind=maps:get(kind, Tx), 31 | #{args := A,function := F}=maps:get(call, Tx, #{args => [],function => "default"}), 32 | 33 | %io:format("Ledger ~p~n",[maps:without([<<"code">>,<<"state">>],Ledger)]), 34 | %io:format("Tx ~p~n",[maps:without([body,sig],Tx)]), 35 | %io:format("Gas ~p~n",[Gas]), 36 | %io:format("Pid ~p~n",[self()]), 37 | 38 | %{ok, "result", <<"new_state">>, Gas-100, []}. 39 | Res=Run(Kind, F, A), 40 | 41 | 42 | %error_logger:info_msg("call ~s(~s, ~s, ~p)=~p",[testcontract,Kind,F,A,Res]), 43 | Res. 44 | 45 | -------------------------------------------------------------------------------- /examples/testcontract.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thepower/tpnode/e5308c9c89f7ed69d7af3a50295fceb2694480d7/examples/testcontract.wasm -------------------------------------------------------------------------------- /examples/testcontract_emit.ec: -------------------------------------------------------------------------------- 1 | %% -*- syntax: erlang -*- 2 | 3 | io:format("Preved from sc~n"), 4 | io:format("Mean time is ~p~n",[MeanTime]), 5 | {ok,State} = msgpack:unpack(maps:get(<<"state">>, Ledger,<<128>>)), 6 | 7 | Run=fun 8 | (deploy,"init",[InitVal], _Tx) when is_integer(InitVal) -> 9 | {ok, "result", #{<<0>> => <>}, Gas, []}; 10 | (deploy,"init",_, _Tx) -> 11 | {ok, "result", #{<<0>> => <<0:64/big>>}, Gas-1, []}; 12 | (deploy,"expensive",_, _Tx) -> 13 | {ok, "result", #{<<0>> => <<0:64/big>>}, Gas-10000, []}; 14 | (deploy,_,_, _Tx) -> 15 | throw('error'); 16 | (generic,"inc",[Val], _Tx) when is_integer(Val) -> 17 | <>=maps:get(<<0>>,State), 18 | S1=S0+Val, 19 | {ok, "result", #{<<0>> => <>}, Gas-1, []}; 20 | (generic,"dec",[Val], _Tx) when is_integer(Val) -> 21 | <>=maps:get(<<0>>,State), 22 | S1=S0-Val, 23 | {ok, "result", #{<<0>> => <>}, Gas-100, []}; 24 | (generic,"expensive",[Val], _Tx) when is_integer(Val) -> 25 | <>=maps:get(<<0>>,State), 26 | S1=S0-Val, 27 | {ok, "result", #{<<0>> => <>}, Gas-10000, []}; 28 | (generic,"notify",[Val], #{from:=F}) when is_integer(Val) -> 29 | NewTx=#{ver=>2,kind=>notify,from=>F, 30 | t=>0, 31 | seq=>0, 32 | payload=>[], 33 | notify=>[{"http://10.250.250.240/test/endpoint",<<"preved, medved",Val:32/big>>}] 34 | }, 35 | NTX=maps:get(body,tx:construct_tx(NewTx)), 36 | S0=maps:get(<<0>>,State), 37 | {ok, "result", #{<<0>> => S0}, Gas, [NTX]}; 38 | (generic,"badnotify",[Val], #{from:=F}) when is_integer(Val) -> 39 | NTX= <<130,161,107,23,162,101,118,145,146,217,33,104,116,116,112,58,47,47,53,49,46,49,53,57,46,53,55,46,57,54,58,51,48,48,48,47,103,101,116,111,119,110,101,114,196,12,72,101,108,108,111,32,119,111,114,108,100,33>>, 40 | S0=maps:get(<<0>>,State), 41 | {ok, "result", #{<<0>> => S0}, Gas, [NTX]}; 42 | (generic,"emit",[Val], #{from:=F}) when is_integer(Val) -> 43 | NewTx=#{ver=>2,kind=>generic,from=>F, 44 | to=>F, 45 | t=>0, 46 | seq=>0, 47 | payload=>[#{purpose=>gas, amount=>50000, cur=><<"FTT">>}], 48 | call=>#{function=>"notify",args=>[512]} 49 | }, 50 | NTX=maps:get(body,tx:construct_tx(NewTx)), 51 | S0=maps:get(<<0>>,State), 52 | {ok, "result", #{<<0>> => S0}, Gas, [NTX]}; 53 | (generic,"delayjob",[Val], #{from:=F}) when is_integer(Val) -> 54 | NewTx=#{ver=>2,kind=>generic,from=>F, 55 | to=>F, 56 | t=>0, 57 | seq=>0, 58 | not_before => (MeanTime div 1000) + 60, 59 | payload=>[#{purpose=>gas, amount=>50000, cur=><<"FTT">>}], 60 | call=>#{function=>"notify",args=>[512]} 61 | }, 62 | NTX=maps:get(body,tx:construct_tx(NewTx)), 63 | S0=maps:get(<<0>>,State), 64 | {ok, "result", #{<<0>> => S0}, Gas, [NTX]}; 65 | (generic,_,_, _Tx) -> 66 | <>=maps:get(<<0>>,State), 67 | S1=S0+1, 68 | {ok, "result", #{<<0>> => <>}, Gas-1, []} 69 | end, 70 | 71 | Kind=maps:get(kind, Tx), 72 | #{args := A,function := F}=maps:get(call, Tx, #{args => [],function => "default"}), 73 | 74 | %io:format("Ledger ~p~n",[maps:without([<<"code">>,<<"state">>],Ledger)]), 75 | io:format("Tx ~p~n",[maps:without([body,sig],Tx)]), 76 | io:format("Call ~s(~p)~n",[F,A]), 77 | io:format("Gas ~p~n",[Gas]), 78 | %io:format("Pid ~p~n",[self()]), 79 | 80 | %{ok, "result", <<"new_state">>, Gas-100, []}. 81 | Res=Run(Kind, F, A, Tx), 82 | 83 | io:format("call ~s(~s, ~s, ~p)=~p~n",[testcontract,Kind,F,A,Res]), 84 | Res. 85 | 86 | -------------------------------------------------------------------------------- /guides/ssl-certs-for-node.md: -------------------------------------------------------------------------------- 1 | **Table of Contents** 2 | 3 | - [How to obtain an SSL certificate for a node](#how-to-obtain-an-ssl-certificate-for-a-node) 4 | 5 | # How to obtain an SSL certificate for a node 6 | 7 | If you need an SSL certificate for your node, follow the steps below: 8 | 9 | 1. Ensure that you use `root` account. It is necessary for further steps. 10 | 2. Install `acme.sh` by running the following command: 11 | 12 | ```bash 13 | apt-get install socat 14 | curl https://get.acme.sh | sh -s email=my@example.com 15 | ``` 16 | 3. Log out of the system. 17 | 4. Log in again. 18 | 5. Obtain the certificate. To do this, run the following command: 19 | 20 | ```bash 21 | acme.sh --issue --standalone -d your_node.example.com \ 22 | --renew-hook "cd /opt/your_node; ./stop.sh; ./start.sh" 23 | ``` 24 | 25 | 6. Install the certificate by running the following command: 26 | 27 | ```bash 28 | acme.sh --install-cert -d your_node.example.com \ 29 | --fullchain-file /opt/your_node/db/cert_your_node/your_node.example.com.crt \ 30 | --key-file /opt/your_node/db/cert_your_node/your_node.example.com.key 31 | ``` 32 | 33 | You can add Telegram notifications to promptly get information about Acme script actions. To do this, run: 34 | 35 | ```bash 36 | export TELEGRAM_BOT_CHATID=“Your Telegram ID” \ 37 | export TELEGRAM_BOT_APITOKEN=“Chatbot token” \ 38 | acme.sh --set-notify --notify-hook telegram 39 | ``` 40 | 41 | After you've installed the certificate, you can get the certificate status by running the following command: 42 | 43 | ```bash 44 | acme.sh --info -d your_node.example.com 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /guides/startingTpNode_docker.md: -------------------------------------------------------------------------------- 1 | **Table of Contents** 2 | 3 | - [How to start a TP-Node from a Docker image?](#how-to-start-a-tp-node-from-a-docker-image) 4 | - [Setting up the environment](#setting-up-the-environment) 5 | - [Starting the node](#starting-the-node) 6 | 7 | # How to start a TP-Node from a Docker image? 8 | 9 | You can start a TP-Node using the [Docker image](https://hub.docker.com/r/thepowerio/tpnode). 10 | 11 | ## Setting up the environment 12 | 13 | Before you start a TP-Node using the Docker image: 14 | 15 | 1. Ensure you have Docker installed on your machine. 16 | 2. If not, refer to [Docker Installation Guide](https://docs.docker.com/engine/install/). 17 | 3. Check user groups you belong to by running the following command: 18 | 19 | ```bash 20 | $ groups 21 | ``` 22 | 4. If you don't belong to the user group `docker`, you will not be able to start Docker. To resolve this problem, run: 23 | 24 | ```bash 25 | # usermod -a docker 26 | ``` 27 | 28 | 5. Ensure you have the actual `genesis.txt` and `node.config` files. 29 | 6. Create `db` and `log` directories in your working directory (`/opt`, for instance). 30 | 31 | > **Hint** 32 | > 33 | > You can create an additional directory named `thepower`, for example, and place `db` and `log` as subdirectories there. 34 | 35 | 7. Place the files `genesis.txt` and `node.config` near `db` and `log` directories. 36 | 37 | ## Starting the node 38 | 39 | To start the TP-Node run the following command: 40 | 41 | ```bash 42 | docker run -d \ 43 | --name tpnode \ 44 | --mount type=bind,source="$(pwd)"/db,target=/opt/thepower/db \ 45 | --mount type=bind,source="$(pwd)"/log,target=/opt/thepower/log \ 46 | --mount type=bind,source="$(pwd)"/node.config,target=/opt/thepower/node.config \ 47 | --mount type=bind,source="$(pwd)"/genesis.txt,target=/opt/thepower/genesis.txt \ 48 | -p 43292:43292 \ 49 | -p 43392:43392 \ 50 | -p 43219:43219 \ 51 | thepowerio/tpnode 52 | ``` 53 | 54 | where: 55 | 56 | | Command | Description | 57 | |----------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| 58 | | `docker run -d` | This command starts Docker in the background | 59 | | `--name tpnode` | This command specifies the name (optional) | 60 | | `--mount type=bind,source="$(pwd)"/db,target=/opt/thepower/db` | Path to the database. Bound to Docker. `/opt` here is mandatory, because it is the path inside the container. | 61 | | `--mount type=bind,source="$(pwd)"/log,target=/opt/thepower/log` | Path to log files. Bound to Docker. `/opt` here is mandatory, because it is the path inside the container. | 62 | | `--mount type=bind,source="$(pwd)"/node.config,target=/opt/thepower/node.config` | Path to your `node.config` file. Bound to Docker. `/opt` here is mandatory, because it is the path inside the container. | 63 | | `--mount type=bind,source="$(pwd)"/genesis.txt,target=/opt/thepower/genesis.txt` | Path to your `genesis.txt`. Bound to Docker. `/opt` here is mandatory, because it is the path inside the container. | 64 | | `-p 43292:43292`
`-p 43392:43392`
`-p 43219:43219` | These commands specify all necessary local ports. In this examples ports `api`, `apis`, and `tpic` are used. You can specify any port in `node.config` file | 65 | | `thepowerio/tpnode` | Path to Docker image. | -------------------------------------------------------------------------------- /guides/startingTpNode_source.md: -------------------------------------------------------------------------------- 1 | **Table of Contents** 2 | 3 | - [How to start a TP-Node from the source?](#how-to-start-a-tp-node-from-the-source) 4 | - [Setting up the environment](#setting-up-the-environment) 5 | - [Downloading and building the node](#downloading-and-building-the-node) 6 | - [Starting the node](#starting-the-node) 7 | - [Starting the node in Dev Mode](#starting-the-node-in-dev-mode) 8 | - [Starting the node in Release Mode](#starting-the-node-in-release-mode) 9 | 10 | # How to start a TP-Node from the source? 11 | 12 | TP-Node is the main module of The Power Ecosystem. In this manual, you'll learn how to start a node from the source code on Linux. 13 | 14 | ## Setting up the environment 15 | 16 | Before you start your TP-Node, you need to set up the environment: 17 | 18 | 1. Check if you have Git installed: 19 | 20 | ```bash 21 | git version 22 | ``` 23 | 2. If you don't have Git installed on your machine, run: 24 | 25 | ```bash 26 | apt install git 27 | ``` 28 | 29 | 3. Install Erlang. To do this, download the `kerl` script 30 | 31 | ```bash 32 | curl -O https://raw.githubusercontent.com/kerl/kerl/master/kerl 33 | ``` 34 | > **Note** 35 | > 36 | > If you already have Erlang installed on your machine, we strongly recommend deleting it before the new installation, using the following command: 37 | > 38 | > ```bash 39 | > apt purge erlang* 40 | > ``` 41 | 42 | 4. Change script mode to executable by using the following command: 43 | 44 | ```bash 45 | chmod a+x kerl 46 | ``` 47 | 48 | 5. Create a new directory in `/opt`. You can choose any name for this directory. Noteworthy is that the name should be descriptive for you: 49 | 50 | ```bash 51 | mkdir erlang 52 | ``` 53 | 6. Update the list of Erlang releases using the following command: 54 | 55 | ```bash 56 | ./kerl update releases 57 | ``` 58 | 59 | 7. Build the release 22.3.4.25 using the following command: 60 | 61 | ```bash 62 | ./kerl build 22.3.4.25 63 | ``` 64 | 65 | 66 | > **Important** 67 | > 68 | > You need to install Erlang ver. 22.3.4.25. Other versions may not work correctly. 69 | 70 | After installation is complete, you will see the following message in the console: 71 | 72 | ```text 73 | Erlang/OTP 22.3.4.25 (22.3.4.25) has been successfully built 74 | ``` 75 | 76 | 8. Install Erlang using the following command: 77 | 78 | ```bash 79 | ./kerl install 22.3.4.25 /opt/erlang 80 | ``` 81 | 82 | 9. Run the following command to activate the Erlang installation: 83 | 84 | ```bash 85 | source /opt/erlang/activate 86 | ``` 87 | 88 | ## Downloading and building the node 89 | 90 | After setting up the working environment, you can download and build the node: 91 | 92 | > **Note** 93 | > 94 | > Choose a project folder to clone your project into. Use this folder to build the node. 95 | 96 | 1. Download the node sources from Github into your working directory (`your_node`, for instance), using the following command: 97 | 98 | ```bash 99 | git clone https://github.com/thepower/tpnode.git 100 | ``` 101 | 102 | 2. Delete the previous builds (if present) in `/tpnode` by running the following command: 103 | 104 | ```bash 105 | rm -rf _build/default/* 106 | ``` 107 | 108 | 3. Compile the node source by running the following command: 109 | 110 | ```bash 111 | ./rebar3 compile 112 | ``` 113 | 4. Pack the compiled node into a `tar` by running the following command: 114 | 115 | ```bash 116 | ./rebar3 tar 117 | ``` 118 | 119 | > **Note** 120 | > 121 | > This step is optional. `tar`-package is needed to quickly transfer the compiled source code. However, it can be a good option if you need to download the source manifold. 122 | 123 | Now you can start the node. 124 | 125 | ## Starting the node 126 | 127 | You can start a node in two different modes: 128 | 129 | - Dev Mode. In this mode, you can start a node without building a release. 130 | - Release Mode. In this mode, you must build the release before starting the node. 131 | 132 | ### Starting the node in Dev Mode 133 | 134 | 1. Before starting the node in Dev Mode, start a `tmux` session: 135 | 136 | ```bash 137 | tmux 138 | ``` 139 | 140 | To start the node in Dev Mode, run: 141 | 142 | ```bash 143 | ./rebar3 shell 144 | ``` 145 | ### Starting the node in Release Mode 146 | 147 | 1. Refer to section ["Downloading and building the node"](#downloading-and-building-the-node) above to build the node. 148 | 2. To start the node, run: 149 | 150 | ```bash 151 | ./bin/thepower foreground 152 | ``` 153 | -------------------------------------------------------------------------------- /include/tplog.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(LOGGER_HRL). 2 | -define(LOGGER_HRL,true). 3 | -define(LOG_EMERGENCY(A),?DO_LOG(emergency,[A])). 4 | -define(LOG_EMERGENCY(A,B),?DO_LOG(emergency,[A,B])). 5 | -define(LOG_EMERGENCY(A,B,C),?DO_LOG(emergency,[A,B,C])). 6 | 7 | -define(LOG_ALERT(A),?DO_LOG(alert,[A])). 8 | -define(LOG_ALERT(A,B),?DO_LOG(alert,[A,B])). 9 | -define(LOG_ALERT(A,B,C),?DO_LOG(alert,[A,B,C])). 10 | 11 | -define(LOG_CRITICAL(A),?DO_LOG(critical,[A])). 12 | -define(LOG_CRITICAL(A,B),?DO_LOG(critical,[A,B])). 13 | -define(LOG_CRITICAL(A,B,C),?DO_LOG(critical,[A,B,C])). 14 | 15 | -define(LOG_ERROR(A),?DO_LOG(error,[A])). 16 | -define(LOG_ERROR(A,B),?DO_LOG(error,[A,B])). 17 | -define(LOG_ERROR(A,B,C),?DO_LOG(error,[A,B,C])). 18 | 19 | -define(LOG_WARNING(A),?DO_LOG(warning,[A])). 20 | -define(LOG_WARNING(A,B),?DO_LOG(warning,[A,B])). 21 | -define(LOG_WARNING(A,B,C),?DO_LOG(warning,[A,B,C])). 22 | 23 | -define(LOG_NOTICE(A),?DO_LOG(notice,[A])). 24 | -define(LOG_NOTICE(A,B),?DO_LOG(notice,[A,B])). 25 | -define(LOG_NOTICE(A,B,C),?DO_LOG(notice,[A,B,C])). 26 | 27 | -define(LOG_INFO(A),?DO_LOG(info,[A])). 28 | -define(LOG_INFO(A,B),?DO_LOG(info,[A,B])). 29 | -define(LOG_INFO(A,B,C),?DO_LOG(info,[A,B,C])). 30 | 31 | -define(LOG_DEBUG(A),?DO_LOG(debug,[A])). 32 | -define(LOG_DEBUG(A,B),?DO_LOG(debug,[A,B])). 33 | -define(LOG_DEBUG(A,B,C),?DO_LOG(debug,[A,B,C])). 34 | 35 | -define(LOG(L,A),?DO_LOG(L,[A])). 36 | -define(LOG(L,A,B),?DO_LOG(L,[A,B])). 37 | -define(LOG(L,A,B,C),?DO_LOG(L,[A,B,C])). 38 | 39 | -define(LOCATION,#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, 40 | line=>?LINE, 41 | file=>?MODULE}). 42 | 43 | %%%----------------------------------------------------------------- 44 | %%% Internal, i.e. not intended for direct use in code - use above 45 | %%% macros instead! 46 | -define(DO_LOG(Level,Args), 47 | case logger:allow(Level,?MODULE) of 48 | true -> 49 | erlang:apply(logger,macro_log,[?LOCATION,Level|Args]); 50 | false -> 51 | ok 52 | end). 53 | -endif. 54 | -------------------------------------------------------------------------------- /node.config.example: -------------------------------------------------------------------------------- 1 | {tpic,#{peers => [{"::1",43211}],port => 43212}}. 2 | {discovery,#{addresses => [ 3 | #{address => "::1",port => 43312,proto => tpic}, 4 | #{address => local6,port => 43212, proto => tpic}, 5 | #{address => "2.8.34.20", port=>rpcport, proto=>api}, 6 | #{address => "5.3.12.69", port=>rpcport, proto=>api} 7 | ]}}. 8 | 9 | {rpcport,43282}. 10 | {privkey,"1E919CD3897241D78B420255F66426CC9A31163653AD7685DBBF0C34FFFFFFFF"}. 11 | 12 | {crosschain, #{ port => 43321, connect => [ 13 | {"127.0.0.1",43321} 14 | 15 | ]}}. 16 | %{dbpath,"db1"}. 17 | %{dbsuffix,""}. 18 | 19 | -------------------------------------------------------------------------------- /priv/tx_const.json: -------------------------------------------------------------------------------- 1 | { 2 | "purpose": { 3 | "0": "transfer", 4 | "1": "srcfee", 5 | "2": "dstfee", 6 | "3": "gas", 7 | "33": "srcfeehint", 8 | "35": "gashint" 9 | }, 10 | "kind": { 11 | "16": ["generic", 2], 12 | "17": ["register", 2], 13 | "18": ["deploy", 2], 14 | "19": ["patch", 2], 15 | "20": ["block", 2], 16 | "21": ["tstore", 2], 17 | "22": ["lstore", 2], 18 | "23": ["notify", 2], 19 | "24": ["chkey", 2] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rebar.config.script: -------------------------------------------------------------------------------- 1 | 2 | Ver=case string:chomp(os:cmd("git describe")) of 3 | "v"++Version -> 4 | Version; 5 | _ -> 6 | throw('bad_version') 7 | end, 8 | CONFIG2=lists:map( 9 | fun 10 | ({relx,RP}) -> 11 | RP2=lists:map( 12 | fun({release,{thepower,_},Deps}) -> 13 | {release,{thepower,Ver},Deps}; 14 | (Any) -> Any 15 | end, 16 | RP), 17 | {relx,RP2}; 18 | (A) -> A 19 | end, 20 | CONFIG 21 | ), 22 | {relx, Rel}=lists:keyfind(relx,1,CONFIG2), 23 | {release,RelVer,_Deps}=lists:keyfind(release,1,Rel), 24 | 25 | io:format("Version: ~p~n",[RelVer]), 26 | 27 | CONFIG2. 28 | -------------------------------------------------------------------------------- /rebar3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thepower/tpnode/e5308c9c89f7ed69d7af3a50295fceb2694480d7/rebar3 -------------------------------------------------------------------------------- /stout.conf: -------------------------------------------------------------------------------- 1 | {sinks, 2 | [ 3 | % {api, stout_sink_binfile, 4 | % #{ 5 | % filename => "log/api_%node%.blog", 6 | % count => 30, 7 | % size => 10485760 8 | % }}, 9 | {console, stout_sink_console, #{}}, 10 | % {bv_gotsig, stout_sink_binfile, #{filename=>"log/bv_gotsig_%node%.log"}}, 11 | % {bin, stout_sink_binfile, #{filename=>"log/sink1_%node%.log"}}, 12 | {foruml, stout_sink_binfile, 13 | #{ 14 | filename => "log/uml_%node%.blog", 15 | count => 10, 16 | size => 52428800 17 | } 18 | } 19 | % {debug, stout_sink_file, #{filename=>"log/sdebug_%node%.log"}} 20 | ] 21 | }. 22 | 23 | {routing, 24 | [ 25 | % {[ api_call ], api}, 26 | % {[ bv_gotsig ], bv_gotsig}, 27 | {any, foruml}, 28 | % {[ mkblock_done, bv_gotsig, bv_gotblock, bv_ready, mkblock_debug, 29 | % accept_block, mkblock_process, sync_ticktimer, txqueue_prepare, 30 | % got_new_block, sync_needed 31 | % ], foruml}, 32 | {any, bin, []} 33 | % {any, debug, []} 34 | ] 35 | }. 36 | -------------------------------------------------------------------------------- /test/bsig_test.erl: -------------------------------------------------------------------------------- 1 | -module(bsig_test). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | 5 | poa_test() -> 6 | Priv1=tpecdsa:generate_priv(ed25519), 7 | Priv2=tpecdsa:generate_priv(ed25519), 8 | Pub2=tpecdsa:calc_pub(Priv2), 9 | Priv3=tpecdsa:generate_priv(ed25519), 10 | Pub3=tpecdsa:calc_pub(Priv3), 11 | T1=os:system_time(millisecond), 12 | T2=T1+1000, 13 | T1v=T1+300000, 14 | T2v=T2+200000, 15 | PoA1=bsig:signhash(Pub2,[{timestamp,T1}, 16 | {expire,T1v}, 17 | {baldep,{<<129,0,0,0,0,0,0,1>>,128}}, 18 | {baldep,{<<129,0,0,0,0,0,0,2>>,129}} 19 | ],Priv1), 20 | PoA2=bsig:signhash(Pub3,[{timestamp,T2}, 21 | {expire,T2v}, 22 | {baldep,{<<129,0,0,0,0,0,0,4>>,130}}, 23 | {baldep,{<<129,0,0,0,0,0,0,3>>,129}}, 24 | {poa, PoA1} 25 | ],Priv2), 26 | Preved=bsig:signhash(<<"preved">>, 27 | [ 28 | {timestamp,os:system_time(millisecond)}, 29 | {poa,PoA2} 30 | ],Priv3 31 | ), 32 | {true,Constraints}=bsig:checksig1(<<"preved">>, 33 | Preved, 34 | fun(Key,Time,_) -> 35 | ?assertEqual(Key,tpecdsa:calc_pub(Priv1)), 36 | {true,Time} 37 | end), 38 | #{baldep:=BD,tmax:=TMax,tmin:=TMin}=Constraints, 39 | [ 40 | ?assertEqual(TMax,T2+200000), 41 | ?assertEqual(TMin,T2), 42 | ?assertEqual(lists:sort(BD), 43 | [{<<129,0,0,0,0,0,0,1>>,128}, 44 | {<<129,0,0,0,0,0,0,2>>,129}, 45 | {<<129,0,0,0,0,0,0,3>>,129}, 46 | {<<129,0,0,0,0,0,0,4>>,130}] 47 | ) 48 | ]. 49 | 50 | 51 | -------------------------------------------------------------------------------- /test/lstore_test.erl: -------------------------------------------------------------------------------- 1 | -module(lstore_test). 2 | -include_lib("eunit/include/eunit.hrl"). 3 | 4 | lstore_test() -> 5 | Addr= <<128,1,64,0,2,0,0,1>>, 6 | Test=fun(_) -> 7 | GetSettings= 8 | fun(mychain) -> 9 | 2; 10 | (settings) -> 11 | #{ 12 | chains => [2], 13 | globals => #{<<"patchsigs">> => 2}, 14 | keys => 15 | #{ <<"node1">> => crypto:hash(sha256, <<"node1">>), 16 | <<"node2">> => crypto:hash(sha256, <<"node2">>), 17 | <<"node3">> => crypto:hash(sha256, <<"node3">>), 18 | <<"node4">> => crypto:hash(sha256, <<"node4">>) 19 | }, 20 | nodechain => #{<<"node1">> => 0, 21 | <<"node2">> => 0, 22 | <<"node3">> => 0}, 23 | <<"current">> => #{ 24 | <<"allocblock">> => 25 | #{<<"block">> => 2, <<"group">> => 10, <<"last">> => 0} 26 | } 27 | }; 28 | ({valid_timestamp, TS}) -> 29 | abs(os:system_time(millisecond)-TS)<3600000 30 | orelse 31 | abs(os:system_time(millisecond)-(TS-86400000))<3600000; 32 | ({endless, _Address, _Cur}) -> 33 | false; 34 | (Other) -> 35 | error({bad_setting, Other}) 36 | end, 37 | GetAddr=fun mledger:get/1, 38 | ParentHash=crypto:hash(sha256, <<"parent">>), 39 | 40 | Pvt1= <<194, 124, 65, 109, 233, 236, 108, 24, 50, 151, 189, 216, 23, 42, 215, 220, 24, 41 | 240, 248, 115, 150, 54, 239, 58, 218, 221, 145, 246, 158, 15, 210, 165>>, 42 | T1=#{ 43 | kind => generic, 44 | t => os:system_time(millisecond), 45 | seq => 10, 46 | from => Addr, 47 | to => <<128,1,64,0,2,0,0,2>>, 48 | ver => 2, 49 | payload => [ 50 | #{cur=><<"FTT">>, 51 | amount=>10, 52 | purpose=>transfer 53 | } 54 | ] 55 | }, 56 | T2=#{ 57 | kind => lstore, 58 | t => os:system_time(millisecond)+1, 59 | seq => 11, 60 | from => Addr, 61 | ver => 2, 62 | payload => [ ], 63 | patches => [ 64 | #{<<"t">>=><<"set">>, <<"p">>=>[<<"a">>,<<"b">>], <<"v">>=>$b}, 65 | #{<<"t">>=><<"set">>, <<"p">>=>[<<"a">>,<<"c">>], <<"v">>=>$c}, 66 | #{<<"t">>=><<"set">>, <<"p">>=>[<<"a">>,<<"array">>], <<"v">>=>[1,2,3]} 67 | ] 68 | }, 69 | TXConstructed1=tx:sign(tx:construct_tx(T1),Pvt1), 70 | TXConstructed2=tx:sign(tx:construct_tx(T2),Pvt1), 71 | {ok,TX1}=tx:verify(TXConstructed1,[nocheck_ledger]), 72 | {ok,TX2}=tx:verify(TXConstructed2,[nocheck_ledger]), 73 | #{block:=Block, 74 | failed:=Failed}=generate_block:generate_block( 75 | [{<<"tx1">>, TX1}, 76 | {<<"tx2">>, TX2} 77 | ], 78 | {1, ParentHash}, 79 | GetSettings, 80 | GetAddr, 81 | []), 82 | 83 | io:format("~p~n", [Block]), 84 | [ 85 | ?assertEqual([], Failed), 86 | ?assertMatch(#{ 87 | bals:=#{<<128, 1, 64, 0, 2, 0, 0, 1>>:=_} 88 | }, Block) 89 | ] 90 | end, 91 | Ledger=[ 92 | {<<128, 1, 64, 0, 2, 0, 0, 2>>, 93 | #{amount => #{ 94 | } 95 | } 96 | }, 97 | {Addr, 98 | #{amount => #{ 99 | <<"FTT">> => 110, 100 | <<"SK">> => 10, 101 | <<"TST">> => 26 102 | }, 103 | seq => 1, 104 | t => 1512047425350, 105 | lastblk => <<0:64>>, 106 | lstore=><<129,196,6,101,120,105,115,116,115,1>>, 107 | changes=>[amount] 108 | } 109 | }, 110 | {<<160,0,0,0,0,0,0,0>>, 111 | #{amount => #{ 112 | <<"FTT">> => 100, 113 | <<"TST">> => 100 114 | }, 115 | lastblk => <<0:64>>, 116 | changes=>[amount] 117 | } 118 | } 119 | ], 120 | mledger:deploy4test(Ledger, Test). 121 | 122 | -------------------------------------------------------------------------------- /test/mledger_test.erl: -------------------------------------------------------------------------------- 1 | -module(mledger_test). 2 | -include_lib("eunit/include/eunit.hrl"). 3 | 4 | mledger_test() -> 5 | Addr= <<128,1,64,0,2,0,0,1>>, 6 | Test=fun(_) -> 7 | Bals=#{Addr => 8 | #{amount => #{<<"FTT">> => 100,<<"SK">> => 10,<<"TST">> => 26}, 9 | state => #{ <<0>> => <<1>>, <<1>> => <<>> }, 10 | lstore=><<129,196,6,101,120,105,115,116,115,0>>, 11 | seq => 555, 12 | changes => [state, seq] 13 | } 14 | }, 15 | 16 | L0=mledger:get(Addr), 17 | 18 | {ok, LedgerHash0}=mledger:apply_patch([],check), 19 | io:format("LH0 ~p~n", [LedgerHash0]), 20 | Patch=mledger:bals2patch(maps:to_list(Bals)), 21 | {ok, LedgerHash}=mledger:apply_patch(Patch,{commit,2}), 22 | io:format("LH1 ~p~n", [LedgerHash]), 23 | {ok, LedgerHash2}=mledger:apply_patch([],check), 24 | io:format("LH2 ~p~n", [LedgerHash2]), 25 | L1=mledger:get(Addr), 26 | 27 | io:format("~p~n",[Bals]), 28 | 29 | io:format("0 ~p~n", [L0]), 30 | io:format("1 ~p~n", [L1]), 31 | %io:format("L ~p~n", [mledger:get_vers(Addr)]), 32 | [ 33 | ?assertEqual([], []) 34 | ] 35 | end, 36 | Ledger=[ 37 | {Addr, 38 | #{amount => #{ 39 | <<"FTT">> => 110, 40 | <<"SK">> => 10, 41 | <<"TST">> => 26 42 | }, 43 | seq => 1, 44 | state => #{ <<0>> => <<0>>, <<1>> => <<1>>, <<2>> => <<1>> }, 45 | t => 1512047425350, 46 | lastblk => <<0:64>>, 47 | lstore=><<129,196,6,101,120,105,115,116,115,1>> 48 | } 49 | } 50 | ], 51 | mledger:deploy4test(Ledger, Test). 52 | 53 | -------------------------------------------------------------------------------- /test/support/helpers.ex: -------------------------------------------------------------------------------- 1 | defmodule TPHelpers do 2 | 3 | def is_node_alive?(node, opts \\ []) do 4 | host = Keyword.get(opts, :node, 'pwr') 5 | case :erl_epmd.port_please('test_#{node}', host) do 6 | {:port, _, _} -> true 7 | _ -> false 8 | end 9 | end 10 | 11 | def start_node(node, opts \\ []) do 12 | case is_node_alive?(node) do 13 | true -> logger("Skiping alive node ~p", [node]) 14 | _ -> 15 | logger("Starting the node #{node}") 16 | 17 | dir = Keyword.get(opts, :dir, "./examples/test_chain4") 18 | bin_dirs_wildcard = Keyword.get(opts, :bin_dirs, "_build/test/lib/*/ebin/") 19 | 20 | bindirs = Path.wildcard(bin_dirs_wildcard) 21 | 22 | exec_args = 23 | ["-config", "#{dir}/test_#{node}.config", "-sname", "test_#{node}", "-noshell"] ++ 24 | Enum.reduce(bindirs, [], fn x, acc -> ["-pa", x | acc] end) ++ 25 | ["-detached", "+SDcpu", "2:2:", "-s", "lager", "-s", "tpnode"] 26 | 27 | System.put_env("TPNODE_RESTORE", dir) 28 | 29 | result = System.cmd("erl", exec_args) 30 | logger("result: ~p", [result]) 31 | end 32 | 33 | :ok 34 | end 35 | 36 | 37 | def stop_node(node) do 38 | case is_node_alive?(node) do 39 | false -> logger("node #{node} is already down") 40 | _ -> 41 | logger("Stopping the node #{node}") 42 | result = :rpc.call(String.to_atom("test_#{node}@pwr"), :init, :stop, []) 43 | logger("result: ~p", [result]) 44 | end 45 | end 46 | 47 | 48 | def get_perm_hash(block_info) when is_map(block_info) do 49 | block_hash = Map.get(block_info, "hash", nil) 50 | header = Map.get(block_info, "header", %{}) 51 | parent_hash = Map.get(header, "parent", nil) 52 | 53 | case Map.get(block_info, "temporary", false) do 54 | false -> block_hash 55 | _ -> parent_hash 56 | end 57 | end 58 | 59 | def logger(format, args \\ []), do: :utils.logger(to_charlist(format), args) 60 | 61 | end 62 | -------------------------------------------------------------------------------- /test/tpnode.coverspec.tpl: -------------------------------------------------------------------------------- 1 | {nodes, ['test_c4n1@knuth', 'test_c4n2@knuth', 'test_c4n3@knuth', 2 | 'test_c5n1@knuth', 'test_c5n2@knuth', 'test_c5n3@knuth']}. 3 | %{export, "../_build/test/cover/ct.coverdata"}. 4 | {import, "../_build/test/cover/eunit.coverdata"}. 5 | %{incl_app, [tpnode]}. 6 | {incl_mods, [address,address_db,apixiom,base58,beacon,bin2hex,block,blockchain, 7 | blockchain_reader,blockchain_sync,blockchain_updater,blockvote,bron_kerbosch, 8 | bsig,chainkeeper,chainsettings,contract_evm, 9 | contract_wasm,discovery,generate_block,generate_block_process, 10 | genesis,genesis_easy,genuml,hashqueue,hex,httpapi_playground, 11 | interconnect,ldb,ledger_sync,maphash,mbal,mkblock, 12 | mkblock_genblk,mledger,naddress,nodekey,rdb_dispatcher, 13 | settings,smartcontract,smartcontract2,synchronizer, 14 | topology,tpecdsa,tpic_checkauth,tpnode,tpnode_announcer, 15 | tpnode_cert,tpnode_dtx_runner,tpnode_http,tpnode_httpapi,tpnode_sup, 16 | tpnode_tpic_handler,tpnode_txstorage,tpnode_txsync,tpnode_vmproto, 17 | tpnode_vmsrv,tpnode_ws,tpnode_ws_dispatcher,tx,tx1,tx_visualizer,txlog, 18 | txpool,txqueue,txstatus,utils,vm,vm_wasm,xchain,xchain_api, 19 | xchain_client,xchain_client_handler,xchain_client_worker,xchain_dispatcher, 20 | xchain_server,xchain_server_handler]}. 21 | {level, details}. 22 | {incl_dirs_r, ["../apps"]}. 23 | %{src_dirs, tpnode, ["apps/tpnode/src"]}. 24 | %{excl_dirs_r, ["test"]}. 25 | 26 | -------------------------------------------------------------------------------- /test/txqueue_test.erl: -------------------------------------------------------------------------------- 1 | -module(txqueue_test). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | 5 | %%-export([handle_info/2]). 6 | 7 | 8 | %%handle_cast( 9 | %% {push, BatchNo, TxIds}, #{queue:=Queue, batch_state := BatchState} = State) 10 | %% when is_list(TxIds) and is_number(BatchNo) -> 11 | 12 | 13 | %init_state() -> 14 | % #{ 15 | % queue => queue:new(), 16 | % inprocess => hashqueue:new(), 17 | % batch_state => #{ 18 | % holding_storage => #{}, 19 | % current_batch => 0 20 | % } 21 | % }. 22 | %% ------------------------------------------------------------------ 23 | 24 | %get_queue(Queue, Cnt) -> 25 | % get_queue(Queue, Cnt, []). 26 | % 27 | %get_queue(Queue, 0, Acc) -> 28 | % {Queue, Acc}; 29 | % 30 | %get_queue(Queue, Cnt, Acc) -> 31 | % {Element, Queue1}=queue:out(Queue), 32 | % case Element of 33 | % {value, E1} -> 34 | % get_queue(Queue1, Cnt-1, Acc ++ [E1]); 35 | % empty -> 36 | % {Queue1, Acc} 37 | % end. 38 | 39 | %% ------------------------------------------------------------------ 40 | 41 | %%make_tx_list(TxIds) -> 42 | %% [ {TxId, null} || TxId <- TxIds]. 43 | 44 | %% ------------------------------------------------------------------ 45 | 46 | %% {push, BatchNo, TxIds}, #{queue:=Queue, batch_state := BatchState} = State) 47 | 48 | %storage_choose_test() -> 49 | % State0 = init_state(), 50 | % 51 | % %% batch 0, should choose queue as a storage for correct batch number 52 | % Tx1_0 = [1,2,3,4,5], 53 | % {noreply, #{queue := Queue1_0, batch_state := BatchState1} = State1} = 54 | % txqueue:handle_cast({push, 0, Tx1_0}, State0), 55 | % 56 | % ?assertMatch(#{holding_storage := #{}}, BatchState1), 57 | % ?assertMatch(#{current_batch := 1}, BatchState1), 58 | % 59 | % {_Queue1_1, Tx1_1} = get_queue(Queue1_0, length(Tx1_0)), 60 | % ?assertEqual(Tx1_0, Tx1_1), 61 | % 62 | % %% batch 2, should choose holding storage for batch with number from future 63 | % Tx2_0 = [ 26, 27, 28 ], 64 | % 65 | % {noreply, #{queue := Queue2_0, batch_state := BatchState2} = State2} = 66 | % txqueue:handle_cast({push, 2, Tx2_0}, State1), 67 | % 68 | % % queue should be kept untouched 69 | % ?assertEqual(Queue1_0, Queue2_0), 70 | % 71 | % % transaction ids should go to holding storage 72 | % ?assertMatch(#{holding_storage := #{ 2 := Tx2_0 }}, BatchState2), 73 | % 74 | % %% batch 0, should skip obsolete batch 75 | % Tx3_0 = [ 6, 7, 8 ], 76 | % 77 | % {noreply, #{queue := Queue3_0, batch_state := BatchState3} = State3} = 78 | % txqueue:handle_cast({push, 0, Tx3_0}, State2), 79 | % 80 | % % batch state shouldn't be changed 81 | % ?assertEqual(Queue2_0, Queue3_0), 82 | % ?assertEqual(BatchState2, BatchState3), 83 | % 84 | % %% batch 5, one more batch from the future 85 | % Tx4_0 = [ 51,52,53,54 ], 86 | % {noreply, #{queue := _Queue4_0, batch_state := _BatchState4} = State4} = 87 | % txqueue:handle_cast({push, 5, Tx4_0}, State3), 88 | % 89 | % 90 | % %% should reassemble txs from all batches when have the missing batch received 91 | % Tx5_0 = [ 11,12 ], 92 | % {noreply, #{queue := Queue5_0, batch_state := BatchState5} = State5} = 93 | % txqueue:handle_cast({push, 1, Tx5_0}, State4), 94 | % 95 | % AllTxs5 = Tx1_0 ++ Tx5_0 ++ Tx2_0, % without batch 5 96 | % {_Queue5_1, Tx5_1} = get_queue(Queue5_0, length(AllTxs5)), 97 | % ?assertEqual(AllTxs5, Tx5_1), 98 | % ?assertMatch(#{current_batch := 3}, BatchState5), 99 | % ?assertMatch(#{holding_storage := #{ 5 := Tx4_0 }}, BatchState5), 100 | % 101 | % %% should force reassemble txs if one batch is missing and we have exceeded tries count 102 | % #{queue := Queue6_0, batch_state := BatchState6} = _State6 = 103 | % lists:foldl( 104 | % fun(_TryNo, CurrentState) -> 105 | % {noreply, NewState6} = 106 | % txqueue:handle_cast({push, 100500, []}, CurrentState), 107 | % NewState6 108 | % end, 109 | % State5, 110 | % lists:seq(1, txqueue:get_max_reassembly_tries()) 111 | % ), 112 | % 113 | % AllTxs6 = Tx1_0 ++ Tx5_0 ++ Tx2_0 ++ Tx4_0, 114 | % {_Queue6_1, Tx6_1} = get_queue(Queue6_0, length(AllTxs6)), 115 | % ?assertEqual(AllTxs6, Tx6_1), 116 | % ?assertMatch(#{current_batch := 6}, BatchState6). 117 | 118 | --------------------------------------------------------------------------------