From 7971208347dc6188ccea0c7e1319851e92c5f537 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 21 Jun 2024 19:48:54 +0300 Subject: [PATCH 1/6] Changed modifier to RC for v6.3.0 --- rskj-core/src/main/resources/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rskj-core/src/main/resources/version.properties b/rskj-core/src/main/resources/version.properties index 8627100e8c1..f27be6b8017 100644 --- a/rskj-core/src/main/resources/version.properties +++ b/rskj-core/src/main/resources/version.properties @@ -1,2 +1,2 @@ versionNumber='6.3.0' -modifier="SNAPSHOT" +modifier="RC" From 2435271cd79825ccab793fd58547c25edd74fa36 Mon Sep 17 00:00:00 2001 From: Juraj Piar Date: Wed, 17 Apr 2024 00:53:58 +0100 Subject: [PATCH 2/6] feat(ci): migrates circleci to github actions Following commits were squashed: feat(ci): adjusts mining-tests feat(ci): makes services work feat(ci): makes services work removes port verification trying docker compose inside mining tests trying docker compose inside mining tests trying docker compose inside mining tests trying docker compose inside mining tests using mining tests fork anew just bitcoin nodes build publish docker reatach rest of job node v -> 12 run tests in same step as rskj reenables build and other jobs no recompile in mining-tests feat(ci): uses rsksmart docker images only push ddocker image if different feat(ci): uses private ghcr trying with a cooy of last working mining tests now adding build job from last working too changes rskj-core/src/main/resources/version.properties to 6.2.0 changes rskj-core/src/main/resources/version.properties to 6.2.0 back to 6.3 and show built jars makes build use temurin 17 adds version to cache uses artifacts for build caching fix(ci): runs test only once on PR opened feat(ci): only on push refactor(ci): rearrange jobs --- .circleci/config.yml | 200 ------------------- .github/workflows/build_and_test.yml | 288 +++++++++++++++++++++++++++ build.gradle | 10 + 3 files changed, 298 insertions(+), 200 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/build_and_test.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 53bc7177463..00000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,200 +0,0 @@ -version: 2.1 # needed for executors - -executors: - rskj-executor: - docker: - - image: openjdk:8-jdk - environment: - _JAVA_OPTIONS: "-Xmx3G -Xms2G" - working_directory: /app - resource_class: medium+ - sonarqube-executor: - docker: - - image: eclipse-temurin:17-jdk - working_directory: /app - mit-executor: - docker: - - image: alpine:3.10 - -jobs: - build: - executor: rskj-executor - steps: - - checkout - - run: - name: Setup - command: apt update -y && apt install -y gnupg2 - - run: - name: Verify files - command: | - curl -sSL https://secchannel.rsk.co/SUPPORT.asc | gpg2 --import - - gpg2 --verify SHA256SUMS.asc && sha256sum --check SHA256SUMS.asc - - run: - name: Build - command: | - ./configure.sh - ./gradlew --no-daemon dependencies - ./gradlew --no-daemon --stacktrace build -x test - - persist_to_workspace: - root: . - paths: - - . - sonarqube: - executor: sonarqube-executor - steps: - - attach_workspace: - at: /app - - run: - name: Run SonarQube analysis - command: | - apt-get update && apt-get install -yqq git - extra_flags="" - if [ -n "$CIRCLE_PULL_REQUEST" ]; then - # https://community.sonarsource.com/t/no-code-or-issue-found-in-pull-request-decorations-github-circleci/8496 - git branch -f master origin/master - # extract PR number, as PR URLs are in the form - # https://github.com/$user/$repo/pull/$pr_number - pr_number=${CIRCLE_PULL_REQUEST##*/} - extra_flags="-Dsonar.pullrequest.base=master - -Dsonar.pullrequest.branch=$CIRCLE_BRANCH - -Dsonar.pullrequest.key=$pr_number" - else - extra_flags="-Dsonar.branch.name=master" - fi - ./gradlew sonarqube --no-daemon -x build -x test \ - $extra_flags \ - -Dsonar.organization=rsksmart \ - -Dsonar.host.url="$SONAR_URL" \ - -Dsonar.login="$SONAR_TOKEN" - rskj-unit-tests: - executor: rskj-executor - steps: - - attach_workspace: - at: /app - - run: - name: rskj tests - command: ./gradlew --no-daemon --stacktrace test - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find rskj-core/build/test-results -type f -name "*.xml" \ - -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - - persist_to_workspace: - root: . - paths: - - . - rskj-int-tests: - executor: rskj-executor - steps: - - attach_workspace: - at: /app - - run: - name: rskj integration tests - command: ./gradlew --no-daemon --stacktrace integrationTest - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find rskj-core/build/test-results -type f -name "*.xml" \ - -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - - persist_to_workspace: - root: . - paths: - - . - mining-tests: - executor: mit-executor - steps: - - run: - name: Setup - command: apk add --no-cache curl jq - - run: - name: Mining integration tests - command: | - wait_for_completion() { - # inspired from https://discuss.circleci.com/t/waiting-for-build-to-complete-when-invoked-via-http-api/14989 - build_number=$1 - poll_interval=60 - - i=0 - max_count=20 - while [ $i -lt $max_count ]; do - # output to avoid CircleCI considering the job stuck - res=$(curl -Ssfu "$CIRCLE_INTEGRATIONS_TOKENS:" \ - "https://circleci.com/api/v1.1/project/github/$MIT_ORGANIZATION/$MIT_PROJECT/$build_number" \ - | jq -r '[.lifecycle, .outcome] | @tsv') - IFS=" " set -- $res - lifecycle=${1:-} - outcome=${2:-} - if [ "$lifecycle" = "queued" ]; then - printf "Build is enqueued. Waiting...\n" - # don't increment $i - else - printf "[%02u/%02u] Waiting for build %s ...\n" \ - "$i" "$max_count" "$build_url" - i=$(($i + 1)) - fi - if [ "$lifecycle" = "finished" ]; then - printf "Build %u finished. Outcome: \"%s\".\n" \ - "$build_number" "$outcome" - # return success iff job outcome is "success" - test "$outcome" = "success" - return $? - fi - - sleep $poll_interval - done - return 1 - } - - json_payload='{ - "build_parameters": { - "RSKJ_CIRCLE_BRANCH": "'$CIRCLE_BRANCH'", - "RSKJ_CIRCLE_USERNAME": "'$CIRCLE_PROJECT_USERNAME'", - "RSKJ_CIRCLE_REPONAME": "'$CIRCLE_PROJECT_REPONAME'", - "RSKJ_PR_NUMBER": "'$CIRCLE_PR_NUMBER'", - "RSKJ_CIRCLE_SHA1": "'$CIRCLE_SHA1'" - } - }' - res=$(curl -Ssf -u "$CIRCLE_INTEGRATIONS_TOKEN:" \ - -H "Content-type: application/json" -d "$json_payload" \ - "https://circleci.com/api/v1.1/project/github/$MIT_ORGANIZATION/$MIT_PROJECT/tree/$MIT_BRANCH" \ - | jq -r '[.build_url, .build_num] | @tsv') - IFS=" " set -- $res - test $# -eq 2 # ensure exactly 2 values are expanded - build_url=$1 - build_num=$2 - printf "Running mining integration tests. Follow it on:\n\n %s\n\n" "$build_url" - sleep 10 # give CircleCI some time to spin up the job - wait_for_completion "$build_num" - -workflows: - # https://circleci.com/docs/2.0/configuration-reference/#version-1 - # As of commit date, it reads: - # Should currently be `2` - version: 2 - build-and-test: - jobs: - - build - - rskj-unit-tests: - requires: - - build - - rskj-int-tests: - requires: - - build - - sonarqube: - requires: - - rskj-unit-tests - - mining-tests: - requires: - - rskj-unit-tests diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml new file mode 100644 index 00000000000..5383131d2d2 --- /dev/null +++ b/.github/workflows/build_and_test.yml @@ -0,0 +1,288 @@ +name: Build and Test + +on: + push: + branches: + - "master" + - "*-rc" + pull_request: + types: [opened, reopened, synchronize] + branches: + - "**" + +jobs: + build-rskj: + runs-on: ubuntu-latest + container: + image: openjdk:8-jdk + steps: + - uses: actions/checkout@v4 + - name: Setup System Tools + run: | + apt update -y + apt install -y gnupg2 curl + + - name: Verify files + run: | + curl -sSL https://secchannel.rsk.co/SUPPORT.asc | gpg2 --import - + gpg2 --verify SHA256SUMS.asc && sha256sum --check SHA256SUMS.asc + + - uses: actions/cache@v4 + name: Cache Gradle + id: cache-gradle + with: + path: | + .gradle/caches + gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Get gradle wrapper and build dependencies + if: steps.cache-gradle.outputs.cache-hit != 'true' + run: | + ./configure.sh + ./gradlew --no-daemon dependencies + + - name: Build + run: | + ./gradlew --no-daemon --stacktrace build -x test + + - name: Archive build artifacts + uses: actions/upload-artifact@v4 + with: + name: build-files + path: | + rskj-core/build + + smell-test: + needs: build-rskj + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Java JDK + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - uses: actions/cache@v4 + name: Restore Gradle cache + id: cache-gradle + with: + path: | + .gradle/caches + gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Get gradle wrapper and build dependencies + run: | + if [ ! -f gradle/wrapper/gradle-wrapper.jar ]; then + ./configure.sh + ./gradlew --no-daemon dependencies + fi + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build-files + path: | + rskj-core/build + + - name: Sonarqube for PRs + if: github.event_name == 'pull_request' + run: | + pr_number=${{ github.event.pull_request.number }} + extra_flags="-Dsonar.pullrequest.base=${{ github.base_ref }} \ + -Dsonar.pullrequest.branch=${{ github.head_ref }} \ + -Dsonar.pullrequest.key=$pr_number" + ./gradlew sonarqube --no-daemon -x build -x test \ + $extra_flags \ + -Dsonar.organization=rsksmart \ + -Dsonar.host.url="https://sonarcloud.io" \ + -Dsonar.token="${{ secrets.SONAR_TOKEN }}" + + - name: Sonarqube for master + if: github.event_name != 'pull_request' + run: | + ./gradlew sonarqube --no-daemon -x build -x test \ + -Dsonar.branch.name=master \ + -Dsonar.organization=rsksmart \ + -Dsonar.host.url="https://sonarcloud.io" \ + -Dsonar.token="${{ secrets.SONAR_TOKEN }}" + + mining-tests: + needs: build-rskj + runs-on: ubuntu-latest + services: + bitcoind1: + image: ghcr.io/rsksmart/rskj/mit_bitcoind1:latest + ports: + - 8331:8331 + - 31591:31591 + - 32591:32591 + options: --name bitcoind1 + bitcoind2: + image: ghcr.io/rsksmart/rskj/mit_bitcoind2:latest + ports: + - 8332:8332 + - 31592:31592 + - 32592:32592 + options: --name bitcoind2 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '12.x' + - name: Check Node.js version + run: node --version + + - name: Checkout Mining Integration Tests Repository + uses: actions/checkout@v4 + with: + repository: rsksmart/mining-integration-tests + ref: ${{ secrets.MINING_INTEGRATION_TESTS_REF }} + token: ${{ secrets.GITHUB_TOKEN }} + path: mining-integration-tests + + - name: Install Mining Integration Tests dependencies + working-directory: mining-integration-tests + run: | + npm ci + + - name: Change mining-integration-tests bitcoind url in config.json to localhost + working-directory: mining-integration-tests + run: | + jq 'if .bitcoind.url? then .bitcoind.url = "localhost" else error(".bitcoind.url not found") end' config.json > config.json.tmp && mv config.json.tmp config.json + + - name: Generate BTC blocks + working-directory: mining-integration-tests + run: | + node --unhandled-rejections=strict generateBtcBlocks.js + + - name: Setup Java JDK + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - uses: actions/cache@v4 + name: Cache Gradle + id: cache-gradle + with: + path: | + .gradle/caches + gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Get gradle wrapper and build dependencies + if: steps.cache-gradle.outputs.cache-hit != 'true' + run: | + ./configure.sh + ./gradlew --no-daemon dependencies + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build-files + path: | + rskj-core/build + + - name: Start RSKj and Run Tests + working-directory: mining-integration-tests + run: | + version=$(tr -d "'\"" < ../rskj-core/src/main/resources/version.properties \ + | cut -d = -f 2- | paste -sd - -) + echo "Using RskJ version $version at ../rskj-core/build/libs/rskj-core-$version-all.jar" + java -Drsk.conf.file=./rsk-integration-test.conf -cp ../rskj-core/build/libs/rskj-core-"$version"-all.jar co.rsk.Start --regtest & rskpid=$! + + tries=0 + MAX_TRIES=10 + while [ $tries -lt $MAX_TRIES ]; do + nc -z 127.0.0.1 4444 && break + echo "Waiting for RskJ..." + tries=$((tries+1)) + sleep 1 + done + + if [ $tries -eq $MAX_TRIES ]; then + echo "RskJ unreachable after $MAX_TRIES attempts. Aborting." >&2 + exit 1 + fi + + npm test + kill $rskpid + + unit-tests: + runs-on: ubuntu-latest + container: + image: openjdk:8-jdk + steps: + - uses: actions/checkout@v4 + + - name: Setup System Tools + run: | + apt update -y + apt install -y curl + + - uses: actions/cache@v4 + name: Cache Gradle + id: cache-gradle + with: + path: | + .gradle/caches + gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Get gradle wrapper and build dependencies + if: steps.cache-gradle.outputs.cache-hit != 'true' + run: | + ./configure.sh + ./gradlew --no-daemon dependencies + + - name: Run tests + run: | + ./gradlew --no-daemon --stacktrace test + + integration-tests: + runs-on: ubuntu-latest + container: + image: openjdk:8-jdk + steps: + - uses: actions/checkout@v4 + + - name: Setup System Tools + run: | + apt update -y + apt install -y curl + + - uses: actions/cache@v4 + name: Cache Gradle + id: cache-gradle + with: + path: | + .gradle/caches + gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Get gradle wrapper and build dependencies + if: steps.cache-gradle.outputs.cache-hit != 'true' + run: | + ./configure.sh + ./gradlew --no-daemon dependencies + + - name: Run tests + run: | + ./gradlew --no-daemon --stacktrace integrationTest diff --git a/build.gradle b/build.gradle index e26d0acfee6..b6d9f595e47 100644 --- a/build.gradle +++ b/build.gradle @@ -7,3 +7,13 @@ subprojects { group = 'co.rsk' version = config.modifier?.trim() ? config.versionNumber + "-" + config.modifier : config.versionNumber } + +sonarqube { + properties { + property "sonar.java.binaries", "rskj-core/build/classes/java/main" + property "sonar.scm.provider", "git" + property "sonar.projectBaseDir", project.projectDir + property "sonar.projectKey", "rskj" + property "sonar.organization", "rsksmart" + } +} From 8130ae00f37d16eea9530dfde12f32cc9c1cc3de Mon Sep 17 00:00:00 2001 From: Juraj Piar Date: Wed, 26 Jun 2024 10:31:27 +0100 Subject: [PATCH 3/6] fix(ci): make sonar work on push to rc --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5383131d2d2..27feda34231 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -109,7 +109,7 @@ jobs: if: github.event_name != 'pull_request' run: | ./gradlew sonarqube --no-daemon -x build -x test \ - -Dsonar.branch.name=master \ + -Dsonar.branch.name="${{ github.ref }}" \ -Dsonar.organization=rsksmart \ -Dsonar.host.url="https://sonarcloud.io" \ -Dsonar.token="${{ secrets.SONAR_TOKEN }}" From b231bde2566de0751ce885ee499a19c3619e5dd5 Mon Sep 17 00:00:00 2001 From: Juraj Piar Date: Thu, 27 Jun 2024 11:11:33 +0100 Subject: [PATCH 4/6] fix(ci): persist test reports for sonar --- .github/workflows/build_and_test.yml | 43 ++++++++++++++++++++-------- build.gradle | 9 ------ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 27feda34231..9cdd671e64a 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -56,10 +56,12 @@ jobs: rskj-core/build smell-test: - needs: build-rskj + needs: unit-tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup Java JDK uses: actions/setup-java@v3 @@ -92,28 +94,37 @@ jobs: path: | rskj-core/build - - name: Sonarqube for PRs + - name: Download test reports + uses: actions/download-artifact@v4 + with: + name: test-report + path: | + rskj-core/build/test-results/ + rskj-core/build/reports/ + + - name: Prepare PR flags for SonarQube analysis if: github.event_name == 'pull_request' run: | - pr_number=${{ github.event.pull_request.number }} + pr_number="${{ github.event.pull_request.number }}" extra_flags="-Dsonar.pullrequest.base=${{ github.base_ref }} \ -Dsonar.pullrequest.branch=${{ github.head_ref }} \ -Dsonar.pullrequest.key=$pr_number" + echo EXTRA_FLAGS="$extra_flags" >> $GITHUB_ENV + + - name: Prepare push flags for SonarQube analysis + if: github.event_name != 'pull_request' + run: | + echo EXTRA_FLAGS="-Dsonar.branch.name=${{ github.ref }}" >> $GITHUB_ENV + + - name: Run SonarQube analysis + run: | + extra_flags="${{ env.EXTRA_FLAGS }}" ./gradlew sonarqube --no-daemon -x build -x test \ $extra_flags \ -Dsonar.organization=rsksmart \ -Dsonar.host.url="https://sonarcloud.io" \ -Dsonar.token="${{ secrets.SONAR_TOKEN }}" - - name: Sonarqube for master - if: github.event_name != 'pull_request' - run: | - ./gradlew sonarqube --no-daemon -x build -x test \ - -Dsonar.branch.name="${{ github.ref }}" \ - -Dsonar.organization=rsksmart \ - -Dsonar.host.url="https://sonarcloud.io" \ - -Dsonar.token="${{ secrets.SONAR_TOKEN }}" - mining-tests: needs: build-rskj runs-on: ubuntu-latest @@ -254,6 +265,14 @@ jobs: run: | ./gradlew --no-daemon --stacktrace test + - name: Persist test reports for sonar + uses: actions/upload-artifact@v4 + with: + name: test-report + path: | + rskj-core/build/test-results/ + rskj-core/build/reports/ + integration-tests: runs-on: ubuntu-latest container: diff --git a/build.gradle b/build.gradle index b6d9f595e47..87a3e53a0f3 100644 --- a/build.gradle +++ b/build.gradle @@ -8,12 +8,3 @@ subprojects { version = config.modifier?.trim() ? config.versionNumber + "-" + config.modifier : config.versionNumber } -sonarqube { - properties { - property "sonar.java.binaries", "rskj-core/build/classes/java/main" - property "sonar.scm.provider", "git" - property "sonar.projectBaseDir", project.projectDir - property "sonar.projectKey", "rskj" - property "sonar.organization", "rsksmart" - } -} From 8d8719e5e17ace2e35dbed8f806f723d785f04f0 Mon Sep 17 00:00:00 2001 From: Angel Soto Date: Wed, 3 Jul 2024 11:07:10 +0300 Subject: [PATCH 5/6] RLP Refactor - decode - index check - decodeInt improvement - removing unnecessary methods --- .../src/main/java/org/ethereum/util/RLP.java | 92 +++++++----------- .../test/java/org/ethereum/util/RLPTest.java | 97 +++++-------------- 2 files changed, 58 insertions(+), 131 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/util/RLP.java b/rskj-core/src/main/java/org/ethereum/util/RLP.java index 23b4ea69820..5caf38f071f 100644 --- a/rskj-core/src/main/java/org/ethereum/util/RLP.java +++ b/rskj-core/src/main/java/org/ethereum/util/RLP.java @@ -155,24 +155,33 @@ private static byte decodeOneByteItem(byte[] data, int index) { } public static int decodeInt(byte[] data, int index) { - int value = 0; // NOTE: there are two ways zero can be encoded - 0x00 and OFFSET_SHORT_ITEM - if ((data[index] & 0xFF) < OFFSET_SHORT_ITEM) { + if (index < 0 || index >= data.length) { + throw new RLPException("Index out of bounds"); + } + + int firstByte = data[index] & 0xFF; + + if (firstByte < OFFSET_SHORT_ITEM) { return data[index]; - } else if ((data[index] & 0xFF) >= OFFSET_SHORT_ITEM - && (data[index] & 0xFF) < OFFSET_LONG_ITEM) { + } + + if (firstByte < OFFSET_LONG_ITEM) { + int value = 0; byte length = (byte) (data[index] - OFFSET_SHORT_ITEM); + if(length >= (data.length - index)) { + throw new RLPException("RLP wrong encoding"); + } byte pow = (byte) (length - 1); for (int i = 1; i <= length; ++i) { value += (data[index + i] & 0xFF) << (8 * pow); pow--; } - } else { - throw new RuntimeException("wrong decode attempt"); + return value; } - return value; + throw new RLPException("wrong decode attempt"); } public static BigInteger decodeBigInteger(byte[] data, int index) { @@ -191,46 +200,6 @@ public static BigInteger decodeBigInteger(byte[] data, int index) { return BigIntegers.fromUnsignedByteArray(bytes); } - public static byte[] decodeIP4Bytes(byte[] data, int index) { - - int offset = 1; - - final byte[] result = new byte[4]; - for (int i = 0; i < 4; i++) { - result[i] = decodeOneByteItem(data, index + offset); - if ((data[index + offset] & 0xFF) > OFFSET_SHORT_ITEM) { - offset += 2; - } else { - offset += 1; - } - } - - // return IP address - return result; - } - - public static int getFirstListElement(byte[] payload, int pos) { - - if (pos >= payload.length) { - return -1; - } - - if ((payload[pos] & 0xFF) >= OFFSET_LONG_LIST) { - byte lengthOfLength = (byte) (payload[pos] - OFFSET_LONG_LIST); - return pos + lengthOfLength + 1; - } - if ((payload[pos] & 0xFF) >= OFFSET_SHORT_LIST - && (payload[pos] & 0xFF) < OFFSET_LONG_LIST) { - return pos + 1; - } - if ((payload[pos] & 0xFF) >= OFFSET_LONG_ITEM - && (payload[pos] & 0xFF) < OFFSET_SHORT_LIST) { - byte lengthOfLength = (byte) (payload[pos] - OFFSET_LONG_ITEM); - return pos + lengthOfLength + 1; - } - return -1; - } - public static int getNextElementIndex(byte[] payload, int pos) { if (pos >= payload.length) { @@ -426,25 +395,22 @@ private static Pair decodeElement(byte[] msgData, int posit if (b0 <= 192 + TINY_SIZE) { length = b0 - 192 + 1; offset = 1; - } - else { + } else { int nbytes = b0 - 247; - length = 1 + nbytes + bytesToLength(msgData, position + 1, nbytes); offset = 1 + nbytes; + length = safeAdd(offset, bytesToLength(msgData, position + 1, nbytes)); } - if (Long.compareUnsigned(length, Integer.MAX_VALUE) > 0) { - throw new RLPException("The current implementation doesn't support lengths longer than Integer.MAX_VALUE because that is the largest number of elements an array can have"); - } + int endingIndex = safeAdd(length, position); - if (position + length > msgData.length) { + if (endingIndex > msgData.length) { throw new RLPException("The RLP byte array doesn't have enough space to hold an element with the specified length"); } - byte[] bytes = Arrays.copyOfRange(msgData, position, position + length); + byte[] bytes = Arrays.copyOfRange(msgData, position, endingIndex); RLPList list = new RLPList(bytes, offset); - return Pair.of(list, position + length); + return Pair.of(list, endingIndex); } if (b0 == EMPTY_MARK) { @@ -463,8 +429,7 @@ private static Pair decodeElement(byte[] msgData, int posit if (b0 > (EMPTY_MARK + TINY_SIZE)) { offset = b0 - (EMPTY_MARK + TINY_SIZE) + 1; length = bytesToLength(msgData, position + 1, offset - 1); - } - else { + } else { length = b0 & 0x7f; offset = 1; } @@ -473,7 +438,8 @@ private static Pair decodeElement(byte[] msgData, int posit throw new RLPException("The current implementation doesn't support lengths longer than Integer.MAX_VALUE because that is the largest number of elements an array can have"); } - if (position + offset + length < 0 || position + offset + length > msgData.length) { + int endingIndex = position + offset + length; + if ( endingIndex < 0 || endingIndex > msgData.length) { throw new RLPException("The RLP byte array doesn't have enough space to hold an element with the specified length"); } @@ -484,6 +450,14 @@ private static Pair decodeElement(byte[] msgData, int posit return Pair.of(new RLPItem(decoded), position + offset + length); } + private static int safeAdd(int a, int b) { + try{ + return Math.addExact(a, b); + }catch (ArithmeticException ex){ + throw new RLPException("The current implementation doesn't support lengths longer than Integer.MAX_VALUE because that is the largest number of elements an array can have"); + } + } + private static int bytesToLength(byte[] bytes, int position, int size) { if (position + size > bytes.length) { throw new RLPException("The length of the RLP item length can't possibly fit the data byte array"); diff --git a/rskj-core/src/test/java/org/ethereum/util/RLPTest.java b/rskj-core/src/test/java/org/ethereum/util/RLPTest.java index e35526741e3..58bbaa5218f 100644 --- a/rskj-core/src/test/java/org/ethereum/util/RLPTest.java +++ b/rskj-core/src/test/java/org/ethereum/util/RLPTest.java @@ -19,6 +19,8 @@ package org.ethereum.util; +import co.rsk.util.RLPException; +import org.apache.commons.codec.binary.Base64; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; import org.ethereum.crypto.HashUtil; @@ -34,7 +36,6 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.math.BigInteger; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; @@ -47,23 +48,6 @@ @SuppressWarnings("squid:S2699") class RLPTest { - @Test - void test1() throws UnknownHostException { - - String peersPacket = "F8 4E 11 F8 4B C5 36 81 " + - "CC 0A 29 82 76 5F B8 40 D8 D6 0C 25 80 FA 79 5C " + - "FC 03 13 EF DE BA 86 9D 21 94 E7 9E 7C B2 B5 22 " + - "F7 82 FF A0 39 2C BB AB 8D 1B AC 30 12 08 B1 37 " + - "E0 DE 49 98 33 4F 3B CF 73 FA 11 7E F2 13 F8 74 " + - "17 08 9F EA F8 4C 21 B0"; - - byte[] payload = Hex.decode(peersPacket); - - byte[] ip = decodeIP4Bytes(payload, 5); - - assertEquals(InetAddress.getByAddress(ip).toString(), ("/54.204.10.41")); - } - @Test void test2() throws UnknownHostException { @@ -80,60 +64,6 @@ void test2() throws UnknownHostException { assertEquals(30303, oneInt); } - @Test - void test3() throws UnknownHostException { - - String peersPacket = "F8 9A 11 F8 4B C5 36 81 " + - "CC 0A 29 82 76 5F B8 40 D8 D6 0C 25 80 FA 79 5C " + - "FC 03 13 EF DE BA 86 9D 21 94 E7 9E 7C B2 B5 22 " + - "F7 82 FF A0 39 2C BB AB 8D 1B AC 30 12 08 B1 37 " + - "E0 DE 49 98 33 4F 3B CF 73 FA 11 7E F2 13 F8 74 " + - "17 08 9F EA F8 4C 21 B0 F8 4A C4 36 02 0A 29 " + - "82 76 5F B8 40 D8 D6 0C 25 80 FA 79 5C FC 03 13 " + - "EF DE BA 86 9D 21 94 E7 9E 7C B2 B5 22 F7 82 FF " + - "A0 39 2C BB AB 8D 1B AC 30 12 08 B1 37 E0 DE 49 " + - "98 33 4F 3B CF 73 FA 11 7E F2 13 F8 74 17 08 9F " + - "EA F8 4C 21 B0 "; - - byte[] payload = Hex.decode(peersPacket); - - int nextIndex = 5; - byte[] ip = decodeIP4Bytes(payload, nextIndex); - assertEquals("/54.204.10.41", InetAddress.getByAddress(ip).toString()); - - nextIndex = getNextElementIndex(payload, nextIndex); - int port = decodeInt(payload, nextIndex); - assertEquals(30303, port); - - nextIndex = getNextElementIndex(payload, nextIndex); - BigInteger peerId = decodeBigInteger(payload, nextIndex); - - BigInteger expectedPeerId = - new BigInteger("11356629247358725515654715129711890958861491612873043044752814241820167155109073064559464053586837011802513611263556758124445676272172838679152022396871088"); - assertEquals(expectedPeerId, peerId); - - nextIndex = getNextElementIndex(payload, nextIndex); - nextIndex = getFirstListElement(payload, nextIndex); - ip = decodeIP4Bytes(payload, nextIndex); - assertEquals("/54.2.10.41", InetAddress.getByAddress(ip).toString()); - - nextIndex = getNextElementIndex(payload, nextIndex); - port = decodeInt(payload, nextIndex); - assertEquals(30303, port); - - nextIndex = getNextElementIndex(payload, nextIndex); - peerId = decodeBigInteger(payload, nextIndex); - - expectedPeerId = - new BigInteger("11356629247358725515654715129711890958861491612873043044752814241820167155109073064559464053586837011802513611263556758124445676272172838679152022396871088"); - - assertEquals(expectedPeerId, peerId); - - nextIndex = getNextElementIndex(payload, nextIndex); - nextIndex = getFirstListElement(payload, nextIndex); - assertEquals(-1, nextIndex); - } - @Test /** encode byte */ void test4() { @@ -1118,4 +1048,27 @@ void shortStringRightBoundTest() { String res = new String(decode2(rlpEncoded).get(0).getRLPData()); assertEquals(testString, res); } + + @Test + void testDecode2_outOfMem() { + byte[] payload = Base64.decodeBase64("BQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/QH4f///9wAAAAAAAAAABSQGYA=="); + assertThrows(RLPException.class, () -> RLP.decode2(payload)); + } + + @Test + void testDecode2() { + byte[] payload = Base64.decodeBase64("ADU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTX//8DLAH///8s="); + assertThrows(RLPException.class, () -> RLP.decode2(payload)); + } + @Test + void testDecodeInt() { + byte[] payload = Base64.decodeBase64("kQo="); + assertThrows(RLPException.class, () -> RLP.decodeInt(payload, 0)); + } + + @Test + void testIncorrectDecodeInt(){ + byte[] payload = {(byte) 0x84, (byte) 0x00, (byte) 0x00, (byte) 0x84, (byte) 0x00, (byte) 0x0f, (byte) 0xab}; + assertThrows(RLPException.class, () -> RLP.decodeInt(payload, 3)); + } } From b883b7c930ca4dec88e11310c399e74d1fcb7430 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Wed, 3 Jul 2024 15:36:01 +0300 Subject: [PATCH 6/6] Changed modifier to ARROWHEAD for v6.3.0 --- rskj-core/src/main/resources/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rskj-core/src/main/resources/version.properties b/rskj-core/src/main/resources/version.properties index f27be6b8017..7010280dc27 100644 --- a/rskj-core/src/main/resources/version.properties +++ b/rskj-core/src/main/resources/version.properties @@ -1,2 +1,2 @@ versionNumber='6.3.0' -modifier="RC" +modifier="ARROWHEAD"