├── bin └── .gitignore ├── doc ├── .gitignore ├── grpcGray.png ├── hydroPowerPlant-stateMachine.pdf ├── hydroPowerPlant-stateMachine.png ├── hydroPowerPlant-timedAutomata.pdf ├── hydroPowerPlant-timedAutomata.png ├── hydroPowerPlant-stateMachine.dot └── hydroPowerPlant-timedAutomata.dot ├── spec ├── cpp │ ├── .bazelignore │ ├── bin │ │ ├── .gitignore │ │ └── runRPCtest.sh │ ├── test-grpc │ │ ├── .gitignore │ │ ├── mathtest.proto │ │ ├── Makefile │ │ ├── server.cc │ │ └── client.cc │ ├── tests │ │ ├── dbft2 │ │ │ ├── CMakeLists.txt │ │ │ ├── dBFT2ContextTests.cpp │ │ │ ├── dBFT2MachineTests.cpp │ │ │ └── dBFT2RPCMachineTests.cpp │ │ ├── events │ │ │ ├── CMakeLists.txt │ │ │ ├── EventTests.cpp │ │ │ └── TimedEventTests.cpp │ │ ├── machine │ │ │ ├── CMakeLists.txt │ │ │ └── MachineIdTests.cpp │ │ ├── single │ │ │ ├── CMakeLists.txt │ │ │ ├── StateTests.cpp │ │ │ ├── SingleTimerStateMachineTests.cpp │ │ │ ├── TransitionTests.cpp │ │ │ ├── ConditionTests.cpp │ │ │ └── ActionTests.cpp │ │ ├── timing │ │ │ ├── CMakeLists.txt │ │ │ ├── TimerDelayableTests.cpp │ │ │ ├── CountdownTimerTests.cpp │ │ │ ├── ClockNotifyTimerTests.cpp │ │ │ ├── ClockTests.cpp │ │ │ └── TimerTests.cpp │ │ ├── utils │ │ │ ├── CMakeLists.txt │ │ │ └── CliTests.cpp │ │ ├── replicated │ │ │ ├── CMakeLists.txt │ │ │ ├── MultiContextTests.cpp │ │ │ ├── ReplicatedSTSMTests.cpp │ │ │ └── MachineContextTests.cpp │ │ └── CMakeLists.txt │ ├── build.sh │ ├── cov.sh │ ├── include │ │ ├── BUILD.bazel │ │ └── libbft │ │ │ ├── BUILD.bazel │ │ │ ├── utils │ │ │ ├── Pointer.hpp │ │ │ ├── Shortcuts.hpp │ │ │ ├── IPrintable.hpp │ │ │ ├── CLI.h │ │ │ └── CLI.cpp │ │ │ ├── timing │ │ │ ├── TimerDelayable.hpp │ │ │ ├── CountdownNotifyTimer.hpp │ │ │ ├── Clock.hpp │ │ │ ├── CountdownTimer.hpp │ │ │ └── Timer.hpp │ │ │ ├── machine │ │ │ ├── MachineId.hpp │ │ │ └── TimedStateMachine.hpp │ │ │ ├── dbft2 │ │ │ └── dBFT2Context.hpp │ │ │ ├── replicated │ │ │ ├── MachineContext.hpp │ │ │ └── MultiContext.hpp │ │ │ ├── events │ │ │ ├── ScheduledEvent.hpp │ │ │ └── Event.hpp │ │ │ ├── single │ │ │ ├── State.hpp │ │ │ └── Transition.hpp │ │ │ └── rpc-replicated │ │ │ └── RPCMachineContext.hpp │ ├── CPPLINT.cfg │ ├── makefile │ ├── cmake │ │ ├── FindHelper.cmake │ │ ├── FindTestHelper.cmake │ │ ├── FindProtobufHelper.cmake │ │ └── FindDefinitions.cmake │ ├── src │ │ ├── p2p │ │ │ ├── bftp2p.proto │ │ │ ├── CMakeLists.txt │ │ │ ├── P2PServer.h │ │ │ ├── P2PServer.cpp │ │ │ ├── mainP2P.cpp │ │ │ └── bftp2p.grpc.pb.cc │ │ ├── README.txt │ │ ├── demo-bft-rpc │ │ │ └── BUILD.bazel │ │ ├── bftevents-grpc │ │ │ ├── CMakeLists.txt │ │ │ ├── bftevent.proto │ │ │ ├── mainBftserver.cc │ │ │ ├── mainBftclient.cc │ │ │ ├── BUILD.bazel │ │ │ ├── BFTEventsClient.hpp │ │ │ ├── BFTEventsServer.hpp │ │ │ └── bftevent.grpc.pb.cc │ │ └── CMakeLists.txt │ ├── BUILD │ ├── MODULE.bazel │ ├── WORKSPACE │ ├── .gitignore │ ├── .bazelrc │ ├── .clang-tidy │ ├── CMakeLists.txt │ └── README.md ├── csharp │ └── README ├── go │ ├── README │ ├── src │ │ ├── single │ │ │ ├── param.go │ │ │ ├── action.go │ │ │ ├── condition_test.go │ │ │ ├── condition.go │ │ │ ├── action_test.go │ │ │ ├── state.go │ │ │ └── transition.go │ │ ├── util │ │ │ └── constants.go │ │ ├── app_test │ │ │ └── main.go │ │ ├── timing │ │ │ ├── clock_test.go │ │ │ ├── clock.go │ │ │ ├── timer_delayable.go │ │ │ ├── timer_test.go │ │ │ ├── countdown_timer.go │ │ │ ├── countdown_notify_timer.go │ │ │ └── timer.go │ │ ├── machine │ │ │ ├── machine_id.go │ │ │ └── timed_state_machine.go │ │ ├── dbft2 │ │ │ └── dbft2_context.go │ │ ├── replicated │ │ │ ├── scheduled_event.go │ │ │ ├── machine_context.go │ │ │ ├── multi_context.go │ │ │ └── replicated_stsm.go │ │ ├── events │ │ │ ├── event.go │ │ │ └── timed_event.go │ │ ├── simple_example │ │ │ └── main.go │ │ ├── rpc_replicated │ │ │ └── rpc_machine_context.go │ │ └── bftevent │ │ │ └── bftevent.pb.go │ ├── Gopkg.toml │ └── Gopkg.lock └── python │ └── README ├── .gitmodules ├── .gitignore ├── .clang-format ├── script ├── install_grpc.sh ├── clone_grpc.sh ├── run_rpc_test_cpp.sh ├── install_protobuf.sh ├── install_lcov.sh ├── grpc_protobuf_install.sh └── clean_cache.sh ├── .devcontainer ├── install-cmake.sh ├── install-grpc.sh ├── devcontainer.json └── Dockerfile ├── configure.sh ├── LICENSE ├── Makefile ├── .travis.yml └── README.md /bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | !*.dot 2 | -------------------------------------------------------------------------------- /spec/cpp/.bazelignore: -------------------------------------------------------------------------------- 1 | tests/ -------------------------------------------------------------------------------- /spec/csharp/README: -------------------------------------------------------------------------------- 1 | Coming soon.. 2 | -------------------------------------------------------------------------------- /spec/go/README: -------------------------------------------------------------------------------- 1 | Go is coming soon.. 2 | -------------------------------------------------------------------------------- /spec/python/README: -------------------------------------------------------------------------------- 1 | Coming soon.. 2 | -------------------------------------------------------------------------------- /spec/cpp/bin/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | *.dot 3 | *.png 4 | -------------------------------------------------------------------------------- /spec/cpp/test-grpc/.gitignore: -------------------------------------------------------------------------------- 1 | *.pb.* 2 | client 3 | server 4 | *.o 5 | -------------------------------------------------------------------------------- /spec/go/src/single/param.go: -------------------------------------------------------------------------------- 1 | package single 2 | 3 | type Param interface{} 4 | -------------------------------------------------------------------------------- /doc/grpcGray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoResearch/libbft/HEAD/doc/grpcGray.png -------------------------------------------------------------------------------- /spec/go/src/util/constants.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | const GraphivizFormat = "graphviz" 4 | -------------------------------------------------------------------------------- /spec/cpp/tests/dbft2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(TestHelper REQUIRED) 2 | ADD_TEST_ALL_FILES(ON) 3 | -------------------------------------------------------------------------------- /spec/cpp/tests/events/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(TestHelper REQUIRED) 2 | ADD_TEST_ALL_FILES(ON) 3 | -------------------------------------------------------------------------------- /spec/cpp/tests/machine/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(TestHelper REQUIRED) 2 | ADD_TEST_ALL_FILES(ON) 3 | -------------------------------------------------------------------------------- /spec/cpp/tests/single/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(TestHelper REQUIRED) 2 | ADD_TEST_ALL_FILES(ON) 3 | -------------------------------------------------------------------------------- /spec/cpp/tests/timing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(TestHelper REQUIRED) 2 | ADD_TEST_ALL_FILES(ON) 3 | -------------------------------------------------------------------------------- /spec/cpp/tests/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(TestHelper REQUIRED) 2 | ADD_TEST_ALL_FILES(ON) 3 | -------------------------------------------------------------------------------- /spec/cpp/tests/replicated/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(TestHelper REQUIRED) 2 | ADD_TEST_ALL_FILES(ON) 3 | -------------------------------------------------------------------------------- /doc/hydroPowerPlant-stateMachine.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoResearch/libbft/HEAD/doc/hydroPowerPlant-stateMachine.pdf -------------------------------------------------------------------------------- /doc/hydroPowerPlant-stateMachine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoResearch/libbft/HEAD/doc/hydroPowerPlant-stateMachine.png -------------------------------------------------------------------------------- /doc/hydroPowerPlant-timedAutomata.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoResearch/libbft/HEAD/doc/hydroPowerPlant-timedAutomata.pdf -------------------------------------------------------------------------------- /doc/hydroPowerPlant-timedAutomata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoResearch/libbft/HEAD/doc/hydroPowerPlant-timedAutomata.png -------------------------------------------------------------------------------- /spec/go/src/app_test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("hello world") 7 | } 8 | -------------------------------------------------------------------------------- /spec/cpp/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cmake -Dtest=ON -DCOVERAGE=1 .. 3 | make VERBOSE=1 4 | GTEST_COLOR=1 ctest --extra-verbose 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "spec/cpp/tests/libgtest"] 2 | path = spec/cpp/tests/libgtest 3 | url = https://github.com/google/googletest.git 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | 4 | build/ 5 | cmake-build-debug/ 6 | 7 | libgtest/ 8 | vendor/ 9 | 10 | *.dot 11 | *.png 12 | *.o 13 | #*.pb.cc 14 | #*.pb.h 15 | 16 | bazel-* 17 | vgcore.* 18 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # clang-format -style=google -dump-config > .clang-format 3 | Language: Cpp 4 | BasedOnStyle: Google 5 | AccessModifierOffset: -1 6 | ReferenceAlignment: Left 7 | DerivePointerAlignment: false 8 | ... 9 | 10 | -------------------------------------------------------------------------------- /spec/cpp/cov.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | lcov -d . -c -o coverage.info 3 | lcov --remove coverage.info '*/cpp/tests/*' '/usr/*' --output-file coverage.info 4 | debug before upload 5 | lcov --list coverage.info 6 | genhtml coverage.info --output-directory out 7 | -------------------------------------------------------------------------------- /spec/cpp/include/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_import", "cc_library") 2 | 3 | package( 4 | default_visibility = ["//visibility:public"], 5 | ) 6 | 7 | cc_library( 8 | name = "LibBFT", 9 | deps = ["//include/libbft:libbft_hpp"], 10 | ) -------------------------------------------------------------------------------- /spec/cpp/CPPLINT.cfg: -------------------------------------------------------------------------------- 1 | #filter=-whitespace 2 | #filter=-legal/copyright 3 | filter=-runtime/references 4 | #filter=-runtime/references,-runtime/explicit 5 | filter=-readability/casting 6 | filter=-readability/todo 7 | #filter=-readability/braces,-readability/casting 8 | #filter=-build/header_guard -------------------------------------------------------------------------------- /script/install_grpc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "Checking folder ${1}grpc/" 3 | if test -f "${1}grpc/"; then 4 | echo "Installing gRPC" 5 | cd ${1}grpc 6 | sudo make install 7 | else 8 | echo "Building and installing gRPC" 9 | cd ${1}grpc 10 | make 11 | sudo make install 12 | fi 13 | -------------------------------------------------------------------------------- /spec/cpp/tests/events/EventTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "events/Event.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(EventsEvent, ToString) { 10 | Event event("event0"); 11 | 12 | EXPECT_EQ("Event [args=0] event0()", event.toString()); 13 | } 14 | -------------------------------------------------------------------------------- /spec/cpp/makefile: -------------------------------------------------------------------------------- 1 | all: build_with_clang_tidy 2 | 3 | build: 4 | bazel build //... 5 | 6 | build_with_clang_tidy: 7 | # bazel build //:app_test --config clang-tidy # --verbose_failures 8 | bazel build //... --config clang-tidy 9 | 10 | refresh_commands_json: 11 | bazel run @hedron_compile_commands//:refresh_all 12 | 13 | 14 | -------------------------------------------------------------------------------- /spec/cpp/tests/dbft2/dBFT2ContextTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "dbft2/dBFT2Context.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(dbft2dBFT2Context, InternalValue) { 10 | dBFT2Context dBft2Context(1, 2, 3, 4, 5); 11 | 12 | EXPECT_EQ(1, dBft2Context.v); 13 | } 14 | -------------------------------------------------------------------------------- /.devcontainer/install-cmake.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from: https://grpc.io/docs/languages/cpp/quickstart/ 3 | 4 | MY_INSTALL_DIR=/opt 5 | cd $MY_INSTALL_DIR 6 | wget -q -O cmake-linux.sh https://github.com/Kitware/CMake/releases/download/v3.19.6/cmake-3.19.6-Linux-x86_64.sh 7 | sh cmake-linux.sh -- --skip-license --prefix=$MY_INSTALL_DIR 8 | rm cmake-linux.sh 9 | -------------------------------------------------------------------------------- /spec/cpp/tests/replicated/MultiContextTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "replicated/MultiContext.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(ReplicatedMultiContext, ToString) { 10 | MultiContext multiContext; 11 | 12 | EXPECT_EQ(0, multiContext.vm.size()); 13 | } 14 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_import", "cc_library") 2 | 3 | package( 4 | default_visibility = ["//visibility:public"], 5 | ) 6 | 7 | cc_library( 8 | name = "libbft_hpp", 9 | hdrs = glob([ 10 | "**/*.hpp", 11 | ]), 12 | include_prefix = "libbft/", 13 | ) 14 | 15 | 16 | -------------------------------------------------------------------------------- /spec/cpp/tests/events/TimedEventTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "events/Event.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(EventsTimedEvent, ToString) { 10 | TimedEvent event(1, "event0"); 11 | 12 | EXPECT_EQ("TimedEvent event0() notexpired 1", event.toString()); 13 | } 14 | -------------------------------------------------------------------------------- /configure.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | projects_folder=spec 3 | 4 | git submodule update --remote --init --recursive 5 | git pull --recurse-submodules 6 | #sudo apt-get install valgrind 7 | 8 | rm -rf ~/go/src/github.com/NeoResearch 9 | mkdir -p ~/go/src/github.com/NeoResearch 10 | ln -s $(pwd)/${projects_folder}/go ~/go/src/github.com/NeoResearch/libbft 11 | -------------------------------------------------------------------------------- /script/clone_grpc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "Checking folder ${1}grpc/" 3 | if test -f "${1}grpc/"; then 4 | echo "gRPC folder is already here" 5 | else 6 | echo "Cloning gRPC" 7 | cd ${1} 8 | git clone https://github.com/grpc/grpc 9 | cd grpc 10 | echo "Cloning Protobuf" 11 | git submodule update --init --recursive 12 | fi 13 | -------------------------------------------------------------------------------- /spec/cpp/tests/timing/TimerDelayableTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "timing/TimerDelayable.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(TimingTimerDelayable, ToString) { 10 | TimerDelayable timerDelayable; 11 | EXPECT_EQ("Timer {name=''}", timerDelayable.toString()); 12 | } 13 | -------------------------------------------------------------------------------- /spec/cpp/tests/timing/CountdownTimerTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "timing/CountdownTimer.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(TimingCountdownTimer, ToString) { 10 | CountdownTimer countdownTimer(1); 11 | EXPECT_EQ("CountdownTimer {name=''}", countdownTimer.toString()); 12 | } 13 | -------------------------------------------------------------------------------- /spec/cpp/tests/dbft2/dBFT2MachineTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "dbft2/dBFT2Machine.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(dbft2dBFT2Machine, ToString) { 10 | dBFT2Machine d2Machine; 11 | 12 | EXPECT_EQ("Welcome to dBFT2Machine : { name = replicated_dBFT}", d2Machine.toString()); 13 | } 14 | -------------------------------------------------------------------------------- /spec/cpp/tests/machine/MachineIdTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "machine/MachineId.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(MachineMachineId, Address) { 10 | MachineId machineId(1, "192.168.1.1"s); 11 | 12 | EXPECT_EQ("192.168.1.1"s, machineId.address); 13 | EXPECT_EQ(1, machineId.id); 14 | } 15 | -------------------------------------------------------------------------------- /spec/cpp/tests/timing/ClockNotifyTimerTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "timing/CountdownNotifyTimer.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(TimingClockNotifyTimer, ToString) { 10 | CountdownNotifyTimer clockNotifyTimer(1); 11 | EXPECT_EQ("CountdownNotifyTimer {name=''}", clockNotifyTimer.toString()); 12 | } 13 | -------------------------------------------------------------------------------- /spec/cpp/tests/dbft2/dBFT2RPCMachineTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "dbft2/dBFT2RPCMachine.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(dbft2dBFT2RPCMachine, ToString) { 10 | // dBFT2RPCMachine d2rpcMachine; 11 | 12 | // EXPECT_EQ("Welcome to dBFT2Machine : { name = replicated_dBFT}", d2rpcMachine.toString()); 13 | } 14 | -------------------------------------------------------------------------------- /spec/cpp/cmake/FindHelper.cmake: -------------------------------------------------------------------------------- 1 | MACRO(SUBDIRLIST result curdir) 2 | FILE(GLOB children RELATIVE ${curdir} ${curdir}/*) 3 | SET(dirlist "") 4 | FOREACH (child ${children}) 5 | IF (IS_DIRECTORY ${curdir}/${child} AND EXISTS ${curdir}/${child}/CMakeLists.txt) 6 | LIST(APPEND dirlist ${child}) 7 | ENDIF () 8 | ENDFOREACH () 9 | SET(${result} ${dirlist}) 10 | ENDMACRO() 11 | -------------------------------------------------------------------------------- /spec/cpp/tests/single/StateTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "single/Transition.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | class MyState : public State { 10 | }; 11 | 12 | TEST(SingleState, ToString) { 13 | auto state = std::shared_ptr(new MyState()); 14 | 15 | EXPECT_EQ("state:{name='';;transitions=[]}", state->toString()); 16 | } 17 | -------------------------------------------------------------------------------- /doc/hydroPowerPlant-stateMachine.dot: -------------------------------------------------------------------------------- 1 | digraph STSM { 2 | //graph [bgcolor=lightgoldenrodyellow] 3 | //rankdir=LR; 4 | size="11" 5 | Empty [ label="", width=0, height=0, style = invis ]; 6 | node [shape = circle]; Normal; 7 | node [shape = circle]; Full; 8 | Empty -> Normal [label = ""]; 9 | Normal -> Full [ label = "level > max / open floodgates "]; 10 | Full -> Normal [ label = " level = safe / close floodgates"]; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /spec/cpp/tests/replicated/ReplicatedSTSMTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "timing/Clock.hpp" 5 | #include "replicated/ReplicatedSTSM.hpp" 6 | 7 | using namespace std; 8 | using namespace libbft; 9 | 10 | TEST(ReplicatedReplicatedSTSM, ToString) { 11 | ReplicatedSTSM stateMachine(std::unique_ptr(new Clock())); 12 | 13 | EXPECT_EQ("ReplicatedSTSM []", stateMachine.toString()); 14 | } 15 | -------------------------------------------------------------------------------- /spec/cpp/src/p2p/bftp2p.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_package = "com.github.com.neoresearch.libbft.p2p"; 4 | 5 | package p2p; 6 | 7 | message Url { 8 | string domain = 1; 9 | int32 port = 2; 10 | } 11 | 12 | // Defines the service 13 | service P2P { 14 | // Function invoked to send the request 15 | rpc register_me (Url) returns (stream Url) {} 16 | rpc update_services (stream Url) returns (stream Url) {} 17 | } 18 | -------------------------------------------------------------------------------- /spec/go/src/timing/clock_test.go: -------------------------------------------------------------------------------- 1 | package timing 2 | 3 | import ( 4 | "fmt" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestClock(t *testing.T) { 10 | name := "teste" 11 | clock := NewClock(name) 12 | assert.NotNil(t, clock) 13 | assert.Contains(t, clock.String(), name) 14 | 15 | a := 4 16 | b := func() int { 17 | return 10 18 | } 19 | c := fmt.Sprintf("a %v %T", a, b) 20 | fmt.Sprintf("a %v", c) 21 | } 22 | -------------------------------------------------------------------------------- /spec/cpp/src/README.txt: -------------------------------------------------------------------------------- 1 | 0)for app_test (no grpc): 2 | bazel build //src:app_test 3 | 4 | 0) testing only (before grpc and protobuf stuff) 5 | bazel build @com_github_grpc_grpc//:grpc++ 6 | bazel build @com_google_protobuf//:protoc_lib 7 | 8 | 1)for bftevents-grpc: 9 | bazel build //src/bftevents-grpc:cc_bftclient 10 | bazel build //src/bftevents-grpc:cc_bftserver 11 | 12 | 2)for dbft-test-rpc: 13 | bazel build //src/dbft-test-rpc:main_test_rpc 14 | 15 | -------------------------------------------------------------------------------- /spec/cpp/src/demo-bft-rpc/BUILD.bazel: -------------------------------------------------------------------------------- 1 | 2 | package(default_visibility = ["//visibility:public"]) 3 | 4 | load("@rules_cc//cc:defs.bzl", "cc_binary") 5 | 6 | 7 | cc_binary( 8 | name = "main_test_rpc", 9 | srcs = ["mainRPC.cpp"], 10 | defines = ["BAZEL_BUILD"], 11 | deps = [ 12 | "@com_github_grpc_grpc//:grpc++", 13 | "//include/libbft:libbft_hpp", 14 | "//src/bftevents-grpc:cc_bftclientserver_impl" 15 | ], 16 | ) -------------------------------------------------------------------------------- /script/run_rpc_test_cpp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #./spec/cpp/build/app_RPCtest 0 4 1 S_drop_3_1000_500_Commit1 10 & 4 | #./spec/cpp/build/app_RPCtest 1 4 1 S_drop_3_1000_500_Commit1 11 & 5 | #./spec/cpp/build/app_RPCtest 2 4 1 S_drop_3_1000_500_Commit1 12 6 | 7 | 8 | ./spec/cpp/build/app_RPCtest 0 4 1 S3_2000_500_Commit1 10 > /dev/null & 9 | ./spec/cpp/build/app_RPCtest 1 4 1 S3_2000_500_Commit1 11 > /dev/null & 10 | ./spec/cpp/build/app_RPCtest 2 4 1 S3_2000_500_Commit1 12 11 | -------------------------------------------------------------------------------- /spec/cpp/tests/single/SingleTimerStateMachineTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "single/SingleTimerStateMachine.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(SingleSingleTimerStateMachine, ToString) { 10 | auto machine = std::shared_ptr>(new SingleTimerStateMachine()); 11 | 12 | EXPECT_EQ("STSM {#id = 0;Timer='Timer {name=''}';States=[]}", machine->toString()); 13 | } 14 | -------------------------------------------------------------------------------- /script/install_protobuf.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "Checking folder ${1}grpc/" 3 | if test -f "${1}grpc/"; then 4 | echo "Installing Protobuf" 5 | cd ${1}grpc/third_party/protobuf 6 | sudo make install 7 | sudo ldconfig 8 | else 9 | echo "Building and installing Protobuf" 10 | cd ${1}/grpc/third_party/protobuf 11 | git submodule update --init --recursive 12 | sh autogen.sh 13 | sh configure --prefix=/usr 14 | make 15 | sudo make install 16 | sudo ldconfig 17 | fi 18 | -------------------------------------------------------------------------------- /spec/cpp/test-grpc/mathtest.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_package = "ex.grpc"; 4 | 5 | package mathtest; 6 | 7 | // Defines the service 8 | service MathTest { 9 | // Function invoked to send the request 10 | rpc sendRequest (MathRequest) returns (MathReply) {} 11 | } 12 | 13 | // The request message containing requested numbers 14 | message MathRequest { 15 | int32 a = 1; 16 | int32 b = 2; 17 | } 18 | 19 | // The response message containing response 20 | message MathReply { 21 | int32 result = 1; 22 | } -------------------------------------------------------------------------------- /spec/cpp/include/libbft/utils/Pointer.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_UTILS_POINTER_HPP_ 6 | #define INCLUDE_LIBBFT_UTILS_POINTER_HPP_ 7 | 8 | #include 9 | 10 | namespace libbft { 11 | 12 | template 13 | std::unique_ptr clonePtr(const T& obj) { 14 | return std::unique_ptr(new T(obj)); 15 | } 16 | 17 | } // namespace libbft 18 | 19 | #endif // INCLUDE_LIBBFT_UTILS_POINTER_HPP_ 20 | -------------------------------------------------------------------------------- /spec/cpp/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(libgtest) 2 | 3 | find_package(Helper REQUIRED) 4 | 5 | include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) 6 | include_directories(${CMAKE_SOURCE_DIR}/src) 7 | 8 | SUBDIRLIST(SUBDIRS ${CMAKE_CURRENT_SOURCE_DIR}) 9 | 10 | FOREACH (subdir ${SUBDIRS}) 11 | if (subdir STREQUAL "libgtest") 12 | message(STATUS "This is the ${subdir}") 13 | else () 14 | message(STATUS "Adding ${subdir}") 15 | add_subdirectory(${subdir}) 16 | endif () 17 | ENDFOREACH () 18 | -------------------------------------------------------------------------------- /script/install_lcov.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "Checking folder ${1}lcov-1.14/" 3 | if test -f "${1}lcov-1.14/"; then 4 | echo "Installing lcov" 5 | cd ${1} 6 | sudo make -C lcov-1.14/ install 7 | gem install coveralls-lcov 8 | else 9 | echo "Building and installing lcov" 10 | cd ${1} 11 | wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.14.orig.tar.gz 12 | tar xf lcov_1.14.orig.tar.gz 13 | sudo make -C lcov-1.14/ install 14 | gem install coveralls-lcov 15 | rm lcov_1.14.orig.tar.gz 16 | fi 17 | -------------------------------------------------------------------------------- /spec/cpp/tests/single/TransitionTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "single/Transition.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | class MyState : public State { 10 | }; 11 | 12 | TEST(SingleTransition, ToString) { 13 | auto transition = std::shared_ptr>(new Transition( 14 | std::shared_ptr(new MyState()), ""s)); 15 | 16 | EXPECT_EQ("t() => {name = '',to='state:{name='';;...}',conditions=[], actions=[], '}", transition->toString()); 17 | } 18 | -------------------------------------------------------------------------------- /doc/hydroPowerPlant-timedAutomata.dot: -------------------------------------------------------------------------------- 1 | digraph STSM { 2 | //graph [bgcolor=lightgoldenrodyellow] 3 | //rankdir=LR; 4 | size="11" 5 | Empty [ label="", width=0, height=0, style = invis ]; 6 | node [shape = circle]; Normal; 7 | node [shape = circle]; Full; 8 | node [shape = circle]; Draining; 9 | Empty -> Normal [label = ""]; 10 | Normal -> Full [ label = "level > max / C := 0 "]; 11 | Full -> Draining [ label = "C >= tMax / open floodgates"]; 12 | Full -> Normal [ label = " level < max"]; 13 | Draining -> Normal [ label = " level = safe / close floodgates"]; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /spec/cpp/BUILD: -------------------------------------------------------------------------------- 1 | 2 | package(default_visibility = ["//visibility:public"]) 3 | 4 | load("@rules_cc//cc:defs.bzl", "cc_binary") 5 | 6 | # SETUP CLANG TIDY 7 | filegroup( 8 | name = "clang_tidy_config", 9 | srcs = [".clang-tidy"], 10 | visibility = ["//visibility:public"], 11 | ) 12 | 13 | #################### 14 | # binary: app_test 15 | #################### 16 | 17 | cc_binary( 18 | name = "app_test", 19 | srcs = ["src/demo/main.cpp"], 20 | defines = ["BAZEL_BUILD"], 21 | deps = [ 22 | "//include:LibBFT", 23 | ], 24 | ) 25 | -------------------------------------------------------------------------------- /spec/cpp/MODULE.bazel: -------------------------------------------------------------------------------- 1 | bazel_dep(name = "hedron_compile_commands", dev_dependency = True) 2 | git_override( 3 | module_name = "hedron_compile_commands", 4 | remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", 5 | commit = "daae6f40adfa5fdb7c89684cbe4d88b691c63b2d", 6 | ) 7 | # bazel run @hedron_compile_commands//:refresh_all 8 | 9 | # From BCR: https://registry.bazel.build/modules/grpc/1.48.1.bcr.3 10 | # Example from: https://github.com/rendezqueue/rendezqueue 11 | bazel_dep(name = "grpc", version = "1.48.1.bcr.3", repo_name = "com_github_grpc_grpc") 12 | -------------------------------------------------------------------------------- /spec/cpp/WORKSPACE: -------------------------------------------------------------------------------- 1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 2 | 3 | # CLANG-TIDY-INTEGRATION (experimental) - https://github.com/erenon/bazel_clang_tidy 4 | 5 | http_archive( 6 | name = "bazel_clang_tidy", 7 | sha256 = "2c2d7e290d78c0020c60f07cec6e3820cce64c285153de8e96637027ee5e3b8e", 8 | strip_prefix = "bazel_clang_tidy-31d62bf825a94468b3d35c5ffd4e014e1c0ff566", 9 | url = "https://github.com/erenon/bazel_clang_tidy/archive/31d62bf825a94468b3d35c5ffd4e014e1c0ff566.tar.gz", 10 | ) 11 | # BUILD WITH clang-tidy: bazel build //... --config clang-tidy 12 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/timing/TimerDelayable.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_TIMING_TIMERDELAYABLE_HPP_ 6 | #define INCLUDE_LIBBFT_TIMING_TIMERDELAYABLE_HPP_ 7 | 8 | // standard Timer for a TSM 9 | #include 10 | 11 | // This Timer allows for non-negative delays 12 | 13 | namespace libbft { 14 | 15 | class TimerDelayable : public Timer {}; 16 | 17 | } // namespace libbft 18 | 19 | #endif // INCLUDE_LIBBFT_TIMING_TIMERDELAYABLE_HPP_ 20 | -------------------------------------------------------------------------------- /spec/go/src/machine/machine_id.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | type MachineId interface { 4 | // get/set 5 | GetId() int 6 | GetAddress() string 7 | } 8 | 9 | type MachineIdService struct { 10 | id int 11 | address string 12 | } 13 | 14 | func NewMachineId(id int, address string) MachineId { 15 | return &MachineIdService{ 16 | id: id, 17 | address: address, 18 | } 19 | } 20 | 21 | func NewDefaultMachineId() MachineId { 22 | return NewMachineId(0, "") 23 | } 24 | 25 | func (m *MachineIdService) GetId() int { 26 | return m.id 27 | } 28 | 29 | func (m *MachineIdService) GetAddress() string { 30 | return m.address 31 | } 32 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/utils/Shortcuts.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_UTILS_SHORTCUTS_HPP_ 6 | #define INCLUDE_LIBBFT_UTILS_SHORTCUTS_HPP_ 7 | 8 | #include 9 | #include 10 | 11 | namespace libbft { 12 | // 13 | template 14 | using sptr = std::shared_ptr; 15 | // 16 | template 17 | using uptr = std::unique_ptr; 18 | // 19 | template 20 | using op = std::optional; 21 | // 22 | } // namespace libbft 23 | 24 | #endif // INCLUDE_LIBBFT_UTILS_SHORTCUTS_HPP_ 25 | -------------------------------------------------------------------------------- /spec/cpp/src/bftevents-grpc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(ProtobufHelper REQUIRED) 2 | 3 | file(GLOB DATAMODEL_PROTOS bftevent.proto) 4 | 5 | foreach(proto ${DATAMODEL_PROTOS}) 6 | GENERATE_PROTO(${proto}) 7 | endforeach(proto) 8 | 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L/usr/local/lib -L/opt/lib") 10 | add_executable( 11 | bftclient 12 | mainBftclient.cc 13 | bftevent.pb.cc 14 | bftevent.grpc.pb.cc 15 | ) 16 | 17 | target_link_libraries_proto(bftclient) 18 | 19 | add_executable( 20 | bftserver 21 | mainBftserver.cc 22 | bftevent.pb.cc 23 | bftevent.grpc.pb.cc 24 | ) 25 | 26 | target_link_libraries_proto(bftserver) 27 | -------------------------------------------------------------------------------- /spec/cpp/tests/replicated/MachineContextTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "single/SingleTimerStateMachine.hpp" 5 | #include "replicated/MultiContext.hpp" 6 | #include "replicated/MachineContext.hpp" 7 | 8 | using namespace std; 9 | using namespace libbft; 10 | 11 | TEST(ReplicatedMachineContext, ToString) { 12 | MachineContext machineContext( 13 | std::shared_ptr(new int{1}), 14 | std::shared_ptr>>(new SingleTimerStateMachine>())); 15 | 16 | EXPECT_EQ("STSM {#id = 0;Timer='Timer {name=''}';States=[]}", machineContext.machine->toString()); 17 | } 18 | -------------------------------------------------------------------------------- /spec/cpp/tests/timing/ClockTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "timing/Clock.hpp" 5 | 6 | using namespace std; 7 | using namespace libbft; 8 | 9 | TEST(TimingClock, ToString) { 10 | unique_ptr clock(new Clock("T")); 11 | EXPECT_EQ("Clock {name='T'}", clock->toString()); 12 | } 13 | 14 | TEST(TimingClock, GetTime) { 15 | unique_ptr clock(new Clock("T")); 16 | 17 | auto actualTime = clock->getTime(); 18 | EXPECT_LT(0, actualTime); 19 | 20 | this_thread::sleep_for(chrono::milliseconds(200)); 21 | EXPECT_LT(0.19, clock->getTime() - actualTime); 22 | EXPECT_GT(0.8, clock->getTime() - actualTime); 23 | } 24 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/utils/IPrintable.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_UTILS_IPRINTABLE_HPP_ 6 | #define INCLUDE_LIBBFT_UTILS_IPRINTABLE_HPP_ 7 | 8 | #include 9 | 10 | namespace libbft { 11 | 12 | enum class StringFormat { Default, Graphviz }; 13 | 14 | class IFPrintable { 15 | public: 16 | virtual std::string toString() { 17 | return toStringFormat(StringFormat::Default); 18 | } 19 | virtual std::string toStringFormat(StringFormat format) = 0; 20 | virtual ~IFPrintable() = default; 21 | }; 22 | 23 | } // namespace libbft 24 | 25 | #endif // INCLUDE_LIBBFT_UTILS_IPRINTABLE_HPP_ 26 | -------------------------------------------------------------------------------- /spec/cpp/src/p2p/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(ProtobufHelper REQUIRED) 2 | 3 | file(GLOB DATAMODEL_PROTOS bftp2p.proto) 4 | 5 | foreach(proto ${DATAMODEL_PROTOS}) 6 | GENERATE_PROTO(${proto}) 7 | endforeach(proto) 8 | 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L/usr/local/lib -L/opt/lib") 10 | add_executable( 11 | p2p 12 | mainP2P.cpp 13 | bftp2p.pb.cc 14 | bftp2p.grpc.pb.cc 15 | P2PServer.cpp 16 | ../../include/libbft/utils/CLI.cpp 17 | ) 18 | 19 | target_link_libraries_proto(p2p) 20 | 21 | set(lib_name "${PROJECT_NAME}_${PROJECT_VERSION}") 22 | message(STATUS "p2p - ${lib_name}") 23 | 24 | # TODO Check why it does not link the CLI.cpp 25 | target_link_libraries( 26 | ${name} 27 | ${lib_name} 28 | ) 29 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/machine/MachineId.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_MACHINE_MACHINEID_HPP_ 6 | #define INCLUDE_LIBBFT_MACHINE_MACHINEID_HPP_ 7 | 8 | // default clock 9 | // #include "timing/Clock.hpp" 10 | 11 | #include 12 | #include 13 | 14 | namespace libbft { 15 | 16 | struct MachineId { 17 | explicit MachineId(int _id = 0, std::string _address = "") 18 | : id(_id), address(std::move(_address)) {} 19 | 20 | int id; 21 | std::string address; 22 | 23 | // what else we need here? 24 | }; 25 | 26 | } // namespace libbft 27 | 28 | #endif // INCLUDE_LIBBFT_MACHINE_MACHINEID_HPP_ 29 | -------------------------------------------------------------------------------- /spec/cpp/src/bftevents-grpc/bftevent.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_package = "com.github.com.neoresearch.libbft.bftevent"; 4 | 5 | package bftevent; 6 | 7 | // Defines the service 8 | service BFTEvent { 9 | // Function invoked to send the request 10 | rpc informEvent (EventInform) returns (EventReply) {} 11 | } 12 | 13 | // The request message containing requested numbers 14 | message EventInform { 15 | int32 from = 1; // right now, untrusted from (TODO: pass crypto key here) 16 | string event = 2; 17 | repeated string event_args = 3; 18 | int32 delay = 4; // optional (add delay in ms to message). for testing purposes only 19 | } 20 | 21 | // The response message containing response 22 | message EventReply { 23 | int32 gotit = 1; 24 | } -------------------------------------------------------------------------------- /spec/go/src/single/action.go: -------------------------------------------------------------------------------- 1 | package single 2 | 3 | import "github.com/NeoResearch/libbft/src/timing" 4 | 5 | type TimedAction func(timing.Timer, Param, int) 6 | 7 | type Action interface { 8 | // get / set 9 | GetName() string 10 | GetTimedAction() TimedAction 11 | 12 | String() string 13 | } 14 | 15 | type ActionService struct { 16 | name string 17 | timedAction TimedAction 18 | } 19 | 20 | func NewAction(name string, action TimedAction) Action { 21 | return &ActionService{ 22 | name, 23 | action, 24 | } 25 | } 26 | 27 | func (a *ActionService) String() string { 28 | return a.name 29 | } 30 | 31 | func (a *ActionService) GetTimedAction() TimedAction { 32 | return a.timedAction 33 | } 34 | 35 | func (a *ActionService) GetName() string { 36 | return a.name 37 | } -------------------------------------------------------------------------------- /spec/go/Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [prune] 29 | go-tests = true 30 | unused-packages = true 31 | -------------------------------------------------------------------------------- /.devcontainer/install-grpc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from: https://grpc.io/docs/languages/cpp/quickstart/ 3 | 4 | # ================================================= 5 | # THIS IS NOT NECESSARY FOR BAZEL... ONLY FOR CMAKE 6 | # WARNING: IT WILL TAKE SOME MINUTES TO BUILD! 7 | # ================================================= 8 | # 9 | echo "THIS WILL TAKE LONG TIME..." 10 | # downloading 11 | MY_INSTALL_DIR=/opt 12 | cd $MY_INSTALL_DIR 13 | git clone --recurse-submodules -b v1.50.1 --depth 1 --shallow-submodules https://github.com/grpc/grpc 14 | # building 15 | cd grpc 16 | mkdir -p cmake/build 17 | pushd cmake/build 18 | /opt/bin/cmake -DgRPC_INSTALL=ON \ 19 | -DgRPC_BUILD_TESTS=OFF \ 20 | -DCMAKE_CXX_STANDARD=17 \ 21 | -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \ 22 | ../.. 23 | make -j 4 24 | make install 25 | popd -------------------------------------------------------------------------------- /spec/cpp/test-grpc/Makefile: -------------------------------------------------------------------------------- 1 | LDFLAGS = -L/usr/local/lib -L/opt/lib `pkg-config --libs protobuf grpc++`\ 2 | -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed\ 3 | -ldl 4 | 5 | CXX = g++ 6 | CPPFLAGS1 += `pkg-config --cflags protobuf grpc` 7 | CXXFLAGS1 += -std=c++11 8 | 9 | GRPC_CPP_PLUGIN = grpc_cpp_plugin 10 | GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)` 11 | 12 | all: client server 13 | 14 | client: mathtest.pb.o mathtest.grpc.pb.o client.o 15 | $(CXX) $^ $(LDFLAGS) -o $@ 16 | 17 | server: mathtest.pb.o mathtest.grpc.pb.o server.o 18 | $(CXX) $^ $(LDFLAGS) -o $@ 19 | 20 | %.grpc.pb.cc: %.proto 21 | protoc --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $< 22 | 23 | %.pb.cc: %.proto 24 | protoc --cpp_out=. $< 25 | 26 | clean: 27 | rm -f *.o *.pb.cc *.pb.h client server 28 | -------------------------------------------------------------------------------- /spec/go/src/timing/clock.go: -------------------------------------------------------------------------------- 1 | package timing 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type Clock interface { 9 | // get / set 10 | GetTime() time.Time 11 | // methods 12 | String() string 13 | } 14 | 15 | type ClockService struct { 16 | name string 17 | } 18 | 19 | func NewDefaultClock() Clock { 20 | return NewClock("") 21 | } 22 | 23 | func NewClock(name string) Clock { 24 | return &ClockService{ 25 | name, 26 | } 27 | } 28 | 29 | func (c *ClockService) String() string { 30 | return fmt.Sprintf("Clock {name='%v'}", c.name) 31 | } 32 | 33 | func (c *ClockService) GetTime() time.Time { 34 | return time.Now() 35 | } 36 | 37 | func Since(clock Clock) float64 { 38 | return SinceTime(clock.GetTime()) 39 | } 40 | 41 | func SinceTime(clock time.Time) float64 { 42 | return float64(time.Since(clock)) / 1e9 43 | } 44 | -------------------------------------------------------------------------------- /spec/go/src/single/condition_test.go: -------------------------------------------------------------------------------- 1 | package single 2 | 3 | import ( 4 | "github.com/NeoResearch/libbft/src/timing" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestCondition(t *testing.T) { 10 | f := func(t timing.Timer, param Param, me int) (bool, error) { 11 | return me%2 == 0, nil 12 | } 13 | condition := NewCondition("teste", f) 14 | assert.NotNil(t, condition) 15 | timer := timing.NewDefaultTimer() 16 | 17 | rfv, rfe := f(timer, "", 1) 18 | assert.Nil(t, rfe) 19 | rcv, rce := condition.GetTimedFunction()(timer, "", 1) 20 | assert.Nil(t, rce) 21 | assert.Equal(t, rfv, rcv) 22 | 23 | for i := 0; i < 10; i++ { 24 | rfv, rfe = f(timer, "", i) 25 | assert.Nil(t, rfe) 26 | rcv, rce = condition.GetTimedFunction()(timer, "", i) 27 | assert.Nil(t, rce) 28 | assert.Equal(t, rfv, rcv) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spec/go/src/single/condition.go: -------------------------------------------------------------------------------- 1 | package single 2 | 3 | import ( 4 | "github.com/NeoResearch/libbft/src/timing" 5 | ) 6 | 7 | type TimedFunction func(timing.Timer, Param, int) (bool, error) 8 | 9 | type Condition interface { 10 | // get / set 11 | GetName() string 12 | GetTimedFunction() TimedFunction 13 | // methods 14 | String() string 15 | } 16 | 17 | type ConditionService struct { 18 | name string 19 | timedFunction TimedFunction 20 | } 21 | 22 | func NewCondition(name string, timedFunction TimedFunction) Condition { 23 | return &ConditionService{ 24 | name, 25 | timedFunction, 26 | } 27 | } 28 | 29 | func (c *ConditionService) String() string { 30 | return c.name 31 | } 32 | 33 | func (c *ConditionService) GetTimedFunction() TimedFunction { 34 | return c.timedFunction 35 | } 36 | 37 | func (c *ConditionService) GetName() string { 38 | return c.name 39 | } 40 | -------------------------------------------------------------------------------- /spec/go/src/dbft2/dbft2_context.go: -------------------------------------------------------------------------------- 1 | package dbft2 2 | 3 | type DBFT2Context interface { 4 | GetView() int 5 | GetHeight() int 6 | GetBlockTime() int 7 | GetNumberOfNodes() int 8 | } 9 | 10 | type DBFT2ContextService struct { 11 | view int 12 | height int 13 | blockTime int 14 | numberOfNodes int 15 | } 16 | 17 | func NewDBFT2Context(view int, height int, blockTime int, numberOfNodes int) DBFT2Context { 18 | return &DBFT2ContextService{ 19 | view, 20 | height, 21 | blockTime, 22 | numberOfNodes, 23 | } 24 | } 25 | 26 | func (d *DBFT2ContextService) GetView() int { 27 | return d.view 28 | } 29 | 30 | func (d *DBFT2ContextService) GetHeight() int { 31 | return d.height 32 | } 33 | 34 | func (d *DBFT2ContextService) GetBlockTime() int { 35 | return d.blockTime 36 | } 37 | 38 | func (d *DBFT2ContextService) GetNumberOfNodes() int { 39 | return d.GetNumberOfNodes() 40 | } 41 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/dbft2/dBFT2Context.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_DBFT2_DBFT2CONTEXT_HPP_ 6 | #define INCLUDE_LIBBFT_DBFT2_DBFT2CONTEXT_HPP_ 7 | 8 | // system includes 9 | // ... 10 | 11 | namespace libbft { 12 | 13 | struct dBFT2Context { 14 | /** view number */ 15 | int v; 16 | /** blockchain height */ 17 | int H; 18 | /** block time (in seconds) */ 19 | int T; 20 | /** number of nodes/replicas */ 21 | int R; 22 | /** number of allowed faulty nodes */ 23 | int f; 24 | /** M limit: 2f+1 */ 25 | int M() const { return 2 * f + 1; } 26 | 27 | // add extra ConsensusContext information here 28 | 29 | dBFT2Context(int _v, int _h, int _t, int _r, int _f) 30 | : v(_v), H(_h), T(_t), R(_r), f(_f) {} 31 | }; 32 | 33 | } // namespace libbft 34 | 35 | #endif // INCLUDE_LIBBFT_DBFT2_DBFT2CONTEXT_HPP_ 36 | -------------------------------------------------------------------------------- /spec/go/src/replicated/scheduled_event.go: -------------------------------------------------------------------------------- 1 | package replicated 2 | 3 | type ScheduledEvent interface { 4 | // get / set 5 | GetName() string 6 | GetEventParams() []string 7 | GetCountdown() float64 8 | GetMachine() int 9 | } 10 | 11 | type ScheduledEventService struct { 12 | name string 13 | eventParams []string 14 | countdown float64 15 | machine int 16 | } 17 | 18 | func NewScheduledEvent(name string, eventParams []string, countdown float64, machine int) ScheduledEvent { 19 | return &ScheduledEventService{ 20 | name, 21 | eventParams, 22 | countdown, 23 | machine, 24 | } 25 | } 26 | 27 | func (s *ScheduledEventService) GetName() string { 28 | return s.name 29 | } 30 | 31 | func (s *ScheduledEventService) GetEventParams() []string { 32 | return s.eventParams 33 | } 34 | 35 | func (s *ScheduledEventService) GetCountdown() float64 { 36 | return s.countdown 37 | } 38 | 39 | func (s *ScheduledEventService) GetMachine() int { 40 | return s.machine 41 | } 42 | -------------------------------------------------------------------------------- /script/grpc_protobuf_install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd ../../ 4 | git submodule update --init --recursive # get submodules for libbft (including grpc) 5 | cd spec/cpp/grpc/third_party/protobuf # install protobuf first 6 | git submodule update --init --recursive # get submodules (for protobuf) 7 | ./autogen.sh # generate installation files 8 | ./configure 9 | make 10 | make check # if this fails, something bad happened 11 | sudo make install # this will install system-wide protobuf and protoc (compiler) 12 | sudo ldconfig # refresh shared library cache 13 | cd ../.. # back to grpc 14 | make 15 | sudo make install # install grpc (and also /usr/local/bin/grpc_cpp_plugin) 16 | 17 | # Testing outputs 18 | protoc --version #libprotoc 3.8.0 19 | ls -la /usr/local/lib/libproto* #/usr/local/lib/libprotobuf.so -> libprotobuf.so.19.0.0 20 | -------------------------------------------------------------------------------- /spec/cpp/.gitignore: -------------------------------------------------------------------------------- 1 | ### Automatically added by Hedron's Bazel Compile Commands Extractor: https://github.com/hedronvision/bazel-compile-commands-extractor 2 | # Ignore the `external` link (that is added by `bazel-compile-commands-extractor`). The link differs between macOS/Linux and Windows, so it shouldn't be checked in. The pattern must not end with a trailing `/` because it's a symlink on macOS/Linux. 3 | /external 4 | # Ignore links to Bazel's output. The pattern needs the `*` because people can change the name of the directory into which your repository is cloned (changing the `bazel-` symlink), and must not end with a trailing `/` because it's a symlink on macOS/Linux. 5 | /bazel-* 6 | # Ignore generated output. Although valuable (after all, the primary purpose of `bazel-compile-commands-extractor` is to produce `compile_commands.json`!), it should not be checked in. 7 | /compile_commands.json 8 | # Ignore the directory in which `clangd` stores its local index. 9 | /.cache/ 10 | MODULE.bazel.lock 11 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "C++", 3 | "build": { 4 | "dockerfile": "Dockerfile" 5 | }, 6 | // Features to add to the dev container. More info: https://containers.dev/features. 7 | // "features": {}, 8 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 9 | // "forwardPorts": [], 10 | // Use 'postCreateCommand' to run commands after the container is created. 11 | // "postCreateCommand": "gcc -v", 12 | // Configure tool-specific properties. 13 | // "customizations": {}, 14 | "extensions": [ 15 | "mine.cpplint", 16 | "DevonDCarew.bazel-code", 17 | "llvm-vs-code-extensions.vscode-clangd" 18 | ], 19 | "remoteEnv": { 20 | "PATH": "${containerEnv:PATH}:/opt/bin" 21 | }, 22 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 23 | "remoteUser": "root", 24 | //"runArgs": ["--userns=keep-id"], 25 | "postCreateCommand": "bazel --version && test -e .clang-format && cp .clang-format ~/.cache" 26 | //"containerUser": "vscode", 27 | } -------------------------------------------------------------------------------- /spec/cpp/src/bftevents-grpc/mainBftserver.cc: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #include // c++20 6 | #include 7 | // 8 | #include 9 | 10 | using namespace std; // NOLINT 11 | using namespace libbft; // NOLINT 12 | 13 | int main(int argc, char** argv) { 14 | // error: do not use pointer arithmetic 15 | // [cppcoreguidelines-pro-bounds-pointer-arithmetic,-warnings-as-errors] 16 | // https://stackoverflow.com/questions/45718389/how-to-avoid-pointer-arithmetic-when-using-char-argv 17 | auto args = std::span(argv, static_cast(argc)); 18 | // 19 | int me = 0; 20 | if (argc >= 2) { 21 | // std::string s(argv[1]); // get value 22 | std::string s(args[1]); // get value 23 | me = stoi(s); 24 | } 25 | 26 | std::cout << "I am # " << me << std::endl; 27 | 28 | BFTEventsServer<> server(me); 29 | server.RunForever(); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /spec/cpp/tests/utils/CliTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "utils/CLI.h" 6 | 7 | using namespace std; 8 | using namespace libbft; 9 | 10 | TEST(Utils, Parse) { 11 | auto argv = std::vector{ 12 | "--oi", 13 | "2", 14 | "-a", 15 | "--dado", 16 | "12" 17 | }; 18 | ArgumentParser parser(argv); 19 | auto oi = Argument('o', true, "oi"); 20 | auto a = Argument('a'); 21 | auto b = Argument('b'); 22 | auto c = Argument('c', true, "casa", "casinha"); 23 | auto d = Argument('d', true, "dado", "123"); 24 | parser.addArguments(std::vector{oi, a, b, c, d}); 25 | parser.parse(); 26 | 27 | EXPECT_TRUE(parser.isPresent(oi)); 28 | EXPECT_EQ("2", parser.getValue(oi)); 29 | EXPECT_TRUE(parser.isPresent(a)); 30 | EXPECT_FALSE(parser.isPresent(b)); 31 | 32 | EXPECT_TRUE(parser.isPresent(c)); 33 | EXPECT_EQ("casinha", parser.getValue(c)); 34 | 35 | EXPECT_TRUE(parser.isPresent(d)); 36 | EXPECT_EQ("12", parser.getValue(d)); 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 NeoResearch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /spec/cpp/.bazelrc: -------------------------------------------------------------------------------- 1 | # requires >= c++14 2 | build --cxxopt='-std=c++20' 3 | build --cxxopt='-Wno-macro-redefined' 4 | build --cxxopt='-Wno-builtin-macro-redefined' 5 | #### https://github.com/llvm/llvm-project/issues/56709 6 | #### https://github.com/erenon/bazel_clang_tidy/issues/29 7 | # 8 | # build --cxxopt='-Wfatal-errors' 9 | build --client_env=CC=clang 10 | build --copt=-DGRPC_BAZEL_BUILD 11 | build --action_env=GRPC_BAZEL_RUNTIME=1 12 | build --define=use_fast_cpp_protos=true 13 | 14 | build:opt --compilation_mode=opt 15 | build:opt --copt=-Wframe-larger-than=16384 16 | 17 | build:dbg --compilation_mode=dbg 18 | build:dbg --copt=-Werror=return-stack-address 19 | 20 | ####### 21 | # FROM: https://github.com/erenon/bazel_clang_tidy 22 | 23 | # Required for bazel_clang_tidy to operate as expected 24 | build:clang-tidy --aspects @bazel_clang_tidy//clang_tidy:clang_tidy.bzl%clang_tidy_aspect 25 | build:clang-tidy --output_groups=report 26 | 27 | # Optionally override the .clang-tidy config file target 28 | build:clang-tidy --@bazel_clang_tidy//:clang_tidy_config=//:clang_tidy_config 29 | 30 | # THEN, just do it: bazel build //... --config clang-tidy -------------------------------------------------------------------------------- /spec/go/src/single/action_test.go: -------------------------------------------------------------------------------- 1 | package single 2 | 3 | import ( 4 | "github.com/NeoResearch/libbft/src/timing" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestAction(t *testing.T) { 10 | value := -1 11 | f := func(t timing.Timer, param Param, me int) { 12 | value = me 13 | } 14 | action := NewAction("teste", f) 15 | assert.NotNil(t, action) 16 | timer := timing.NewDefaultTimer() 17 | 18 | f(timer, "", 1) 19 | assert.Equal(t, 1, value) 20 | value = -1 21 | action.GetTimedAction()(timer, "", 1) 22 | assert.Equal(t, 1, value) 23 | 24 | for i := 0; i < 10; i++ { 25 | value = -1 26 | f(timer, "", i) 27 | assert.Equal(t, i, value) 28 | 29 | value = -1 30 | action.GetTimedAction()(timer, "", i) 31 | assert.Equal(t, i, value) 32 | } 33 | } 34 | 35 | type actionReference struct { 36 | age int 37 | } 38 | 39 | func TestActionReference(t *testing.T) { 40 | obj := actionReference{1} 41 | f := func(t timing.Timer, param Param, me int) { 42 | param.(*actionReference).age += 1 43 | } 44 | 45 | timer := timing.NewDefaultTimer() 46 | f(timer, &obj, 1) 47 | assert.Equal(t, 2, obj.age) 48 | } 49 | -------------------------------------------------------------------------------- /spec/go/Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" 6 | name = "github.com/davecgh/go-spew" 7 | packages = ["spew"] 8 | pruneopts = "UT" 9 | revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" 10 | version = "v1.1.1" 11 | 12 | [[projects]] 13 | digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" 14 | name = "github.com/pmezard/go-difflib" 15 | packages = ["difflib"] 16 | pruneopts = "UT" 17 | revision = "792786c7400a136282c1664665ae0a8db921c6c2" 18 | version = "v1.0.0" 19 | 20 | [[projects]] 21 | digest = "1:972c2427413d41a1e06ca4897e8528e5a1622894050e2f527b38ddf0f343f759" 22 | name = "github.com/stretchr/testify" 23 | packages = ["assert"] 24 | pruneopts = "UT" 25 | revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" 26 | version = "v1.3.0" 27 | 28 | [solve-meta] 29 | analyzer-name = "dep" 30 | analyzer-version = 1 31 | input-imports = ["github.com/stretchr/testify/assert"] 32 | solver-name = "gps-cdcl" 33 | solver-version = 1 34 | -------------------------------------------------------------------------------- /spec/cpp/src/p2p/P2PServer.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBBFT_P2P_P2P_SERVER 2 | #define LIBBFT_P2P_P2P_SERVER 3 | 4 | #include 5 | #include 6 | 7 | #include "bftp2p.pb.h" 8 | #include "bftp2p.grpc.pb.h" 9 | 10 | inline std::string urlToString(const ::p2p::Url *url) { 11 | return url->domain() + ":" + std::to_string(url->port()); 12 | } 13 | 14 | inline ::p2p::Url stringToUrl(const std::string& server) { 15 | p2p::Url resp; 16 | auto pos = server.find(':'); 17 | resp.set_domain(server.substr(0, pos)); 18 | resp.set_port(std::stoi(server.substr(pos + 1))); 19 | return resp; 20 | } 21 | 22 | class P2PServiceImpl final : public p2p::P2P::Service { 23 | private: 24 | std::string me; 25 | std::unordered_set servers; 26 | public: 27 | explicit P2PServiceImpl(const ::p2p::Url *urlMe); 28 | 29 | virtual ~P2PServiceImpl() {} 30 | 31 | grpc::Status register_me( 32 | ::grpc::ServerContext *context, const ::p2p::Url *request, ::grpc::ServerWriter<::p2p::Url> *writer); 33 | 34 | grpc::Status update_services( 35 | ::grpc::ServerContext *context, ::grpc::ServerReaderWriter<::p2p::Url, ::p2p::Url> *stream); 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /spec/cpp/.clang-tidy: -------------------------------------------------------------------------------- 1 | UseColor: true 2 | 3 | Checks: > 4 | cppcoreguidelines-*, 5 | google-*, 6 | performance-*, 7 | -cppcoreguidelines-avoid-magic-numbers, 8 | -google-readability-todo, 9 | -performance-unnecessary-value-param, 10 | -google-readability-braces-around-statements, 11 | -cppcoreguidelines-non-private-member-variables-in-classes, 12 | -clang-diagnostic-builtin-macro-redefined 13 | HeaderFilterRegex: ".*" 14 | # disable: cppcoreguidelines-pro-type-union-access 15 | # how to disable: bugprone-easily-swappable-parameters 16 | # bugprone-*, 17 | # -cppcoreguidelines-pro-type-union-access, 18 | # -bugprone-easily-swappable-parameters 19 | 20 | # TODO: include 'performance-unnecessary-value-param' 21 | 22 | 23 | CheckOptions: 24 | - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor 25 | value: '1' 26 | # https://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/special-member-functions.html 27 | 28 | FormatStyle: 'file' 29 | 30 | WarningsAsErrors: "*" 31 | 32 | # ignore protobuf and gRPC generated header (bug in union impl) 33 | # Code '(? 3 | 4 | using namespace std; 5 | 6 | P2PServiceImpl::P2PServiceImpl(const ::p2p::Url *urlMe) { 7 | me = urlToString(urlMe); 8 | } 9 | 10 | grpc::Status P2PServiceImpl::register_me( 11 | ::grpc::ServerContext *context, const ::p2p::Url *request, ::grpc::ServerWriter<::p2p::Url> *writer) { 12 | const string registered = urlToString(request); 13 | servers.emplace(registered); 14 | cout << me << " is registering: " << registered << endl; 15 | 16 | writer->Write(stringToUrl(me)); 17 | for (auto &server : servers) { 18 | writer->Write(stringToUrl(server)); 19 | } 20 | 21 | return grpc::Status::OK; 22 | } 23 | 24 | grpc::Status P2PServiceImpl::update_services( 25 | ::grpc::ServerContext *context, ::grpc::ServerReaderWriter<::p2p::Url, ::p2p::Url> *stream) { 26 | cout << me << " is updating its services " << endl; 27 | p2p::Url url; 28 | while (stream->Read(&url)) { 29 | servers.emplace(urlToString(&url)); 30 | } 31 | 32 | stream->Write(stringToUrl(me)); 33 | for (auto &server : servers) { 34 | stream->Write(stringToUrl(server)); 35 | } 36 | return grpc::Status::OK; 37 | } 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | projects_folder=spec 2 | 3 | all: cpp_bazel # cpp_cmake 4 | 5 | # (cd ~/go/src/github.com/NeoResearch/libbft && dep ensure && go run main.go) 6 | 7 | cpp_bazel: 8 | (cd $(projects_folder)/cpp/ && bazel build ...) 9 | valgrind $(projects_folder)/cpp/bazel-bin/app_test # bazel run //:app_test 10 | 11 | cpp_cmake: 12 | #export BFTEVENTS=`pwd`/spec/cpp/src/bftevents-grpc 13 | (mkdir -p $(projects_folder)/cpp/build && cd $(projects_folder)/cpp/build && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .. && make) 14 | valgrind ./$(projects_folder)/cpp/build/app_test 15 | 16 | cpp_test: cpp 17 | @echo "Performing basic tests now" 18 | @echo 19 | (cd $(projects_folder)/cpp/build/tests && make test) 20 | 21 | go: 22 | (cd ~/go/src/github.com/NeoResearch/libbft && dep ensure && go build *.go) 23 | 24 | go_test: go 25 | (cd ~/go/src/github.com/NeoResearch/libbft && go test ./...) 26 | 27 | csharp: 28 | (cd $(projects_folder)/csharp && dotnet build) 29 | 30 | test: cpp_test 31 | @echo 32 | @echo "Generating coverage (see tests/reports/)" 33 | @echo 34 | #(cd tests && make test-coverage) 35 | 36 | @echo 37 | @echo "Performing hard tests now... this will take a while!" 38 | @echo 39 | #(cd tests && make test-hard) 40 | 41 | clean: 42 | (cd $(projects_folder)/cpp/build && make clean) 43 | -------------------------------------------------------------------------------- /spec/cpp/test-grpc/server.cc: -------------------------------------------------------------------------------- 1 | // From: https://medium.com/@andrewvetovitz/grpc-c-introduction-45a66ca9461f 2 | 3 | #include 4 | 5 | #include "mathtest.grpc.pb.h" 6 | #include 7 | 8 | using grpc::Server; 9 | using grpc::ServerBuilder; 10 | using grpc::ServerContext; 11 | using grpc::Status; 12 | 13 | using mathtest::MathReply; 14 | using mathtest::MathRequest; 15 | using mathtest::MathTest; 16 | 17 | class MathServiceImplementation final : public MathTest::Service 18 | { 19 | Status sendRequest(ServerContext* context, const MathRequest* request, MathReply* reply) override 20 | { 21 | int a = request->a(); 22 | int b = request->b(); 23 | 24 | reply->set_result(a * b); 25 | 26 | return Status::OK; 27 | } 28 | }; 29 | 30 | using namespace std; 31 | 32 | void 33 | Run() 34 | { 35 | 36 | //cout << "HI" << endl; 37 | //return; 38 | std::string address("0.0.0.0:5000"); 39 | MathServiceImplementation service; 40 | 41 | ServerBuilder builder; 42 | 43 | builder.AddListeningPort(address, grpc::InsecureServerCredentials()); 44 | builder.RegisterService(&service); 45 | 46 | std::unique_ptr server(builder.BuildAndStart()); 47 | std::cout << "Server listening on port: " << address << std::endl; 48 | 49 | server->Wait(); 50 | } 51 | 52 | int 53 | main(int argc, char** argv) 54 | { 55 | Run(); 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /spec/cpp/tests/single/ConditionTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "single/Transition.hpp" 6 | #include "timing/Clock.hpp" 7 | #include "timing/Timer.hpp" 8 | 9 | using namespace std; 10 | using namespace libbft; 11 | 12 | TEST(SingleCondition, ToString) { 13 | Condition::TimedFunctionType f = [](const Timer &t, std::shared_ptr p, const MachineId &) -> bool { 14 | return *p % 2 == 0; 15 | }; 16 | unique_ptr> condition(new Condition("T", f)); 17 | 18 | EXPECT_EQ("T", condition->toString()); 19 | } 20 | 21 | TEST(SingleCondition, UseTimedFunction) { 22 | Condition::TimedFunctionType f = [](const Timer &t, std::shared_ptr p, const MachineId &) -> bool { 23 | return *p % 2 == 0; 24 | }; 25 | unique_ptr> condition(new Condition("T", f)); 26 | auto timer = std::shared_ptr(new Timer("T", std::unique_ptr(new Clock()))); 27 | auto p = std::shared_ptr(new int); 28 | *p = 1; 29 | 30 | const MachineId &id = MachineId(-1); 31 | auto rfv = f(*timer, p, id); 32 | auto rcv = condition->timedFunction(*timer, p, id); 33 | EXPECT_EQ(rfv, rcv); 34 | 35 | for (auto i = std::shared_ptr(new int{0}); *i < 10; ++(*i)) { 36 | rfv = f(*timer, i, id); 37 | rcv = condition->timedFunction(*timer, i, id); 38 | EXPECT_EQ(rfv, rcv); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /spec/cpp/cmake/FindTestHelper.cmake: -------------------------------------------------------------------------------- 1 | MACRO(ADD_TEST_ALL_FILES asan) 2 | get_filename_component(test_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) 3 | 4 | if (${asan} STREQUAL "ON") 5 | message(STATUS "Adding address sanitizer to ${test_name}") 6 | ADD_TEST_DEFINITIONS() 7 | else () 8 | message(STATUS "No address sanitizer to ${test_name}") 9 | endif () 10 | message(STATUS "Creating test ${test_name}") 11 | 12 | file(GLOB_RECURSE test_sources *) 13 | list(FILTER test_sources INCLUDE REGEX "cpp$") 14 | 15 | set(lib_name "${PROJECT_NAME}_${PROJECT_VERSION}") 16 | 17 | add_executable( 18 | "${test_name}_test" 19 | ${test_sources} 20 | ) 21 | 22 | target_link_libraries( 23 | "${test_name}_test" 24 | ${lib_name} 25 | gtest 26 | gtest_main 27 | ${CMAKE_THREAD_LIBS_INIT} 28 | ) 29 | 30 | if (${asan} STREQUAL "ON") 31 | target_link_libraries("${test_name}_test" asan) 32 | endif () 33 | 34 | target_link_libraries("${test_name}_test" ${lib_name}) 35 | include_directories(${CMAKE_SOURCE_DIR}/src) 36 | 37 | message(STATUS "Adding test: ${test_name}_test") 38 | add_test(NAME "${test_name}_test_caller" COMMAND "${test_name}_test") 39 | 40 | target_compile_options("${test_name}_test" PRIVATE --coverage) 41 | target_link_libraries("${test_name}_test" --coverage) 42 | ENDMACRO() 43 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/replicated/MachineContext.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_REPLICATED_MACHINECONTEXT_HPP_ 6 | #define INCLUDE_LIBBFT_REPLICATED_MACHINECONTEXT_HPP_ 7 | 8 | // system includes 9 | #include 10 | #include 11 | #include 12 | 13 | // libbft includes 14 | 15 | // Prototype? 16 | #include 17 | #include 18 | #include 19 | 20 | namespace libbft { 21 | 22 | template 23 | struct MultiContext; 24 | 25 | template 26 | struct MachineContext { 27 | using TParam = std::shared_ptr; 28 | using TSingleTimerStateMachine = 29 | std::shared_ptr>>; 30 | 31 | TParam params; 32 | TSingleTimerStateMachine machine; 33 | Events events; 34 | 35 | // void addEvent(Event>* e) 36 | //{ 37 | // events.push_back(e); 38 | // } 39 | 40 | MachineContext(TParam _params, TSingleTimerStateMachine _machine) 41 | : params(_params), machine(_machine) {} 42 | }; 43 | 44 | } // namespace libbft 45 | 46 | // forward declaration 47 | #include 48 | 49 | #endif // INCLUDE_LIBBFT_REPLICATED_MACHINECONTEXT_HPP_ 50 | -------------------------------------------------------------------------------- /spec/go/src/timing/timer_delayable.go: -------------------------------------------------------------------------------- 1 | package timing 2 | 3 | type TimerDelayable interface { 4 | // get / set 5 | GetCountdown() float64 6 | // methods 7 | Reset() 8 | ElapsedTime() float64 9 | RemainingTime() float64 10 | Expired() bool 11 | Init(countdown float64) Timer 12 | InitDefault() Timer 13 | 14 | GetTimer() Timer 15 | String() string 16 | } 17 | 18 | type TimerDelayableService struct { 19 | timer Timer 20 | } 21 | 22 | func NewTimerDelayable(name string, clock Clock, countdown float64) TimerDelayable { 23 | return &TimerDelayableService{ 24 | NewTimer(name, clock, countdown), 25 | } 26 | } 27 | 28 | func (t *TimerDelayableService) String() string { 29 | return t.String() 30 | } 31 | 32 | func (t *TimerDelayableService) Reset() { 33 | t.Reset() 34 | } 35 | 36 | func (t *TimerDelayableService) ElapsedTime() float64 { 37 | return t.ElapsedTime() 38 | } 39 | 40 | func (t *TimerDelayableService) RemainingTime() float64 { 41 | return t.RemainingTime() 42 | } 43 | 44 | func (t *TimerDelayableService) Expired() bool { 45 | return t.Expired() 46 | } 47 | 48 | func (t *TimerDelayableService) GetCountdown() float64 { 49 | return t.GetTimer().GetCountdown() 50 | } 51 | 52 | func (t *TimerDelayableService) Init(countdown float64) Timer { 53 | return t.GetTimer().Init(countdown) 54 | } 55 | 56 | func (t *TimerDelayableService) GetTimer() Timer { 57 | return t.timer 58 | } 59 | 60 | func (t *TimerDelayableService) InitDefault() Timer { 61 | return t.GetTimer().InitDefault() 62 | } 63 | -------------------------------------------------------------------------------- /spec/go/src/events/event.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type Event interface { 9 | // get / set 10 | GetName() string 11 | GetFrom() int 12 | GetParameters() []string 13 | // methods 14 | IsActivated(name string, parameters []string) bool 15 | 16 | String() string 17 | } 18 | 19 | type EventService struct { 20 | name string 21 | from int 22 | parameters []string 23 | } 24 | 25 | func NewEvent(name string, from int, parameters []string) Event { 26 | return &EventService{ 27 | name, 28 | from, 29 | parameters, 30 | } 31 | } 32 | 33 | func (e *EventService) GetName() string { 34 | return e.name 35 | } 36 | 37 | func (e *EventService) GetFrom() int { 38 | return e.from 39 | } 40 | 41 | func (e *EventService) String() string { 42 | var sb strings.Builder 43 | sb.WriteString(fmt.Sprintf("Event %v(", e.GetName())) 44 | for i, param := range e.GetParameters() { 45 | sb.WriteString(fmt.Sprintf("%v", param)) 46 | if i != len(e.GetParameters())-1 { 47 | sb.WriteString(",") 48 | } 49 | } 50 | sb.WriteString(")") 51 | return sb.String() 52 | } 53 | 54 | func (e *EventService) IsActivated(name string, parameters []string) bool { 55 | if e.GetName() == name && len(e.GetParameters()) == len(parameters) { 56 | for i, v := range e.GetParameters() { 57 | if parameters[i] != v { 58 | return false 59 | } 60 | } 61 | return true 62 | } 63 | return false 64 | } 65 | 66 | func (e *EventService) GetParameters() []string { 67 | return e.parameters 68 | } 69 | -------------------------------------------------------------------------------- /spec/cpp/tests/timing/TimerTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "timing/Timer.hpp" 6 | 7 | using namespace std; 8 | using namespace libbft; 9 | 10 | TEST(TimingTimer, ToString) { 11 | unique_ptr timer(new Timer("T")); 12 | EXPECT_EQ("Timer {name='T'}", timer->toString()); 13 | } 14 | 15 | TEST(TimingTimer, GetCountdown) { 16 | unique_ptr timer(new Timer("T")); 17 | timer->init(2); 18 | EXPECT_EQ(2, timer->getCountdown()); 19 | } 20 | 21 | TEST(TimingTimer, ElapsedTime) { 22 | unique_ptr timer(new Timer("T")); 23 | 24 | this_thread::sleep_for(chrono::milliseconds(200)); 25 | EXPECT_LT(.15, timer->elapsedTime()); 26 | } 27 | 28 | TEST(TimingTimer, Expired) { 29 | unique_ptr timer(new Timer("T")); 30 | timer->init(.3); 31 | 32 | this_thread::sleep_for(chrono::milliseconds(100)); 33 | EXPECT_FALSE(timer->expired()); 34 | this_thread::sleep_for(chrono::milliseconds(250)); 35 | EXPECT_TRUE(timer->expired()); 36 | } 37 | 38 | TEST(TimingTimer, RemainingTime) { 39 | unique_ptr timer(new Timer("T")); 40 | timer->init(1); 41 | 42 | this_thread::sleep_for(chrono::milliseconds(100)); 43 | ASSERT_GE(1.0, timer->remainingTime()); 44 | ASSERT_LE(.5, timer->remainingTime()); 45 | 46 | this_thread::sleep_for(chrono::milliseconds(100)); 47 | ASSERT_GE(.85, timer->remainingTime()); 48 | ASSERT_LE(.4, timer->remainingTime()); 49 | 50 | this_thread::sleep_for(chrono::milliseconds(800)); 51 | ASSERT_TRUE(timer->expired()); 52 | ASSERT_EQ(0, timer->remainingTime()); 53 | } 54 | -------------------------------------------------------------------------------- /spec/cpp/test-grpc/client.cc: -------------------------------------------------------------------------------- 1 | // From: https://medium.com/@andrewvetovitz/grpc-c-introduction-45a66ca9461f 2 | 3 | #include 4 | 5 | #include "mathtest.grpc.pb.h" 6 | #include 7 | 8 | using grpc::Channel; 9 | using grpc::ClientContext; 10 | using grpc::Status; 11 | 12 | using mathtest::MathReply; 13 | using mathtest::MathRequest; 14 | using mathtest::MathTest; 15 | 16 | class MathTestClient 17 | { 18 | public: 19 | MathTestClient(std::shared_ptr channel) 20 | : stub_(MathTest::NewStub(channel)) 21 | { 22 | } 23 | 24 | int sendRequest(int a, int b) 25 | { 26 | MathRequest request; 27 | 28 | request.set_a(a); 29 | request.set_b(b); 30 | 31 | MathReply reply; 32 | 33 | ClientContext context; 34 | 35 | Status status = stub_->sendRequest(&context, request, &reply); 36 | 37 | if (status.ok()) { 38 | return reply.result(); 39 | } else { 40 | std::cout << status.error_code() << ": " << status.error_message() << std::endl; 41 | return -1; 42 | } 43 | } 44 | 45 | private: 46 | std::unique_ptr stub_; 47 | }; 48 | 49 | void 50 | Run() 51 | { 52 | std::string address("0.0.0.0:5000"); 53 | MathTestClient client(grpc::CreateChannel(address, grpc::InsecureChannelCredentials())); 54 | 55 | int response; 56 | 57 | int a = 5; 58 | int b = 10; 59 | 60 | response = client.sendRequest(a, b); 61 | std::cout << "Answer received: " << a << " * " << b << " = " << response << std::endl; 62 | } 63 | 64 | int 65 | main(int argc, char* argv[]) 66 | { 67 | Run(); 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /spec/go/src/timing/timer_test.go: -------------------------------------------------------------------------------- 1 | package timing 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestTimer(t *testing.T) { 10 | name := "timer" 11 | timer := NewTimer(name, NewClock("clock"), -1) 12 | assert.NotNil(t, timer) 13 | assert.Contains(t, timer.String(), name) 14 | } 15 | 16 | func startWait(t *testing.T, name string) { 17 | timer := NewTimer(name, NewClock("clock"), -1) 18 | assert.NotNil(t, timer) 19 | assert.Contains(t, timer.String(), name) 20 | 21 | time.Sleep(200 * time.Millisecond) 22 | assert.True(t, timer.ElapsedTime() >= .15) 23 | assert.False(t, timer.Expired()) 24 | } 25 | 26 | func startWait2(t *testing.T, name string) { 27 | timer := NewTimer(name, NewClock("clock"), .3) 28 | time.Sleep(100 * time.Millisecond) 29 | assert.False(t, timer.Expired()) 30 | time.Sleep(250 * time.Millisecond) 31 | assert.True(t, timer.Expired()) 32 | } 33 | 34 | func TestTimerElapsedTimeExpired(t *testing.T) { 35 | startWait(t, "timer") 36 | startWait2(t, "timer") 37 | } 38 | 39 | func startRemainingTime(t *testing.T, name string) { 40 | timer := NewTimer(name, NewClock("clock"), 1) 41 | time.Sleep(100 * time.Millisecond) 42 | assert.True(t, timer.RemainingTime() < 1.0) 43 | assert.True(t, timer.RemainingTime() > .5) 44 | time.Sleep(100 * time.Millisecond) 45 | assert.True(t, timer.RemainingTime() < .8) 46 | assert.True(t, timer.RemainingTime() > .4) 47 | time.Sleep(800 * time.Millisecond) 48 | assert.True(t, timer.Expired()) 49 | assert.Equal(t, 0.0, timer.RemainingTime()) 50 | } 51 | 52 | func TestTimerRemainingTime(t *testing.T) { 53 | startRemainingTime(t, "timer") 54 | } 55 | -------------------------------------------------------------------------------- /spec/go/src/replicated/machine_context.go: -------------------------------------------------------------------------------- 1 | package replicated 2 | 3 | import ( 4 | "errors" 5 | "github.com/NeoResearch/libbft/src/events" 6 | "github.com/NeoResearch/libbft/src/machine" 7 | "github.com/NeoResearch/libbft/src/single" 8 | ) 9 | 10 | type MachineContext interface { 11 | // get / set 12 | GetParams() single.Param 13 | GetMachine() machine.SingleTimerStateMachine 14 | GetEvents() []events.Event 15 | // methods 16 | AddEvent(event events.Event) 17 | RemoveEvent(index int) error 18 | } 19 | 20 | type MachineContextService struct { 21 | params single.Param 22 | singleTimerStateMachine machine.SingleTimerStateMachine 23 | events []events.Event 24 | } 25 | 26 | func NewMachineContext(params single.Param, singleTimerStateMachine machine.SingleTimerStateMachine, events []events.Event) MachineContext { 27 | return &MachineContextService{ 28 | params, 29 | singleTimerStateMachine, 30 | events, 31 | } 32 | } 33 | 34 | func (m *MachineContextService) GetParams() single.Param { 35 | return m.params 36 | } 37 | 38 | func (m *MachineContextService) GetMachine() machine.SingleTimerStateMachine { 39 | return m.singleTimerStateMachine 40 | } 41 | 42 | func (m *MachineContextService) GetEvents() []events.Event { 43 | return m.events 44 | } 45 | 46 | func (m *MachineContextService) AddEvent(event events.Event) { 47 | m.events = append(m.events, event) 48 | } 49 | 50 | func (m *MachineContextService) RemoveEvent(index int) error { 51 | if index < 0 || index > len(m.events) { 52 | return errors.New("invalid index") 53 | } 54 | m.events = append(m.events[:index], m.events[index+1:]...) 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/events/ScheduledEvent.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_EVENTS_SCHEDULEDEVENT_HPP_ 6 | #define INCLUDE_LIBBFT_EVENTS_SCHEDULEDEVENT_HPP_ 7 | 8 | // C++ 9 | #include 10 | #include 11 | #include 12 | 13 | // libbft includes 14 | #include 15 | #include 16 | #include 17 | 18 | namespace libbft { 19 | 20 | /** 21 | * Scheduled class: launches a 'thing' (type T) after clock has expired 22 | * simple class to hold information, could be an std::array perhaps, if named 23 | */ 24 | struct ScheduledEvent { 25 | /** event name */ 26 | std::string name; 27 | /** event parameters */ 28 | std::vector eventParams; 29 | /** countdown in seconds for event */ 30 | double countdown; 31 | /** machine to send event */ 32 | MachineId machineTo; 33 | // machine 'from' is not needed, as this is usually seen as a system event, 34 | // but if necessary, we can add this option here, to "simulate" a timed 35 | // message from other nodes 36 | 37 | ScheduledEvent( 38 | std::string _name, double _countdown, MachineId _machineTo, 39 | std::vector _eventParams = std::vector(0)) 40 | : name(std::move(_name)), 41 | eventParams(std::move(_eventParams)), 42 | countdown(_countdown), 43 | machineTo(std::move(_machineTo)) {} 44 | }; 45 | 46 | } // namespace libbft 47 | 48 | #endif // INCLUDE_LIBBFT_EVENTS_SCHEDULEDEVENT_HPP_ 49 | -------------------------------------------------------------------------------- /spec/go/src/simple_example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/NeoResearch/libbft/src/machine" 6 | "github.com/NeoResearch/libbft/src/single" 7 | "github.com/NeoResearch/libbft/src/timing" 8 | "github.com/NeoResearch/libbft/src/util" 9 | "os" 10 | "os/exec" 11 | ) 12 | 13 | func check(e error) { 14 | if e != nil { 15 | panic(e) 16 | } 17 | } 18 | 19 | func main() { 20 | initial := single.NewState(false, "Initial") 21 | final := single.NewState(true, "Final") 22 | 23 | initial.AddTransition(single.NewTransaction(final, "after1sec").AddCondition(single.NewCondition("C >= 1", func(t timing.Timer, d single.Param, me int) (bool, error) { 24 | fmt.Printf("WAITING TRANSITION TO HAPPEN %v s\n", t.ElapsedTime()) 25 | return t.ElapsedTime() >= 3.0, nil 26 | }))) 27 | 28 | myMachine := machine.NewSingleTimerStateMachineClock(timing.NewTimerName("C")) 29 | 30 | err := myMachine.RegisterState(initial) 31 | check(err) 32 | err = myMachine.RegisterState(final) 33 | check(err) 34 | 35 | fmt.Printf("Machine => %v\n", myMachine) 36 | 37 | fmt.Println("BEFORE RUN, WILL PRINT AS GRAPHVIZ!") 38 | graphviz := myMachine.StringFormat(util.GraphivizFormat) 39 | fmt.Printf("%v\n", graphviz) 40 | 41 | myMachine.SetWatchdog(5) 42 | myMachine.RunDefault() 43 | 44 | fileName := "fgraph_STSM.dot" 45 | f, err := os.Create(fileName) 46 | check(err) 47 | defer f.Close() 48 | 49 | _, err = f.WriteString(fmt.Sprintf("%v\n", graphviz)) 50 | check(err) 51 | imageName := "fgraph_STSM.png" 52 | 53 | //fmt.Printf("Generating image '%v'\n", imageName) 54 | //cmd := exec.Command("dot", fmt.Sprintf("-Tpng %v -o %v", fileName, imageName)) 55 | //err = cmd.Run() 56 | //check(err) 57 | } 58 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/utils/CLI.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_UTILS_CLI_H_ 6 | #define INCLUDE_LIBBFT_UTILS_CLI_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace libbft { 13 | class Argument { 14 | private: 15 | char shortName; 16 | bool itHasValue; 17 | std::string longName; 18 | std::string defaultValue; 19 | 20 | public: 21 | explicit Argument(char shortName = '\0', bool itHasValue = false, 22 | std::string longName = "", std::string defaultValue = ""); 23 | 24 | char getShortName() const; 25 | bool hasValue() const; 26 | std::string getLongName() const; 27 | bool hasLongName() const; 28 | std::string getDefaultValue() const; 29 | bool hasDefaultValue() const; 30 | 31 | std::string getShortNameParam() const; 32 | std::string getLongNameParam() const; 33 | }; 34 | 35 | class ArgumentParser { 36 | private: 37 | std::vector args; 38 | std::unordered_map shortArgument; 39 | std::unordered_map longArgument; 40 | std::unordered_map argumentValue; 41 | 42 | public: 43 | explicit ArgumentParser(int argc, char** argv); 44 | explicit ArgumentParser(std::vector& argv); 45 | 46 | void addArguments(const std::vector& arguments); 47 | void addArgument(const Argument& arg); 48 | void parse(); 49 | std::string getValue(const Argument& arg) const; 50 | bool isPresent(const Argument& arg) const; 51 | }; 52 | 53 | } // namespace libbft 54 | 55 | #endif // INCLUDE_LIBBFT_UTILS_CLI_H_ 56 | -------------------------------------------------------------------------------- /spec/cpp/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(ProtobufHelper REQUIRED) 2 | 3 | file(GLOB_RECURSE project_sources *) 4 | list(FILTER project_sources EXCLUDE REGEX "main.*\\.cpp$") 5 | list(FILTER project_sources EXCLUDE REGEX "main.*\\.cc$") 6 | list(FILTER project_sources EXCLUDE REGEX "\\.txt$") 7 | list(FILTER project_sources EXCLUDE REGEX "\\.h$") 8 | list(FILTER project_sources EXCLUDE REGEX "\\.gitignore$") 9 | list(FILTER project_sources EXCLUDE REGEX "Makefile$") 10 | list(FILTER project_sources EXCLUDE REGEX "\\.proto$") 11 | #list(FILTER project_sources INCLUDE REGEX "pp$") 12 | 13 | set(lib_name "${PROJECT_NAME}_${PROJECT_VERSION}") 14 | message(STATUS "${lib_name} - ${project_sources}") 15 | add_library(${lib_name} ${project_sources}) 16 | target_link_libraries_proto(${lib_name}) 17 | #set_source_files_properties(${project_sources} PROPERTIES LANGUAGE CXX) 18 | set_target_properties(${lib_name} PROPERTIES LINKER_LANGUAGE CXX) 19 | 20 | add_executable(app_test demo/main.cpp ${project_sources}) 21 | 22 | target_link_libraries_proto(app_test) 23 | target_link_libraries( 24 | app_test 25 | ${lib_name} 26 | ) 27 | 28 | add_subdirectory(p2p) 29 | add_subdirectory(bftevents-grpc) 30 | ##add_subdirectory(demo) 31 | ##add_subdirectory(demo-bft-rpc) 32 | 33 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L/usr/local/lib -L/opt/lib") 34 | add_executable(app_RPCtest 35 | demo-bft-rpc/mainRPC.cpp 36 | bftevents-grpc/bftevent.pb.cc 37 | bftevents-grpc/bftevent.grpc.pb.cc 38 | ${project_sources} 39 | ) 40 | 41 | target_link_libraries_proto(app_RPCtest) 42 | 43 | if (build_type STREQUAL "debug") 44 | # Fix asan 45 | # target_link_libraries(app_test asan) 46 | # target_link_libraries(app_RPCtest asan) 47 | endif() 48 | -------------------------------------------------------------------------------- /spec/cpp/src/bftevents-grpc/mainBftclient.cc: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #include // C++20 6 | #include 7 | #include 8 | 9 | // 10 | #include 11 | 12 | void RunTest(int me, int to) { 13 | std::stringstream ss; 14 | ss << "0.0.0.0:500" << to; // 0 -> 5000 15 | std::string toAddress(ss.str()); 16 | 17 | BFTEventsClient client(me, toAddress); 18 | 19 | int from = me; 20 | std::string event = "MustStart()"; 21 | 22 | std::vector eventArgs; 23 | eventArgs.push_back("a1"); 24 | eventArgs.push_back("a2"); 25 | 26 | int response = client.informEvent(from, event, eventArgs); 27 | std::cout << "Answer received: " << from << " ; " << event << " => " 28 | << response << std::endl; 29 | } 30 | 31 | int main(int argc, char* argv[]) { 32 | // error: do not use pointer arithmetic 33 | // [cppcoreguidelines-pro-bounds-pointer-arithmetic,-warnings-as-errors] 34 | // https://stackoverflow.com/questions/45718389/how-to-avoid-pointer-arithmetic-when-using-char-argv 35 | auto args = std::span(argv, static_cast(argc)); 36 | // 37 | int me = 0; 38 | if (argc >= 2) { 39 | // std::string s(argv[1]); // get value 40 | std::string s(args[1]); // get value 41 | me = stoi(s); 42 | } 43 | int to = me; 44 | 45 | if (argc >= 3) { 46 | // std::string s(argv[2]); // get value 47 | std::string s(args[2]); // get value 48 | to = stoi(s); 49 | } 50 | 51 | std::cout << "I am # " << me << std::endl; 52 | std::cout << "will send to # " << to << std::endl; 53 | 54 | RunTest(me, to); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /spec/cpp/src/bftevents-grpc/BUILD.bazel: -------------------------------------------------------------------------------- 1 | 2 | package(default_visibility = ["//visibility:public"]) 3 | 4 | load("@rules_proto//proto:defs.bzl", "proto_library") 5 | load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_proto_library") 6 | load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") 7 | 8 | # proto deps 9 | 10 | proto_library( 11 | name = "bftevent_lib_proto", 12 | srcs = ["bftevent.proto"], 13 | ) 14 | 15 | cc_proto_library( 16 | name = "bftevent_cclib_proto", 17 | deps = [":bftevent_lib_proto"], 18 | ) 19 | 20 | cc_grpc_library( 21 | name = "bftevent_cclib_grpc", 22 | srcs = [":bftevent_lib_proto"], 23 | grpc_only = True, 24 | deps = [":bftevent_cclib_proto"], 25 | ) 26 | 27 | 28 | #################### 29 | # library: *.hpp 30 | #################### 31 | 32 | cc_library( 33 | name = "cc_bftclientserver_impl", 34 | hdrs = glob([ 35 | "**/*.hpp", 36 | ]), 37 | include_prefix = "bftevents-grpc/", 38 | defines = ["BAZEL_BUILD"], 39 | deps = [ 40 | "//include:LibBFT", 41 | ":bftevent_cclib_grpc", 42 | ], 43 | ) 44 | 45 | #################### 46 | # binary: bftevents 47 | #################### 48 | 49 | 50 | cc_binary( 51 | name = "cc_bftclient", 52 | srcs = ["mainBftclient.cc"], 53 | defines = ["BAZEL_BUILD"], 54 | deps = [ 55 | "@com_github_grpc_grpc//:grpc++", 56 | ":cc_bftclientserver_impl", 57 | ], 58 | ) 59 | 60 | cc_binary( 61 | name = "cc_bftserver", 62 | srcs = ["mainBftserver.cc"], 63 | defines = ["BAZEL_BUILD"], 64 | deps = [ 65 | "@com_github_grpc_grpc//:grpc++", 66 | #"@com_github_grpc_grpc//:grpc++_reflection", 67 | #":bftevent_cclib_grpc", 68 | ":cc_bftclientserver_impl" 69 | ], 70 | ) 71 | -------------------------------------------------------------------------------- /spec/go/src/timing/countdown_timer.go: -------------------------------------------------------------------------------- 1 | package timing 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "time" 7 | ) 8 | 9 | type CountdownTimer interface { 10 | // get / set 11 | GetClock() Clock 12 | GetMyTime() time.Time 13 | GetName() string 14 | GetCountdown() float64 15 | // methods 16 | Init(countdown float64) 17 | Reset() 18 | Expired() bool 19 | 20 | String() string 21 | } 22 | 23 | type CountdownTimerService struct { 24 | clock Clock 25 | myTime time.Time 26 | name string 27 | countdown float64 28 | } 29 | 30 | func NewCountdownTimer(countdown float64, clock Clock, name string, myTime float64) CountdownTimer { 31 | return &CountdownTimerService{ 32 | clock, 33 | clock.GetTime(), 34 | name, 35 | countdown, 36 | } 37 | } 38 | 39 | func (c *CountdownTimerService) GetClock() Clock { 40 | return c.clock 41 | } 42 | 43 | func (c *CountdownTimerService) GetMyTime() time.Time { 44 | return c.myTime 45 | } 46 | 47 | func (c *CountdownTimerService) GetName() string { 48 | return c.name 49 | } 50 | 51 | func (c *CountdownTimerService) String() string { 52 | return fmt.Sprintf("CountdownTimer {name=%v}", c.GetName()) 53 | } 54 | 55 | func (c *CountdownTimerService) GetCountdown() float64 { 56 | return c.countdown 57 | } 58 | 59 | func (c *CountdownTimerService) Init(countdown float64) { 60 | c.countdown = countdown 61 | if c.clock == nil { 62 | c.clock = NewDefaultClock() 63 | } 64 | c.myTime = c.GetClock().GetTime() 65 | } 66 | 67 | func (c *CountdownTimerService) Reset() { 68 | c.myTime = c.GetClock().GetTime() 69 | } 70 | 71 | func (c *CountdownTimerService) Expired() bool { 72 | elapsed := SinceTime(c.GetMyTime()) 73 | remaining := infiniteTime 74 | if c.GetCountdown() >= 0.0 { 75 | remaining = math.Max(0.0, c.GetCountdown() - elapsed) 76 | } 77 | 78 | return remaining == 0.0 79 | } 80 | -------------------------------------------------------------------------------- /script/clean_cache.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | rm -rf ${1}/grpc/third_party/protobuf/*.log 3 | rm -rf ${1}/grpc/third_party/protobuf/libtool 4 | 5 | rm -rf ${1}/grpc/third_party/protobuf/third_party/googletest/autom4te.cache/requests 6 | rm -rf ${1}/grpc/third_party/protobuf/third_party/googletest/*.log 7 | rm -rf ${1}/grpc/third_party/protobuf/third_party/googletest/googlemock/autom4te.cache/requests 8 | rm -rf ${1}/grpc/third_party/protobuf/third_party/googletest/googlemock/*.log 9 | rm -rf ${1}/grpc/third_party/protobuf/third_party/googletest/googlemock/libtool 10 | rm -rf ${1}/grpc/third_party/protobuf/third_party/googletest/googletest/autom4te.cache/requests 11 | rm -rf ${1}/grpc/third_party/protobuf/third_party/googletest/googletest/*.log 12 | rm -rf ${1}/grpc/third_party/protobuf/third_party/googletest/googletest/libtool 13 | #rm -rf ${1}/grpc/third_party/protobuf/third_party/ 14 | 15 | rm -rf ${1}/grpc/libs/opt/*.a 16 | rm -rf ${1}/grpc/libs/opt/libgrpc++_error_details.so.1.27.0-dev 17 | rm -rf ${1}/grpc/libs/opt/libgrpcpp_channelz.so.1.27.0-dev 18 | rm -rf ${1}/grpc/libs/opt/libgrpc++_reflection.so.1.27.0-dev 19 | rm -rf ${1}/grpc/libs/opt/libgrpc++_error_details.so.1.27.0-dev 20 | rm -rf ${1}/grpc/libs/opt/libgrpcpp_channelz.so.1.27.0-dev 21 | rm -rf ${1}/grpc/libs/opt/libg 22 | rm -rf ${1}/grpc/libs/opt/libgrpc_cronet.so.9.0.0 23 | rm -rf ${1}/grpc/libs/opt/libgrpc++.so.1.27.0-dev 24 | rm -rf ${1}/grpc/libs/opt/libgrpc.so.9.0.0 25 | rm -rf ${1}/grpc/libs/opt/libgrpc++_unsecure.so.1.27.0-dev 26 | rm -rf ${1}/grpc/libs/opt/libgrpc_unsecure.so.9.0.0 27 | 28 | rm -rf ${1}/grpc/objs/opt/${1}/gens/src/proto/grpc/channelz/*.o 29 | rm -rf ${1}/grpc/objs/opt/${1}/grpc/gens/src/proto/grpc/core/*.o 30 | rm -rf ${1}/grpc/objs/opt/${1}/grpc/gens/src/proto/grpc/reflection/v1alpha/*.o 31 | #rm -rf ${1}/grpc/objs/opt/home/travis/build/NeoResearch/libbft/grpc/gens/ 32 | -------------------------------------------------------------------------------- /spec/go/src/timing/countdown_notify_timer.go: -------------------------------------------------------------------------------- 1 | package timing 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type CountdownNotifyTimer interface { 9 | // superclass 10 | GetCountdownTimer() CountdownTimer 11 | // get / set 12 | GetClock() Clock 13 | GetMyTime() time.Time 14 | GetName() string 15 | GetCountdown() float64 16 | // methods 17 | Init(countdown float64) 18 | Reset() 19 | Expired() bool 20 | 21 | String() string 22 | } 23 | 24 | type CountdownNotifyTimerService struct { 25 | countdownTimer CountdownTimer 26 | } 27 | 28 | func NewCountdownNotifyTimer(countdown float64, clock Clock, name string, myTime float64) CountdownNotifyTimer { 29 | return &CountdownNotifyTimerService { 30 | NewCountdownTimer(countdown, clock, name, myTime), 31 | } 32 | } 33 | 34 | func (c *CountdownNotifyTimerService) GetCountdownTimer() CountdownTimer { 35 | return c.countdownTimer 36 | } 37 | 38 | func (c *CountdownNotifyTimerService) GetClock() Clock { 39 | return c.GetCountdownTimer().GetClock() 40 | } 41 | 42 | func (c *CountdownNotifyTimerService) GetMyTime() time.Time { 43 | return c.GetCountdownTimer().GetMyTime() 44 | } 45 | 46 | func (c *CountdownNotifyTimerService) GetName() string { 47 | return c.GetCountdownTimer().GetName() 48 | } 49 | 50 | func (c *CountdownNotifyTimerService) GetCountdown() float64 { 51 | return c.GetCountdownTimer().GetCountdown() 52 | } 53 | 54 | func (c *CountdownNotifyTimerService) Init(countdown float64) { 55 | c.GetCountdownTimer().Init(countdown) 56 | } 57 | 58 | func (c *CountdownNotifyTimerService) Reset() { 59 | c.GetCountdownTimer().Reset() 60 | } 61 | 62 | func (c *CountdownNotifyTimerService) Expired() bool { 63 | return c.GetCountdownTimer().Expired() 64 | } 65 | 66 | func (c *CountdownNotifyTimerService) String() string { 67 | return fmt.Sprintf("CountdownNotifyTimer {name=%v}", c.GetName()) 68 | } 69 | -------------------------------------------------------------------------------- /spec/go/src/events/timed_event.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/NeoResearch/libbft/src/timing" 6 | "strings" 7 | ) 8 | 9 | type TimedEvent interface { 10 | // superclass 11 | getEvent() Event 12 | // get / set 13 | GetName() string 14 | GetFrom() int 15 | GetParameters() []string 16 | // methods 17 | IsActivated(name string, parameters []string) bool 18 | 19 | // get / set 20 | GetTimer() timing.Timer 21 | 22 | String() string 23 | } 24 | 25 | type TimedEventService struct { 26 | event Event 27 | timer timing.Timer 28 | } 29 | 30 | func NewTimedEvent(name string, from int, parameters []string, countdown float64) TimedEvent { 31 | resp := TimedEventService{ 32 | event: NewEvent(name, from, parameters), 33 | timer: timing.NewDefaultTimer(), 34 | } 35 | resp.timer.Init(countdown) 36 | return &resp 37 | } 38 | 39 | func (t *TimedEventService) GetName() string { 40 | return t.getEvent().GetName() 41 | } 42 | 43 | func (t *TimedEventService) GetFrom() int { 44 | return t.getEvent().GetFrom() 45 | } 46 | 47 | func (t *TimedEventService) GetParameters() []string { 48 | return t.getEvent().GetParameters() 49 | } 50 | 51 | func (t *TimedEventService) IsActivated(name string, parameters []string) bool { 52 | return t.getEvent().IsActivated(name, parameters) && t.GetTimer().Expired() 53 | } 54 | 55 | func (t *TimedEventService) GetTimer() timing.Timer { 56 | return t.timer 57 | } 58 | 59 | func (t *TimedEventService) String() string { 60 | var sb strings.Builder 61 | 62 | sb.WriteString(fmt.Sprintf("TimedEvent %v", t.GetName())) 63 | for i, v := range t.GetParameters() { 64 | if i != len(t.GetParameters()) { 65 | sb.WriteString(",") 66 | } 67 | sb.WriteString(v) 68 | } 69 | if t.GetTimer().Expired() { 70 | sb.WriteString(") expired") 71 | } else { 72 | sb.WriteString(") notexpired") 73 | } 74 | return sb.String() 75 | } 76 | 77 | func (t *TimedEventService) getEvent() Event { 78 | return t.event 79 | } 80 | -------------------------------------------------------------------------------- /spec/go/src/timing/timer.go: -------------------------------------------------------------------------------- 1 | package timing 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "time" 7 | ) 8 | 9 | const infiniteTime = 1000000000.0 10 | 11 | type Timer interface { 12 | // get / set 13 | GetCountdown() float64 14 | // methods 15 | Reset() 16 | ElapsedTime() float64 17 | RemainingTime() float64 18 | Expired() bool 19 | InitDefault() Timer 20 | Init(countdown float64) Timer 21 | 22 | String() string 23 | } 24 | 25 | type TimerService struct { 26 | clock Clock 27 | myTime time.Time 28 | name string 29 | countdown float64 30 | } 31 | 32 | func NewDefaultTimer() Timer { 33 | return NewTimer("", nil, -1.0) 34 | } 35 | 36 | func NewTimerName(name string) Timer { 37 | return NewTimer(name, nil, -1.0) 38 | } 39 | 40 | func NewTimer(name string, clock Clock, countdown float64) Timer { 41 | if clock == nil { 42 | clock = NewDefaultClock() 43 | } 44 | return &TimerService{ 45 | clock, 46 | clock.GetTime(), 47 | name, 48 | countdown, 49 | } 50 | } 51 | 52 | func (t *TimerService) Reset() { 53 | t.myTime = t.clock.GetTime() 54 | } 55 | 56 | func (t *TimerService) ElapsedTime() float64 { 57 | return SinceTime(t.myTime) 58 | } 59 | 60 | func (t *TimerService) RemainingTime() float64 { 61 | if t.GetCountdown() >= 0.0 { 62 | return math.Max(0.0, t.GetCountdown()-t.ElapsedTime()) 63 | } else { 64 | return infiniteTime 65 | } 66 | } 67 | 68 | func (t *TimerService) Expired() bool { 69 | return t.RemainingTime() == 0.0 70 | } 71 | 72 | func (t *TimerService) String() string { 73 | return fmt.Sprintf("Timer {name='%v'}", t.name) 74 | } 75 | 76 | func (t *TimerService) GetCountdown() float64 { 77 | return t.countdown 78 | } 79 | 80 | func (t *TimerService) Init(countdown float64) Timer { 81 | t.countdown = countdown 82 | if t.clock == nil { 83 | t.clock = NewDefaultClock() 84 | } 85 | t.myTime = t.clock.GetTime() 86 | return t 87 | } 88 | 89 | func (t *TimerService) InitDefault() Timer { 90 | return t.Init(-1) 91 | } 92 | -------------------------------------------------------------------------------- /spec/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | set(PROJECT_NAME libbft) 4 | set(PROJECT_VERSION 0.1.0) 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build") 7 | 8 | project(${PROJECT_NAME} VERSION ${PROJECT_VERSION}) 9 | 10 | #if (EXISTS /usr/bin/gcc-11) 11 | # if (EXISTS /usr/bin/g++-11) 12 | # set(CMAKE_C_COMPILER /usr/bin/gcc-11) 13 | # set(CMAKE_CXX_COMPILER /usr/bin/g++-11) 14 | # endif() 15 | #endif() 16 | 17 | 18 | set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake") 19 | find_package(Definitions REQUIRED) 20 | 21 | ADD_MY_DEFINITIONS() 22 | 23 | #set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS "${CMAKE_CXX_SOURCE_FILE_EXTENSIONS};hpp;HPP") 24 | #message(STATUS "Ignoring extensios ${CMAKE_CXX_IGNORE_EXTENSIONS}") 25 | #list(REMOVE_ITEM CMAKE_CXX_IGNORE_EXTENSIONS HPP hpp) 26 | #message(STATUS "Ignoring extensios ${CMAKE_CXX_IGNORE_EXTENSIONS}") 27 | 28 | #set(PROTOBUF_GENERATED_PATH src CACHE STRING proto_gen) 29 | # 30 | # 31 | #set(Protobuf_INCLUDE_DIRS /opt/include) 32 | # 33 | # 34 | # IMPORTANT: ln -s /opt/bin/grpc_cpp_plugin /usr/local/bin/grpc_cpp_plugin 35 | set(GRPC_PLUGIN_CPP /usr/local/bin/grpc_cpp_plugin CACHE STRING grpc_bin) 36 | 37 | option(BUILD_TESTS "Build test programs" ON) 38 | message(STATUS "BUILD_TESTS = ${BUILD_TESTS}") 39 | 40 | set(CMAKE_CXX_CPPCHECK "cppcheck") 41 | 42 | include_directories(src) 43 | add_subdirectory(src) 44 | if (BUILD_TESTS) 45 | message(STATUS "Building the tests") 46 | enable_testing() 47 | SET(COVERAGE OFF CACHE BOOL "Coverage") 48 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") 49 | add_definitions(--coverage) 50 | add_definitions(-fprofile-arcs) 51 | add_definitions(-ftest-coverage) 52 | #find_package(GTest REQUIRED) 53 | #include_directories(${GTEST_INCLUDE_DIRS}) 54 | add_subdirectory(tests) 55 | else() 56 | message(STATUS "Ignoring the tests") 57 | endif() 58 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/timing/CountdownNotifyTimer.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_TIMING_COUNTDOWNNOTIFYTIMER_HPP_ 6 | #define INCLUDE_LIBBFT_TIMING_COUNTDOWNNOTIFYTIMER_HPP_ 7 | 8 | // system includes 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | // libbft includes 15 | #include 16 | #include 17 | 18 | // standard Timer for a TSM 19 | 20 | namespace libbft { 21 | 22 | /** 23 | * this CountdownTimer should "notify" its "condition_variable" when expired 24 | * this can be used for more ellaborate interconnection between running 25 | * processes, events and timers 26 | */ 27 | class CountdownNotifyTimer : public CountdownTimer { 28 | public: 29 | explicit CountdownNotifyTimer(double _countdown, std::string _name = "", 30 | TClock _clock = nullptr) 31 | : CountdownTimer(_countdown, std::move(_name), std::move(_clock)) { 32 | init(_countdown); 33 | } 34 | 35 | ~CountdownNotifyTimer() override = default; 36 | 37 | public: 38 | void init(double _countdown) override { 39 | this->countdown = _countdown; 40 | // TODO: should launch an actual system timer here (thread-based) 41 | } 42 | 43 | void reset() override { 44 | // TODO: should stop current running CountdownTimer, and relaunch it 45 | } 46 | 47 | bool expired() const override { 48 | // TODO: should evaluate if current system timer has expired. 49 | // when expired, it should automatically trigger "condition_variable", 50 | // and "notify_all" dependent threads 51 | return false; 52 | } 53 | 54 | std::string toString() const override { 55 | std::stringstream ss; 56 | ss << "CountdownNotifyTimer {name='" << name << "'}"; 57 | return ss.str(); 58 | } 59 | }; 60 | 61 | } // namespace libbft 62 | 63 | #endif // INCLUDE_LIBBFT_TIMING_COUNTDOWNNOTIFYTIMER_HPP_ 64 | -------------------------------------------------------------------------------- /spec/cpp/cmake/FindProtobufHelper.cmake: -------------------------------------------------------------------------------- 1 | #================================================================# 2 | # Protobuf code generation 3 | #================================================================# 4 | find_package(Protobuf REQUIRED) 5 | 6 | #find_library(PROTOBUF_LIBRARY protobuf HINTS /opt) 7 | #include_directories(protos) 8 | include_directories(/opt/include) 9 | ##link_directories(/opt/lib) 10 | 11 | #file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} PROTOMODEL_PATH) 12 | #file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${PROTOBUF_GENERATED_PATH} PROTOBINDING_PATH) 13 | 14 | MACRO(GENERATE_PROTO proto) 15 | file(TO_NATIVE_PATH ${proto} proto_native) 16 | message(STATUS "Generating Protobuf ${proto}") 17 | execute_process( 18 | COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} 19 | # --proto_path=${PROTOMODEL_PATH} 20 | --proto_path=${CMAKE_CURRENT_SOURCE_DIR} 21 | # --cpp_out=${PROTOBINDING_PATH} ${proto_native} 22 | --cpp_out=${CMAKE_CURRENT_SOURCE_DIR} ${proto_native} 23 | RESULT_VARIABLE rv 24 | ) 25 | 26 | execute_process( 27 | COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} 28 | # --proto_path=${PROTOMODEL_PATH} 29 | --proto_path=${CMAKE_CURRENT_SOURCE_DIR} 30 | # --grpc_out=${PROTOBINDING_PATH} 31 | --grpc_out=${CMAKE_CURRENT_SOURCE_DIR} 32 | --plugin=protoc-gen-grpc=${GRPC_PLUGIN_CPP} ${proto_native} 33 | RESULT_VARIABLE rv 34 | ) 35 | ENDMACRO() 36 | 37 | MACRO(target_link_libraries_proto name) 38 | #find_package(gRPC CONFIG REQUIRED) 39 | #find_package(Protobuf REQUIRED) 40 | ###find_library(PROTOBUF_AND_GRPC protobuf grpc++ grpc++_reflection HINTS /opt/lib) 41 | target_link_libraries( 42 | ${name} 43 | protobuf 44 | pthread 45 | # gRPC::grpc++ 46 | grpc++ 47 | grpc++_reflection 48 | dl 49 | m 50 | rt 51 | ###${PROTOBUF_AND_GRPC} 52 | ${_GRPC_GRPCPP_UNSECURE} 53 | ${_PROTOBUF_LIBPROTOBUF} 54 | ) 55 | ENDMACRO() 56 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/timing/Clock.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_TIMING_CLOCK_HPP_ // NOLINT 6 | #define INCLUDE_LIBBFT_TIMING_CLOCK_HPP_ 7 | 8 | // system includes 9 | #include // NOLINT 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // 16 | #include 17 | 18 | // standard Timer for a TSM 19 | 20 | namespace libbft { 21 | 22 | /** 23 | * this clock is supposed to be a very precise one 24 | * it can also be used on tests as a drifted clock (using inheritance) 25 | */ 26 | class Clock { 27 | private: 28 | std::string name; 29 | 30 | public: 31 | explicit Clock(std::string _name = "") : name{std::move(_name)} {} 32 | // keep this as non-default (for clarity) 33 | Clock(const Clock& other) : name{other.name} {} 34 | 35 | // MANDATORY PACK 36 | virtual ~Clock() = default; 37 | 38 | // Mandatory? 39 | Clock(Clock&& corpse) noexcept = default; 40 | Clock& operator=(const Clock& other) = default; 41 | Clock& operator=(Clock&& corpse) = default; 42 | 43 | public: 44 | /** 45 | * get time in seconds (milliseconds precision here.. could be more, perhaps) 46 | * @return 47 | */ 48 | virtual double getTime() { 49 | // using chrono 50 | std::chrono::system_clock::time_point now = 51 | std::chrono::system_clock::now(); 52 | auto now_ms = std::chrono::time_point_cast(now); 53 | auto epoch = now_ms.time_since_epoch(); // which epoch? unix? 54 | auto value = std::chrono::duration_cast(epoch); 55 | int64_t duration = value.count(); 56 | return static_cast(duration) / 1000.0; 57 | } 58 | 59 | std::string toString() const { 60 | std::stringstream ss; 61 | ss << "Clock {name='" << name << "'}"; 62 | return ss.str(); 63 | } 64 | }; 65 | 66 | using TClock = uptr; 67 | 68 | } // namespace libbft 69 | 70 | #endif // INCLUDE_LIBBFT_TIMING_CLOCK_HPP_ // NOLINT 71 | -------------------------------------------------------------------------------- /spec/cpp/bin/runRPCtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this script automatically changes to base directory and execute RPC test 4 | # requires: my_index N f H T InitialDelayMS RegularDelayMS 5 | rand=$RANDOM 6 | 7 | echo "shared random = $rand" 8 | 9 | # test 1: just primary alone 10 | #(cd `dirname "$0"` && ../build/app_RPCtest 0 4 1 S3_2000_0_Commit1 $rand) # primary (2 secs) 11 | 12 | # test 2: primary on background and backup on front (2/4 nodes) 13 | #(cd `dirname "$0"` && ../build/app_RPCtest 0 4 1 S3_2000_0_Commit1 $rand > /dev/null &) # primary (2 secs) 14 | #(cd `dirname "$0"` && ../build/app_RPCtest 1 4 1 S3_1000_0_Commit1 $rand) # backup (1 secs) 15 | 16 | # test 3: primary/backup on background and backup on front (3/4 nodes) 17 | #(cd `dirname "$0"` && ../build/app_RPCtest 0 4 1 S3_2000_0_Commit1 $rand > /dev/null &) # primary (2 secs) 18 | #(cd `dirname "$0"` && ../build/app_RPCtest 1 4 1 S3_1000_0_Commit1 $rand > /dev/null &) # backup (1 secs) 19 | #(cd `dirname "$0"` && ../build/app_RPCtest 2 4 1 S3_1000_0_Commit1 $rand) # backup (1 secs) 20 | 21 | # test 4: no primary (dead). 2 backups on background and backup on front (4/4 nodes) 22 | ###(cd `dirname "$0"` && ../build/app_RPCtest 0 4 1 S3_2000_0_Commit1 $rand > /dev/null &) # primary (2 secs) 23 | #(cd `dirname "$0"` && ../build/app_RPCtest 1 4 1 S3_1000_0_Commit1 $rand > /dev/null &) # backup (1 secs) 24 | #(cd `dirname "$0"` && ../build/app_RPCtest 2 4 1 S3_1000_0_Commit1 $rand > /dev/null &) # backup (1 secs) 25 | #(cd `dirname "$0"` && ../build/app_RPCtest 3 4 1 S3_1000_0_Commit1 $rand) # backup (1 secs) 26 | 27 | # test 5: no primary (dead). 2 backups on background and backup on front (4/4 nodes) - delay 500 ms 28 | ###(cd `dirname "$0"` && ../build/app_RPCtest 0 4 1 S3_2000_500_Commit1 $rand > /dev/null &) # primary (2 secs) 29 | (cd `dirname "$0"` && ../build/app_RPCtest 1 4 1 S3_1000_500_Commit1 ${rand} > /dev/null &) # backup (1 secs) 30 | (cd `dirname "$0"` && ../build/app_RPCtest 2 4 1 S3_1000_500_Commit1 ${rand} > /dev/null &) # backup (1 secs) 31 | (cd `dirname "$0"` && ../build/app_RPCtest 3 4 1 S3_1000_500_Commit1 ${rand}) # backup (1 secs) 32 | 33 | -------------------------------------------------------------------------------- /spec/cpp/tests/single/ActionTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "single/Transition.hpp" 5 | #include "timing/Clock.hpp" 6 | #include "timing/Timer.hpp" 7 | 8 | using namespace std; 9 | using namespace libbft; 10 | 11 | TEST(SingleAction, ToString) { 12 | Action::TimedActionType f = [](const Timer &t, std::shared_ptr p, const MachineId &) -> void { 13 | }; 14 | unique_ptr> action(new Action("T", f)); 15 | 16 | EXPECT_EQ("T", action->toString()); 17 | } 18 | 19 | TEST(SingleAction, UseTimedAction) { 20 | int value = -1; 21 | Action::TimedActionType f = [&value](const Timer &t, std::shared_ptr p, const MachineId &) -> void { 22 | value = *p; 23 | }; 24 | unique_ptr> action(new Action("T", f)); 25 | Timer timer("T", std::unique_ptr(new Clock())); 26 | 27 | auto p = std::shared_ptr(new int{1}); 28 | const MachineId &id = MachineId(-1); 29 | f(timer, p, id); 30 | EXPECT_EQ(1, value); 31 | value = -1; 32 | 33 | action->timedAction(timer, p, id); 34 | EXPECT_EQ(1, value); 35 | value = -1; 36 | 37 | for (auto i = std::shared_ptr(new int{0}); *i < 10; ++(*i)) { 38 | f(timer, i, id); 39 | EXPECT_EQ(*i, value); 40 | value = -1; 41 | 42 | action->timedAction(timer, i, id); 43 | EXPECT_EQ(*i, value); 44 | value = -1; 45 | } 46 | } 47 | 48 | struct ActionReference { 49 | int age; 50 | }; 51 | 52 | using TActionReference = std::shared_ptr; 53 | 54 | TEST(SingleAction, UseTimedActionReference) { 55 | Action::TimedActionType f = [](const Timer &t, TActionReference p, const MachineId &id) -> void { 56 | p->age = id.id; 57 | }; 58 | unique_ptr> action(new Action("T", f)); 59 | Timer timer("T", std::unique_ptr(new Clock())); 60 | 61 | auto p = std::shared_ptr(new ActionReference()); 62 | p->age = -1; 63 | 64 | const MachineId &id = MachineId(1); 65 | f(timer, p, id); 66 | EXPECT_EQ(1, p->age); 67 | p->age = -1; 68 | 69 | action->timedAction(timer, p, id); 70 | EXPECT_EQ(1, p->age); 71 | } 72 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/timing/CountdownTimer.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_TIMING_COUNTDOWNTIMER_HPP_ 6 | #define INCLUDE_LIBBFT_TIMING_COUNTDOWNTIMER_HPP_ 7 | 8 | // system includes 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // libbft includes 16 | #include 17 | 18 | // standard Timer for a TSM 19 | 20 | namespace libbft { 21 | 22 | class CountdownTimer { 23 | protected: 24 | /** beware if clock precision is terrible */ 25 | TClock clock; 26 | /** nice precision timer */ 27 | double mytime; 28 | /** object name */ 29 | std::string name; 30 | /** countdown timer (if value is positive) */ 31 | double countdown; 32 | 33 | public: 34 | explicit CountdownTimer(double _countdown, std::string _name = "", 35 | TClock _clock = nullptr) 36 | : clock{std::move(_clock)}, 37 | mytime{0.0}, 38 | name{std::move(_name)}, 39 | countdown{0.0} { 40 | init(_countdown); 41 | } 42 | 43 | virtual ~CountdownTimer() = default; 44 | 45 | public: 46 | virtual void init(double _countdown) { 47 | // update countdown 48 | countdown = _countdown; 49 | if (!clock) { 50 | // beware if it's a terrible clock 51 | clock = std::unique_ptr(new Clock()); 52 | } 53 | // this should be a precision time 54 | mytime = clock->getTime(); 55 | } 56 | 57 | double getCountdown() const { return countdown; } 58 | 59 | virtual void reset() { 60 | // this should be a precision time 61 | mytime = clock->getTime(); 62 | } 63 | 64 | /** 65 | * when returning 0.0, time is over 66 | * @return 67 | */ 68 | virtual bool expired() const { 69 | // elapsed time 70 | double newtime = clock->getTime(); 71 | double elapsed = newtime - mytime; 72 | double remaining = 1000000000.0; // INF 73 | if (countdown >= 0.0) { 74 | remaining = std::max(0.0, countdown - elapsed); 75 | } 76 | 77 | return remaining == 0.0; 78 | } 79 | 80 | virtual std::string toString() const { 81 | std::stringstream ss; 82 | ss << "CountdownTimer {name='" << name << "'}"; 83 | return ss.str(); 84 | } 85 | }; 86 | 87 | } // namespace libbft 88 | 89 | #endif // INCLUDE_LIBBFT_TIMING_COUNTDOWNTIMER_HPP_ 90 | -------------------------------------------------------------------------------- /spec/cpp/src/bftevents-grpc/BFTEventsClient.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef SRC_BFTEVENTS_GRPC_BFTEVENTSCLIENT_HPP_ 6 | #define SRC_BFTEVENTS_GRPC_BFTEVENTSCLIENT_HPP_ 7 | 8 | // C++ 9 | #include 10 | #include 11 | #include 12 | 13 | // 14 | #include 15 | 16 | #ifdef BAZEL_BUILD 17 | #include "src/bftevents-grpc/bftevent.grpc.pb.h" 18 | #else 19 | #include "bftevent.grpc.pb.h" 20 | #endif 21 | 22 | using grpc::Channel; 23 | using grpc::ClientContext; 24 | using grpc::Status; 25 | 26 | using bftevent::BFTEvent; 27 | using bftevent::EventInform; 28 | using bftevent::EventReply; 29 | 30 | class BFTEventsClient { 31 | private: 32 | std::unique_ptr stub_; 33 | 34 | public: 35 | int me{0}; 36 | 37 | explicit BFTEventsClient(int _me) 38 | : stub_(BFTEvent::NewStub(std::shared_ptr(grpc::CreateChannel( 39 | getAddress(_me), grpc::InsecureChannelCredentials())))), 40 | me(_me) { 41 | // 42 | } 43 | BFTEventsClient(int _me, std::string toAddress) 44 | : stub_(BFTEvent::NewStub(std::shared_ptr(grpc::CreateChannel( 45 | toAddress, grpc::InsecureChannelCredentials())))), 46 | me(_me) { 47 | // 48 | } 49 | 50 | // helper function to calculate address 51 | static std::string getAddress(int me) { 52 | std::stringstream ss; 53 | ss << "0.0.0.0:500" << me; // 0 -> 5000 54 | std::string _address = ss.str(); 55 | return _address; 56 | } 57 | 58 | bool informEvent(int from, std::string event, 59 | std::vector eventArgs, int delayMS = 0) { 60 | EventInform request; 61 | 62 | request.set_from(from); 63 | request.set_event(event); 64 | request.set_delay(delayMS); 65 | 66 | for (const auto& eventArg : eventArgs) { 67 | request.add_event_args(eventArg); 68 | } 69 | 70 | EventReply reply; 71 | 72 | ClientContext context; 73 | 74 | Status status = stub_->informEvent(&context, request, &reply); 75 | 76 | return status.ok(); 77 | /* 78 | if (status.ok()) { 79 | return reply.gotit(); 80 | } else { 81 | std::cout << status.error_code() << ": " << status.error_message() << 82 | std::endl; return -1; 83 | } 84 | */ 85 | } 86 | }; 87 | 88 | using TBFTEventsClient = std::shared_ptr; 89 | 90 | #endif // SRC_BFTEVENTS_GRPC_BFTEVENTSCLIENT_HPP_ 91 | -------------------------------------------------------------------------------- /spec/go/src/replicated/multi_context.go: -------------------------------------------------------------------------------- 1 | package replicated 2 | 3 | import ( 4 | "errors" 5 | "github.com/NeoResearch/libbft/src/events" 6 | ) 7 | 8 | type MultiContext interface { 9 | GetVm() []MachineContext 10 | Broadcast(event string, from int, eventParams []string) error 11 | BroadcastEvent(event events.Event, from int) error 12 | SendToVm(event string, from int, to int, eventParams []string) error 13 | SendToVmEvent(event events.Event, to int) error 14 | HasEvent(name string, at int, eventParams []string) (bool, error) 15 | ConsumeEvent(name string, at int, eventParams []string) error 16 | } 17 | 18 | type MultiContextService struct { 19 | vm []MachineContext 20 | } 21 | 22 | func NewMultiContext(vm []MachineContext) MultiContext { 23 | return &MultiContextService{ 24 | vm, 25 | } 26 | } 27 | 28 | func (m *MultiContextService) GetVm() []MachineContext { 29 | return m.vm 30 | } 31 | 32 | func (m *MultiContextService) Broadcast(event string, from int, eventParams []string) error { 33 | return m.BroadcastEvent(events.NewEvent(event, from, eventParams), from) 34 | } 35 | 36 | func (m *MultiContextService) BroadcastEvent(event events.Event, from int) error { 37 | for i := 0; i < len(m.GetVm()); i++ { 38 | if i != from { 39 | return m.SendToVmEvent(event, i) 40 | } 41 | } 42 | return nil 43 | } 44 | 45 | func (m *MultiContextService) SendToVm(event string, from int, to int, eventParams []string) error { 46 | return m.SendToVmEvent(events.NewEvent(event, from, eventParams), to) 47 | } 48 | 49 | func (m *MultiContextService) SendToVmEvent(event events.Event, to int) error { 50 | if to < 0 || to >= len(m.GetVm()) { 51 | return errors.New("invalid \"to\" destination") 52 | } 53 | m.GetVm()[to].AddEvent(event) 54 | return nil 55 | } 56 | 57 | func (m *MultiContextService) HasEvent(name string, at int, eventParams []string) (bool, error) { 58 | if at < 0 || at >= len(m.GetVm()) { 59 | return false, errors.New("invalid \"at\" destination") 60 | } 61 | for _, event := range m.GetVm()[at].GetEvents() { 62 | if event.IsActivated(name, eventParams) { 63 | return true, nil 64 | } 65 | } 66 | return false, nil 67 | } 68 | 69 | func (m *MultiContextService) ConsumeEvent(name string, at int, eventParams []string) error { 70 | if at < 0 || at >= len(m.GetVm()) { 71 | return errors.New("invalid \"at\" destination") 72 | } 73 | for i, event := range m.GetVm()[at].GetEvents() { 74 | if event.IsActivated(name, eventParams) { 75 | return m.GetVm()[at].RemoveEvent(i) 76 | } 77 | } 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/single/State.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_SINGLE_STATE_HPP_ 6 | #define INCLUDE_LIBBFT_SINGLE_STATE_HPP_ 7 | 8 | // system includes 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // standard Transition 19 | #include 20 | #include 21 | 22 | // standard State 23 | 24 | // every state is a Timed state (states that allow timed transitions) 25 | 26 | namespace libbft { 27 | 28 | template 29 | class State { 30 | public: 31 | using TParam = std::shared_ptr; 32 | using TTransition = std::shared_ptr>; 33 | 34 | /** should only access for get string, etc (on graphviz)... TODO: design 35 | * better protection here */ 36 | std::vector transitions; 37 | 38 | public: 39 | std::string name; 40 | bool isFinal; 41 | 42 | explicit State(bool _isFinal = false, std::string _name = "") 43 | : name(std::move(_name)), isFinal(_isFinal) {} 44 | 45 | void addTransition(TTransition t) { transitions.push_back(t); } 46 | 47 | TTransition tryGetTransition(Timer& timer, TParam p, MachineId me) { 48 | // cout << "Trying to Get Transition" << endl; 49 | // should be non-deterministic and asynchronous... 50 | // TODO: simulate this with random, at least, to avoid getting stuck on 51 | // tests by chance 52 | std::vector _transitions = transitions; 53 | 54 | auto rng = std::default_random_engine{}; 55 | std::shuffle(std::begin(_transitions), std::end(_transitions), rng); 56 | 57 | for (unsigned i = 0; i < _transitions.size(); i++) { 58 | if (_transitions[i]->isValid(timer, p, me)) return _transitions[i]; 59 | } 60 | return nullptr; 61 | } 62 | 63 | std::string toStringR(bool recursive) const { 64 | std::stringstream ss; 65 | ss << "state:{"; 66 | ss << "name='" << name << "';"; 67 | if (isFinal) ss << "FINAL"; 68 | ss << ";"; 69 | if (recursive) { 70 | ss << "transitions=["; 71 | for (unsigned i = 0; i < transitions.size(); i++) 72 | ss << transitions[i]->toString() << ";"; 73 | ss << "]"; 74 | } else { 75 | ss << "..."; 76 | } 77 | ss << "}"; 78 | return ss.str(); 79 | } 80 | }; 81 | 82 | } // namespace libbft 83 | 84 | #endif // INCLUDE_LIBBFT_SINGLE_STATE_HPP_ 85 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | dist: xenial 3 | language: cpp 4 | #env: 5 | # global: 6 | # # This flag may limit the parallel build of jobs 7 | # - MAKEFLAGS="-j 4" 8 | cache: 9 | # ccache: true 10 | directories: 11 | - ${TRAVIS_BUILD_DIR}/lcov-1.14 12 | - ${TRAVIS_BUILD_DIR}/grpc 13 | before_cache: 14 | - sh ${TRAVIS_BUILD_DIR}/script/clean_cache.sh ${TRAVIS_BUILD_DIR}/ 15 | addons: 16 | apt: 17 | update: true 18 | packages: 19 | - autoconf 20 | - automake 21 | - build-essential 22 | - cmake 23 | - clang-6.0 24 | - curl 25 | - g++ 26 | - make 27 | - libc++-dev 28 | - libcurl3-dev 29 | - libgflags-dev 30 | - libgtest-dev 31 | - libtool 32 | - pkg-config 33 | - unzip 34 | - cppcheck 35 | install: 36 | # lcov 37 | - sh ${TRAVIS_BUILD_DIR}/script/install_lcov.sh ${TRAVIS_BUILD_DIR}/ 38 | # gRPC and Protobuf 39 | - sh ${TRAVIS_BUILD_DIR}/script/clone_grpc.sh ${TRAVIS_BUILD_DIR}/ 40 | # Protobuf 41 | - sh ${TRAVIS_BUILD_DIR}/script/install_protobuf.sh ${TRAVIS_BUILD_DIR}/ 42 | # gRPC 43 | - sh ${TRAVIS_BUILD_DIR}/script/install_grpc.sh ${TRAVIS_BUILD_DIR}/ 44 | - pip install --user cpp-coveralls 45 | 46 | before_script: 47 | - cd ${TRAVIS_BUILD_DIR} 48 | - lcov --directory . --zerocounters 49 | 50 | compiler: 51 | - g++ 52 | 53 | jobs: 54 | include: 55 | - stage: build 56 | script: 57 | - cd ${TRAVIS_BUILD_DIR}/spec/cpp 58 | - mkdir build 59 | - cd build 60 | - cmake -DBUILD_TESTS=ON -DCOVERAGE=1 .. 61 | - make 62 | - GTEST_COLOR=1 ctest --extra-verbose 63 | 64 | # - stage: test 65 | # script: 66 | # - cd ${TRAVIS_BUILD_DIR}/spec/cpp 67 | # - mkdir build 68 | # - cd build 69 | # - cmake -DBUILD_TESTS=ON .. 70 | # - make 71 | # - GTEST_COLOR=1 ctest --extra-verbose 72 | 73 | after_success: 74 | - cd ${TRAVIS_BUILD_DIR}/spec/cpp/build 75 | - rm -rf tests/libgtest/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.gcda 76 | - rm -rf tests/libgtest/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.gcda 77 | # capture coverage info 78 | - lcov --directory . --capture --output-file coverage.info 79 | # filter out system and test code 80 | - lcov --remove coverage.info '*/cpp/tests/*' '/usr/*' --output-file coverage.info 81 | # debug before upload 82 | - lcov --list coverage.info 83 | # uploads to coveralls.. for a private repo using a token 84 | # - coveralls-lcov --repo-token ${COVERALLS_TOKEN} coverage.info 85 | # for open source 86 | - coveralls-lcov coverage.info 87 | 88 | notifications: 89 | email: false 90 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/cpp:0-ubuntu-22.04 2 | 3 | # general building stuff 4 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 5 | && apt-get -y install --no-install-recommends build-essential autoconf libtool pkg-config 6 | 7 | # install graphviz 8 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 9 | && apt-get -y install --no-install-recommends graphviz 10 | 11 | # [Optional] Uncomment this section to install additional vcpkg ports. 12 | # RUN su vscode -c "${VCPKG_ROOT}/vcpkg install " 13 | 14 | # [Optional] Uncomment this section to install additional packages. 15 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 16 | # && apt-get -y install --no-install-recommends 17 | 18 | #RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 19 | # && apt-get -y install --no-install-recommends nodejs npm 20 | 21 | RUN curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - 22 | 23 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 24 | && apt-get -y install --no-install-recommends nodejs 25 | 26 | # install bazel 27 | RUN npm install -g @bazel/bazelisk 28 | 29 | # install vcpkg: https://vcpkg.io/en/getting-started.html 30 | RUN (cd /opt && git clone https://github.com/Microsoft/vcpkg.git) 31 | RUN /opt/vcpkg/bootstrap-vcpkg.sh 32 | 33 | # install pip and cpplint 34 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 35 | && apt-get -y install --no-install-recommends python3-pip 36 | # install cpplint into /usr/local/bin/cpplint 37 | RUN pip install cpplint 38 | 39 | # install protobuf 40 | #RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 41 | # && apt-get -y install --no-install-recommends protobuf-compiler libprotobuf-dev 42 | #RUN /opt/vcpkg/vcpkg install protobuf 43 | 44 | # install grpc 45 | #RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 46 | # && apt-get -y install --no-install-recommends libgrpc++-dev 47 | 48 | # remove cmake 49 | RUN apt-get update && apt-get -y remove cmake 50 | 51 | # install cmake 52 | COPY ./install-cmake.sh /tmp/ 53 | RUN chmod +x /tmp/install-cmake.sh && /tmp/install-cmake.sh && rm -f /tmp/install-cmake.sh 54 | 55 | # build grpc 56 | COPY ./install-grpc.sh /tmp/ 57 | RUN chmod +x /tmp/install-grpc.sh && /tmp/install-grpc.sh && rm -f /tmp/install-grpc.sh 58 | 59 | # make shortcut to grpc_cpp_plugin 60 | RUN ln -s /opt/bin/grpc_cpp_plugin /usr/local/bin/grpc_cpp_plugin 61 | 62 | # MOVE UP: 63 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 64 | && apt-get -y install --no-install-recommends clang-tidy clang-format clangd 65 | -------------------------------------------------------------------------------- /spec/go/src/single/state.go: -------------------------------------------------------------------------------- 1 | package single 2 | 3 | import ( 4 | "fmt" 5 | "github.com/NeoResearch/libbft/src/timing" 6 | "math/rand" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | type State interface { 12 | // get / set 13 | GetName() string 14 | GetTransitions() []Transition 15 | IsFinal() bool 16 | // methods 17 | AddTransition(transition Transition) 18 | TryGetTransition(timer timing.Timer, param Param, me int) (Transition, error) 19 | 20 | StringRecursive(recursive bool) string 21 | String() string 22 | } 23 | 24 | type StateService struct { 25 | name string 26 | isFinal bool 27 | transitions []Transition 28 | } 29 | 30 | func NewDefaultState(isFinal bool, name string) State { 31 | return NewState(false, "") 32 | } 33 | 34 | func NewState(isFinal bool, name string) State { 35 | return &StateService{ 36 | name, 37 | isFinal, 38 | make([]Transition, 0), 39 | } 40 | } 41 | 42 | func (s *StateService) GetTransitions() []Transition { 43 | return s.transitions 44 | } 45 | 46 | func (s *StateService) AddTransition(transition Transition) { 47 | s.transitions = append(s.transitions, transition) 48 | } 49 | 50 | func (s *StateService) TryGetTransition(timer timing.Timer, param Param, me int) (Transition, error) { 51 | rand.Seed(time.Now().UnixNano()) 52 | transitions := make([]Transition, len(s.GetTransitions())) 53 | copy(transitions, s.GetTransitions()) 54 | rand.Shuffle(len(s.GetTransitions()), func(i, j int) { s.GetTransitions()[i], s.GetTransitions()[j] = s.GetTransitions()[j], s.GetTransitions()[i] }) 55 | for _, transition := range transitions { 56 | resp, err := transition.IsValid(timer, param, me) 57 | if err != nil { 58 | return nil, err 59 | } 60 | if resp { 61 | return transition, nil 62 | } 63 | } 64 | return nil, nil 65 | } 66 | 67 | func (s *StateService) IsFinal() bool { 68 | return s.isFinal 69 | } 70 | 71 | func (s *StateService) String() string { 72 | return s.StringRecursive(true) 73 | } 74 | 75 | func (s *StateService) StringRecursive(recursive bool) string { 76 | var sb strings.Builder 77 | sb.WriteString("state:{") 78 | sb.WriteString(fmt.Sprintf("name='%v';", s.GetName())) 79 | if s.IsFinal() { 80 | sb.WriteString("FINAL") 81 | } 82 | sb.WriteString(";") 83 | 84 | if recursive { 85 | sb.WriteString("transitions=[") 86 | for _, transition := range s.GetTransitions() { 87 | sb.WriteString(fmt.Sprintf("%v;", transition)) 88 | } 89 | sb.WriteString("]") 90 | } else { 91 | sb.WriteString("...") 92 | } 93 | sb.WriteString("}") 94 | return sb.String() 95 | } 96 | 97 | func (s *StateService) GetName() string { 98 | return s.name 99 | } 100 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/timing/Timer.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_TIMING_TIMER_HPP_ 6 | #define INCLUDE_LIBBFT_TIMING_TIMER_HPP_ 7 | 8 | // system includes 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // libbft includes 16 | #include 17 | 18 | // standard Timer for a TSM 19 | 20 | namespace libbft { 21 | 22 | class Timer { 23 | private: 24 | /** object name */ 25 | std::string name; 26 | /** beware if clock precision is terrible */ 27 | TClock clock; 28 | /** nice precision timer */ 29 | double mytime; 30 | /** countdown timer (if value is positive) - in seconds */ 31 | double countdown{-1.0}; 32 | 33 | public: 34 | explicit Timer(std::string _name = "", TClock _clock = nullptr) 35 | : name{std::move(_name)}, 36 | clock{std::move(_clock)}, 37 | mytime{0.0}, 38 | countdown{0.0} { 39 | init(); 40 | } 41 | 42 | /** 43 | * countdown in seconds 44 | * @param _countdown 45 | * @return 46 | */ 47 | Timer* init(double _countdown = -1.0) { 48 | // update countdown 49 | countdown = _countdown; 50 | if (!clock) { 51 | // beware if it's a terrible clock 52 | clock = std::unique_ptr(new Clock()); 53 | } 54 | // this should be a precision time 55 | mytime = clock->getTime(); 56 | return this; // allow chaining effect 57 | } 58 | 59 | double getCountdown() const { return countdown; } 60 | 61 | public: 62 | void reset() { 63 | // this should be a precision time 64 | mytime = clock->getTime(); 65 | } 66 | 67 | /** 68 | * time in seconds (for counting-up) 69 | * @return 70 | */ 71 | double elapsedTime() const { 72 | // this should be a precision time 73 | double newtime = clock->getTime(); 74 | return newtime - mytime; 75 | } 76 | 77 | /** 78 | * time in seconds (for counting-down) 79 | * @return 80 | */ 81 | double remainingTime() const { 82 | if (countdown >= 0.0) 83 | return std::max(0.0, countdown - elapsedTime()); 84 | else 85 | return 1000000000.0; // INF 86 | } 87 | 88 | /** 89 | * When returning 0.0, time is over 90 | * @return 91 | */ 92 | bool expired() const { return remainingTime() == 0.0; } 93 | 94 | std::string toString() const { 95 | std::stringstream ss; 96 | ss << "Timer {name='" << name << "'}"; 97 | return ss.str(); 98 | } 99 | }; 100 | 101 | using TTimer = std::unique_ptr; 102 | 103 | } // namespace libbft 104 | 105 | #endif // INCLUDE_LIBBFT_TIMING_TIMER_HPP_ 106 | -------------------------------------------------------------------------------- /spec/go/src/rpc_replicated/rpc_machine_context.go: -------------------------------------------------------------------------------- 1 | package rpc_replicated 2 | 3 | import ( 4 | "fmt" 5 | "github.com/NeoResearch/libbft/src/bftevent" 6 | "github.com/NeoResearch/libbft/src/events" 7 | "github.com/NeoResearch/libbft/src/machine" 8 | "github.com/NeoResearch/libbft/src/single" 9 | ) 10 | 11 | type RPCMachineContext interface { 12 | // get / set 13 | GetParams() []single.Param 14 | GetMe() int 15 | GetWorld() []bftevent.BFTEventClient 16 | GetEvents() []events.Event 17 | // methods 18 | TestSetRegularDelay(testRegularDelayMS int) 19 | TestSetDropRate(dropRate float64) 20 | HasEvent(name string, eventArgs []string) bool 21 | RegisterEvent(event events.Event) 22 | AddEvents(events []events.Event) 23 | Broadcast(event string, eventArgs []string) 24 | AddEventFromRPC(name string, from machine.MachineId, parameters []string, delay int) 25 | 26 | String() string 27 | } 28 | 29 | type RPCMachineContextService struct { 30 | params []single.Param 31 | me int 32 | world []bftevent.BFTEventClient 33 | events []events.Event 34 | testRegularDelayMS int 35 | testDropRate float64 36 | } 37 | 38 | func NewRPCMachineContext(params []single.Param, me int, world []bftevent.BFTEventClient, seed int) RPCMachineContext { 39 | return &RPCMachineContextService{ 40 | events: nil, 41 | testRegularDelayMS: 0, 42 | testDropRate: 0.0, 43 | } 44 | } 45 | 46 | func (r *RPCMachineContextService) GetParams() []single.Param { 47 | return r.params 48 | } 49 | 50 | func (r *RPCMachineContextService) GetMe() int { 51 | return r.me 52 | } 53 | 54 | func (r *RPCMachineContextService) GetWorld() []bftevent.BFTEventClient { 55 | return r.world 56 | } 57 | 58 | func (r *RPCMachineContextService) GetEvents() []events.Event { 59 | return r.events 60 | } 61 | 62 | func (r *RPCMachineContextService) TestSetRegularDelay(testRegularDelayMS int) { 63 | r.testRegularDelayMS = testRegularDelayMS 64 | } 65 | 66 | func (r *RPCMachineContextService) TestSetDropRate(dropRate float64) { 67 | r.testDropRate = dropRate 68 | } 69 | 70 | func (r *RPCMachineContextService) HasEvent(name string, eventArgs []string) bool { 71 | for _, v := range r.GetEvents() { 72 | if v.IsActivated(name, eventArgs) { 73 | return true 74 | } 75 | } 76 | return false 77 | } 78 | 79 | func (r *RPCMachineContextService) RegisterEvent(event events.Event) { 80 | fmt.Printf("RPCMachineContext registering event '{}'", event) 81 | r.events = append(r.events, event) 82 | } 83 | 84 | func (r *RPCMachineContextService) AddEvents(events []events.Event) { 85 | for _, event := range r.GetEvents() { 86 | r.RegisterEvent(event) 87 | } 88 | } 89 | 90 | func (r *RPCMachineContextService) Broadcast(event string, eventArgs []string) { 91 | panic("implement me") 92 | } 93 | 94 | func (r *RPCMachineContextService) AddEventFromRPC(name string, from machine.MachineId, parameters []string, delay int) { 95 | if delay == 0 { 96 | r.RegisterEvent(events.NewEvent(name, from, parameters)) 97 | } else { 98 | r.RegisterEvent(events.NewTimedEvent(delay / 1000.0, name, from, parameters)) 99 | } 100 | } 101 | 102 | func (r *RPCMachineContextService) String() string { 103 | panic("implement me") 104 | } 105 | -------------------------------------------------------------------------------- /spec/go/src/single/transition.go: -------------------------------------------------------------------------------- 1 | package single 2 | 3 | import ( 4 | "fmt" 5 | "github.com/NeoResearch/libbft/src/timing" 6 | "github.com/NeoResearch/libbft/src/util" 7 | "strings" 8 | ) 9 | 10 | type Transition interface { 11 | // get / set 12 | GetActions() []Action 13 | GetConditions() []Condition 14 | GetName() string 15 | GetTo() State 16 | 17 | // methods 18 | AddCondition(condition Condition) Transition 19 | AddAction(action Action) Transition 20 | IsValid(timer timing.Timer, param Param, me int) (bool, error) 21 | Execute(timer timing.Timer, param Param, me int) State 22 | 23 | StringFormat(format string) string 24 | String() string 25 | } 26 | 27 | type TransitionService struct { 28 | to State 29 | name string 30 | conditions []Condition 31 | actions []Action 32 | } 33 | 34 | func NewTransactionState(state State) Transition { 35 | return NewTransaction(state, "") 36 | } 37 | 38 | func NewTransaction(state State, name string) Transition { 39 | return &TransitionService{ 40 | state, 41 | name, 42 | make([]Condition, 0), 43 | make([]Action, 0), 44 | } 45 | } 46 | 47 | func (t *TransitionService) AddCondition(condition Condition) Transition { 48 | t.conditions = append(t.conditions, condition) 49 | return t 50 | } 51 | 52 | func (t *TransitionService) AddAction(action Action) Transition { 53 | t.actions = append(t.actions, action) 54 | return t 55 | } 56 | 57 | func (t *TransitionService) IsValid(timer timing.Timer, param Param, me int) (bool, error) { 58 | for _, condition := range t.GetConditions() { 59 | resp, err := condition.GetTimedFunction()(timer, param, me) 60 | if err != nil { 61 | return resp, err 62 | } 63 | if !resp { 64 | return false, nil 65 | } 66 | } 67 | return true, nil 68 | } 69 | 70 | func (t *TransitionService) Execute(timer timing.Timer, param Param, me int) State { 71 | for _, action := range t.GetActions() { 72 | action.GetTimedAction()(timer, param, me) 73 | } 74 | return t.to 75 | } 76 | 77 | func (t *TransitionService) GetConditions() []Condition { 78 | return t.conditions 79 | } 80 | 81 | func (t *TransitionService) GetActions() []Action { 82 | return t.actions 83 | } 84 | 85 | func (t *TransitionService) StringFormat(format string) string { 86 | var sb strings.Builder 87 | if format == util.GraphivizFormat { 88 | sb.WriteString(fmt.Sprintf(" -> %v", t.GetTo().GetName())) 89 | sb.WriteString(" [ label = \"") 90 | first := true 91 | for _, condition := range t.GetConditions() { 92 | if !first { 93 | sb.WriteString(" \n ") 94 | } 95 | sb.WriteString(fmt.Sprintf("%s ", condition.GetName())) 96 | first = false 97 | } 98 | for _, action := range t.GetActions() { 99 | if !first { 100 | sb.WriteString(" \n ") 101 | } 102 | sb.WriteString(fmt.Sprintf("%s ", action.GetName())) 103 | first = false 104 | } 105 | sb.WriteString("\"];") 106 | } else { 107 | sb.WriteString(fmt.Sprintf("t() => {name = '%v',", t.GetName())) 108 | sb.WriteString(fmt.Sprintf("to='%v',", t.GetTo().StringRecursive(false))) 109 | sb.WriteString("conditions=[") 110 | for _, condition := range t.GetConditions() { 111 | sb.WriteString(fmt.Sprintf("%v;", condition)) 112 | } 113 | sb.WriteString("], ") 114 | sb.WriteString("actions=[") 115 | for _, action := range t.GetActions() { 116 | sb.WriteString(fmt.Sprintf("%v;", action)) 117 | } 118 | sb.WriteString("], ") 119 | sb.WriteString("}") 120 | } 121 | 122 | return sb.String() 123 | } 124 | 125 | func (t *TransitionService) String() string { 126 | return t.StringFormat("") 127 | } 128 | 129 | func (t *TransitionService) GetTo() State { 130 | return t.to 131 | } 132 | 133 | func (t *TransitionService) GetName() string { 134 | return t.name 135 | } 136 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/utils/CLI.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #include // C++20 6 | #include 7 | #include 8 | #include 9 | 10 | // 11 | #include "CLI.h" 12 | 13 | using namespace libbft; // NOLINT 14 | 15 | Argument::Argument(char _shortName, bool _itHasValue, std::string _longName, 16 | std::string _defaultValue) 17 | : shortName(_shortName), 18 | itHasValue(_itHasValue), 19 | longName(std::move(_longName)), 20 | defaultValue(std::move(_defaultValue)) {} 21 | 22 | char Argument::getShortName() const { return shortName; } 23 | 24 | bool Argument::hasValue() const { return itHasValue; } 25 | 26 | std::string Argument::getLongName() const { return longName; } 27 | 28 | bool Argument::hasLongName() const { return !getLongName().empty(); } 29 | 30 | std::string Argument::getDefaultValue() const { return defaultValue; } 31 | 32 | bool Argument::hasDefaultValue() const { return !getDefaultValue().empty(); } 33 | 34 | std::string Argument::getShortNameParam() const { 35 | std::stringstream ss; 36 | ss << "-" << getShortName(); 37 | return ss.str(); 38 | } 39 | 40 | std::string Argument::getLongNameParam() const { return "--" + getLongName(); } 41 | 42 | ArgumentParser::ArgumentParser(int argc, char** argv) { 43 | auto span_args = std::span(argv, static_cast(argc)); 44 | 45 | for (auto i = 0; i < argc; ++i) { 46 | args.emplace_back(std::string(span_args[i])); 47 | } 48 | } 49 | 50 | ArgumentParser::ArgumentParser(std::vector& argv) { 51 | for (auto& arg : argv) { 52 | args.emplace_back(arg); 53 | } 54 | } 55 | 56 | void ArgumentParser::addArguments(const std::vector& arguments) { 57 | for (auto& arg : arguments) { 58 | addArgument(arg); 59 | } 60 | } 61 | 62 | void ArgumentParser::addArgument(const Argument& arg) { 63 | shortArgument[arg.getShortNameParam()] = arg; 64 | if (arg.hasLongName()) { 65 | longArgument[arg.getLongNameParam()] = arg; 66 | } 67 | } 68 | 69 | void ArgumentParser::parse() { 70 | for (auto& kv : shortArgument) { 71 | if (kv.second.hasDefaultValue()) { 72 | argumentValue[kv.first] = kv.second.getDefaultValue(); 73 | } 74 | } 75 | 76 | auto size = args.size(); 77 | for (auto i = 0ul; i < size; ++i) { 78 | if (shortArgument.find(args[i]) != shortArgument.end()) { 79 | auto arg = shortArgument.find(args[i]); 80 | if (arg->second.hasValue()) { 81 | if (i < size - 1) { 82 | argumentValue[arg->second.getShortNameParam()] = args[i++ + 1]; 83 | } else { 84 | // It should treat the error 85 | } 86 | } else { 87 | argumentValue[arg->second.getShortNameParam()] = ""; 88 | } 89 | } else if (longArgument.find(args[i]) != longArgument.end()) { 90 | auto arg = longArgument.find(args[i]); 91 | if (arg->second.hasValue()) { 92 | if (i < size - 1) { 93 | argumentValue[arg->second.getShortNameParam()] = args[i++ + 1]; 94 | } else { 95 | // It should treat the error 96 | } 97 | } else { 98 | argumentValue[arg->second.getShortNameParam()] = ""; 99 | } 100 | } else { 101 | // It was not found 102 | } 103 | } 104 | } 105 | 106 | std::string ArgumentParser::getValue(const Argument& arg) const { 107 | const auto& iterator = argumentValue.find(arg.getShortNameParam()); 108 | return iterator->second; 109 | } 110 | 111 | bool ArgumentParser::isPresent(const Argument& arg) const { 112 | const auto& iterator = argumentValue.find(arg.getShortNameParam()); 113 | return iterator != argumentValue.end(); 114 | } 115 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/replicated/MultiContext.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_REPLICATED_MULTICONTEXT_HPP_ 6 | #define INCLUDE_LIBBFT_REPLICATED_MULTICONTEXT_HPP_ 7 | 8 | // system includes 9 | #include 10 | #include 11 | #include 12 | 13 | // libbft includes 14 | 15 | // Prototype? 16 | #include 17 | #include 18 | #include 19 | 20 | namespace libbft { 21 | 22 | template 23 | struct MachineContext; 24 | 25 | template 26 | struct MultiContext { 27 | using TParam = std::shared_ptr; 28 | 29 | /** vector of machines */ 30 | std::vector> vm; 31 | 32 | TParam getParams(int id_me) { return vm[id_me].params; } 33 | 34 | std::vector getEvents(int id_me) { return vm[id_me].events; } 35 | 36 | /** from may be -1, if broadcasted from system */ 37 | void broadcast(std::string event, MachineId from, 38 | std::vector eventParams) { 39 | std::cout << " -> BROADCASTING EVENT '" << event << "' from " << from.id 40 | << std::endl; 41 | broadcast( 42 | std::shared_ptr(new Event(event, from, std::move(eventParams))), 43 | from); 44 | } 45 | 46 | /** 47 | * from may be -1, if broadcasted from system 48 | * @param event 49 | * @param from 50 | */ 51 | void broadcast(TEvent event, MachineId from) { 52 | for (int i = 0; i < static_cast(vm.size()); i++) { 53 | if (i != from.id) { 54 | // this may break with memory leaks (TODO: use shared_ptr, or copy-based 55 | // final class) 56 | sendTo(event, MachineId(i)); 57 | } 58 | } 59 | } 60 | 61 | /** 62 | * @param event 63 | * @param to should be valid (0 <= to <= R) 64 | */ 65 | void sendTo(TEvent event, MachineId to) { 66 | std::cout << " => SEND TO " << to.id << std::endl; 67 | assert((to.id >= 0) && (to.id < static_cast(vm.size()))); 68 | vm[to.id].events.push_back(event); 69 | } 70 | 71 | /** 72 | * 73 | * @param event 74 | * @param from may be -1, if broadcasted from system 75 | * @param to should be valid (0 <= to <= R) 76 | * @param eventParams 77 | */ 78 | void sendTo(std::string event, MachineId from, MachineId to, 79 | std::vector eventParams) { 80 | sendTo(new Event(std::move(event), std::move(from), std::move(eventParams)), 81 | to); 82 | } 83 | 84 | bool hasEvent(std::string name, int at, 85 | std::vector eventParams) { 86 | for (unsigned i = 0; i < vm[at].events.size(); i++) { 87 | // cout << "comparing " << name << "(" << parameters << ") with: " << 88 | // vm[at].events[i]->toString() << endl; 89 | if (vm[at].events[i]->isActivated(name, eventParams)) return true; 90 | } 91 | return false; 92 | } 93 | 94 | void consumeEvent(std::string name, int at, 95 | std::vector eventParams) { 96 | for (unsigned i = 0; i < vm[at].events.size(); i++) 97 | if (vm[at].events[i]->isActivated(name, eventParams)) { 98 | vm[at].events.erase(vm[at].events.begin() + i); 99 | return; 100 | } 101 | } 102 | 103 | /* 104 | void processEvent(string name, int at) 105 | { 106 | for (unsigned i = 0; i < vm[at].events.size(); i++) 107 | if (vm[at].events[i]->getType() == name) { 108 | vm[at].events.erase(vm[at].events.begin() + i); 109 | } 110 | } 111 | */ 112 | }; 113 | 114 | } // namespace libbft 115 | 116 | // forward declaration 117 | #include 118 | 119 | #endif // INCLUDE_LIBBFT_REPLICATED_MULTICONTEXT_HPP_ 120 | -------------------------------------------------------------------------------- /spec/go/src/machine/timed_state_machine.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import ( 4 | "fmt" 5 | "github.com/NeoResearch/libbft/src/single" 6 | "github.com/NeoResearch/libbft/src/timing" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | type TimedStateMachine interface { 12 | // get / set 13 | GetClock() timing.Clock 14 | GetMe() int 15 | GetName() string 16 | // methods 17 | OnEnterState(current single.State, param single.Param) 18 | BeforeUpdateState(current single.State, param single.Param) bool 19 | AfterUpdateState(current single.State, param single.Param, updated bool) bool 20 | UpdateState(current single.State, param single.Param) (single.State, bool, error) 21 | IsFinal(current single.State, param single.Param) bool 22 | Initialize(current single.State, param single.Param) single.State 23 | OnFinished(current single.State, param single.Param) 24 | Run(current single.State, param single.Param) (single.State, error) 25 | 26 | String() string 27 | } 28 | 29 | type TimedStateMachineService struct { 30 | clock timing.Clock 31 | me int 32 | name string 33 | } 34 | 35 | func NewDefaultTimedStateMachine() TimedStateMachine { 36 | return NewTimedStateMachine(nil, 0, "") 37 | } 38 | 39 | func NewTimedStateMachine(clock timing.Clock, me int, name string) TimedStateMachine { 40 | return &TimedStateMachineService{ 41 | clock, 42 | me, 43 | name, 44 | } 45 | } 46 | 47 | func (t *TimedStateMachineService) GetClock() timing.Clock { 48 | return t.clock 49 | } 50 | 51 | func (t *TimedStateMachineService) GetMe() int { 52 | return t.me 53 | } 54 | 55 | func (t *TimedStateMachineService) BeforeUpdateState(current single.State, param single.Param) bool { 56 | panic("interface method") 57 | } 58 | 59 | func (t *TimedStateMachineService) UpdateState(current single.State, param single.Param) (single.State, bool, error) { 60 | panic("interface method") 61 | } 62 | 63 | func (t *TimedStateMachineService) IsFinal(current single.State, param single.Param) bool { 64 | panic("interface method") 65 | } 66 | 67 | func (t *TimedStateMachineService) Initialize(current single.State, param single.Param) single.State { 68 | panic("interface method") 69 | } 70 | 71 | func (t *TimedStateMachineService) OnEnterState(current single.State, param single.Param) { 72 | panic("interface method") 73 | } 74 | 75 | func (t *TimedStateMachineService) AfterUpdateState(current single.State, param single.Param, updated bool) bool { 76 | if !updated { 77 | time.Sleep(100 * time.Millisecond) 78 | } 79 | return false 80 | } 81 | 82 | func (t *TimedStateMachineService) OnFinished(current single.State, param single.Param) { 83 | panic("interface method") 84 | } 85 | 86 | func (t *TimedStateMachineService) Run(initial single.State, param single.Param) (single.State, error) { 87 | return RunMachine(t, initial, param) 88 | } 89 | 90 | func (t *TimedStateMachineService) String() string { 91 | var sb strings.Builder 92 | sb.WriteString("TSM {") 93 | sb.WriteString(fmt.Sprintf("#id = %v;", t.GetMe())) 94 | sb.WriteString(fmt.Sprintf("clock = %v;", t.GetClock())) 95 | sb.WriteString("TSM }") 96 | return sb.String() 97 | } 98 | 99 | func (t *TimedStateMachineService) GetName() string { 100 | return t.name 101 | } 102 | 103 | func RunMachine(t TimedStateMachine, initial single.State, param single.Param) (single.State, error) { 104 | current := t.Initialize(initial, param) 105 | if current == nil { 106 | return nil, nil 107 | } 108 | t.OnEnterState(current, param) 109 | 110 | for !t.IsFinal(current, param) { 111 | var err error 112 | if t.BeforeUpdateState(current, param) { 113 | return nil, nil 114 | } 115 | updated := false 116 | current, updated, err = t.UpdateState(current, param) 117 | if err != nil { 118 | return nil, nil 119 | } 120 | if updated { 121 | t.OnEnterState(current, param) 122 | } 123 | if t.AfterUpdateState(current, param, updated) { 124 | return nil, nil 125 | } 126 | } 127 | 128 | t.OnFinished(current, param) 129 | 130 | return current, nil 131 | } 132 | -------------------------------------------------------------------------------- /spec/cpp/src/p2p/mainP2P.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "utils/CLI.h" 13 | #include "P2PServer.h" 14 | #include "bftp2p.grpc.pb.h" 15 | #include "bftp2p.pb.h" 16 | 17 | using namespace grpc; 18 | using namespace libbft; 19 | using namespace p2p; 20 | using namespace std; 21 | 22 | using grpc::Server; 23 | using grpc::ServerBuilder; 24 | 25 | 26 | int main(int argc, char **argv) { 27 | ArgumentParser parser(argc, argv); 28 | auto domain = Argument('d', true, "domain", "0.0.0.0"); 29 | auto port = Argument('p', true, "port", "50051"); 30 | auto peers = Argument('e', true, "peers"); 31 | parser.addArguments(std::vector{domain, port, peers}); 32 | parser.parse(); 33 | 34 | string serverAddress(parser.getValue(domain) + ":" + parser.getValue(port)); 35 | const p2p::Url url = stringToUrl(serverAddress); 36 | 37 | P2PServiceImpl p2p(&url); 38 | 39 | thread serverThread([&serverAddress, &p2p] { 40 | ServerBuilder builder; 41 | builder.AddListeningPort(serverAddress, grpc::InsecureServerCredentials()); 42 | builder.RegisterService(&p2p); 43 | unique_ptr server(builder.BuildAndStart()); 44 | cout << "Starting server: " << serverAddress << endl; 45 | server->Wait(); 46 | }); 47 | 48 | thread clientThread([&parser, &peers, &url, &serverAddress] { 49 | if (parser.isPresent(peers)) { 50 | auto thePeers = parser.getValue(peers); 51 | unordered_set peersSet; 52 | queue peersQueue; 53 | const unsigned long thePeersSize = thePeers.size(); 54 | 55 | auto addPeer = [&peersSet, &peersQueue, &serverAddress](string name) { 56 | if (name != serverAddress) { 57 | if (peersSet.emplace(name).second) { 58 | peersQueue.emplace(name); 59 | } 60 | } 61 | }; 62 | 63 | for (unsigned long i = 0; i < thePeersSize;) { 64 | auto comma = thePeers.find(',', i); 65 | bool shouldBreak = false; 66 | string thePeerName; 67 | if (comma != -1ul) { 68 | thePeerName = thePeers.substr(i, comma - i); 69 | } else { 70 | thePeerName = thePeers.substr(i); 71 | shouldBreak = true; 72 | } 73 | 74 | addPeer(thePeerName); 75 | if (shouldBreak) { 76 | break; 77 | } 78 | i = comma + 1; 79 | } 80 | 81 | cout << "Connecting to " << peersSet.size() << " addresses" << endl; 82 | while (!peersQueue.empty()) { 83 | auto &peerName = peersQueue.front(); 84 | peersQueue.pop(); 85 | cout << "Connecting to " << peerName << endl; 86 | 87 | auto stub = P2P::NewStub(CreateChannel(peerName, InsecureChannelCredentials())); 88 | ClientContext context; 89 | auto reader = stub->register_me(&context, url); 90 | p2p::Url peerUrl; 91 | while (reader->Read(&peerUrl)) { 92 | addPeer(urlToString(&peerUrl)); 93 | } 94 | if (!reader->Finish().ok()) { 95 | cout << "There was a problem connecting to " << peerName << endl; 96 | peersSet.erase(peerName); 97 | } 98 | } 99 | 100 | if (!peersSet.empty()) { 101 | auto stub = P2P::NewStub(CreateChannel(serverAddress, InsecureChannelCredentials())); 102 | ClientContext context; 103 | auto stream = stub->update_services(&context); 104 | for (auto &peerName: peersSet) { 105 | stream->Write(stringToUrl(peerName)); 106 | } 107 | stream->Finish(); 108 | } 109 | } 110 | }); 111 | 112 | serverThread.join(); 113 | clientThread.join(); 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /spec/cpp/src/bftevents-grpc/BFTEventsServer.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef SRC_BFTEVENTS_GRPC_BFTEVENTSSERVER_HPP_ 6 | #define SRC_BFTEVENTS_GRPC_BFTEVENTSSERVER_HPP_ 7 | 8 | // C++ 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | // grpc 15 | #include 16 | 17 | #ifdef BAZEL_BUILD 18 | #include "src/bftevents-grpc/bftevent.grpc.pb.h" 19 | #else 20 | #include "bftevent.grpc.pb.h" 21 | #endif 22 | 23 | // machine using this server 24 | #include 25 | 26 | namespace libbft { 27 | namespace bft = bftevent; 28 | 29 | template 30 | class BFTEventsServer final : public bft::BFTEvent::Service { 31 | public: 32 | using TRPCMachineContext = std::shared_ptr>; 33 | 34 | private: 35 | grpc::Status informEvent(grpc::ServerContext* context, 36 | const bft::EventInform* request, 37 | bft::EventReply* reply) override { 38 | std::cout << " ->-> RPC received inform!" << std::endl; 39 | int from = request->from(); 40 | std::string event = request->event(); 41 | int delay = request->delay(); 42 | 43 | std::vector eventArgs(request->event_args_size()); 44 | for (unsigned i = 0; i < eventArgs.size(); i++) 45 | eventArgs[i] = request->event_args(i); 46 | 47 | std::cout << " ->-> RPC inform from " << from << " is event '" << event 48 | << "' with args = " << eventArgs.size() << std::endl; 49 | for (unsigned i = 0; i < eventArgs.size(); i++) 50 | std::cout << " ->-> RPC arg " << i << ":" << eventArgs[i] << std::endl; 51 | 52 | if (myMachine == nullptr) { 53 | std::cout << " ->-> RPC no machine to respond to!!! Print!" << std::endl; 54 | std::cout << " ->-> RPC from = " << from << std::endl; 55 | std::cout << " ->-> RPC event = " << event << std::endl; 56 | } else { 57 | std::cout << " ->-> RPC Sending event to myMachine! delay=" << delay 58 | << std::endl; 59 | MachineId mFrom( 60 | from); // TODO(@igormcoelho): find address of sender machine 61 | myMachine->addEventFromRPC(event, mFrom, eventArgs, delay); 62 | } 63 | 64 | int gotit = 99; 65 | 66 | reply->set_gotit(gotit); 67 | 68 | return grpc::Status::OK; 69 | } 70 | 71 | public: 72 | // rpc machine context to serve event responses 73 | TRPCMachineContext myMachine; 74 | 75 | // pair of server pointer and string address 76 | std::pair, std::string> server; 77 | 78 | explicit BFTEventsServer(int me, TRPCMachineContext _myMachine = nullptr) 79 | : myMachine(_myMachine), server(setupServer(me)) {} 80 | 81 | // helper function to calculate address 82 | static std::string getAddress(int me) { 83 | std::stringstream ss; 84 | ss << "0.0.0.0:500" << me; // 0 -> 5000 85 | std::string _address = ss.str(); 86 | return _address; 87 | } 88 | 89 | std::pair, std::string> setupServer(int me) { 90 | std::string _address = getAddress(me); 91 | grpc::ServerBuilder builder; 92 | 93 | std::cout << "will setupServer on address: " << _address << std::endl; 94 | 95 | builder.AddListeningPort(_address, grpc::InsecureServerCredentials()); 96 | builder.RegisterService(this); // &service 97 | 98 | return make_pair(std::unique_ptr(builder.BuildAndStart()), 99 | _address); 100 | } 101 | 102 | void RunForever() { 103 | std::cout << " =>=>=> BFT Events Server listening on port: " 104 | << server.second << std::endl; 105 | server.first->Wait(); 106 | } 107 | 108 | void Stop() { 109 | std::cout << " =>=>=> Stopping BFT Events Server at " << server.second 110 | << std::endl; 111 | server.first->Shutdown(); 112 | } 113 | }; 114 | 115 | } // namespace libbft 116 | 117 | #endif // SRC_BFTEVENTS_GRPC_BFTEVENTSSERVER_HPP_ 118 | -------------------------------------------------------------------------------- /spec/cpp/cmake/FindDefinitions.cmake: -------------------------------------------------------------------------------- 1 | MACRO(ADD_MY_DEFINITIONS) 2 | message(STATUS "Adding definitiions") 3 | # https://developers.redhat.com/blog/2018/03/21/compiler-and-linker-flags-gcc/ 4 | # https://caiorss.github.io/C-Cpp-Notes/compiler-flags-options.html 5 | add_definitions(-std=c++17) 6 | #add_definitions(-fconcepts) 7 | add_definitions(-fpermissive) 8 | add_definitions(-fasynchronous-unwind-tables) 9 | 10 | #add_definitions(-fno-rtti) 11 | add_definitions(-pedantic-errors) 12 | add_definitions(-pipe) 13 | 14 | add_definitions(-W) 15 | add_definitions(-Wall) 16 | add_definitions(-Wconversion) 17 | add_definitions(-Wcast-align) 18 | add_definitions(-Wextra) 19 | #add_definitions(-Werror) 20 | add_definitions(-Werror=format-security) 21 | #add_definitions(-Werror=implicit-function-declaration) 22 | add_definitions(-Wpedantic) 23 | add_definitions(-Wno-long-long) 24 | add_definitions(-Wno-missing-braces) 25 | add_definitions(-Wno-parentheses) 26 | add_definitions(-Wno-switch) 27 | add_definitions(-Wno-unused-parameter) 28 | add_definitions(-Wold-style-cast) 29 | add_definitions(-Wpointer-arith) 30 | add_definitions(-Wshadow) 31 | #add_definitions(-Wswitch-default) 32 | #add_definitions(-Wswitch-enum) 33 | add_definitions(-Wcast-qual) 34 | #add_definitions(-Wundef) 35 | add_definitions(-Wunused) 36 | add_definitions(-Wunreachable-code) 37 | add_definitions(-Wunused-function) 38 | add_definitions(-Wunused-variable) 39 | add_definitions(-Wwrite-strings) 40 | 41 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-prototypes") 42 | 43 | string(TOLOWER "${CMAKE_BUILD_TYPE}" build_type) 44 | if (build_type STREQUAL "debug") 45 | message(STATUS "--- Debug build ---") 46 | add_definitions(-g) 47 | add_definitions(-grecord-gcc-switches) 48 | # add_definitions(-D_GLIBCXX_DEBUG) # Not sure why it's breaking the mainRPC.cpp build 49 | # add_definitions(-D_GLIBCXX_DEBUG_PEDANTIC) # Not sure why it's breaking the mainRPC.cpp build 50 | add_definitions(-D_FORTIFY_SOURCE=2) 51 | add_definitions(-D_GLIBCXX_ASSERTIONS) 52 | add_definitions(-ggdb) 53 | add_definitions(-O0) 54 | add_definitions(-fstack-protector-all) 55 | 56 | SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined") 57 | else () 58 | message(STATUS "--- Release build ---") 59 | add_definitions(-O3) 60 | # add_definitions(-Ofast) 61 | add_definitions(-finline-functions) 62 | add_definitions(-funroll-loops) 63 | 64 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -frecursive") 65 | endif () 66 | ENDMACRO() 67 | 68 | MACRO(ADD_TEST_DEFINITIONS) 69 | message(STATUS "Adding test definitions") 70 | 71 | string(TOLOWER "${CMAKE_BUILD_TYPE}" build_type) 72 | if (build_type STREQUAL "debug") 73 | message(STATUS "--- Debug build ---") 74 | message(STATUS "Adding sanitizer") 75 | add_definitions(-fsanitize=address) 76 | add_definitions(-fsanitize=alignment) 77 | add_definitions(-fsanitize=bool) 78 | add_definitions(-fsanitize=bounds) 79 | add_definitions(-fsanitize=bounds-strict) 80 | add_definitions(-fsanitize=builtin) 81 | add_definitions(-fsanitize=enum) 82 | add_definitions(-fsanitize=float-divide-by-zero) 83 | add_definitions(-fsanitize=float-cast-overflow) 84 | add_definitions(-fsanitize=integer-divide-by-zero) 85 | add_definitions(-fsanitize=leak) 86 | # add_definitions(-fsanitize=kernel-address) 87 | add_definitions(-fsanitize=nonnull-attribute) 88 | add_definitions(-fsanitize=null) 89 | add_definitions(-fsanitize=object-size) 90 | add_definitions(-fsanitize=pointer-compare) 91 | add_definitions(-fsanitize=pointer-overflow) 92 | add_definitions(-fsanitize=pointer-subtract) 93 | add_definitions(-fsanitize=return) 94 | add_definitions(-fsanitize=returns-nonnull-attribute) 95 | add_definitions(-fsanitize=signed-integer-overflow) 96 | add_definitions(-fsanitize=shift) 97 | add_definitions(-fsanitize=shift-base) 98 | add_definitions(-fsanitize=shift-exponent) 99 | # add_definitions(-fsanitize=thread) 100 | add_definitions(-fsanitize=undefined) 101 | add_definitions(-fsanitize=unreachable) 102 | add_definitions(-fsanitize=vla-bound) 103 | add_definitions(-fsanitize=vptr) 104 | endif () 105 | ENDMACRO() 106 | -------------------------------------------------------------------------------- /spec/cpp/src/bftevents-grpc/bftevent.grpc.pb.cc: -------------------------------------------------------------------------------- 1 | // Generated by the gRPC C++ plugin. 2 | // If you make any local change, they will be lost. 3 | // source: bftevent.proto 4 | 5 | #include "bftevent.pb.h" 6 | #include "bftevent.grpc.pb.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | namespace bftevent { 23 | 24 | static const char* BFTEvent_method_names[] = { 25 | "/bftevent.BFTEvent/informEvent", 26 | }; 27 | 28 | std::unique_ptr< BFTEvent::Stub> BFTEvent::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) { 29 | (void)options; 30 | std::unique_ptr< BFTEvent::Stub> stub(new BFTEvent::Stub(channel, options)); 31 | return stub; 32 | } 33 | 34 | BFTEvent::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) 35 | : channel_(channel), rpcmethod_informEvent_(BFTEvent_method_names[0], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel) 36 | {} 37 | 38 | ::grpc::Status BFTEvent::Stub::informEvent(::grpc::ClientContext* context, const ::bftevent::EventInform& request, ::bftevent::EventReply* response) { 39 | return ::grpc::internal::BlockingUnaryCall< ::bftevent::EventInform, ::bftevent::EventReply, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_informEvent_, context, request, response); 40 | } 41 | 42 | void BFTEvent::Stub::async::informEvent(::grpc::ClientContext* context, const ::bftevent::EventInform* request, ::bftevent::EventReply* response, std::function f) { 43 | ::grpc::internal::CallbackUnaryCall< ::bftevent::EventInform, ::bftevent::EventReply, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_informEvent_, context, request, response, std::move(f)); 44 | } 45 | 46 | void BFTEvent::Stub::async::informEvent(::grpc::ClientContext* context, const ::bftevent::EventInform* request, ::bftevent::EventReply* response, ::grpc::ClientUnaryReactor* reactor) { 47 | ::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_informEvent_, context, request, response, reactor); 48 | } 49 | 50 | ::grpc::ClientAsyncResponseReader< ::bftevent::EventReply>* BFTEvent::Stub::PrepareAsyncinformEventRaw(::grpc::ClientContext* context, const ::bftevent::EventInform& request, ::grpc::CompletionQueue* cq) { 51 | return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::bftevent::EventReply, ::bftevent::EventInform, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_informEvent_, context, request); 52 | } 53 | 54 | ::grpc::ClientAsyncResponseReader< ::bftevent::EventReply>* BFTEvent::Stub::AsyncinformEventRaw(::grpc::ClientContext* context, const ::bftevent::EventInform& request, ::grpc::CompletionQueue* cq) { 55 | auto* result = 56 | this->PrepareAsyncinformEventRaw(context, request, cq); 57 | result->StartCall(); 58 | return result; 59 | } 60 | 61 | BFTEvent::Service::Service() { 62 | AddMethod(new ::grpc::internal::RpcServiceMethod( 63 | BFTEvent_method_names[0], 64 | ::grpc::internal::RpcMethod::NORMAL_RPC, 65 | new ::grpc::internal::RpcMethodHandler< BFTEvent::Service, ::bftevent::EventInform, ::bftevent::EventReply, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>( 66 | [](BFTEvent::Service* service, 67 | ::grpc::ServerContext* ctx, 68 | const ::bftevent::EventInform* req, 69 | ::bftevent::EventReply* resp) { 70 | return service->informEvent(ctx, req, resp); 71 | }, this))); 72 | } 73 | 74 | BFTEvent::Service::~Service() { 75 | } 76 | 77 | ::grpc::Status BFTEvent::Service::informEvent(::grpc::ServerContext* context, const ::bftevent::EventInform* request, ::bftevent::EventReply* response) { 78 | (void) context; 79 | (void) request; 80 | (void) response; 81 | return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 82 | } 83 | 84 | 85 | } // namespace bftevent 86 | 87 | -------------------------------------------------------------------------------- /spec/cpp/README.md: -------------------------------------------------------------------------------- 1 | # This is libbft on C++ 2 | 3 | Trying to stick to these rules: 4 | https://www.acodersjourney.com/top-25-cplusplus-api-design-mistakes-and-how-to-avoid-them/ 5 | 6 | ## NEW: basic build 7 | 8 | Just type `make` (it will use Bazel build). 9 | 10 | Or execute: `bazel build ... --config clang-tidy` 11 | 12 | You can remove `--config clang-tidy` to prevent strict checking, but it's necessary to keep it for contributions. 13 | 14 | ## OLD: Basic build 15 | 16 | Just enter `build` and use cmake. 17 | 18 | ``` 19 | cd spec/cpp/build 20 | cmake .. 21 | make 22 | ``` 23 | 24 | To quickly test it: 25 | ``` 26 | cd build 27 | ./app_test 28 | ``` 29 | 30 | Quick test using RPC: 31 | ``` 32 | make cpp && ./spec/cpp/bin/runRPCtest.sh 33 | ``` 34 | 35 | This should be equivalent to (after manually building on `bftevents-grpc`): 36 | ``` 37 | (cd spec/cpp/build/src && c++ -g -Wall -Ofast -lpthread -L/usr/local/lib -lprotobuf -lgrpc++ -lgrpc++_reflection -dl `pwd`/../../src/bftevents-grpc/bftevent.grpc.pb.o `pwd`/../../src/bftevents-grpc/bftevent.pb.o CMakeFiles/app_RPCtest.dir/mainRPC.cpp.o -o ../app_RPCtest) 38 | ``` 39 | 40 | This should build everything (even `bftevents-grpc`): 41 | ``` 42 | (cd spec/cpp/src/bftevents-grpc/ && protoc --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` bftevent.proto && protoc --cpp_out=. bftevent.proto && g++ -c -std=c++11 `pkg-config --cflags protobuf grpc` bftevent.pb.cc -L/usr/local/lib `pkg-config --libs protobuf grpc++` -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed -ldl -o bftevent.pb.o && g++ -c -std=c++11 `pkg-config --cflags protobuf grpc` bftevent.grpc.pb.cc -L/usr/local/lib `pkg-config --libs protobuf grpc++` -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed -ldl -o bftevent.grpc.pb.o && cd ../../src && g++ -g -Wall -Ofast mainRPC.cpp -o ../build/app_RPCtest ./bftevents-grpc/bftevent.grpc.pb.o ./bftevents-grpc/bftevent.pb.o -lpthread -L/usr/local/lib -lprotobuf -lgrpc++ -lgrpc++_reflection -ldl) 43 | ``` 44 | 45 | 46 | ## Elaborate build: gRPC and protobuf 47 | 48 | It's possible to build without gRPC, in a raw prototype version, however a real RPC can be build using gRPC and protobuf libraries. 49 | 50 | ### Dependencies for gRPC and protobuf 51 | 52 | First advice: if you have `protobuf` installed, remove it... it can cause strange conflicts with gRPC (unfortunately this took many hours from me). 53 | You can try to keep many versions, at your own risk, I had problems when managing `3.8.0` (from gRPC) together with `3.9.0` (local version). 54 | 55 | Official instructions to install gRPC: https://github.com/grpc/grpc/blob/master/BUILDING.md 56 | 57 | Things you may need (linux/ubuntu): 58 | ``` 59 | apt-get install build-essential autoconf libtool pkg-config 60 | apt-get install libgflags-dev libgtest-dev 61 | apt-get install zlib1g-dev libc-ares-dev 62 | apt-get install clang libc++-dev 63 | ``` 64 | 65 | My advice is (it will take around 3 GB from your computer, be prepared!): 66 | 67 | ``` 68 | cd ../../ 69 | git submodule update --init --recursive # get submodules for libbft (including grpc) 70 | cd spec/cpp/grpc/third_party/protobuf # install protobuf first 71 | git submodule update --init --recursive # get submodules (for protobuf) 72 | ./autogen.sh # generate installation files 73 | ./configure 74 | make 75 | make check # if this fails, something bad happened 76 | sudo make install # this will install system-wide protobuf and protoc (compiler) 77 | sudo ldconfig # refresh shared library cache 78 | cd ../.. # back to grpc 79 | make 80 | sudo make install # install grpc (and also /usr/local/bin/grpc_cpp_plugin) 81 | ``` 82 | 83 | ### Test it 84 | 85 | `protoc --version` should be `libprotoc 3.8.0` 86 | 87 | `ls -la /usr/local/lib/libproto*` should have `/usr/local/lib/libprotobuf.so -> libprotobuf.so.19.0.0` 88 | 89 | **Warning:** I had a **huge** problem when two versions existed here... `19.0.0` and `20.0.0`, so if this happens with you, get rid of `20.0.0` before it's too late! 90 | 91 | If you want to test `protobuf`: 92 | ``` 93 | cd grpc/third_party/protobuf/examples 94 | make clean 95 | make 96 | touch book.txt 97 | ./add_person_cpp book.txt # put some data 98 | ./list_people_cpp book.txt # get info 99 | ``` 100 | 101 | If you want to test `grpc`, do this simple test (_huge thanks to the authors of `https://medium.com/@andrewvetovitz/grpc-c-introduction-45a66ca9461f`_): 102 | ``` 103 | cd spec/cpp/test-grpc 104 | make clean 105 | make 106 | ./server # open ./client in other window 107 | ``` 108 | 109 | If this works, you're good to go! \o/ 110 | 111 | ## Final build: libbft + gRPC 112 | 113 | ``` 114 | cd spec/cpp/src/bftevents-grpc 115 | make 116 | ./bftserver # open ./bftclient in another terminal 117 | 118 | ``` 119 | 120 | 121 | ## License 122 | 123 | libbft is MIT License (although some specific components can have distinct licenses...) 124 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/machine/TimedStateMachine.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_MACHINE_TIMEDSTATEMACHINE_HPP_ 6 | #define INCLUDE_LIBBFT_MACHINE_TIMEDSTATEMACHINE_HPP_ 7 | 8 | // C 9 | #include 10 | 11 | // C++ 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // default clock 18 | #include 19 | #include 20 | #include 21 | 22 | namespace libbft { 23 | 24 | /** 25 | * requires a StateType (perhaps TransitionType) and a machine-specific Param 26 | * (context type) 27 | * @tparam StateType 28 | * @tparam Param 29 | */ 30 | template 31 | class TimedStateMachine : public IFPrintable { 32 | public: 33 | using TParam = std::shared_ptr; 34 | using TStateType = std::shared_ptr; 35 | 36 | /** state machine clock */ 37 | TClock clock; 38 | /** an identifier for itself */ 39 | MachineId me{0}; 40 | /** string name */ 41 | std::string name{""}; 42 | 43 | /** a Timed State Machine requires a global clock, and a unique personal 44 | * identifier */ 45 | explicit TimedStateMachine(TClock _clock = nullptr, 46 | MachineId _me = MachineId{0}, 47 | std::string _name = "") 48 | : clock(std::move(_clock)), me(std::move(_me)), name(std::move(_name)) { 49 | // clock must exist 50 | if (!clock) { 51 | clock = std::unique_ptr(new Clock()); 52 | } 53 | } 54 | 55 | /** 56 | * TODO: delete lot's of stuff 57 | * unique_ptr the clock perhaps? 58 | */ 59 | // virtual ~TimedStateMachine() = default; 60 | // virtual ~TimedStateMachine() {} 61 | 62 | /** triggers this, after state machine enters this state */ 63 | virtual void onEnterState(StateType& current, TParam p) = 0; 64 | 65 | /** 66 | * do global processing (return 'true' means 'break' process) 67 | * TODO: may use a global processing FLAG here, to break 68 | */ 69 | virtual bool beforeUpdateState(StateType& current, TParam p) = 0; 70 | 71 | /** 72 | * do global after-processing (return 'true' means 'break' process) 73 | * TODO: may use a global processing FLAG here, to break 74 | * @param current 75 | * @param p 76 | * @param updated 77 | * @return 78 | */ 79 | virtual bool afterUpdateState(StateType& current, TParam p, bool updated) { 80 | // sleeping a little bit if not updated (avoid wasting resources) 81 | if (!updated) usleep(1000 * 100); // 100 milli (in microsecs) 82 | return false; // do not 'break' 83 | } 84 | 85 | /** 86 | * get next state (null state is not allowed) 87 | * may return the same state, if nothing happened 88 | * @param current 89 | * @param p 90 | * @return 91 | */ 92 | virtual bool updateState(TStateType& current, TParam p) = 0; 93 | 94 | virtual bool isFinal(const StateType& current, TParam p) = 0; 95 | 96 | /** 97 | * initialize runtime states and timers, etc 98 | * if initial state is not given, it must provide one here 99 | * @param current 100 | * @param p 101 | * @return 102 | */ 103 | virtual TStateType initialize(TStateType current, TParam p) = 0; 104 | 105 | /** 106 | * launch when machine is finished 107 | * @param final 108 | * @param p 109 | */ 110 | virtual void OnFinished(const StateType& final, TParam p) = 0; 111 | 112 | /** 113 | * execute the state machine (returns final state, or nullptr if on failure) 114 | * @param initial 115 | * @param p 116 | * @return 117 | */ 118 | // virtual TStateType run(TStateType initial = nullptr, TParam p = nullptr) { 119 | virtual TStateType run(TStateType initial, TParam p) { 120 | // 121 | auto current = this->initialize(initial, p); 122 | // if no state given, abort 123 | if (!current) return nullptr; 124 | 125 | onEnterState(*current, p); 126 | 127 | // while current is not final 128 | while (!isFinal(*current, p)) { 129 | // preprocess stuff (watchdogs? global transitions?) 130 | if (beforeUpdateState(*current, p)) return nullptr; 131 | 132 | bool updated = updateState(current, p); 133 | if (updated) { 134 | onEnterState(*current, p); 135 | } 136 | 137 | // post-process stuff (sleep if not evolving, etc) 138 | if (afterUpdateState(*current, p, updated)) return nullptr; 139 | } 140 | 141 | OnFinished(*current, p); 142 | 143 | return current; 144 | } 145 | 146 | std::string toStringFormat(StringFormat format) override { 147 | std::stringstream ss; 148 | if (format == StringFormat::Graphviz) { 149 | } else { 150 | // standard text 151 | 152 | ss << "TSM {"; 153 | ss << "#id = " << me.id << ";"; 154 | ss << "clock = " << clock->toString() << ";"; 155 | ss << "}"; 156 | } 157 | return ss.str(); 158 | } 159 | }; 160 | 161 | } // namespace libbft 162 | 163 | #endif // INCLUDE_LIBBFT_MACHINE_TIMEDSTATEMACHINE_HPP_ 164 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/rpc-replicated/RPCMachineContext.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_RPC_REPLICATED_RPCMACHINECONTEXT_HPP_ 6 | #define INCLUDE_LIBBFT_RPC_REPLICATED_RPCMACHINECONTEXT_HPP_ 7 | 8 | // system includes 9 | #include 10 | #include 11 | #include 12 | 13 | // libbft includes 14 | 15 | // Prototype? 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace libbft { 23 | 24 | template 25 | struct RPCMachineContext { 26 | using TParam = std::unique_ptr; 27 | 28 | /** my params */ 29 | TParam params; 30 | /** my id */ 31 | int me; 32 | /** the world I can connect to */ 33 | std::vector world; 34 | 35 | private: 36 | /** my events */ 37 | Events events; 38 | /** regular delay (in MS): for testing purposes only (fork simulation) */ 39 | int testRegularDelayMS{0}; 40 | /** regular drop rate: for testing purposes only (fork simulation) */ 41 | double testDropRate{0.0}; 42 | /** machine random */ 43 | std::mt19937 generator; 44 | // std::uniform_real_distribution dis(0.0, 1.0); 45 | // double randomRealBetweenZeroAndOne = dis(generator); 46 | 47 | public: 48 | RPCMachineContext(TParam _params, int _me, 49 | std::vector _world, int seed = 99) 50 | : params(std::move(_params)), 51 | me(_me), 52 | world(std::move(_world)), 53 | generator(seed) {} 54 | 55 | /** 56 | * just to test purposes: force a delay on message passing 57 | * @param _testRegularDelayMS 58 | */ 59 | void testSetRegularDelay(int _testRegularDelayMS) { 60 | this->testRegularDelayMS = _testRegularDelayMS; 61 | } 62 | 63 | /** 64 | * just to test purposes: force a drop rate on message passing 65 | * @param _dropRate 66 | */ 67 | void testSetDropRate(double _dropRate) { this->testDropRate = _dropRate; } 68 | 69 | /** 70 | * Different from MultiContext... in this one, I can only access my own events 71 | * @param name 72 | * @param eventArgs 73 | * @return 74 | */ 75 | bool hasEvent(std::string name, std::vector eventArgs) { 76 | for (unsigned i = 0; i < events.size(); i++) { 77 | if (events[i]->isActivated(name, eventArgs)) return true; 78 | } 79 | return false; 80 | } 81 | 82 | std::vector getEvents() { return events; } 83 | 84 | void registerEvent(TEvent event) { 85 | std::cout << "RPCMachineContext registering event '" << event->toString() 86 | << "'" << std::endl; 87 | events.push_back(event); 88 | } 89 | 90 | /** 91 | * this is used to add events that come from any other sources, and get 92 | * pending. TODO(@igormcoelho): is this the best design? 93 | * @param pendingEvents 94 | */ 95 | void addEvents(Events pendingEvents) { 96 | // do manual insertion of events, because of print messages 97 | for (auto& pendingEvent : pendingEvents) { 98 | registerEvent(pendingEvent); 99 | } 100 | // events.insert(events.begin() + 0, pendingEvents.begin(), 101 | // pendingEvents.end()); 102 | } 103 | 104 | void broadcast(std::string event, std::vector eventArgs) { 105 | std::cout << " -~-~:broadcasting event '" << event 106 | << "' with params = " << eventArgs.size() << std::endl; 107 | for (unsigned i = 0; i < world.size(); i++) { 108 | std::cout << " -~-~:sending to " << i << " event '" << event 109 | << "' with params = " << eventArgs.size() << std::endl; 110 | // if send with delay 111 | int delay = 0; 112 | if (testRegularDelayMS > 0) { 113 | std::uniform_int_distribution<> dis(0, testRegularDelayMS); 114 | delay = dis(this->generator); 115 | } 116 | 117 | if (testDropRate > 0.0) { 118 | std::uniform_real_distribution dis(0.0, 1.0); 119 | double x = dis(this->generator); 120 | if (x < testDropRate) { 121 | std::cout << "LOST EVENT '" << event << "' for " << i << std::endl; 122 | continue; // do not send message 123 | } 124 | } 125 | 126 | world[i]->informEvent(me, event, eventArgs, delay); 127 | } 128 | } 129 | 130 | /* 131 | bool launchTimedEvent(ScheduledEvent se) 132 | { 133 | if (se.machineTo.id != me) 134 | return false; // not for me 135 | 136 | // launch TimedEvent, from -1 (broadcast/system style..). Could be from 137 | 'me' too. events.push_back(new TimedEvent(se.countdown, se.name, 138 | MachineId(-1), se.eventParams)); return true; 139 | } 140 | */ 141 | 142 | void addEventFromRPC(std::string _name, MachineId _from, 143 | std::vector _parameters, int delay = 0) { 144 | if (delay == 0) { 145 | registerEvent( 146 | std::shared_ptr(new Event(_name, _from, _parameters))); 147 | } else { 148 | registerEvent(std::shared_ptr( 149 | new TimedEvent(delay / 1000.0, _name, _from, _parameters))); 150 | } 151 | } 152 | }; 153 | 154 | } // namespace libbft 155 | 156 | #endif // INCLUDE_LIBBFT_RPC_REPLICATED_RPCMACHINECONTEXT_HPP_ 157 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/events/Event.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_EVENTS_EVENT_HPP_ 6 | #define INCLUDE_LIBBFT_EVENTS_EVENT_HPP_ 7 | 8 | // system includes 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // standard Transition 18 | #include 19 | #include 20 | // #include "State.h" 21 | 22 | namespace libbft { 23 | 24 | /* 25 | class EventParameter 26 | { 27 | protected: 28 | // type of EventParameter class (usually, empty... is string) 29 | string type; 30 | // content stored on event parameter (as string). other implementations can 31 | ignore this field. string content; 32 | 33 | public: 34 | EventParameter(string _type = "", string _content = "") 35 | : type(_type) 36 | , content(_content) 37 | { 38 | } 39 | 40 | EventParameter(string _type = "", int _content = 0) 41 | : type(_type) 42 | { 43 | stringstream ss; 44 | ss << _content; 45 | content = ss.str(); 46 | } 47 | 48 | string getType() const 49 | { 50 | return type; 51 | } 52 | 53 | // default implementation of equals uses toString() visualization 54 | virtual bool equals(const EventParameter& other) 55 | { 56 | cout << "equals?? content = " << content << " -> " << other.toString() << 57 | endl; return (type == other.getType()) && (this->toString() == 58 | other.toString()); 59 | } 60 | 61 | virtual string toString() const 62 | { 63 | stringstream ss; 64 | ss << "EventParameter:{type=" << type << ";content='" << content << "'}"; 65 | return ss.str(); 66 | } 67 | }; 68 | */ 69 | 70 | /** 71 | * this Event class is mostly used for simulation 72 | * it indicates a type for event (for matching), a name for printing, a source 73 | * 'from', and possibly a countdown Timer 74 | */ 75 | class Event { 76 | protected: 77 | /** event name (used to matching) */ 78 | std::string name; 79 | /** event called from machine 'from'. If -1, it came from a broadcast (or 80 | * machine itself) */ 81 | MachineId from; 82 | /** extra parameter to compare */ 83 | std::vector parameters; 84 | 85 | public: 86 | explicit Event( 87 | std::string _name, MachineId _from = MachineId(-1), 88 | std::vector _parameters = std::vector(0)) 89 | : name(std::move(_name)), 90 | from(std::move(_from)), 91 | parameters(std::move(_parameters)) {} 92 | 93 | virtual ~Event() = default; 94 | 95 | /** 96 | * TODO: receive a lambda for special validation and filtering here? 97 | * perhaps... (bool matching?) 98 | * @param _name 99 | * @param pattern 100 | * @return 101 | */ 102 | virtual bool isActivated(std::string _name, 103 | std::vector pattern) const { 104 | // return (name == _name) && checkEventArgs(parameters, pattern, matching); 105 | return (name == _name) && (parameters == pattern); 106 | } 107 | 108 | virtual MachineId getFrom() const { return from; } 109 | 110 | /* 111 | static bool checkEventArgs(vector myArgs, vector pattern, bool 112 | matching) 113 | { 114 | // if not matching, a raw comparison is made (no wildcards) 115 | if (!matching) 116 | return myArgs == pattern; 117 | else { 118 | // perform matching. wilcard * is supported here.. suppressing event 119 | if (myArgs.size() != pattern.size()) 120 | return false; 121 | for (unsigned i = 0; myArgs.size(); i++) { 122 | if (pattern[i] == "*") 123 | continue; // always accepts 124 | else if (pattern[i] != myArgs[i]) 125 | return false; 126 | } 127 | return true; 128 | } 129 | } 130 | */ 131 | 132 | virtual std::string toString() const { 133 | std::stringstream ss; 134 | ss << "Event [args=" << parameters.size() << "] " << name << "("; 135 | auto comma = ""; 136 | for (auto& parameter : this->parameters) { 137 | ss << comma << parameter; 138 | comma = ","; 139 | } 140 | ss << ")"; 141 | return ss.str(); 142 | } 143 | }; 144 | 145 | using TEvent = std::shared_ptr; 146 | using Events = std::vector; 147 | 148 | class TimedEvent : public Event { 149 | protected: 150 | /** Timer sent in countdown mode */ 151 | TTimer timer; 152 | 153 | public: 154 | TimedEvent(double countdown, std::string _name, 155 | MachineId _from = MachineId(-1), 156 | std::vector _parameters = std::vector(0)) 157 | : Event(std::move(_name), std::move(_from), std::move(_parameters)) { 158 | timer = std::unique_ptr((new Timer())->init(countdown)); 159 | } 160 | 161 | ~TimedEvent() override = default; 162 | 163 | bool isActivated(std::string _name, 164 | std::vector pattern) const override { 165 | return (this->name == _name) && (timer->expired()) && 166 | (this->parameters == pattern); 167 | } 168 | 169 | std::string toString() const override { 170 | std::stringstream ss; 171 | ss << "TimedEvent " << this->name << "("; 172 | auto comma = ""; 173 | for (auto& parameter : this->parameters) { 174 | ss << comma << parameter; 175 | comma = ","; 176 | } 177 | // default suffix '()' (empty parameters) 178 | ss << ") " << (timer->expired() ? "expired" : "notexpired") << " " 179 | << timer->remainingTime(); 180 | return ss.str(); 181 | } 182 | }; 183 | 184 | } // namespace libbft 185 | 186 | #endif // INCLUDE_LIBBFT_EVENTS_EVENT_HPP_ 187 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libbft 2 | 3 |

4 | 5 | Current TravisCI build status. 6 | 7 | 8 | Current neo version. 9 | 10 | 11 | Coverage Status 12 | 13 | 14 | Current total lines. 15 | 16 | 17 | License. 18 | 19 |

20 | 21 | ## A lightweight and multi-language library collection for byzantine fault tolerance 22 | 23 | Byzantine Fault Tolerance (BFT) is the core mechanism for building attack-resistant and self-adaptive systems. 24 | It is a widespread concept since the proposal of a Practical BFT (by Miguel Castro and Barbara Liskov), with multiple implementations nowadays (such as dBFT on Neo Blockchain). 25 | This project intends to allow easy implementation of BFT protocols on C++ and multiple languages (portability for cross-language is fundamental!). 26 | 27 | It is intended to be also used as the official dBFT for [neopt](https://github.com/neoresearch/neopt), a C++ implementation of Neo Blockchain components, focused on portability. 28 | It may also help the development of other ecosystem tools, protocols, and even develop other blockchain and fail-safe storage technologies. 29 | 30 | 31 | ## Build Instructions 32 | 33 | Currently, C++ spec is the reference one. After that, focus will be on Go, Python and C# ports. 34 | 35 | For stability, try with VSCode and DevContainers Extension (see .devcontainers and Dockerfile). 36 | 37 | ### Bazel is the first try (for C++) 38 | 39 | If using DevContainers, you already have Bazel (otherwise, it's quite simple to install it). 40 | Just type `make`. 41 | 42 | ### CMake is the second official one (for C++) 43 | 44 | Currently, CMake is likely broken, due to advances in grpc/protobuf projects. 45 | We will try to fix it as soon as possible. 46 | 47 | If using DevContainers, some working grpc version is already available (just need to fix CMakeLists.txt). 48 | Remember to: `ln -s /opt/bin/grpc_cpp_plugin /usr/local/bin/grpc_cpp_plugin` 49 | 50 | #### Old instructions 51 | `cmake` is your friend! Please install latest version (you can use Snappy to ensure that!): 52 | ```sh 53 | sudo snap install cmake --classic 54 | ``` 55 | Image on DevContainer already manually a supported cmake version (installed on `/opt` together with grpc) 56 | 57 | ### Builds for C++ 58 | Existing frameworks use high-level languages that may not be suitable for very lightweight architectures, 59 | such as microcontrollers with very limited computing capabilities. 60 | 61 | C/C++ is interoperable with nearly all existing languages, so the idea is to provide modules that can be 62 | reused on other projects (on other languages too). 63 | 64 | On debian-based systems (or ubuntu), just type: 65 | ```sh 66 | make cpp && ./spec/cpp/build/app_test 67 | ``` 68 | 69 | Getting submodules: `git submodule update --init --recursive` and `git pull --recurse-submodules`. 70 | 71 | Installing graphviz: `sudo apt install graphviz` 72 | 73 | #### tests for C++ 74 | 75 | It will also configure test library (as long as you cloned this project with `--submodules` too). 76 | To test, just run `make test`. 77 | 78 | **Note:** tests are still under development. 79 | 80 | #### C++ Standard 81 | Currently, C++11 is adopted, in order to keep the best compatibility between conversors and compilers. However, it is recommended to migrate to C++17 as soon as possible, if this does not break compatibility with any existing modules and tools. 82 | 83 | Let's please follow the [CppCoreGuidelines](https://github.com/isocpp/CppCoreGuidelines). 84 | 85 | #### vscode IDE 86 | If using vscode IDE, it is recommended to install the following extensions: 87 | * C/C++ (currently 0.23.0-insiders2) 88 | * C++ Intellisense (currently 0.2.2) 89 | * GoogleTest Adapter (currently 1.8.3) 90 | 91 | #### C++ Format Style 92 | The currently adopted style for C++ is `Mozilla`, with indentation level set to 3. 93 | Recommended configuration for vscode: 94 | ```json 95 | { 96 | "[cpp]": { 97 | "editor.tabSize" : 3, 98 | "editor.detectIndentation": false 99 | }, 100 | "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle : Mozilla , ColumnLimit : 0, IndentWidth: 3, AccessModifierOffset: -3}" 101 | } 102 | ``` 103 | 104 | #### Variable Naming Style 105 | 106 | Let's please follow the [CppCoreGuidelines](https://github.com/isocpp/CppCoreGuidelines), and as usual, classic CamelCase `C++` naming is welcome :) 107 | 108 | ### Builds for Go 109 | 110 | Still under development. 111 | 112 | ### Builds for C# 113 | 114 | Still under development. 115 | 116 | ### Builds for Python 117 | 118 | Still under development. 119 | 120 | ## Citation 121 | 122 | Cite this in your paper as: 123 | 124 | ```bibtex 125 | @article{libbft2019, 126 | author = {Rodolfo Pereira Araujo and Igor Coelho and Luiz Satoru Ochi and Vitor Nazario Coelho}, 127 | year = {2019}, 128 | month = {10}, 129 | title = "LibBFT: A High-Performace Timed Automata Library Collection for Byzantine Fault Tolerance", 130 | doi = "10.1109/SBAC-PAD.2019.00045", 131 | isbn = "978-1-7281-4194-7", 132 | issn = "2643-3001", 133 | publisher = "IEEE", 134 | series = "2019 31st International Symposium on Computer Architecture and High Performance Computing (SBAC-PAD)" 135 | } 136 | ``` 137 | 138 | ### License 139 | 140 | Code follows `MIT License`. 141 | -------------------------------------------------------------------------------- /spec/cpp/src/p2p/bftp2p.grpc.pb.cc: -------------------------------------------------------------------------------- 1 | // Generated by the gRPC C++ plugin. 2 | // If you make any local change, they will be lost. 3 | // source: bftp2p.proto 4 | 5 | #include "bftp2p.pb.h" 6 | #include "bftp2p.grpc.pb.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | namespace p2p { 23 | 24 | static const char* P2P_method_names[] = { 25 | "/p2p.P2P/register_me", 26 | "/p2p.P2P/update_services", 27 | }; 28 | 29 | std::unique_ptr< P2P::Stub> P2P::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) { 30 | (void)options; 31 | std::unique_ptr< P2P::Stub> stub(new P2P::Stub(channel, options)); 32 | return stub; 33 | } 34 | 35 | P2P::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) 36 | : channel_(channel), rpcmethod_register_me_(P2P_method_names[0], options.suffix_for_stats(),::grpc::internal::RpcMethod::SERVER_STREAMING, channel) 37 | , rpcmethod_update_services_(P2P_method_names[1], options.suffix_for_stats(),::grpc::internal::RpcMethod::BIDI_STREAMING, channel) 38 | {} 39 | 40 | ::grpc::ClientReader< ::p2p::Url>* P2P::Stub::register_meRaw(::grpc::ClientContext* context, const ::p2p::Url& request) { 41 | return ::grpc::internal::ClientReaderFactory< ::p2p::Url>::Create(channel_.get(), rpcmethod_register_me_, context, request); 42 | } 43 | 44 | void P2P::Stub::async::register_me(::grpc::ClientContext* context, const ::p2p::Url* request, ::grpc::ClientReadReactor< ::p2p::Url>* reactor) { 45 | ::grpc::internal::ClientCallbackReaderFactory< ::p2p::Url>::Create(stub_->channel_.get(), stub_->rpcmethod_register_me_, context, request, reactor); 46 | } 47 | 48 | ::grpc::ClientAsyncReader< ::p2p::Url>* P2P::Stub::Asyncregister_meRaw(::grpc::ClientContext* context, const ::p2p::Url& request, ::grpc::CompletionQueue* cq, void* tag) { 49 | return ::grpc::internal::ClientAsyncReaderFactory< ::p2p::Url>::Create(channel_.get(), cq, rpcmethod_register_me_, context, request, true, tag); 50 | } 51 | 52 | ::grpc::ClientAsyncReader< ::p2p::Url>* P2P::Stub::PrepareAsyncregister_meRaw(::grpc::ClientContext* context, const ::p2p::Url& request, ::grpc::CompletionQueue* cq) { 53 | return ::grpc::internal::ClientAsyncReaderFactory< ::p2p::Url>::Create(channel_.get(), cq, rpcmethod_register_me_, context, request, false, nullptr); 54 | } 55 | 56 | ::grpc::ClientReaderWriter< ::p2p::Url, ::p2p::Url>* P2P::Stub::update_servicesRaw(::grpc::ClientContext* context) { 57 | return ::grpc::internal::ClientReaderWriterFactory< ::p2p::Url, ::p2p::Url>::Create(channel_.get(), rpcmethod_update_services_, context); 58 | } 59 | 60 | void P2P::Stub::async::update_services(::grpc::ClientContext* context, ::grpc::ClientBidiReactor< ::p2p::Url,::p2p::Url>* reactor) { 61 | ::grpc::internal::ClientCallbackReaderWriterFactory< ::p2p::Url,::p2p::Url>::Create(stub_->channel_.get(), stub_->rpcmethod_update_services_, context, reactor); 62 | } 63 | 64 | ::grpc::ClientAsyncReaderWriter< ::p2p::Url, ::p2p::Url>* P2P::Stub::Asyncupdate_servicesRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, void* tag) { 65 | return ::grpc::internal::ClientAsyncReaderWriterFactory< ::p2p::Url, ::p2p::Url>::Create(channel_.get(), cq, rpcmethod_update_services_, context, true, tag); 66 | } 67 | 68 | ::grpc::ClientAsyncReaderWriter< ::p2p::Url, ::p2p::Url>* P2P::Stub::PrepareAsyncupdate_servicesRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq) { 69 | return ::grpc::internal::ClientAsyncReaderWriterFactory< ::p2p::Url, ::p2p::Url>::Create(channel_.get(), cq, rpcmethod_update_services_, context, false, nullptr); 70 | } 71 | 72 | P2P::Service::Service() { 73 | AddMethod(new ::grpc::internal::RpcServiceMethod( 74 | P2P_method_names[0], 75 | ::grpc::internal::RpcMethod::SERVER_STREAMING, 76 | new ::grpc::internal::ServerStreamingHandler< P2P::Service, ::p2p::Url, ::p2p::Url>( 77 | [](P2P::Service* service, 78 | ::grpc::ServerContext* ctx, 79 | const ::p2p::Url* req, 80 | ::grpc::ServerWriter<::p2p::Url>* writer) { 81 | return service->register_me(ctx, req, writer); 82 | }, this))); 83 | AddMethod(new ::grpc::internal::RpcServiceMethod( 84 | P2P_method_names[1], 85 | ::grpc::internal::RpcMethod::BIDI_STREAMING, 86 | new ::grpc::internal::BidiStreamingHandler< P2P::Service, ::p2p::Url, ::p2p::Url>( 87 | [](P2P::Service* service, 88 | ::grpc::ServerContext* ctx, 89 | ::grpc::ServerReaderWriter<::p2p::Url, 90 | ::p2p::Url>* stream) { 91 | return service->update_services(ctx, stream); 92 | }, this))); 93 | } 94 | 95 | P2P::Service::~Service() { 96 | } 97 | 98 | ::grpc::Status P2P::Service::register_me(::grpc::ServerContext* context, const ::p2p::Url* request, ::grpc::ServerWriter< ::p2p::Url>* writer) { 99 | (void) context; 100 | (void) request; 101 | (void) writer; 102 | return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 103 | } 104 | 105 | ::grpc::Status P2P::Service::update_services(::grpc::ServerContext* context, ::grpc::ServerReaderWriter< ::p2p::Url, ::p2p::Url>* stream) { 106 | (void) context; 107 | (void) stream; 108 | return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 109 | } 110 | 111 | 112 | } // namespace p2p 113 | 114 | -------------------------------------------------------------------------------- /spec/cpp/include/libbft/single/Transition.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (C) 2019-2022 - LibBFT developers 3 | // https://github.com/neoresearch/libbft 4 | 5 | #ifndef INCLUDE_LIBBFT_SINGLE_TRANSITION_HPP_ 6 | #define INCLUDE_LIBBFT_SINGLE_TRANSITION_HPP_ 7 | 8 | // system includes 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // libbft 19 | // #include "State.h" // forward declaration 20 | #include 21 | #include 22 | #include 23 | //// #include "Event.hpp" 24 | 25 | // standard Transition 26 | // Every transition may or may not be a timed transition 27 | 28 | namespace libbft { 29 | 30 | /** 31 | * forward declaration 32 | * @tparam Param 33 | */ 34 | template 35 | class State; 36 | 37 | /** 38 | * Condition has a name and boolean function (should not affect any param or 39 | * Timer) 40 | * @tparam Param 41 | */ 42 | template 43 | struct Condition { 44 | using TParam = std::shared_ptr; 45 | using TimedFunctionType = 46 | std::function; 47 | 48 | std::string name = "true"; 49 | /** TODO Should not we avoid exposing it like this? */ 50 | TimedFunctionType timedFunction = 51 | [](const Timer& t, TParam p, const MachineId&) -> bool { return true; }; 52 | 53 | Condition(std::string _name, TimedFunctionType _timedFunction) 54 | : name(std::move(_name)), timedFunction(_timedFunction) {} 55 | 56 | std::string toString() const { return name; } 57 | }; 58 | 59 | /** 60 | * Action is a Condition that always returns true (can affect params and Timer) 61 | * @tparam Param 62 | */ 63 | template 64 | struct Action { 65 | using TParam = std::shared_ptr; 66 | using TimedActionType = std::function; 67 | 68 | std::string name = "nop"; 69 | /** TODO Should not we avoid exposing it like this? */ 70 | TimedActionType timedAction = [](Timer&, TParam, const MachineId&) -> void {}; 71 | 72 | Action(std::string _name, TimedActionType _timedAction) 73 | : name(std::move(_name)), timedAction(_timedAction) {} 74 | 75 | std::string toString() const { return name; } 76 | }; 77 | 78 | template 79 | class Transition { 80 | public: 81 | using TState = sptr>; 82 | using TParam = sptr; 83 | 84 | private: 85 | /** state to go after executing this transition */ 86 | TState to; 87 | /** transition name (not really useful) */ 88 | std::string name; 89 | /** boolean conditions (if all are valid, transition is valid) */ 90 | std::vector> conditions; 91 | /** actions to be performed during Transition execution (reset timers, etc) */ 92 | std::vector> actions; 93 | 94 | public: 95 | /** 96 | * a Transition goes to some state 'to' 97 | * @param _to 98 | * @param _name 99 | */ 100 | explicit Transition(TState _to, std::string _name = "") 101 | : to{_to}, name{std::move(_name)} { 102 | assert(to != nullptr); 103 | } 104 | 105 | virtual ~Transition() = default; 106 | 107 | /** 108 | * add a new boolean Condition (returns pointer to itself, to allow cascading 109 | * effect) 110 | * @param c 111 | * @return 112 | */ 113 | Transition* add(Condition c) { 114 | conditions.push_back(c); 115 | return this; // allow chaining effect 116 | } 117 | 118 | /** 119 | * add a new Action (returns pointer to itself, to allow cascading effect) 120 | * @param a 121 | * @return 122 | */ 123 | Transition* add(Action a) { 124 | actions.push_back(a); 125 | return this; // allow chaining effect 126 | } 127 | 128 | /** 129 | * returns 'true' if all conditions are valid (or no conditions are required) 130 | * @param timer 131 | * @param p 132 | * @param me 133 | * @return 134 | */ 135 | virtual bool isValid(const Timer& timer, TParam p, MachineId me) { 136 | for (auto& condition : conditions) { 137 | if (!condition.timedFunction(timer, p, me)) { 138 | return false; 139 | } 140 | } 141 | return true; 142 | } 143 | 144 | /** 145 | * execute transition and returns the next State 146 | * @param timer 147 | * @param p 148 | * @param me 149 | * @return 150 | */ 151 | virtual TState execute(Timer& timer, TParam p, MachineId me) { 152 | for (auto& action : actions) { 153 | action.timedAction(timer, p, me); 154 | } 155 | return to; 156 | } 157 | 158 | /** 159 | * converts to string 160 | * @param format 161 | * @return 162 | */ 163 | 164 | std::string toString() const { return toStringFormat(StringFormat::Default); } 165 | 166 | std::string toStringFormat(StringFormat format) const { 167 | std::stringstream ss; 168 | if (format == StringFormat::Graphviz) { 169 | ss << " -> " << this->to->name; 170 | ss << " [ label = \""; 171 | 172 | bool first = true; 173 | for (unsigned i = 0; i < conditions.size(); i++) { 174 | if (!first) ss << " \\n "; 175 | ss << conditions[i].name << " "; 176 | first = false; 177 | } 178 | for (unsigned i = 0; i < actions.size(); i++) { 179 | if (!first) ss << " \\n "; 180 | 181 | ss << actions[i].name << " "; 182 | first = false; 183 | } 184 | ss << "\"];"; 185 | return ss.str(); 186 | } else { 187 | // default print 188 | ss << "t() => {name = '" << name << "',"; 189 | ss << "to='" << to->toStringR(false) << "',"; 190 | ss << "conditions=["; 191 | for (unsigned i = 0; i < conditions.size(); i++) 192 | ss << conditions[i].toString() << ";"; 193 | ss << "], "; 194 | ss << "actions=["; 195 | for (unsigned i = 0; i < actions.size(); i++) 196 | ss << actions[i].toString() << ";"; 197 | ss << "], "; 198 | ss << "'}"; 199 | } 200 | return ss.str(); 201 | } 202 | }; 203 | 204 | } // namespace libbft 205 | 206 | // forward declaration 207 | #include 208 | 209 | #endif // INCLUDE_LIBBFT_SINGLE_TRANSITION_HPP_ 210 | -------------------------------------------------------------------------------- /spec/go/src/replicated/replicated_stsm.go: -------------------------------------------------------------------------------- 1 | package replicated 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/NeoResearch/libbft/src/events" 7 | "github.com/NeoResearch/libbft/src/machine" 8 | "github.com/NeoResearch/libbft/src/single" 9 | "github.com/NeoResearch/libbft/src/timing" 10 | "github.com/NeoResearch/libbft/src/util" 11 | "strings" 12 | ) 13 | 14 | type MultiState []single.State 15 | 16 | type ReplicatedSTSM interface { 17 | // superclass 18 | getTimedStateMachine() machine.TimedStateMachine 19 | // get / set 20 | GetClock() timing.Clock 21 | GetMe() int 22 | GetName() string 23 | // methods 24 | OnEnterState(current single.State, param single.Param) 25 | BeforeUpdateState(current single.State, param single.Param) bool 26 | AfterUpdateState(current single.State, param single.Param, updated bool) bool 27 | UpdateState(current single.State, param single.Param) (single.State, bool, error) 28 | IsFinal(current single.State, param single.Param) bool 29 | Initialize(current single.State, param single.Param) single.State 30 | OnFinished(current single.State, param single.Param) 31 | Run(current single.State, param single.Param) (single.State, error) 32 | 33 | // get / set 34 | GetMachines() []machine.SingleTimerStateMachine 35 | SetMachines(machines []machine.SingleTimerStateMachine) 36 | GetScheduledEvents() []ScheduledEvent 37 | GetWatchdog() timing.Timer 38 | SetWatchdog(timer timing.Timer) 39 | AddScheduleEvent(countdown float64, machine int, name string, eventParams []string) 40 | RegisterMachine(machine machine.SingleTimerStateMachine) 41 | LaunchScheduleEvents(param MultiContext) error 42 | InitializeMulti(current MultiState, param MultiContext) (MultiState, error) 43 | IsFinalMulti(current MultiState, param MultiContext) bool 44 | UpdateStateMulti(current MultiState, param MultiContext) (MultiState, bool, error) 45 | OnEnterStateMulti(current MultiState, param MultiContext) 46 | BeforeUpdateStateMulti(current MultiState, param MultiContext) bool 47 | 48 | StringFormat(format string) string 49 | String() string 50 | } 51 | 52 | type ReplicatedSTSMService struct { 53 | timedStateMachine machine.TimedStateMachine 54 | machines []machine.SingleTimerStateMachine 55 | scheduledEvents []ScheduledEvent 56 | watchdog timing.Timer 57 | } 58 | 59 | func NewReplicatedSTSM(clock timing.Clock, me int, name string) ReplicatedSTSM { 60 | return NewReplicatedSTSMSizes(clock, me, name, 0, 0) 61 | } 62 | 63 | func NewReplicatedSTSMSizes(clock timing.Clock, me int, name string, numberOfMachines int, numberOfScheduledEvents int) ReplicatedSTSM { 64 | return &ReplicatedSTSMService{ 65 | machine.NewTimedStateMachine(clock, me, name), 66 | make([]machine.SingleTimerStateMachine, numberOfMachines), 67 | make([]ScheduledEvent, numberOfScheduledEvents), 68 | nil, 69 | } 70 | } 71 | 72 | func (r *ReplicatedSTSMService) getTimedStateMachine() machine.TimedStateMachine { 73 | return r.timedStateMachine 74 | } 75 | 76 | func (r *ReplicatedSTSMService) GetClock() timing.Clock { 77 | return r.getTimedStateMachine().GetClock() 78 | } 79 | 80 | func (r *ReplicatedSTSMService) GetMe() int { 81 | return r.getTimedStateMachine().GetMe() 82 | } 83 | 84 | func (r *ReplicatedSTSMService) GetName() string { 85 | return r.getTimedStateMachine().GetName() 86 | } 87 | 88 | func (r *ReplicatedSTSMService) OnEnterState(current single.State, param single.Param) { 89 | r.getTimedStateMachine().OnEnterState(current, param) 90 | } 91 | 92 | func (r *ReplicatedSTSMService) BeforeUpdateState(current single.State, param single.Param) bool { 93 | return r.getTimedStateMachine().BeforeUpdateState(current, param) 94 | } 95 | 96 | func (r *ReplicatedSTSMService) AfterUpdateState(current single.State, param single.Param, updated bool) bool { 97 | return r.getTimedStateMachine().AfterUpdateState(current, param, updated) 98 | } 99 | 100 | func (r *ReplicatedSTSMService) UpdateState(current single.State, param single.Param) (single.State, bool, error) { 101 | return r.getTimedStateMachine().UpdateState(current, param) 102 | } 103 | 104 | func (r *ReplicatedSTSMService) IsFinal(current single.State, param single.Param) bool { 105 | return r.getTimedStateMachine().IsFinal(current, param) 106 | } 107 | 108 | func (r *ReplicatedSTSMService) Initialize(current single.State, param single.Param) single.State { 109 | return r.getTimedStateMachine().Initialize(current, param) 110 | } 111 | 112 | func (r *ReplicatedSTSMService) OnFinished(current single.State, param single.Param) { 113 | fmt.Println() 114 | fmt.Println("=================") 115 | fmt.Println("finished machine!") 116 | fmt.Println("=================") 117 | } 118 | 119 | func (r *ReplicatedSTSMService) Run(current single.State, param single.Param) (single.State, error) { 120 | return r.getTimedStateMachine().Run(current, param) 121 | } 122 | 123 | func (r *ReplicatedSTSMService) StringFormat(format string) string { 124 | var sb strings.Builder 125 | if format == util.GraphivizFormat { 126 | } else { 127 | sb.WriteString("ReplicatedSTSM [") 128 | for _, machine := range r.GetMachines() { 129 | sb.WriteString(fmt.Sprintf("%v;", machine)) 130 | } 131 | sb.WriteString("]") 132 | 133 | } 134 | return sb.String() 135 | } 136 | 137 | func (r *ReplicatedSTSMService) String() string { 138 | return r.StringFormat("") 139 | } 140 | 141 | func (r *ReplicatedSTSMService) GetMachines() []machine.SingleTimerStateMachine { 142 | return r.machines 143 | } 144 | 145 | func (r *ReplicatedSTSMService) GetScheduledEvents() []ScheduledEvent { 146 | return r.scheduledEvents 147 | } 148 | 149 | func (r *ReplicatedSTSMService) GetWatchdog() timing.Timer { 150 | return r.watchdog 151 | } 152 | 153 | func (r *ReplicatedSTSMService) SetWatchdog(timer timing.Timer) { 154 | r.watchdog = timer 155 | } 156 | 157 | func (r *ReplicatedSTSMService) AddScheduleEvent(countdown float64, machine int, name string, eventParams []string) { 158 | r.scheduledEvents = append(r.scheduledEvents, NewScheduledEvent(name, eventParams, countdown, machine)) 159 | } 160 | 161 | func (r *ReplicatedSTSMService) RegisterMachine(machine machine.SingleTimerStateMachine) { 162 | r.machines = append(r.machines, machine) 163 | } 164 | 165 | func (r *ReplicatedSTSMService) LaunchScheduleEvents(param MultiContext) error { 166 | fmt.Println("launching scheduled events!") 167 | for _, event := range r.GetScheduledEvents() { 168 | if event.GetMachine() == -1 { 169 | return param.BroadcastEvent(events.NewTimedEvent(event.GetName(), -1, event.GetEventParams(), event.GetCountdown()), -1) 170 | } else { 171 | return param.SendToVmEvent(events.NewTimedEvent(event.GetName(), -1, event.GetEventParams(), event.GetCountdown()), event.GetMachine()) 172 | } 173 | } 174 | return nil 175 | } 176 | 177 | func (r *ReplicatedSTSMService) InitializeMulti(current MultiState, param MultiContext) (MultiState, error) { 178 | if current == nil { 179 | //current = new MultiState(machines.size(), nullptr); 180 | } 181 | if len(current) != len(r.GetMachines()) { 182 | return nil, errors.New("invalid number of states") 183 | } 184 | 185 | fmt.Println() 186 | fmt.Println("===========") 187 | fmt.Println("begin run()") 188 | fmt.Println("===========") 189 | 190 | fmt.Println("initializing multimachine") 191 | if r.GetWatchdog() != nil { 192 | r.GetWatchdog().Reset() 193 | } else { 194 | fmt.Println("No watchdog configured") 195 | } 196 | 197 | for i, machine := range r.GetMachines() { 198 | machine.Initialize(current[i], param) 199 | } 200 | 201 | return current, r.LaunchScheduleEvents(param) 202 | } 203 | 204 | func (r *ReplicatedSTSMService) IsFinalMulti(current MultiState, param MultiContext) bool { 205 | for _, state := range current { 206 | if state == nil || !state.IsFinal() { 207 | return false 208 | } 209 | } 210 | return true 211 | } 212 | 213 | func (r *ReplicatedSTSMService) UpdateStateMulti(current MultiState, param MultiContext) (MultiState, bool, error) { 214 | resp := false 215 | temp := false 216 | for i, machine := range r.GetMachines() { 217 | var err error 218 | current[i], temp, err = machine.UpdateState(current[i], param) 219 | if err != nil { 220 | return nil, false, err 221 | } 222 | if temp { 223 | resp = true 224 | } 225 | } 226 | 227 | return current, resp, nil 228 | } 229 | 230 | func (r *ReplicatedSTSMService) OnEnterStateMulti(current MultiState, param MultiContext) { 231 | fmt.Println("updating multi state! STATES:") 232 | for i, state := range current { 233 | fmt.Printf("Machine %v => %v\n", i, state) 234 | } 235 | if r.GetWatchdog() != nil { 236 | r.GetWatchdog().Reset() 237 | } 238 | } 239 | 240 | func (r *ReplicatedSTSMService) BeforeUpdateStateMulti(current MultiState, param MultiContext) bool { 241 | if r.GetWatchdog() != nil && r.GetWatchdog().Expired() { 242 | fmt.Printf("StateMachine FAILED MAXTIME %v\n", r.GetWatchdog().GetCountdown()) 243 | return true 244 | } 245 | return false 246 | } 247 | 248 | func (r *ReplicatedSTSMService) SetMachines(machines []machine.SingleTimerStateMachine) { 249 | r.machines = machines 250 | } -------------------------------------------------------------------------------- /spec/go/src/bftevent/bftevent.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: bftevent.proto 3 | 4 | package bftevent 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | proto "github.com/golang/protobuf/proto" 10 | grpc "google.golang.org/grpc" 11 | codes "google.golang.org/grpc/codes" 12 | status "google.golang.org/grpc/status" 13 | math "math" 14 | ) 15 | 16 | // Reference imports to suppress errors if they are not otherwise used. 17 | var _ = proto.Marshal 18 | var _ = fmt.Errorf 19 | var _ = math.Inf 20 | 21 | // This is a compile-time assertion to ensure that this generated file 22 | // is compatible with the proto package it is being compiled against. 23 | // A compilation error at this line likely means your copy of the 24 | // proto package needs to be updated. 25 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 26 | 27 | // The request message containing requested numbers 28 | type EventInform struct { 29 | From int32 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"` 30 | Event string `protobuf:"bytes,2,opt,name=event,proto3" json:"event,omitempty"` 31 | EventArgs []string `protobuf:"bytes,3,rep,name=event_args,json=eventArgs,proto3" json:"event_args,omitempty"` 32 | Delay int32 `protobuf:"varint,4,opt,name=delay,proto3" json:"delay,omitempty"` 33 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 34 | XXX_unrecognized []byte `json:"-"` 35 | XXX_sizecache int32 `json:"-"` 36 | } 37 | 38 | func (m *EventInform) Reset() { *m = EventInform{} } 39 | func (m *EventInform) String() string { return proto.CompactTextString(m) } 40 | func (*EventInform) ProtoMessage() {} 41 | func (*EventInform) Descriptor() ([]byte, []int) { 42 | return fileDescriptor_a7b16aa215e9c9cc, []int{0} 43 | } 44 | 45 | func (m *EventInform) XXX_Unmarshal(b []byte) error { 46 | return xxx_messageInfo_EventInform.Unmarshal(m, b) 47 | } 48 | func (m *EventInform) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 49 | return xxx_messageInfo_EventInform.Marshal(b, m, deterministic) 50 | } 51 | func (m *EventInform) XXX_Merge(src proto.Message) { 52 | xxx_messageInfo_EventInform.Merge(m, src) 53 | } 54 | func (m *EventInform) XXX_Size() int { 55 | return xxx_messageInfo_EventInform.Size(m) 56 | } 57 | func (m *EventInform) XXX_DiscardUnknown() { 58 | xxx_messageInfo_EventInform.DiscardUnknown(m) 59 | } 60 | 61 | var xxx_messageInfo_EventInform proto.InternalMessageInfo 62 | 63 | func (m *EventInform) GetFrom() int32 { 64 | if m != nil { 65 | return m.From 66 | } 67 | return 0 68 | } 69 | 70 | func (m *EventInform) GetEvent() string { 71 | if m != nil { 72 | return m.Event 73 | } 74 | return "" 75 | } 76 | 77 | func (m *EventInform) GetEventArgs() []string { 78 | if m != nil { 79 | return m.EventArgs 80 | } 81 | return nil 82 | } 83 | 84 | func (m *EventInform) GetDelay() int32 { 85 | if m != nil { 86 | return m.Delay 87 | } 88 | return 0 89 | } 90 | 91 | // The response message containing response 92 | type EventReply struct { 93 | Gotit int32 `protobuf:"varint,1,opt,name=gotit,proto3" json:"gotit,omitempty"` 94 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 95 | XXX_unrecognized []byte `json:"-"` 96 | XXX_sizecache int32 `json:"-"` 97 | } 98 | 99 | func (m *EventReply) Reset() { *m = EventReply{} } 100 | func (m *EventReply) String() string { return proto.CompactTextString(m) } 101 | func (*EventReply) ProtoMessage() {} 102 | func (*EventReply) Descriptor() ([]byte, []int) { 103 | return fileDescriptor_a7b16aa215e9c9cc, []int{1} 104 | } 105 | 106 | func (m *EventReply) XXX_Unmarshal(b []byte) error { 107 | return xxx_messageInfo_EventReply.Unmarshal(m, b) 108 | } 109 | func (m *EventReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 110 | return xxx_messageInfo_EventReply.Marshal(b, m, deterministic) 111 | } 112 | func (m *EventReply) XXX_Merge(src proto.Message) { 113 | xxx_messageInfo_EventReply.Merge(m, src) 114 | } 115 | func (m *EventReply) XXX_Size() int { 116 | return xxx_messageInfo_EventReply.Size(m) 117 | } 118 | func (m *EventReply) XXX_DiscardUnknown() { 119 | xxx_messageInfo_EventReply.DiscardUnknown(m) 120 | } 121 | 122 | var xxx_messageInfo_EventReply proto.InternalMessageInfo 123 | 124 | func (m *EventReply) GetGotit() int32 { 125 | if m != nil { 126 | return m.Gotit 127 | } 128 | return 0 129 | } 130 | 131 | func init() { 132 | proto.RegisterType((*EventInform)(nil), "bftevent.EventInform") 133 | proto.RegisterType((*EventReply)(nil), "bftevent.EventReply") 134 | } 135 | 136 | func init() { proto.RegisterFile("bftevent.proto", fileDescriptor_a7b16aa215e9c9cc) } 137 | 138 | var fileDescriptor_a7b16aa215e9c9cc = []byte{ 139 | // 197 bytes of a gzipped FileDescriptorProto 140 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4b, 0x4a, 0x2b, 0x49, 141 | 0x2d, 0x4b, 0xcd, 0x2b, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x80, 0xf1, 0x95, 0x72, 142 | 0xb8, 0xb8, 0x5d, 0x41, 0x0c, 0xcf, 0xbc, 0xb4, 0xfc, 0xa2, 0x5c, 0x21, 0x21, 0x2e, 0x96, 0xb4, 143 | 0xa2, 0xfc, 0x5c, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xd6, 0x20, 0x30, 0x5b, 0x48, 0x84, 0x8b, 0x15, 144 | 0xac, 0x56, 0x82, 0x49, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc2, 0x11, 0x92, 0xe5, 0xe2, 0x02, 0x33, 145 | 0xe2, 0x13, 0x8b, 0xd2, 0x8b, 0x25, 0x98, 0x15, 0x98, 0x35, 0x38, 0x83, 0x38, 0xc1, 0x22, 0x8e, 146 | 0x45, 0xe9, 0xc5, 0x20, 0x4d, 0x29, 0xa9, 0x39, 0x89, 0x95, 0x12, 0x2c, 0x60, 0x93, 0x20, 0x1c, 147 | 0x25, 0x25, 0x2e, 0x2e, 0xb0, 0x6d, 0x41, 0xa9, 0x05, 0x39, 0x95, 0x20, 0x35, 0xe9, 0xf9, 0x25, 148 | 0x99, 0x25, 0x50, 0xdb, 0x20, 0x1c, 0x23, 0x0f, 0x2e, 0x0e, 0x27, 0xb7, 0x10, 0xb0, 0x32, 0x21, 149 | 0x1b, 0x2e, 0xee, 0x4c, 0xb0, 0xc3, 0x20, 0x5c, 0x51, 0x3d, 0xb8, 0x3f, 0x90, 0x1c, 0x2d, 0x25, 150 | 0x82, 0x26, 0x0c, 0x36, 0x5d, 0x89, 0xc1, 0x89, 0x93, 0x8b, 0x3d, 0xb5, 0x42, 0x2f, 0xbd, 0xa8, 151 | 0x20, 0x39, 0x89, 0x0d, 0xec, 0x6f, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x96, 0x2b, 0x5b, 152 | 0x2d, 0x09, 0x01, 0x00, 0x00, 153 | } 154 | 155 | // Reference imports to suppress errors if they are not otherwise used. 156 | var _ context.Context 157 | var _ grpc.ClientConn 158 | 159 | // This is a compile-time assertion to ensure that this generated file 160 | // is compatible with the grpc package it is being compiled against. 161 | const _ = grpc.SupportPackageIsVersion4 162 | 163 | // BFTEventClient is the client API for BFTEvent service. 164 | // 165 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 166 | type BFTEventClient interface { 167 | // Function invoked to send the request 168 | InformEvent(ctx context.Context, in *EventInform, opts ...grpc.CallOption) (*EventReply, error) 169 | } 170 | 171 | type bFTEventClient struct { 172 | cc *grpc.ClientConn 173 | } 174 | 175 | func NewBFTEventClient(cc *grpc.ClientConn) BFTEventClient { 176 | return &bFTEventClient{cc} 177 | } 178 | 179 | func (c *bFTEventClient) InformEvent(ctx context.Context, in *EventInform, opts ...grpc.CallOption) (*EventReply, error) { 180 | out := new(EventReply) 181 | err := c.cc.Invoke(ctx, "/bftevent.BFTEvent/informEvent", in, out, opts...) 182 | if err != nil { 183 | return nil, err 184 | } 185 | return out, nil 186 | } 187 | 188 | // BFTEventServer is the server API for BFTEvent service. 189 | type BFTEventServer interface { 190 | // Function invoked to send the request 191 | InformEvent(context.Context, *EventInform) (*EventReply, error) 192 | } 193 | 194 | // UnimplementedBFTEventServer can be embedded to have forward compatible implementations. 195 | type UnimplementedBFTEventServer struct { 196 | } 197 | 198 | func (*UnimplementedBFTEventServer) InformEvent(ctx context.Context, req *EventInform) (*EventReply, error) { 199 | return nil, status.Errorf(codes.Unimplemented, "method InformEvent not implemented") 200 | } 201 | 202 | func RegisterBFTEventServer(s *grpc.Server, srv BFTEventServer) { 203 | s.RegisterService(&_BFTEvent_serviceDesc, srv) 204 | } 205 | 206 | func _BFTEvent_InformEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 207 | in := new(EventInform) 208 | if err := dec(in); err != nil { 209 | return nil, err 210 | } 211 | if interceptor == nil { 212 | return srv.(BFTEventServer).InformEvent(ctx, in) 213 | } 214 | info := &grpc.UnaryServerInfo{ 215 | Server: srv, 216 | FullMethod: "/bftevent.BFTEvent/InformEvent", 217 | } 218 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 219 | return srv.(BFTEventServer).InformEvent(ctx, req.(*EventInform)) 220 | } 221 | return interceptor(ctx, in, info, handler) 222 | } 223 | 224 | var _BFTEvent_serviceDesc = grpc.ServiceDesc{ 225 | ServiceName: "bftevent.BFTEvent", 226 | HandlerType: (*BFTEventServer)(nil), 227 | Methods: []grpc.MethodDesc{ 228 | { 229 | MethodName: "informEvent", 230 | Handler: _BFTEvent_InformEvent_Handler, 231 | }, 232 | }, 233 | Streams: []grpc.StreamDesc{}, 234 | Metadata: "bftevent.proto", 235 | } 236 | --------------------------------------------------------------------------------