diff --git a/.github/workflows/gradle-generate-publish-artifacts.yml b/.github/workflows/gradle-generate-publish-artifacts.yml new file mode 100644 index 00000000..753a2deb --- /dev/null +++ b/.github/workflows/gradle-generate-publish-artifacts.yml @@ -0,0 +1,388 @@ +name: Build - Artifacts + +on: + workflow_dispatch: + inputs: + ref: + description: 'Git ref (branch/tag/commit)' + required: false + default: 'main' + type: string + +permissions: + id-token: write + contents: read + +env: + # build.gradle $buildDir/repo + CUSTOM_REPO_PATH: ${{ github.workspace }}/build/repo + CUSTOM_DOWNLOAD_PATH: ${{ github.workspace }}/download/artifacts + +jobs: + build: + runs-on: ubuntu-latest + outputs: + group: ${{ steps.set-outputs.outputs.group }} + project: ${{ steps.set-outputs.outputs.project }} + version: ${{ steps.set-outputs.outputs.version }} + + steps: + - name: Validate trigger source + run: | + set -euo pipefail + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo "✅: Manual workflow dispatch" + else + echo "❌: This workflow should only be manually dispatched" + exit 1 + fi + + - name: Checkout repository + uses: actions/checkout@v5 + with: + ref: ${{ inputs.ref }} + + - name: Set up JDK 8 + uses: actions/setup-java@v5 + with: + java-version: '8' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v5 + with: + cache-read-only: false + + - name: Extract Project Info + run: | + set -euo pipefail + GROUP=$(grep -E "^group\s+'[^']+'" build.gradle | sed -E "s/.*'([^']+)'.*/\1/" || true) + VERSION=$(grep -E "^version\s+'[^']+'" build.gradle | sed -E "s/.*'([^']+)'.*/\1/" || true) + PROJECT=$(grep -E "^rootProject\.name\s*=\s*'[^']+'" build.gradle | sed -E "s/.*'([^']+)'.*/\1/" || true) + if [ -z "$PROJECT" ] && [ -f "settings.gradle" ]; then + PROJECT=$(grep -E "^rootProject\.name\s*=\s*'[^']+'" settings.gradle | sed -E "s/.*'([^']+)'.*/\1/" || true) + fi + + if [ -z "$GROUP" ] || [ -z "$PROJECT" ] || [ -z "$VERSION" ]; then + echo "❌: Missing group/project/version in build.gradle" + exit 1 + fi + + echo "group=$GROUP" >> $GITHUB_ENV + echo "project=$PROJECT" >> $GITHUB_ENV + echo "version=$VERSION" >> $GITHUB_ENV + + GROUP_PATH=$(echo "$GROUP" | tr '.' '/') + echo "MAVEN_GROUP_PATH=$GROUP_PATH" >> $GITHUB_ENV + + ARTIFACT_PATH="${{ env.CUSTOM_REPO_PATH }}/$GROUP_PATH/$PROJECT/$VERSION" + echo "ARTIFACT_PATH=$ARTIFACT_PATH" >> $GITHUB_ENV + + echo "✅ Project: $PROJECT, Version: $VERSION, Group: $GROUP" + + - name: Publish to CI Maven repository + run: ./gradlew clean -xtest -xcheck --refresh-dependencies publishAllPublicationsToCiLocalRepository + + - name: Verify artifacts and checksums + run: | + set -euo pipefail + if [ ! -d "${{ env.ARTIFACT_PATH }}" ]; then + echo "❌: Artifacts not found at ${{ env.ARTIFACT_PATH }}" + exit 1 + fi + + REQUIRED_FILES=( + "${{ env.project }}-${{ env.version }}.jar" + "${{ env.project }}-${{ env.version }}.jar.md5" + "${{ env.project }}-${{ env.version }}.jar.sha1" + "${{ env.project }}-${{ env.version }}.jar.sha256" + "${{ env.project }}-${{ env.version }}.jar.sha512" + "${{ env.project }}-${{ env.version }}.module" + "${{ env.project }}-${{ env.version }}.module.md5" + "${{ env.project }}-${{ env.version }}.module.sha1" + "${{ env.project }}-${{ env.version }}.module.sha256" + "${{ env.project }}-${{ env.version }}.module.sha512" + "${{ env.project }}-${{ env.version }}.pom" + "${{ env.project }}-${{ env.version }}.pom.md5" + "${{ env.project }}-${{ env.version }}.pom.sha1" + "${{ env.project }}-${{ env.version }}.pom.sha256" + "${{ env.project }}-${{ env.version }}.pom.sha512" + "${{ env.project }}-${{ env.version }}-javadoc.jar" + "${{ env.project }}-${{ env.version }}-javadoc.jar.md5" + "${{ env.project }}-${{ env.version }}-javadoc.jar.sha1" + "${{ env.project }}-${{ env.version }}-javadoc.jar.sha256" + "${{ env.project }}-${{ env.version }}-javadoc.jar.sha512" + "${{ env.project }}-${{ env.version }}-sources.jar" + "${{ env.project }}-${{ env.version }}-sources.jar.md5" + "${{ env.project }}-${{ env.version }}-sources.jar.sha1" + "${{ env.project }}-${{ env.version }}-sources.jar.sha256" + "${{ env.project }}-${{ env.version }}-sources.jar.sha512" + ) + + MISSING_FILES=() + for file in "${REQUIRED_FILES[@]}"; do + if [ ! -f "${{ env.ARTIFACT_PATH }}/$file" ]; then + MISSING_FILES+=("$file") + fi + done + + if [ ${#MISSING_FILES[@]} -gt 0 ]; then + echo "❌ Missing required files:" + for f in "${MISSING_FILES[@]}"; do echo " - $f"; done + exit 1 + fi + + echo "✅ All required files verified (${#REQUIRED_FILES[@]} files)" + + - name: Remove Maven metadata files + run: | + set -euo pipefail + DELETED_COUNT=$(find ${{ env.CUSTOM_REPO_PATH }} -name "maven-metadata*" -type f -delete -print | wc -l) + echo "✅ Removed $DELETED_COUNT Maven metadata files" + + - name: Upload publish files + uses: actions/upload-artifact@v4 + with: + name: ${{ env.project }}-${{ env.version }}-artifacts + path: ${{ env.CUSTOM_REPO_PATH }}/ + if-no-files-found: error + retention-days: 30 + + - name: Set Outputs + id: set-outputs + run: | + set -euo pipefail + echo "group=${{ env.MAVEN_GROUP_PATH }}" >> $GITHUB_OUTPUT + echo "project=${{ env.project }}" >> $GITHUB_OUTPUT + echo "version=${{ env.version }}" >> $GITHUB_OUTPUT + + - name: Generate summary + run: | + set -euo pipefail + COMMIT_ID=$(git rev-parse HEAD) + COMMIT_MSG=$(git log -1 --pretty=%B) + echo "## Build Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Project:** ${{ env.project }}" >> $GITHUB_STEP_SUMMARY + echo "- **Version:** ${{ env.version }}" >> $GITHUB_STEP_SUMMARY + echo "- **Group:** ${{ env.MAVEN_GROUP_PATH }}" >> $GITHUB_STEP_SUMMARY + echo "- **Git Ref:** ${{ inputs.ref }}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit ID:** $COMMIT_ID" >> $GITHUB_STEP_SUMMARY + echo "- **Commit Message:** $COMMIT_MSG" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + echo "### Local Repository Files" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + ls -lh "${{ env.ARTIFACT_PATH }}" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "- **Artifact Check:** ✓ All required files present" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + sign-and-upload: + runs-on: self-hosted + needs: build + steps: + - name: Validate secrets + run: | + set -euo pipefail + if [ -z "${{ secrets.GPG_FINGERPRINT }}" ]; then + echo "❌: GPG_FINGERPRINT secret not configured" + exit 1 + fi + if [ -z "${{ secrets.S3_BUCKET_PRE_STAGE }}" ]; then + echo "❌: S3_BUCKET_PRE_STAGE secret not configured" + exit 1 + fi + if [ -z "${{ secrets.AWS_ROLE_ARN_PRE_STAGE_UPLOAD }}" ]; then + echo "❌: AWS_ROLE_ARN_PRE_STAGE_UPLOAD secret not configured" + exit 1 + fi + if [ -z "${{ secrets.AWS_REGION }}" ]; then + echo "❌: AWS_REGION secret not configured" + exit 1 + fi + echo "✅ All required secrets configured" + + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: ${{ needs.build.outputs.project }}-${{ needs.build.outputs.version }}-artifacts + path: ${{ env.CUSTOM_DOWNLOAD_PATH }} + + - name: Verify downloaded artifacts + run: | + set -euo pipefail + FULL_PATH="${{ env.CUSTOM_DOWNLOAD_PATH }}/${{ needs.build.outputs.group }}/${{ needs.build.outputs.project }}/${{ needs.build.outputs.version }}" + if [ ! -d "$FULL_PATH" ]; then + echo "❌: Downloaded artifacts not found at expected path" + exit 1 + fi + FILE_COUNT=$(find "$FULL_PATH" -type f | wc -l) + echo "✅: Downloaded $FILE_COUNT files to $FULL_PATH" + echo "FULL_PATH=$FULL_PATH" >> $GITHUB_ENV + + - name: Wait for hardware key (YubiKey/HSM) + run: | + set -euo pipefail + echo "⏳ Waiting for GPG hardware key..." + MAX_RETRIES=60 + RETRY_INTERVAL=2 + for i in $(seq 1 $MAX_RETRIES); do + if gpg --card-status > /dev/null 2>&1; then + echo "✅: GPG hardware key detected and ready (after $((i*RETRY_INTERVAL))s)" + break + fi + echo "[$i/$MAX_RETRIES] Hardware key not detected, retrying in ${RETRY_INTERVAL}s..." + sleep $RETRY_INTERVAL + done + + if ! gpg --card-status > /dev/null 2>&1; then + echo "❌ Timeout waiting for hardware key after $((MAX_RETRIES*RETRY_INTERVAL)) seconds" + exit 1 + fi + + - name: Sign artifacts + run: | + set -euo pipefail + cd "${{ env.FULL_PATH }}" + + PREFIX="${{ needs.build.outputs.project }}-${{ needs.build.outputs.version }}" + FILES=( + "${PREFIX}.jar" + "${PREFIX}.module" + "${PREFIX}-sources.jar" + "${PREFIX}-javadoc.jar" + "${PREFIX}.pom" + ) + + compute_md5() { + if command -v md5sum >/dev/null 2>&1; then + md5sum "$1" | awk '{print $1}' + else + md5 "$1" | awk '{print $4}' + fi + } + + SIGNED_COUNT=0 + for file in "${FILES[@]}"; do + if [ ! -f "$file" ]; then + echo "❌ Missing file to sign: $file" + exit 1 + fi + echo "Signing $file..." + gpg --quiet --batch --yes --local-user ${{ secrets.GPG_FINGERPRINT }} --armor --detach-sign "$file" + compute_md5 ${file}.asc > ${file}.asc.md5 + shasum -a 1 ${file}.asc | awk '{print $1}' > ${file}.asc.sha1 + shasum -a 256 ${file}.asc | awk '{print $1}' > ${file}.asc.sha256 + shasum -a 512 ${file}.asc | awk '{print $1}' > ${file}.asc.sha512 + SIGNED_COUNT=$((SIGNED_COUNT + 1)) + done + echo "✓ Successfully signed $SIGNED_COUNT files" + + - name: Verify signatures + run: | + set -euo pipefail + cd "${{ env.FULL_PATH }}" + VERIFIED_COUNT=0 + FAILED_SIGS=() + for sig in *.asc; do + if [ -f "$sig" ]; then + echo "Verifying $sig..." + if ! gpg --verify "$sig" "${sig%.asc}" > /dev/null 2>&1; then + FAILED_SIGS+=("$sig") + else + VERIFIED_COUNT=$((VERIFIED_COUNT + 1)) + fi + fi + done + + if [ ${#FAILED_SIGS[@]} -gt 0 ]; then + echo "❌ Signature verification failed for: ${FAILED_SIGS[*]}" + exit 1 + fi + echo "✅ All $VERIFIED_COUNT signatures verified" + + - name: Zip signed artifacts + run: | + set -euo pipefail + cd ${{ env.CUSTOM_DOWNLOAD_PATH }} + BUNDLE_NAME="${{ needs.build.outputs.project }}-${{ needs.build.outputs.version }}-bundle.zip" + zip -r "$BUNDLE_NAME" ${{ needs.build.outputs.group }} >/dev/null + if [ ! -f "$BUNDLE_NAME" ]; then + echo "❌ Error: Bundle file not created" + exit 1 + fi + + BUNDLE_SIZE=$(du -h "$BUNDLE_NAME" | cut -f1) + echo "✓ Bundle created: $BUNDLE_NAME (${BUNDLE_SIZE})" + echo "BUNDLE_NAME=$BUNDLE_NAME" >> $GITHUB_ENV + echo "BUNDLE_SIZE=$BUNDLE_SIZE" >> $GITHUB_ENV + + - name: Verify bundle contents + run: | + set -euo pipefail + cd ${{ env.CUSTOM_DOWNLOAD_PATH }} + echo "Bundle contents:" + unzip -l "${{ env.BUNDLE_NAME }}" | head -50 + + ASC_COUNT=$(unzip -l "${{ env.BUNDLE_NAME }}" | grep -c "\.asc$" || true) + if [ "$ASC_COUNT" -lt 5 ]; then + echo "❌ Bundle missing signature files (found $ASC_COUNT)" + exit 1 + fi + echo "✅ Bundle verified: contains $ASC_COUNT signature files" + + - name: Upload signed bundle + uses: actions/upload-artifact@v4 + with: + name: ${{ needs.build.outputs.project }}-${{ needs.build.outputs.version }}-bundle + path: ${{ env.CUSTOM_DOWNLOAD_PATH }}/${{ env.BUNDLE_NAME }} + retention-days: 30 + + - name: Configure AWS Credentials (OIDC) + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN_PRE_STAGE_UPLOAD }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Upload artifacts to S3 + run: | + set -euo pipefail + cd ${{ env.CUSTOM_DOWNLOAD_PATH }} + + DEST="s3://${{ secrets.S3_BUCKET_PRE_STAGE }}" + if [ -n "${{ secrets.S3_PREFIX }}" ]; then + DEST="$DEST/${{ secrets.S3_PREFIX }}" + fi + DEST="$DEST/${{ needs.build.outputs.version }}" + echo "Uploading ${{ env.BUNDLE_NAME }}" + aws s3 cp "${{ env.BUNDLE_NAME }}" "$DEST/" --only-show-errors + echo "✅ Successfully uploaded to S3" + + - name: Generate upload summary + if: success() + run: | + set -euo pipefail + echo "## ✅ Artifact Signing & Upload Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Project Information" >> $GITHUB_STEP_SUMMARY + echo "- **Project:** ${{ needs.build.outputs.project }}" >> $GITHUB_STEP_SUMMARY + echo "- **Version:** ${{ needs.build.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "- **Group Path:** ${{ needs.build.outputs.group }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Bundle Details" >> $GITHUB_STEP_SUMMARY + echo "- **Bundle Name:** \`${{ env.BUNDLE_NAME }}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Bundle Size:** ${{ env.BUNDLE_SIZE }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Signed Files" >> $GITHUB_STEP_SUMMARY + find "${{ env.FULL_PATH }}" -type f -name "*.asc" -exec basename {} \; | sed 's/^/- /' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Upload Time:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + - name: Cleanup downloaded artifacts + if: always() + run: | + set -euo pipefail + rm -rf "${{ env.CUSTOM_DOWNLOAD_PATH }}" || true + echo "✅ Cleaned up downloaded artifacts" diff --git a/README.md b/README.md index 19572853..d6f93bde 100644 --- a/README.md +++ b/README.md @@ -6,33 +6,26 @@ libp2p is a p2p network SDK implemented in java language. The functional modules * support compressed message transmission among nodes # Note -Starting from version 2.2.6, `libp2p` has removed the logback component and adopted the logger facade. If logging is required, you need to introduce a logging framework manually. +Starting from version 2.2.7, `libp2p` has removed the logback component and adopted the logger facade. If logging is required, you need to introduce a logging framework manually. Here’s how to include logback in your project using Gradle: -1. Add the following dependencies to your build.gradle file +1. Uncomment the ch.qos.logback:logback-classic dependency in build.gradle file ``` dependencies { - implementation group: 'ch.qos.logback', name: 'logback-classic', version: 'x.x.xx' + implementation("ch.qos.logback:logback-classic:1.2.13") { + ... + } } ``` -2. Create or edit logback.xml in src/main/resources to configure logging. Here is an example of logback.xml: - ```xml - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - +2. Refresh the dependency verification metadata to prevent verification failures +```bash +$ ./gradlew clean --refresh-dependencies --write-verification-metadata sha256 ``` +3. Rename or copy logback.xml.example in src/main/resources to logback.xml. # Build -Building libp2p requires `git` and `Oracle JDK 1.8` to be installed, other JDK versions are not supported yet. Make sure you operate on `Linux` and `MacOS` operating systems. +Building libp2p requires `git`, `JDK 17` or `Oracle JDK 1.8` to be installed, other JDK versions are not supported yet. Make sure you operate on `Linux` and `MacOS` operating systems. Clone the repo and switch to the `main` branch @@ -50,12 +43,12 @@ $ ./gradlew clean build -x test libp2p can run independently or be imported into other projects. ## Run independently -Running libp2p requires Oracle JDK 1.8 to be installed, other JDK versions are not supported yet. Make sure you operate on Linux and MacOS operating systems. +Running libp2p requires JDK 17 or Oracle JDK 1.8 to be installed, other JDK versions are not supported yet. Make sure you operate on Linux and MacOS operating systems. then run the following command to start the node: ```bash $ nohup java -jar libp2p.jar [options] >> start.log 2>&1 & ``` -See the manual for details on [options](https://github.com/tronprotocol/libp2p/tree/develop/src/main/java/org/tron/p2p/example/README.md) +See the manual for details on [options](src/main/java/org/tron/p2p/example/README.md) ## How to include the dependency ### Gradle Setting @@ -69,7 +62,7 @@ repositories { Then add the required packages as dependencies. Please add dependencies locally. ```bash dependencies { - implementation group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.6' + implementation group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.7' } ``` Or if you are using the jar files as your dependencies: @@ -92,16 +85,16 @@ dependencies { io.github.tronprotocol libp2p - 2.2.6 + 2.2.7 ``` ## Example -For some examples please check our [example package](https://github.com/tronprotocol/libp2p/tree/develop/src/main/java/org/tron/p2p/example). +For some examples please check our [example package](src/main/java/org/tron/p2p/example). # Integrity Check * After February 21, 2023, releases are signed the gpg key: ``` pub: 1254 F859 D2B1 BD9F 66E7 107D F859 BCB4 4A28 290B uid: build@tron.network - ``` \ No newline at end of file + ``` diff --git a/build.gradle b/build.gradle index f33867e3..914ca9b1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ group 'io.github.tronprotocol' -version '2.2.6' +version '2.2.7' buildscript { repositories { @@ -16,9 +16,11 @@ apply plugin: 'java' apply plugin: 'com.google.protobuf' apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'application' +apply plugin: 'maven-publish' -def protobufVersion = "3.25.5" -def grpcVersion = "1.60.0" +def protobufVersion = "3.25.8" +def grpcVersion = "1.75.0" +def protocGenVersion = '1.60.0' // https://github.com/grpc/grpc-java/pull/11371 , 1.64.x is not supported CentOS 7. mainClassName = 'org.tron.p2p.example.StartApp' @@ -64,12 +66,12 @@ dependencies { implementation group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: '1.79' implementation group: 'org.bouncycastle', name: 'bcpkix-jdk18on', version: '1.79' - implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.18.0' implementation group: 'commons-cli', name: 'commons-cli', version: '1.5.0' - compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.12' - testImplementation group: 'org.projectlombok', name: 'lombok', version: '1.18.12' - annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.12' + compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.34' + testImplementation group: 'org.projectlombok', name: 'lombok', version: '1.18.34' + annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.34' implementation group: 'dnsjava', name: 'dnsjava', version: '3.6.2' implementation('software.amazon.awssdk:route53:2.18.41', { @@ -84,10 +86,21 @@ dependencies { exclude group: 'io.netty', module: 'netty-transport-classes-epoll' exclude group: 'io.netty', module: 'netty-transport-native-unix-common' }) - implementation group: 'com.aliyun', name: 'alidns20150109', version: '3.0.1',{ + implementation group: 'com.aliyun', name: 'alidns20150109', version: '3.0.1', { exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on' exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on' + exclude group: 'pull-parser', module: 'pull-parser' + exclude group: 'xpp3', module: 'xpp3' } + + //If logging is required, uncomment this dependency + //implementation("ch.qos.logback:logback-classic:1.2.13") { + // exclude group: 'jaxen', module: 'jaxen' + // exclude group: 'javax.xml.stream', module: 'stax-api' + // exclude group: 'net.java.dev.msv', module: 'xsdlib' + // exclude group: 'pull-parser', module: 'pull-parser' + // exclude group: 'xpp3', module: 'xpp3' + //} } tasks.matching { it instanceof Test }.all { @@ -103,7 +116,7 @@ protobuf { plugins { grpc { - artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion" + artifact = "io.grpc:protoc-gen-grpc-java:$protocGenVersion" } } generateProtoTasks { @@ -176,3 +189,42 @@ artifacts { } processResources.dependsOn(generateProto) // explicit_dependency + +publishing { + repositories { + // CI + maven { + name = 'ciLocal' + url = "$buildDir/repo" + } + } + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourcesJar + artifact javadocJar + pom { + name = 'libp2p' + description = 'libp2p is a p2p network SDK implemented in java language.' + url = 'https://github.com/tronprotocol/libp2p' + licenses { + license { + name = 'The Apache Software License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + name = 'chengtx01' + email = 'ctxhorse@gmail.com' + } + } + scm { + url = 'https://github.com/tronprotocol/libp2p' + connection = 'scm:git:git://github.com/tronprotocol/libp2p.git' + developerConnection = 'scm:git:ssh://git@github.com:tronprotocol/libp2p.git' + } + } + } + } +} \ No newline at end of file diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index f56282f1..382b1f41 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -122,12 +122,12 @@ - - - + + + - - + + @@ -143,9 +143,9 @@ - - - + + + @@ -163,20 +163,12 @@ - - - + + + - - - - - - - - - - + + @@ -184,14 +176,9 @@ - - - - - - - - + + + @@ -210,12 +197,12 @@ - - - + + + - - + + @@ -226,21 +213,35 @@ + + + + + + + + + + + + + + - - - - - + + + + + @@ -249,14 +250,6 @@ - - - - - - - - @@ -265,9 +258,17 @@ - - - + + + + + + + + + + + @@ -278,49 +279,48 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -409,14 +409,6 @@ - - - - - - - - @@ -441,48 +433,47 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - @@ -505,105 +496,108 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + + + + - - + + @@ -699,9 +693,9 @@ - - - + + + @@ -725,17 +719,12 @@ - - - + + + - - - - - - - + + @@ -763,6 +752,11 @@ + + + + + @@ -844,14 +838,6 @@ - - - - - - - - @@ -860,40 +846,33 @@ - - - + + + - - + + - - - - - - - + + - - - + + + - - - - + + - - - + + + - - - + + + @@ -916,9 +895,6 @@ - - - @@ -967,12 +943,20 @@ - - - + + + + + + + + + + + - - + + @@ -1020,6 +1004,14 @@ + + + + + + + + diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 00000000..512d5a11 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,2 @@ +install: + - ./gradlew clean -xtest -xcheck publishToMavenLocal diff --git a/src/main/java/org/tron/p2p/base/Constant.java b/src/main/java/org/tron/p2p/base/Constant.java index cea5c1f0..b87cfa28 100644 --- a/src/main/java/org/tron/p2p/base/Constant.java +++ b/src/main/java/org/tron/p2p/base/Constant.java @@ -9,7 +9,8 @@ public class Constant { public static final List ipV4Urls = Arrays.asList( "http://checkip.amazonaws.com", "https://ifconfig.me/ip", "https://4.ipw.cn/"); public static final List ipV6Urls = Arrays.asList( - "https://v6.ident.me", "http://6.ipw.cn/"); + "https://v6.ident.me", "http://6.ipw.cn/", "https://api6.ipify.org", + "https://ipv6.icanhazip.com"); public static final String ipV4Hex = "00000000"; //32 bit public static final String ipV6Hex = "00000000000000000000000000000000"; //128 bit } diff --git a/src/main/java/org/tron/p2p/base/Parameter.java b/src/main/java/org/tron/p2p/base/Parameter.java index 7a808841..50055949 100644 --- a/src/main/java/org/tron/p2p/base/Parameter.java +++ b/src/main/java/org/tron/p2p/base/Parameter.java @@ -24,6 +24,8 @@ public class Parameter { public static final int UDP_NETTY_WORK_THREAD_NUM = 1; + public static final int CONN_MAX_QUEUE_SIZE = 10; + public static final int NODE_CONNECTION_TIMEOUT = 2000; public static final int KEEP_ALIVE_TIMEOUT = 20_000; diff --git a/src/main/java/org/tron/p2p/connection/business/detect/NodeDetectService.java b/src/main/java/org/tron/p2p/connection/business/detect/NodeDetectService.java index 4752aca9..3ef53b59 100644 --- a/src/main/java/org/tron/p2p/connection/business/detect/NodeDetectService.java +++ b/src/main/java/org/tron/p2p/connection/business/detect/NodeDetectService.java @@ -36,7 +36,7 @@ public class NodeDetectService implements MessageProcess { .newBuilder().maximumSize(5000).expireAfterWrite(1, TimeUnit.HOURS).build(); private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor( - new BasicThreadFactory.Builder().namingPattern("nodeDetectService").build()); + BasicThreadFactory.builder().namingPattern("nodeDetectService").build()); private final long NODE_DETECT_THRESHOLD = 5 * 60 * 1000; diff --git a/src/main/java/org/tron/p2p/connection/business/keepalive/KeepAliveService.java b/src/main/java/org/tron/p2p/connection/business/keepalive/KeepAliveService.java index 48550965..19eea3b0 100644 --- a/src/main/java/org/tron/p2p/connection/business/keepalive/KeepAliveService.java +++ b/src/main/java/org/tron/p2p/connection/business/keepalive/KeepAliveService.java @@ -21,7 +21,7 @@ public class KeepAliveService implements MessageProcess { private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor( - new BasicThreadFactory.Builder().namingPattern("keepAlive").build()); + BasicThreadFactory.builder().namingPattern("keepAlive").build()); public void init() { executor.scheduleWithFixedDelay(() -> { diff --git a/src/main/java/org/tron/p2p/connection/business/pool/ConnPoolService.java b/src/main/java/org/tron/p2p/connection/business/pool/ConnPoolService.java index cbaa68a9..6996f863 100644 --- a/src/main/java/org/tron/p2p/connection/business/pool/ConnPoolService.java +++ b/src/main/java/org/tron/p2p/connection/business/pool/ConnPoolService.java @@ -14,6 +14,7 @@ import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -42,7 +43,7 @@ public class ConnPoolService extends P2pEventHandler { private final List activePeers = Collections.synchronizedList(new ArrayList<>()); - private Cache peerClientCache = CacheBuilder.newBuilder() + private final Cache peerClientCache = CacheBuilder.newBuilder() .maximumSize(1000).expireAfterWrite(120, TimeUnit.SECONDS).recordStats().build(); @Getter private final AtomicInteger passivePeersCount = new AtomicInteger(0); @@ -50,14 +51,15 @@ public class ConnPoolService extends P2pEventHandler { private final AtomicInteger activePeersCount = new AtomicInteger(0); @Getter private final AtomicInteger connectingPeersCount = new AtomicInteger(0); - private final ScheduledExecutorService poolLoopExecutor = Executors.newSingleThreadScheduledExecutor( - new BasicThreadFactory.Builder().namingPattern("connPool").build()); - private final ScheduledExecutorService disconnectExecutor = Executors.newSingleThreadScheduledExecutor( - new BasicThreadFactory.Builder().namingPattern("randomDisconnect").build()); + private final ScheduledThreadPoolExecutor poolLoopExecutor = new ScheduledThreadPoolExecutor(1, + BasicThreadFactory.builder().namingPattern("connPool").build()); + private final ScheduledExecutorService disconnectExecutor = + Executors.newSingleThreadScheduledExecutor( + BasicThreadFactory.builder().namingPattern("randomDisconnect").build()); public P2pConfig p2pConfig = Parameter.p2pConfig; private PeerClient peerClient; - private List configActiveNodes = new ArrayList<>(); + private final List configActiveNodes = new ArrayList<>(); private int minCandidateSize = 50; public ConnPoolService() { @@ -66,6 +68,7 @@ public ConnPoolService() { Parameter.addP2pEventHandle(this); configActiveNodes.addAll(p2pConfig.getActiveNodes()); } catch (P2pException e) { + //no exception will throw } } @@ -274,6 +277,10 @@ public void triggerConnect(InetSocketAddress address) { return; } connectingPeersCount.decrementAndGet(); + if (poolLoopExecutor.getQueue().size() >= Parameter.CONN_MAX_QUEUE_SIZE) { + log.warn("ConnPool task' size is greater than or equal to {}", Parameter.CONN_MAX_QUEUE_SIZE); + return; + } try { if (!ChannelManager.isShutdown) { poolLoopExecutor.submit(() -> { @@ -317,6 +324,7 @@ public synchronized void onDisconnect(Channel peer) { @Override public void onMessage(Channel channel, byte[] data) { + //do nothing } public void close() { @@ -329,6 +337,7 @@ public void close() { } }); poolLoopExecutor.shutdownNow(); + disconnectExecutor.shutdownNow(); } catch (Exception e) { log.warn("Problems shutting down executor", e); } diff --git a/src/main/java/org/tron/p2p/connection/message/handshake/HelloMessage.java b/src/main/java/org/tron/p2p/connection/message/handshake/HelloMessage.java index fc421165..a3727d2d 100644 --- a/src/main/java/org/tron/p2p/connection/message/handshake/HelloMessage.java +++ b/src/main/java/org/tron/p2p/connection/message/handshake/HelloMessage.java @@ -7,6 +7,7 @@ import org.tron.p2p.discover.Node; import org.tron.p2p.protos.Connect; import org.tron.p2p.protos.Discover; +import org.tron.p2p.utils.ByteArray; import org.tron.p2p.utils.NetUtil; public class HelloMessage extends Message { @@ -52,7 +53,7 @@ public Node getFrom() { @Override public String toString() { - return "[HelloMessage: " + helloMessage; + return "[HelloMessage: " + format(); } @Override @@ -60,4 +61,17 @@ public boolean valid() { return NetUtil.validNode(getFrom()); } + public String format() { + String[] lines = helloMessage.toString().split("\n"); + StringBuilder sb = new StringBuilder(); + for (String line : lines) { + if (line.contains("nodeId")) { + String nodeId = ByteArray.toHexString(helloMessage.getFrom().getNodeId().toByteArray()); + line = " nodeId: \"" + nodeId + "\""; + } + sb.append(line).append("\n"); + } + return sb.toString(); + } + } diff --git a/src/main/java/org/tron/p2p/connection/socket/PeerClient.java b/src/main/java/org/tron/p2p/connection/socket/PeerClient.java index 0e3c8fb8..2f0bd943 100644 --- a/src/main/java/org/tron/p2p/connection/socket/PeerClient.java +++ b/src/main/java/org/tron/p2p/connection/socket/PeerClient.java @@ -23,7 +23,7 @@ public class PeerClient { public void init() { workerGroup = new NioEventLoopGroup(0, - new BasicThreadFactory.Builder().namingPattern("peerClient-%d").build()); + BasicThreadFactory.builder().namingPattern("peerClient-%d").build()); } public void close() { diff --git a/src/main/java/org/tron/p2p/connection/socket/PeerServer.java b/src/main/java/org/tron/p2p/connection/socket/PeerServer.java index d3b1b9db..8a1b7d9a 100644 --- a/src/main/java/org/tron/p2p/connection/socket/PeerServer.java +++ b/src/main/java/org/tron/p2p/connection/socket/PeerServer.java @@ -39,10 +39,10 @@ public void close() { public void start(int port) { EventLoopGroup bossGroup = new NioEventLoopGroup(1, - new BasicThreadFactory.Builder().namingPattern("peerBoss").build()); + BasicThreadFactory.builder().namingPattern("peerBoss").build()); //if threads = 0, it is number of core * 2 EventLoopGroup workerGroup = new NioEventLoopGroup(Parameter.TCP_NETTY_WORK_THREAD_NUM, - new BasicThreadFactory.Builder().namingPattern("peerWorker-%d").build()); + BasicThreadFactory.builder().namingPattern("peerWorker-%d").build()); P2pChannelInitializer p2pChannelInitializer = new P2pChannelInitializer("", false, true); try { ServerBootstrap b = new ServerBootstrap(); diff --git a/src/main/java/org/tron/p2p/discover/Node.java b/src/main/java/org/tron/p2p/discover/Node.java index 028336f5..51156d2f 100644 --- a/src/main/java/org/tron/p2p/discover/Node.java +++ b/src/main/java/org/tron/p2p/discover/Node.java @@ -16,6 +16,8 @@ public class Node implements Serializable, Cloneable { private static final long serialVersionUID = -4267600517925770636L; + @Setter + @Getter private byte[] id; @Getter @@ -24,6 +26,8 @@ public class Node implements Serializable, Cloneable { @Getter protected String hostV6; + @Setter + @Getter protected int port; @Setter @@ -32,6 +36,7 @@ public class Node implements Serializable, Cloneable { @Setter private int p2pVersion; + @Getter private long updateTime; public Node(InetSocketAddress address) { @@ -111,26 +116,10 @@ public String getHexIdShort() { return getIdShort(getHexId()); } - public byte[] getId() { - return id; - } - - public void setId(byte[] id) { - this.id = id; - } - public String getHostKey() { return getPreferInetSocketAddress().getAddress().getHostAddress(); } - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - public String getIdString() { if (id == null) { return null; @@ -138,10 +127,6 @@ public String getIdString() { return new String(id); } - public long getUpdateTime() { - return updateTime; - } - public void touch() { updateTime = System.currentTimeMillis(); } @@ -179,8 +164,8 @@ public boolean equals(Object o) { return false; } - private String getIdShort(String Id) { - return Id == null ? "" : Id.substring(0, 8); + private String getIdShort(String hexId) { + return hexId == null ? "" : hexId.substring(0, 8); } public InetSocketAddress getInetSocketAddressV4() { diff --git a/src/main/java/org/tron/p2p/discover/protocol/kad/DiscoverTask.java b/src/main/java/org/tron/p2p/discover/protocol/kad/DiscoverTask.java index d633b78a..4ab23ec3 100644 --- a/src/main/java/org/tron/p2p/discover/protocol/kad/DiscoverTask.java +++ b/src/main/java/org/tron/p2p/discover/protocol/kad/DiscoverTask.java @@ -15,7 +15,7 @@ public class DiscoverTask { private ScheduledExecutorService discoverer = Executors.newSingleThreadScheduledExecutor( - new BasicThreadFactory.Builder().namingPattern("discoverTask").build()); + BasicThreadFactory.builder().namingPattern("discoverTask").build()); private KadService kadService; diff --git a/src/main/java/org/tron/p2p/discover/protocol/kad/KadService.java b/src/main/java/org/tron/p2p/discover/protocol/kad/KadService.java index 7f2051e1..1a137c4c 100644 --- a/src/main/java/org/tron/p2p/discover/protocol/kad/KadService.java +++ b/src/main/java/org/tron/p2p/discover/protocol/kad/KadService.java @@ -57,7 +57,7 @@ public void init() { bootNodes.add(new Node(address)); } this.pongTimer = Executors.newSingleThreadScheduledExecutor( - new BasicThreadFactory.Builder().namingPattern("pongTimer").build()); + BasicThreadFactory.builder().namingPattern("pongTimer").build()); this.homeNode = new Node(Parameter.p2pConfig.getNodeID(), Parameter.p2pConfig.getIp(), Parameter.p2pConfig.getIpv6(), Parameter.p2pConfig.getPort()); this.table = new NodeTable(homeNode); diff --git a/src/main/java/org/tron/p2p/dns/sync/Client.java b/src/main/java/org/tron/p2p/dns/sync/Client.java index 064e0da5..95781ec2 100644 --- a/src/main/java/org/tron/p2p/dns/sync/Client.java +++ b/src/main/java/org/tron/p2p/dns/sync/Client.java @@ -10,7 +10,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -42,7 +41,7 @@ public class Client { private final Map clientTrees = new HashMap<>(); private final ScheduledExecutorService syncer = Executors.newSingleThreadScheduledExecutor( - new BasicThreadFactory.Builder().namingPattern("dnsSyncer").build()); + BasicThreadFactory.builder().namingPattern("dnsSyncer").build()); public Client() { this.cache = CacheBuilder.newBuilder() diff --git a/src/main/java/org/tron/p2p/dns/update/PublishService.java b/src/main/java/org/tron/p2p/dns/update/PublishService.java index 7a94543e..5fd7cb4f 100644 --- a/src/main/java/org/tron/p2p/dns/update/PublishService.java +++ b/src/main/java/org/tron/p2p/dns/update/PublishService.java @@ -25,7 +25,7 @@ public class PublishService { private static final long publishDelay = 1 * 60 * 60; private ScheduledExecutorService publisher = Executors.newSingleThreadScheduledExecutor( - new BasicThreadFactory.Builder().namingPattern("publishService").build()); + BasicThreadFactory.builder().namingPattern("publishService").build()); private Publish publish; public void init() { diff --git a/src/main/java/org/tron/p2p/example/README.md b/src/main/java/org/tron/p2p/example/README.md index fded4071..49687352 100644 --- a/src/main/java/org/tron/p2p/example/README.md +++ b/src/main/java/org/tron/p2p/example/README.md @@ -226,10 +226,10 @@ New p2p config instance P2pConfig config = new P2pConfig(); ``` -Set p2p version +Set p2p networkId (also called p2p version) ```bash -config.setVersion(11111); +config.setNetworkId(11111); ``` Set TCP and UDP listen port @@ -416,6 +416,6 @@ p2pService.start(config); ``` For details please -check [ImportUsing](https://github.com/tronprotocol/libp2p/blob/develop/src/main/java/org/tron/p2p/example/ImportUsing.java), [DnsExample1](https://github.com/tronprotocol/libp2p/blob/develop/src/main/java/org/tron/p2p/example/DnsExample1.java), [DnsExample2](https://github.com/tronprotocol/libp2p/blob/develop/src/main/java/org/tron/p2p/example/DnsExample2.java) +check [ImportUsing](ImportUsing.java), [DnsExample1](DnsExample1.java), [DnsExample2](DnsExample2.java) diff --git a/src/main/java/org/tron/p2p/utils/NetUtil.java b/src/main/java/org/tron/p2p/utils/NetUtil.java index 9a1be291..d1682df8 100644 --- a/src/main/java/org/tron/p2p/utils/NetUtil.java +++ b/src/main/java/org/tron/p2p/utils/NetUtil.java @@ -3,6 +3,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -11,15 +12,12 @@ import java.net.SocketException; import java.net.URL; import java.net.URLConnection; -import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -45,6 +43,8 @@ public class NetUtil { public static final Pattern PATTERN_IPv6 = Pattern.compile( "^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$"); + private static final String IPADDRESS_LOCALHOST = "127.0.0.1"; + public static boolean validIpV4(String ip) { if (StringUtils.isEmpty(ip)) { return false; @@ -91,31 +91,36 @@ public static byte[] getNodeId() { return id; } - private static String getExternalIp(String url) { + private static String getExternalIp(String url, boolean isAskIpv4) { BufferedReader in = null; String ip = null; try { URLConnection urlConnection = new URL(url).openConnection(); + urlConnection.setConnectTimeout(10_000); //ms + urlConnection.setReadTimeout(10_000); //ms in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); ip = in.readLine(); if (ip == null || ip.trim().isEmpty()) { throw new IOException("Invalid address: " + ip); } - try { - InetAddress.getByName(ip); - } catch (Exception e) { + InetAddress inetAddress = InetAddress.getByName(ip); + if (isAskIpv4 && !validIpV4(inetAddress.getHostAddress())) { + throw new IOException("Invalid address: " + ip); + } + if (!isAskIpv4 && !validIpV6(inetAddress.getHostAddress())) { throw new IOException("Invalid address: " + ip); } return ip; } catch (Exception e) { log.warn("Fail to get {} by {}, cause:{}", Constant.ipV4Urls.contains(url) ? "ipv4" : "ipv6", url, e.getMessage()); - return ip; + return null; } finally { if (in != null) { try { in.close(); } catch (IOException e) { + //ignore } } } @@ -177,14 +182,14 @@ private static boolean isReservedAddress(InetAddress inetAddress) { public static String getExternalIpV4() { long t1 = System.currentTimeMillis(); - String ipV4 = getIp(Constant.ipV4Urls); + String ipV4 = getIp(Constant.ipV4Urls, true); log.debug("GetExternalIpV4 cost {} ms", System.currentTimeMillis() - t1); return ipV4; } public static String getExternalIpV6() { long t1 = System.currentTimeMillis(); - String ipV6 = getIp(Constant.ipV6Urls); + String ipV6 = getIp(Constant.ipV6Urls, false); if (null == ipV6) { ipV6 = getOuterIPv6Address(); } @@ -212,40 +217,64 @@ public static InetSocketAddress parseInetSocketAddress(String para) { } } - private static String getIp(List multiSrcUrls) { - ExecutorService executor = Executors.newCachedThreadPool( - new BasicThreadFactory.Builder().namingPattern("getIp").build()); + private static String getIp(List multiSrcUrls, boolean isAskIpv4) { + int threadSize = multiSrcUrls.size(); + ExecutorService executor = Executors.newFixedThreadPool(threadSize, + BasicThreadFactory.builder().namingPattern("getIp-%d").build()); CompletionService completionService = new ExecutorCompletionService<>(executor); - List> tasks = new ArrayList<>(); - multiSrcUrls.forEach(url -> tasks.add(() -> getExternalIp(url))); - - for (Callable task : tasks) { - completionService.submit(task); + for (String url : multiSrcUrls) { + completionService.submit(() -> getExternalIp(url, isAskIpv4)); } - Future future; - String result = null; - try { - future = completionService.take(); - result = future.get(); - } catch (InterruptedException | ExecutionException e) { - //ignore - } finally { - executor.shutdownNow(); + String ip = null; + for (int i = 0; i < threadSize; i++) { + try { + //block until any result return + Future f = completionService.take(); + String result = f.get(); + if (StringUtils.isNotEmpty(result)) { + ip = result; + break; + } + } catch (Exception ignored) { + //ignore + } } - return result; + executor.shutdownNow(); + return ip; } public static String getLanIP() { - String lanIP; - try (Socket s = new Socket("www.baidu.com", 80)) { - lanIP = s.getLocalAddress().getHostAddress(); - } catch (IOException e) { - log.warn("Can't get lan IP. Fall back to 127.0.0.1: " + e); - lanIP = "127.0.0.1"; - } - return lanIP; + Enumeration networkInterfaces; + try { + networkInterfaces = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e) { + log.warn("Can't get lan IP. Fall back to {}", IPADDRESS_LOCALHOST, e); + return IPADDRESS_LOCALHOST; + } + while (networkInterfaces.hasMoreElements()) { + NetworkInterface ni = networkInterfaces.nextElement(); + try { + if (!ni.isUp() || ni.isLoopback() || ni.isVirtual()) { + continue; + } + } catch (SocketException e) { + continue; + } + Enumeration inetAds = ni.getInetAddresses(); + while (inetAds.hasMoreElements()) { + InetAddress inetAddress = inetAds.nextElement(); + if (inetAddress instanceof Inet4Address && !isReservedAddress(inetAddress)) { + String ipAddress = inetAddress.getHostAddress(); + if (PATTERN_IPv4.matcher(ipAddress).find()) { + return ipAddress; + } + } + } + } + log.warn("Can't get lan IP. Fall back to {}", IPADDRESS_LOCALHOST); + return IPADDRESS_LOCALHOST; } } diff --git a/src/main/resources/logback.xml.example b/src/main/resources/logback.xml.example new file mode 100644 index 00000000..ef52dc1c --- /dev/null +++ b/src/main/resources/logback.xml.example @@ -0,0 +1,42 @@ + + + + + + + + + %d{HH:mm:ss.SSS} %-5level [%t] [%c{1}]\(%F:%L\) %m%n + + + INFO + + + + + ./logs/server.log + + + ./logs/server-%d{yyyy-MM-dd}.%i.log.gz + + 500MB + 7 + 50GB + + + %d{HH:mm:ss.SSS} %-5level [%t] [%c{1}]\(%F:%L\) %m%n + + + TRACE + + + + + + + + + + + diff --git a/src/test/java/org/tron/p2p/utils/NetUtilTest.java b/src/test/java/org/tron/p2p/utils/NetUtilTest.java index 78fb5b54..f78f8faf 100644 --- a/src/test/java/org/tron/p2p/utils/NetUtilTest.java +++ b/src/test/java/org/tron/p2p/utils/NetUtilTest.java @@ -1,15 +1,15 @@ package org.tron.p2p.utils; -import java.lang.reflect.InvocationTargetException; +import java.io.IOException; import java.lang.reflect.Method; +import java.net.InetSocketAddress; +import java.net.Socket; import org.junit.Assert; import org.junit.Test; import org.tron.p2p.base.Constant; import org.tron.p2p.discover.Node; import org.tron.p2p.protos.Discover; -import java.net.InetSocketAddress; - public class NetUtilTest { @Test @@ -83,11 +83,11 @@ public void testGetIP() { //notice: please check that you only have one externalIP String ip1 = null, ip2 = null, ip3 = null; try { - Method method = NetUtil.class.getDeclaredMethod("getExternalIp", String.class); + Method method = NetUtil.class.getDeclaredMethod("getExternalIp", String.class, boolean.class); method.setAccessible(true); - ip1 = (String) method.invoke(NetUtil.class, Constant.ipV4Urls.get(0)); - ip2 = (String) method.invoke(NetUtil.class, Constant.ipV4Urls.get(1)); - ip3 = (String) method.invoke(NetUtil.class, Constant.ipV4Urls.get(2)); + ip1 = (String) method.invoke(NetUtil.class, Constant.ipV4Urls.get(0), true); + ip2 = (String) method.invoke(NetUtil.class, Constant.ipV4Urls.get(1), true); + ip3 = (String) method.invoke(NetUtil.class, Constant.ipV4Urls.get(2), true); } catch (Exception e) { Assert.fail(); } @@ -97,10 +97,22 @@ public void testGetIP() { Assert.assertEquals(ip3, ip4); } + private String getLanIP2() { + String lanIP; + try (Socket s = new Socket("www.baidu.com", 80)) { + lanIP = s.getLocalAddress().getHostAddress(); + } catch (IOException e) { + lanIP = "127.0.0.1"; + } + return lanIP; + } + @Test public void testGetLanIP() { String lanIpv4 = NetUtil.getLanIP(); Assert.assertNotNull(lanIpv4); + String lanIpv4Old = getLanIP2(); + Assert.assertEquals(lanIpv4, lanIpv4Old); } @Test