diff --git a/.buildkite/coverage.yml b/.buildkite/coverage.yml deleted file mode 100644 index c5a50bc64f4..00000000000 --- a/.buildkite/coverage.yml +++ /dev/null @@ -1,29 +0,0 @@ -steps: - - command: | - echo "--- :hammer: Building" && \ - /usr/bin/cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++-4.0 -DCMAKE_C_COMPILER=clang-4.0 -DBOOST_ROOT="${BOOST_ROOT}" -DWASM_ROOT="${WASM_ROOT}" -DOPENSSL_ROOT_DIR="${OPENSSL_ROOT_DIR}" -DBUILD_MONGO_DB_PLUGIN=true -DENABLE_COVERAGE_TESTING=true -DBUILD_DOXYGEN=false && \ - /usr/bin/ninja - echo "--- :spiral_note_pad: Generating Code Coverage Report" && \ - /usr/bin/ninja EOSIO_ut_coverage && \ - echo "--- :arrow_up: Publishing Code Coverage Report" && \ - buildkite-agent artifact upload "EOSIO_ut_coverage/**/*" s3://eos-coverage/$BUILDKITE_JOB_ID && \ - cp /config/.coveralls.yml . && \ - /usr/local/bin/coveralls-lcov EOSIO_ut_coverage_filtered.info && \ - echo "+++ View Report" && \ - printf "\033]1339;url=https://eos-coverage.s3-us-west-2.amazonaws.com/$BUILDKITE_JOB_ID/EOSIO_ut_coverage/index.html;content=View Full Coverage Report\a\n" - label: ":spiral_note_pad: Generate Report" - agents: - queue: "automation-large-builder-fleet" - plugins: - docker#v1.4.0: - image: "eosio/ci:ubuntu18" - workdir: /data/job - mounts: - - /etc/buildkite-agent/config:/config - environment: - - BOOST_ROOT=/root/opt/boost - - OPENSSL_ROOT_DIR=/usr/include/openssl - - WASM_ROOT=/root/opt/wasm - - PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/opt/wasm/bin - - CI=true - timeout: 60 diff --git a/.buildkite/debug.yml b/.buildkite/debug.yml deleted file mode 100644 index 28576d56195..00000000000 --- a/.buildkite/debug.yml +++ /dev/null @@ -1,230 +0,0 @@ -steps: - - command: | - echo "--- Creating symbolic link to job directory :file_folder:" && \ - sleep 5 && ln -s "$(pwd)" /data/job && cd /data/job && \ - echo "+++ Building :hammer:" && \ - echo 1 | ./eosio_build.sh -o Debug && \ - echo "--- Compressing build directory :compression:" && \ - tar -pczf build.tar.gz build/ - label: ":darwin: Build" - agents: - - "role=macos-builder" - artifact_paths: "build.tar.gz" - timeout: 60 - - - command: | - echo "+++ :hammer: Building" && \ - echo 1 | ./eosio_build.sh -o Debug && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz build/ - label: ":ubuntu: Build" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: "build.tar.gz" - plugins: - docker#v2.0.0: - image: "eosio/ci:ubuntu" - workdir: /data/job - timeout: 60 - - - command: | - echo "+++ :hammer: Building" && \ - echo 1 | ./eosio_build.sh -o Debug && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz build/ - label: ":ubuntu: 18.04 Build" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: "build.tar.gz" - plugins: - docker#v2.0.0: - image: "eosio/ci:ubuntu18" - workdir: /data/job - timeout: 60 - - - command: | - echo "+++ :hammer: Building" && \ - echo 1 | ./eosio_build.sh -o Debug && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz build/ - label: ":fedora: Build" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: "build.tar.gz" - plugins: - docker#v2.0.0: - image: "eosio/ci:fedora" - workdir: /data/job - timeout: 60 - - - command: | - echo "+++ :hammer: Building" && \ - echo 1 | ./eosio_build.sh -o Debug && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz build/ - label: ":centos: Build" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: "build.tar.gz" - plugins: - docker#v2.0.0: - image: "eosio/ci:centos" - workdir: /data/job - timeout: 60 - - - command: | - echo "+++ :hammer: Building" && \ - echo 1 | ./eosio_build.sh -o Debug && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz build/ - label: ":aws: Build" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: "build.tar.gz" - plugins: - docker#v2.0.0: - image: "eosio/ci:amazonlinux" - workdir: /data/job - timeout: 60 - - - wait - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":darwin: Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -LE long_running_tests --output-on-failure - retry: - automatic: - limit: 1 - label: ":darwin: Tests" - agents: - - "role=macos-tester" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - timeout: 60 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":ubuntu: Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - cd /data/job/build && ctest -LE long_running_tests --output-on-failure - retry: - automatic: - limit: 1 - label: ":ubuntu: Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - plugins: - docker#v2.0.0: - image: "eosio/ci:ubuntu" - workdir: /data/job - timeout: 60 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":ubuntu: 18.04 Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - cd /data/job/build && ctest -LE long_running_tests --output-on-failure - retry: - automatic: - limit: 1 - label: ":ubuntu: 18.04 Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - plugins: - docker#v2.0.0: - image: "eosio/ci:ubuntu18" - workdir: /data/job - timeout: 60 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":fedora: Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - cd /data/job/build && ctest -LE long_running_tests --output-on-failure - retry: - automatic: - limit: 1 - label: ":fedora: Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - plugins: - docker#v2.0.0: - image: "eosio/ci:fedora" - workdir: /data/job - timeout: 60 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":centos: Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - cd /data/job/build && ctest -LE long_running_tests --output-on-failure - retry: - automatic: - limit: 1 - label: ":centos: Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - plugins: - docker#v2.0.0: - image: "eosio/ci:centos" - workdir: /data/job - timeout: 60 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":aws: Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - cd /data/job/build && ctest -LE long_running_tests --output-on-failure - retry: - automatic: - limit: 1 - label: ":aws: Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - plugins: - docker#v2.0.0: - image: "eosio/ci:amazonlinux" - workdir: /data/job - timeout: 60 diff --git a/.buildkite/docker.yml b/.buildkite/docker.yml deleted file mode 100644 index f8f6d8e0a12..00000000000 --- a/.buildkite/docker.yml +++ /dev/null @@ -1,74 +0,0 @@ -steps: - - command: | - echo "AUTHENTICATING GOOGLE SERVICE ACCOUNT" && \ - gcloud --quiet auth activate-service-account b1-automation-svc@b1-automation-dev.iam.gserviceaccount.com --key-file=/etc/gcp-service-account.json && \ - docker-credential-gcr configure-docker && \ - echo "BUILDING BUILD IMAGE" && \ - cd Docker/builder && \ - docker build -t eosio/builder:latest -t eosio/builder:$BUILDKITE_COMMIT . --build-arg branch=$BUILDKITE_COMMIT && \ - docker tag eosio/builder:$BUILDKITE_COMMIT gcr.io/b1-automation-dev/eosio/builder:$BUILDKITE_COMMIT && \ - docker tag eosio/builder:latest gcr.io/b1-automation-dev/eosio/builder:latest && \ - echo "PUSHING DOCKER IMAGES" && \ - docker push gcr.io/b1-automation-dev/eosio/builder:$BUILDKITE_COMMIT && \ - docker push gcr.io/b1-automation-dev/eosio/builder:latest && \ - echo "TRASHING OLD IMAGES" && \ - docker rmi eosio/builder:$BUILDKITE_COMMIT && \ - docker rmi eosio/builder:latest && \ - docker rmi gcr.io/b1-automation-dev/eosio/builder:$BUILDKITE_COMMIT && \ - docker rmi gcr.io/b1-automation-dev/eosio/builder:latest - label: "Docker build builder" - agents: - queue: "automation-docker-builder-fleet" - timeout: 300 - - - wait - - - command: | - echo "AUTHENTICATING GOOGLE SERVICE ACCOUNT" && \ - gcloud --quiet auth activate-service-account b1-automation-svc@b1-automation-dev.iam.gserviceaccount.com --key-file=/etc/gcp-service-account.json && \ - docker-credential-gcr configure-docker && \ - echo "BUILDING EOS IMAGE" && \ - docker pull gcr.io/b1-automation-dev/eosio/builder:$BUILDKITE_COMMIT && \ - cd Docker && \ - docker build -t eosio/eos:latest -t eosio/eos:$BUILDKITE_COMMIT . --build-arg branch=$BUILDKITE_BRANCH && \ - docker tag eosio/eos:$BUILDKITE_COMMIT gcr.io/b1-automation-dev/eosio/eos:$BUILDKITE_COMMIT && \ - docker tag eosio/eos:latest gcr.io/b1-automation-dev/eosio/eos:latest && \ - echo "PUSHING DOCKER IMAGES" && \ - docker push gcr.io/b1-automation-dev/eosio/eos:$BUILDKITE_COMMIT && \ - docker push gcr.io/b1-automation-dev/eosio/eos:latest && \ - echo "TRASHING OLD IMAGES" && \ - docker rmi eosio/eos:$BUILDKITE_COMMIT && \ - docker rmi eosio/eos:latest && \ - docker rmi gcr.io/b1-automation-dev/eosio/eos:$BUILDKITE_COMMIT && \ - docker rmi gcr.io/b1-automation-dev/eosio/eos:latest && \ - docker rmi gcr.io/b1-automation-dev/eosio/builder:$BUILDKITE_COMMIT - label: "Docker build eos" - agents: - queue: "automation-docker-builder-fleet" - timeout: 300 - - - command: | - echo "AUTHENTICATING GOOGLE SERVICE ACCOUNT" && \ - gcloud --quiet auth activate-service-account b1-automation-svc@b1-automation-dev.iam.gserviceaccount.com --key-file=/etc/gcp-service-account.json && \ - docker-credential-gcr configure-docker && \ - echo "BUILDING EOS DEV IMAGE" && \ - docker pull gcr.io/b1-automation-dev/eosio/builder:$BUILDKITE_COMMIT && \ - cd Docker/dev && \ - docker build -t eosio/eos-dev:latest -t eosio/eos-dev:$BUILDKITE_COMMIT . --build-arg branch=$BUILDKITE_BRANCH && \ - docker tag eosio/eos-dev:$BUILDKITE_COMMIT gcr.io/b1-automation-dev/eosio/eos-dev:$BUILDKITE_COMMIT && \ - docker tag eosio/eos-dev:latest gcr.io/b1-automation-dev/eosio/eos-dev:latest && \ - echo "PUSHING DOCKER IMAGES" && \ - docker push gcr.io/b1-automation-dev/eosio/eos-dev:$BUILDKITE_COMMIT && \ - docker push gcr.io/b1-automation-dev/eosio/eos-dev:latest && \ - echo "TRASHING OLD IMAGES" && \ - docker rmi eosio/eos-dev:$BUILDKITE_COMMIT && \ - docker rmi eosio/eos-dev:latest && \ - docker rmi gcr.io/b1-automation-dev/eosio/eos-dev:$BUILDKITE_COMMIT && \ - docker rmi gcr.io/b1-automation-dev/eosio/eos-dev:latest && \ - docker rmi gcr.io/b1-automation-dev/eosio/builder:$BUILDKITE_COMMIT - label: "Docker build eos-dev" - agents: - queue: "automation-docker-builder-fleet" - timeout: 300 - - - wait diff --git a/.buildkite/long_running_tests.yml b/.buildkite/long_running_tests.yml deleted file mode 100644 index e22016c4de4..00000000000 --- a/.buildkite/long_running_tests.yml +++ /dev/null @@ -1,212 +0,0 @@ -steps: - - command: | - echo "--- Creating symbolic link to job directory :file_folder:" && \ - sleep 5 && ln -s "$(pwd)" /data/job && cd /data/job && \ - echo "+++ Building :hammer:" && \ - echo 1 | ./eosio_build.sh && \ - echo "--- Compressing build directory :compression:" && \ - tar -pczf build.tar.gz build/ - label: ":darwin: Build" - agents: - - "role=macos-builder" - artifact_paths: "build.tar.gz" - timeout: 60 - - - command: | - echo "+++ :hammer: Building" && \ - echo 1 | ./eosio_build.sh && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz build/ - label: ":ubuntu: Build" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: "build.tar.gz" - plugins: - docker#v1.4.0: - image: "eosio/ci:ubuntu" - workdir: /data/job - timeout: 60 - - - command: | - echo "+++ :hammer: Building" && \ - echo 1 | ./eosio_build.sh && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz build/ - label: ":ubuntu: 18.04 Build" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: "build.tar.gz" - plugins: - docker#v1.4.0: - image: "eosio/ci:ubuntu18" - workdir: /data/job - timeout: 60 - - - command: | - echo "+++ :hammer: Building" && \ - echo 1 | ./eosio_build.sh && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz build/ - label: ":fedora: Build" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: "build.tar.gz" - plugins: - docker#v1.4.0: - image: "eosio/ci:fedora" - workdir: /data/job - timeout: 60 - - - command: | - echo "+++ :hammer: Building" && \ - echo 1 | ./eosio_build.sh && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz build/ - label: ":centos: Build" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: "build.tar.gz" - plugins: - docker#v1.4.0: - image: "eosio/ci:centos" - workdir: /data/job - timeout: 60 - - - command: | - echo "+++ :hammer: Building" && \ - echo 1 | ./eosio_build.sh && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz build/ - label: ":aws: Build" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: "build.tar.gz" - plugins: - docker#v1.4.0: - image: "eosio/ci:amazonlinux" - workdir: /data/job - timeout: 60 - - - wait - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":darwin: Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -L long_running_tests --output-on-failure - label: ":darwin: Tests" - agents: - - "role=macos-tester" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - timeout: 100 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":ubuntu: Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - cd /data/job/build && ctest -L long_running_tests --output-on-failure - label: ":ubuntu: Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - plugins: - docker#v1.4.0: - image: "eosio/ci:ubuntu" - workdir: /data/job - timeout: 100 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":ubuntu: 18.04 Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - cd /data/job/build && ctest -L long_running_tests --output-on-failure - label: ":ubuntu: 18.04 Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - plugins: - docker#v1.4.0: - image: "eosio/ci:ubuntu18" - workdir: /data/job - timeout: 100 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":fedora: Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - cd /data/job/build && ctest -L long_running_tests --output-on-failure - label: ":fedora: Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - plugins: - docker#v1.4.0: - image: "eosio/ci:fedora" - workdir: /data/job - timeout: 100 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":centos: Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - cd /data/job/build && ctest -L long_running_tests --output-on-failure - label: ":centos: Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - plugins: - docker#v1.4.0: - image: "eosio/ci:centos" - workdir: /data/job - timeout: 100 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":aws: Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - cd /data/job/build && ctest -L long_running_tests --output-on-failure - label: ":aws: Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - plugins: - docker#v1.4.0: - image: "eosio/ci:amazonlinux" - workdir: /data/job - timeout: 100 diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline_linux_build.yml similarity index 70% rename from .buildkite/pipeline.yml rename to .buildkite/pipeline_linux_build.yml index 893a19f9ff9..b2eaaf5c4d4 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline_linux_build.yml @@ -1,31 +1,4 @@ steps: - - command: | - echo "--- Creating symbolic link to job directory :file_folder:" && \ - sleep 5 && ln -s "$(pwd)" /data/job && cd /data/job && \ - echo "+++ Building :hammer:" && \ - echo 1 | ./eosio_build.sh && \ - echo "--- Compressing build directory :compression:" && \ - tar -pczf build.tar.gz build/ - label: ":darwin: High Sierra Build" - agents: - - "role=macos-builder" - artifact_paths: "build.tar.gz" - timeout: 60 - - - command: | - echo "--- Creating symbolic link to job directory :file_folder:" && \ - sleep 5 && ln -s "$(pwd)" /data/job && cd /data/job && \ - echo "+++ Building :hammer:" && \ - echo 1 | ./eosio_build.sh && \ - echo "--- Compressing build directory :compression:" && \ - tar -pczf build.tar.gz build/ - label: ":darwin: Mojave Build" - agents: - - "role=builder" - - "os=mojave" - artifact_paths: "build.tar.gz" - timeout: 60 - - command: | echo "+++ :hammer: Building" && \ echo 1 | ./eosio_build.sh && \ @@ -103,77 +76,6 @@ steps: - wait - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":darwin: High Sierra Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -j8 -LE _tests --output-on-failure - label: ":darwin: High Sierra Tests" - agents: - - "role=macos-tester" - - "os=high-sierra" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - timeout: 60 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":darwin: High Sierra Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -L nonparallelizable_tests --output-on-failure - label: ":darwin: High Sierra NP Tests" - agents: - - "role=macos-tester" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - timeout: 60 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":darwin: Mojave Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -j8 -LE _tests --output-on-failure - label: ":darwin: Mojave Tests" - agents: - - "role=tester" - - "os=mojave" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - timeout: 60 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":darwin: Mojave Build" && \ - tar -zxf build.tar.gz && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -L nonparallelizable_tests --output-on-failure - label: ":darwin: Mojave NP Tests" - agents: - - "role=tester" - - "os=mojave" - artifact_paths: - - "mongod.log" - - "build/genesis.json" - - "build/config.ini" - timeout: 60 - - command: | echo "--- :arrow_down: Downloading build directory" && \ buildkite-agent artifact download "build.tar.gz" . --step ":ubuntu: Build" && \ @@ -386,36 +288,6 @@ steps: - wait - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":darwin: High Sierra Build" && \ - tar -zxf build.tar.gz && \ - echo "+++ :microscope: Starting package build" && \ - ln -s "$(pwd)" /data/job && cd /data/job/build/packages && bash generate_package.sh brew - label: ":darwin: High Sierra Package Builder" - agents: - - "role=macos-builder" - - "os=high-sierra" - artifact_paths: - - "build/packages/*.tar.gz" - - "build/packages/*.rb" - timeout: 60 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":darwin: Mojave Build" && \ - tar -zxf build.tar.gz && \ - echo "+++ :microscope: Starting package build" && \ - ln -s "$(pwd)" /data/job && cd /data/job/build/packages && bash generate_package.sh brew - label: ":darwin: Mojave Package Builder" - agents: - - "role=builder" - - "os=mojave" - artifact_paths: - - "build/packages/*.tar.gz" - - "build/packages/*.rb" - timeout: 60 - - command: | echo "--- :arrow_down: Downloading build directory" && \ buildkite-agent artifact download "build.tar.gz" . --step ":ubuntu: Build" && \ @@ -508,19 +380,4 @@ steps: env: OS: "el7" PKGTYPE: "rpm" - timeout: 60 - - - wait - - - command: | - echo "--- :arrow_down: Downloading brew files" && \ - buildkite-agent artifact download "build/packages/eosio.rb" . --step ":darwin: High Sierra Package Builder" && \ - mv build/packages/eosio.rb build/packages/eosio_highsierra.rb && \ - buildkite-agent artifact download "build/packages/eosio.rb" . --step ":darwin: Mojave Package Builder" - label: ":darwin: Brew Updater" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "build/packages/eosio_highsierra.rb" - - "build/packages/eosio.rb" - timeout: 60 + timeout: 60 \ No newline at end of file diff --git a/.buildkite/pipeline_mac_build.yml b/.buildkite/pipeline_mac_build.yml new file mode 100644 index 00000000000..73d0ab98ffd --- /dev/null +++ b/.buildkite/pipeline_mac_build.yml @@ -0,0 +1,131 @@ +steps: + - command: | + echo "--- Creating symbolic link to job directory :file_folder:" && \ + sleep 5 && ln -s "$(pwd)" /data/job && cd /data/job && \ + echo "+++ Building :hammer:" && \ + echo 1 | ./eosio_build.sh && \ + echo "--- Compressing build directory :compression:" && \ + tar -pczf build.tar.gz build/ + label: ":darwin: High Sierra Build" + agents: + - "role=macos-builder" + artifact_paths: "build.tar.gz" + timeout: 60 + + - command: | + echo "--- Creating symbolic link to job directory :file_folder:" && \ + sleep 5 && ln -s "$(pwd)" /data/job && cd /data/job && \ + echo "+++ Building :hammer:" && \ + echo 1 | ./eosio_build.sh && \ + echo "--- Compressing build directory :compression:" && \ + tar -pczf build.tar.gz build/ + label: ":darwin: Mojave Build" + agents: + - "role=builder" + - "os=mojave" + artifact_paths: "build.tar.gz" + timeout: 60 + + - wait + + - command: | + echo "--- :arrow_down: Downloading build directory" && \ + buildkite-agent artifact download "build.tar.gz" . --step ":darwin: High Sierra Build" && \ + tar -zxf build.tar.gz && \ + echo "--- :m: Starting MongoDB" && \ + $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ + echo "+++ :microscope: Running tests" && \ + ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -j8 -LE _tests --output-on-failure + label: ":darwin: High Sierra Tests" + agents: + - "role=macos-builder" + artifact_paths: + - "mongod.log" + - "build/genesis.json" + - "build/config.ini" + timeout: 60 + + - command: | + echo "--- :arrow_down: Downloading build directory" && \ + buildkite-agent artifact download "build.tar.gz" . --step ":darwin: High Sierra Build" && \ + tar -zxf build.tar.gz && \ + echo "--- :m: Starting MongoDB" && \ + $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ + echo "+++ :microscope: Running tests" && \ + ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -L nonparallelizable_tests --output-on-failure + label: ":darwin: High Sierra NP Tests" + agents: + - "role=macos-builder" + artifact_paths: + - "mongod.log" + - "build/genesis.json" + - "build/config.ini" + timeout: 60 + + - command: | + echo "--- :arrow_down: Downloading build directory" && \ + buildkite-agent artifact download "build.tar.gz" . --step ":darwin: Mojave Build" && \ + tar -zxf build.tar.gz && \ + echo "--- :m: Starting MongoDB" && \ + $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ + echo "+++ :microscope: Running tests" && \ + ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -j8 -LE _tests --output-on-failure + label: ":darwin: Mojave Tests" + agents: + - "role=builder" + - "os=mojave" + artifact_paths: + - "mongod.log" + - "build/genesis.json" + - "build/config.ini" + timeout: 60 + + - command: | + echo "--- :arrow_down: Downloading build directory" && \ + buildkite-agent artifact download "build.tar.gz" . --step ":darwin: Mojave Build" && \ + tar -zxf build.tar.gz && \ + echo "--- :m: Starting MongoDB" && \ + $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ + echo "+++ :microscope: Running tests" && \ + ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -L nonparallelizable_tests --output-on-failure + label: ":darwin: Mojave NP Tests" + agents: + - "role=builder" + - "os=mojave" + artifact_paths: + - "mongod.log" + - "build/genesis.json" + - "build/config.ini" + timeout: 60 + + - wait + + - command: | + echo "--- :arrow_down: Downloading build directory" && \ + buildkite-agent artifact download "build.tar.gz" . --step ":darwin: High Sierra Build" && \ + tar -zxf build.tar.gz && \ + echo "+++ :microscope: Starting package build" && \ + ln -s "$(pwd)" /data/job && cd /data/job/build/packages && bash generate_package.sh brew + label: ":darwin: High Sierra Package Builder" + agents: + - "role=macos-builder" + - "os=high-sierra" + artifact_paths: + - "build/packages/*.tar.gz" + - "build/packages/*.rb" + timeout: 60 + + - command: | + echo "--- :arrow_down: Downloading build directory" && \ + buildkite-agent artifact download "build.tar.gz" . --step ":darwin: Mojave Build" && \ + tar -zxf build.tar.gz && \ + echo "+++ :microscope: Starting package build" && \ + ln -s "$(pwd)" /data/job && cd /data/job/build/packages && bash generate_package.sh brew + label: ":darwin: Mojave Package Builder" + agents: + - "role=builder" + - "os=mojave" + artifact_paths: + - "build/packages/*.tar.gz" + - "build/packages/*.rb" + timeout: 60 diff --git a/.buildkite/sanitizers.yml b/.buildkite/sanitizers.yml deleted file mode 100644 index b8588135610..00000000000 --- a/.buildkite/sanitizers.yml +++ /dev/null @@ -1,131 +0,0 @@ -steps: - - command: | - echo "--- :hammer: Building with Undefined Sanitizer" && \ - /usr/bin/cmake -GNinja \ - -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_CXX_COMPILER=clang++-4.0 \ - -DCMAKE_C_COMPILER=clang-4.0 \ - -DBOOST_ROOT="${BOOST_ROOT}" \ - -DWASM_ROOT="${WASM_ROOT}" \ - -DOPENSSL_ROOT_DIR="${OPENSSL_ROOT_DIR}" \ - -DBUILD_MONGO_DB_PLUGIN=true \ - -DENABLE_COVERAGE_TESTING=true\ - -DBUILD_DOXYGEN=false -DCMAKE_CXX_FLAGS="-fsanitize=undefined -fsanitize-recover=all -g -fno-omit-frame-pointer" \ - -DCMAKE_C_FLAGS="-fsanitize=undefined -fsanitize-recover=all -g -fno-omit-frame-pointer" \ - -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=undefined -fsanitize-recover=all -rtlib=compiler-rt -lgcc_s -pthread" \ - -DCMAKE_MODULE_LINKER_FLAGS="-fsanitize=undefined -fsanitize-recover=all -rtlib=compiler-rt -lgcc_s -pthread" && \ - echo "--- :shinto_shrine: Running ninja" && \ - /usr/bin/ninja | tee ninja.log && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz * - echo "--- :beers: Done" - label: ":_: Undefined Sanitizer" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "build.tar.gz" - - "ninja.log" - plugins: - docker#v1.4.0: - image: "eosio/ci:ubuntu18" - command: ["--privileged"] - workdir: /data/job - mounts: - - /etc/buildkite-agent/config:/config - environment: - - BOOST_ROOT=/root/opt/boost - - OPENSSL_ROOT_DIR=/usr/include/openssl - - WASM_ROOT=/root/opt/wasm - - PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/opt/wasm/bin - - CI=true - - UBSAN_OPTIONS=print_stacktrace=1 - timeout: 60 - - - command: | - echo "--- :hammer: Building with Address Sanitizer" && \ - /usr/bin/cmake -GNinja \ - -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_CXX_COMPILER=clang++-4.0 \ - -DCMAKE_C_COMPILER=clang-4.0 \ - -DBOOST_ROOT="${BOOST_ROOT}" \ - -DWASM_ROOT="${WASM_ROOT}" \ - -DOPENSSL_ROOT_DIR="${OPENSSL_ROOT_DIR}" \ - -DBUILD_MONGO_DB_PLUGIN=true \ - -DENABLE_COVERAGE_TESTING=true \ - -DBUILD_DOXYGEN=false \ - -DCMAKE_CXX_FLAGS="-fsanitize=address -fsanitize-recover=all -O1 -g -fno-omit-frame-pointer" \ - -DCMAKE_C_FLAGS="-fsanitize=address -fsanitize-recover=all -O1 -g -fno-omit-frame-pointer" \ - -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address -fsanitize-recover=all -rtlib=compiler-rt -lgcc_s" \ - -DCMAKE_MODULE_LINKER_FLAGS="-fsanitize=address -fsanitize-recover=all -rtlib=compiler-rt -lgcc_s" - echo "--- :shinto_shrine: Running ninja" && \ - /usr/bin/ninja | tee ninja.log && \ - echo "--- :compression: Compressing build directory" && \ - tar -pczf build.tar.gz * - echo "--- :beers: Done" - label: ":_: Address Sanitizer" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "build.tar.gz" - - "ninja.log" - plugins: - docker#v1.4.0: - image: "eosio/ci:ubuntu18" - command: ["--privileged"] - workdir: /data/job - mounts: - - /etc/buildkite-agent/config:/config - environment: - - BOOST_ROOT=/root/opt/boost - - OPENSSL_ROOT_DIR=/usr/include/openssl - - WASM_ROOT=/root/opt/wasm - - PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/opt/wasm/bin - - CI=true - - ASAN_OPTIONS=fast_unwind_on_malloc=0:halt_on_error=0:detect_odr_violation=0:detect_leaks=0:symbolize=1:verbosity=1 - timeout: 60 - - - wait - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":_: Undefined Sanitizer" && \ - tar -zxf build.tar.gz --no-same-owner && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - ctest -j8 -LE _tests -V -O sanitizer.log || true - label: ":_: Undefined Sanitizer Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "sanitizer.log" - plugins: - docker#v1.4.0: - image: "eosio/ci:ubuntu18" - workdir: /data/job - mounts: - - /etc/buildkite-agent/config:/config - timeout: 120 - - - command: | - echo "--- :arrow_down: Downloading build directory" && \ - buildkite-agent artifact download "build.tar.gz" . --step ":_: Address Sanitizer" && \ - tar -zxf build.tar.gz --no-same-owner && \ - echo "--- :m: Starting MongoDB" && \ - $(which mongod) --fork --logpath "$(pwd)"/mongod.log && \ - echo "+++ :microscope: Running tests" && \ - ctest -j8 -LE _tests -V -O sanitizer.log || true - label: ":_: Address Sanitizer Tests" - agents: - queue: "automation-large-builder-fleet" - artifact_paths: - - "mongod.log" - - "sanitizer.log" - plugins: - docker#v1.4.0: - image: "eosio/ci:ubuntu18" - workdir: /data/job - mounts: - - /etc/buildkite-agent/config:/config - timeout: 120 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e49f6dd0907..6ec500711d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,9 +33,9 @@ set( CMAKE_CXX_STANDARD 14 ) set( CMAKE_CXX_EXTENSIONS ON ) set( CXX_STANDARD_REQUIRED ON) -set(VERSION_MAJOR 2) +set(VERSION_MAJOR 3) set(VERSION_MINOR 0) -set(VERSION_PATCH 3) +set(VERSION_PATCH 0) if(VERSION_SUFFIX) set(VERSION_FULL "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_SUFFIX}") diff --git a/Docker/README.md b/Docker/README.md index b5228e17bbc..02eabc07b1c 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -20,10 +20,10 @@ cd bos/Docker docker build . -t boscore/bos -s BOS ``` -The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v2.0.3 tag, you could do the following: +The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v3.0.0 tag, you could do the following: ```bash -docker build -t boscore/bos:v2.0.3 --build-arg branch=v2.0.3 . +docker build -t boscore/bos:v3.0.0 --build-arg branch=v3.0.0 . ``` diff --git a/README.md b/README.md index 0b90a01c333..db7f7e0f82c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # BOSCore - Born for DApps. Born for Usability. -## BOSCore Version: v2.0.3 -### Basic EOSIO Version: v1.6.4 (support REX) +## BOSCore Version: v3.0.0 +### Basic EOSIO Version: v1.6.6 (support REX) # Background The emergence of EOS has brought new imagination to the blockchain. In just a few months since the main network was launched, the version has undergone dozens of upgrades, not only the stability has been greatly improved, but also the new functions have been gradually realized. The node team is also actively involved in building the EOSIO ecosystem. What is even more exciting is that EOS has attracted more and more development teams. There are already hundreds of DApp running on the EOS main network. The transaction volume and circulation market value far exceed Ethereum, and the space for development is growing broader. @@ -39,16 +39,6 @@ Attention: 3. Treat update of eosio/eos code as new feature. 4. Emergent issues must repaired by adopting hotfixes mode. -## BOSCore Workflow -BOSCore encourage community developer actively participate in contributing the code, members should follow the workflow below. -![BOSCore Workflow](./images/bos-workflow.png) - -Attention: -1. Only allow Feature Branch or bug fix to submit PR to Develop Branch. -2. Rebase is required before submitting PR to Develop Branch. -3. Treat update of eosio/eos code as new feature. -4. Emergent issues must repaired by adopting hotfixes mode. - BOSCore bases on EOSIO, so you can also referer: [Getting Started](https://developers.eos.io/eosio-nodeos/docs/overview-1) diff --git a/README_CN.md b/README_CN.md index 23b9199068f..718c2ace476 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,7 +1,7 @@ # BOSCore - 更可用的链,为DApp而生。 -## BOSCore Version: v2.0.3 -### Basic EOSIO Version: v1.6.4 (support REX) +## BOSCore Version: v3.0.0 +### Basic EOSIO Version: v1.6.6 (support REX) # 背景 EOS的出现给区块链带来了新的想象力,主网启动短短几个月以来,版本经历了几十次升级,不仅稳定性得到了很大提高,并且新功能也逐步实现,各个节点团队也积极参与建设EOSIO生态。让人更加兴奋的是,EOS已经吸引了越来越多的开发团队,当前已经有数百个DApp在EOS主网上面运行,其交易量和流通市值远超以太坊,可发展的空间愈来愈广阔。 @@ -39,16 +39,6 @@ BOSCore 鼓励社区开发者参与代码贡献,社区成员应当遵循以下 3. EOSIO 主网版本作为一个 Feature Branch 来对待 4. 紧急问题修复采用 hotfixes 模式 -## BOSCore 开发流程 -BOSCore 鼓励社区开发者参与代码贡献,社区成员应当遵循以下工作流: -![BOSCore Workflow](./images/bos-workflow.png) - -注意: -1. 只有待发布的 Feature Branch 或者Bug修复才应该向 Develop Branch 提交 -2. 向 Develop Branch 提交 PR 之前需要现在本地执行 rebase 操作 -3. EOSIO 主网版本作为一个 Feature Branch 来对待 -4. 紧急问题修复采用 hotfixes 模式 - BOSCore是基于EOSIO技术的扩展,所以EOSIO的相关资料也可以参考: [EOSIO 开始](https://developers.eos.io/eosio-nodeos/docs/overview-1) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 96838c2fc25..690b10a947e 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -33,7 +33,7 @@ namespace eosio { namespace chain { * contain a transaction mroot, action mroot, or new_producers as those components * are derived from chain state. */ - block_header_state block_header_state::generate_next( block_timestamp_type when, bool new_version )const { + block_header_state block_header_state::generate_next( block_timestamp_type when, bool pbft_enabled )const { block_header_state result; if( when != block_timestamp_type() ) { @@ -65,7 +65,7 @@ namespace eosio { namespace chain { result.pbft_stable_checkpoint_blocknum = pbft_stable_checkpoint_blocknum; - if (new_version) { + if (pbft_enabled) { result.dpos_irreversible_blocknum = dpos_irreversible_blocknum; } else { result.producer_to_last_implied_irb[prokey.producer_name] = result.dpos_proposed_irreversible_blocknum; @@ -81,7 +81,7 @@ namespace eosio { namespace chain { auto num_active_producers = active_schedule.producers.size(); uint32_t required_confs = (uint32_t)(num_active_producers * 2 / 3) + 1; - if (!new_version) { + if (!pbft_enabled) { if (confirm_count.size() < config::maximum_tracked_dpos_confirmations) { result.confirm_count.reserve(confirm_count.size() + 1); result.confirm_count = confirm_count; @@ -97,10 +97,10 @@ namespace eosio { namespace chain { return result; } /// generate_next - bool block_header_state::maybe_promote_pending( bool new_version ) { + bool block_header_state::maybe_promote_pending( bool pbft_enabled ) { bool should_promote_pending = pending_schedule.producers.size(); - if ( !new_version ) { + if ( !pbft_enabled ) { should_promote_pending = should_promote_pending && dpos_irreversible_blocknum >= pending_schedule_lib_num; } @@ -115,7 +115,7 @@ namespace eosio { namespace chain { new_producer_to_last_produced[pro.producer_name] = existing->second; } else { //TODO: max of bft and dpos lib - if (new_version) { + if (pbft_enabled) { new_producer_to_last_produced[pro.producer_name] = bft_irreversible_blocknum; } else { new_producer_to_last_produced[pro.producer_name] = dpos_irreversible_blocknum; @@ -131,7 +131,7 @@ namespace eosio { namespace chain { new_producer_to_last_implied_irb[pro.producer_name] = existing->second; } else { //TODO: max of bft and dpos lib - if (new_version) { + if (pbft_enabled) { new_producer_to_last_implied_irb[pro.producer_name] = bft_irreversible_blocknum; } else { new_producer_to_last_implied_irb[pro.producer_name] = dpos_irreversible_blocknum; @@ -168,13 +168,13 @@ namespace eosio { namespace chain { * * If the header specifies new_producers then apply them accordingly. */ - block_header_state block_header_state::next( const signed_block_header& h, bool skip_validate_signee, bool new_version )const { + block_header_state block_header_state::next( const signed_block_header& h, bool skip_validate_signee, bool pbft_enabled )const { EOS_ASSERT( h.timestamp != block_timestamp_type(), block_validate_exception, "", ("h",h) ); //EOS_ASSERT( h.header_extensions.size() == 0, block_validate_exception, "no supported extensions" ); EOS_ASSERT( h.timestamp > header.timestamp, block_validate_exception, "block must be later in time" ); EOS_ASSERT( h.previous == id, unlinkable_block_exception, "block must link to current state" ); - auto result = generate_next( h.timestamp, new_version); + auto result = generate_next( h.timestamp, pbft_enabled); EOS_ASSERT( result.header.producer == h.producer, wrong_producer, "wrong producer specified" ); EOS_ASSERT( result.header.schedule_version == h.schedule_version, producer_schedule_exception, "schedule_version in signed block is corrupted" ); @@ -189,10 +189,10 @@ namespace eosio { namespace chain { /// must result in header state changes - result.set_confirmed(h.confirmed, new_version); + result.set_confirmed(h.confirmed, pbft_enabled); - auto was_pending_promoted = result.maybe_promote_pending(new_version); + auto was_pending_promoted = result.maybe_promote_pending(pbft_enabled); if( h.new_producers ) { EOS_ASSERT( !was_pending_promoted, producer_schedule_exception, "cannot set pending producer schedule in the same block in which pending was promoted to active" ); @@ -214,7 +214,7 @@ namespace eosio { namespace chain { return result; } /// next - void block_header_state::set_confirmed( uint16_t num_prev_blocks, bool new_version ) { + void block_header_state::set_confirmed( uint16_t num_prev_blocks, bool pbft_enabled ) { /* idump((num_prev_blocks)(confirm_count.size())); @@ -222,7 +222,7 @@ namespace eosio { namespace chain { std::cerr << "confirm_count["<block_file.generic_string())); + block_stream.open(block_file.generic_string().c_str(), LOG_WRITE); + index_stream.open(index_file.generic_string().c_str(), LOG_WRITE); + + close(); + + block_stream.open(block_file.generic_string().c_str(), LOG_RW); + index_stream.open(index_file.generic_string().c_str(), LOG_RW); + + open_files = true; + } } block_log::block_log(const fc::path& data_dir) @@ -88,26 +84,21 @@ namespace eosio { namespace chain { block_log::~block_log() { if (my) { flush(); + my->close(); my.reset(); } } void block_log::open(const fc::path& data_dir) { - if (my->block_stream.is_open()) - my->block_stream.close(); - if (my->index_stream.is_open()) - my->index_stream.close(); + my->close(); if (!fc::is_directory(data_dir)) fc::create_directories(data_dir); + my->block_file = data_dir / "blocks.log"; my->index_file = data_dir / "blocks.index"; - //ilog("Opening block log at ${path}", ("path", my->block_file.generic_string())); - my->block_stream.open(my->block_file.generic_string().c_str(), LOG_WRITE); - my->index_stream.open(my->index_file.generic_string().c_str(), LOG_WRITE); - my->block_write = true; - my->index_write = true; + my->reopen(); /* On startup of the block log, there are several states the log file and the index file can be * in relation to each other. @@ -132,7 +123,6 @@ namespace eosio { namespace chain { if (log_size) { ilog("Log is nonempty"); - my->check_block_read(); my->block_stream.seekg( 0 ); my->version = 0; my->block_stream.read( (char*)&my->version, sizeof(my->version) ); @@ -152,12 +142,13 @@ namespace eosio { namespace chain { } my->head = read_head(); - my->head_id = my->head->id(); + if( my->head ) { + my->head_id = my->head->id(); + } else { + my->head_id = {}; + } if (index_size) { - my->check_block_read(); - my->check_index_read(); - ilog("Index is nonempty"); uint64_t block_pos; my->block_stream.seekg(-sizeof(uint64_t), std::ios::end); @@ -180,10 +171,9 @@ namespace eosio { namespace chain { } } else if (index_size) { ilog("Index is nonempty, remove and recreate it"); - my->index_stream.close(); + my->close(); fc::remove_all(my->index_file); - my->index_stream.open(my->index_file.generic_string().c_str(), LOG_WRITE); - my->index_write = true; + my->reopen(); } } @@ -191,9 +181,10 @@ namespace eosio { namespace chain { try { EOS_ASSERT( my->genesis_written_to_block_log, block_log_append_fail, "Cannot append to block log until the genesis is first written" ); - my->check_block_write(); - my->check_index_write(); + my->check_open_files(); + my->block_stream.seekp(0, std::ios::end); + my->index_stream.seekp(0, std::ios::end); uint64_t pos = my->block_stream.tellp(); EOS_ASSERT(my->index_stream.tellp() == sizeof(uint64_t) * (b->block_num() - my->first_block_num), block_log_append_fail, @@ -220,22 +211,17 @@ namespace eosio { namespace chain { } void block_log::reset( const genesis_state& gs, const signed_block_ptr& first_block, uint32_t first_block_num ) { - if (my->block_stream.is_open()) - my->block_stream.close(); - if (my->index_stream.is_open()) - my->index_stream.close(); + my->close(); fc::remove_all(my->block_file); fc::remove_all(my->index_file); - my->block_stream.open(my->block_file.generic_string().c_str(), LOG_WRITE); - my->index_stream.open(my->index_file.generic_string().c_str(), LOG_WRITE); - my->block_write = true; - my->index_write = true; + my->reopen(); auto data = fc::raw::pack(gs); my->version = 0; // version of 0 is invalid; it indicates that the genesis was not properly written to the block log my->first_block_num = first_block_num; + my->block_stream.seekp(0, std::ios::end); my->block_stream.write((char*)&my->version, sizeof(my->version)); my->block_stream.write((char*)&my->first_block_num, sizeof(my->first_block_num)); my->block_stream.write(data.data(), data.size()); @@ -251,22 +237,16 @@ namespace eosio { namespace chain { auto pos = my->block_stream.tellp(); - my->block_stream.close(); - my->block_stream.open(my->block_file.generic_string().c_str(), std::ios::in | std::ios::out | std::ios::binary ); // Bypass append-only writing just once - static_assert( block_log::max_supported_version > 0, "a version number of zero is not supported" ); my->version = block_log::max_supported_version; my->block_stream.seekp( 0 ); my->block_stream.write( (char*)&my->version, sizeof(my->version) ); my->block_stream.seekp( pos ); flush(); - - my->block_write = false; - my->check_block_write(); // Reset to append-only writing. } std::pair block_log::read_block(uint64_t pos)const { - my->check_block_read(); + my->check_open_files(); my->block_stream.seekg(pos); std::pair result; @@ -290,7 +270,7 @@ namespace eosio { namespace chain { } uint64_t block_log::get_block_pos(uint32_t block_num) const { - my->check_index_read(); + my->check_open_files(); if (!(my->head && block_num <= block_header::num_from_id(my->head_id) && block_num >= my->first_block_num)) return npos; my->index_stream.seekg(sizeof(uint64_t) * (block_num - my->first_block_num)); @@ -300,7 +280,7 @@ namespace eosio { namespace chain { } signed_block_ptr block_log::read_head()const { - my->check_block_read(); + my->check_open_files(); uint64_t pos; @@ -328,16 +308,22 @@ namespace eosio { namespace chain { void block_log::construct_index() { ilog("Reconstructing Block Log Index..."); - my->index_stream.close(); + my->close(); + fc::remove_all(my->index_file); - my->index_stream.open(my->index_file.generic_string().c_str(), LOG_WRITE); - my->index_write = true; + + my->reopen(); uint64_t end_pos; - my->check_block_read(); my->block_stream.seekg(-sizeof( uint64_t), std::ios::end); my->block_stream.read((char*)&end_pos, sizeof(end_pos)); + + if( end_pos == npos ) { + ilog( "Block log contains no blocks. No need to construct index." ); + return; + } + signed_block tmp; uint64_t pos = 0; @@ -357,6 +343,7 @@ namespace eosio { namespace chain { my->block_stream.read((char*) &totem, sizeof(totem)); } + my->index_stream.seekp(0, std::ios::end); while( pos < end_pos ) { fc::raw::unpack(my->block_stream, tmp); my->block_stream.read((char*)&pos, sizeof(pos)); diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 54cfd5c3211..77080583b1e 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -3,15 +3,15 @@ namespace eosio { namespace chain { - block_state::block_state( const block_header_state& prev, block_timestamp_type when, bool new_version ) - :block_header_state( prev.generate_next( when, new_version) ), + block_state::block_state( const block_header_state& prev, block_timestamp_type when, bool pbft_enabled ) + :block_header_state( prev.generate_next( when, pbft_enabled) ), block( std::make_shared() ) { static_cast(*block) = header; } - block_state::block_state( const block_header_state& prev, signed_block_ptr b, bool skip_validate_signee, bool new_version ) - :block_header_state( prev.next( *b, skip_validate_signee, new_version)), block( move(b) ) + block_state::block_state( const block_header_state& prev, signed_block_ptr b, bool skip_validate_signee, bool pbft_enabled ) + :block_header_state( prev.next( *b, skip_validate_signee, pbft_enabled)), block( move(b) ) { } diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index c6f611de03c..2aa4644afb4 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -127,8 +127,6 @@ struct controller_impl { bool pbft_upgrading = false; optional pending_pbft_lib; optional pending_pbft_checkpoint; - vector proposed_schedule_blocks; - vector promoted_schedule_blocks; block_state_ptr pbft_prepared; block_state_ptr my_prepare; block_state_ptr head; @@ -362,7 +360,7 @@ struct controller_impl { read_from_snapshot( snapshot ); //do upgrade migration if necessary; - migrate_upgrade(); //compatiable for snapshot integrity test + update_pbft_status(); //compatiable for snapshot integrity test auto end = blog.read_head(); if( !end ) { @@ -377,7 +375,7 @@ struct controller_impl { } } else { //do upgrade migration if necessary; - migrate_upgrade(); //compatiable for snapshot integrity test + update_pbft_status(); //compatiable for snapshot integrity test if( !head ) { initialize_fork_db(); // set head to genesis state } @@ -428,43 +426,35 @@ struct controller_impl { //*bos end* } - void migrate_upgrade() { - //generate upo. - try { - db.get(); - if (pbft_enabled) wlog("pbft enabled"); - } catch( const boost::exception& e) { - wlog("no upo found, generating..."); - db.create([](auto&){}); - } - update_pbft_status(); - } - void update_pbft_status() { - auto utb = optional{}; - auto& upo = db.get(); - if (upo.upgrade_target_block_num > 0) utb = upo.upgrade_target_block_num; - - auto ucb = optional{}; - if (upo.upgrade_complete_block_num > 0) ucb = upo.upgrade_complete_block_num; + try { + auto utb = optional{}; + auto& upo = db.get(); + if (upo.upgrade_target_block_num > 0) utb = upo.upgrade_target_block_num; + auto ucb = optional{}; + if (upo.upgrade_complete_block_num > 0) ucb = upo.upgrade_complete_block_num; - if (utb && !ucb && head->dpos_irreversible_blocknum >= *utb) { - db.modify( upo, [&]( auto& up ) { - up.upgrade_complete_block_num = head->block_num; - }); - if (!replaying) wlog("pbft will be working after the block ${b}", ("b", head->block_num)); - } + if (utb && !ucb && head->dpos_irreversible_blocknum >= *utb) { + db.modify( upo, [&]( auto& up ) { + up.upgrade_complete_block_num = head->block_num; + }); + if (!replaying) wlog("pbft will be working after the block ${b}", ("b", head->block_num)); + } - if ( !pbft_enabled && utb && head->block_num >= *utb) { - if (!pbft_upgrading) pbft_upgrading = true; + if ( !pbft_enabled && utb && head->block_num >= *utb) { + if (!pbft_upgrading) pbft_upgrading = true; - // new version starts from the next block of ucb, this is to avoid inconsistency after pre calculation inside schedule loop. - if (ucb && head->block_num > *ucb) { + // new version starts from the next block of ucb, this is to avoid inconsistency after pre calculation inside schedule loop. + if (ucb && head->block_num > *ucb) { if (pbft_upgrading) pbft_upgrading = false; pbft_enabled = true; - } - } + } + } + } catch( const boost::exception& e) { + wlog("no upo found, generating..."); + db.create([](auto&){}); + } } ~controller_impl() { @@ -553,24 +543,20 @@ struct controller_impl { section.add_row(conf.genesis, db); }); - snapshot->write_section([this]( auto §ion ){ - section.add_row(batch_pbft_snapshot_migration{}, db); - }); + snapshot->write_section([]( auto §ion ){}); + + auto lscb = fork_db.get_block_in_current_chain_by_num(fork_db.head()->pbft_stable_checkpoint_blocknum); + if (pbft_enabled && lscb) { + snapshot->write_section([]( auto §ion ) {}); - if (pbft_enabled) { - snapshot->write_section([this]( auto §ion ) { - section.add_row(batch_pbft_enabled{}, db); - }); - snapshot->write_section([this](auto §ion) { - auto bid = fork_db.get_block_in_current_chain_by_num(fork_db.head()->pbft_stable_checkpoint_blocknum)->id; - EOS_ASSERT(bid != block_id_type{}, snapshot_exception, "cannot find lscb block"); - auto bss = fork_db.fetch_branch_from(fork_db.head()->id, bid).first; - section.template add_row(bss, db); - }); + snapshot->write_section([this, &lscb](auto §ion) { + auto bss = fork_db.fetch_branch_from(fork_db.head()->id, lscb->id).first; + section.template add_row(bss, db); + }); } else { - snapshot->write_section([this]( auto §ion ){ - section.template add_row(*fork_db.head(), db); - }); + snapshot->write_section([this]( auto §ion ) { + section.template add_row(*fork_db.head(), db); + }); } controller_index_set::walk_indices([this, &snapshot]( auto utils ){ @@ -601,54 +587,43 @@ struct controller_impl { header.validate(); }); - bool migrated = snapshot->has_section(); - if (migrated) { - auto upgraded = snapshot->has_section(); - if (upgraded) { - snapshot->read_section([this](auto §ion) { - branch_type bss; - section.template read_row(bss, db); - EOS_ASSERT(!bss.empty(), snapshot_exception, "no last stable checkpoint block in the snapshot"); - - wlog("${n} reversible blocks found in the snapshot", ("n", bss.size())); - - for (auto i = bss.rbegin(); i != bss.rend(); ++i ) { - if (i == bss.rbegin()) { - fork_db.set(*i); - snapshot_head_block = (*i)->block_num; - } else { - fork_db.add((*i), true, true); - } - fork_db.set_validity((*i), true); - fork_db.mark_in_current_chain((*i), true); - } - head = fork_db.head(); - }); - } else { - snapshot->read_section([this](auto §ion) { - block_header_state head_header_state; - section.read_row(head_header_state, db); - - auto head_state = std::make_shared(head_header_state); - fork_db.set(head_state); - fork_db.set_validity(head_state, true); - fork_db.mark_in_current_chain(head_state, true); - head = head_state; - snapshot_head_block = head->block_num; - }); - } + bool migrated = snapshot->has_section(); + auto upgraded = snapshot->has_section(); + if (migrated && upgraded) { + snapshot->read_section([this](auto §ion) { + branch_type bss; + section.template read_row(bss, db); + if (bss.empty()) elog( "no last stable checkpoint block found in the snapshot, perhaps corrupted"); + + ilog("${n} fork_db blocks found in the snapshot", ("n", bss.size())); + + for (auto i = bss.rbegin(); i != bss.rend(); ++i ) { + if (i == bss.rbegin()) { + fork_db.set(*i); + snapshot_head_block = (*i)->block_num; + } else { + fork_db.add((*i), true, true); + } + fork_db.set_validity((*i), true); + fork_db.mark_in_current_chain((*i), true); + } + head = fork_db.head(); + }); } else { - snapshot->read_section([this](snapshot_reader::section_reader §ion) { + snapshot->read_section([this, &migrated](snapshot_reader::section_reader §ion) { block_header_state head_header_state; - section.read_pbft_migrate_row(head_header_state, db); - + if (migrated) { + section.read_row(head_header_state, db); + } else { + section.read_pbft_migrate_row(head_header_state, db); + } auto head_state = std::make_shared(head_header_state); fork_db.set(head_state); fork_db.set_validity(head_state, true); fork_db.mark_in_current_chain(head_state, true); head = head_state; snapshot_head_block = head->block_num; - }); + }); } @@ -661,14 +636,14 @@ struct controller_impl { } if(snapshot->has_section()){ - snapshot->read_section([this]( auto& section ) { + snapshot->read_section([this]( auto& section ) { bool more = !section.empty(); while(more) { - decltype(utils)::create(db, [this, §ion, &more]( auto &row ) { + decltype(utils)::create(db, [this, §ion, &more]( auto &row ) { more = section.read_row(row, db); - }); + }); } - }); + }); } }); @@ -931,26 +906,6 @@ struct controller_impl { } // "bos end" - optional upgrade_target_block() { - - const auto& upo = db.get(); - if (upo.upgrade_target_block_num > 0) { - return upo.upgrade_target_block_num; - } else { - return optional{}; - } - } - - optional upgrade_complete_block() { - - const auto& upo = db.get(); - if (upo.upgrade_complete_block_num > 0) { - return upo.upgrade_complete_block_num; - } else { - return optional{}; - } - } - /** * @post regardless of the success of commit block there is no active pending block */ @@ -979,6 +934,10 @@ struct controller_impl { }); } + if (pbft_enabled && pending->_pending_block_state->pbft_watermark) { + if (auto bs = fork_db.get_block(pending->_pending_block_state->id)) fork_db.mark_as_pbft_watermark(bs); + } + emit( self.accepted_block, pending->_pending_block_state ); } catch (...) { // dont bother resetting pending, instead abort the block @@ -1422,17 +1381,10 @@ struct controller_impl { const auto& gpo = db.get(); auto lib_num = std::max(pending->_pending_block_state->dpos_irreversible_blocknum, pending->_pending_block_state->bft_irreversible_blocknum); - auto lscb_num = pending->_pending_block_state->pbft_stable_checkpoint_blocknum; if (pbft_enabled && gpo.proposed_schedule_block_num) { - proposed_schedule_blocks.emplace_back(*gpo.proposed_schedule_block_num); - for ( auto itr = proposed_schedule_blocks.begin(); itr != proposed_schedule_blocks.end();) { - if ((*itr) < lscb_num) { - itr = proposed_schedule_blocks.erase(itr); - } else { - ++itr; - } - } + auto bs = fork_db.get_block_in_current_chain_by_num(*gpo.proposed_schedule_block_num); + if (bs) fork_db.mark_as_pbft_watermark(bs); } bool should_promote_pending_schedule = false; @@ -1464,14 +1416,7 @@ struct controller_impl { pending->_pending_block_state->set_new_producers(gpo.proposed_schedule); if (pbft_enabled) { - promoted_schedule_blocks.emplace_back(pending->_pending_block_state->block_num); - for ( auto itr = promoted_schedule_blocks.begin(); itr != promoted_schedule_blocks.end();) { - if ((*itr) < lscb_num) { - itr = promoted_schedule_blocks.erase(itr); - } else { - ++itr; - } - } + pending->_pending_block_state->pbft_watermark = true; } } db.modify( gpo, [&]( auto& gp ) { @@ -2378,13 +2323,8 @@ block_id_type controller::last_stable_checkpoint_block_id() const { return block_id_type{}; } - -vector controller::proposed_schedule_block_nums() const { - return my->proposed_schedule_blocks; -} - -vector controller::promoted_schedule_block_nums() const { - return my->promoted_schedule_blocks; +vector controller::get_watermarks() const { + return my->fork_db.get_watermarks_in_forkdb(); } bool controller::is_replaying() const { @@ -2585,6 +2525,11 @@ void controller::set_pbft_my_prepare(const block_id_type& id) { } } +block_id_type controller::get_pbft_prepared() const { + if (my->pbft_prepared) return my->pbft_prepared->id; + return block_id_type{}; +} + block_id_type controller::get_pbft_my_prepare() const { if (my->my_prepare) return my->my_prepare->id; return block_id_type{}; @@ -2716,7 +2661,6 @@ producer_schedule_type controller::initial_schedule() const { return producer_schedule_type{ 0, {{eosio::chain::config::system_account_name, my->conf.genesis.initial_key}} }; } - bool controller::is_known_unexpired_transaction( const transaction_id_type& id) const { return db().find(id); } diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index d6624b54f04..2163a5a5960 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -17,6 +17,7 @@ namespace eosio { namespace chain { struct by_block_id; struct by_block_num; struct by_lib_block_num; + struct by_watermark; struct by_prev; typedef multi_index_container< block_state_ptr, @@ -32,13 +33,20 @@ namespace eosio { namespace chain { >, ordered_non_unique< tag, composite_key< block_state, - member, - member, - member, - member, - member + member, + member, + member, + member, + member >, composite_key_compare< std::greater, std::greater, std::greater, std::greater, std::greater > + >, + ordered_non_unique< tag, + composite_key< block_state, + member, + member + >, + composite_key_compare< std::greater<>, std::less<> > > > > fork_multi_index_type; @@ -127,8 +135,6 @@ namespace eosio { namespace chain { my->head = get_block( head_id ); } - - fc::remove( fork_db_dat ); } } @@ -187,7 +193,7 @@ namespace eosio { namespace chain { } } - block_state_ptr fork_database::add( const block_state_ptr& n, bool skip_validate_previous, bool new_version ) { + block_state_ptr fork_database::add( const block_state_ptr& n, bool skip_validate_previous, bool pbft_enabled ) { EOS_ASSERT( n, fork_database_exception, "attempt to add null block state" ); EOS_ASSERT( my->head, fork_db_block_not_found, "no head block set" ); @@ -202,7 +208,6 @@ namespace eosio { namespace chain { auto prior = my->index.find( n->block->previous ); - //TODO: to be optimised. if (prior != my->index.end()) { if ((*prior)->pbft_prepared) mark_pbft_prepared_fork(*prior); if ((*prior)->pbft_my_prepare) mark_pbft_my_prepare_fork(*prior); @@ -215,20 +220,20 @@ namespace eosio { namespace chain { auto oldest = *my->index.get().begin(); - auto should_prune_oldest = oldest->block_num < lib; - - if (new_version) { - should_prune_oldest = should_prune_oldest && oldest->block_num < checkpoint; - } - - if ( should_prune_oldest ) { + if (!pbft_enabled && oldest->block_num < lib) { prune( oldest ); + } else { + // prune all blocks below lscb + while (oldest->block_num < lib && oldest->block_num < checkpoint ) { + prune( oldest ); + oldest = *my->index.get().begin(); + } } return n; } - block_state_ptr fork_database::add( signed_block_ptr b, bool skip_validate_signee, bool new_version ) { + block_state_ptr fork_database::add( signed_block_ptr b, bool skip_validate_signee, bool pbft_enabled ) { EOS_ASSERT( b, fork_database_exception, "attempt to add null block" ); EOS_ASSERT( my->head, fork_db_block_not_found, "no head block set" ); const auto& by_id_idx = my->index.get(); @@ -238,9 +243,9 @@ namespace eosio { namespace chain { auto prior = by_id_idx.find( b->previous ); EOS_ASSERT( prior != by_id_idx.end(), unlinkable_block_exception, "unlinkable block", ("id", string(b->id()))("previous", string(b->previous)) ); - auto result = std::make_shared( **prior, move(b), skip_validate_signee, new_version); + auto result = std::make_shared( **prior, move(b), skip_validate_signee, pbft_enabled); EOS_ASSERT( result, fork_database_exception , "fail to add new block state" ); - return add(result, true, new_version); + return add(result, true, pbft_enabled); } const block_state_ptr& fork_database::head()const { return my->head; } @@ -601,5 +606,24 @@ namespace eosio { namespace chain { } } + vector fork_database::get_watermarks_in_forkdb() { + vector watermarks; + auto& pidx = my->index.get(); + auto pitr = pidx.begin(); + while (pitr != pidx.end() && (*pitr)->pbft_watermark) { + watermarks.emplace_back((*pitr)->block_num); //should consider only current_chain? + ++pitr; + } + return watermarks; + } + + void fork_database::mark_as_pbft_watermark( const block_state_ptr& h) { + auto& by_id_idx = my->index.get(); + auto itr = by_id_idx.find( h->id ); + EOS_ASSERT( itr != by_id_idx.end(), fork_db_block_not_found, "could not find block in fork database" ); + by_id_idx.modify( itr, [&]( auto& bsp ) { bsp->pbft_watermark = true; }); + } + + - } } /// eosio::chain + } } /// eosio::chain diff --git a/libraries/chain/include/eosio/chain/abi_serializer.hpp b/libraries/chain/include/eosio/chain/abi_serializer.hpp index 8f8fca4cdeb..06b9d2bb761 100644 --- a/libraries/chain/include/eosio/chain/abi_serializer.hpp +++ b/libraries/chain/include/eosio/chain/abi_serializer.hpp @@ -680,7 +680,7 @@ void abi_serializer::to_variant( const T& o, variant& vo, Resolver resolver, con impl::abi_traverse_context ctx(max_serialization_time); impl::abi_to_variant::add(mvo, "_", o, resolver, ctx); vo = std::move(mvo["_"]); -} FC_RETHROW_EXCEPTIONS(error, "Failed to serialize type", ("object",o)) +} FC_RETHROW_EXCEPTIONS(error, "Failed to serialize: ${type}", ("type", boost::core::demangle( typeid(o).name() ) )) template void abi_serializer::from_variant( const variant& v, T& o, Resolver resolver, const fc::microseconds& max_serialization_time ) try { diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index a443532013a..56d234cf10b 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -29,13 +29,13 @@ struct block_header_state { vector confirmations; uint32_t pbft_stable_checkpoint_blocknum = 0; - block_header_state next( const signed_block_header& h, bool trust = false, bool new_version = false)const; - block_header_state generate_next( block_timestamp_type when, bool new_version = false )const; + block_header_state next( const signed_block_header& h, bool trust = false, bool pbft_enabled = false )const; + block_header_state generate_next( block_timestamp_type when, bool pbft_enabled = false )const; void set_new_producers( producer_schedule_type next_pending ); - void set_confirmed( uint16_t num_prev_blocks, bool new_version = false ); + void set_confirmed( uint16_t num_prev_blocks, bool pbft_enabled = false ); void add_confirmation( const header_confirmation& c ); - bool maybe_promote_pending( bool new_version = false); + bool maybe_promote_pending( bool pbft_enabled = false ); bool has_pending_producers()const { return pending_schedule.producers.size(); } diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 3745beaaeaf..ef42af36bab 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -13,8 +13,8 @@ namespace eosio { namespace chain { struct block_state : public block_header_state { explicit block_state( const block_header_state& cur ):block_header_state(cur){} - block_state( const block_header_state& prev, signed_block_ptr b, bool skip_validate_signee, bool new_version ); - block_state( const block_header_state& prev, block_timestamp_type when, bool new_version ); + block_state( const block_header_state& prev, signed_block_ptr b, bool skip_validate_signee, bool pbft_enabled ); + block_state( const block_header_state& prev, block_timestamp_type when, bool pbft_enabled ); block_state() = default; /// weak_ptr prev_block_state.... @@ -23,6 +23,7 @@ namespace eosio { namespace chain { bool in_current_chain = false; bool pbft_prepared = false; bool pbft_my_prepare = false; + bool pbft_watermark = false; /// this data is redundant with the data stored in block, but facilitates /// recapturing transactions when we pop a block @@ -33,4 +34,4 @@ namespace eosio { namespace chain { } } /// namespace eosio::chain -FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(validated)(in_current_chain)(pbft_prepared)(pbft_my_prepare) ) +FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(validated)(in_current_chain)(pbft_prepared)(pbft_my_prepare)(pbft_watermark) ) diff --git a/libraries/chain/include/eosio/chain/chain_snapshot.hpp b/libraries/chain/include/eosio/chain/chain_snapshot.hpp index 43801333d2f..58ea0d45725 100644 --- a/libraries/chain/include/eosio/chain/chain_snapshot.hpp +++ b/libraries/chain/include/eosio/chain/chain_snapshot.hpp @@ -29,16 +29,11 @@ struct chain_snapshot_header { } }; -struct batch_pbft_snapshot_migration{ - bool migrated = true; -}; +struct batch_pbft_snapshot_migrated{}; -struct batch_pbft_enabled { - bool enabled = true; -}; +struct batch_pbft_enabled{}; +struct batch_pbft_lscb_branch{}; } } -FC_REFLECT(eosio::chain::chain_snapshot_header,(version)) -FC_REFLECT(eosio::chain::batch_pbft_snapshot_migration,(migrated)) -FC_REFLECT(eosio::chain::batch_pbft_enabled,(enabled)) \ No newline at end of file +FC_REFLECT(eosio::chain::chain_snapshot_header,(version)) \ No newline at end of file diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 9bc79f70eee..7a90125da6e 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -168,8 +168,7 @@ namespace eosio { namespace chain { bool pending_pbft_lib(); - vector proposed_schedule_block_nums()const; - vector promoted_schedule_block_nums()const; + vector get_watermarks() const; void set_pbft_latest_checkpoint( const block_id_type& id ); uint32_t last_stable_checkpoint_block_num()const; @@ -291,6 +290,7 @@ namespace eosio { namespace chain { void set_pbft_prepared(const block_id_type& id); void set_pbft_my_prepare(const block_id_type& id); + block_id_type get_pbft_prepared()const; block_id_type get_pbft_my_prepare()const; void reset_pbft_my_prepare(); void reset_pbft_prepared(); diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 55e40580eb2..823c65c5b92 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -40,8 +40,8 @@ namespace eosio { namespace chain { * block_state and will return a pointer to the new block state or * throw on error. */ - block_state_ptr add( signed_block_ptr b, bool skip_validate_signee, bool new_version ); - block_state_ptr add( const block_state_ptr& next_block, bool skip_validate_previous, bool new_version ); + block_state_ptr add( signed_block_ptr b, bool skip_validate_signee, bool pbft_enabled ); + block_state_ptr add( const block_state_ptr& next_block, bool skip_validate_previous, bool pbft_enabled ); void remove( const block_id_type& id ); void add( const header_confirmation& c ); @@ -82,6 +82,10 @@ namespace eosio { namespace chain { void remove_pbft_prepared_fork(); + vector get_watermarks_in_forkdb(); + + void mark_as_pbft_watermark( const block_state_ptr& h); + private: unique_ptr my; }; diff --git a/libraries/chain/include/eosio/chain/pbft.hpp b/libraries/chain/include/eosio/chain/pbft.hpp index dd275943451..ba78d3ab1ea 100644 --- a/libraries/chain/include/eosio/chain/pbft.hpp +++ b/libraries/chain/include/eosio/chain/pbft.hpp @@ -1,3 +1,5 @@ +#include + #pragma once #include @@ -10,126 +12,110 @@ namespace eosio { using namespace fc; struct psm_cache { - vector prepares_cache; - vector commits_cache; - vector view_changes_cache; - vector prepared_certificate; - vector> committed_certificate; - vector view_changed_certificate; + pbft_prepare prepares_cache; + pbft_commit commits_cache; + pbft_view_change view_changes_cache; + pbft_prepared_certificate prepared_certificate; + vector committed_certificate; + pbft_view_changed_certificate view_changed_certificate; }; - class psm_machine { - class psm_state *current; + class psm_state; + using psm_state_ptr = std::shared_ptr; + + class psm_machine : public std::enable_shared_from_this { public: explicit psm_machine(pbft_database& pbft_db); ~psm_machine(); - void set_current(psm_state *s) { - current = s; + void set_current(psm_state_ptr s) { + current = std::move(s); } - void on_prepare(pbft_prepare &e); - void send_prepare(); + psm_state_ptr get_current() { + return current; + } - void on_commit(pbft_commit &e); - void send_commit(); + void on_prepare(pbft_metadata_ptr e); + void on_commit(pbft_metadata_ptr e); + void on_view_change(pbft_metadata_ptr e); + void on_new_view(const pbft_metadata_ptr &e); - void on_view_change(pbft_view_change &e); + void send_prepare(); + void send_commit(); void send_view_change(); - void on_new_view(pbft_new_view &e); - - template - void transit_to_committed_state(T const & s, bool to_new_view); - - template - void transit_to_prepared_state(T const & s); - - void send_pbft_view_change(); - - template - void transit_to_view_change_state(T const & s); - - template - void transit_to_new_view(const pbft_new_view &new_view, T const &s); - - const vector &get_prepares_cache() const; + void transit_to_committed_state(psm_state_ptr s, bool to_new_view); + void transit_to_prepared_state(psm_state_ptr s); + void transit_to_view_change_state(psm_state_ptr s); + void transit_to_new_view(const pbft_metadata_ptr& e, const psm_state_ptr& s); - void set_prepares_cache(const vector &pcache); + void do_send_view_change(); + bool maybe_new_view(const psm_state_ptr& s); - const vector &get_commits_cache() const; + const pbft_prepare& get_prepares_cache() const; + void set_prepares_cache(const pbft_prepare &pcache); - void set_commits_cache(const vector &ccache); + const pbft_commit& get_commits_cache() const; + void set_commits_cache(const pbft_commit &ccache); - const vector &get_view_changes_cache() const; - - void set_view_changes_cache(const vector &vc_cache); + const pbft_view_change& get_view_changes_cache() const; + void set_view_changes_cache(const pbft_view_change &vc_cache); const uint32_t &get_current_view() const; - void set_current_view(const uint32_t &cv); - const vector &get_prepared_certificate() const; - - void set_prepared_certificate(const vector &pcert); - - const vector> &get_committed_certificate() const; + const pbft_prepared_certificate& get_prepared_certificate() const; + void set_prepared_certificate(const pbft_prepared_certificate &pcert); - void set_committed_certificate(const vector> &ccert); + const vector& get_committed_certificate() const; + void set_committed_certificate(const vector &ccert); - const vector &get_view_changed_certificate() const; - - void set_view_changed_certificate(const vector &vc_cert); - - const uint32_t &get_target_view_retries() const; + const pbft_view_changed_certificate& get_view_changed_certificate() const; + void set_view_changed_certificate(const pbft_view_changed_certificate &vc_cert); + const uint32_t& get_target_view_retries() const; void set_target_view_retries(const uint32_t &tv_reties); - const uint32_t &get_target_view() const; - + const uint32_t& get_target_view() const; void set_target_view(const uint32_t &tv); - const uint32_t &get_view_change_timer() const; - + const uint32_t& get_view_change_timer() const; void set_view_change_timer(const uint32_t &vc_timer); void manually_set_current_view(const uint32_t &cv); protected: - psm_cache cache; - uint32_t current_view; - uint32_t target_view_retries; - uint32_t target_view; - uint32_t view_change_timer; + psm_cache cache; + uint32_t current_view; + uint32_t target_view_retries; + uint32_t target_view; + uint32_t view_change_timer; private: + psm_state_ptr current; pbft_database &pbft_db; - }; - class psm_state { + using psm_machine_ptr = std::shared_ptr; + + class psm_state : public std::enable_shared_from_this { public: psm_state(); ~psm_state(); - virtual void on_prepare(psm_machine *m, pbft_prepare &e, pbft_database &pbft_db) = 0; - - virtual void send_prepare(psm_machine *m, pbft_database &pbft_db) = 0; - - virtual void on_commit(psm_machine *m, pbft_commit &e, pbft_database &pbft_db) = 0; - - virtual void send_commit(psm_machine *m, pbft_database &pbft_db) = 0; + virtual void on_prepare(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) = 0; + virtual void on_commit(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) = 0; + virtual void on_view_change(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) = 0; - virtual void on_view_change(psm_machine *m, pbft_view_change &e, pbft_database &pbft_db) = 0; - - virtual void send_view_change(psm_machine *m, pbft_database &pbft_db) = 0; - - virtual void on_new_view(psm_machine *m, pbft_new_view &e, pbft_database &pbft_db) = 0; - - virtual void manually_set_view(psm_machine *m, const uint32_t &view) = 0; + virtual void send_prepare(psm_machine_ptr m, pbft_database &pbft_db) = 0; + virtual void send_commit(psm_machine_ptr m, pbft_database &pbft_db) = 0; + virtual void send_view_change(psm_machine_ptr m, pbft_database &pbft_db) = 0; + virtual const char* get_name() = 0; + std::shared_ptr get_self() { return shared_from_this(); }; }; class psm_prepared_state final: public psm_state { @@ -138,24 +124,17 @@ namespace eosio { psm_prepared_state(); ~psm_prepared_state(); - void on_prepare(psm_machine *m, pbft_prepare &e, pbft_database &pbft_db) override; - - void send_prepare(psm_machine *m, pbft_database &pbft_db) override; - - void on_commit(psm_machine *m, pbft_commit &e, pbft_database &pbft_db) override; - - void send_commit(psm_machine *m, pbft_database &pbft_db) override; - - void on_view_change(psm_machine *m, pbft_view_change &e, pbft_database &pbft_db) override; + void on_prepare(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) override; + void on_commit(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) override; + void on_view_change(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) override; - void send_view_change(psm_machine *m, pbft_database &pbft_db) override; - - void on_new_view(psm_machine *m, pbft_new_view &e, pbft_database &pbft_db) override; - - void manually_set_view(psm_machine *m, const uint32_t &view) override; + void send_prepare(psm_machine_ptr m, pbft_database &pbft_db) override; + void send_commit(psm_machine_ptr m, pbft_database &pbft_db) override; + void send_view_change(psm_machine_ptr m, pbft_database &pbft_db) override; bool pending_commit_local; + const char* get_name() override { return "{==== PREPARED ====}"; } }; class psm_committed_state final: public psm_state { @@ -163,75 +142,58 @@ namespace eosio { psm_committed_state(); ~psm_committed_state(); - void on_prepare(psm_machine *m, pbft_prepare &e, pbft_database &pbft_db) override; - - void send_prepare(psm_machine *m, pbft_database &pbft_db) override; - - void on_commit(psm_machine *m, pbft_commit &e, pbft_database &pbft_db) override; - - void send_commit(psm_machine *m, pbft_database &pbft_db) override; - - void on_view_change(psm_machine *m, pbft_view_change &e, pbft_database &pbft_db) override; + void on_prepare(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) override; + void on_commit(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) override; + void on_view_change(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) override; - void send_view_change(psm_machine *m, pbft_database &pbft_db) override; + void send_prepare(psm_machine_ptr m, pbft_database &pbft_db) override; + void send_commit(psm_machine_ptr m, pbft_database &pbft_db) override; + void send_view_change(psm_machine_ptr m, pbft_database &pbft_db) override; - void on_new_view(psm_machine *m, pbft_new_view &e, pbft_database &pbft_db) override; - - void manually_set_view(psm_machine *m, const uint32_t &view) override; - - bool pending_commit_local; + const char* get_name() override { return "{==== COMMITTED ====}"; } }; class psm_view_change_state final: public psm_state { public: - void on_prepare(psm_machine *m, pbft_prepare &e, pbft_database &pbft_db) override; - - void send_prepare(psm_machine *m, pbft_database &pbft_db) override; - - void on_commit(psm_machine *m, pbft_commit &e, pbft_database &pbft_db) override; - - void send_commit(psm_machine *m, pbft_database &pbft_db) override; + psm_view_change_state(); + ~psm_view_change_state(); - void on_view_change(psm_machine *m, pbft_view_change &e, pbft_database &pbft_db) override; + void on_prepare(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) override; + void on_commit(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) override; + void on_view_change(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) override; - void send_view_change(psm_machine *m, pbft_database &pbft_db) override; + void send_prepare(psm_machine_ptr m, pbft_database &pbft_db) override; + void send_commit(psm_machine_ptr m, pbft_database &pbft_db) override; + void send_view_change(psm_machine_ptr m, pbft_database &pbft_db) override; - void on_new_view(psm_machine *m, pbft_new_view &e, pbft_database &pbft_db) override; - - void manually_set_view(psm_machine *m, const uint32_t &view) override; - }; - - struct pbft_config { - uint32_t view_change_timeout = 6; - bool bp_candidate = false; + const char* get_name() override { return "{==== VIEW CHANGE ====}"; } }; class pbft_controller { public: - pbft_controller(controller& ctrl); + explicit pbft_controller(controller& ctrl); ~pbft_controller(); - pbft_database pbft_db; - psm_machine state_machine; - pbft_config config; + const uint16_t view_change_timeout = 6; + + pbft_database pbft_db; + std::shared_ptr state_machine; void maybe_pbft_prepare(); void maybe_pbft_commit(); void maybe_pbft_view_change(); - void send_pbft_checkpoint(); + void maybe_pbft_checkpoint(); - void on_pbft_prepare(pbft_prepare &p); - void on_pbft_commit(pbft_commit &c); - void on_pbft_view_change(pbft_view_change &vc); - void on_pbft_new_view(pbft_new_view &nv); - void on_pbft_checkpoint(pbft_checkpoint &cp); + void on_pbft_prepare(pbft_metadata_ptr p); + void on_pbft_commit(pbft_metadata_ptr c); + void on_pbft_view_change(pbft_metadata_ptr vc); + void on_pbft_new_view(const pbft_metadata_ptr &nv); + void on_pbft_checkpoint(const pbft_metadata_ptr &cp); private: - fc::path datadir; - - + fc::path datadir; }; } } /// namespace eosio::chain -FC_REFLECT(eosio::chain::pbft_controller, (pbft_db)(state_machine)(config)) \ No newline at end of file +FC_REFLECT(eosio::chain::pbft_controller, (pbft_db)(state_machine)) \ No newline at end of file diff --git a/libraries/chain/include/eosio/chain/pbft_database.hpp b/libraries/chain/include/eosio/chain/pbft_database.hpp index e680e57d934..7fd03ebb3e7 100644 --- a/libraries/chain/include/eosio/chain/pbft_database.hpp +++ b/libraries/chain/include/eosio/chain/pbft_database.hpp @@ -9,450 +9,322 @@ #include #include #include -#include -#include -#include +#include namespace eosio { namespace chain { using boost::multi_index_container; using namespace boost::multi_index; using namespace std; - using boost::uuids::uuid; + using pbft_view_type = uint32_t; - struct block_info { - block_id_type block_id; - block_num_type block_num = 0; + constexpr uint16_t pbft_checkpoint_granularity = 100; + constexpr uint16_t oldest_stable_checkpoint = 10000; + + enum class pbft_message_type : uint8_t { + prepare, + commit, + checkpoint, + view_change, + new_view }; - struct pbft_prepare { - string uuid; - uint32_t view; - block_num_type block_num = 0; - block_id_type block_id; - public_key_type public_key; - chain_id_type chain_id = chain_id_type(""); - signature_type producer_signature; - time_point timestamp = time_point::now(); - - - bool operator==(const pbft_prepare &rhs) const { - return view == rhs.view - && block_num == rhs.block_num - && block_id == rhs.block_id - && public_key == rhs.public_key - && chain_id == rhs.chain_id - && timestamp == rhs.timestamp; + struct block_info_type { + block_id_type block_id; + + block_num_type block_num() const { + return fc::endian_reverse_u32(block_id._hash[0]); } - bool operator!=(const pbft_prepare &rhs) const { - return !(*this == rhs); + bool operator==(const block_info_type &rhs) const { + return block_id == rhs.block_id; } - bool operator<(const pbft_prepare &rhs) const { - if (block_num < rhs.block_num) { - return true; - } else return block_num == rhs.block_num && view < rhs.view; + bool operator!=(const block_info_type &rhs) const { + return !(*this == rhs); } - digest_type digest() const { - digest_type::encoder enc; - fc::raw::pack(enc, view); - fc::raw::pack(enc, block_num); - fc::raw::pack(enc, block_id); - fc::raw::pack(enc, public_key); - fc::raw::pack(enc, chain_id); - fc::raw::pack(enc, timestamp); - return enc.result(); + bool empty() const { + return block_id == block_id_type(); } + }; + + struct pbft_message_common { + explicit pbft_message_common(pbft_message_type t): type{t} {}; - bool is_signature_valid() const { + pbft_message_type type; + time_point timestamp = time_point::now(); + + ~pbft_message_common() = default; + }; + + template + struct pbft_message_metadata { + explicit pbft_message_metadata(pbft_message_body m, chain_id_type chain_id): msg{m} { try { - auto pk = crypto::public_key(producer_signature, digest(), true); - return public_key == pk; + sender_key = crypto::public_key(msg.sender_signature, msg.digest(chain_id), true); } catch (fc::exception & /*e*/) { - return false; + wlog("bad pbft message signature: ${m}", ("m", msg)); } } + + pbft_message_body msg; + public_key_type sender_key; }; - struct pbft_commit { - string uuid; - uint32_t view; - block_num_type block_num = 0; - block_id_type block_id; - public_key_type public_key; - chain_id_type chain_id = chain_id_type(""); - signature_type producer_signature; - time_point timestamp = time_point::now(); - - - bool operator==(const pbft_commit &rhs) const { - return view == rhs.view - && block_num == rhs.block_num - && block_id == rhs.block_id - && public_key == rhs.public_key - && chain_id == rhs.chain_id - && timestamp == rhs.timestamp; - } + template + using pbft_metadata_ptr = std::shared_ptr>; - bool operator!=(const pbft_commit &rhs) const { - return !(*this == rhs); - } + struct pbft_prepare { + explicit pbft_prepare() = default; - bool operator<(const pbft_commit &rhs) const { - if (block_num < rhs.block_num) { + pbft_message_common common = pbft_message_common(pbft_message_type::prepare); + pbft_view_type view = 0; + block_info_type block_info; + signature_type sender_signature; + + bool operator<(const pbft_prepare &rhs) const { + if (block_info.block_num() < rhs.block_info.block_num()) { return true; - } else return block_num == rhs.block_num && view < rhs.view; + } else if (block_info.block_num() == rhs.block_info.block_num()) { + return view < rhs.view; + } else { + return false; + } } - digest_type digest() const { + bool empty() const { + return !view + && block_info.empty() + && sender_signature == signature_type(); + } + + digest_type digest(chain_id_type chain_id) const { digest_type::encoder enc; - fc::raw::pack(enc, view); - fc::raw::pack(enc, block_num); - fc::raw::pack(enc, block_id); - fc::raw::pack(enc, public_key); fc::raw::pack(enc, chain_id); - fc::raw::pack(enc, timestamp); + fc::raw::pack(enc, common); + fc::raw::pack(enc, view); + fc::raw::pack(enc, block_info); return enc.result(); } - - bool is_signature_valid() const { - try { - auto pk = crypto::public_key(producer_signature, digest(), true); - return public_key == pk; - } catch (fc::exception & /*e*/) { - return false; - } - } }; - struct pbft_checkpoint { - string uuid; - block_num_type block_num = 0; - block_id_type block_id; - public_key_type public_key; - chain_id_type chain_id = chain_id_type(""); - signature_type producer_signature; - time_point timestamp = time_point::now(); - - bool operator==(const pbft_checkpoint &rhs) const { - return block_num == rhs.block_num - && block_id == rhs.block_id - && public_key == rhs.public_key - && chain_id == rhs.chain_id - && timestamp == rhs.timestamp; + struct pbft_commit { + explicit pbft_commit() = default; - } + pbft_message_common common = pbft_message_common(pbft_message_type::commit); + pbft_view_type view = 0; + block_info_type block_info; + signature_type sender_signature; - bool operator!=(const pbft_checkpoint &rhs) const { - return !(*this == rhs); + bool operator<(const pbft_commit &rhs) const { + if (block_info.block_num() < rhs.block_info.block_num()) { + return true; + } else if (block_info.block_num() == rhs.block_info.block_num()) { + return view < rhs.view; + } else { + return false; + } } - bool operator<(const pbft_checkpoint &rhs) const { - return block_num < rhs.block_num; + bool empty() const { + return !view + && block_info.empty() + && sender_signature == signature_type(); } - digest_type digest() const { + digest_type digest(chain_id_type chain_id) const { digest_type::encoder enc; - fc::raw::pack(enc, block_num); - fc::raw::pack(enc, block_id); - fc::raw::pack(enc, public_key); fc::raw::pack(enc, chain_id); - fc::raw::pack(enc, timestamp); + fc::raw::pack(enc, common); + fc::raw::pack(enc, view); + fc::raw::pack(enc, block_info); return enc.result(); } - - bool is_signature_valid() const { - try { - auto pk = crypto::public_key(producer_signature, digest(), true); - return public_key == pk; - } catch (fc::exception & /*e*/) { - return false; - } - } }; - struct pbft_stable_checkpoint { - block_num_type block_num = 0; - block_id_type block_id; - vector checkpoints; - chain_id_type chain_id = chain_id_type(""); + using pbft_commit_ptr = std::shared_ptr; - bool operator==(const pbft_stable_checkpoint &rhs) const { - return block_id == rhs.block_id - && block_num == rhs.block_num - && checkpoints == rhs.checkpoints - && chain_id == rhs.chain_id; - } + struct pbft_checkpoint { + explicit pbft_checkpoint() = default; - bool operator!=(const pbft_stable_checkpoint &rhs) const { - return !(*this == rhs); - } + pbft_message_common common = pbft_message_common(pbft_message_type::checkpoint); + block_info_type block_info; + signature_type sender_signature; - bool operator<(const pbft_stable_checkpoint &rhs) const { - return block_num < rhs.block_num; + bool operator<(const pbft_checkpoint &rhs) const { + return block_info.block_num() < rhs.block_info.block_num(); } - digest_type digest() const { + digest_type digest(chain_id_type chain_id) const { digest_type::encoder enc; - fc::raw::pack(enc, block_num); - fc::raw::pack(enc, block_id); - fc::raw::pack(enc, checkpoints); fc::raw::pack(enc, chain_id); + fc::raw::pack(enc, common); + fc::raw::pack(enc, block_info); return enc.result(); } }; - struct pbft_prepared_certificate { - block_id_type block_id; - block_num_type block_num = 0; - vector prepares; + struct pbft_stable_checkpoint { + explicit pbft_stable_checkpoint() = default; - public_key_type public_key; - signature_type producer_signature; + block_info_type block_info; + vector checkpoints; - bool operator==(const pbft_prepared_certificate &rhs) const { - return block_num == rhs.block_num - && block_id == rhs.block_id - && prepares == rhs.prepares - && public_key == rhs.public_key; + bool operator<(const pbft_stable_checkpoint &rhs) const { + return block_info.block_num() < rhs.block_info.block_num(); } - bool operator!=(const pbft_prepared_certificate &rhs) const { - return !(*this == rhs); + bool empty() const { + return block_info == block_info_type() + && checkpoints.empty(); } + }; - digest_type digest() const { - digest_type::encoder enc; - fc::raw::pack(enc, block_id); - fc::raw::pack(enc, block_num); - fc::raw::pack(enc, prepares); - fc::raw::pack(enc, public_key); - return enc.result(); + struct pbft_prepared_certificate { + explicit pbft_prepared_certificate() = default; + + block_info_type block_info; + set pre_prepares; + vector prepares; + + bool operator<(const pbft_prepared_certificate &rhs) const { + return block_info.block_num() < rhs.block_info.block_num(); } - bool is_signature_valid() const { - try { - auto pk = crypto::public_key(producer_signature, digest(), true); - return public_key == pk; - } catch (fc::exception & /*e*/) { - return false; - } + bool empty() const { + return block_info == block_info_type() + && prepares.empty(); } }; struct pbft_committed_certificate { - block_id_type block_id; - block_num_type block_num = 0; - vector commits; - - public_key_type public_key; - signature_type producer_signature; + explicit pbft_committed_certificate() = default; - bool operator==(const pbft_committed_certificate &rhs) const { - return block_num == rhs.block_num - && block_id == rhs.block_id - && commits == rhs.commits - && public_key == rhs.public_key; - } - - bool operator!=(const pbft_committed_certificate &rhs) const { - return !(*this == rhs); - } + block_info_type block_info; + vector commits; bool operator<(const pbft_committed_certificate &rhs) const { - return block_num < rhs.block_num; + return block_info.block_num() < rhs.block_info.block_num(); } - digest_type digest() const { - digest_type::encoder enc; - fc::raw::pack(enc, block_id); - fc::raw::pack(enc, block_num); - fc::raw::pack(enc, commits); - fc::raw::pack(enc, public_key); - return enc.result(); - } - - bool is_signature_valid() const { - try { - auto pk = crypto::public_key(producer_signature, digest(), true); - return public_key == pk; - } catch (fc::exception & /*e*/) { - return false; - } + bool empty() const { + return block_info == block_info_type() + && commits.empty(); } }; - struct pbft_view_change { - string uuid; - uint32_t current_view; - uint32_t target_view; - pbft_prepared_certificate prepared; - vector committed; - pbft_stable_checkpoint stable_checkpoint; - public_key_type public_key; - chain_id_type chain_id = chain_id_type(""); - signature_type producer_signature; - time_point timestamp = time_point::now(); - - bool operator==(const pbft_view_change &rhs) const { - return current_view == rhs.current_view - && target_view == rhs.target_view - && prepared == rhs.prepared - && committed == rhs.committed - && stable_checkpoint == rhs.stable_checkpoint - && public_key == rhs.public_key - && chain_id == rhs.chain_id - && timestamp == rhs.timestamp; - } + explicit pbft_view_change() = default; - bool operator!=(const pbft_view_change &rhs) const { - return !(*this == rhs); - } + pbft_message_common common = pbft_message_common(pbft_message_type::view_change); + pbft_view_type current_view = 0; + pbft_view_type target_view = 1; + pbft_prepared_certificate prepared_cert; + vector committed_certs; + pbft_stable_checkpoint stable_checkpoint; + signature_type sender_signature; bool operator<(const pbft_view_change &rhs) const { return target_view < rhs.target_view; } - digest_type digest() const { + digest_type digest(chain_id_type chain_id) const { digest_type::encoder enc; + fc::raw::pack(enc, chain_id); + fc::raw::pack(enc, common); fc::raw::pack(enc, current_view); fc::raw::pack(enc, target_view); - fc::raw::pack(enc, prepared); - fc::raw::pack(enc, committed); + fc::raw::pack(enc, prepared_cert); + fc::raw::pack(enc, committed_certs); fc::raw::pack(enc, stable_checkpoint); - fc::raw::pack(enc, public_key); - fc::raw::pack(enc, chain_id); - fc::raw::pack(enc, timestamp); return enc.result(); } - bool is_signature_valid() const { - try { - auto pk = crypto::public_key(producer_signature, digest(), true); - return public_key == pk; - } catch (fc::exception & /*e*/) { - return false; - } + bool empty() const { + return !current_view + && target_view == 1 + && prepared_cert.empty() + && committed_certs.empty() + && stable_checkpoint.empty() + && sender_signature == signature_type(); } }; struct pbft_view_changed_certificate { - uint32_t view; - vector view_changes; + explicit pbft_view_changed_certificate() = default; - public_key_type public_key; - signature_type producer_signature; + pbft_view_type target_view = 0; + vector view_changes; - bool operator==(const pbft_view_changed_certificate &rhs) const { - return view == rhs.view - && view_changes == rhs.view_changes - && public_key == rhs.public_key; - } - - bool operator!=(const pbft_view_changed_certificate &rhs) const { - return !(*this == rhs); - } - - digest_type digest() const { - digest_type::encoder enc; - fc::raw::pack(enc, view); - fc::raw::pack(enc, view_changes); - fc::raw::pack(enc, public_key); - return enc.result(); - } - - bool is_signature_valid() const { - try { - auto pk = crypto::public_key(producer_signature, digest(), true); - return public_key == pk; - } catch (fc::exception & /*e*/) { - return false; - } + bool empty() const { + return !target_view + && view_changes.empty(); } }; struct pbft_new_view { - string uuid; - uint32_t view; - pbft_prepared_certificate prepared; - vector committed; - pbft_stable_checkpoint stable_checkpoint; - pbft_view_changed_certificate view_changed; - public_key_type public_key; - chain_id_type chain_id = chain_id_type(""); - signature_type producer_signature; - time_point timestamp = time_point::now(); - - bool operator==(const pbft_new_view &rhs) const { - return view == rhs.view - && prepared == rhs.prepared - && committed == rhs.committed - && stable_checkpoint == rhs.stable_checkpoint - && view_changed == rhs.view_changed - && public_key == rhs.public_key - && chain_id == rhs.chain_id - && timestamp == rhs.timestamp; - } + explicit pbft_new_view() = default; - bool operator!=(const pbft_new_view &rhs) const { - return !(*this == rhs); - } + pbft_message_common common = pbft_message_common(pbft_message_type::new_view); + pbft_view_type new_view = 0; + pbft_prepared_certificate prepared_cert; + vector committed_certs; + pbft_stable_checkpoint stable_checkpoint; + pbft_view_changed_certificate view_changed_cert; + signature_type sender_signature; bool operator<(const pbft_new_view &rhs) const { - return view < rhs.view; + return new_view < rhs.new_view; } - digest_type digest() const { + digest_type digest(chain_id_type chain_id) const { digest_type::encoder enc; - fc::raw::pack(enc, view); - fc::raw::pack(enc, prepared); - fc::raw::pack(enc, committed); - fc::raw::pack(enc, stable_checkpoint); - fc::raw::pack(enc, view_changed); - fc::raw::pack(enc, public_key); fc::raw::pack(enc, chain_id); - fc::raw::pack(enc, timestamp); + fc::raw::pack(enc, common); + fc::raw::pack(enc, new_view); + fc::raw::pack(enc, prepared_cert); + fc::raw::pack(enc, committed_certs); + fc::raw::pack(enc, stable_checkpoint); + fc::raw::pack(enc, view_changed_cert); return enc.result(); } - bool is_signature_valid() const { - try { - auto pk = crypto::public_key(producer_signature, digest(), true); - return public_key == pk; - } catch (fc::exception & /*e*/) { - return false; - } + bool empty() const { + return new_view == 0 + && prepared_cert.empty() + && committed_certs.empty() + && stable_checkpoint.empty() + && view_changed_cert.empty() + && sender_signature == signature_type(); } }; struct pbft_state { - block_id_type block_id; - block_num_type block_num = 0; - vector prepares; - bool should_prepared = false; - vector commits; - bool should_committed = false; + block_id_type block_id; + block_num_type block_num = 0; + flat_map, pbft_prepare> prepares; + bool is_prepared = false; + flat_map, pbft_commit> commits; + bool is_committed = false; }; - struct pbft_view_state { - uint32_t view; - vector view_changes; - bool should_view_changed = false; + struct pbft_view_change_state { + pbft_view_type view; + flat_map view_changes; + bool is_view_changed = false; }; struct pbft_checkpoint_state { - block_id_type block_id; - block_num_type block_num = 0; - vector checkpoints; - bool is_stable = false; + block_id_type block_id; + block_num_type block_num = 0; + flat_map checkpoints; + bool is_stable = false; }; using pbft_state_ptr = std::shared_ptr; - using pbft_view_state_ptr = std::shared_ptr; + using pbft_view_change_state_ptr = std::shared_ptr; using pbft_checkpoint_state_ptr = std::shared_ptr; struct by_block_id; @@ -462,63 +334,58 @@ namespace eosio { typedef multi_index_container< pbft_state_ptr, indexed_by< - hashed_unique< + hashed_unique < tag, member, std::hash >, ordered_non_unique< tag, - composite_key< - pbft_state, - member - >, - composite_key_compare> + member, + less<> >, ordered_non_unique< tag, composite_key< pbft_state, - member, + member, member >, - composite_key_compare, greater<>> + composite_key_compare< greater<>, greater<> > >, ordered_non_unique< tag, composite_key< pbft_state, - member, + member, member >, - composite_key_compare, greater<>> + composite_key_compare< greater<>, greater<> > > > - > - pbft_state_multi_index_type; + > pbft_state_multi_index_type; struct by_view; struct by_count_and_view; typedef multi_index_container< - pbft_view_state_ptr, + pbft_view_change_state_ptr, indexed_by< - hashed_unique< + ordered_unique< tag, - member, - std::hash + member, + greater<> >, ordered_non_unique< tag, composite_key< - pbft_view_state, - member, - member + pbft_view_change_state, + member, + member >, composite_key_compare, greater<>> > > - > - pbft_view_state_multi_index_type; + > pbft_view_state_multi_index_type; struct by_block_id; struct by_num; @@ -532,15 +399,11 @@ namespace eosio { >, ordered_non_unique< tag, - composite_key< - pbft_checkpoint_state, - member - >, - composite_key_compare> + member, + less<> > > - > - pbft_checkpoint_state_multi_index_type; + > pbft_checkpoint_state_multi_index_type; class pbft_database { public: @@ -550,82 +413,73 @@ namespace eosio { void close(); - bool should_prepared(); - - bool should_committed(); - - uint32_t should_view_change(); - - bool should_new_view(uint32_t target_view); - - bool is_new_primary(uint32_t target_view); - - uint32_t get_proposed_new_view_num(); - - void add_pbft_prepare(pbft_prepare &p); - - void add_pbft_commit(pbft_commit &c); - - void add_pbft_view_change(pbft_view_change &vc); - - void add_pbft_checkpoint(pbft_checkpoint &cp); - - vector send_and_add_pbft_prepare( - const vector &pv = vector{}, - uint32_t current_view = 0); - - vector send_and_add_pbft_commit( - const vector &cv = vector{}, - uint32_t current_view = 0); - - vector send_and_add_pbft_view_change( - const vector &vcv = vector{}, - const vector &ppc = vector{}, - const vector> &pcc = vector>{}, - uint32_t current_view = 0, - uint32_t new_view = 1); - + void add_pbft_prepare(pbft_prepare &p, const public_key_type &pk); + void add_pbft_commit(pbft_commit &c, const public_key_type &pk); + void add_pbft_view_change(pbft_view_change &vc, const public_key_type &pk); + void add_pbft_checkpoint(pbft_checkpoint &cp, const public_key_type &pk); + + pbft_prepare send_and_add_pbft_prepare(const pbft_prepare &cached_prepare = pbft_prepare(), pbft_view_type current_view = 0); + pbft_commit send_and_add_pbft_commit(const pbft_commit &cached_commit = pbft_commit(), pbft_view_type current_view = 0); + pbft_view_change send_and_add_pbft_view_change( + const pbft_view_change &cached_view_change = pbft_view_change(), + const pbft_prepared_certificate &ppc = pbft_prepared_certificate(), + const vector &pcc = vector{}, + pbft_view_type current_view = 0, + pbft_view_type target_view = 1); pbft_new_view send_pbft_new_view( - const vector &vcc = vector{}, - uint32_t current_view = 1); - + const pbft_view_changed_certificate &vcc = pbft_view_changed_certificate(), + pbft_view_type current_view = 1); vector generate_and_add_pbft_checkpoint(); + void send_pbft_checkpoint(); - bool is_valid_prepare(const pbft_prepare &p); + bool should_prepared(); + bool should_committed(); + pbft_view_type should_view_change(); + bool should_new_view(pbft_view_type target_view); - bool is_valid_commit(const pbft_commit &c); + //new view + bool has_new_primary(const public_key_type &pk); + pbft_view_type get_proposed_new_view_num(); + pbft_view_type get_committed_view(); + public_key_type get_new_view_primary_key(pbft_view_type target_view); + void mark_as_prepared(const block_id_type &bid); + void mark_as_committed(const block_id_type &bid); void commit_local(); + void checkpoint_local(); - bool pending_pbft_lib(); - - void prune_pbft_index(); - - uint32_t get_committed_view(); - - chain_id_type chain_id(); - - vector generate_prepared_certificate(); - - vector> generate_committed_certificate(); - - vector generate_view_changed_certificate(uint32_t target_view); - - pbft_stable_checkpoint get_stable_checkpoint_by_id(const block_id_type &block_id); - - pbft_stable_checkpoint fetch_stable_checkpoint_from_blk_extn(const signed_block_ptr &b); - - block_info cal_pending_stable_checkpoint() const; + //view change + pbft_prepared_certificate generate_prepared_certificate(); + vector generate_committed_certificate(); + pbft_view_changed_certificate generate_view_changed_certificate(pbft_view_type target_view); + bool should_stop_view_change(const pbft_view_change &vc); + //validations + bool is_valid_prepare(const pbft_prepare &p, const public_key_type &pk); + bool is_valid_commit(const pbft_commit &c, const public_key_type &pk); + bool is_valid_checkpoint(const pbft_checkpoint &cp, const public_key_type &pk); + bool is_valid_view_change(const pbft_view_change &vc, const public_key_type &pk); + void validate_new_view(const pbft_new_view &nv, const public_key_type &pk); + bool is_valid_stable_checkpoint(const pbft_stable_checkpoint &scp, bool add_to_pbft_db = false); bool should_send_pbft_msg(); - bool should_recv_pbft_msg(const public_key_type &pub_key); - void send_pbft_checkpoint(); + bool pending_pbft_lib(); + chain_id_type get_chain_id() {return chain_id;} + pbft_stable_checkpoint get_stable_checkpoint_by_id(const block_id_type &block_id, bool incl_blk_extn = true); + pbft_stable_checkpoint fetch_stable_checkpoint_from_blk_extn(const signed_block_ptr &b); + block_info_type cal_pending_stable_checkpoint() const; + + void cleanup_on_new_view(); + void update_fork_schedules(); - bool is_valid_checkpoint(const pbft_checkpoint &cp); + //api related + pbft_state_ptr get_pbft_state_by_id(const block_id_type &id) const; + vector get_checkpoints_by_num(const block_num_type &num) const; + pbft_view_change_state_ptr get_view_changes_by_target_view(const pbft_view_type &tv) const; + vector get_pbft_watermarks() const; + flat_map get_pbft_fork_schedules() const; - bool is_valid_stable_checkpoint(const pbft_stable_checkpoint &scp); signal pbft_outgoing_prepare; signal pbft_incoming_prepare; @@ -642,16 +496,6 @@ namespace eosio { signal pbft_outgoing_checkpoint; signal pbft_incoming_checkpoint; - bool is_valid_view_change(const pbft_view_change &vc); - - bool is_valid_new_view(const pbft_new_view &nv); - - bool should_stop_view_change(const pbft_view_change &vc); - - pbft_state_ptr get_pbft_state_by_id(const block_id_type& id)const; - - block_num_type get_current_pbft_watermark(); - private: controller &ctrl; pbft_state_multi_index_type pbft_state_index; @@ -659,55 +503,54 @@ namespace eosio { pbft_checkpoint_state_multi_index_type checkpoint_index; fc::path pbft_db_dir; fc::path checkpoints_dir; - boost::uuids::random_generator uuid_generator; vector prepare_watermarks; + flat_map fork_schedules; + chain_id_type chain_id = ctrl.get_chain_id(); - bool is_valid_prepared_certificate(const pbft_prepared_certificate &certificate); - - bool is_valid_committed_certificate(const pbft_committed_certificate &certificate); - - public_key_type get_new_view_primary_key(uint32_t target_view); - - vector> fetch_fork_from(vector block_infos); - - vector fetch_first_fork_from(vector &bi); - bool is_valid_longest_fork( - const block_info &bi, - vector block_infos, - unsigned long threshold, - unsigned long non_fork_bp_count); + bool is_less_than_high_watermark(const block_num_type &bnum); + bool is_valid_prepared_certificate(const pbft_prepared_certificate &certificate, bool add_to_pbft_db = false); + bool is_valid_committed_certificate(const pbft_committed_certificate &certificate, bool add_to_pbft_db = false); + bool is_valid_longest_fork(const block_info_type &bi, vector block_infos, unsigned long threshold, unsigned long non_fork_bp_count); producer_schedule_type lscb_active_producers() const; + vector& get_updated_watermarks(); + flat_map& get_updated_fork_schedules(); + block_num_type get_current_pbft_watermark(); + + vector> fetch_fork_from(vector &block_infos); + vector fetch_first_fork_from(vector &bi); template void emit(const Signal &s, Arg &&a); - void set(pbft_state_ptr s); - - void set(pbft_checkpoint_state_ptr s); - + void set(const pbft_state_ptr& s); + void set(const pbft_checkpoint_state_ptr& s); void prune(const pbft_state_ptr &h); - + void prune(const pbft_checkpoint_state_ptr &h); }; - } } /// namespace eosio::chain -FC_REFLECT(eosio::chain::block_info, (block_id)(block_num)) -FC_REFLECT(eosio::chain::pbft_prepare, - (uuid)(view)(block_num)(block_id)(public_key)(chain_id)(producer_signature)(timestamp)) -FC_REFLECT(eosio::chain::pbft_commit, - (uuid)(view)(block_num)(block_id)(public_key)(chain_id)(producer_signature)(timestamp)) -FC_REFLECT(eosio::chain::pbft_view_change, - (uuid)(current_view)(target_view)(prepared)(committed)(stable_checkpoint)(public_key)(chain_id)(producer_signature)(timestamp)) -FC_REFLECT(eosio::chain::pbft_new_view, - (uuid)(view)(prepared)(committed)(stable_checkpoint)(view_changed)(public_key)(chain_id)(producer_signature)(timestamp)) -FC_REFLECT(eosio::chain::pbft_state, (block_id)(block_num)(prepares)(should_prepared)(commits)(should_committed)) -FC_REFLECT(eosio::chain::pbft_prepared_certificate, (block_id)(block_num)(prepares)(public_key)(producer_signature)) -FC_REFLECT(eosio::chain::pbft_committed_certificate, (block_id)(block_num)(commits)(public_key)(producer_signature)) -FC_REFLECT(eosio::chain::pbft_view_changed_certificate, (view)(view_changes)(public_key)(producer_signature)) -FC_REFLECT(eosio::chain::pbft_checkpoint, - (uuid)(block_num)(block_id)(public_key)(chain_id)(producer_signature)(timestamp)) -FC_REFLECT(eosio::chain::pbft_stable_checkpoint, (block_num)(block_id)(checkpoints)(chain_id)) +FC_REFLECT(eosio::chain::block_info_type, (block_id)) +FC_REFLECT_ENUM(eosio::chain::pbft_message_type, (prepare)(commit)(checkpoint)(view_change)(new_view)) + +FC_REFLECT(eosio::chain::pbft_message_common, (type)(timestamp)) + +FC_REFLECT_TEMPLATE((typename pbft_message_body), eosio::chain::pbft_message_metadata, (msg)(sender_key)) + +FC_REFLECT(eosio::chain::pbft_prepare, (common)(view)(block_info)(sender_signature)) +FC_REFLECT(eosio::chain::pbft_commit, (common)(view)(block_info)(sender_signature)) +FC_REFLECT(eosio::chain::pbft_checkpoint,(common)(block_info)(sender_signature)) +FC_REFLECT(eosio::chain::pbft_view_change, (common)(current_view)(target_view)(prepared_cert)(committed_certs)(stable_checkpoint)(sender_signature)) +FC_REFLECT(eosio::chain::pbft_new_view, (common)(new_view)(prepared_cert)(committed_certs)(stable_checkpoint)(view_changed_cert)(sender_signature)) + + +FC_REFLECT(eosio::chain::pbft_prepared_certificate, (block_info)(pre_prepares)(prepares)) +FC_REFLECT(eosio::chain::pbft_committed_certificate,(block_info)(commits)) +FC_REFLECT(eosio::chain::pbft_view_changed_certificate, (target_view)(view_changes)) +FC_REFLECT(eosio::chain::pbft_stable_checkpoint, (block_info)(checkpoints)) + +FC_REFLECT(eosio::chain::pbft_state, (block_id)(block_num)(prepares)(is_prepared)(commits)(is_committed)) +FC_REFLECT(eosio::chain::pbft_view_change_state, (view)(view_changes)(is_view_changed)) FC_REFLECT(eosio::chain::pbft_checkpoint_state, (block_id)(block_num)(checkpoints)(is_stable)) \ No newline at end of file diff --git a/libraries/chain/include/eosio/chain/snapshot.hpp b/libraries/chain/include/eosio/chain/snapshot.hpp index 52b986510bd..b1a3a44892f 100644 --- a/libraries/chain/include/eosio/chain/snapshot.hpp +++ b/libraries/chain/include/eosio/chain/snapshot.hpp @@ -239,6 +239,7 @@ namespace eosio { namespace chain { fc::raw::unpack(tmp_ds, data); auto original_data_length = tmp_ds.tellp() - 4; in.seekg(original_data_length); + data.pbft_stable_checkpoint_blocknum = 0; }else{ fc::raw::unpack(in, data); } diff --git a/libraries/chain/pbft.cpp b/libraries/chain/pbft.cpp index 2236de50f5a..8789abce3cf 100644 --- a/libraries/chain/pbft.cpp +++ b/libraries/chain/pbft.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -5,9 +7,9 @@ namespace eosio { namespace chain { - pbft_controller::pbft_controller(controller &ctrl) : pbft_db(ctrl), state_machine(pbft_db) { - config.view_change_timeout = 6; - config.bp_candidate = true; + pbft_controller::pbft_controller(controller &ctrl) : + pbft_db(ctrl), + state_machine(new psm_machine(pbft_db)) { datadir = ctrl.state_dir(); if (!fc::is_directory(datadir)) @@ -21,9 +23,8 @@ namespace eosio { fc::datastream ds(content.data(), content.size()); uint32_t current_view; fc::raw::unpack(ds, current_view); - state_machine.set_current_view(current_view); - - state_machine.set_target_view(state_machine.get_current_view() + 1); + state_machine->set_current_view(current_view); + state_machine->set_target_view(state_machine->get_current_view() + 1); ilog("current view: ${cv}", ("cv", current_view)); } @@ -35,143 +36,154 @@ namespace eosio { std::ofstream out(pbft_db_dat.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc); - uint32_t current_view = state_machine.get_current_view(); + uint32_t current_view = state_machine->get_current_view(); fc::raw::pack(out, current_view); } void pbft_controller::maybe_pbft_prepare() { if (!pbft_db.should_send_pbft_msg()) return; - state_machine.send_prepare(); + state_machine->send_prepare(); } void pbft_controller::maybe_pbft_commit() { if (!pbft_db.should_send_pbft_msg()) return; - state_machine.send_commit(); + state_machine->send_commit(); } void pbft_controller::maybe_pbft_view_change() { if (!pbft_db.should_send_pbft_msg()) return; - if (state_machine.get_view_change_timer() <= config.view_change_timeout) { - if (!state_machine.get_view_changes_cache().empty()) { - pbft_db.send_and_add_pbft_view_change(state_machine.get_view_changes_cache()); + if (state_machine->get_view_change_timer() <= view_change_timeout) { + if (!state_machine->get_view_changes_cache().empty()) { + pbft_db.send_and_add_pbft_view_change(state_machine->get_view_changes_cache()); } - state_machine.set_view_change_timer(state_machine.get_view_change_timer() + 1); + state_machine->set_view_change_timer(state_machine->get_view_change_timer() + 1); } else { - state_machine.set_view_change_timer(0); - state_machine.send_view_change(); + state_machine->set_view_change_timer(0); + state_machine->send_view_change(); } } - void pbft_controller::on_pbft_prepare(pbft_prepare &p) { - if (!config.bp_candidate) return; - state_machine.on_prepare(p); + void pbft_controller::maybe_pbft_checkpoint() { + if (!pbft_db.should_send_pbft_msg()) return; + pbft_db.send_pbft_checkpoint(); + pbft_db.checkpoint_local(); } - void pbft_controller::on_pbft_commit(pbft_commit &c) { - if (!config.bp_candidate) return; - state_machine.on_commit(c); + void pbft_controller::on_pbft_prepare(pbft_metadata_ptr p) { + state_machine->on_prepare(std::move(p)); } - void pbft_controller::on_pbft_view_change(pbft_view_change &vc) { - if (!config.bp_candidate) return; - state_machine.on_view_change(vc); + void pbft_controller::on_pbft_commit(pbft_metadata_ptr c) { + state_machine->on_commit(std::move(c)); } - void pbft_controller::on_pbft_new_view(pbft_new_view &nv) { - if (!config.bp_candidate) return; - state_machine.on_new_view(nv); + void pbft_controller::on_pbft_view_change(pbft_metadata_ptr vc) { + state_machine->on_view_change(std::move(vc)); } - void pbft_controller::send_pbft_checkpoint() { - if (!pbft_db.should_send_pbft_msg()) return; - pbft_db.send_pbft_checkpoint(); + void pbft_controller::on_pbft_new_view(const pbft_metadata_ptr &nv) { + state_machine->on_new_view(nv); } - void pbft_controller::on_pbft_checkpoint(pbft_checkpoint &cp) { - pbft_db.add_pbft_checkpoint(cp); + void pbft_controller::on_pbft_checkpoint(const pbft_metadata_ptr &cp) { + if (!pbft_db.is_valid_checkpoint(cp->msg, cp->sender_key)) return; + pbft_db.add_pbft_checkpoint(cp->msg, cp->sender_key); + pbft_db.checkpoint_local(); } psm_state::psm_state() = default; - psm_state::~psm_state() = default; - psm_machine::psm_machine(pbft_database &pbft_db) : pbft_db(pbft_db) { - this->set_current(new psm_committed_state); + set_current(std::make_shared()); - this->set_prepares_cache(vector{}); - this->set_commits_cache(vector{}); - this->set_view_changes_cache(vector{}); + set_prepares_cache(pbft_prepare()); + set_commits_cache(pbft_commit()); + set_view_changes_cache(pbft_view_change()); - this->set_prepared_certificate(vector{}); - this->set_view_changed_certificate(vector{}); + set_prepared_certificate(pbft_prepared_certificate{}); + set_committed_certificate(vector{}); + set_view_changed_certificate(pbft_view_changed_certificate{}); - this->view_change_timer = 0; - this->target_view_retries = 0; - this->current_view = 0; - this->target_view = this->current_view + 1; + view_change_timer = 0; + target_view_retries = 0; + current_view = 0; + target_view = current_view + 1; } psm_machine::~psm_machine() = default; - void psm_machine::on_prepare(pbft_prepare &e) { - current->on_prepare(this, e, pbft_db); + void psm_machine::on_prepare(pbft_metadata_ptr e) { + current->on_prepare(shared_from_this(), std::move(e), pbft_db); } void psm_machine::send_prepare() { - current->send_prepare(this, pbft_db); + current->send_prepare(shared_from_this(), pbft_db); } - void psm_machine::on_commit(pbft_commit &e) { - current->on_commit(this, e, pbft_db); + void psm_machine::on_commit(pbft_metadata_ptr e) { + current->on_commit(shared_from_this(), std::move(e), pbft_db); } void psm_machine::send_commit() { - current->send_commit(this, pbft_db); + current->send_commit(shared_from_this(), pbft_db); } - void psm_machine::on_view_change(pbft_view_change &e) { - current->on_view_change(this, e, pbft_db); + void psm_machine::on_view_change(pbft_metadata_ptr e) { + current->on_view_change(shared_from_this(), std::move(e), pbft_db); } void psm_machine::send_view_change() { - current->send_view_change(this, pbft_db); + current->send_view_change(shared_from_this(), pbft_db); } - void psm_machine::on_new_view(pbft_new_view &e) { - current->on_new_view(this, e, pbft_db); + void psm_machine::on_new_view(const pbft_metadata_ptr &e) { + if (e->msg.new_view <= get_current_view()) return; + + try { + pbft_db.validate_new_view(e->msg, e->sender_key); + } catch (const fc::exception& ex) { + elog("bad new view, ${s} ", ("s",ex.to_string())); + return; + } + + try { + transit_to_new_view(e, current); + } catch(...) { + elog("apply new view failed, waiting for next round.. ${nv} ", ("nv", e->msg)); + } } void psm_machine::manually_set_current_view(const uint32_t &cv) { - current->manually_set_view(this, cv); + set_current_view(cv); + set_target_view(cv + 1); + transit_to_view_change_state(current); } /** * psm_prepared_state */ - psm_prepared_state::psm_prepared_state() { - pending_commit_local = false; - } - + psm_prepared_state::psm_prepared_state() {pending_commit_local = false;} psm_prepared_state::~psm_prepared_state() = default; - void psm_prepared_state::on_prepare(psm_machine *m, pbft_prepare &e, pbft_database &pbft_db) { + void psm_prepared_state::on_prepare(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) { //ignore } - void psm_prepared_state::send_prepare(psm_machine *m, pbft_database &pbft_db) { + void psm_prepared_state::send_prepare(psm_machine_ptr m, pbft_database &pbft_db) { //retry if (m->get_prepares_cache().empty()) return; pbft_db.send_and_add_pbft_prepare(m->get_prepares_cache(), m->get_current_view()); } - void psm_prepared_state::on_commit(psm_machine *m, pbft_commit &e, pbft_database &pbft_db) { + void psm_prepared_state::on_commit(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) { - if (e.view < m->get_current_view()) return; + if (e->msg.view < m->get_current_view()) return; + if (!pbft_db.is_valid_commit(e->msg, e->sender_key)) return; - pbft_db.add_pbft_commit(e); + pbft_db.add_pbft_commit(e->msg, e->sender_key); //`pending_commit_local` is used to mark committed local status in psm machine; //`pbft_db.pending_pbft_lib()` is used to mark commit local status in controller; @@ -184,12 +196,12 @@ namespace eosio { if (pending_commit_local && !pbft_db.pending_pbft_lib()) { pbft_db.send_pbft_checkpoint(); - m->transit_to_committed_state(this, false); + pbft_db.checkpoint_local(); + m->transit_to_committed_state(shared_from_this(), false); } } - - void psm_prepared_state::send_commit(psm_machine *m, pbft_database &pbft_db) { + void psm_prepared_state::send_commit(psm_machine_ptr m, pbft_database &pbft_db) { auto commits = pbft_db.send_and_add_pbft_commit(m->get_commits_cache(), m->get_current_view()); if (!commits.empty()) { @@ -203,66 +215,50 @@ namespace eosio { if (pending_commit_local && !pbft_db.pending_pbft_lib()) { pbft_db.send_pbft_checkpoint(); - m->transit_to_committed_state(this, false); + pbft_db.checkpoint_local(); + m->transit_to_committed_state(shared_from_this(), false); } } - void psm_prepared_state::on_view_change(psm_machine *m, pbft_view_change &e, pbft_database &pbft_db) { + void psm_prepared_state::on_view_change(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) { - if (e.target_view <= m->get_current_view()) return; + if (e->msg.target_view <= m->get_current_view()) return; + if (!pbft_db.is_valid_view_change(e->msg, e->sender_key)) return; - pbft_db.add_pbft_view_change(e); + pbft_db.add_pbft_view_change(e->msg, e->sender_key); //if received >= f+1 view_change on some view, transit to view_change and send view change auto target_view = pbft_db.should_view_change(); if (target_view > 0 && target_view > m->get_current_view()) { m->set_target_view(target_view); - m->transit_to_view_change_state(this); + m->transit_to_view_change_state(shared_from_this()); } } - void psm_prepared_state::send_view_change(psm_machine *m, pbft_database &pbft_db) { - m->transit_to_view_change_state(this); + void psm_prepared_state::send_view_change(psm_machine_ptr m, pbft_database &pbft_db) { + m->transit_to_view_change_state(shared_from_this()); } - void psm_prepared_state::on_new_view(psm_machine *m, pbft_new_view &e, pbft_database &pbft_db) { - - if (e.view <= m->get_current_view()) return; - - try { - m->transit_to_new_view(e, this); - } catch(const fc::exception& ex) { - wlog("bad new view, ${s} ", ("s",ex.to_string())); - } - } - - void psm_prepared_state::manually_set_view(psm_machine *m, const uint32_t ¤t_view) { - m->set_current_view(current_view); - m->set_target_view(current_view+1); - m->transit_to_view_change_state(this); - } - - psm_committed_state::psm_committed_state() { - pending_commit_local = false; - } + psm_committed_state::psm_committed_state() = default; psm_committed_state::~psm_committed_state() = default; /** * psm_committed_state */ - void psm_committed_state::on_prepare(psm_machine *m, pbft_prepare &e, pbft_database &pbft_db) { + void psm_committed_state::on_prepare(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) { //validate - if (e.view < m->get_current_view()) return; + if (e->msg.view < m->get_current_view()) return; + if (!pbft_db.is_valid_prepare(e->msg, e->sender_key)) return; //do action add prepare - pbft_db.add_pbft_prepare(e); + pbft_db.add_pbft_prepare(e->msg, e->sender_key); //if prepare >= 2f+1, transit to prepared - if (pbft_db.should_prepared()) m->transit_to_prepared_state(this); + if (pbft_db.should_prepared()) m->transit_to_prepared_state(shared_from_this()); } - void psm_committed_state::send_prepare(psm_machine *m, pbft_database &pbft_db) { + void psm_committed_state::send_prepare(psm_machine_ptr m, pbft_database &pbft_db) { auto prepares = pbft_db.send_and_add_pbft_prepare(m->get_prepares_cache(), m->get_current_view()); @@ -271,266 +267,197 @@ namespace eosio { } //if prepare >= 2f+1, transit to prepared - if (pbft_db.should_prepared()) m->transit_to_prepared_state(this); + if (pbft_db.should_prepared()) m->transit_to_prepared_state(shared_from_this()); } - void psm_committed_state::on_commit(psm_machine *m, pbft_commit &e, pbft_database &pbft_db) { + void psm_committed_state::on_commit(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) { - if (e.view < m->get_current_view()) return; + if (e->msg.view < m->get_current_view()) return; + if (!pbft_db.is_valid_commit(e->msg, e->sender_key)) return; - pbft_db.add_pbft_commit(e); + pbft_db.add_pbft_commit(e->msg, e->sender_key); } - void psm_committed_state::send_commit(psm_machine *m, pbft_database &pbft_db) { + void psm_committed_state::send_commit(psm_machine_ptr m, pbft_database &pbft_db) { if (m->get_commits_cache().empty()) return; pbft_db.send_and_add_pbft_commit(m->get_commits_cache(), m->get_current_view()); } - void psm_committed_state::on_view_change(psm_machine *m, pbft_view_change &e, pbft_database &pbft_db) { + void psm_committed_state::on_view_change(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) { - if (e.target_view <= m->get_current_view()) return; + if (e->msg.target_view <= m->get_current_view()) return; + if (!pbft_db.is_valid_view_change(e->msg, e->sender_key)) return; - pbft_db.add_pbft_view_change(e); + pbft_db.add_pbft_view_change(e->msg, e->sender_key); //if received >= f+1 view_change on some view, transit to view_change and send view change auto new_view = pbft_db.should_view_change(); if (new_view > 0 && new_view > m->get_current_view()) { m->set_target_view(new_view); - m->transit_to_view_change_state(this); - } - } - - void psm_committed_state::send_view_change(psm_machine *m, pbft_database &pbft_db) { - m->transit_to_view_change_state(this); - } - - void psm_committed_state::on_new_view(psm_machine *m, pbft_new_view &e, pbft_database &pbft_db) { - - if (e.view <= m->get_current_view()) return; - - try { - m->transit_to_new_view(e, this); - } catch(const fc::exception& ex) { - wlog("bad new view, ${s} ", ("s",ex.to_string())); + m->transit_to_view_change_state(shared_from_this()); } } - void psm_committed_state::manually_set_view(psm_machine *m, const uint32_t ¤t_view) { - m->set_current_view(current_view); - m->set_target_view(current_view+1); - m->transit_to_view_change_state(this); + void psm_committed_state::send_view_change(psm_machine_ptr m, pbft_database &pbft_db) { + m->transit_to_view_change_state(shared_from_this()); } + psm_view_change_state::psm_view_change_state() = default; + psm_view_change_state::~psm_view_change_state() = default; /** * psm_view_change_state */ - void psm_view_change_state::on_prepare(psm_machine *m, pbft_prepare &e, pbft_database &pbft_db) { + void psm_view_change_state::on_prepare(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) { //ignore; } - void psm_view_change_state::send_prepare(psm_machine *m, pbft_database &pbft_db) { + void psm_view_change_state::send_prepare(psm_machine_ptr m, pbft_database &pbft_db) { //ignore; } - void psm_view_change_state::on_commit(psm_machine *m, pbft_commit &e, pbft_database &pbft_db) { + void psm_view_change_state::on_commit(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) { //ignore; } - void psm_view_change_state::send_commit(psm_machine *m, pbft_database &pbft_db) { + void psm_view_change_state::send_commit(psm_machine_ptr m, pbft_database &pbft_db) { //ignore; } - void psm_view_change_state::on_view_change(psm_machine *m, pbft_view_change &e, pbft_database &pbft_db) { + void psm_view_change_state::on_view_change(psm_machine_ptr m, pbft_metadata_ptr e, pbft_database &pbft_db) { //skip from view change state if my lib is higher than my view change state height. auto vc = m->get_view_changes_cache(); - if (!vc.empty() && pbft_db.should_stop_view_change(vc.front())) { - m->transit_to_committed_state(this, false); + if (!vc.empty() && pbft_db.should_stop_view_change(vc)) { + m->transit_to_committed_state(shared_from_this(), false); return; } - if (e.target_view <= m->get_current_view()) return; - - pbft_db.add_pbft_view_change(e); + if (e->msg.target_view <= m->get_current_view()) return; + if (!pbft_db.is_valid_view_change(e->msg, e->sender_key)) return; - //if view_change >= 2f+1, calculate next primary, send new view if is primary - auto nv = m->get_target_view(); - if (pbft_db.should_new_view(nv) && pbft_db.is_new_primary(nv)) { - - m->set_view_changed_certificate(pbft_db.generate_view_changed_certificate(nv)); - - auto new_view = pbft_db.get_proposed_new_view_num(); - if (new_view != nv) return; - - auto nv_msg = pbft_db.send_pbft_new_view( - m->get_view_changed_certificate(), - new_view); - - if (nv_msg == pbft_new_view{}) return; + pbft_db.add_pbft_view_change(e->msg, e->sender_key); - try { - m->transit_to_new_view(nv_msg, this); - } catch(const fc::exception& ex) { - wlog("bad new view, ${s} ", ("s",ex.to_string())); - } - return; - } + m->maybe_new_view(shared_from_this()); } - void psm_view_change_state::send_view_change(psm_machine *m, pbft_database &pbft_db) { + void psm_view_change_state::send_view_change(psm_machine_ptr m, pbft_database &pbft_db) { //skip from view change state if my lib is higher than my view change state height. auto vc = m->get_view_changes_cache(); - if (!vc.empty() && pbft_db.should_stop_view_change(vc.front())) { - m->transit_to_committed_state(this, false); + if (!vc.empty() && pbft_db.should_stop_view_change(vc)) { + m->transit_to_committed_state(shared_from_this(), false); return; } - m->send_pbft_view_change(); - - //if view_change >= 2f+1, calculate next primary, send new view if is primary - auto nv = m->get_target_view(); - if (pbft_db.should_new_view(nv) && pbft_db.is_new_primary(nv)) { - - m->set_view_changed_certificate(pbft_db.generate_view_changed_certificate(nv)); + m->do_send_view_change(); - auto new_view = pbft_db.get_proposed_new_view_num(); - if (new_view != nv) return; - - auto nv_msg = pbft_db.send_pbft_new_view( - m->get_view_changed_certificate(), - new_view); - - if (nv_msg == pbft_new_view{}) return; - - try { - m->transit_to_new_view(nv_msg, this); - } catch(const fc::exception& ex) { - wlog("bad new view, ${s} ", ("s",ex.to_string())); - } - return; - } - } - - - void psm_view_change_state::on_new_view(psm_machine *m, pbft_new_view &e, pbft_database &pbft_db) { - - if (e.view <= m->get_current_view()) return; - - try { - m->transit_to_new_view(e, this); - } catch(const fc::exception& ex) { - wlog("bad new view, ${s} ", ("s",ex.to_string())); - } + m->maybe_new_view(shared_from_this()); } - void psm_view_change_state::manually_set_view(psm_machine *m, const uint32_t ¤t_view) { - m->set_current_view(current_view); - m->set_target_view(current_view+1); - m->transit_to_view_change_state(this); - } - - template - void psm_machine::transit_to_committed_state(T const & s, bool to_new_view) { + void psm_machine::transit_to_committed_state(psm_state_ptr s, bool to_new_view) { if (!to_new_view) { auto nv = pbft_db.get_committed_view(); - if (nv > this->get_current_view()) this->set_current_view(nv); - this->set_target_view(this->get_current_view() + 1); + if (nv > get_current_view()) set_current_view(nv); + set_target_view(get_current_view() + 1); } - auto prepares = this->pbft_db.send_and_add_pbft_prepare(vector{}, this->get_current_view()); + auto prepares = pbft_db.send_and_add_pbft_prepare(pbft_prepare(), get_current_view()); set_prepares_cache(prepares); - this->set_view_changes_cache(vector{}); - this->set_view_change_timer(0); + set_view_changes_cache(pbft_view_change()); + set_view_change_timer(0); - this->set_current(new psm_committed_state); - delete s; + set_current(std::make_shared()); + s.reset(); } - template - void psm_machine::transit_to_prepared_state(T const & s) { + void psm_machine::transit_to_prepared_state(psm_state_ptr s) { - auto commits = this->pbft_db.send_and_add_pbft_commit(vector{}, this->get_current_view()); + auto commits = pbft_db.send_and_add_pbft_commit(pbft_commit(), get_current_view()); set_commits_cache(commits); - this->set_view_changes_cache(vector{}); + set_view_changes_cache(pbft_view_change()); - this->set_current(new psm_prepared_state); - delete s; + set_current(std::make_shared()); + s.reset(); } - template - void psm_machine::transit_to_view_change_state(T const &s) { - - this->set_commits_cache(vector{}); - this->set_prepares_cache(vector{}); + void psm_machine::transit_to_view_change_state(psm_state_ptr s) { - this->set_view_change_timer(0); - this->set_target_view_retries(0); + set_commits_cache(pbft_commit()); + set_prepares_cache(pbft_prepare()); - this->set_current(new psm_view_change_state); - if (pbft_db.should_send_pbft_msg()) this->send_pbft_view_change(); + set_view_change_timer(0); + set_target_view_retries(0); - delete s; + set_current(std::make_shared()); + if (pbft_db.should_send_pbft_msg()) { + do_send_view_change(); + auto nv = maybe_new_view(s); + if (nv) return; + } + s.reset(); } - template - void psm_machine::transit_to_new_view(const pbft_new_view &new_view, T const &s) { + bool psm_machine::maybe_new_view(const psm_state_ptr &s) { + //if view_change >= 2f+1, calculate next primary, send new view if is primary + auto nv = get_target_view(); + auto pk = pbft_db.get_new_view_primary_key(nv); + if (pbft_db.should_new_view(nv) && pbft_db.has_new_primary(pk)) { - auto valid_nv = false; - try { - valid_nv = pbft_db.is_valid_new_view(new_view); - } catch (const fc::exception& ex) { - throw; - } - EOS_ASSERT(valid_nv, pbft_exception, "new view is not valid, waiting for next round.."); + set_view_changed_certificate(pbft_db.generate_view_changed_certificate(nv)); - this->set_current_view(new_view.view); - this->set_target_view(new_view.view + 1); + auto new_view = pbft_db.get_proposed_new_view_num(); + if (new_view != nv) return false; - this->set_prepares_cache(vector{}); + auto nv_msg = pbft_db.send_pbft_new_view( + get_view_changed_certificate(), + new_view); - this->set_view_change_timer(0); - this->set_target_view_retries(0); + if (nv_msg.empty()) return false; - this->pbft_db.prune_pbft_index(); + try { + pbft_db.validate_new_view(nv_msg, pk); + } catch (const fc::exception& ex) { + elog("bad new view, ${s} ", ("s", ex.to_string())); + return false; + } - if (!(new_view.stable_checkpoint == pbft_stable_checkpoint{})) { - for (auto cp :new_view.stable_checkpoint.checkpoints) { - try { - pbft_db.add_pbft_checkpoint(cp); - } catch (...) { - wlog( "checkpoint insertion failure: ${cp}", ("cp", cp)); - } + try { + transit_to_new_view(std::make_shared>(nv_msg, pbft_db.get_chain_id()), s); + return true; + } catch(const fc::exception& ex) { + elog("apply new view failed, waiting for next round.. ${nv} ", ("nv", nv_msg)); } } + return false; + } + + void psm_machine::transit_to_new_view(const pbft_metadata_ptr& e, const psm_state_ptr& s) { + + set_current_view(e->msg.new_view); + set_target_view(e->msg.new_view + 1); + + set_prepares_cache(pbft_prepare()); + + set_view_change_timer(0); + set_target_view_retries(0); + + pbft_db.cleanup_on_new_view(); - if (!new_view.committed.empty()) { - auto committed_certs = new_view.committed; + if (!e->msg.committed_certs.empty()) { + auto committed_certs = e->msg.committed_certs; std::sort(committed_certs.begin(), committed_certs.end()); - for (auto cp :committed_certs) { - for (auto c: cp.commits) { - try { - pbft_db.add_pbft_commit(c); - } catch (...) { - wlog( "commit insertion failure: ${cp}", ("cp", cp)); - } - } + for (auto const &cc :committed_certs) { + pbft_db.mark_as_committed(cc.block_info.block_id); } } - if (!new_view.prepared.prepares.empty()) { - for (auto p: new_view.prepared.prepares) { - try { - pbft_db.add_pbft_prepare(p); - } catch (...) { - wlog("prepare insertion failure: ${p}", ("p", p)); - } - } + if (!e->msg.prepared_cert.prepares.empty()) { + pbft_db.mark_as_prepared(e->msg.prepared_cert.block_info.block_id); if (pbft_db.should_prepared()) { transit_to_prepared_state(s); return; @@ -540,116 +467,116 @@ namespace eosio { transit_to_committed_state(s, true); } - void psm_machine::send_pbft_view_change() { - - if (this->get_target_view_retries() == 0) { - this->set_view_changes_cache(vector{}); - this->set_prepared_certificate(pbft_db.generate_prepared_certificate()); - this->set_committed_certificate(pbft_db.generate_committed_certificate()); - } + void psm_machine::do_send_view_change() { - EOS_ASSERT((this->get_target_view() > this->get_current_view()), pbft_exception, - "target view should be always greater than current view"); + auto reset_view_change_state = [&]() { + set_view_changes_cache(pbft_view_change()); + set_prepared_certificate(pbft_db.generate_prepared_certificate()); + set_committed_certificate(pbft_db.generate_committed_certificate()); + }; - if (this->get_target_view_retries() < pow(2, this->get_target_view() - this->get_current_view() - 1)) { - this->set_target_view_retries(this->get_target_view_retries() + 1); + if (get_target_view_retries() < pow(2,get_target_view() - get_current_view() - 1)) { + if (get_target_view_retries() == 0) reset_view_change_state(); + set_target_view_retries(get_target_view_retries() + 1); } else { - this->set_target_view_retries(0); - this->set_target_view(this->get_target_view() + 1); - this->set_view_changes_cache(vector{}); + set_target_view_retries(0); + set_target_view(get_target_view() + 1); + reset_view_change_state(); } + EOS_ASSERT((get_target_view() > get_current_view()), pbft_exception, + "target view should be always greater than current view"); + auto view_changes = pbft_db.send_and_add_pbft_view_change( - this->get_view_changes_cache(), - this->get_prepared_certificate(), - this->get_committed_certificate(), - this->get_current_view(), - this->get_target_view()); + get_view_changes_cache(), + get_prepared_certificate(), + get_committed_certificate(), + get_current_view(), + get_target_view()); if (!view_changes.empty()) { - this->set_view_changes_cache(view_changes); + set_view_changes_cache(view_changes); } } - const vector &psm_machine::get_prepares_cache() const { - return this->cache.prepares_cache; + const pbft_prepare& psm_machine::get_prepares_cache() const { + return cache.prepares_cache; } - void psm_machine::set_prepares_cache(const vector &pcache) { - this->cache.prepares_cache = pcache; + void psm_machine::set_prepares_cache(const pbft_prepare &pcache) { + cache.prepares_cache = pcache; } - const vector &psm_machine::get_commits_cache() const { - return this->cache.commits_cache; + const pbft_commit& psm_machine::get_commits_cache() const { + return cache.commits_cache; } - void psm_machine::set_commits_cache(const vector &ccache) { - this->cache.commits_cache = ccache; + void psm_machine::set_commits_cache(const pbft_commit &ccache) { + cache.commits_cache = ccache; } - const vector &psm_machine::get_view_changes_cache() const { - return this->cache.view_changes_cache; + const pbft_view_change& psm_machine::get_view_changes_cache() const { + return cache.view_changes_cache; } - void psm_machine::set_view_changes_cache(const vector &vc_cache) { - this->cache.view_changes_cache = vc_cache; + void psm_machine::set_view_changes_cache(const pbft_view_change &vc_cache) { + cache.view_changes_cache = vc_cache; } - const uint32_t &psm_machine::get_current_view() const { - return this->current_view; + const uint32_t& psm_machine::get_current_view() const { + return current_view; } void psm_machine::set_current_view(const uint32_t &cv) { - this->current_view = cv; + current_view = cv; } - const vector &psm_machine::get_prepared_certificate() const { - return this->cache.prepared_certificate; + const pbft_prepared_certificate& psm_machine::get_prepared_certificate() const { + return cache.prepared_certificate; } - void psm_machine::set_prepared_certificate(const vector &pcert) { - this->cache.prepared_certificate = pcert; + void psm_machine::set_prepared_certificate(const pbft_prepared_certificate &pcert) { + cache.prepared_certificate = pcert; } - const vector> &psm_machine::get_committed_certificate() const { - return this->cache.committed_certificate; + const vector& psm_machine::get_committed_certificate() const { + return cache.committed_certificate; } - void psm_machine::set_committed_certificate(const vector> &ccert) { - this->cache.committed_certificate = ccert; + void psm_machine::set_committed_certificate(const vector &ccert) { + cache.committed_certificate = ccert; } - const vector &psm_machine::get_view_changed_certificate() const { - return this->cache.view_changed_certificate; + const pbft_view_changed_certificate& psm_machine::get_view_changed_certificate() const { + return cache.view_changed_certificate; } - void psm_machine::set_view_changed_certificate( - const vector &vc_cert) { - this->cache.view_changed_certificate = vc_cert; + void psm_machine::set_view_changed_certificate(const pbft_view_changed_certificate &vc_cert) { + cache.view_changed_certificate = vc_cert; } - const uint32_t &psm_machine::get_target_view_retries() const { - return this->target_view_retries; + const uint32_t& psm_machine::get_target_view_retries() const { + return target_view_retries; } void psm_machine::set_target_view_retries(const uint32_t &tv_reties) { - this->target_view_retries = tv_reties; + target_view_retries = tv_reties; } - const uint32_t &psm_machine::get_target_view() const { - return this->target_view; + const uint32_t& psm_machine::get_target_view() const { + return target_view; } void psm_machine::set_target_view(const uint32_t &tv) { - this->target_view = tv; + target_view = tv; } - const uint32_t &psm_machine::get_view_change_timer() const { - return this->view_change_timer; + const uint32_t& psm_machine::get_view_change_timer() const { + return view_change_timer; } void psm_machine::set_view_change_timer(const uint32_t &vc_timer) { - this->view_change_timer = vc_timer; + view_change_timer = vc_timer; } } } \ No newline at end of file diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index 5440aec0db2..7bcaf19fa45 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -8,11 +8,12 @@ namespace eosio { pbft_database::pbft_database(controller &ctrl) : ctrl(ctrl) { - checkpoint_index = pbft_checkpoint_state_multi_index_type{}; - view_state_index = pbft_view_state_multi_index_type{}; + checkpoint_index = pbft_checkpoint_state_multi_index_type(); + view_state_index = pbft_view_state_multi_index_type(); prepare_watermarks = vector{}; pbft_db_dir = ctrl.state_dir(); checkpoints_dir = ctrl.blocks_dir(); + chain_id = ctrl.get_chain_id(); if (!fc::is_directory(pbft_db_dir)) fc::create_directories(pbft_db_dir); @@ -23,9 +24,8 @@ namespace eosio { fc::datastream ds(content.data(), content.size()); - // keep these unused variables. - uint32_t current_view; - fc::raw::unpack(ds, current_view); + //skip current_view in pbftdb.dat. + ds.seekp(ds.tellp() + 4); unsigned_int size; fc::raw::unpack(ds, size); @@ -34,18 +34,8 @@ namespace eosio { fc::raw::unpack(ds, s); set(std::make_shared(move(s))); } - - unsigned_int watermarks_size; - fc::raw::unpack(ds, watermarks_size); - for (uint32_t i = 0, n = watermarks_size.value; i < n; ++i) { - block_num_type h; - fc::raw::unpack(ds, h); - prepare_watermarks.emplace_back(h); - } - sort(prepare_watermarks.begin(), prepare_watermarks.end()); - } else { - pbft_state_index = pbft_state_multi_index_type{}; + pbft_state_index = pbft_state_multi_index_type(); } if (!fc::is_directory(checkpoints_dir)) fc::create_directories(checkpoints_dir); @@ -66,13 +56,14 @@ namespace eosio { } ilog("checkpoint index size: ${cs}", ("cs", checkpoint_index.size())); } else { - checkpoint_index = pbft_checkpoint_state_multi_index_type{}; + checkpoint_index = pbft_checkpoint_state_multi_index_type(); } + + fc::remove(checkpoints_db); } void pbft_database::close() { - fc::path checkpoints_db = checkpoints_dir / config::checkpoints_filename; std::ofstream c_out(checkpoints_db.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc); @@ -80,7 +71,7 @@ namespace eosio { uint32_t num_records_in_checkpoint_db = checkpoint_index.size(); fc::raw::pack(c_out, unsigned_int{num_records_in_checkpoint_db}); - for (const auto &s: checkpoint_index) { + for (auto const &s: checkpoint_index) { fc::raw::pack(c_out, *s); } @@ -90,80 +81,69 @@ namespace eosio { uint32_t num_records_in_db = pbft_state_index.size(); fc::raw::pack(out, unsigned_int{num_records_in_db}); - for (const auto &s : pbft_state_index) { + for (auto const &s : pbft_state_index) { fc::raw::pack(out, *s); } - - uint32_t watermarks_size = prepare_watermarks.size(); - fc::raw::pack(out, unsigned_int{watermarks_size}); - - for (const auto &n: prepare_watermarks) { - fc::raw::pack(out, n); - } - pbft_state_index.clear(); checkpoint_index.clear(); - prepare_watermarks.clear(); } pbft_database::~pbft_database() { close(); } - - void pbft_database::add_pbft_prepare(pbft_prepare &p) { - - if (!is_valid_prepare(p)) return; + void pbft_database::add_pbft_prepare(pbft_prepare &p, const public_key_type &pk) { auto &by_block_id_index = pbft_state_index.get(); - auto current = ctrl.fetch_block_state_by_id(p.block_id); + auto current = ctrl.fetch_block_state_by_id(p.block_info.block_id); while ((current) && (current->block_num > ctrl.last_irreversible_block_num())) { auto curr_itr = by_block_id_index.find(current->id); if (curr_itr == by_block_id_index.end()) { try { - auto curr_ps = pbft_state{current->id, current->block_num, {p}}; - auto curr_psp = make_shared(curr_ps); + flat_map, pbft_prepare> prepares; + prepares[std::make_pair(p.view, pk)] = p; + pbft_state curr_ps; + curr_ps.block_id = current->id; + curr_ps.block_num = current->block_num; + curr_ps.prepares = prepares; + auto curr_psp = std::make_shared(move(curr_ps)); pbft_state_index.insert(curr_psp); } catch (...) { - wlog( "prepare insert failure: ${p}", ("p", p)); + elog( "prepare insert failure: ${p}", ("p", p)); } } else { auto prepares = (*curr_itr)->prepares; - auto p_itr = find_if(prepares.begin(), prepares.end(), - [&](const pbft_prepare &prep) { - return prep.public_key == p.public_key && prep.view == p.view; - }); - if (p_itr == prepares.end()) { + if (prepares.find(std::make_pair(p.view, pk)) == prepares.end()) { by_block_id_index.modify(curr_itr, [&](const pbft_state_ptr &psp) { - psp->prepares.emplace_back(p); - std::sort(psp->prepares.begin(), psp->prepares.end(), less<>()); + psp->prepares[std::make_pair(p.view, pk)] = p; }); } } curr_itr = by_block_id_index.find(current->id); if (curr_itr == by_block_id_index.end()) return; - auto prepares = (*curr_itr)->prepares; + auto cpsp = *curr_itr; + auto prepares = cpsp->prepares; auto as = current->active_schedule.producers; - flat_map prepare_count; - for (const auto &pre: prepares) { - if (prepare_count.find(pre.view) == prepare_count.end()) prepare_count[pre.view] = 0; - } + auto threshold = as.size()* 2 / 3 + 1; + if (prepares.size() >= threshold && !cpsp->is_prepared && is_less_than_high_watermark(cpsp->block_num)) { + flat_map prepare_count; + for (auto const &pre: prepares) { + if (prepare_count.find(pre.second.view) == prepare_count.end()) prepare_count[pre.second.view] = 0; + } - if (!(*curr_itr)->should_prepared) { - for (auto const &sp: as) { + for (auto const &bp: as) { for (auto const &pp: prepares) { - if (sp.block_signing_key == pp.public_key) prepare_count[pp.view] += 1; + if (bp.block_signing_key == pp.first.second) prepare_count[pp.first.first] += 1; } } for (auto const &e: prepare_count) { - if (e.second >= as.size() * 2 / 3 + 1) { - by_block_id_index.modify(curr_itr, - [&](const pbft_state_ptr &psp) { psp->should_prepared = true; }); + if (e.second >= threshold) { + mark_as_prepared(cpsp->block_id); } } } @@ -171,102 +151,115 @@ namespace eosio { } } + void pbft_database::mark_as_prepared(const block_id_type &bid) { + auto &by_block_id_index = pbft_state_index.get(); + auto itr = by_block_id_index.find(bid); + auto bnum = block_info_type{bid}.block_num(); + + if (itr == by_block_id_index.end()) { + pbft_state ps; + ps.block_id = bid; + ps.block_num = bnum; + ps.is_prepared = true; + auto psp = std::make_shared(move(ps)); + pbft_state_index.insert(psp); + return; + } + by_block_id_index.modify(itr, [&](const pbft_state_ptr &p) { p->is_prepared = true; }); + } - vector pbft_database::send_and_add_pbft_prepare(const vector &pv, uint32_t current_view) { + pbft_prepare pbft_database::send_and_add_pbft_prepare(const pbft_prepare &cached_prepare, pbft_view_type current_view) { + auto prepare_to_be_cached = pbft_prepare(); auto head_block_num = ctrl.head_block_num(); - if (head_block_num <= 1) return vector{}; + if (head_block_num <= 1) return prepare_to_be_cached; auto my_prepare = ctrl.get_pbft_my_prepare(); auto reserve_prepare = [&](const block_id_type &in) { - if (in == block_id_type{} || !ctrl.fetch_block_state_by_id(in)) return false; + if (in == block_id_type() || !ctrl.fetch_block_state_by_id(in)) return false; auto lib = ctrl.last_irreversible_block_id(); - if (lib == block_id_type{}) return true; + if (lib == block_id_type()) return true; auto forks = ctrl.fork_db().fetch_branch_from(in, lib); return !forks.first.empty() && forks.second.empty(); }; - vector new_pv; - if (!pv.empty()) { - for (auto p : pv) { - //change uuid, sign again, update cache, then emit - auto uuid = boost::uuids::to_string(uuid_generator()); - p.uuid = uuid; - p.timestamp = time_point::now(); - p.producer_signature = ctrl.my_signature_providers()[p.public_key](p.digest()); - emit(pbft_outgoing_prepare, p); - } - return vector{}; + + if (!cached_prepare.empty()) { + for (auto const &sp : ctrl.my_signature_providers()) { + //sign again, update cache, then emit + auto retry_p = cached_prepare; + retry_p.common.timestamp = time_point::now(); + retry_p.sender_signature = sp.second(retry_p.digest(chain_id)); + emit(pbft_outgoing_prepare, retry_p); + } + return prepare_to_be_cached; } else if (reserve_prepare(my_prepare)) { for (auto const &sp : ctrl.my_signature_providers()) { - auto uuid = boost::uuids::to_string(uuid_generator()); - auto my_prepare_num = ctrl.fetch_block_state_by_id(my_prepare)->block_num; - auto p = pbft_prepare{uuid, current_view, my_prepare_num, my_prepare, sp.first, chain_id()}; - p.producer_signature = sp.second(p.digest()); - emit(pbft_outgoing_prepare, p); - new_pv.emplace_back(p); - } - return new_pv; + pbft_prepare reserve_p; + reserve_p.view=current_view; reserve_p.block_info={my_prepare}; + reserve_p.sender_signature = sp.second(reserve_p.digest(chain_id)); + emit(pbft_outgoing_prepare, reserve_p); + if (prepare_to_be_cached.empty()) prepare_to_be_cached = reserve_p; + } + return prepare_to_be_cached; } else { auto current_watermark = get_current_pbft_watermark(); auto lib = ctrl.last_irreversible_block_num(); - uint32_t high_water_mark_block_num = head_block_num; + uint32_t high_watermark_block_num = head_block_num; if ( current_watermark > 0 ) { - high_water_mark_block_num = std::min(head_block_num, current_watermark); + high_watermark_block_num = std::min(head_block_num, current_watermark); } - if (high_water_mark_block_num <= lib) return vector{}; + if (high_watermark_block_num <= lib) return prepare_to_be_cached; - block_id_type high_water_mark_block_id = ctrl.get_block_id_for_num(high_water_mark_block_num); - for (auto const &sp : ctrl.my_signature_providers()) { - auto uuid = boost::uuids::to_string(uuid_generator()); - auto p = pbft_prepare{uuid, current_view, high_water_mark_block_num, high_water_mark_block_id, - sp.first, chain_id()}; - p.producer_signature = sp.second(p.digest()); - add_pbft_prepare(p); - emit(pbft_outgoing_prepare, p); - new_pv.emplace_back(p); - ctrl.set_pbft_my_prepare(high_water_mark_block_id); + if (auto hwbs = ctrl.fork_db().get_block_in_current_chain_by_num(high_watermark_block_num)) { + auto sent = false; + for (auto const &sp : ctrl.my_signature_providers()) { + pbft_prepare new_p; + new_p.view=current_view; new_p.block_info={hwbs->id}; + new_p.sender_signature = sp.second(new_p.digest(chain_id)); + if (is_valid_prepare(new_p, sp.first)) { + emit(pbft_outgoing_prepare, new_p); + add_pbft_prepare(new_p, sp.first); + sent = true; + if (prepare_to_be_cached.empty()) prepare_to_be_cached = new_p; + } + } + if (sent) ctrl.set_pbft_my_prepare(hwbs->id); } - return new_pv; + return prepare_to_be_cached; } } bool pbft_database::should_prepared() { - const auto &by_prepare_and_num_index = pbft_state_index.get(); + auto const &by_prepare_and_num_index = pbft_state_index.get(); auto itr = by_prepare_and_num_index.begin(); if (itr == by_prepare_and_num_index.end()) return false; pbft_state_ptr psp = *itr; - auto current_watermark = get_current_pbft_watermark(); - if (psp->block_num > current_watermark && current_watermark > 0) return false; - - if (psp->should_prepared && (psp->block_num > ctrl.last_irreversible_block_num())) { + if (psp->is_prepared && (psp->block_num > ctrl.last_irreversible_block_num())) { ctrl.set_pbft_prepared((*itr)->block_id); return true; } return false; } - bool pbft_database::is_valid_prepare(const pbft_prepare &p) { - if (p.chain_id != chain_id()) return false; + bool pbft_database::is_valid_prepare(const pbft_prepare &p, const public_key_type &pk) { // a prepare msg under lscb (which is no longer in fork_db), can be treated as null, thus true. - if (p.block_num <= ctrl.last_stable_checkpoint_block_num()) return true; - if (!p.is_signature_valid()) return false; - return should_recv_pbft_msg(p.public_key); + if (p.block_info.block_num() <= ctrl.last_stable_checkpoint_block_num()) return true; + return should_recv_pbft_msg(pk); } - void pbft_database::add_pbft_commit(pbft_commit &c) { + void pbft_database::add_pbft_commit(pbft_commit &c, const public_key_type &pk) { - if (!is_valid_commit(c)) return; auto &by_block_id_index = pbft_state_index.get(); - auto current = ctrl.fetch_block_state_by_id(c.block_id); + auto current = ctrl.fetch_block_state_by_id(c.block_info.block_id); while ((current) && (current->block_num > ctrl.last_irreversible_block_num())) { @@ -274,21 +267,22 @@ namespace eosio { if (curr_itr == by_block_id_index.end()) { try { - auto curr_ps = pbft_state{current->id, current->block_num, .commits={c}}; - auto curr_psp = make_shared(curr_ps); + flat_map, pbft_commit> commits; + commits[std::make_pair(c.view, pk)] = c; + pbft_state curr_ps; + curr_ps.block_id = current->id; + curr_ps.block_num = current->block_num; + curr_ps.commits = commits; + auto curr_psp = std::make_shared(move(curr_ps)); pbft_state_index.insert(curr_psp); } catch (...) { - wlog("commit insertion failure: ${c}", ("c", c)); + elog("commit insertion failure: ${c}", ("c", c)); } } else { auto commits = (*curr_itr)->commits; - auto p_itr = find_if(commits.begin(), commits.end(), - [&](const pbft_commit &comm) { - return comm.public_key == c.public_key && comm.view == c.view; - }); - if (p_itr == commits.end()) { + if (commits.find(std::make_pair(c.view, pk)) == commits.end()) { by_block_id_index.modify(curr_itr, [&](const pbft_state_ptr &psp) { - psp->commits.emplace_back(c); + psp->commits[std::make_pair(c.view, pk)] = c; std::sort(psp->commits.begin(), psp->commits.end(), less<>()); }); } @@ -297,26 +291,26 @@ namespace eosio { curr_itr = by_block_id_index.find(current->id); if (curr_itr == by_block_id_index.end()) return; - auto commits = (*curr_itr)->commits; + auto cpsp = *curr_itr; - auto as = current->active_schedule; - flat_map commit_count; - for (const auto &com: commits) { - if (commit_count.find(com.view) == commit_count.end()) commit_count[com.view] = 0; - } - - if (!(*curr_itr)->should_committed) { + auto as = current->active_schedule.producers; + auto threshold = as.size()* 2 / 3 + 1; + auto commits = cpsp->commits; + if (commits.size() >= threshold && !cpsp->is_committed && is_less_than_high_watermark(cpsp->block_num)) { + flat_map commit_count; + for (auto const &com: commits) { + if (commit_count.find(com.second.view) == commit_count.end()) commit_count[com.second.view] = 0; + } - for (auto const &sp: as.producers) { + for (auto const &bp: as) { for (auto const &pc: commits) { - if (sp.block_signing_key == pc.public_key) commit_count[pc.view] += 1; + if (bp.block_signing_key == pc.first.second) commit_count[pc.first.first] += 1; } } for (auto const &e: commit_count) { - if (e.second >= current->active_schedule.producers.size() * 2 / 3 + 1) { - by_block_id_index.modify(curr_itr, - [&](const pbft_state_ptr &psp) { psp->should_committed = true; }); + if (e.second >= threshold) { + mark_as_committed(cpsp->block_id); } } } @@ -324,60 +318,67 @@ namespace eosio { } } - vector pbft_database::send_and_add_pbft_commit(const vector &cv, uint32_t current_view) { - if (!cv.empty()) { - for (auto c : cv) { - //change uuid, sign again, update cache, then emit - auto uuid = boost::uuids::to_string(uuid_generator()); - c.uuid = uuid; - c.timestamp = time_point::now(); - c.producer_signature = ctrl.my_signature_providers()[c.public_key](c.digest()); - emit(pbft_outgoing_commit, c); - } - return vector{}; + pbft_commit pbft_database::send_and_add_pbft_commit(const pbft_commit &cached_commit, pbft_view_type current_view) { + auto commit_to_be_cached = pbft_commit(); + + if (!cached_commit.empty()) { + for (auto const &sp : ctrl.my_signature_providers()) { + //sign again, update cache, then emit + auto retry_c = cached_commit; + retry_c.common.timestamp = time_point::now(); + retry_c.sender_signature = sp.second(retry_c.digest(chain_id)); + emit(pbft_outgoing_commit, retry_c); + } + return commit_to_be_cached; } else { - const auto &by_prepare_and_num_index = pbft_state_index.get(); + auto const &by_prepare_and_num_index = pbft_state_index.get(); auto itr = by_prepare_and_num_index.begin(); - if (itr == by_prepare_and_num_index.end()) { - return vector{}; - } - vector new_cv; + if (itr == by_prepare_and_num_index.end()) return commit_to_be_cached; + pbft_state_ptr psp = *itr; auto bs = ctrl.fork_db().get_block(psp->block_id); + if (!bs) return commit_to_be_cached; - if (psp->should_prepared && (psp->block_num > ctrl.last_irreversible_block_num())) { + if (psp->is_prepared && (psp->block_num > ctrl.last_irreversible_block_num())) { for (auto const &sp : ctrl.my_signature_providers()) { - auto uuid = boost::uuids::to_string(uuid_generator()); - auto c = pbft_commit{uuid, current_view, psp->block_num, psp->block_id, sp.first, chain_id()}; - c.producer_signature = sp.second(c.digest()); - add_pbft_commit(c); - emit(pbft_outgoing_commit, c); - new_cv.emplace_back(c); + pbft_commit new_c; + new_c.view=current_view; + new_c.block_info={psp->block_id}; + new_c.sender_signature = sp.second(new_c.digest(chain_id)); + + if (is_valid_commit(new_c, sp.first)) { + emit(pbft_outgoing_commit, new_c); + add_pbft_commit(new_c, sp.first); + if (commit_to_be_cached.empty()) commit_to_be_cached = new_c; + } } } - return new_cv; + return commit_to_be_cached; } } + void pbft_database::mark_as_committed(const block_id_type &bid) { + auto &by_block_id_index = pbft_state_index.get(); + auto itr = by_block_id_index.find(bid); + if (itr == by_block_id_index.end()) return; + by_block_id_index.modify(itr, [&](const pbft_state_ptr &p) { p->is_committed = true; }); + } + bool pbft_database::should_committed() { - const auto &by_commit_and_num_index = pbft_state_index.get(); + auto const &by_commit_and_num_index = pbft_state_index.get(); auto itr = by_commit_and_num_index.begin(); if (itr == by_commit_and_num_index.end()) return false; pbft_state_ptr psp = *itr; - auto current_watermark = get_current_pbft_watermark(); - - if (psp->block_num > current_watermark && current_watermark > 0) return false; - - return (psp->should_committed && (psp->block_num > ctrl.last_irreversible_block_num())); + return (psp->is_committed && (psp->block_num > ctrl.last_irreversible_block_num())); } - uint32_t pbft_database::get_committed_view() { - uint32_t new_view = 0; + pbft_view_type pbft_database::get_committed_view() { + pbft_view_type new_view = 0; if (!should_committed()) return new_view; - const auto &by_commit_and_num_index = pbft_state_index.get(); + auto const &by_commit_and_num_index = pbft_state_index.get(); auto itr = by_commit_and_num_index.begin(); pbft_state_ptr psp = *itr; @@ -387,32 +388,34 @@ namespace eosio { auto commits = (*itr)->commits; - flat_map commit_count; - for (const auto &com: commits) { - if (commit_count.find(com.view) == commit_count.end()) { - commit_count[com.view] = 1; - } else { - commit_count[com.view] += 1; + auto threshold = as.size() * 2 / 3 + 1; + + flat_map commit_count; + for (auto const &com: commits) { + if (commit_count.find(com.second.view) == commit_count.end()) commit_count[com.second.view] = 0; + } + + for (auto const &bp: as) { + for (auto const &pc: commits) { + if (bp.block_signing_key == pc.first.second) commit_count[pc.first.first] += 1; } } for (auto const &e: commit_count) { - if (e.second >= as.size() * 2 / 3 + 1 && e.first > new_view) { + if (e.second >= threshold && e.first > new_view) { new_view = e.first; } } return new_view; } - bool pbft_database::is_valid_commit(const pbft_commit &c) { - if (c.chain_id != chain_id()) return false; - if (c.block_num <= ctrl.last_stable_checkpoint_block_num()) return true; - if (!c.is_signature_valid()) return false; - return should_recv_pbft_msg(c.public_key); + bool pbft_database::is_valid_commit(const pbft_commit &c, const public_key_type &pk) { + if (c.block_info.block_num() <= ctrl.last_stable_checkpoint_block_num()) return true; + return should_recv_pbft_msg(pk); } void pbft_database::commit_local() { - const auto &by_commit_and_num_index = pbft_state_index.get(); + auto const &by_commit_and_num_index = pbft_state_index.get(); auto itr = by_commit_and_num_index.begin(); if (itr == by_commit_and_num_index.end()) return; @@ -425,26 +428,27 @@ namespace eosio { return ctrl.pending_pbft_lib(); } - void pbft_database::add_pbft_view_change(pbft_view_change &vc) { - if (!is_valid_view_change(vc)) return; - auto active_bps = lscb_active_producers().producers; + void pbft_database::add_pbft_view_change(pbft_view_change &vc, const public_key_type &pk) { + + auto lscb_bps = lscb_active_producers().producers; auto &by_view_index = view_state_index.get(); auto itr = by_view_index.find(vc.target_view); if (itr == by_view_index.end()) { - auto vs = pbft_view_state{vc.target_view, .view_changes={vc}}; - auto vsp = make_shared(vs); - view_state_index.insert(vsp); + flat_map view_changes; + view_changes[pk] = vc; + pbft_view_change_state vcs; + vcs.view = vc.target_view; + vcs.view_changes = view_changes; + auto vcsp = std::make_shared(move(vcs)); + view_state_index.insert(vcsp); } else { auto pvs = (*itr); auto view_changes = pvs->view_changes; - auto p_itr = find_if(view_changes.begin(), view_changes.end(), - [&](const pbft_view_change &existed) { - return existed.public_key == vc.public_key; - }); - if (p_itr == view_changes.end()) { - by_view_index.modify(itr, [&](const pbft_view_state_ptr &pvsp) { - pvsp->view_changes.emplace_back(vc); + + if (view_changes.find(pk) == view_changes.end()) { + by_view_index.modify(itr, [&](const pbft_view_change_state_ptr &pvsp) { + pvsp->view_changes[pk] = vc; }); } } @@ -452,21 +456,24 @@ namespace eosio { itr = by_view_index.find(vc.target_view); if (itr == by_view_index.end()) return; - auto vc_count = 0; - if (!(*itr)->should_view_changed) { - for (auto const &sp: active_bps) { - for (auto const &v: (*itr)->view_changes) { - if (sp.block_signing_key == v.public_key) vc_count += 1; + auto vsp = *itr; + auto threshold = lscb_bps.size() * 2 / 3 + 1; + if (vsp->view_changes.size() >= threshold && !vsp->is_view_changed) { + auto vc_count = 0; + + for (auto const &bp: lscb_bps) { + for (auto const &v: vsp->view_changes) { + if (bp.block_signing_key == v.first) vc_count += 1; } } - if (vc_count >= active_bps.size() * 2 / 3 + 1) { - by_view_index.modify(itr, [&](const pbft_view_state_ptr &pvsp) { pvsp->should_view_changed = true; }); + if (vc_count >= threshold) { + by_view_index.modify(itr, [&](const pbft_view_change_state_ptr &pvsp) { pvsp->is_view_changed = true; }); } } } - uint32_t pbft_database::should_view_change() { - uint32_t nv = 0; + pbft_view_type pbft_database::should_view_change() { + pbft_view_type nv = 0; auto &by_view_index = view_state_index.get(); auto itr = by_view_index.begin(); if (itr == by_view_index.end()) return nv; @@ -477,8 +484,8 @@ namespace eosio { auto pvs = (*itr); for (auto const &bp: active_bps) { - for (auto const &pp: pvs->view_changes) { - if (bp.block_signing_key == pp.public_key) vc_count += 1; + for (auto const &v: pvs->view_changes) { + if (bp.block_signing_key == v.first) vc_count += 1; } } //if contains self or view_change >= f+1, transit to view_change and send view change @@ -491,211 +498,208 @@ namespace eosio { return nv; } - vector pbft_database::send_and_add_pbft_view_change( - const vector &vcv, - const vector &ppc, - const vector> &pcc, - uint32_t current_view, - uint32_t new_view) { - if (!vcv.empty()) { - for (auto vc : vcv) { - //change uuid, sign again, update cache, then emit - auto uuid = boost::uuids::to_string(uuid_generator()); - vc.uuid = uuid; - vc.timestamp = time_point::now(); - vc.producer_signature = ctrl.my_signature_providers()[vc.public_key](vc.digest()); - emit(pbft_outgoing_view_change, vc); - } - return vector{}; - } else { - vector new_vcv; + pbft_view_change pbft_database::send_and_add_pbft_view_change( + const pbft_view_change &cached_view_change, + const pbft_prepared_certificate &ppc, + const vector &pcc, + pbft_view_type current_view, + pbft_view_type target_view) { + auto view_change_to_be_cached = pbft_view_change(); + if (!cached_view_change.empty()) { + for (auto const &sp : ctrl.my_signature_providers()) { + //sign again, update cache, then emit + auto retry_vc = cached_view_change; + retry_vc.common.timestamp = time_point::now(); + retry_vc.sender_signature = sp.second(retry_vc.digest(chain_id)); + emit(pbft_outgoing_view_change, retry_vc); + } + return view_change_to_be_cached; + } else { for (auto const &my_sp : ctrl.my_signature_providers()) { - auto ppc_ptr = find_if(ppc.begin(), ppc.end(), - [&](const pbft_prepared_certificate &v) { - return v.public_key == my_sp.first; - }); - - auto my_ppc = pbft_prepared_certificate{}; - if (ppc_ptr != ppc.end()) my_ppc = *ppc_ptr; - - auto my_pcc = vector{}; - for (auto const &c: pcc) { - for (auto const &e: c) { - if (my_pcc.empty() && e.public_key == my_sp.first) { - my_pcc = c; - } - } - } + auto my_lsc = get_stable_checkpoint_by_id(ctrl.last_stable_checkpoint_block_id()); - auto uuid = boost::uuids::to_string(uuid_generator()); - auto vc = pbft_view_change{uuid, current_view, new_view, my_ppc, my_pcc, my_lsc, my_sp.first, chain_id()}; - vc.producer_signature = my_sp.second(vc.digest()); - emit(pbft_outgoing_view_change, vc); - add_pbft_view_change(vc); - new_vcv.emplace_back(vc); + + pbft_view_change new_vc; + new_vc.current_view=current_view; + new_vc.target_view=target_view; + new_vc.prepared_cert=ppc; + new_vc.committed_certs=pcc; + new_vc.stable_checkpoint=my_lsc; + new_vc.sender_signature = my_sp.second(new_vc.digest(chain_id)); + if (is_valid_view_change(new_vc, my_sp.first)) { + emit(pbft_outgoing_view_change, new_vc); + add_pbft_view_change(new_vc, my_sp.first); + if (view_change_to_be_cached.empty()) view_change_to_be_cached = new_vc; + } } - return new_vcv; + return view_change_to_be_cached; } } - bool pbft_database::should_new_view(const uint32_t target_view) { + bool pbft_database::should_new_view(const pbft_view_type target_view) { auto &by_view_index = view_state_index.get(); auto itr = by_view_index.find(target_view); if (itr == by_view_index.end()) return false; - return (*itr)->should_view_changed; + return (*itr)->is_view_changed; } - uint32_t pbft_database::get_proposed_new_view_num() { + pbft_view_type pbft_database::get_proposed_new_view_num() { auto &by_count_and_view_index = view_state_index.get(); auto itr = by_count_and_view_index.begin(); - if (itr == by_count_and_view_index.end() || !(*itr)->should_view_changed) return 0; + if (itr == by_count_and_view_index.end() || !(*itr)->is_view_changed) return 0; return (*itr)->view; } - bool pbft_database::is_new_primary(const uint32_t target_view) { + bool pbft_database::has_new_primary(const public_key_type &pk) { - auto primary_key = get_new_view_primary_key(target_view); - if (primary_key == public_key_type{}) return false; + if (pk == public_key_type()) return false; auto sps = ctrl.my_signature_providers(); - auto sp_itr = sps.find(primary_key); + auto sp_itr = sps.find(pk); return sp_itr != sps.end(); } - void pbft_database::prune_pbft_index() { -// pbft_state_index.clear(); + void pbft_database::cleanup_on_new_view() { view_state_index.clear(); ctrl.reset_pbft_my_prepare(); } pbft_new_view pbft_database::send_pbft_new_view( - const vector &vcc, - uint32_t current_view) { + const pbft_view_changed_certificate &vcc, + pbft_view_type current_view) { auto primary_key = get_new_view_primary_key(current_view); - if (!is_new_primary(current_view)) return pbft_new_view{}; + if (!has_new_primary(primary_key) || vcc.empty()) return pbft_new_view(); - //`sp_itr` is not possible to be the end iterator, since it's already been checked in `is_new_primary`. + //`sp_itr` is not possible to be the end iterator, since it's already been checked in `has_new_primary`. auto my_sps = ctrl.my_signature_providers(); auto sp_itr = my_sps.find(primary_key); - auto vcc_ptr = find_if(vcc.begin(), vcc.end(), - [&](const pbft_view_changed_certificate &v) { return v.public_key == primary_key; }); - - if (vcc_ptr == vcc.end()) return pbft_new_view{}; - - auto highest_ppc = pbft_prepared_certificate{}; + auto highest_ppc = pbft_prepared_certificate(); auto highest_pcc = vector{}; - auto highest_sc = pbft_stable_checkpoint{}; + auto highest_sc = pbft_stable_checkpoint(); - for (const auto &vc: vcc_ptr->view_changes) { - if (vc.prepared.block_num > highest_ppc.block_num && is_valid_prepared_certificate(vc.prepared)) { - highest_ppc = vc.prepared; + for (auto const &vc: vcc.view_changes) { + if (vc.prepared_cert.block_info.block_num() > highest_ppc.block_info.block_num()) { + highest_ppc = vc.prepared_cert; } - for (auto const &cc: vc.committed) { - if (is_valid_committed_certificate(cc)) { - auto p_itr = find_if(highest_pcc.begin(), highest_pcc.end(), - [&](const pbft_committed_certificate &ext) { return ext.block_id == cc.block_id; }); - if (p_itr == highest_pcc.end()) highest_pcc.emplace_back(cc); - } + for (auto const &cc: vc.committed_certs) { + auto p_itr = find_if(highest_pcc.begin(), highest_pcc.end(), + [&](const pbft_committed_certificate &ext) { return ext.block_info.block_id == cc.block_info.block_id; }); + if (p_itr == highest_pcc.end()) highest_pcc.emplace_back(cc); } - if (vc.stable_checkpoint.block_num > highest_sc.block_num && - is_valid_stable_checkpoint(vc.stable_checkpoint)) { + if (vc.stable_checkpoint.block_info.block_num() > highest_sc.block_info.block_num()) { highest_sc = vc.stable_checkpoint; } } - auto uuid = boost::uuids::to_string(uuid_generator()); - - auto nv = pbft_new_view{uuid, current_view, highest_ppc, highest_pcc, highest_sc, *vcc_ptr, sp_itr->first, chain_id()}; - - nv.producer_signature = sp_itr->second(nv.digest()); + pbft_new_view nv; + nv.new_view=current_view; + nv.prepared_cert=highest_ppc; + nv.committed_certs=highest_pcc; + nv.stable_checkpoint=highest_sc; + nv.view_changed_cert=vcc; + nv.sender_signature = sp_itr->second(nv.digest(chain_id)); emit(pbft_outgoing_new_view, nv); return nv; } - vector pbft_database::generate_prepared_certificate() { - auto ppc = vector{}; - const auto &by_prepare_and_num_index = pbft_state_index.get(); + pbft_prepared_certificate pbft_database::generate_prepared_certificate() { + + auto const &by_prepare_and_num_index = pbft_state_index.get(); auto itr = by_prepare_and_num_index.begin(); - if (itr == by_prepare_and_num_index.end()) return vector{}; + if (itr == by_prepare_and_num_index.end()) return pbft_prepared_certificate(); pbft_state_ptr psp = *itr; auto prepared_block_state = ctrl.fetch_block_state_by_id(psp->block_id); - if (!prepared_block_state) return vector{}; + if (!prepared_block_state) return pbft_prepared_certificate(); auto as = prepared_block_state->active_schedule.producers; - if (psp->should_prepared && (psp->block_num > (ctrl.last_irreversible_block_num()))) { - for (auto const &my_sp : ctrl.my_signature_providers()) { - auto prepares = psp->prepares; - auto valid_prepares = vector{}; + if (psp->is_prepared && psp->block_num > ctrl.last_irreversible_block_num()) { + auto prepares = psp->prepares; + auto valid_prepares = vector{}; - flat_map prepare_count; - flat_map> prepare_msg; + flat_map prepare_count; + flat_map> prepare_msg; - for (const auto &pre: prepares) { - if (prepare_count.find(pre.view) == prepare_count.end()) prepare_count[pre.view] = 0; - prepare_msg[pre.view].emplace_back(pre); - } + for (auto const &pre: prepares) { + if (prepare_count.find(pre.first.first) == prepare_count.end()) prepare_count[pre.first.first] = 0; + prepare_msg[pre.first.first].emplace_back(pre.second); + } - for (auto const &sp: as) { - for (auto const &pp: prepares) { - if (sp.block_signing_key == pp.public_key) prepare_count[pp.view] += 1; - } + for (auto const &bp: as) { + for (auto const &pp: prepares) { + if (bp.block_signing_key == pp.first.second) prepare_count[pp.first.first] += 1; } + } - for (auto const &e: prepare_count) { - if (e.second >= as.size() * 2 / 3 + 1) { - valid_prepares = prepare_msg[e.first]; - } + auto bp_threshold = as.size() * 2 / 3 + 1; + for (auto const &e: prepare_count) { + if (e.second >= bp_threshold) { + valid_prepares = prepare_msg[e.first]; } + } - if (valid_prepares.empty()) return vector{}; + if (valid_prepares.empty()) return pbft_prepared_certificate(); - auto pc = pbft_prepared_certificate{psp->block_id, psp->block_num, valid_prepares, my_sp.first}; - pc.producer_signature = my_sp.second(pc.digest()); - ppc.emplace_back(pc); + pbft_prepared_certificate pc; + pc.block_info={psp->block_id}; pc.prepares=valid_prepares; pc.pre_prepares.emplace(psp->block_id); + for (auto const &p: valid_prepares) { + auto bid = p.block_info.block_id; + while (bid != psp->block_id) { + pc.pre_prepares.emplace(bid); + bid = ctrl.fetch_block_state_by_id(bid)->prev(); + } } - return ppc; - } else return vector{}; + return pc; + } else return pbft_prepared_certificate(); } - vector> pbft_database::generate_committed_certificate() { - auto pcc = vector>{}; - pcc.resize(ctrl.my_signature_providers().size()); - const auto &by_commit_and_num_index = pbft_state_index.get(); + vector pbft_database::generate_committed_certificate() { + + auto pcc = vector{}; + + auto const &by_commit_and_num_index = pbft_state_index.get(); auto itr = by_commit_and_num_index.begin(); - if (itr == by_commit_and_num_index.end()) return vector>{}; + if (itr == by_commit_and_num_index.end()) return pcc; pbft_state_ptr psp = *itr; - if (!psp->should_committed) return vector>{}; + if (!psp->is_committed) return pcc; auto highest_committed_block_num = psp->block_num; vector ccb; + auto lib_num = ctrl.last_irreversible_block_num(); + //adding my highest committed cert. - if ( highest_committed_block_num > ctrl.last_stable_checkpoint_block_num() ) { + auto lscb_num = ctrl.last_stable_checkpoint_block_num(); + if ( highest_committed_block_num <= lib_num && highest_committed_block_num > lscb_num ) { ccb.emplace_back(highest_committed_block_num); } - for (auto i = 0; i < prepare_watermarks.size() && prepare_watermarks[i] < highest_committed_block_num; ++i) { + auto watermarks = get_updated_watermarks(); + for (auto& watermark : watermarks) { //adding committed cert on every water mark. - ccb.emplace_back(prepare_watermarks[i]); + if (watermark < lib_num && watermark > lscb_num) { + ccb.emplace_back(watermark); + } } - const auto &by_id_index = pbft_state_index.get(); + auto const &by_id_index = pbft_state_index.get(); - for (const auto &committed_block_num: ccb) { + std::sort(ccb.begin(), ccb.end()); + pcc.reserve(ccb.size()); + for (auto const &committed_block_num: ccb) { auto cbs = ctrl.fetch_block_state_by_number(committed_block_num); - if (!cbs) return vector>{}; + if (!cbs) return pcc; auto it = by_id_index.find(cbs->id); - if (it == by_id_index.end() || !(*it)->should_committed) { - return vector>{}; + if (it == by_id_index.end() || !(*it)->is_committed) { + return pcc; } auto as = cbs->active_schedule.producers; @@ -703,89 +707,93 @@ namespace eosio { auto commits = (*it)->commits; auto valid_commits = vector{}; - flat_map commit_count; - flat_map> commit_msg; + flat_map commit_count; + flat_map> commit_msg; - for (const auto &com: commits) { - if (commit_count.find(com.view) == commit_count.end()) commit_count[com.view] = 0; - commit_msg[com.view].emplace_back(com); + for (auto const &com: commits) { + if (commit_count.find(com.first.first) == commit_count.end()) commit_count[com.first.first] = 0; + commit_msg[com.first.first].emplace_back(com.second); } - for (auto const &sp: as) { + for (auto const &bp: as) { for (auto const &cc: commits) { - if (sp.block_signing_key == cc.public_key) commit_count[cc.view] += 1; + if (bp.block_signing_key == cc.first.second) commit_count[cc.first.first] += 1; } } + auto bp_threshold = as.size() * 2 / 3 + 1; for (auto const &e: commit_count) { - if (e.second >= as.size() * 2 / 3 + 1) { + if (e.second >= bp_threshold) { valid_commits = commit_msg[e.first]; } } - if (valid_commits.empty()) return vector>{}; + if (valid_commits.empty()) return pcc; - auto j = 0; - for (auto const &my_sp : ctrl.my_signature_providers()) { - auto cc = pbft_committed_certificate{cbs->id, cbs->block_num, valid_commits, my_sp.first}; - cc.producer_signature = my_sp.second(cc.digest()); - pcc[j].emplace_back(cc); - ++j; - } + pbft_committed_certificate cc; + cc.block_info={cbs->id}; cc.commits=valid_commits; + pcc.emplace_back(cc); } return pcc; } - vector pbft_database::generate_view_changed_certificate(uint32_t target_view) { - auto vcc = vector{}; + pbft_view_changed_certificate pbft_database::generate_view_changed_certificate(pbft_view_type target_view) { + + auto pvcc = pbft_view_changed_certificate(); auto &by_view_index = view_state_index.get(); auto itr = by_view_index.find(target_view); - if (itr == by_view_index.end()) return vcc; + if (itr == by_view_index.end()) return pvcc; auto pvs = *itr; - if (pvs->should_view_changed) { - for (auto const &my_sp : ctrl.my_signature_providers()) { - auto pc = pbft_view_changed_certificate{pvs->view, pvs->view_changes, my_sp.first}; - pc.producer_signature = my_sp.second(pc.digest()); - vcc.emplace_back(pc); + if (pvs->is_view_changed) { + + pvcc.target_view=pvs->view; + pvcc.view_changes.reserve(pvs->view_changes.size()); + for(auto & view_change : pvs->view_changes) { + pvcc.view_changes.emplace_back( view_change.second ); } - return vcc; - } else return vector{}; + return pvcc; + } else return pvcc; } - bool pbft_database::is_valid_prepared_certificate(const pbft_prepared_certificate &certificate) { + + + bool pbft_database::is_valid_prepared_certificate(const pbft_prepared_certificate &certificate, bool add_to_pbft_db) { // an empty certificate is valid since it acts as a null digest in pbft. - if (certificate == pbft_prepared_certificate{}) return true; + if (certificate.empty()) return true; // a certificate under lscb (no longer in fork_db) is also treated as null. - if (certificate.block_num <= ctrl.last_stable_checkpoint_block_num()) return true; + if (certificate.block_info.block_num() <= ctrl.last_stable_checkpoint_block_num()) return true; - auto valid = true; - valid = valid && certificate.is_signature_valid(); - for (auto const &p : certificate.prepares) { - valid = valid && is_valid_prepare(p); - if (!valid) return false; + auto prepares = certificate.prepares; + auto prepares_metadata = vector>{}; + prepares_metadata.reserve(prepares.size()); + + for (auto &p : prepares) { + auto pmm = pbft_message_metadata(p, chain_id); + prepares_metadata.emplace_back(pmm); + if (!is_valid_prepare(p, pmm.sender_key)) return false; + if (add_to_pbft_db) add_pbft_prepare(p, pmm.sender_key); } - auto cert_id = certificate.block_id; + auto cert_id = certificate.block_info.block_id; auto cert_bs = ctrl.fetch_block_state_by_id(cert_id); auto producer_schedule = lscb_active_producers(); - if (certificate.block_num > 0 && cert_bs) { + if (certificate.block_info.block_num() > 0 && cert_bs) { producer_schedule = cert_bs->active_schedule; } auto bp_threshold = producer_schedule.producers.size() * 2 / 3 + 1; - auto prepares = certificate.prepares; - flat_map prepare_count; + flat_map prepare_count; - for (const auto &pre: prepares) { - if (prepare_count.find(pre.view) == prepare_count.end()) prepare_count[pre.view] = 0; + for (auto const &pm: prepares_metadata) { + if (prepare_count.find(pm.msg.view) == prepare_count.end()) prepare_count[pm.msg.view] = 0; } - for (auto const &sp: producer_schedule.producers) { - for (auto const &pp: prepares) { - if (sp.block_signing_key == pp.public_key) prepare_count[pp.view] += 1; + for (auto const &bp: producer_schedule.producers) { + for (auto const &pm: prepares_metadata) { + if (bp.block_signing_key == pm.sender_key) prepare_count[pm.msg.view] += 1; } } @@ -800,52 +808,57 @@ namespace eosio { if (!should_prepared) return false; //validate prepare - auto lscb = ctrl.last_stable_checkpoint_block_num(); + auto lscb_num = ctrl.last_stable_checkpoint_block_num(); auto non_fork_bp_count = 0; - vector prepare_infos; + vector prepare_infos; + prepare_infos.reserve(certificate.prepares.size()); for (auto const &p : certificate.prepares) { //only search in fork db - if (p.block_num <= lscb) { + if (p.block_info.block_num() <= lscb_num) { ++non_fork_bp_count; } else { - prepare_infos.emplace_back(block_info{p.block_id, p.block_num}); + prepare_infos.emplace_back(p.block_info); } } - auto cert_info = block_info{certificate.block_id, certificate.block_num}; - return is_valid_longest_fork(cert_info, prepare_infos, bp_threshold, non_fork_bp_count); + return is_valid_longest_fork(certificate.block_info, prepare_infos, bp_threshold, non_fork_bp_count); } - bool pbft_database::is_valid_committed_certificate(const pbft_committed_certificate &certificate) { + bool pbft_database::is_valid_committed_certificate(const pbft_committed_certificate &certificate, bool add_to_pbft_db) { // an empty certificate is valid since it acts as a null digest in pbft. - if (certificate == pbft_committed_certificate{}) return true; + if (certificate.empty()) return true; // a certificate under lscb (no longer in fork_db) is also treated as null. - if (certificate.block_num <= ctrl.last_stable_checkpoint_block_num()) return true; + if (certificate.block_info.block_num() <= ctrl.last_stable_checkpoint_block_num()) return true; - auto valid = true; - valid = valid && certificate.is_signature_valid(); - for (auto const &c : certificate.commits) { - valid = valid && is_valid_commit(c); - if (!valid) return false; + auto commits = certificate.commits; + auto commits_metadata = vector>{}; + commits_metadata.reserve(commits.size()); + + for (auto &c : commits) { + auto pmm = pbft_message_metadata(c, chain_id); + commits_metadata.emplace_back(pmm); + if (!is_valid_commit(c, pmm.sender_key)) return false; + if (add_to_pbft_db) add_pbft_commit(c, pmm.sender_key); } - auto cert_id = certificate.block_id; + if (add_to_pbft_db && should_committed()) commit_local(); + + auto cert_id = certificate.block_info.block_id; auto cert_bs = ctrl.fetch_block_state_by_id(cert_id); auto producer_schedule = lscb_active_producers(); - if (certificate.block_num > 0 && cert_bs) { + if (certificate.block_info.block_num() > 0 && cert_bs) { producer_schedule = cert_bs->active_schedule; } auto bp_threshold = producer_schedule.producers.size() * 2 / 3 + 1; - auto commits = certificate.commits; - flat_map commit_count; + flat_map commit_count; - for (const auto &pre: commits) { - if (commit_count.find(pre.view) == commit_count.end()) commit_count[pre.view] = 0; + for (auto const &cm: commits_metadata) { + if (commit_count.find(cm.msg.view) == commit_count.end()) commit_count[cm.msg.view] = 0; } - for (auto const &sp: producer_schedule.producers) { - for (auto const &pp: commits) { - if (sp.block_signing_key == pp.public_key) commit_count[pp.view] += 1; + for (auto const &bp: producer_schedule.producers) { + for (auto const &cm: commits_metadata) { + if (bp.block_signing_key == cm.sender_key) commit_count[cm.msg.view] += 1; } } @@ -860,134 +873,149 @@ namespace eosio { if (!should_committed) return false; //validate commit - auto lscb = ctrl.last_stable_checkpoint_block_num(); + auto lscb_num = ctrl.last_stable_checkpoint_block_num(); auto non_fork_bp_count = 0; - vector commit_infos; - for (auto const &p : certificate.commits) { + vector commit_infos; + commit_infos.reserve(certificate.commits.size()); + for (auto const &c : certificate.commits) { //only search in fork db - if (p.block_num <= lscb) { + if (c.block_info.block_num() <= lscb_num) { ++non_fork_bp_count; } else { - commit_infos.emplace_back(block_info{p.block_id, p.block_num}); + commit_infos.emplace_back(c.block_info); } } - auto cert_info = block_info{certificate.block_id, certificate.block_num}; - return is_valid_longest_fork(cert_info, commit_infos, bp_threshold, non_fork_bp_count); + return is_valid_longest_fork(certificate.block_info, commit_infos, bp_threshold, non_fork_bp_count); } - bool pbft_database::is_valid_view_change(const pbft_view_change &vc) { - if (vc.chain_id != chain_id()) return false; + bool pbft_database::is_valid_view_change(const pbft_view_change &vc, const public_key_type &pk) { - return vc.is_signature_valid() - && should_recv_pbft_msg(vc.public_key); + return should_recv_pbft_msg(pk); // No need to check prepared cert and stable checkpoint, until generate or validate a new view msg } - bool pbft_database::is_valid_new_view(const pbft_new_view &nv) { - //all signatures should be valid + void pbft_database::validate_new_view(const pbft_new_view &nv, const public_key_type &pk) { - EOS_ASSERT(nv.chain_id == chain_id(), pbft_exception, "wrong chain."); + EOS_ASSERT(pk == get_new_view_primary_key(nv.new_view), pbft_exception, + "new view is not signed with expected key"); - EOS_ASSERT(is_valid_prepared_certificate(nv.prepared), pbft_exception, - "bad prepared certificate: ${pc}", ("pc", nv.prepared)); + EOS_ASSERT(is_valid_prepared_certificate(nv.prepared_cert, true), pbft_exception, + "bad prepared certificate: ${pc}", ("pc", nv.prepared_cert)); - EOS_ASSERT(is_valid_stable_checkpoint(nv.stable_checkpoint), pbft_exception, + EOS_ASSERT(is_valid_stable_checkpoint(nv.stable_checkpoint, true), pbft_exception, "bad stable checkpoint: ${scp}", ("scp", nv.stable_checkpoint)); - EOS_ASSERT(nv.view_changed.is_signature_valid(), pbft_exception, "bad view changed signature"); - - EOS_ASSERT(nv.is_signature_valid(), pbft_exception, "bad new view signature"); + auto committed_certs = nv.committed_certs; + std::sort(committed_certs.begin(), committed_certs.end()); + for (auto const &c: committed_certs) { + EOS_ASSERT(is_valid_committed_certificate(c, true), pbft_exception, + "bad committed certificate: ${cc}", ("cc", c)); + } - EOS_ASSERT(nv.view_changed.view == nv.view, pbft_exception, "target view not match"); + EOS_ASSERT(nv.view_changed_cert.target_view == nv.new_view, pbft_exception, "target view not match"); vector lscb_producers; - for (const auto& pk: lscb_active_producers().producers) { - lscb_producers.emplace_back(pk.block_signing_key); + lscb_producers.reserve(lscb_active_producers().producers.size()); + for (auto const &bp: lscb_active_producers().producers) { + lscb_producers.emplace_back(bp.block_signing_key); } auto schedule_threshold = lscb_producers.size() * 2 / 3 + 1; - vector view_change_producers; + auto view_changes = nv.view_changed_cert.view_changes; + auto view_changes_metadata = vector>{}; + view_changes_metadata.reserve(view_changes.size()); - for (auto vc: nv.view_changed.view_changes) { - if (is_valid_view_change(vc)) { - add_pbft_view_change(vc); - view_change_producers.emplace_back(vc.public_key); + vector view_change_producers; + view_change_producers.reserve(view_changes.size()); + for (auto &vc: view_changes) { + auto pmm = pbft_message_metadata(vc, chain_id); + view_changes_metadata.emplace_back(pmm); + if (is_valid_view_change(vc, pmm.sender_key)) { + add_pbft_view_change(vc, pmm.sender_key); + view_change_producers.emplace_back(pmm.sender_key); } } vector intersection; - std::sort(lscb_producers.begin(),lscb_producers.end()); - std::sort(view_change_producers.begin(),view_change_producers.end()); - std::set_intersection(lscb_producers.begin(),lscb_producers.end(), - view_change_producers.begin(),view_change_producers.end(), + std::sort(lscb_producers.begin(), lscb_producers.end()); + std::sort(view_change_producers.begin(), view_change_producers.end()); + std::set_intersection(lscb_producers.begin(), lscb_producers.end(), + view_change_producers.begin(), view_change_producers.end(), back_inserter(intersection)); EOS_ASSERT(intersection.size() >= schedule_threshold, pbft_exception, "view changes count not enough"); - EOS_ASSERT(should_new_view(nv.view), pbft_exception, "should not enter new view: ${nv}", ("nv", nv.view)); + EOS_ASSERT(should_new_view(nv.new_view), pbft_exception, "should not enter new view: ${nv}", + ("nv", nv.new_view)); - auto highest_ppc = pbft_prepared_certificate{}; + auto highest_ppc = pbft_prepared_certificate(); auto highest_pcc = vector{}; - auto highest_scp = pbft_stable_checkpoint{}; + auto highest_scp = pbft_stable_checkpoint(); - for (const auto &vc: nv.view_changed.view_changes) { - if (vc.prepared.block_num > highest_ppc.block_num - && is_valid_prepared_certificate(vc.prepared)) { - highest_ppc = vc.prepared; + for (auto const &vc: nv.view_changed_cert.view_changes) { + if (vc.prepared_cert.block_info.block_num() > highest_ppc.block_info.block_num() + && is_valid_prepared_certificate(vc.prepared_cert)) { + highest_ppc = vc.prepared_cert; } - for (auto const &cc: vc.committed) { + for (auto const &cc: vc.committed_certs) { if (is_valid_committed_certificate(cc)) { auto p_itr = find_if(highest_pcc.begin(), highest_pcc.end(), - [&](const pbft_committed_certificate &ext) { return ext.block_id == cc.block_id; }); + [&](const pbft_committed_certificate &ext) { + return ext.block_info.block_id == cc.block_info.block_id; + }); if (p_itr == highest_pcc.end()) highest_pcc.emplace_back(cc); } } - if (vc.stable_checkpoint.block_num > highest_scp.block_num - && is_valid_stable_checkpoint(vc.stable_checkpoint)) { + if (vc.stable_checkpoint.block_info.block_num() > highest_scp.block_info.block_num() + && is_valid_stable_checkpoint(vc.stable_checkpoint, true)) { highest_scp = vc.stable_checkpoint; } } - EOS_ASSERT(highest_ppc == nv.prepared, pbft_exception, - "prepared certificate not match, should be ${hpcc} but ${pc} given", - ("hpcc",highest_ppc)("pc", nv.prepared)); - - EOS_ASSERT(highest_pcc == nv.committed, pbft_exception, "committed certificate not match"); - - EOS_ASSERT(highest_scp == nv.stable_checkpoint, pbft_exception, - "stable checkpoint not match, should be ${hscp} but ${scp} given", - ("hpcc",highest_scp)("pc", nv.stable_checkpoint)); + EOS_ASSERT(highest_ppc.block_info == nv.prepared_cert.block_info, pbft_exception, + "prepared certificate does not match, should be ${hppc} but ${pc} given", + ("hppc", highest_ppc)("pc", nv.prepared_cert)); + + std::sort(highest_pcc.begin(), highest_pcc.end()); + EOS_ASSERT(highest_pcc.size() == committed_certs.size(), pbft_exception, + "wrong committed certificates size"); + for (auto i = 0; i < committed_certs.size(); ++i) { + EOS_ASSERT(highest_pcc[i].block_info == committed_certs[i].block_info, pbft_exception, + "committed certificate does not match, should be ${hpcc} but ${cc} given", + ("hpcc", highest_pcc[i])("cc", committed_certs[i])); + } - return true; + EOS_ASSERT(highest_scp.block_info == nv.stable_checkpoint.block_info, pbft_exception, + "stable checkpoint does not match, should be ${hscp} but ${scp} given", + ("hpcc", highest_scp)("pc", nv.stable_checkpoint)); } bool pbft_database::should_stop_view_change(const pbft_view_change &vc) { auto lscb_num = ctrl.last_stable_checkpoint_block_num(); - return lscb_num > vc.prepared.block_num - && lscb_num > vc.stable_checkpoint.block_num; + auto vc_lscb = vc.stable_checkpoint.block_info.block_num(); + return vc_lscb > 0 && lscb_num > vc_lscb; } - vector> pbft_database::fetch_fork_from(const vector block_infos) { - auto bi = block_infos; + vector> pbft_database::fetch_fork_from(vector &block_infos) { - vector> result; - if (bi.empty()) { + vector> result; + if (block_infos.empty()) { return result; } - if (bi.size() == 1) { - result.emplace_back(initializer_list{bi.front()}); + if (block_infos.size() == 1) { + result.emplace_back(initializer_list{block_infos.front()}); return result; } - sort(bi.begin(), bi.end(), - [](const block_info &a, const block_info &b) -> bool { return a.block_num > b.block_num; }); + sort(block_infos.begin(), block_infos.end(), + [](const block_info_type &a, const block_info_type &b) -> bool { return a.block_num() > b.block_num(); }); - while (!bi.empty()) { - auto fork = fetch_first_fork_from(bi); + while (!block_infos.empty()) { + auto fork = fetch_first_fork_from(block_infos); if (!fork.empty()) { result.emplace_back(fork); } @@ -995,8 +1023,8 @@ namespace eosio { return result; } - vector pbft_database::fetch_first_fork_from(vector &bi) { - vector result; + vector pbft_database::fetch_first_fork_from(vector &bi) { + vector result; if (bi.empty()) { return result; } @@ -1006,11 +1034,11 @@ namespace eosio { return result; } //bi should be sorted desc - auto high = bi.front().block_num; - auto low = bi.back().block_num; + auto high = bi.front().block_num(); + auto low = bi.back().block_num(); auto id = bi.front().block_id; - auto num = bi.front().block_num; + auto num = bi.front().block_num(); while (num <= high && num >= low && !bi.empty()) { auto bs = ctrl.fetch_block_state_by_id(id); @@ -1036,10 +1064,10 @@ namespace eosio { return result; } - bool pbft_database::is_valid_longest_fork(const block_info &bi, vector block_infos, unsigned long threshold, unsigned long non_fork_bp_count) { + bool pbft_database::is_valid_longest_fork(const block_info_type &bi, vector block_infos, unsigned long threshold, unsigned long non_fork_bp_count) { auto forks = fetch_fork_from(block_infos); - vector longest_fork; + vector longest_fork; for (auto const &f : forks) { if (f.size() > longest_fork.size()) { longest_fork = f; @@ -1051,33 +1079,26 @@ namespace eosio { auto calculated_block_info = longest_fork.back(); - auto current_bs = ctrl.fetch_block_state_by_id(calculated_block_info.block_id); - while (current_bs) { - if (bi.block_id == current_bs->id && bi.block_num == current_bs->block_num) { - return true; - } - current_bs = ctrl.fetch_block_state_by_id(current_bs->prev()); - } - return false; + return bi.block_id == calculated_block_info.block_id; } pbft_stable_checkpoint pbft_database::fetch_stable_checkpoint_from_blk_extn(const signed_block_ptr &b) { try { if (b) { - auto &ext = b->block_extensions; + auto &extn = b->block_extensions; - for (auto it = ext.begin(); it != ext.end();) { + for (auto it = extn.begin(); it != extn.end();) { if (it->first == static_cast(block_extension_type::pbft_stable_checkpoint)) { - auto scp_v = it->second; - fc::datastream ds_decode(scp_v.data(), scp_v.size()); + auto scp_ds = it->second; + fc::datastream ds(scp_ds.data(), scp_ds.size()); - pbft_stable_checkpoint scp_decode; - fc::raw::unpack(ds_decode, scp_decode); + pbft_stable_checkpoint scp; + fc::raw::unpack(ds, scp); - if (is_valid_stable_checkpoint(scp_decode)) { - return scp_decode; + if (is_valid_stable_checkpoint(scp)) { + return scp; } else { - it = ext.erase(it); + it = extn.erase(it); } } else { it++; @@ -1085,202 +1106,195 @@ namespace eosio { } } } catch(...) { - wlog("no stable checkpoints found in the block extension"); + elog("no stable checkpoints found in the block extension"); } - return pbft_stable_checkpoint{}; + return pbft_stable_checkpoint(); } - pbft_stable_checkpoint pbft_database::get_stable_checkpoint_by_id(const block_id_type &block_id) { - const auto &by_block = checkpoint_index.get(); + pbft_stable_checkpoint pbft_database::get_stable_checkpoint_by_id(const block_id_type &block_id, bool incl_blk_extn ) { + auto const &by_block = checkpoint_index.get(); auto itr = by_block.find(block_id); if (itr == by_block.end()) { - auto blk = ctrl.fetch_block_by_id(block_id); - return fetch_stable_checkpoint_from_blk_extn(blk); + if (incl_blk_extn) { + auto blk = ctrl.fetch_block_by_id(block_id); + return fetch_stable_checkpoint_from_blk_extn(blk); + } + return pbft_stable_checkpoint(); } auto cpp = *itr; if (cpp->is_stable) { - if (ctrl.my_signature_providers().empty()) return pbft_stable_checkpoint{}; - auto psc = pbft_stable_checkpoint{cpp->block_num, cpp->block_id, cpp->checkpoints, chain_id()}; + pbft_stable_checkpoint psc; + psc.block_info={cpp->block_id}; + psc.checkpoints.reserve(cpp->checkpoints.size()); + for (auto & checkpoint : cpp->checkpoints) { + psc.checkpoints.emplace_back(checkpoint.second) ; + } return psc; - } else return pbft_stable_checkpoint{}; + } else return pbft_stable_checkpoint(); } - block_info pbft_database::cal_pending_stable_checkpoint() const { + block_info_type pbft_database::cal_pending_stable_checkpoint() const { - //TODO: maybe use watermarks instead? - auto lscb_num = ctrl.last_stable_checkpoint_block_num(); - auto lscb_id = ctrl.last_stable_checkpoint_block_id(); - auto lscb_info = block_info{lscb_id, lscb_num}; + auto pending_scb_num = ctrl.last_stable_checkpoint_block_num(); + auto pending_scb_info = block_info_type{ctrl.last_stable_checkpoint_block_id()}; - const auto &by_blk_num = checkpoint_index.get(); - auto itr = by_blk_num.lower_bound(lscb_num); - if (itr == by_blk_num.end()) return lscb_info; + auto const &by_blk_num = checkpoint_index.get(); + auto itr = by_blk_num.lower_bound(pending_scb_num); + if (itr == by_blk_num.end()) return pending_scb_info; while (itr != by_blk_num.end()) { - if ((*itr)->is_stable && ctrl.fetch_block_state_by_id((*itr)->block_id)) { - auto lscb = ctrl.fetch_block_state_by_number(ctrl.last_stable_checkpoint_block_num()); + if (auto bs = ctrl.fetch_block_state_by_id((*itr)->block_id)) { + auto scb = ctrl.fetch_block_state_by_number(pending_scb_num); - auto head_checkpoint_schedule = ctrl.fetch_block_state_by_id( - (*itr)->block_id)->active_schedule; + auto head_checkpoint_schedule = bs->active_schedule; producer_schedule_type current_schedule; producer_schedule_type new_schedule; - if (lscb_num == 0) { - const auto& ucb = ctrl.get_upgrade_properties().upgrade_complete_block_num; -// if (ucb == 0) return lscb_info; + if (pending_scb_num == 0) { + auto const &ucb = ctrl.get_upgrade_properties().upgrade_complete_block_num; if (ucb == 0) { current_schedule = ctrl.initial_schedule(); new_schedule = ctrl.initial_schedule(); } else { - auto bs = ctrl.fetch_block_state_by_number(ucb); - if (!bs) return lscb_info; - current_schedule = bs->active_schedule; - new_schedule = bs->pending_schedule; + auto ucb_state = ctrl.fetch_block_state_by_number(ucb); + if (!ucb_state) return pending_scb_info; + current_schedule = ucb_state->active_schedule; + new_schedule = ucb_state->pending_schedule; } - } else if (lscb) { - current_schedule = lscb->active_schedule; - new_schedule = lscb->pending_schedule; + } else if (scb) { + current_schedule = scb->active_schedule; + new_schedule = scb->pending_schedule; } else { - return lscb_info; + return pending_scb_info; } - if ((*itr)->is_stable - && (head_checkpoint_schedule == current_schedule || head_checkpoint_schedule == new_schedule)) { - lscb_info.block_id = (*itr)->block_id; - lscb_info.block_num = (*itr)->block_num; + if ((*itr)->is_stable) { + if (head_checkpoint_schedule == current_schedule || head_checkpoint_schedule == new_schedule) { + pending_scb_info = block_info_type{(*itr)->block_id}; + pending_scb_num = pending_scb_info.block_num(); + } else { + return pending_scb_info; + } } } ++itr; } - return lscb_info; + return pending_scb_info; } vector pbft_database::generate_and_add_pbft_checkpoint() { + auto checkpoint = [&](const block_num_type &in) { + auto const& ucb = ctrl.get_upgrade_properties().upgrade_complete_block_num; + if (!ctrl.is_pbft_enabled()) return false; + if (in <= ucb) return false; + auto watermarks = get_updated_watermarks(); + return in == ucb + 1 // checkpoint on first pbft block; + || in % pbft_checkpoint_granularity == 1 // checkpoint on every 100 block; + || std::find(watermarks.begin(), watermarks.end(), in) != watermarks.end(); // checkpoint on bp schedule change; + }; + auto new_pc = vector{}; - const auto &by_commit_and_num_index = pbft_state_index.get(); + auto const &by_commit_and_num_index = pbft_state_index.get(); auto itr = by_commit_and_num_index.begin(); - if (itr == by_commit_and_num_index.end() || !(*itr)->should_committed) return new_pc; + if (itr == by_commit_and_num_index.end() || !(*itr)->is_committed) return new_pc; pbft_state_ptr psp = (*itr); - vector pending_checkpoint_block_num; - - block_num_type my_latest_checkpoint = 0; - - auto checkpoint = [&](const block_num_type &in) { - const auto& ucb = ctrl.get_upgrade_properties().upgrade_complete_block_num; - if (!ctrl.is_pbft_enabled()) return false; - return in >= ucb - && (in == ucb + 1 || in % 100 == 1 || std::find(prepare_watermarks.begin(), prepare_watermarks.end(), in) != prepare_watermarks.end()); - }; - - for (auto i = psp->block_num; - i > std::max(ctrl.last_stable_checkpoint_block_num(), static_cast(1)); --i) { + flat_map pending_checkpoint_block_num; // block_height and retry_flag + auto lscb_num = ctrl.last_stable_checkpoint_block_num(); + for (auto i = psp->block_num; i > lscb_num && i > 1; --i) { if (checkpoint(i)) { - my_latest_checkpoint = max(i, my_latest_checkpoint); auto &by_block = checkpoint_index.get(); - auto c_itr = by_block.find(ctrl.get_block_id_for_num(i)); - if (c_itr == by_block.end()) { - pending_checkpoint_block_num.emplace_back(i); - } else { - auto checkpoints = (*c_itr)->checkpoints; - bool contains_mine = false; - for (auto const &my_sp : ctrl.my_signature_providers()) { - auto p_itr = find_if(checkpoints.begin(), checkpoints.end(), - [&](const pbft_checkpoint &ext) { return ext.public_key == my_sp.first; }); - if (p_itr != checkpoints.end()) contains_mine = true; - } - if (!contains_mine) { - pending_checkpoint_block_num.emplace_back(i); + + if (auto bs = ctrl.fork_db().get_block_in_current_chain_by_num(i)) { + auto c_itr = by_block.find(bs->id); + if (c_itr == by_block.end()) { + pending_checkpoint_block_num[i] = false; + } else { + auto checkpoints = (*c_itr)->checkpoints; + for (auto const &my_sp : ctrl.my_signature_providers()) { + if (checkpoints.find(my_sp.first) != checkpoints.end() && !(*c_itr)->is_stable) { + pending_checkpoint_block_num[i] = true; //retry sending at this time. + } + } + if (pending_checkpoint_block_num.find(i) == pending_checkpoint_block_num.end()) { + pending_checkpoint_block_num[i] = false; + } } } } } + auto &by_block = checkpoint_index.get(); if (!pending_checkpoint_block_num.empty()) { std::sort(pending_checkpoint_block_num.begin(), pending_checkpoint_block_num.end()); - for (auto h: pending_checkpoint_block_num) { - for (auto const &my_sp : ctrl.my_signature_providers()) { - auto uuid = boost::uuids::to_string(uuid_generator()); - auto cp = pbft_checkpoint{uuid, h, ctrl.get_block_id_for_num(h), - my_sp.first, .chain_id=chain_id()}; - cp.producer_signature = my_sp.second(cp.digest()); - add_pbft_checkpoint(cp); - new_pc.emplace_back(cp); - } - } - } else if (my_latest_checkpoint > 1) { - auto lscb_id = ctrl.get_block_id_for_num(my_latest_checkpoint); - auto &by_block = checkpoint_index.get(); - auto h_itr = by_block.find(lscb_id); - if (h_itr != by_block.end()) { - auto checkpoints = (*h_itr)->checkpoints; - for (auto const &my_sp : ctrl.my_signature_providers()) { - for (auto const &cp: checkpoints) { - if (my_sp.first == cp.public_key) { - auto retry_cp = cp; - auto uuid = boost::uuids::to_string(uuid_generator()); - retry_cp.uuid = uuid; - retry_cp.timestamp = time_point::now(); - retry_cp.producer_signature = my_sp.second(retry_cp.digest()); - new_pc.emplace_back(retry_cp); + for (auto& bnum_and_retry: pending_checkpoint_block_num) { + if (auto bs = ctrl.fork_db().get_block_in_current_chain_by_num(bnum_and_retry.first)) { + for (auto const &my_sp : ctrl.my_signature_providers()) { + pbft_checkpoint cp; + cp.block_info={bs->id}; + cp.sender_signature = my_sp.second(cp.digest(chain_id)); + if (!bnum_and_retry.second && is_valid_checkpoint(cp, my_sp.first)) { //first time sending this checkpoint + add_pbft_checkpoint(cp, my_sp.first); } + new_pc.emplace_back(cp); } } } + } else if (lscb_num > 0) { //retry sending my lscb + for (auto const &my_sp : ctrl.my_signature_providers()) { + pbft_checkpoint cp; + cp.block_info={ctrl.last_stable_checkpoint_block_id()}; + cp.sender_signature = my_sp.second(cp.digest(chain_id)); + new_pc.emplace_back(cp); + } } - return new_pc; } - void pbft_database::add_pbft_checkpoint(pbft_checkpoint &cp) { - - if (!is_valid_checkpoint(cp)) return; - - auto lscb_num = ctrl.last_stable_checkpoint_block_num(); + void pbft_database::add_pbft_checkpoint(pbft_checkpoint &cp, const public_key_type &pk) { - auto cp_block_state = ctrl.fetch_block_state_by_id(cp.block_id); + auto cp_block_state = ctrl.fetch_block_state_by_id(cp.block_info.block_id); if (!cp_block_state) return; - auto active_bps = cp_block_state->active_schedule.producers; - auto checkpoint_count = count_if(active_bps.begin(), active_bps.end(), [&](const producer_key &p) { - return p.block_signing_key == cp.public_key; - }); - if (checkpoint_count == 0) return; auto &by_block = checkpoint_index.get(); - auto itr = by_block.find(cp.block_id); + auto itr = by_block.find(cp.block_info.block_id); if (itr == by_block.end()) { - auto cs = pbft_checkpoint_state{cp.block_id, cp.block_num, .checkpoints={cp}}; - auto csp = make_shared(cs); + flat_map checkpoints; + checkpoints[pk] = cp; + pbft_checkpoint_state cs; + cs.block_id = cp.block_info.block_id; + cs.block_num = cp.block_info.block_num(); + cs.checkpoints = checkpoints; + auto csp = std::make_shared(move(cs)); checkpoint_index.insert(csp); - itr = by_block.find(cp.block_id); + itr = by_block.find(cp.block_info.block_id); } else { auto csp = (*itr); auto checkpoints = csp->checkpoints; - auto p_itr = find_if(checkpoints.begin(), checkpoints.end(), - [&](const pbft_checkpoint &existed) { - return existed.public_key == cp.public_key; - }); - if (p_itr == checkpoints.end()) { + if (checkpoints.find(pk) == checkpoints.end()) { by_block.modify(itr, [&](const pbft_checkpoint_state_ptr &pcp) { - csp->checkpoints.emplace_back(cp); + csp->checkpoints[pk] = cp; }); } } auto csp = (*itr); - auto cp_count = 0; - if (!csp->is_stable) { - for (auto const &sp: active_bps) { - for (auto const &pp: csp->checkpoints) { - if (sp.block_signing_key == pp.public_key) cp_count += 1; + auto active_bps = cp_block_state->active_schedule.producers; + auto threshold = active_bps.size() * 2 / 3 + 1; + if (csp->checkpoints.size() >= threshold && !csp->is_stable) { + auto cp_count = 0; + + for (auto const &bp: active_bps) { + for (auto const &c: csp->checkpoints) { + if (bp.block_signing_key == c.first) cp_count += 1; } } - if (cp_count >= active_bps.size() * 2 / 3 + 1) { + if (cp_count >= threshold) { by_block.modify(itr, [&](const pbft_checkpoint_state_ptr &pcp) { csp->is_stable = true; }); auto id = csp->block_id; auto blk = ctrl.fetch_block_by_id(id); @@ -1301,115 +1315,109 @@ namespace eosio { } } } + } - auto lscb_info = cal_pending_stable_checkpoint(); - auto pending_num = lscb_info.block_num; - auto pending_id = lscb_info.block_id; + void pbft_database::send_pbft_checkpoint() { + auto cps_to_send = generate_and_add_pbft_checkpoint(); + for (auto const &cp: cps_to_send) { + emit(pbft_outgoing_checkpoint, cp); + } + } + + void pbft_database::checkpoint_local() { + auto pending_scb_info = cal_pending_stable_checkpoint(); + auto lscb_num = ctrl.last_stable_checkpoint_block_num(); + auto pending_num = pending_scb_info.block_num(); + auto pending_id = pending_scb_info.block_id; if (pending_num > lscb_num) { ctrl.set_pbft_latest_checkpoint(pending_id); if (ctrl.last_irreversible_block_num() < pending_num) ctrl.pbft_commit_local(pending_id); - const auto &by_block_id_index = pbft_state_index.get(); + auto &by_block_id_index = pbft_state_index.get(); auto pitr = by_block_id_index.find(pending_id); if (pitr != by_block_id_index.end()) { prune(*pitr); } } - - } - - void pbft_database::send_pbft_checkpoint() { - auto cps_to_send = generate_and_add_pbft_checkpoint(); - for (auto const &cp: cps_to_send) { - emit(pbft_outgoing_checkpoint, cp); + auto &bni = checkpoint_index.get(); + auto oldest = bni.begin(); + if ( oldest != bni.end() + && (*oldest)->is_stable + && (*oldest)->block_num < lscb_num - oldest_stable_checkpoint ) { + prune(*oldest); } } - bool pbft_database::is_valid_checkpoint(const pbft_checkpoint &cp) { + bool pbft_database::is_valid_checkpoint(const pbft_checkpoint &cp, const public_key_type &pk) { + + if (cp.block_info.block_num() > ctrl.head_block_num() || cp.block_info.block_num() <= ctrl.last_stable_checkpoint_block_num()) return false; - if (cp.block_num > ctrl.head_block_num() - || cp.block_num <= ctrl.last_stable_checkpoint_block_num() - || !cp.is_signature_valid()) - return false; - auto bs = ctrl.fetch_block_state_by_id(cp.block_id); - if (bs) { + if (auto bs = ctrl.fetch_block_state_by_id(cp.block_info.block_id)) { auto active_bps = bs->active_schedule.producers; - for (const auto &bp: active_bps) { - if (bp.block_signing_key == cp.public_key) return true; + for (auto const &bp: active_bps) { + if (bp.block_signing_key == pk) return true; } } return false; } - bool pbft_database::is_valid_stable_checkpoint(const pbft_stable_checkpoint &scp) { - if (scp.block_num <= ctrl.last_stable_checkpoint_block_num()) + bool pbft_database::is_valid_stable_checkpoint(const pbft_stable_checkpoint &scp, bool add_to_pbft_db) { + if (scp.block_info.block_num() <= ctrl.last_stable_checkpoint_block_num()) // the stable checkpoint is way behind lib, no way getting the block state, // it will not be applied nor saved, thus considered safe. return true; - auto valid = true; - for (const auto &c: scp.checkpoints) { - valid = valid && is_valid_checkpoint(c) - && c.block_id == scp.block_id - && c.block_num == scp.block_num; - if (!valid) return false; + auto checkpoints = scp.checkpoints; + auto checkpoints_metadata = vector>{}; + checkpoints_metadata.reserve(checkpoints.size()); + + for (auto &cp : checkpoints) { + auto pmm = pbft_message_metadata(cp, chain_id); + checkpoints_metadata.emplace_back(pmm); + if (cp.block_info != scp.block_info || !is_valid_checkpoint(cp, pmm.sender_key)) return false; + if (add_to_pbft_db) add_pbft_checkpoint(cp, pmm.sender_key); } - auto bs = ctrl.fetch_block_state_by_number(scp.block_num); - if (bs) { + if (add_to_pbft_db) checkpoint_local(); + + + if (auto bs = ctrl.fetch_block_state_by_number(scp.block_info.block_num())) { auto as = bs->active_schedule; auto cp_count = 0; - for (auto const &sp: as.producers) { - for (auto const &v: scp.checkpoints) { - if (sp.block_signing_key == v.public_key) cp_count += 1; + for (auto const &bp: as.producers) { + for (auto const &cpm: checkpoints_metadata) { + if (bp.block_signing_key == cpm.sender_key) cp_count += 1; } } - valid = valid && cp_count >= as.producers.size() * 2 / 3 + 1; - } else { - return false; + return cp_count >= as.producers.size() * 2 / 3 + 1; } - return valid; + return false; + } bool pbft_database::should_send_pbft_msg() { - //use last_stable_checkpoint producer schedule - auto lscb_num = ctrl.last_stable_checkpoint_block_num(); - - auto as = lscb_active_producers(); - auto my_sp = ctrl.my_signature_providers(); - - for (auto i = lscb_num; i <= ctrl.head_block_num(); ++i) { - for (auto const &bp: as.producers) { - for (auto const &my: my_sp) { - if (bp.block_signing_key == my.first) return true; - } + auto schedules = get_updated_fork_schedules(); + for (auto const &bp: schedules) { + for (auto const &sp: ctrl.my_signature_providers()) { + if (bp.first == sp.first) return true; } - auto bs = ctrl.fetch_block_state_by_number(i); - if (bs && bs->active_schedule != as) as = bs->active_schedule; } return false; } bool pbft_database::should_recv_pbft_msg(const public_key_type &pub_key) { - auto lscb_num = ctrl.last_stable_checkpoint_block_num(); - - auto as = lscb_active_producers(); - auto my_sp = ctrl.my_signature_providers(); - for (auto i = lscb_num; i <= ctrl.head_block_num(); ++i) { - for (auto const &bp: as.producers) { - if (bp.block_signing_key == pub_key) return true; - } - auto bs = ctrl.fetch_block_state_by_number(i); - if (bs && bs->active_schedule != as) as = bs->active_schedule; + auto schedules = get_updated_fork_schedules(); + for (auto const &bp: schedules) { + if (bp.first == pub_key) return true; } return false; } - public_key_type pbft_database::get_new_view_primary_key(const uint32_t target_view) { + public_key_type pbft_database::get_new_view_primary_key(const pbft_view_type target_view) { auto active_bps = lscb_active_producers().producers; - if (active_bps.empty()) return public_key_type{}; + if (active_bps.empty()) return public_key_type(); return active_bps[target_view % active_bps.size()].block_signing_key; } @@ -1418,79 +1426,146 @@ namespace eosio { auto num = ctrl.last_stable_checkpoint_block_num(); if (num == 0) { - const auto &ucb = ctrl.get_upgrade_properties().upgrade_complete_block_num; + auto const &ucb = ctrl.get_upgrade_properties().upgrade_complete_block_num; if (ucb == 0) return ctrl.initial_schedule(); num = ucb; } - auto bs = ctrl.fetch_block_state_by_number(num); - if (!bs) return ctrl.initial_schedule(); - - if (bs->pending_schedule.producers.empty()) return bs->active_schedule; - return bs->pending_schedule; - } - - chain_id_type pbft_database::chain_id() { - return ctrl.get_chain_id(); + if (auto bs = ctrl.fetch_block_state_by_number(num)) { + if (bs->pending_schedule.producers.empty()) return bs->active_schedule; + return bs->pending_schedule; + } + return ctrl.initial_schedule(); } block_num_type pbft_database::get_current_pbft_watermark() { - auto unique_merge = [&](vector &v1, vector &v2) - { - std::sort(v1.begin(), v1.end()); - v1.reserve(v1.size() + v2.size()); - v1.insert(v1.end(), v2.begin(), v2.end()); + auto lib = ctrl.last_irreversible_block_num(); - sort( v1.begin(), v1.end() ); - v1.erase( unique( v1.begin(), v1.end() ), v1.end() ); - }; + auto watermarks = get_updated_watermarks(); + if (watermarks.empty()) return 0; - auto lib = ctrl.last_irreversible_block_num(); - auto lscb = ctrl.last_stable_checkpoint_block_num(); + auto cw = std::upper_bound(watermarks.begin(), watermarks.end(), lib); + + if (cw == watermarks.end() || *cw <= lib) return 0; - auto proposed_schedule_blocks = ctrl.proposed_schedule_block_nums(); - auto promoted_schedule_blocks = ctrl.promoted_schedule_block_nums(); - unique_merge(prepare_watermarks, proposed_schedule_blocks); - unique_merge(prepare_watermarks, promoted_schedule_blocks); + return *cw; + } + void pbft_database::update_fork_schedules() { - for ( auto itr = prepare_watermarks.begin(); itr != prepare_watermarks.end();) { - if ((*itr) <= lscb) { - itr = prepare_watermarks.erase(itr); - } else { - ++itr; + auto vector_minus = [&](vector &v1, vector &v2) + { + vector diff; + std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), + std::inserter(diff, diff.begin())); + return diff; + }; + + auto watermarks = ctrl.get_watermarks(); + + if (watermarks != prepare_watermarks) { + auto prev = prepare_watermarks; + prepare_watermarks = watermarks; + std::sort(prepare_watermarks.begin(), prepare_watermarks.end()); + auto added = vector_minus(prepare_watermarks, prev); + auto removed = vector_minus(prev, prepare_watermarks); + for (auto i: added) { + if (auto bs = ctrl.fetch_block_state_by_number(i)) { + auto as = bs->active_schedule.producers; + for (auto &bp: as) { + auto key = bp.block_signing_key; + if (fork_schedules.find(key) == fork_schedules.end()) { + fork_schedules[key] = i; + } else if ( i > fork_schedules[key]) { + fork_schedules[key] = i; + } + } + } + } + if (!removed.empty()) { + auto removed_num = *max_element(removed.begin(), removed.end()); + for (auto itr = fork_schedules.begin(); itr != fork_schedules.end();) { + if ((*itr).second <= removed_num) { + itr = fork_schedules.erase(itr); + } else { + ++itr; + } + } } } - std::sort(prepare_watermarks.begin(), prepare_watermarks.end()); + auto lscb_bps = lscb_active_producers().producers; + auto lscb_num = ctrl.last_stable_checkpoint_block_num(); + for (auto &bp: lscb_bps) { + if (fork_schedules.find(bp.block_signing_key) == fork_schedules.end() + || fork_schedules[bp.block_signing_key] < lscb_num) { + fork_schedules[bp.block_signing_key] = lscb_num; + } + } + } - if (prepare_watermarks.empty()) return 0; + vector& pbft_database::get_updated_watermarks() { + update_fork_schedules(); + return prepare_watermarks; + } - auto cw = *std::upper_bound(prepare_watermarks.begin(), prepare_watermarks.end(), lib); + flat_map& pbft_database::get_updated_fork_schedules() { + update_fork_schedules(); + return fork_schedules; + } - if (cw > lib) return cw; else return 0; + bool pbft_database::is_less_than_high_watermark(const block_num_type &bnum) { + auto current_watermark = get_current_pbft_watermark(); + return current_watermark == 0 || bnum <= current_watermark; } pbft_state_ptr pbft_database::get_pbft_state_by_id(const block_id_type& id) const { auto &by_block_id_index = pbft_state_index.get(); - auto itr = by_block_id_index.find(id); - if (itr != by_block_id_index.end()) { - return (*itr); + if (itr != by_block_id_index.end()) return (*itr); + + return pbft_state_ptr(); + } + + vector pbft_database::get_checkpoints_by_num(const block_num_type& num) const { + auto results = vector{}; + auto &by_num_index = checkpoint_index.get(); + + auto pitr = by_num_index.lower_bound( num ); + while(pitr != by_num_index.end() && (*pitr)->block_num == num ) { + results.emplace_back(*(*pitr)); + ++pitr; } - return pbft_state_ptr{}; + return results; } - void pbft_database::set(pbft_state_ptr s) { + pbft_view_change_state_ptr pbft_database::get_view_changes_by_target_view(const pbft_view_type& tv) const { + auto &by_view_index = view_state_index.get(); + auto itr = by_view_index.find(tv); + + if (itr != by_view_index.end()) return (*itr); + + return pbft_view_change_state_ptr(); + } + + vector pbft_database::get_pbft_watermarks() const { + return prepare_watermarks; + } + + flat_map pbft_database::get_pbft_fork_schedules() const { + return fork_schedules; + } + + void pbft_database::set(const pbft_state_ptr& s) { auto result = pbft_state_index.insert(s); EOS_ASSERT(result.second, pbft_exception, "unable to insert pbft state, duplicate state detected"); } - void pbft_database::set(pbft_checkpoint_state_ptr s) { + void pbft_database::set(const pbft_checkpoint_state_ptr& s) { auto result = checkpoint_index.insert(s); EOS_ASSERT(result.second, pbft_exception, "unable to insert pbft checkpoint index, duplicate state detected"); @@ -1512,6 +1587,22 @@ namespace eosio { } } + void pbft_database::prune(const pbft_checkpoint_state_ptr &h) { + auto num = h->block_num; + + auto &by_bn = checkpoint_index.get(); + auto bni = by_bn.begin(); + while (bni != by_bn.end() && (*bni)->block_num < num) { + prune(*bni); + bni = by_bn.begin(); + } + + auto itr = checkpoint_index.find(h->block_id); + if (itr != checkpoint_index.end()) { + checkpoint_index.erase(itr); + } + } + template void pbft_database::emit(const Signal &s, Arg &&a) { try { diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index d5da8ca279b..e2c8cd56c3b 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -124,7 +124,8 @@ namespace bacc = boost::accumulators; else { struct itimerval enable = {{0, 0}, {0, (int)x.count()-deadline_timer_verification.timer_overhead}}; expired = 0; - expired |= !!setitimer(ITIMER_REAL, &enable, NULL); + if(setitimer(ITIMER_REAL, &enable, NULL)) + expired = 1; } } diff --git a/pipeline.jsonc b/pipeline.jsonc new file mode 100644 index 00000000000..0f78488b9fb --- /dev/null +++ b/pipeline.jsonc @@ -0,0 +1,14 @@ +{ + "eosio-lrt": + { + "pipeline-branch": "legacy-os" + }, + "eosio-nightly-builds": + { + "pipeline-branch": "legacy-os" + }, + "eosio-base-images": + { + "pipeline-branch": "release/1.6.x" + } +} \ No newline at end of file diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index d81bd231a45..8eb52950702 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(producer_api_plugin) add_subdirectory(history_plugin) add_subdirectory(history_api_plugin) add_subdirectory(pbft_plugin) +add_subdirectory(pbft_api_plugin) add_subdirectory(state_history_plugin) add_subdirectory(wallet_plugin) diff --git a/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp b/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp index 766a73df56f..a168ea54928 100644 --- a/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp +++ b/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp @@ -64,11 +64,11 @@ namespace eosio { namespace chain { namespace plugin_interface { namespace pbft { namespace incoming { - using prepare_channel = channel_decl; - using commit_channel = channel_decl; - using view_change_channel = channel_decl; - using new_view_channel = channel_decl; - using checkpoint_channel = channel_decl; + using prepare_channel = channel_decl>; + using commit_channel = channel_decl>; + using view_change_channel = channel_decl>; + using new_view_channel = channel_decl>; + using checkpoint_channel = channel_decl>; } diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 965acc44a35..ce0eab28de5 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -178,11 +178,11 @@ class chain_plugin_impl { fc::microseconds abi_serializer_max_time_ms; fc::optional snapshot_path; - void on_pbft_incoming_prepare(pbft_prepare p); - void on_pbft_incoming_commit(pbft_commit c); - void on_pbft_incoming_view_change(pbft_view_change vc); - void on_pbft_incoming_new_view(pbft_new_view nv); - void on_pbft_incoming_checkpoint(pbft_checkpoint cp); + void on_pbft_incoming_prepare(pbft_metadata_ptr p); + void on_pbft_incoming_commit(pbft_metadata_ptr c); + void on_pbft_incoming_view_change(pbft_metadata_ptr vc); + void on_pbft_incoming_new_view(pbft_metadata_ptr nv); + void on_pbft_incoming_checkpoint(pbft_metadata_ptr cp); // retained references to channels for easy publication channels::pre_accepted_block::channel_type& pre_accepted_block_channel; @@ -214,30 +214,30 @@ class chain_plugin_impl { fc::optional accepted_confirmation_connection; //pbft - fc::optional pbft_outgoing_prepare_connection; - pbft::incoming::prepare_channel::channel_type::handle pbft_incoming_prepare_subscription; - pbft::outgoing::prepare_channel::channel_type& pbft_outgoing_prepare_channel; - pbft::incoming::prepare_channel::channel_type& pbft_incoming_prepare_channel; + fc::optional pbft_outgoing_prepare_connection; + pbft::incoming::prepare_channel::channel_type::handle pbft_incoming_prepare_subscription; + pbft::outgoing::prepare_channel::channel_type& pbft_outgoing_prepare_channel; + pbft::incoming::prepare_channel::channel_type& pbft_incoming_prepare_channel; - fc::optional pbft_outgoing_commit_connection; + fc::optional pbft_outgoing_commit_connection; pbft::incoming::commit_channel::channel_type::handle pbft_incoming_commit_subscription; - pbft::outgoing::commit_channel::channel_type& pbft_outgoing_commit_channel; - pbft::incoming::commit_channel::channel_type& pbft_incoming_commit_channel; - - fc::optional pbft_outgoing_view_change_connection; - pbft::incoming::view_change_channel::channel_type::handle pbft_incoming_view_change_subscription; - pbft::outgoing::view_change_channel::channel_type& pbft_outgoing_view_change_channel; - pbft::incoming::view_change_channel::channel_type& pbft_incoming_view_change_channel; - - fc::optional pbft_outgoing_new_view_connection; - pbft::incoming::new_view_channel::channel_type::handle pbft_incoming_new_view_subscription; - pbft::outgoing::new_view_channel::channel_type& pbft_outgoing_new_view_channel; - pbft::incoming::new_view_channel::channel_type& pbft_incoming_new_view_channel; - - fc::optional pbft_outgoing_checkpoint_connection; - pbft::incoming::checkpoint_channel::channel_type::handle pbft_incoming_checkpoint_subscription; - pbft::outgoing::checkpoint_channel::channel_type& pbft_outgoing_checkpoint_channel; - pbft::incoming::checkpoint_channel::channel_type& pbft_incoming_checkpoint_channel; + pbft::outgoing::commit_channel::channel_type& pbft_outgoing_commit_channel; + pbft::incoming::commit_channel::channel_type& pbft_incoming_commit_channel; + + fc::optional pbft_outgoing_view_change_connection; + pbft::incoming::view_change_channel::channel_type::handle pbft_incoming_view_change_subscription; + pbft::outgoing::view_change_channel::channel_type& pbft_outgoing_view_change_channel; + pbft::incoming::view_change_channel::channel_type& pbft_incoming_view_change_channel; + + fc::optional pbft_outgoing_new_view_connection; + pbft::incoming::new_view_channel::channel_type::handle pbft_incoming_new_view_subscription; + pbft::outgoing::new_view_channel::channel_type& pbft_outgoing_new_view_channel; + pbft::incoming::new_view_channel::channel_type& pbft_incoming_new_view_channel; + + fc::optional pbft_outgoing_checkpoint_connection; + pbft::incoming::checkpoint_channel::channel_type::handle pbft_incoming_checkpoint_subscription; + pbft::outgoing::checkpoint_channel::channel_type& pbft_outgoing_checkpoint_channel; + pbft::incoming::checkpoint_channel::channel_type& pbft_incoming_checkpoint_channel; }; chain_plugin::chain_plugin() @@ -805,23 +805,23 @@ void chain_plugin::plugin_initialize(const variables_map& options) { //pbft - my->pbft_incoming_prepare_subscription = my->pbft_incoming_prepare_channel.subscribe( [this]( pbft_prepare p ){ + my->pbft_incoming_prepare_subscription = my->pbft_incoming_prepare_channel.subscribe( [this]( pbft_metadata_ptr p ){ my->on_pbft_incoming_prepare(p); }); - my->pbft_incoming_commit_subscription = my->pbft_incoming_commit_channel.subscribe( [this]( pbft_commit c ){ + my->pbft_incoming_commit_subscription = my->pbft_incoming_commit_channel.subscribe( [this]( pbft_metadata_ptr c ){ my->on_pbft_incoming_commit(c); }); - my->pbft_incoming_view_change_subscription = my->pbft_incoming_view_change_channel.subscribe( [this]( pbft_view_change vc ){ + my->pbft_incoming_view_change_subscription = my->pbft_incoming_view_change_channel.subscribe( [this]( pbft_metadata_ptr vc ){ my->on_pbft_incoming_view_change(vc); }); - my->pbft_incoming_new_view_subscription = my->pbft_incoming_new_view_channel.subscribe( [this]( pbft_new_view nv ){ + my->pbft_incoming_new_view_subscription = my->pbft_incoming_new_view_channel.subscribe( [this]( pbft_metadata_ptr nv ){ my->on_pbft_incoming_new_view(nv); }); - my->pbft_incoming_checkpoint_subscription = my->pbft_incoming_checkpoint_channel.subscribe( [this]( pbft_checkpoint cp ){ + my->pbft_incoming_checkpoint_subscription = my->pbft_incoming_checkpoint_channel.subscribe( [this]( pbft_metadata_ptr cp ){ my->on_pbft_incoming_checkpoint(cp); }); @@ -856,23 +856,23 @@ void chain_plugin::plugin_initialize(const variables_map& options) { } -void chain_plugin_impl::on_pbft_incoming_prepare(pbft_prepare p){ +void chain_plugin_impl::on_pbft_incoming_prepare(pbft_metadata_ptr p){ pbft_ctrl->on_pbft_prepare(p); } -void chain_plugin_impl::on_pbft_incoming_commit(pbft_commit c){ +void chain_plugin_impl::on_pbft_incoming_commit(pbft_metadata_ptr c){ pbft_ctrl->on_pbft_commit(c); } -void chain_plugin_impl::on_pbft_incoming_view_change(pbft_view_change vc){ +void chain_plugin_impl::on_pbft_incoming_view_change(pbft_metadata_ptr vc){ pbft_ctrl->on_pbft_view_change(vc); } -void chain_plugin_impl::on_pbft_incoming_new_view(pbft_new_view nv){ +void chain_plugin_impl::on_pbft_incoming_new_view(pbft_metadata_ptr nv){ pbft_ctrl->on_pbft_new_view(nv); } -void chain_plugin_impl::on_pbft_incoming_checkpoint(pbft_checkpoint cp){ +void chain_plugin_impl::on_pbft_incoming_checkpoint(pbft_metadata_ptr cp){ pbft_ctrl->on_pbft_checkpoint(cp); } @@ -1209,8 +1209,8 @@ read_only::get_info_results read_only::get_info(const read_only::get_info_params db.fork_db_head_block_id(), db.fork_db_head_block_time(), db.fork_db_head_block_producer(), - pbft_ctrl.state_machine.get_current_view(), - pbft_ctrl.state_machine.get_target_view(), + pbft_ctrl.state_machine->get_current_view(), + pbft_ctrl.state_machine->get_target_view(), db.last_stable_checkpoint_block_num(), rm.get_virtual_block_cpu_limit(), rm.get_virtual_block_net_limit(), diff --git a/plugins/net_plugin/include/eosio/net_plugin/net_plugin.hpp b/plugins/net_plugin/include/eosio/net_plugin/net_plugin.hpp index 7fb193a979b..3de8b9a8d34 100644 --- a/plugins/net_plugin/include/eosio/net_plugin/net_plugin.hpp +++ b/plugins/net_plugin/include/eosio/net_plugin/net_plugin.hpp @@ -39,6 +39,8 @@ namespace eosio { vector connections()const; bool is_syncing()const; + void maybe_sync_stable_checkpoints(); + size_t num_peers() const; private: std::unique_ptr my; diff --git a/plugins/net_plugin/include/eosio/net_plugin/protocol.hpp b/plugins/net_plugin/include/eosio/net_plugin/protocol.hpp index 65b81583364..73a2e9caa04 100644 --- a/plugins/net_plugin/include/eosio/net_plugin/protocol.hpp +++ b/plugins/net_plugin/include/eosio/net_plugin/protocol.hpp @@ -146,6 +146,11 @@ struct request_p2p_message{ bool discoverable; string p2p_peer_list; }; + + struct compressed_pbft_message { + std::vector content; + }; + using net_message = static_variant; + checkpoint_request_message, + compressed_pbft_message>; + } // namespace eosio FC_REFLECT( eosio::select_ids, (mode)(pending)(ids) ) @@ -184,6 +191,7 @@ FC_REFLECT( eosio::sync_request_message, (start_block)(end_block) ) FC_REFLECT( eosio::request_p2p_message, (discoverable) ) FC_REFLECT( eosio::response_p2p_message, (discoverable)(p2p_peer_list) ) FC_REFLECT( eosio::checkpoint_request_message, (start_block)(end_block) ) +FC_REFLECT( eosio::compressed_pbft_message, (content)) /** diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index 2dcafa4feda..3b1a38f133b 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -28,6 +28,10 @@ #include +#include +#include +#include + using namespace eosio::chain::plugin_interface::compat; namespace fc { @@ -109,7 +113,9 @@ namespace eosio { }; class net_plugin_impl { private: - std::shared_ptr> encode_pbft_message(const net_message &msg)const; + std::vector compress_pbft(const std::shared_ptr>& m)const; + std::vector decompress_pbft(const std::vector& m)const; + std::shared_ptr> encode_pbft_message(const net_message &msg, bool compress = false)const; public: net_plugin_impl(); @@ -175,7 +181,7 @@ namespace eosio { std::unordered_map pbft_message_cache{}; const int pbft_message_cache_TTL = 600; - const int pbft_message_TTL = 10; + const int pbft_message_TTL = 60; channels::transaction_ack::channel_type::handle incoming_transaction_ack_subscription; eosio::chain::plugin_interface::pbft::outgoing::prepare_channel::channel_type::handle pbft_outgoing_prepare_subscription; @@ -240,16 +246,16 @@ namespace eosio { void handle_message( connection_ptr c, const response_p2p_message &msg); //pbft messages - bool maybe_add_pbft_cache(const string &uuid); - void clean_expired_pbft_cache(); + bool maybe_add_to_pbft_cache(const string &key); + void clean_expired_pbft_messages(); template bool is_pbft_msg_outdated(M const & msg); template bool is_pbft_msg_valid(M const & msg); - void bcast_pbft_msg(const net_message &msg); + void bcast_pbft_msg(const net_message &msg, int ttl); - void forward_pbft_msg(connection_ptr c, const net_message &msg); + void forward_pbft_msg(connection_ptr c, const net_message &msg, int ttl); void pbft_outgoing_prepare(const pbft_prepare &prepare); void pbft_outgoing_commit(const pbft_commit &commit); @@ -264,6 +270,7 @@ namespace eosio { void handle_message( connection_ptr c, const pbft_checkpoint &msg); void handle_message( connection_ptr c, const pbft_stable_checkpoint &msg); void handle_message( connection_ptr c, const checkpoint_request_message &msg); + void handle_message( connection_ptr c, const compressed_pbft_message &msg); void start_conn_timer(boost::asio::steady_timer::duration du, std::weak_ptr from_connection); void start_txn_timer(); @@ -677,6 +684,7 @@ namespace eosio { bool trigger_send, go_away_reason close_after_send, bool to_sync_queue = false); void enqueue_pbft( const std::shared_ptr>& m, const time_point_sec deadline); + bool pbft_read_to_send(); void cancel_sync(go_away_reason); @@ -684,7 +692,6 @@ namespace eosio { bool enqueue_sync_block(); void request_sync_blocks(uint32_t start, uint32_t end); - void request_sync_checkpoints(uint32_t start, uint32_t end); void cancel_wait(); void sync_wait(); void fetch_wait(); @@ -783,6 +790,7 @@ namespace eosio { uint32_t sync_last_requested_num; uint32_t sync_next_expected_num; uint32_t sync_req_span; + uint32_t last_req_scp_num; connection_ptr source; stages state; @@ -807,6 +815,7 @@ namespace eosio { void recv_notice(const connection_ptr& c, const notice_message& msg); bool is_syncing(); void set_in_sync(); + void sync_stable_checkpoints(const connection_ptr& c, uint32_t target); }; class dispatch_manager { @@ -938,7 +947,6 @@ namespace eosio { void connection::blk_send_branch() { controller& cc = my_impl->chain_plug->chain(); - pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); uint32_t head_num = cc.fork_db_head_block_num(); notice_message note; note.known_blocks.mode = normal; @@ -1288,8 +1296,7 @@ namespace eosio { to_sync_queue); } - void connection::enqueue_pbft(const std::shared_ptr>& m, - const time_point_sec deadline = time_point_sec(static_cast(600))) + void connection::enqueue_pbft(const std::shared_ptr>& m, const time_point_sec deadline) { pbft_queue.push_back(queued_pbft_message{m, deadline }); if (buffer_queue.is_out_queue_empty()) { @@ -1373,42 +1380,6 @@ namespace eosio { sync_wait(); } - void connection::request_sync_checkpoints(uint32_t start, uint32_t end) { - fc_dlog(logger, "request sync checkpoints"); - checkpoint_request_message crm = {start,end}; - enqueue( net_message(crm)); - sync_wait(); - } - -// bool connection::process_next_message(net_plugin_impl& impl, uint32_t message_length) { -// vector tmp_data; -// tmp_data.resize(message_length); -// -// try { -// auto ds = pending_message_buffer.create_datastream(); -// auto read_index = pending_message_buffer.read_index(); -// pending_message_buffer.peek(tmp_data.data(),message_length,read_index); -// -// net_message msg; -// fc::raw::unpack(ds, msg); -// msg_handler m(impl, shared_from_this() ); -// if( msg.contains() ) { -// m( std::move( msg.get() ) ); -// } else if( msg.contains() ) { -// m( std::move( msg.get() ) ); -// } else { -// msg.visit( m ); -// } -// } catch( const fc::exception& e ) { -// wlog("error message length: ${l}", ("l", message_length)); -// wlog("error raw bytes ${s}", ("s", tmp_data)); -// edump((e.to_detail_string() )); -// impl.close( shared_from_this() ); -// return false; -// } -// return true; -// } - bool connection::process_next_message(net_plugin_impl& impl, uint32_t message_length) { try { auto ds = pending_message_buffer.create_datastream(); @@ -1635,6 +1606,21 @@ namespace eosio { request_next_chunk(c); } + void sync_manager::sync_stable_checkpoints(const connection_ptr& c, uint32_t target) { + controller& cc = chain_plug->chain(); + uint32_t lscb_num = cc.last_stable_checkpoint_block_num(); + if (last_req_scp_num < lscb_num || last_req_scp_num == 0 || last_req_scp_num >= target) last_req_scp_num = lscb_num; + auto end = target; + auto max_target_scp_num = last_req_scp_num + pbft_checkpoint_granularity * 10; + if (target > max_target_scp_num) end = max_target_scp_num; + + checkpoint_request_message crm = {last_req_scp_num+1,end}; + c->enqueue( net_message(crm)); + fc_dlog(logger, "request sync stable checkpoints from ${s} to ${e}", + ("s", last_req_scp_num+1)("e", max_target_scp_num)); + last_req_scp_num = max_target_scp_num; + } + void sync_manager::reassign_fetch(const connection_ptr& c, go_away_reason reason) { fc_ilog(logger, "reassign_fetch, our last req is ${cc}, next expected is ${ne} peer ${p}", ( "cc",sync_last_requested_num)("ne",sync_next_expected_num)("p",c->peer_name())); @@ -1649,7 +1635,6 @@ namespace eosio { void sync_manager::recv_handshake(const connection_ptr& c, const handshake_message& msg) { controller& cc = chain_plug->chain(); uint32_t lib_num = cc.last_irreversible_block_num(); - uint32_t lscb_num = cc.last_stable_checkpoint_block_num(); uint32_t peer_lib = msg.last_irreversible_block_num; reset_lib_num(c); c->syncing = false; @@ -1668,13 +1653,6 @@ namespace eosio { uint32_t head = cc.fork_db_head_block_num(); block_id_type head_id = cc.fork_db_head_block_id(); - auto upgraded = cc.is_pbft_enabled(); - if (peer_lib > lscb_num && upgraded) { - //there might be a better way to sync checkpoints, yet we do not want to modify the existing handshake msg. - fc_dlog(logger, "request sync checkpoints"); - c->request_sync_checkpoints(lscb_num, peer_lib); - } - if (head_id == msg.head_id) { fc_dlog(logger, "sync check state 0"); // notify peer of our pending transactions @@ -2066,7 +2044,52 @@ namespace eosio { } //------------------------------------------------------------------------ - std::shared_ptr> net_plugin_impl::encode_pbft_message(const net_message &msg) const { + + namespace bio = boost::iostreams; + template + struct read_limiter { + using char_type = char; + using category = bio::multichar_output_filter_tag; + + template + size_t write(Sink &sink, const char* s, size_t count) + { + EOS_ASSERT(_total + count <= Limit, tx_decompression_error, "Exceeded maximum decompressed transaction size"); + _total += count; + return bio::write(sink, s, count); + } + size_t _total = 0; + }; + + std::vector net_plugin_impl::compress_pbft(const std::shared_ptr>& m) const { + std::vector out; + bio::filtering_ostream comp; + comp.push(bio::zlib_compressor(bio::zlib::best_compression)); + comp.push(bio::back_inserter(out)); + bio::write(comp, m->data(), m->size()); + bio::close(comp); + return out; + } + + std::vector net_plugin_impl::decompress_pbft(const std::vector& m) const { + try { + std::vector out; + bio::filtering_ostream decomp; + decomp.push(bio::zlib_decompressor()); + decomp.push(read_limiter<1*1024*1024>()); // limit to 10 megs decompressed for zip bomb protections + decomp.push(bio::back_inserter(out)); + bio::write(decomp, m.data(), m.size()); + bio::close(decomp); + return out; + } catch( fc::exception& er ) { + throw; + } catch( ... ) { + fc::unhandled_exception er( FC_LOG_MESSAGE( warn, "internal decompression error"), std::current_exception() ); + throw er; + } + } + + std::shared_ptr> net_plugin_impl::encode_pbft_message(const net_message &msg, bool compress) const { uint32_t payload_size = fc::raw::pack_size( msg ); @@ -2078,8 +2101,23 @@ namespace eosio { fc::datastream ds( send_buffer->data(), buffer_size); ds.write( header, header_size ); fc::raw::pack( ds, msg ); + auto out_buffer = send_buffer; - return send_buffer; + if (compress) { + auto cpnv = compressed_pbft_message{ compress_pbft(send_buffer) }; + payload_size = fc::raw::pack_size( cpnv ); + + header = reinterpret_cast(&payload_size); + header_size = sizeof(payload_size); + buffer_size = header_size + payload_size; + + auto compressed_buffer = std::make_shared>(buffer_size); + fc::datastream ds( compressed_buffer->data(), buffer_size); + ds.write( header, header_size ); + fc::raw::pack( ds, &cpnv ); + out_buffer = compressed_buffer; + } + return out_buffer; } void net_plugin_impl::connect(const connection_ptr& c) { @@ -2280,7 +2318,7 @@ namespace eosio { void net_plugin_impl::start_read_message(const connection_ptr& conn) { try { - if(!conn->socket) { + if(!conn->socket || !conn->socket->is_open()) { return; } connection_wptr weak_conn = conn; @@ -2732,11 +2770,11 @@ namespace eosio { } void net_plugin_impl::handle_message(const connection_ptr& c, const request_message& msg) { - if( msg.req_blocks.ids.size() > 1 ) { - elog( "Invalid request_message, req_blocks.ids.size ${s}", ("s", msg.req_blocks.ids.size()) ); - close(c); - return; - } +// if( msg.req_blocks.ids.size() > 1 ) { +// elog( "Invalid request_message, req_blocks.ids.size ${s}", ("s", msg.req_blocks.ids.size()) ); +// close(c); +// return; +// } // we should enable requesting multiple blocks switch (msg.req_blocks.mode) { case catch_up : @@ -2746,7 +2784,10 @@ namespace eosio { case normal : peer_ilog(c, "received request_message:normal"); if( !msg.req_blocks.ids.empty() ) { - c->blk_send(msg.req_blocks.ids.back()); + fc_dlog( logger, "received request_message, sending ${num} blocks from my node", ("num", msg.req_blocks.ids.size())); + for (auto const &bid: msg.req_blocks.ids) { + c->blk_send(bid); + } } break; default:; @@ -2793,19 +2834,22 @@ namespace eosio { if ( msg.end_block == 0 || msg.end_block < msg.start_block) return; - fc_dlog(logger, "received checkpoint request message"); + fc_dlog(logger, "received checkpoint request message ${m}", ("m", msg)); vector scp_stack; controller &cc = my_impl->chain_plug->chain(); pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - for (auto i = msg.end_block; i >= msg.start_block && i>0; --i) { + auto end_block = std::min(msg.end_block, cc.last_stable_checkpoint_block_num()); + + for (auto i = end_block; i >= msg.start_block && i>0; --i) { auto bid = cc.get_block_id_for_num(i); auto scp = pcc.pbft_db.get_stable_checkpoint_by_id(bid); - if (scp != pbft_stable_checkpoint{}) { + if (!scp.empty()) { scp_stack.push_back(scp); } } - fc_dlog(logger, "sent ${n} stable checkpoints on my node",("n",scp_stack.size())); + + if (!scp_stack.empty()) fc_dlog(logger, "sending ${n} stable checkpoints on my node",("n",scp_stack.size())); while (scp_stack.size()) { c->enqueue(scp_stack.back()); @@ -2862,9 +2906,23 @@ namespace eosio { fc_dlog(logger, "canceling wait on ${p}", ("p",c->peer_name())); c->cancel_wait(); + auto accept_pbft_stable_checkpoint = [&]() { + auto &pcc = chain_plug->pbft_ctrl(); + auto scp = pcc.pbft_db.fetch_stable_checkpoint_from_blk_extn(msg); + + if (!scp.empty() && scp.block_info.block_num() > cc.last_stable_checkpoint_block_num()) { + if (pcc.pbft_db.get_stable_checkpoint_by_id(msg->id(), false).empty()) { + handle_message(c, scp); + } else { + pcc.pbft_db.checkpoint_local(); + } + } + }; + try { if( cc.fetch_block_by_id(blk_id)) { sync_master->recv_block(c, blk_id, blk_num); + accept_pbft_stable_checkpoint(); return; } } catch( ...) { @@ -2880,14 +2938,8 @@ namespace eosio { go_away_reason reason = fatal_other; try { chain_plug->accept_block(msg); //, sync_master->is_active(c)); + accept_pbft_stable_checkpoint(); reason = no_reason; - auto blk = msg; - auto &pcc = chain_plug->pbft_ctrl(); - auto scp = pcc.pbft_db.fetch_stable_checkpoint_from_blk_extn(blk); - - if (scp != pbft_stable_checkpoint{}) { - handle_message(c, scp); - } } catch( const unlinkable_block_exception &ex) { peer_elog(c, "bad signed_block : ${m}", ("m",ex.what())); reason = unlinkable; @@ -2931,19 +2983,24 @@ namespace eosio { template bool net_plugin_impl::is_pbft_msg_outdated(M const & msg) { - return (time_point_sec(time_point::now()) > time_point_sec(msg.timestamp) + pbft_message_TTL); + if (time_point_sec(time_point::now()) > time_point_sec(msg.common.timestamp) + pbft_message_TTL) { + fc_dlog( logger, "received an outdated pbft message ${m}", ("m", msg)); + return true; + } + return false; } template bool net_plugin_impl::is_pbft_msg_valid(M const & msg) { // Do some basic validations of an incoming pbft msg, bad msgs should be quickly discarded without affecting state. - return (chain_id == msg.chain_id && !is_pbft_msg_outdated(msg) && !sync_master->is_syncing()); + return !is_pbft_msg_outdated(msg) + && !sync_master->is_syncing(); } - void net_plugin_impl::bcast_pbft_msg(const net_message &msg) { + void net_plugin_impl::bcast_pbft_msg(const net_message &msg, int ttl) { if (sync_master->is_syncing()) return; - auto deadline = time_point_sec(time_point::now()) + pbft_message_TTL; + auto deadline = time_point_sec(time_point::now()) + ttl; for (auto &conn: connections) { if (conn->pbft_ready()) { @@ -2952,8 +3009,8 @@ namespace eosio { } } - void net_plugin_impl::forward_pbft_msg(connection_ptr c, const net_message &msg) { - auto deadline = time_point_sec(time_point::now()) + pbft_message_TTL; + void net_plugin_impl::forward_pbft_msg(connection_ptr c, const net_message &msg, int ttl) { + auto deadline = time_point_sec(time_point::now()) + ttl; for (auto &conn: connections) { if (conn != c && conn->pbft_ready()) { @@ -2963,70 +3020,66 @@ namespace eosio { } void net_plugin_impl::pbft_outgoing_prepare(const pbft_prepare &msg) { - auto added = maybe_add_pbft_cache(msg.uuid); + auto added = maybe_add_to_pbft_cache(std::string(msg.sender_signature)); if (!added) return; pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (!pcc.pbft_db.is_valid_prepare(msg)) return; - bcast_pbft_msg(msg); - fc_ilog( logger, "sent prepare at height: ${n}, view: ${v}, from ${k}, ", ("n", msg.block_num)("v", msg.view)("k", msg.public_key)); + bcast_pbft_msg(msg, pbft_message_TTL); + fc_dlog( logger, "sent prepare at height: ${n}, view: ${v} ", ("n", msg.block_info.block_num())("v", msg.view)); } void net_plugin_impl::pbft_outgoing_commit(const pbft_commit &msg) { - auto added = maybe_add_pbft_cache(msg.uuid); + auto added = maybe_add_to_pbft_cache(std::string(msg.sender_signature)); if (!added) return; pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (!pcc.pbft_db.is_valid_commit(msg)) return; - bcast_pbft_msg(msg); - fc_ilog( logger, "sent commit at height: ${n}, view: ${v}, from ${k}, ", ("n", msg.block_num)("v", msg.view)("k", msg.public_key)); + bcast_pbft_msg(msg, pbft_message_TTL); + fc_dlog( logger, "sent commit at height: ${n}, view: ${v} ", ("n", msg.block_info.block_num())("v", msg.view)); } void net_plugin_impl::pbft_outgoing_view_change(const pbft_view_change &msg) { - auto added = maybe_add_pbft_cache(msg.uuid); + auto added = maybe_add_to_pbft_cache(std::string(msg.sender_signature)); if (!added) return; pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (!pcc.pbft_db.is_valid_view_change(msg)) return; - bcast_pbft_msg(msg); - fc_ilog( logger, "sent view change {cv: ${cv}, tv: ${tv}} from ${v}", ("cv", msg.current_view)("tv", msg.target_view)("v", msg.public_key)); + bcast_pbft_msg(msg, pbft_message_TTL); + fc_dlog( logger, "sent view change {cv: ${cv}, tv: ${tv}}", ("cv", msg.current_view)("tv", msg.target_view)); } void net_plugin_impl::pbft_outgoing_new_view(const pbft_new_view &msg) { - auto added = maybe_add_pbft_cache(msg.uuid); + auto added = maybe_add_to_pbft_cache(std::string(msg.sender_signature)); if (!added) return; pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (!pcc.pbft_db.is_valid_new_view(msg)) return; - bcast_pbft_msg(msg); - fc_ilog( logger, "sent new view at view: ${v}, from ${k}, ", ("v", msg.view)("k", msg.public_key)); + bcast_pbft_msg(msg, INT_MAX); + fc_dlog( logger, "sent new view at view: ${v} ", ("v", msg.new_view)); } void net_plugin_impl::pbft_outgoing_checkpoint(const pbft_checkpoint &msg) { - auto added = maybe_add_pbft_cache(msg.uuid); + auto added = maybe_add_to_pbft_cache(std::string(msg.sender_signature)); if (!added) return; pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (!pcc.pbft_db.is_valid_checkpoint(msg)) return; - bcast_pbft_msg(msg); + bcast_pbft_msg(msg, pbft_message_TTL); + fc_dlog( logger, "sent checkpoint at height: ${n} ", ("n", msg.block_info.block_num())); } - bool net_plugin_impl::maybe_add_pbft_cache(const string &uuid){ - auto itr = pbft_message_cache.find(uuid); + bool net_plugin_impl::maybe_add_to_pbft_cache(const string &key){ + auto itr = pbft_message_cache.find(key); if (itr == pbft_message_cache.end()) { //add to cache - pbft_message_cache[uuid] = time_point_sec(time_point::now()) + pbft_message_cache_TTL; + pbft_message_cache[key] = time_point_sec(time_point::now()) + pbft_message_cache_TTL; return true; } return false; } - void net_plugin_impl::clean_expired_pbft_cache(){ + void net_plugin_impl::clean_expired_pbft_messages(){ auto itr = pbft_message_cache.begin(); auto now = time_point::now(); @@ -3042,94 +3095,129 @@ namespace eosio { if (!is_pbft_msg_valid(msg)) return; - auto added = maybe_add_pbft_cache(msg.uuid); + auto added = maybe_add_to_pbft_cache(std::string(msg.sender_signature)); if (!added) return; - pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (!pcc.pbft_db.is_valid_prepare(msg)) return; + auto pmm = pbft_message_metadata(msg, chain_id); - forward_pbft_msg(c, msg); - fc_ilog( logger, "received prepare at height: ${n}, view: ${v}, from ${k}, ", ("n", msg.block_num)("v", msg.view)("k", msg.public_key)); + pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); + if (!pcc.pbft_db.is_valid_prepare(msg, pmm.sender_key)) return; - pbft_incoming_prepare_channel.publish(msg); + forward_pbft_msg(c, msg, pbft_message_TTL); + fc_dlog( logger, "received prepare at height: ${n}, view: ${v}, from ${k}, ", ("n", msg.block_info.block_num())("v", msg.view)("k", pmm.sender_key)); + pbft_incoming_prepare_channel.publish(std::make_shared>(pmm)); } void net_plugin_impl::handle_message( connection_ptr c, const pbft_commit &msg) { if (!is_pbft_msg_valid(msg)) return; - auto added = maybe_add_pbft_cache(msg.uuid); + auto added = maybe_add_to_pbft_cache(std::string(msg.sender_signature)); if (!added) return; + auto pmm = pbft_message_metadata(msg, chain_id); + pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (!pcc.pbft_db.is_valid_commit(msg)) return; + if (!pcc.pbft_db.is_valid_commit(msg, pmm.sender_key)) return; - forward_pbft_msg(c, msg); - fc_ilog( logger, "received commit at height: ${n}, view: ${v}, from ${k}, ", ("n", msg.block_num)("v", msg.view)("k", msg.public_key)); + forward_pbft_msg(c, msg, pbft_message_TTL); + fc_dlog( logger, "received commit at height: ${n}, view: ${v}, from ${k}, ", ("n", msg.block_info.block_num())("v", msg.view)("k", pmm.sender_key)); - pbft_incoming_commit_channel.publish(msg); + pbft_incoming_commit_channel.publish(std::make_shared>(pmm)); } void net_plugin_impl::handle_message( connection_ptr c, const pbft_view_change &msg) { if (!is_pbft_msg_valid(msg)) return; - auto added = maybe_add_pbft_cache(msg.uuid); + auto added = maybe_add_to_pbft_cache(std::string(msg.sender_signature)); if (!added) return; + auto pmm = pbft_message_metadata(msg, chain_id); + pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (!pcc.pbft_db.is_valid_view_change(msg)) return; + controller &ctrl = my_impl->chain_plug->chain(); + if (!pcc.pbft_db.is_valid_view_change(msg, pmm.sender_key)) return; - forward_pbft_msg(c, msg); - fc_ilog( logger, "received view change {cv: ${cv}, tv: ${tv}} from ${v}", ("cv", msg.current_view)("tv", msg.target_view)("v", msg.public_key)); + auto missing_blocks = set{}; + for (auto const &b: msg.prepared_cert.pre_prepares) { + if (!ctrl.fetch_block_by_id(b)) missing_blocks.emplace(b); + } + + if (!missing_blocks.empty()) { + fc_dlog( logger, "requesting ${num} missing blocks from view change", ("num", missing_blocks.size())); + request_message req; + for (auto const &b: missing_blocks) { + req.req_blocks.ids.push_back(b); + } + req.req_trx.mode = normal; + req.req_blocks.mode = normal; + c->enqueue(req); + } - pbft_incoming_view_change_channel.publish(msg); + forward_pbft_msg(c, msg, pbft_message_TTL); + fc_dlog( logger, "received view change {cv: ${cv}, tv: ${tv}} from ${v}", ("cv", msg.current_view)("tv", msg.target_view)("v", pmm.sender_key)); + + pbft_incoming_view_change_channel.publish(std::make_shared>(pmm)); } void net_plugin_impl::handle_message( connection_ptr c, const pbft_new_view &msg) { - if (!is_pbft_msg_valid(msg)) return; - - auto added = maybe_add_pbft_cache(msg.uuid); + auto added = maybe_add_to_pbft_cache(std::string(msg.sender_signature)); if (!added) return; + auto pmm = pbft_message_metadata(msg, chain_id); + pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (!pcc.pbft_db.is_valid_new_view(msg)) return; + if (pmm.sender_key != pcc.pbft_db.get_new_view_primary_key(msg.new_view)) return; - forward_pbft_msg(c, msg); - fc_ilog( logger, "received new view: ${n}, from ${v}", ("n", msg)("v", msg.public_key)); + forward_pbft_msg(c, msg, INT_MAX); + fc_dlog( logger, "received new view: ${n}, from ${v}", ("n", msg)("v", pmm.sender_key)); - pbft_incoming_new_view_channel.publish(msg); + pbft_incoming_new_view_channel.publish(std::make_shared>(pmm)); + } + + void net_plugin_impl::handle_message( connection_ptr c, const compressed_pbft_message &msg) { + + auto decompressed_msg = decompress_pbft(msg.content); + + net_message message; + fc::datastream ds(decompressed_msg.data(), decompressed_msg.size()); + fc::raw::unpack(ds, message); + + try { + msg_handler m(*this, c); + message.visit( m ); + } catch( const fc::exception& e ) { + edump((e.to_detail_string() )); + } } void net_plugin_impl::handle_message( connection_ptr c, const pbft_checkpoint &msg) { if (!is_pbft_msg_valid(msg)) return; - auto added = maybe_add_pbft_cache(msg.uuid); + auto added = maybe_add_to_pbft_cache(std::string(msg.sender_signature)); if (!added) return; + auto pmm = pbft_message_metadata(msg, chain_id); + pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (!pcc.pbft_db.is_valid_checkpoint(msg)) return; + if (!pcc.pbft_db.is_valid_checkpoint(msg, pmm.sender_key)) return; - forward_pbft_msg(c, msg); - fc_ilog( logger, "received checkpoint at ${n}, from ${v}", ("n", msg.block_num)("v", msg.public_key)); + forward_pbft_msg(c, msg, pbft_message_TTL); + fc_dlog( logger, "received checkpoint at ${n}, from ${v}", ("n", msg.block_info.block_num())("v", pmm.sender_key)); - pbft_incoming_checkpoint_channel.publish(msg); + pbft_incoming_checkpoint_channel.publish(std::make_shared>(pmm)); } void net_plugin_impl::handle_message( connection_ptr c, const pbft_stable_checkpoint &msg) { - if (chain_id != msg.chain_id) return; pbft_controller &pcc = my_impl->chain_plug->pbft_ctrl(); - if (pcc.pbft_db.is_valid_stable_checkpoint(msg)) { - fc_ilog(logger, "received stable checkpoint at ${n}, from ${v}", ("n", msg.block_num)("v", c->peer_name())); - for (auto cp: msg.checkpoints) { - pbft_incoming_checkpoint_channel.publish(cp); - } - } + if (!pcc.pbft_db.is_valid_stable_checkpoint(msg, true)) return; + fc_ilog(logger, "received stable checkpoint at ${n}, from ${v}", ("n", msg.block_info.block_num())("v", c->peer_name())); } void net_plugin_impl::start_conn_timer(boost::asio::steady_timer::duration du, std::weak_ptr from_connection) { @@ -3165,7 +3253,7 @@ namespace eosio { if (ec) { wlog ("pbft message cache ticker error: ${m}", ("m", ec.message())); } - clean_expired_pbft_cache(); + clean_expired_pbft_messages(); }); } @@ -3764,10 +3852,25 @@ namespace eosio { return result; } - bool net_plugin::is_syncing()const { - return my->sync_master->is_syncing(); + bool net_plugin::is_syncing()const { + return my->sync_master->is_syncing(); + } + + void net_plugin::maybe_sync_stable_checkpoints() { + controller& cc = my->chain_plug->chain(); + if (!cc.is_pbft_enabled()) return; + //there might be a better way to sync checkpoints, yet we do not want to modify the existing handshake msg. + uint32_t head = cc.fork_db_head_block_num(); + + for (auto const &c: my->connections) { + if (c->current()) { + my->sync_master->sync_stable_checkpoints(c, head); + } + } + } + net_plugin_impl::net_plugin_impl(): pbft_incoming_prepare_channel(app().get_channel()), pbft_incoming_commit_channel(app().get_channel()), diff --git a/plugins/pbft_api_plugin/CMakeLists.txt b/plugins/pbft_api_plugin/CMakeLists.txt new file mode 100644 index 00000000000..cda4ea2a0e8 --- /dev/null +++ b/plugins/pbft_api_plugin/CMakeLists.txt @@ -0,0 +1,7 @@ +file(GLOB HEADERS "include/eosio/pbft_api_plugin/*.hpp") +add_library( pbft_api_plugin + pbft_api_plugin.cpp + ${HEADERS} ) + +target_link_libraries( pbft_api_plugin pbft_plugin http_plugin appbase ) +target_include_directories( pbft_api_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/plugins/pbft_api_plugin/README.md b/plugins/pbft_api_plugin/README.md new file mode 100644 index 00000000000..54d3552089a --- /dev/null +++ b/plugins/pbft_api_plugin/README.md @@ -0,0 +1,87 @@ +# pbft_api_plugin + + +## Description +**pbft_api_plugin** exposes functionality from the pbft_plugin to the RPC API interface managed by the http_plugin, this plugin provides a chance to recover from a disaster ASAP. + +**!!! SHOULD ONLY BE USED ON SECURE NETWORKS** + + +## Dependencies +**chain_plugin** +**pbft_plugin** +**http_plugin** + + +## Usage & API reference + + # config.ini + plugin = eosio::pbft_api_plugin + + # nodeos startup params + --plugin eosio::pbft_api_plugin + +* **get_pbft_record** + + -- To obtain pbft database state on a specified block id + + ``` + curl --request POST --data string --url http://localhost:8888/v1/pbft/get_pbft_record + ``` + +* **get_pbft_checkpoints_record** + + -- To obtain pbft checkpoints on a specified block number + + ``` + curl --request POST --data uint32_t --url http://localhost:8888/v1/pbft/get_pbft_checkpoints_record + ``` +* **get_view_change_record** + + -- To obtain pbft view change messages on a specified target view + + ``` + curl --request POST --data uint32_t --url http://localhost:8888/v1/pbft/get_view_change_record + ``` +* **get_watermarks** + + -- To obtain pbft high watermarks + + ``` + curl --request POST --url http://localhost:8888/v1/pbft/get_watermarks + ``` +* **get_fork_schedules** + + -- To obtain all possible producer schedules inside fork database + + ``` + curl --request POST --url http://localhost:8888/v1/pbft/get_fork_schedules + ``` +* **get_pbft_status** + + -- To obtain current status from pbft state machine + + ``` + curl --request POST --url http://localhost:8888/v1/pbft/get_pbft_status + ``` +* **get_pbft_prepared_id** + + -- To obtain current prepared block id + + ``` + curl --request POST --url http://localhost:8888/v1/pbft/get_pbft_prepared_id + ``` +* **get_pbft_my_prepare_id** + + -- To obtain the block id that I recently prepare at + + ``` + curl --request POST --url http://localhost:8888/v1/pbft/get_pbft_my_prepare_id + ``` +* **set_pbft_current_view** + + -- To set the value of the current pbft view on my node, the node will transit to view change state afterwards + + ``` + curl --request POST --data uint32_t --url http://localhost:8888/v1/pbft/set_pbft_current_view + ``` \ No newline at end of file diff --git a/plugins/pbft_api_plugin/include/eosio/pbft_api_plugin/pbft_api_plugin.hpp b/plugins/pbft_api_plugin/include/eosio/pbft_api_plugin/pbft_api_plugin.hpp new file mode 100644 index 00000000000..f90e1cd2bb3 --- /dev/null +++ b/plugins/pbft_api_plugin/include/eosio/pbft_api_plugin/pbft_api_plugin.hpp @@ -0,0 +1,35 @@ +/** + * @file + * @copyright defined in eos/LICENSE + */ +#pragma once + +#include +#include + +#include + +namespace eosio { + +using namespace appbase; + +class pbft_api_plugin : public plugin { + public: + APPBASE_PLUGIN_REQUIRES( (pbft_plugin)(http_plugin)) + + pbft_api_plugin() = default; + pbft_api_plugin(const pbft_api_plugin&) = delete; + pbft_api_plugin(pbft_api_plugin&&) = delete; + pbft_api_plugin& operator=(const pbft_api_plugin&) = delete; + pbft_api_plugin& operator=(pbft_api_plugin&&) = delete; + virtual ~pbft_api_plugin() override = default; + + virtual void set_program_options(options_description& cli, options_description& cfg) override {} + void plugin_initialize(const variables_map& vm); + void plugin_startup(); + void plugin_shutdown() {} + + private: +}; + +} diff --git a/plugins/pbft_api_plugin/pbft_api_plugin.cpp b/plugins/pbft_api_plugin/pbft_api_plugin.cpp new file mode 100644 index 00000000000..33fcd7b8654 --- /dev/null +++ b/plugins/pbft_api_plugin/pbft_api_plugin.cpp @@ -0,0 +1,90 @@ +/** + * @file + * @copyright defined in eos/LICENSE + */ +#include +#include + +#include +#include + +#include + +namespace eosio { namespace detail { + struct pbft_api_plugin_response { + std::string result; + }; +}} + +FC_REFLECT(eosio::detail::pbft_api_plugin_response, (result)); + +namespace eosio { + +static appbase::abstract_plugin& _pbft_api_plugin = app().register_plugin(); + +using namespace eosio; + +#define CALL(api_name, api_handle, call_name, INVOKE, http_response_code) \ +{std::string("/v1/" #api_name "/" #call_name), \ + [&api_handle](string, string body, url_response_callback cb) mutable { \ + try { \ + if (body.empty()) body = "{}"; \ + INVOKE \ + cb(http_response_code, fc::json::to_string(result)); \ + } catch (...) { \ + http_plugin::handle_exception(#api_name, #call_name, body, cb); \ + } \ + }} +#define INVOKE_R(api_handle, call_name) \ + auto result = api_handle.call_name(); + +#define INVOKE_R_P(api_handle, call_name, in_param) \ + auto result = api_handle.call_name(fc::json::from_string(body).as()); + +#define INVOKE_W_P(api_handle, call_name, in_param) \ + api_handle.call_name(fc::json::from_string(body).as()); \ + eosio::detail::pbft_api_plugin_response result{"ok"}; + +void pbft_api_plugin::plugin_startup() { + ilog("starting pbft_api_plugin"); + // lifetime of plugin is lifetime of application + auto& pbft = app().get_plugin(); + + app().get_plugin().add_api({ + CALL(pbft, pbft, get_watermarks, INVOKE_R(pbft, get_watermarks), 200), + CALL(pbft, pbft, get_fork_schedules, INVOKE_R(pbft, get_fork_schedules), 200), + CALL(pbft, pbft, get_pbft_record, INVOKE_R_P(pbft, get_pbft_record, block_id_type), 200), + CALL(pbft, pbft, get_pbft_checkpoints_record, INVOKE_R_P(pbft, get_pbft_checkpoints_record, block_num_type), 200), + CALL(pbft, pbft, get_view_change_record, INVOKE_R_P(pbft, get_view_change_record, pbft_view_type), 200), + CALL(pbft, pbft, get_pbft_status, INVOKE_R(pbft, get_pbft_status), 200), + CALL(pbft, pbft, get_pbft_prepared_id, INVOKE_R(pbft, get_pbft_prepared_id), 200), + CALL(pbft, pbft, get_pbft_my_prepare_id, INVOKE_R(pbft, get_pbft_my_prepare_id), 200), + CALL(pbft, pbft, set_pbft_current_view, INVOKE_W_P(pbft, set_pbft_current_view, pbft_view_type), 201), + }); +} + +void pbft_api_plugin::plugin_initialize(const variables_map& options) { + try { + const auto& _http_plugin = app().get_plugin(); + if( !_http_plugin.is_on_loopback()) { + wlog( "\n" + "**********SECURITY WARNING**********\n" + "* *\n" + "* -- PBFT API -- *\n" + "* - EXPOSED to the LOCAL NETWORK - *\n" + "* - USE ONLY ON SECURE NETWORKS! - *\n" + "* *\n" + "************************************\n" ); + + } + } FC_LOG_AND_RETHROW() +} + +#undef INVOKE_R +#undef INVOKE_R_P +#undef INVOKE_W_P + + +#undef CALL + +} diff --git a/plugins/pbft_plugin/include/eosio/pbft_plugin/pbft_plugin.hpp b/plugins/pbft_plugin/include/eosio/pbft_plugin/pbft_plugin.hpp index d94c74785e3..69f67ea6cf8 100644 --- a/plugins/pbft_plugin/include/eosio/pbft_plugin/pbft_plugin.hpp +++ b/plugins/pbft_plugin/include/eosio/pbft_plugin/pbft_plugin.hpp @@ -4,6 +4,9 @@ */ #pragma once #include +#include +#include +#include namespace eosio { @@ -13,7 +16,7 @@ class pbft_plugin : public appbase::plugin { public: pbft_plugin(); virtual ~pbft_plugin(); - + APPBASE_PLUGIN_REQUIRES() virtual void set_program_options(options_description&, options_description& cfg) override; @@ -21,6 +24,19 @@ class pbft_plugin : public appbase::plugin { void plugin_startup(); void plugin_shutdown(); + + pbft_state get_pbft_record( const block_id_type& bid )const; + vector get_pbft_checkpoints_record(const block_num_type &bnum)const; + pbft_view_change_state get_view_change_record(const pbft_view_type& view)const; + vector get_watermarks()const; + flat_map get_fork_schedules()const; + const char* get_pbft_status()const; + block_id_type get_pbft_prepared_id()const; + block_id_type get_pbft_my_prepare_id()const; + + void set_pbft_current_view(const pbft_view_type &view); + + private: std::unique_ptr my; }; diff --git a/plugins/pbft_plugin/pbft_plugin.cpp b/plugins/pbft_plugin/pbft_plugin.cpp index 63bb1415b2c..0dcc60a5d11 100644 --- a/plugins/pbft_plugin/pbft_plugin.cpp +++ b/plugins/pbft_plugin/pbft_plugin.cpp @@ -2,9 +2,6 @@ #include #include -#include -#include -#include #include namespace eosio { @@ -21,7 +18,7 @@ namespace eosio { boost::asio::steady_timer::duration prepare_timeout{std::chrono::milliseconds{1000}}; boost::asio::steady_timer::duration commit_timeout{std::chrono::milliseconds{1000}}; - boost::asio::steady_timer::duration view_change_timeout{std::chrono::seconds{5}}; + boost::asio::steady_timer::duration view_change_check_interval{std::chrono::seconds{5}}; boost::asio::steady_timer::duration checkpoint_timeout{std::chrono::seconds{50}}; void prepare_timer_tick(); @@ -61,7 +58,57 @@ namespace eosio { my->checkpoint_timer_tick(); } - void pbft_plugin::plugin_shutdown() { + void pbft_plugin::plugin_shutdown() {} + + pbft_state pbft_plugin::get_pbft_record( const block_id_type& bid ) const { + pbft_controller& pbft_ctrl = app().get_plugin().pbft_ctrl(); + auto record = pbft_ctrl.pbft_db.get_pbft_state_by_id(bid); + if (record) return *record; + return pbft_state(); + } + + vector pbft_plugin::get_pbft_checkpoints_record(const block_num_type &bnum) const { + pbft_controller& pbft_ctrl = app().get_plugin().pbft_ctrl(); + auto records = pbft_ctrl.pbft_db.get_checkpoints_by_num(bnum); + if (!records.empty()) return records; + return vector(); + } + pbft_view_change_state pbft_plugin::get_view_change_record(const pbft_view_type& view) const { + pbft_controller& pbft_ctrl = app().get_plugin().pbft_ctrl(); + auto record = pbft_ctrl.pbft_db.get_view_changes_by_target_view(view); + if (record) return *record; + return pbft_view_change_state(); + } + + vector pbft_plugin::get_watermarks() const { + pbft_controller& pbft_ctrl = app().get_plugin().pbft_ctrl(); + return pbft_ctrl.pbft_db.get_pbft_watermarks(); + } + + flat_map pbft_plugin::get_fork_schedules() const { + pbft_controller& pbft_ctrl = app().get_plugin().pbft_ctrl(); + return pbft_ctrl.pbft_db.get_pbft_fork_schedules(); + } + + const char* pbft_plugin::get_pbft_status() const { + pbft_controller& pbft_ctrl = app().get_plugin().pbft_ctrl(); + return pbft_ctrl.state_machine->get_current()->get_name(); + } + + block_id_type pbft_plugin::get_pbft_prepared_id() const { + auto& ctrl = app().get_plugin().chain(); + return ctrl.get_pbft_prepared(); + } + + block_id_type pbft_plugin::get_pbft_my_prepare_id() const { + auto& ctrl = app().get_plugin().chain(); + return ctrl.get_pbft_my_prepare(); + } + + void pbft_plugin::set_pbft_current_view(const pbft_view_type& view) { + //this is used to boost the recovery from a disaster, do not set this unless you have to do so. + pbft_controller& pbft_ctrl = app().get_plugin().pbft_ctrl(); + pbft_ctrl.state_machine->manually_set_current_view(view); } void pbft_plugin_impl::prepare_timer_tick() { @@ -71,8 +118,8 @@ namespace eosio { prepare_timer_tick(); if (ec) { wlog ("pbft plugin prepare timer tick error: ${m}", ("m", ec.message())); - } else { - if (pbft_ready()) pbft_ctrl.maybe_pbft_prepare(); + } else if (pbft_ready()) { + pbft_ctrl.maybe_pbft_prepare(); } }); } @@ -84,8 +131,8 @@ namespace eosio { commit_timer_tick(); if (ec) { wlog ("pbft plugin commit timer tick error: ${m}", ("m", ec.message())); - } else { - if (pbft_ready()) pbft_ctrl.maybe_pbft_commit(); + } else if (pbft_ready()) { + pbft_ctrl.maybe_pbft_commit(); } }); } @@ -97,13 +144,13 @@ namespace eosio { } catch (boost::system::system_error &e) { elog("view change timer cancel error: ${e}", ("e", e.what())); } - view_change_timer->expires_from_now(view_change_timeout); + view_change_timer->expires_from_now(view_change_check_interval); view_change_timer->async_wait([&](boost::system::error_code ec) { view_change_timer_tick(); if (ec) { wlog ("pbft plugin view change timer tick error: ${m}", ("m", ec.message())); - } else { - if (pbft_ready()) pbft_ctrl.maybe_pbft_view_change(); + } else if (pbft_ready()) { + pbft_ctrl.maybe_pbft_view_change(); } }); } @@ -115,8 +162,14 @@ namespace eosio { checkpoint_timer_tick(); if (ec) { wlog ("pbft plugin checkpoint timer tick error: ${m}", ("m", ec.message())); - } else { - if (pbft_ready()) pbft_ctrl.send_pbft_checkpoint(); + } else if (pbft_ready()) { + pbft_ctrl.maybe_pbft_checkpoint(); + + chain::controller &ctrl = app().get_plugin().chain(); + if ( ctrl.head_block_num() - ctrl.last_stable_checkpoint_block_num() / pbft_checkpoint_granularity > 1) { + //perhaps we need to sync stable checkpoints from other peers + app().get_plugin().maybe_sync_stable_checkpoints(); + } } }); } diff --git a/plugins/producer_api_plugin/producer_api_plugin.cpp b/plugins/producer_api_plugin/producer_api_plugin.cpp index 62f4664ed82..7fcde1ac98c 100644 --- a/plugins/producer_api_plugin/producer_api_plugin.cpp +++ b/plugins/producer_api_plugin/producer_api_plugin.cpp @@ -90,8 +90,6 @@ void producer_api_plugin::plugin_startup() { INVOKE_R_V(producer, get_integrity_hash), 201), CALL(producer, producer, create_snapshot, INVOKE_R_V(producer, create_snapshot), 201), - CALL(producer, producer, set_pbft_current_view, - INVOKE_V_R(producer, set_pbft_current_view, uint32_t), 201), }); } diff --git a/plugins/producer_plugin/include/eosio/producer_plugin/producer_plugin.hpp b/plugins/producer_plugin/include/eosio/producer_plugin/producer_plugin.hpp index f6d10eb277b..c43f0e0f38b 100644 --- a/plugins/producer_plugin/include/eosio/producer_plugin/producer_plugin.hpp +++ b/plugins/producer_plugin/include/eosio/producer_plugin/producer_plugin.hpp @@ -82,8 +82,6 @@ class producer_plugin : public appbase::plugin { integrity_hash_information get_integrity_hash() const; snapshot_information create_snapshot() const; - void set_pbft_current_view(const uint32_t view); - signal confirmed_block; private: std::shared_ptr my; diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index e614e84d398..b7232b9a5ca 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -225,12 +225,12 @@ class producer_plugin_impl : public std::enable_shared_from_thisid; - auto new_version = chain.is_pbft_enabled(); + auto pbft_enabled = chain.is_pbft_enabled(); - auto new_bs = bsp->generate_next(new_block_header.timestamp, new_version); + auto new_bs = bsp->generate_next(new_block_header.timestamp, pbft_enabled); // for newly installed producers we can set their watermarks to the block they became active - if (new_bs.maybe_promote_pending(new_version) && bsp->active_schedule.version != new_bs.active_schedule.version) { + if (new_bs.maybe_promote_pending(pbft_enabled) && bsp->active_schedule.version != new_bs.active_schedule.version) { flat_set new_producers; new_producers.reserve(new_bs.active_schedule.producers.size()); for( const auto& p: new_bs.active_schedule.producers) { @@ -979,12 +979,6 @@ producer_plugin::snapshot_information producer_plugin::create_snapshot() const { return {head_id, snapshot_path}; } -void producer_plugin::set_pbft_current_view(const uint32_t view) { - //this is used to recover from a disaster, do not set this unless you have to do so. - pbft_controller& pbft_ctrl = app().get_plugin().pbft_ctrl(); - pbft_ctrl.state_machine.manually_set_current_view(view); -} - optional producer_plugin_impl::calculate_next_block_time(const account_name& producer_name, const block_timestamp_type& current_block_time) const { chain::controller& chain = chain_plug->chain(); const auto& hbs = chain.head_block_state(); @@ -1104,9 +1098,9 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() { _pending_block_mode = pending_block_mode::speculating; } - auto new_version = chain.is_pbft_enabled(); + auto pbft_enabled = chain.is_pbft_enabled(); - if (_pending_block_mode == pending_block_mode::producing && !new_version) { + if (_pending_block_mode == pending_block_mode::producing && !pbft_enabled) { // determine if our watermark excludes us from producing at this point if (currrent_watermark_itr != _producer_watermarks.end()) { if (currrent_watermark_itr->second >= hbs->block_num + 1) { @@ -1128,7 +1122,7 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() { try { uint16_t blocks_to_confirm = 0; - if (_pending_block_mode == pending_block_mode::producing && !new_version) { + if (_pending_block_mode == pending_block_mode::producing && !pbft_enabled) { // determine how many blocks this producer can confirm // 1) if it is not a producer from this node, assume no confirmations (we will discard this block anyway) // 2) if it is a producer on this node that has never produced, the conservative approach is to assume no @@ -1236,7 +1230,7 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() { if( preprocess_deadline <= fc::time_point::now() ) exhausted = true; if( exhausted ) break; - const auto& trx = itr->second; + const transaction_metadata_ptr trx = itr->second; auto category = calculate_transaction_category(trx); if (category == tx_category::EXPIRED || (category == tx_category::UNEXPIRED_UNPERSISTED && _producers.empty())) diff --git a/programs/nodeos/CMakeLists.txt b/programs/nodeos/CMakeLists.txt index 9399302f246..4714803f680 100644 --- a/programs/nodeos/CMakeLists.txt +++ b/programs/nodeos/CMakeLists.txt @@ -64,6 +64,7 @@ target_link_libraries( ${NODE_EXECUTABLE_NAME} PRIVATE -Wl,${whole_archive_flag} producer_api_plugin -Wl,${no_whole_archive_flag} PRIVATE -Wl,${whole_archive_flag} test_control_plugin -Wl,${no_whole_archive_flag} PRIVATE -Wl,${whole_archive_flag} test_control_api_plugin -Wl,${no_whole_archive_flag} + PRIVATE -Wl,${whole_archive_flag} pbft_api_plugin -Wl,${no_whole_archive_flag} PRIVATE -Wl,${build_id_flag} PRIVATE chain_plugin http_plugin producer_plugin http_client_plugin pbft_plugin PRIVATE eosio_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c88caa1f995..fbe6cc94f42 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -43,6 +43,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_voting_test.py ${CMAKE_CURRENT configure_file(${CMAKE_CURRENT_SOURCE_DIR}/consensus-validation-malicious-producers.py ${CMAKE_CURRENT_BINARY_DIR}/consensus-validation-malicious-producers.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/validate-dirty-db.py ${CMAKE_CURRENT_BINARY_DIR}/validate-dirty-db.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/launcher_test.py ${CMAKE_CURRENT_BINARY_DIR}/launcher_test.py COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-label.sh ${CMAKE_CURRENT_BINARY_DIR}/version-label.sh COPYONLY) #To run plugin_test with all log from blockchain displayed, put --verbose after --, i.e. plugin_test -- --verbose add_test(NAME plugin_test COMMAND plugin_test --report_level=detailed --color_output) @@ -78,6 +79,7 @@ add_test(NAME validate_dirty_db_test COMMAND tests/validate-dirty-db.py -v --cle set_property(TEST validate_dirty_db_test PROPERTY LABELS nonparallelizable_tests) add_test(NAME launcher_test COMMAND tests/launcher_test.py -v --clean-run --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST launcher_test PROPERTY LABELS nonparallelizable_tests) +add_test(NAME version-label-test COMMAND tests/version-label.sh WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) # Long running tests add_test(NAME nodeos_sanity_lr_test COMMAND tests/nodeos_run_test.py -v --sanity-test --clean-run --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/tests/testUtils.py b/tests/testUtils.py index a8dbe0fd4d2..85a7f89f301 100755 --- a/tests/testUtils.py +++ b/tests/testUtils.py @@ -1,3 +1,4 @@ +import re import errno import subprocess import time @@ -217,12 +218,17 @@ def arePortsAvailable(ports): @staticmethod def pgrepCmd(serverName): - pgrepOpts="-fl" # pylint: disable=deprecated-method - if platform.linux_distribution()[0] in ["Ubuntu", "LinuxMint", "Fedora","CentOS Linux","arch"]: + # pgrep differs on different platform (amazonlinux1 and 2 for example). We need to check if pgrep -h has -a available and add that if so: + try: + pgrepHelp = re.search('-a', subprocess.Popen("pgrep --help 2>/dev/null", shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')) + pgrepHelp.group(0) # group() errors if -a is not found, so we don't need to do anything else special here. pgrepOpts="-a" + except AttributeError as error: + # If no -a, AttributeError: 'NoneType' object has no attribute 'group' + pgrepOpts="-fl" - return "pgrep %s %s" % (pgrepOpts, serverName) + return "pgrep %s %s" % (pgrepOpts, serverName)\ @staticmethod def getBlockLog(blockLogLocation, silentErrors=False, exitOnError=False): diff --git a/tests/version-label.sh b/tests/version-label.sh new file mode 100755 index 00000000000..008c469d2b8 --- /dev/null +++ b/tests/version-label.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# The purpose of this test is to ensure that the output of the "nodeos --version" command matches the version string defined by our CMake files +# If the environment variable BUILDKITE_TAG is empty or unset, this test will echo success +echo '##### Nodeos Version Label Test #####' +if [[ "$BUILDKITE_TAG" == '' || "$BUILDKITE" != 'true' ]]; then + echo 'This test is only run in Buildkite against tagged builds.' + [[ "$BUILDKITE" != 'true' ]] && echo 'This is not Buildkite.' + [[ "$BUILDKITE_TAG" == '' ]] && echo 'This is not a tagged build.' + echo 'Exiting...' + exit 0 +fi +echo 'Tagged build detected, running test.' +# orient ourselves +[[ "$EOSIO_ROOT" == '' ]] && EOSIO_ROOT=$(echo $(pwd)/ | grep -ioe '.*/eos/') +[[ "$EOSIO_ROOT" == '' ]] && EOSIO_ROOT=$(echo $(pwd)/ | grep -ioe '.*/EOSIO/eosio/') +[[ "$EOSIO_ROOT" == '' ]] && EOSIO_ROOT=$(echo $(pwd)/ | grep -ioe '.*/build/' | sed 's,/build/,,') +echo "Using EOSIO_ROOT=\"$EOSIO_ROOT\"." +# determine expected value +CMAKE_CACHE="$EOSIO_ROOT/build/CMakeCache.txt" +CMAKE_LISTS="$EOSIO_ROOT/CMakeLists.txt" +if [[ -f "$CMAKE_CACHE" && $(cat "$CMAKE_CACHE" | grep -c 'DOXY_EOS_VERSION') > 0 ]]; then + echo "Parsing \"$CMAKE_CACHE\"..." + EXPECTED="v$(cat "$CMAKE_CACHE" | grep 'DOXY_EOS_VERSION' | cut -d '=' -f 2)" +elif [[ -f "$CMAKE_LISTS" ]]; then + echo "Parsing \"$CMAKE_LISTS\"..." + export $(cat $CMAKE_LISTS | grep -ie 'set *( *VERSION_MAJOR' | cut -d '(' -f 2 | cut -d ')' -f 1 | awk '{print $1"="$2}') + export $(cat $CMAKE_LISTS | grep -ie 'set *( *VERSION_MINOR' | cut -d '(' -f 2 | cut -d ')' -f 1 | awk '{print $1"="$2}') + export $(cat $CMAKE_LISTS | grep -ie 'set *( *VERSION_PATCH' | cut -d '(' -f 2 | cut -d ')' -f 1 | awk '{print $1"="$2}') + if [[ $(cat $CMAKE_LISTS | grep -ice 'set *( *VERSION_SUFFIX') > 0 ]]; then + echo 'Using version suffix...' + export $(cat $CMAKE_LISTS | grep -ie 'set *( *VERSION_SUFFIX' | cut -d '(' -f 2 | cut -d ')' -f 1 | awk '{print $1"="$2}') + export $(echo "$(cat $CMAKE_LISTS | grep -ie 'set *( *VERSION_FULL.*VERSION_SUFFIX' | cut -d '(' -f 2 | cut -d ')' -f 1 | awk '{print $1"="$2}')" | sed "s/VERSION_MAJOR/$VERSION_MAJOR/" | sed "s/VERSION_MINOR/$VERSION_MINOR/" | sed "s/VERSION_PATCH/$VERSION_PATCH/" | sed "s/VERSION_SUFFIX/$VERSION_SUFFIX/" | tr -d '"{}$') + else + echo 'No version suffix found.' + export $(echo "$(cat $CMAKE_LISTS | grep -ie 'set *( *VERSION_FULL' | grep -ive 'VERSION_SUFFIX' | cut -d '(' -f 2 | cut -d ')' -f 1 | awk '{print $1"="$2}')" | sed "s/VERSION_MAJOR/$VERSION_MAJOR/" | sed "s/VERSION_MINOR/$VERSION_MINOR/" | sed "s/VERSION_PATCH/$VERSION_PATCH/" | tr -d '"{}$') + fi + EXPECTED="v$VERSION_FULL" +fi +# fail if no expected value was found +if [[ "$EXPECTED" == '' ]]; then + echo 'ERROR: Could not determine expected value for version label!' + set +e + echo "EOSIO_ROOT=\"$EOSIO_ROOT\"" + echo "CMAKE_CACHE=\"$CMAKE_CACHE\"" + echo "CMAKE_LISTS=\"$CMAKE_LISTS\"" + echo '' + echo "VERSION_MAJOR=\"$VERSION_MAJOR\"" + echo "VERSION_MINOR=\"$VERSION_MINOR\"" + echo "VERSION_PATCH=\"$VERSION_PATCH\"" + echo "VERSION_SUFFIX=\"$VERSION_SUFFIX\"" + echo "VERSION_FULL=\"$VERSION_FULL\"" + echo '' + echo '$ cat "$CMAKE_CACHE" | grep "DOXY_EOS_VERSION"' + cat "$CMAKE_CACHE" | grep "DOXY_EOS_VERSION" + echo '$ pwd' + pwd + echo '$ ls -la "$EOSIO_ROOT"' + ls -la "$EOSIO_ROOT" + echo '$ ls -la "$EOSIO_ROOT/build"' + ls -la "$EOSIO_ROOT/build" + exit 1 +fi +echo "Expecting \"$EXPECTED\"..." +# get nodeos version +ACTUAL=$($EOSIO_ROOT/build/bin/nodeos --version) || : # nodeos currently returns -1 for --version +# test +if [[ "$EXPECTED" == "$ACTUAL" ]]; then + echo 'Passed with \"$ACTUAL\".' + exit 0 +fi +echo 'Failed!' +echo "\"$EXPECTED\" != \"$ACTUAL\"" +exit 1 diff --git a/unittests/main.cpp b/unittests/main.cpp index 0644ce80545..63b40b68b51 100644 --- a/unittests/main.cpp +++ b/unittests/main.cpp @@ -26,7 +26,11 @@ boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { break; } } - if(!is_verbose) fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::off); + if(is_verbose) { + fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); + } else { + fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::off); + } // Register fc::exception translator boost::unit_test::unit_test_monitor.register_exception_translator(&translate_fc_exception); diff --git a/unittests/pbft_tests.cpp b/unittests/pbft_tests.cpp index e2bf7918272..266a043bf5a 100644 --- a/unittests/pbft_tests.cpp +++ b/unittests/pbft_tests.cpp @@ -187,6 +187,46 @@ void push_blocks( tester& from, tester& to ) { } } +BOOST_AUTO_TEST_CASE(view_change_validation) { + tester tester; + controller &ctrl = *tester.control; + pbft_controller pbft_ctrl{ctrl}; + + auto msp = make_signature_provider(); + ctrl.set_my_signature_providers(msp); + + ctrl.set_upo(48); + + tester.create_accounts( {N(alice),N(bob),N(carol),N(deny)} ); + tester.set_producers({N(alice),N(bob),N(carol),N(deny)}); + tester.produce_blocks(100); + + pbft_ctrl.maybe_pbft_prepare(); + pbft_ctrl.maybe_pbft_commit(); + tester.produce_blocks(1); + + BOOST_CHECK_EQUAL(ctrl.is_pbft_enabled(), true); + BOOST_CHECK_EQUAL(ctrl.head_block_num(), 102); + + + for(int i = 0; i< pbft_ctrl.view_change_timeout; i++){ + pbft_ctrl.maybe_pbft_view_change(); + } + pbft_ctrl.state_machine->do_send_view_change(); + auto new_view = pbft_ctrl.pbft_db.get_proposed_new_view_num(); + auto vcc = pbft_ctrl.pbft_db.generate_view_changed_certificate(new_view); + auto nv_msg = pbft_ctrl.pbft_db.send_pbft_new_view(vcc, new_view); + + bool nv_flag; + try { + pbft_ctrl.pbft_db.validate_new_view(nv_msg, tester::get_public_key(N(carol), "active")); + nv_flag = true; + } catch (fc::exception &e) { + nv_flag = false; + } + BOOST_CHECK_EQUAL(nv_flag, false); +} + BOOST_AUTO_TEST_CASE(switch_fork_when_accept_new_view_with_prepare_certificate_on_short_fork) { tester short_prepared_fork, long_non_prepared_fork, new_view_generator; controller &ctrl_short_prepared_fork = *short_prepared_fork.control.get(); @@ -262,15 +302,15 @@ BOOST_AUTO_TEST_CASE(switch_fork_when_accept_new_view_with_prepare_certificate_o BOOST_CHECK_EQUAL(ctrl_long_non_prepared_fork.last_irreversible_block_num(), 101); //generate new view with short fork prepare certificate - pbft_new_view_generator.state_machine.set_prepares_cache({}); + pbft_new_view_generator.state_machine->set_prepares_cache(pbft_prepare()); BOOST_CHECK_EQUAL(pbft_new_view_generator.pbft_db.should_send_pbft_msg(), true); pbft_new_view_generator.maybe_pbft_prepare(); BOOST_CHECK_EQUAL(pbft_new_view_generator.pbft_db.should_prepared(), true); BOOST_CHECK_EQUAL(ctrl_new_view_generator.head_block_num(), 136); - for(int i = 0; ido_send_view_change(); auto new_view = pbft_new_view_generator.pbft_db.get_proposed_new_view_num(); auto vcc = pbft_new_view_generator.pbft_db.generate_view_changed_certificate(new_view); auto nv_msg = pbft_new_view_generator.pbft_db.send_pbft_new_view( @@ -290,7 +330,7 @@ BOOST_AUTO_TEST_CASE(switch_fork_when_accept_new_view_with_prepare_certificate_o BOOST_CHECK_EQUAL(ctrl_short_prepared_fork.head_block_num(), 137); //can switch fork after apply prepare certificate in new view - pbft_short_prepared_fork.state_machine.on_new_view(nv_msg); + pbft_short_prepared_fork.state_machine->on_new_view(std::make_shared>(nv_msg, ctrl_new_view_generator.get_chain_id())); BOOST_CHECK_EQUAL(ctrl_short_prepared_fork.head_block_num(), 136); BOOST_CHECK_EQUAL(ctrl_short_prepared_fork.last_irreversible_block_num(), 101);