From 5d429c28f1efac912e6a1ed3748752d3ec280db0 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Tue, 2 Apr 2024 16:26:39 +0800 Subject: [PATCH 01/15] gradle-version=7.6.4 --- actuator/build.gradle | 6 +-- build.gradle | 30 ++++++------ chainbase/build.gradle | 13 +++--- common/build.gradle | 42 ++++++++++------- consensus/build.gradle | 4 +- crypto/build.gradle | 2 +- example/actuator-example/build.gradle | 2 +- framework/build.gradle | 44 +++++++++--------- gradle/wrapper/gradle-wrapper.jar | Bin 56172 -> 55616 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 22 +++++++-- gradlew.bat | 18 ++++++- plugins/build.gradle | 28 +++++------ protocol/build.gradle | 17 +++---- protocol/gradle/wrapper/gradle-wrapper.jar | Bin 56172 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 --- 16 files changed, 138 insertions(+), 99 deletions(-) delete mode 100644 protocol/gradle/wrapper/gradle-wrapper.jar delete mode 100644 protocol/gradle/wrapper/gradle-wrapper.properties diff --git a/actuator/build.gradle b/actuator/build.gradle index 9b200064fb0..1143dc83618 100644 --- a/actuator/build.gradle +++ b/actuator/build.gradle @@ -1,9 +1,9 @@ description = "actuator – a series of transactions for blockchain." dependencies { - compile project(":chainbase") - compile project(":protocol") - compile project(":crypto") + api project(":chainbase") + api project(":protocol") + api project(":crypto") } test { diff --git a/build.gradle b/build.gradle index a56be97afa1..0bdbfe6199a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,10 @@ allprojects { version = "1.0.0" - apply plugin: "java" + apply plugin: "java-library" } subprojects { - apply plugin: "java" apply plugin: "jacoco" - apply plugin: "maven" apply plugin: "maven-publish" sourceCompatibility = JavaVersion.VERSION_1_8 @@ -36,18 +34,18 @@ subprojects { } dependencies { - compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' - compile group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.25' - compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.9' - compile group: 'com.google.guava', name: 'guava', version: '30.1-jre' - compile "com.google.code.findbugs:jsr305:3.0.0" - compile group: 'org.springframework', name: 'spring-context', version: '5.3.18' - compile group: 'org.springframework', name: 'spring-tx', version: '5.3.18' - compile "org.apache.commons:commons-lang3:3.4" - compile group: 'org.apache.commons', name: 'commons-math', version: '2.2' - compile "org.apache.commons:commons-collections4:4.1" - compile group: 'joda-time', name: 'joda-time', version: '2.3' - compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' + implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' + implementation group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.25' + implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.9' + implementation group: 'com.google.guava', name: 'guava', version: '30.1-jre' + implementation "com.google.code.findbugs:jsr305:3.0.0" + implementation group: 'org.springframework', name: 'spring-context', version: '5.3.18' + implementation group: 'org.springframework', name: 'spring-tx', version: '5.3.18' + implementation "org.apache.commons:commons-lang3:3.4" + implementation group: 'org.apache.commons', name: 'commons-math', version: '2.2' + implementation "org.apache.commons:commons-collections4:4.1" + implementation group: 'joda-time', name: 'joda-time', version: '2.3' + implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' compileOnly 'org.projectlombok:lombok:1.18.12' annotationProcessor 'org.projectlombok:lombok:1.18.12' @@ -61,12 +59,14 @@ subprojects { task sourcesJar(type: Jar, dependsOn: classes) { classifier = "sources" from sourceSets.main.allSource + duplicatesStrategy = DuplicatesStrategy.INCLUDE // allow duplicates } tasks.withType(AbstractArchiveTask) { preserveFileTimestamps = false reproducibleFileOrder = true + duplicatesStrategy = DuplicatesStrategy.INCLUDE // allow duplicates } configurations.all { diff --git a/chainbase/build.gradle b/chainbase/build.gradle index 408fe56ba42..737ea77d1a4 100644 --- a/chainbase/build.gradle +++ b/chainbase/build.gradle @@ -7,12 +7,12 @@ def jansiVersion = "1.16" // -------------------------------------- dependencies { - compile project(":protocol") - compile project(":common") - compile project(":crypto") - compile "org.fusesource.jansi:jansi:$jansiVersion" - compile 'io.github.tronprotocol:zksnark-java-sdk:1.0.0' - compile 'org.reflections:reflections:0.9.11' + api project(":protocol") + api project(":common") + api project(":crypto") + api "org.fusesource.jansi:jansi:$jansiVersion" + api 'io.github.tronprotocol:zksnark-java-sdk:1.0.0' + api 'org.reflections:reflections:0.9.11' } @@ -46,6 +46,7 @@ jacoco { } jacocoTestReport { + dependsOn(processResources) // explicit_dependency reports { xml.enabled = true html.enabled = true diff --git a/common/build.gradle b/common/build.gradle index 6c1545e5d13..f84bf529f81 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -30,30 +30,38 @@ if (isWindows()) { } dependencies { - compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.4.1' - compile "com.cedarsoftware:java-util:1.8.0" - compile group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.1.1' - compile group: 'commons-codec', name: 'commons-codec', version: '1.11' - compile group: 'com.beust', name: 'jcommander', version: '1.72' - compile group: 'com.typesafe', name: 'config', version: '1.3.2' - compile group: leveldbGroup, name: leveldbName, version: leveldbVersion - compile group: 'org.rocksdb', name: 'rocksdbjni', version: '5.15.10' + api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.4.2' // https://github.com/FasterXML/jackson-databind/issues/3627 + api "com.cedarsoftware:java-util:1.8.0" + api group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.1.1' + api group: 'commons-codec', name: 'commons-codec', version: '1.11' + api group: 'com.beust', name: 'jcommander', version: '1.72' + api group: 'com.typesafe', name: 'config', version: '1.3.2' + api group: leveldbGroup, name: leveldbName, version: leveldbVersion + api group: 'org.rocksdb', name: 'rocksdbjni', version: '5.15.10' // https://mvnrepository.com/artifact/org.quartz-scheduler/quartz - compile group: 'org.quartz-scheduler', name: 'quartz', version: '2.3.2' - compile group: 'io.prometheus', name: 'simpleclient', version: '0.15.0' - compile group: 'io.prometheus', name: 'simpleclient_httpserver', version: '0.15.0' - compile group: 'io.prometheus', name: 'simpleclient_hotspot', version: '0.15.0' - compile 'org.aspectj:aspectjrt:1.8.13' - compile 'org.aspectj:aspectjweaver:1.8.13' - compile 'org.aspectj:aspectjtools:1.8.13' - compile group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.1',{ + api group: 'org.quartz-scheduler', name: 'quartz', version: '2.3.2' + api group: 'io.prometheus', name: 'simpleclient', version: '0.15.0' + api group: 'io.prometheus', name: 'simpleclient_httpserver', version: '0.15.0' + api group: 'io.prometheus', name: 'simpleclient_hotspot', version: '0.15.0' + api 'org.aspectj:aspectjrt:1.8.13' + api 'org.aspectj:aspectjweaver:1.8.13' + api 'org.aspectj:aspectjtools:1.8.13' + api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.1',{ exclude group: 'io.grpc', module: 'grpc-context' exclude group: 'io.grpc', module: 'grpc-core' exclude group: 'io.grpc', module: 'grpc-netty' exclude group: 'com.google.protobuf', module: 'protobuf-java' exclude group: 'com.google.protobuf', module: 'protobuf-java-util' + // https://github.com/dom4j/dom4j/pull/116 + // https://github.com/gradle/gradle/issues/13656 + // https://github.com/dom4j/dom4j/issues/99 + 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' } - compile project(":protocol") + api project(":protocol") } jacocoTestReport { diff --git a/consensus/build.gradle b/consensus/build.gradle index 4ecd7180d13..04cc24be5fd 100644 --- a/consensus/build.gradle +++ b/consensus/build.gradle @@ -1,8 +1,8 @@ description = "consensus – a distributed consensus arithmetic for blockchain." dependencies { - compile project(":chainbase") - compile project(":protocol") + api project(":chainbase") + api project(":protocol") } test { diff --git a/crypto/build.gradle b/crypto/build.gradle index b551471bf49..82814af49e6 100644 --- a/crypto/build.gradle +++ b/crypto/build.gradle @@ -11,7 +11,7 @@ repositories { } dependencies { - compile project(":common") + api project(":common") } jacocoTestReport { diff --git a/example/actuator-example/build.gradle b/example/actuator-example/build.gradle index d0130e11375..e17c75895a1 100644 --- a/example/actuator-example/build.gradle +++ b/example/actuator-example/build.gradle @@ -1,6 +1,6 @@ description = "actuator-example – a example of actuator." dependencies { - compile project(":actuator") + api project(":actuator") } diff --git a/framework/build.gradle b/framework/build.gradle index 8c4fbfc4583..24e3a6c2a56 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -38,38 +38,38 @@ task version(type: Exec) { dependencies { //local libraries - compile fileTree(dir: 'libs', include: '*.jar') + implementation fileTree(dir: 'libs', include: '*.jar') // end local libraries - testCompile group: 'org.hamcrest', name: 'hamcrest-junit', version: '1.0.0.1' - testCompile group: 'com.github.stefanbirkner', name: 'system-rules', version: '1.16.0' + testImplementation group: 'org.hamcrest', name: 'hamcrest-junit', version: '1.0.0.1' + testImplementation group: 'com.github.stefanbirkner', name: 'system-rules', version: '1.16.0' - compile group: 'com.google.inject', name: 'guice', version: '4.1.0' - compile group: 'io.dropwizard.metrics', name: 'metrics-core', version: '3.1.2' - compile group: 'com.github.davidb', name: 'metrics-influxdb', version: '0.8.2' - compile group: 'com.carrotsearch', name: 'java-sizeof', version: '0.0.5' + implementation group: 'com.google.inject', name: 'guice', version: '4.1.0' + implementation group: 'io.dropwizard.metrics', name: 'metrics-core', version: '3.1.2' + implementation group: 'com.github.davidb', name: 'metrics-influxdb', version: '0.8.2' + implementation group: 'com.carrotsearch', name: 'java-sizeof', version: '0.0.5' // http - compile 'org.eclipse.jetty:jetty-server:9.4.53.v20231009' - compile 'org.eclipse.jetty:jetty-servlet:9.4.53.v20231009' - compile 'com.alibaba:fastjson:1.2.83' + implementation 'org.eclipse.jetty:jetty-server:9.4.53.v20231009' + implementation 'org.eclipse.jetty:jetty-servlet:9.4.53.v20231009' + implementation 'com.alibaba:fastjson:1.2.83' // end http // https://mvnrepository.com/artifact/com.github.briandilley.jsonrpc4j/jsonrpc4j - compile group: 'com.github.briandilley.jsonrpc4j', name: 'jsonrpc4j', version: '1.6' + implementation group: 'com.github.briandilley.jsonrpc4j', name: 'jsonrpc4j', version: '1.6' // https://mvnrepository.com/artifact/javax.portlet/portlet-api compileOnly group: 'javax.portlet', name: 'portlet-api', version: '3.0.1' - compile "io.vavr:vavr:0.9.2" - compile group: 'org.pf4j', name: 'pf4j', version: '2.5.0' + implementation "io.vavr:vavr:0.9.2" + implementation group: 'org.pf4j', name: 'pf4j', version: '2.5.0' testImplementation group: 'org.springframework', name: 'spring-test', version: '5.2.0.RELEASE' testImplementation group: 'org.springframework', name: 'spring-web', version: '5.2.0.RELEASE' - compile group: 'org.zeromq', name: 'jeromq', version: '0.5.3' - compile project(":chainbase") - compile project(":protocol") - compile project(":actuator") - compile project(":consensus") + implementation group: 'org.zeromq', name: 'jeromq', version: '0.5.3' + api project(":chainbase") + api project(":protocol") + api project(":actuator") + api project(":consensus") } check.dependsOn 'lint' @@ -152,9 +152,11 @@ def binaryRelease(taskName, jarName, mainClass) { from(sourceSets.main.output) { include "/**" } - + // explicit_dependency + dependsOn (project(':actuator').jar, project(':consensus').jar, project(':chainbase').jar, + project(':crypto').jar, project(':common').jar, project(':protocol').jar) from { - configurations.compile.collect { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } @@ -179,7 +181,7 @@ def createScript(project, mainClass, name) { outputDir = new File(project.buildDir, 'scripts') mainClassName = mainClass applicationName = name - classpath = project.tasks[JavaPlugin.JAR_TASK_NAME].outputs.files + project.configurations.runtime + classpath = project.tasks[JavaPlugin.JAR_TASK_NAME].outputs.files + project.configurations.runtimeClasspath // defaultJvmOpts = ['-XX:+UseConcMarkSweepGC', // '-XX:+PrintGCDetails', // '-Xloggc:./gc.log', diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 28861d273a5d270fd8f65dd74570c17c9c507736..5c2d1cf016b3885f6930543d57b744ea8c220a1a 100644 GIT binary patch delta 47414 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 delta 47982 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 Orw`I9xB+3o!2TZq3*2e| diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1b00285c540..3994438e229 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Thu Aug 01 15:17:43 CST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/gradlew b/gradlew index cccdd3d517f..83f2acfdc31 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -109,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` diff --git a/gradlew.bat b/gradlew.bat index e95643d6a2c..24467a141f7 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/plugins/build.gradle b/plugins/build.gradle index 7f226d7099c..741254912de 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -25,17 +25,17 @@ configurations.getByName('checkstyleConfig') { dependencies { //local libraries - compile fileTree(dir: 'libs', include: '*.jar') - testCompile project(":framework") - testCompile project(":framework").sourceSets.test.output - compile group: 'info.picocli', name: 'picocli', version: '4.6.3' - compile group: 'com.typesafe', name: 'config', version: '1.3.2' - compile group: 'me.tongfei', name: 'progressbar', version: '0.9.3' - compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' - compile group: 'org.rocksdb', name: 'rocksdbjni', version: '5.15.10' - compile 'io.github.tronprotocol:leveldbjni-all:1.18.2' - compile 'io.github.tronprotocol:leveldb:1.18.2' - compile project(":protocol") + implementation fileTree(dir: 'libs', include: '*.jar') + testImplementation project(":framework") + testImplementation project(":framework").sourceSets.test.output + implementation group: 'info.picocli', name: 'picocli', version: '4.6.3' + implementation group: 'com.typesafe', name: 'config', version: '1.3.2' + implementation group: 'me.tongfei', name: 'progressbar', version: '0.9.3' + implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' + implementation group: 'org.rocksdb', name: 'rocksdbjni', version: '5.15.10' + implementation 'io.github.tronprotocol:leveldbjni-all:1.18.2' + implementation 'io.github.tronprotocol:leveldb:1.18.2' + implementation project(":protocol") } check.dependsOn 'lint' @@ -98,8 +98,10 @@ def binaryRelease(taskName, jarName, mainClass) { include "/**" } + dependsOn project(':protocol').jar // explicit_dependency + from { - configurations.compile.collect { + configurations.runtimeClasspath.collect { // https://docs.gradle.org/current/userguide/upgrading_version_6.html#changes_6.3 it.isDirectory() ? it : zipTree(it) } } @@ -120,7 +122,7 @@ def createScript(project, mainClass, name) { outputDir = new File(project.buildDir, 'scripts') mainClassName = mainClass applicationName = name - classpath = project.tasks[JavaPlugin.JAR_TASK_NAME].outputs.files + project.configurations.runtime + classpath = project.tasks[JavaPlugin.JAR_TASK_NAME].outputs.files + project.configurations.runtimeClasspath } project.tasks[name].dependsOn(project.jar) project.applicationDistribution.with { diff --git a/protocol/build.gradle b/protocol/build.gradle index 922d6d19859..615958e1b5f 100644 --- a/protocol/build.gradle +++ b/protocol/build.gradle @@ -4,20 +4,20 @@ def protobufVersion = '3.21.12' def grpcVersion = '1.52.1' dependencies { - compile group: 'com.google.protobuf', name: 'protobuf-java', version: protobufVersion - compile group: 'com.google.protobuf', name: 'protobuf-java-util', version: protobufVersion - compile group: 'net.jcip', name: 'jcip-annotations', version: '1.0' + api group: 'com.google.protobuf', name: 'protobuf-java', version: protobufVersion + api group: 'com.google.protobuf', name: 'protobuf-java-util', version: protobufVersion + api group: 'net.jcip', name: 'jcip-annotations', version: '1.0' // checkstyleConfig "com.puppycrawl.tools:checkstyle:${versions.checkstyle}" // google grpc - compile group: 'io.grpc', name: 'grpc-netty', version: grpcVersion - compile group: 'io.grpc', name: 'grpc-protobuf', version: grpcVersion - compile group: 'io.grpc', name: 'grpc-stub', version: grpcVersion - compile group: 'io.grpc', name: 'grpc-services', version: grpcVersion + api group: 'io.grpc', name: 'grpc-netty', version: grpcVersion + api group: 'io.grpc', name: 'grpc-protobuf', version: grpcVersion + api group: 'io.grpc', name: 'grpc-stub', version: grpcVersion + api group: 'io.grpc', name: 'grpc-services', version: grpcVersion // end google grpc - compile group: 'com.google.api.grpc', name: 'proto-google-common-protos', version: '2.15.0' + api group: 'com.google.api.grpc', name: 'proto-google-common-protos', version: '2.15.0' } tasks.matching { it instanceof Test }.all { @@ -66,3 +66,4 @@ clean.doFirst { delete "src/main/java" } +processResources.dependsOn(generateProto) // explicit_dependency diff --git a/protocol/gradle/wrapper/gradle-wrapper.jar b/protocol/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 28861d273a5d270fd8f65dd74570c17c9c507736..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56172 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

ZM+x{iQDVZRnLYbdXrQjU&=u%hsN4|smH&B~F zl9&;!OVFi3WD3zQ4LVBdL(o~|cH9FsJF;ercBChpx%O(MV?;LbB0l@%fAs}pz_{r# z0Dj;jA`lSoKe1XV8(UYK-+jT~Ka@&N`cB5bdxh)jN3O^!C~uu?r-esfioO{{^p#dw z&nEf9gwJa#P?^hDhztY~V$S+G6;DZPBCxOBp~k5wC=8&^H7ncko(=o+?V=< z;zNM<*-26bU?p4017Y-n0GT^U$in3)LKr5+RfKc;*uERo+g%7~JAMRsuz67MLA4<8 zzov)@dBTTNFE0tQ^~Ms4+@R%tT|@?&x<7Gl_;jJrZ%IJW*B?qD=_Fr-f3f<=_0{~E zE7^vGq(d^XDS_g8*%~8#J_)c8Y5>zDE>1F&QMceJYZ{98uuS1($i=!0wJ~EaO|H^l zP1vJHr?{no%=86UkPB{=GDIH0A*v3$ClNrRtjC?7Avqy3pAOO?gKYe9=ZwVP&Q(aJ zet6kIe`xOO=Q<7c;tN{$_dGBGtMabUw1{%F6kJ zV<=;Dkr?i^9D9mko~Eqw>d#o}57svg&7ACcoE0jbJ0w9ja4l^i#G}21LlmfOlr-|W zi;y&_i6!gNCS}p1X{r`nFX>GS^iuBM;G7?ssUPZ@dZ#go(JxOKKv+?lb(oC@8!eq>W5#H*(LQEHe$=8gB(2_>*YSHm z20m@1amL={>u8c2DpDsbK&)a~sZ}oSYLp&w&>|{;Q1Ba?eM+1vQTc3`o&!4me7a9^ zO1%MAJvYDNEV(vkHOPQFsL)~-Zb5OxWtR8ZG5_O&%}V9qNW%+9&sitkE*uVu`m#C2 zN>6SBEpahyMKhCGnvjQ91hs2MG7@*x5gL^3m>Z1kxOzlrq)_OX8-xPXIkZ+L`W4=K zGi61`L>}=|i=>Dw*OOOjqv+(@PHE(wop9e16JJjV6JMV|IVvXpE;6PVCk8HWSz&?F zph@HESgnaU^MWsIj^gR)eI(;O4zW`0-I&-AML%EgF47QKqSqkFE=(pu>kodN`VXhf zm1mTKzZ|}$n>x!tvP>2afzf3yzlZ`7W%eYhczms4=JvW_Uorx1?64vz*FdPW52+m* zi{avqj78R|#D>d8<`>l66`7G_yDcj+(nsb>VB+T8ywaUkU|CZfesX4w7IJ2qbI%o! zuImh{cnvjPO;OhBgXt-Vk+lSd6qbe)RcBQi4xKEp*5#o?Ga}dF!k{;4d2WzU^Lysf9|L)HF=YZEYU0dTW@1_=5Z~y5wD3KH`D$yK0ekO^fexAO~L$t>TxAV zFds-}dk7IFa1aB!pBzD*KR6!|B_utHteSL$0{z%NfkS7(}92TyLX zl?=WtJmKFv)tx?EJzjD8(KEVw>)$(ycMjVxV2pLy;0$(LySU%7RYhPAGj;|OX_SYbpBRuc42l!-phN_8Nj!up>1#Y)etTxkGn}8$5WoMCp_3 z`V_N7?=vKE3Dbq%y+eMP5upZ=*OE|w0Uqv1=%R;cGawUqEYVlHIJr!m_=Fc#`^)~c z=T|Fc%Y9m1X#FY5g7_hK5E9h!tKbdg$l1;slS$Vke4fY<$w$T3y0SJZc@-9Ldn-*0 zUHf&-(@SF{g&}Y%^X+Pzy9mi4Tpxwe)>(QgOxHG%!HOvPb!xo?OTu6@^kM_5j#D#H zNc0&m`!8?q%h8shyQ=95Xaj=j=MZmg4Y=GOdGCoK;=e3U|F->d2RLZ_M=Mbob4N#j zYxw&|7jWGEr!Q{SzxQEWvDX)zndA}h(?E^kN7#fveL@}#!5~kc(DSdMt4w2Er`wS*qqT zxD-Xn4NV=oB5cU z*KBdZc6r0#sWTmIQAh~md6mdfG*64xB2pBPyDnQ_Ia<5v%uIshD9gjJOajXh*g1t{ z^<(t;Rs5t#f$}esHrfMrjC?INWgl`Krb1kM(7GAm8Q>M&JEdrK#{vD)xwr?u!$i+J z1~CvLoEeiV@wu{FEg#K@W6y?=DU#`t6$`^KXZ)5F^!OoHOdY~k6u~Azd;B_E z+HCNqxpr%us=*mMV07<~))FJ`qL-8)g)saG>%*VyJ@8lV3|r;+=&&)G?T!#iNU{nc zN7Wec{Lh1-$WT)qBJo3fY{nUv{mDLan%L6{)82c8=HuwT+2&NQEu)hxso|S~1_RT9 zr1u#?x{D{z$H>)gd)E@inCOLs9`G|0CGRv`oAcxM_Q85_&BvSZ*t>d}*oMc4fjN+`>crs2PN*33oyS;~fcCTEBKA_AWUkv0CeAcrAGsouCrlrUY7 zGtPsyX-ALgw$o|dO}>3CVK^lm6*QFz%YeMHz0x3U zu-l|fQ>zMnT5@kJ-EzKy8KjOaR*>c_4bNU5<4;Rp1}Rv?yP_i_6OUYOyA4sonek%d zudbMQCIQ>MSIDT~#*@`bbx@c~RxRbhZbKC^;joD(ShlLI3`OSZzqG z>R2u_2`5B^(AJU)lb05Xt#OeCVo=*xBIsIoc8zam^P68%&)vv>MER*UujZRnW?T&@ zYJ<)yDvN!Pz%^y8DZn>%S{tej2g8j}SFEet{a8Bb=r>r|VFy=d13gUJQsI-XU#q5G zzHXSxg?Z2$rvQH=tLCs~n#ynd8I$a7&rPM0;fp?x+X{2T28)=?LG2>3z^+{9?#*KW zJ3vxr!wTCstwxevC57uIbI~Gr*J$75kS-=`%Vn%>{guAuzRQf|x!cCmbpG)La2DMvls&nXmi@NeH-Bc#9|x=wpWI2#oa&BurvxqldPC9SY3m zJ5RlUp-=@F3he)6?e+Umc)vxE^zT8iFr&bRQ8VTxU_S;O$@B>!9CFGmnMRLEXlIzo z#zbN={`RjO6c_b?)m(cWA^Nd$;A)cBuCUH{J z9A;Q$=?q(TY|k}s!xN1{%yJIa{uNd&r4yl|AKlEn!4p$?wp=cw<~Uf@+uU?QL$&_JTC3I4#xl+J>7unv+bdeQdCvx`FQ2t$41EDV!ASZ3`<3xoQv8kRRlDvGS6` zX3a-Mf=A6lVD3L;HR(gwh>gYe9WnL%l_%{jTT=fYqm8cc(UN56{K!aK_z z<7Rpi1}O}^OToAnQJ&soj2ZsM`{IjBbBNO~-m)-5AQl7GR6X@V0I5CP+p)q1u5xy) zmQAXsk6|5StC6Vm3BBa9r2c?<{bU_NR*jqd*LN^zTeT8VTEpxOgBPa&@Izb*LNd{4 z7oo;kv!d~!fon;) z$R1OKw$m=93x&)igIz5QbXlJ`yFwRYI1qh@8J_$oZyQjZDfK=UKp&ymv@mH5;l>9Z zfUFIIKFH4Wp2d+EH&e7f>AO%H5$Y6{m`=^GOT8f%M%Qo{a6u*`c58{(OIp%Y!XNA8 z)B)MWnSX%43_T&D_nQ{7u9|HXI3}5=iTdDfEI}t*d`wFh+XnqY zll^2uw++hQGZ~Gr+SOofsLx=6lK}Zv1}rDgFA1*1W6CS`F=A?3Ql2>^+P^-N!S0P) z5*ywG919;tZwLFJc2Sc$QSV3)g*tqXcE$)yzavJxCc)s99dyR%^hBvX3oS zTyC^q(}<{|Bi08A5Abc4%qJH4ELLPV*h64%QfkW-$nlP{@2O4|%b7Dlxb=ahMm$QH zap=3CgTK!ejh}tGHXC^n(K1*{=Z6-u#v84gL3YvarorJxZu>byOF$A)*LVj%r3;Po zLoxp51+9jHE)wdZ4z{(CEm5g*%Q?J4U8>IF7wNbcGa^5!6WPv*`{mD61~j>X7Ppk- zPPqsCQeKLbykCg!i^I_RVRl&vMQg-=ofEZ#LqKW(b7BV|i{l@iP5%D&f8RX)7j>4> z>2J{kysoSD#u}2ey7?5K;f*lHl==65;d7}Nh|=<~ukBXs#`f*2Cv>9tgX9tz7(yPN@{BH1hr>(^H#b;MFm z3~Z$x@WOHxKG8yu==WRhC3aG$1IJe zxvR-L2p4QLShE7lOC4=mbGFcOvIV#4V68CP(%Rk&BDN%B%CzDl2<|O|7O6ktwe9XA zZ|{z=;siKJ6qu|8>-f1+yvJoSShLushDxgQi=Z*!`N+$HK&hd?RCdYk;Xp;Fgv&d~ zpk1_mk=VxDZ4f&?IvfJ_Xe6daMIH!4N2m1W7iIFETcTWpU}8|J;fO9tOkTw2WZd9~ zt7n=bHRu!^@zsqcXJ7W(lY{7`{!cJ{k>WG~ z!_nKwIzB14VVFa(FO}=l_f$Th)s(UqCR&N}gjd4i+yv5CeF@lDUl!SZf@)wzWaHF1 zVZtD%710K13TwTY`(PtF=g??+j8|aiUy$bdF7Y`t_K>I4!O`?zr?gHKd;}eSBB)Cz z@myoHjP8PaQzeGAP}zJR9DxE(kVQ;o`j~f~<%CXrR1&MmsHp11w;-)k@KwUkN?HbA zV3|K7dXs5AR7e&)-=KpN0o9!oAx~xt4QZK$Ouh|h$LE)Nx@h=qaVuHaia zx*aOksgYl5$$K@ON6&?f6oCDE0_^|)hkN|@hX+~8o4=jXzn)pQ2p;JXNsB=ELq7Q> z0t=2n`q2<-Fbx_73vbdDU=Du&%{8FD_>n>Hc?pIj6WR61j=9@*Dr|ok3EzG&{4&M4 z$;sWK+tv97sfSp>^%yssH!dWkBcu=#E_Ri=s5fRA4}&F%g@ze_+-werIM23yGThaP#tYGd zFF?Urd%T8&2$H6+YM!UtoXxxLT-~I&4Sz>b_*0!N(lPCc#xk-znS9_7^zGqQ%bS z&Dv(`W$ogMwGLP&JpyAr%ox^62CLg2>WF?S&LHD(C*Sz$zNQ%DLkOy7vM_|h3O%}R zz*fAq38}>o_8VZd*=WKlb-qEZAP+laYztgFm@S{(h4+5o<;}V^_<~msO$Q;hK%hY; zp@~TXjlOj*zKxO3Oqr!6knThbz6CBykPGgwZTA^gqS!a!GmtN%5c} zYDP!6KuVmV*@%&}*oCmj{zzsBZck*6Fkd5!x_};4 z&bxJ>_Q8+e_1KxGHtfGobDRl*_i z`GrC+wGk>_{7!)#Y(oEp`>!*88w5!$1i<3k0q15+|HKRak5yoj(x&ZqfSJouqQE$U zwUjw3tjX(HDc_keq>HmK60Ram;N80T1v^u=>^Cz%@;~fEkn!C^+>2pOTQ3_0fSP~L z#=pxv_d3X2-SqW&{a^>QD2m3-=CCwcV6h98tqC|MLU5q>J{qopO!L?c)N|>}6H`BZ z{LbBhamRZja1C;s*uMPtcnp2`4LLi&~(j)V+>8t;+5X4NpSiYjw`EBjozv0&&_p)gK(@ zY%-Cqe4H@j5iJTerUnpI1v!IE^i$*|Z!A0H4p7pRT!$_9L(}0fbvvzVQ)IBTCBZ%L`z@gSbEQb&@Hw)f8Fe`n;2+*%_E}u0j2ulJhx=a zN_&D@7ZV?Zrf-{e+uH66!u2!9Ga%Kj_W1|YYD7l6D$P3h9Ru3smbC8H7!hbgpRd}- z$2z@3#0w;wy1n`zQ3UNzAVch`uuIRA=H#3dwK~!u>eU~}m<1?-sT!mORx*vv4ox_J z;qEVDGgv}Rh+@U}k*wfW`eE4N-XU#0Ed_Srz*jG^B4=!7Of(m#DnK8Zjf5l&pwmQ2 zd}bb;-&0<0pWJFv)CJfPXCBbAq9T9dUDvwy@yj-b4 z2JixPd3)ptg*AiJr-LKC5%xhgpc|G@<5k2opVrAB0}Pp#mB>63p`LG}5rgfk+2f0C zDtX?%1@_jToKGZSXF_TN_>u`pM1;(eP-w4sox{990;*}5RyLq3uejuaEjM*0R$@CoSW%uIIW#&{1>a?O^5V)S74=!U_hbt9=szDlAX z=O1ch!c&mYC@^QVNN7i)?>eQC%pUl*IKt zVjOr8oKpOes5r`a7{13PTKT4Tcv{)fLS@j7^c!dJ41n11d)Jgf(j_;s{)Fjxe!??@ z$WCey7TQ~C1BZ-?4pB@XMuvtKJhkt;-0Kliq1GZKARq;*{~)dX+eO&#o_CgpyI$ga z(_7ZWl}wkHl^;+64IJ9C-@IP#O&S*PPU=RvmP8E3cW zSxU=vhaFB2jXNzmx1A(wiHhUUfbk(KC>hTos|d;Pz(;$`9kzi4avetL)E(wH>bBri zvS2BlY;`6Yx!`fgd4PgzV%TTWP4WVn$YjP~lvE6ILvJS87rYv*?tG46;gZbb1SkuW zd<(L&v{63FLOO?Rxnc~ad0|G6`6-cLlne@i8o4P``dMYAd=5z!rDD)T>NeE!vcl|- zo7X&L@tEb9CL_|w^GxHhFwzrA%fSIMowTheE8`WKnAvGx;3kjdrE3=MEYtT7cIK>g7ALut}?IfTES1R{Q%_moQDb`%u zT#Q=Wct#Og%CJ!Ori?N~7siR@PFTbv2`xPQa4=rlnTfTg{iK(?0^RcsYMS!@+Y z?Om^8-uJ6@Eb)ugFNp?CE5-q|PkL35A*YA+@&srNhW>RGtGm78t&DhZ!Jkt^T$&*A z{oF__MqGM-82hDm65%xT*Xi-NMXl$EGko8cJ+MTL?B?lU##zR7L0bgPXXIYNfFH0H zT4~)aGSz^A7Bx=WAfzaTA2L{5(Wr`Q{zSsmYSZUaUKPs^_7Ou;Lz@(iKiC_>d=W&H2i_ce9W6}l!hGU#Ut0K~537P~S%=yPun@Zupw;o;Z$8}Bi$_#lAIQSt zwl^=&IETx}c2j-FfvkcT4*2P6@Ez9{M)4|9PGQlWE$ODQB5tcMUIyfp_LN?rp{Z~* zFR)|3D~E+V0>fW(JsTkXz=hbm7SB?S%0pjt|E;;9u@7n*+63OhXyyw?2}%vFjlR_{ zJyixsqET_BkCXXblIZ<}=@J{_2DWOSBu1dn7}38Qh^_WNXXd0&u_PdV-`K3BDM^}i zQ(`7#a(LV-HpSv)V^-%{O#n_fWvLJBhCb6rS?EYO%G07 zpi6})iR6b?0e45LsxS&9u-vyc=da2v*85%xx619A$Bq^OlqC1QjVh zh%`TqPe7Cmr4;3o35#wtMS}s2aH+_25lg66QJWWbId15uir38l5^Ax!ng%6%i)dOY z4!$29Cj9xtjA=Pjqe$0tZlijdgp-*`rdy>qRdKm#_Kc)M3mMYcPALXAT5SHDtAu`J zV1aU9p`QhwnzlxUAT!f%h55{D!%va9~I|G+;^-G)Mr7rEP@AtsiwDZ&!?Wg6!BOU!u zpmY>U#nr}8NA;`%%Fp$0R_U8HIJFR%#R!gR8ug) zeVn;G65**O!uM#glV#8oL*inMX{^bD=XD??GHMPqC&PR&uG=;+y7C2{m!t-&n`kMZ z2G(msu^*+XB`d(EVJ>P)`fTJJEM1k;lE*&$`k zW_10^UFs~3UcFxK7FkXbZCDZ+1*RlL<4UAW4bgiv{^^I0L9ve7xCN^20N;XeSlbxw z?071Oxmj}M&CmQ9@ws@2#P7S{#o`Qe`SoIEivd^0Qe8w4G@PY4m$4@;KPs+jNp%yR zXdk#rhl#J?b~;Ey5*uG3I0#BV$kGvm6y$&F>)zR81nx(w4o4LSTNMKaHEdwM zOKwp^ZIG+ol1*B5qnkim+i*O(3fmkFOkjVUn|^Ll5kveCHi0b%=j_S1fgL}y4m($d z4ONaRhZQFn*DYBgo%$cG9abZEDxxQ-R#^E1ec~K*8cR4(!yvs3sMfYHf#$L-OIk~7 zL&%mUp@SGX7WC`ZS!^##APbycLOyz<)RJ*fq#5YC-EA*lR}l6#YAIRE*S;22&c&5f&Npv^YiN`TJ>{K zB|iKNeVrAMRWq0YtP@`Qm%PBB6z)pjNJ`2{)&A%;)Wfyn?CBY|t4>w<_#(QsQa%K& zbwtR)M??}ie^6?0j>8)E&8^ebwc;s8_Jumy8ECV#~bcps}wF} z9?>2kTtZ>k8pb(A9}6&adEz}#QjAo*-70WRd1p(yj^+djKW`_p8-;w{wdRsO`qClZ zN{A$jw)*z*|WEG$AMZ<|na#c!PNWxib;b zlb`6-!mOo^jVd;@H*`G%uQXPyhhNN?xb8th@YSLN_W}+aS$A<$MakP54H^6l)JB#| ziRh1Q?}!`VJ=mCV_OI(D-GXLV_$|8UUKtk-hr%Jhob%3cvwZpjfE*stL!p+DTIiE` zR)uiuntu$=OuKgghhU_KsaouhaFO~6T!hpS03*s=pwu0}Pg>IO z>cbMga+G$#9 ze&_=1t`a5xj`T8F7>r{CQqa;F0iJ=I8ix~;H-@+S+=B&_pO2iA69pKq@D3RsdTdF& zF`0%V$T)t^p#48R89K@;{m+vT;r50Z;%gvVHoajBKp}qMvW}s9;TKr)B>Bj(58=d? zJZC@q+eGqyiQ~msEL0z6cN*=_ymj5p1mOrt^nnkXJ{=0gs@YtP3L|OF22Eh;b?P?# z(PtxFean>yR!E`T7`%D$E9Hr5(i1O@j%*fX(kZ*x*%PS{<@nA`$tfXca4vv?z!|X& zo~Q<5kSF?=E*VUiMaP&`_Z>#@-nUJ|BpO=-u_|1j^jK{}Gf85Bww8JbQWWKM-GwLz z5v`3V=y|!)%LniEQl2kf-Sp;kD!uC#9v%TDTrC7@ZIwR}_P)346bHorfO$w*fGZ?q{_|~0b6atm=;bA z7o9V}Ro!uDK1S>TKN&zh6h^k`6D{s18(KHv38!_#Q`>=93di52dJa#-*Ta5|G`Y?f z3GPj{U!p^vp$alfP&|o+sZ+v2jF(v=ykN6JSSJ^Im6x1xa|c=wn4IN68xpMS4`Ty6VoN@JTngOcp4anJNO=W zHuFV?Uw;Y1@F&;p6Z2i!yugB4_1=Y^IHkE$60|HMEg%114zhjY`kGzbwa$sVhHiww zvW^@D4E+?2_`wyG@RHJS_)lg-uPi)FNG6b`4dJoCL}vw|PYt0<5qKSkp|O%HHg+}* zg4x8WD!Lo;?j0+q<+mtq&}$*7b70vTtQ+A*E;_M7$R-DR{nmIUJx{2^3}WBpk9rV? zRLH)SYU(SCu+yFVd?~G@FE6?1_|$!Wm>?nCgLzWn9&U+AitY9j8xu@&bCTy$B9i1l zOJ=`MN?0C!`zz?M#K8~+%CA89nZBk%x3te+p{9{<%Gw(PNgi!X_$aP#7+rOGE3T!l zDznm%GZjpEQO|V3Z?N1Zdyc_3^r)Ryhbg#E7TsP2eUckYY>8Vp-Q`@S-?*|zCzIh-5% z=)Mk$*+aSJK~pC#Eyk4?;|Iod$0OVLR&VkIOKFGufD?f7C_eeZl=cQ_hNf^cggv29 zyPPLv8+@Vt!ud8sdkW9-We<3c$HYU&zK;7O#J^y55Rq$;yyZs3JIER^Ri!S1Y5Ft1 zhqoB9ZzR9CiRtvm{E+FOK1U!-5Pu{{-n9;jXiZzHHsDV2 zjK5b7^Qz6^gKvzlUi1B)`*S2#D}xkX-*nisjpi+qPu?#D<3+36=8m4BGO%64{hV^EQ}4Qpe!1%%^nCY#J8{`2qJIX2|pNczPVlB1>us~*i(TmD%I+&DGU~t|-?|Jwv|9$~|$)uDMhqzJk1!+1rx7 zMvzy@+fe#MZJI?SGw|IOZMvkt`Z{$2FJPU`Vi<3=I6w!xK&;=j%az7C`o3hdi=o?o zKG<(fDJk`G=;-L$xhGO19Ln zfsRd2IHrAB%n7P`Ztldcf{`lP(HPogO_SbL z1gVPe8)}MFju0z8d~V6mH#MchlD2zV-aGCE4c{J@XZq@c7212`mpjw^zTts#xzrSF6{ zZp!EtnHGB_bM`GRA?sncl6xG%rP!8Ff_K^C2HI}Q?BsArc7ySZu2p+l-@@mR!i5*2 z{rqxYnbR?qc78?d`ni_0Z!{tO2ff)M1E0Tqr_izb_^U-1Wx+~BE6 zcSvT|NsV(xYxK)aCjRg%_$_;Vci3_N^5%pO{nO_)&eo(C>%#7=mjm$@&5rxewr6ke zvep}D&R|{uTf~Nd%`US4+$R3Nvj(GoC8z(!8ThXwX0>Bo95qZI6Z(mIX-IiGKe8jT zy?Pp{ZzL-~lu6$P0)YVPO(gS&fmt*OblgU+XhN1UpQ|*_U1h2k%iY4#=RhSdZ)JRa z?ml#JpPzOEafI@V%=m+$=0p;G39=xu zR~a-w(Ko%!bmOVnQBqLm=BA(9nr&4LK);N4>!{persBgE!9~ko3RAPV;M7vOe8BPo zt`WTuLDdcaelo7WvO`VPg(ZTGMs%O<=F97E8+ykcG}IEf*J62rtA#v%4*li4?A`}- zvEZ=BlJy=~2c3%_B?doi_?XJ4Qm=&7Hba%o*UJ9;RN69&>k!>BjE8P78?*QB<8!Y6 zPYLF%`BT9udAqOA#|oxtGYv<45PEhKV?|HjIeC*9A5EA{HjzE(Yzsvz+c%X zEk&m@XB~^x+cV}r9`FcKC})-t=rvQD(Ok;nnSAE-ncXMNk>D=Y155kt_GcK4Qr}YkW6{CrHk#8tm2NY;T+f@F4LP$zXYvG z4I7O*Aw7nWrZ)Ku#hg--?4U!kLC=%(VSi~$Si#O|6|GB0ZTjbf!3^slHS51+6x zXR`e88SC!JpR>W%ai)t{48lI@2FT`snWu zH@cx-W9(Q>uh6ECOEJXx4zF3c%uyYfhoF?C{q~{nLHf+$#4ebTz6yMo;N>5WUi=mT zf{O3PZRW=R(Sjo~02*)Uo-1?wD8gS44!;M2lbof)FUL{c>>kXgOdqOS5urV2b7JXM zedfaQS#;2L86l%h&0eVg{K69~WG#&o;dq4HaIYn)LCvQqtdpsS8J)f%mX#-{g!LJi z-JRc>k=reg#1PA7TP8Z14$hRZOdqs3n181^oEwV|IKDFyb?PY|vsYH)I4xgoxMm82 z4!#{H$3PqRp;~>R-jH$^sXz`F0du_EO{$;D#?lR&63((!Tfzp+@g#2SNO_H>9RwA0 z*FiXAL)1}&JV`5=s$?3pEs4$QR9=;COzf)=NmIdzmhJ6aiauAjh)be%VwFY`kMPt5 z@ulR&7_KgSIh{ruXBNf_pY_v(XMoij{o`{-oQySW*Ofr?4H$A-U464n_+f^Z0Rkx7 zql_YWHky;uBj!Vp#%I1;v*|EW9J!)kW=v?=BSU=OvF3{u7f87L-MrkG3ZRW)R_yi9 z_&bjm#lPL~`(t&*BbRi#vf~6>l6ThfVH%$0#)PZ|u zU;OCrJ0u|W3K3$AfmB+b(DC|1?!}DaL;E>II}~6Zj|lM4QE8%r6T*{d8lkJI*6?Gf}Qn7nk{sf(6}ABonW+U{z&}I z11r7aH8S}~&mXpwdWn@27s((BrC%@-@{+c3Bay-X<8Y%;@FB^aq0 zmbMUf!^M`H*~sYJC-Dm!M>}(Tb_8oD}BpP;$I0 z(*}~?@$&Y>7$(K@wQ`1;rRPMc0vE*Am01Yg;NhtFievBFL(5t(@EgCb`DRLH?$h0s z02JS~at<{_tt1iT3~s^f`VBd#PyqvAzZ*I z$)h?VK;koP{7>o48=4I=SY=6;bl`QxIGha4U)Hza=(#6e-UltYh;1}Md0Q>;fV7^SWHXG@gM^MdWWfm~ zECx|%iAdo(Gf4I$W!!DSxL%G4CQ!uJ`m9)5f;~vvjl38($8qEy!@X6$)jPc#fq4ITTVe=a2PqyIyl9=4bpM52}wEXsl3PdJjw# zY9_AAs1eZHqVK8*-hNtqinLvFVYL$hpIQnkF=y(Vcq#i?PlMz#Z#He!a~cr03y`P< z#IC3IC9u>}l&6Xl`x`*xwq_Ua1&5E4T(cmxruEWFliGjoIxlUd-kf!4E7|D^hk!=< zJYi+0CeYkC+MK#^5m=TIcsxlVo)o0dShH;hMogPy8qhFGBSh~RT^pIkNhL7>E#>A2 zogZ|m0#+x|E;)!xs(+ahwZi49)8L#y)E2L;zfa{D$P?0=+CmsAk!QpmY{OA$;m~OS z{etSKrK8VD@x-;Y;T0Bw=TO=XV8 z>p|ugJqKH%ijGsDu$x?xTVls1#T9EbOxfmpDP_aJuKX#vQze#e6|ST&2Wr%13+E^S zNkRzT1Jx<3R@)AznU>P>P*@hAv4R4d<)qCfW5bX@b9w*$3Hq*%f*5F0&H8Mgc6Hpg zmNwgT!DXWxC!v0(HarB&grOprUz&XXL9_o_c>RY!u~b>ir`hRds`(3yUsz})c{6X= z=ah*_H!?be@T+n$!Do@wE+5X5&5O3j6lmCWgK`rqqrdlPf}{E*bXD|em(O=vYvuV; zNbzI9Nq-eTr{fa&7R7No>Yzz4Z}d@N1$cRfFL8&E$nq)FN93d-$2(5-LD!$kKzUY- zn|5TF^!n)@q!q{DG*EqZ&^Giu{}dstDf4U0kLexsfse67dH8*Hj}$n(pUC`mzulHH z{d7Gcjn37fx;Z3y7WgUOBd>IKRQp80%P7oMluq~~tn5eLtc1xR>FY*aY#=_4jel4O zgCDCJg-cQwgh95VF!UnH$N=yPk=v}r7zUGY<#fr(L9m+xyT2tL+}BRRonNu4ban;W zy>xR+V)a|Ib=O~Zg^`D~66QFFmffKgFTx_<-jRuFxeN(<0YZ9V03p3xe=|lLY%Pop zo&E_Oa#p;QT;C^@plL8rVK099{``|3&~yyvU1Ehu>U#;${Cl0cWKU!GC4P|0gI4x`Wm3yy3e1`u-&cp>ypGMLr!sAAeWI5p}j@L)ht~D zrIo&B)~+EDcH@C-SKDYTvQKGBaZPj^N(%p4nmEkHK#0~~_s zD1E<1nuxpr9*uMv9Tbg26`~tfy4T5nvk=NfK@`H{w-RXJD>)x^3x$qbU9}YMbY*g^ zLnU?BI*$vz*;EXtuCj4~rP_%bS+Hi#fXC=NVhPvR>-#avjw2w;6+*LalS7%o^o$=1 zQ~p}Ncq${!Ix%wUls6!ILI@g6sR7v$7p54k1h^mq*$Zl%Q7dNqTJxtpIIXwPtnQ)Y zhxBZb@vuXS59w(l)KH}luH=jUz!On-$!URP%?y?+HO7H%BNF z7|_UM{x$tJnc3Fi+tCHw18kK-03StUg_5TcIQhW}HCKedcZ`Q@8p>$pG4@mQ_^^2H ziYeZP^g3d=CznH_;<;l4mk^aYi|jyUX6=_Ag&dgGMlf7%GtH085c&i&oycoqgqYyk zXJ6;A#UfnV*p-OFkw36v8yi5|dXKh><<2ZT#W;z|gm^S_#`?QA*Ejp9ds0w3+DYrN z8`IT-N~zMo-7BlRjpm2nbSIh!gDK|%iF_y&%f%UxA67&0+Xa@it~T?juNuN<;S@Nv zaI0#XsfDYWb?i60oq#i)OUt)G;CLQpEnC&jr4#i-nTzjstcBpb*-{w)5H^*+Q;(HK zg`DL0ME@yU#S}`CYTvN#qcJMAW55_SV;A&1=oyJ!ao2U@7q;%aGG6V11G?6UB0{b~UHBp|?2`2W<^|HbDI2>AHlT>g9S8T=t3ApsBqfa{Nf z0k}1AHn%dObuczGHn(&7vnqfTE!EV-^e^g38A;lD)){6NAV53{1SDukx52+3NL~u~ z0}2q}w?AP6Oz-~+fN}0!kr7cApp}pnrGH;dKJzZ|w{S2O!1WvSAB7Td`~Oyx5s;M- z6;V>AlM#K7@LP?4Hw*|_{8LE>-2Wz0@V{yR*oXd9y8cz;U$O@Ot0MBBssRBV{k7u1 zBp3dpWg-q4YBqplLJ$4Brkb-@EV_7k8}0q_4$#SgGQ z^S=NA9}YKn0cR&O01LIb;UC;7?^`&A+P7)~F#E>f0s#^J2_Fb(2Vg<}qlMqSwfAuD ze$x4Q0GKhr^&3U@A7uex?EeD}@VurD#*U8C0Ihdpn}5qsyoaasDD3Y5bY&Rq@0k#P zzz<>mEj)mL+sfGyz$7DTZe=WBXb5OQM&Cx?^uINbbvp{`0qF2xK!^XP2lz*sCHUJ0 z#2oYi+Nml4o=S0BYh!6!TT5rVzwa8d?P0VBfX#IPIsy+nWB@w;gEC4^$5r^r`?KjN>n0>9T(dCJ#_<5pZ-gwl)Ch<&sF-8tPjK0}R%| z+`#z{miPBY`(Et+kB0K)|G!)L`)+uz^7{woi`w5}zV|);qWSy&iQlVY{((-d{kQ1< zGSa`%$b1j|UX<_;Xb8Rk1^riv!uP1}Rd@bC^)mlQ8a(d-e**wm+5eT_bawtIs{p`1 z8SQV8pYJQbSKaxeGPK2iRQ|W{$$xhS-^0IGQuzZu$?Ctt|C5Ep`-a}@9sJRdy8VAN z^rz?lFX{*H;olSY{{esI@W0^S`O5EM-}BY~0W0hDzhM8o3Gp8DJ!kPBnAslx3-kB1 zjQ=(>zGnyi12x?9AE5qsuHgTtk~2n8Ac%tKBpzaqu&Hekst^n z8Y#wNCPo7yW{a0GwZ~Dbd9B@ljip}u8M@mVsR` zVy0iH{ltuN`^&dq0!RoW(t@0)W=IgDB85?0QT}FTiXY4+fLTWmu=pn+H8FEfFvh3TTt b+=;!jU|P+J`>$CfFsoU|bwOU-ceCsYH7qU$ diff --git a/protocol/gradle/wrapper/gradle-wrapper.properties b/protocol/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 1b0ff046d00..00000000000 --- a/protocol/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Thu Aug 01 15:17:43 CST 2019 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip From 75a06a14c8618c624eeb9fbf4ca8603d1bb292df Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Wed, 22 May 2024 18:05:02 +0800 Subject: [PATCH 02/15] feat(jdk): update jdk to jdk11. --- build.gradle | 25 ++++++++++++++++++------- chainbase/build.gradle | 5 ----- common/build.gradle | 2 +- crypto/build.gradle | 1 - framework/build.gradle | 4 ---- plugins/build.gradle | 3 --- 6 files changed, 19 insertions(+), 21 deletions(-) diff --git a/build.gradle b/build.gradle index 0bdbfe6199a..81611d1fb19 100644 --- a/build.gradle +++ b/build.gradle @@ -7,8 +7,8 @@ subprojects { apply plugin: "jacoco" apply plugin: "maven-publish" - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' @@ -47,13 +47,20 @@ subprojects { implementation group: 'joda-time', name: 'joda-time', version: '2.3' implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' - compileOnly 'org.projectlombok:lombok:1.18.12' - annotationProcessor 'org.projectlombok:lombok:1.18.12' - testCompileOnly 'org.projectlombok:lombok:1.18.12' - testAnnotationProcessor 'org.projectlombok:lombok:1.18.12' + compileOnly 'org.projectlombok:lombok:1.18.28' + annotationProcessor 'org.projectlombok:lombok:1.18.28' + testCompileOnly 'org.projectlombok:lombok:1.18.28' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.28' + + implementation 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.3' + implementation 'org.glassfish.jaxb:jaxb-runtime:2.3.3' + implementation 'javax.annotation:javax.annotation-api:1.3.2' + implementation group: 'javax.jws', name: 'javax.jws-api', version: '1.1' + annotationProcessor 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.3' + annotationProcessor 'javax.annotation:javax.annotation-api:1.3.2' testImplementation group: 'junit', name: 'junit', version: '4.13.2' - testImplementation "org.mockito:mockito-core:2.13.0" + testImplementation "org.mockito:mockito-core:3.3.0" } task sourcesJar(type: Jar, dependsOn: classes) { @@ -62,6 +69,10 @@ subprojects { duplicatesStrategy = DuplicatesStrategy.INCLUDE // allow duplicates } + jacoco { + toolVersion = "0.8.6" + } + tasks.withType(AbstractArchiveTask) { preserveFileTimestamps = false diff --git a/chainbase/build.gradle b/chainbase/build.gradle index 737ea77d1a4..bc82d9496c3 100644 --- a/chainbase/build.gradle +++ b/chainbase/build.gradle @@ -2,7 +2,6 @@ description = "chainbase – a decentralized database for blockchain." // Dependency versions // --------------------------------------- -def jacocoVersion = "0.8.0" def jansiVersion = "1.16" // -------------------------------------- @@ -41,10 +40,6 @@ test { } } -jacoco { - toolVersion = jacocoVersion // See http://www.eclemma.org/jacoco/. -} - jacocoTestReport { dependsOn(processResources) // explicit_dependency reports { diff --git a/common/build.gradle b/common/build.gradle index f84bf529f81..f15591e3572 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -4,7 +4,6 @@ plugins { version '1.0.0' -sourceCompatibility = 1.8 // Dependency versions // --------------------------------------- @@ -46,6 +45,7 @@ dependencies { api 'org.aspectj:aspectjrt:1.8.13' api 'org.aspectj:aspectjweaver:1.8.13' api 'org.aspectj:aspectjtools:1.8.13' + implementation 'com.github.zhang0125:tron-math:79b00fd78f' api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.1',{ exclude group: 'io.grpc', module: 'grpc-context' exclude group: 'io.grpc', module: 'grpc-core' diff --git a/crypto/build.gradle b/crypto/build.gradle index 82814af49e6..79dad6447e4 100644 --- a/crypto/build.gradle +++ b/crypto/build.gradle @@ -4,7 +4,6 @@ plugins { version '1.0.0' -sourceCompatibility = 1.8 repositories { mavenCentral() diff --git a/framework/build.gradle b/framework/build.gradle index 24e3a6c2a56..cbc0c4aa11d 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -14,10 +14,6 @@ def versions = [ checkstyle: '8.7', ] -jacoco { - toolVersion = "0.8.1" -} - configurations { checkstyleConfig diff --git a/plugins/build.gradle b/plugins/build.gradle index 741254912de..fac722e0eaa 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -5,9 +5,6 @@ plugins { apply plugin: 'application' apply plugin: 'checkstyle' -jacoco { - toolVersion = "0.8.4" -} def versions = [ checkstyle: '8.7', ] From 5ba7d491ed4f8b4bf564814ba226e841e777551f Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Thu, 23 May 2024 16:32:49 +0800 Subject: [PATCH 03/15] feat(math): add MathWrapper. --- .../AccountPermissionUpdateActuator.java | 3 +- .../core/actuator/ExchangeInjectActuator.java | 9 +- .../actuator/ExchangeWithdrawActuator.java | 8 - .../actuator/MarketSellAssetActuator.java | 14 +- .../ParticipateAssetIssueActuator.java | 17 +- .../actuator/ShieldedTransferActuator.java | 10 +- .../tron/core/actuator/TransferActuator.java | 7 +- .../core/actuator/TransferAssetActuator.java | 3 +- .../actuator/UnDelegateResourceActuator.java | 5 +- .../org/tron/core/actuator/VMActuator.java | 39 +-- .../org/tron/core/utils/TransactionUtil.java | 3 +- .../tron/core/vm/PrecompiledContracts.java | 8 +- .../main/java/org/tron/core/vm/VMUtils.java | 5 +- .../UnDelegateResourceProcessor.java | 5 +- .../java/org/tron/core/vm/program/Memory.java | 16 +- .../org/tron/core/vm/program/Program.java | 15 +- .../core/vm/repository/RepositoryImpl.java | 5 +- .../org/tron/core/vm/utils/FreezeV2Util.java | 11 +- .../java/org/tron/common/utils/Commons.java | 7 +- .../org/tron/common/utils/ForkController.java | 4 +- .../org/tron/core/capsule/AccountCapsule.java | 31 ++- .../tron/core/capsule/ContractCapsule.java | 5 +- .../core/capsule/ContractStateCapsule.java | 8 +- .../tron/core/capsule/ExchangeProcessor.java | 6 +- .../org/tron/core/capsule/ReceiptCapsule.java | 11 +- .../tron/core/capsule/utils/MarketUtils.java | 11 +- .../org/tron/core/db/EnergyProcessor.java | 4 +- .../org/tron/core/db/ResourceProcessor.java | 12 +- .../java/org/tron/core/db/StorageMarket.java | 234 ---------------- .../org/tron/core/db/TransactionTrace.java | 7 +- .../tron/core/service/MortgageService.java | 3 +- .../org/tron/core/store/AssetIssueStore.java | 7 - .../core/store/DynamicPropertiesStore.java | 7 +- .../org/tron/common/math/MathWrapper.java | 150 ++++++++++ .../tron/common/math/StrictMathWrapper.java | 222 +++++++++++++++ .../org/tron/common/runtime/vm/DataWord.java | 5 +- .../java/org/tron/common/utils/ByteUtil.java | 6 +- .../java/org/tron/common/math/MathTest.java | 19 ++ .../logsfilter/ContractEventParser.java | 3 +- .../src/main/java/org/tron/core/Wallet.java | 11 +- .../java/org/tron/core/capsule/utils/RLP.java | 3 +- .../java/org/tron/core/config/args/Args.java | 26 +- .../main/java/org/tron/core/db/Manager.java | 13 +- .../SyncBlockChainMsgHandler.java | 3 +- .../jsonrpc/filters/LogFilterWrapper.java | 3 +- .../main/java/org/tron/program/DBConvert.java | 4 +- .../tron/common/runtime/vm/FreezeV2Test.java | 3 +- .../tron/common/runtime/vm/MemoryTest.java | 4 +- .../PrecompiledContractsVerifyProofTest.java | 3 +- .../org/tron/common/runtime/vm/VoteTest.java | 3 +- .../common/utils/client/utils/AbiUtil.java | 3 +- .../common/utils/client/utils/DataWord.java | 3 +- .../java/org/tron/core/StorageMarketTest.java | 256 ------------------ .../actuator/utils/TransactionUtilTest.java | 13 +- .../capsule/utils/ExchangeProcessorTest.java | 6 + .../core/capsule/utils/MerkleTreeTest.java | 3 +- .../db/MarketPairPriceToOrderStoreTest.java | 3 +- .../core/jsonrpc/ConcurrentHashMapTest.java | 3 +- .../tron/core/zksnark/LibrustzcashTest.java | 3 +- .../tron/core/zksnark/SaplingNoteTest.java | 3 +- .../org/tron/keystroe/CredentialsTest.java | 1 - 61 files changed, 632 insertions(+), 686 deletions(-) delete mode 100644 chainbase/src/main/java/org/tron/core/db/StorageMarket.java create mode 100644 common/src/main/java/org/tron/common/math/MathWrapper.java create mode 100644 common/src/main/java/org/tron/common/math/StrictMathWrapper.java create mode 100644 common/src/test/java/org/tron/common/math/MathTest.java delete mode 100644 framework/src/test/java/org/tron/core/StorageMarketTest.java diff --git a/actuator/src/main/java/org/tron/core/actuator/AccountPermissionUpdateActuator.java b/actuator/src/main/java/org/tron/core/actuator/AccountPermissionUpdateActuator.java index fcc4d775d43..78a54a50ff6 100644 --- a/actuator/src/main/java/org/tron/core/actuator/AccountPermissionUpdateActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/AccountPermissionUpdateActuator.java @@ -8,6 +8,7 @@ import java.util.Objects; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.Commons; import org.tron.common.utils.DecodeUtil; import org.tron.core.capsule.AccountCapsule; @@ -111,7 +112,7 @@ private boolean checkPermission(Permission permission) throws ContractValidateEx throw new ContractValidateException("key's weight should be greater than 0"); } try { - weightSum = Math.addExact(weightSum, key.getWeight()); + weightSum = MathWrapper.addExact(weightSum, key.getWeight()); } catch (ArithmeticException e) { throw new ContractValidateException(e.getMessage()); } diff --git a/actuator/src/main/java/org/tron/core/actuator/ExchangeInjectActuator.java b/actuator/src/main/java/org/tron/core/actuator/ExchangeInjectActuator.java index 7848f898ced..20f29b117ce 100755 --- a/actuator/src/main/java/org/tron/core/actuator/ExchangeInjectActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/ExchangeInjectActuator.java @@ -9,6 +9,7 @@ import java.util.Arrays; import java.util.Objects; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.common.utils.Commons; import org.tron.common.utils.DecodeUtil; @@ -71,14 +72,14 @@ public boolean execute(Object object) throws ContractExeException { if (Arrays.equals(tokenID, firstTokenID)) { anotherTokenID = secondTokenID; - anotherTokenQuant = Math - .floorDiv(Math.multiplyExact(secondTokenBalance, tokenQuant), firstTokenBalance); + anotherTokenQuant = MathWrapper.floorDiv( + MathWrapper.multiplyExact(secondTokenBalance, tokenQuant), firstTokenBalance); exchangeCapsule.setBalance(firstTokenBalance + tokenQuant, secondTokenBalance + anotherTokenQuant); } else { anotherTokenID = firstTokenID; - anotherTokenQuant = Math - .floorDiv(Math.multiplyExact(firstTokenBalance, tokenQuant), secondTokenBalance); + anotherTokenQuant = MathWrapper.floorDiv( + MathWrapper.multiplyExact(firstTokenBalance, tokenQuant), secondTokenBalance); exchangeCapsule.setBalance(firstTokenBalance + anotherTokenQuant, secondTokenBalance + tokenQuant); } diff --git a/actuator/src/main/java/org/tron/core/actuator/ExchangeWithdrawActuator.java b/actuator/src/main/java/org/tron/core/actuator/ExchangeWithdrawActuator.java index 8929305d68c..fb8fe9384d3 100755 --- a/actuator/src/main/java/org/tron/core/actuator/ExchangeWithdrawActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/ExchangeWithdrawActuator.java @@ -76,16 +76,12 @@ public boolean execute(Object object) throws ContractExeException { BigInteger bigTokenQuant = new BigInteger(String.valueOf(tokenQuant)); if (Arrays.equals(tokenID, firstTokenID)) { anotherTokenID = secondTokenID; -// anotherTokenQuant = Math -// .floorDiv(Math.multiplyExact(secondTokenBalance, tokenQuant), firstTokenBalance); anotherTokenQuant = bigSecondTokenBalance.multiply(bigTokenQuant) .divide(bigFirstTokenBalance).longValueExact(); exchangeCapsule.setBalance(firstTokenBalance - tokenQuant, secondTokenBalance - anotherTokenQuant); } else { anotherTokenID = firstTokenID; -// anotherTokenQuant = Math -// .floorDiv(Math.multiplyExact(firstTokenBalance, tokenQuant), secondTokenBalance); anotherTokenQuant = bigFirstTokenBalance.multiply(bigTokenQuant) .divide(bigSecondTokenBalance).longValueExact(); exchangeCapsule.setBalance(firstTokenBalance - anotherTokenQuant, @@ -210,8 +206,6 @@ public boolean validate() throws ContractValidateException { BigDecimal bigSecondTokenBalance = new BigDecimal(String.valueOf(secondTokenBalance)); BigDecimal bigTokenQuant = new BigDecimal(String.valueOf(tokenQuant)); if (Arrays.equals(tokenID, firstTokenID)) { -// anotherTokenQuant = Math -// .floorDiv(Math.multiplyExact(secondTokenBalance, tokenQuant), firstTokenBalance); anotherTokenQuant = bigSecondTokenBalance.multiply(bigTokenQuant) .divideToIntegralValue(bigFirstTokenBalance).longValueExact(); if (firstTokenBalance < tokenQuant || secondTokenBalance < anotherTokenQuant) { @@ -230,8 +224,6 @@ public boolean validate() throws ContractValidateException { } } else { -// anotherTokenQuant = Math -// .floorDiv(Math.multiplyExact(firstTokenBalance, tokenQuant), secondTokenBalance); anotherTokenQuant = bigFirstTokenBalance.multiply(bigTokenQuant) .divideToIntegralValue(bigSecondTokenBalance).longValueExact(); if (secondTokenBalance < tokenQuant || firstTokenBalance < anotherTokenQuant) { diff --git a/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java b/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java index 15e5a98f86a..483a9f05f4b 100644 --- a/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java @@ -28,6 +28,7 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.Commons; import org.tron.common.utils.DecodeUtil; import org.tron.core.capsule.AccountCapsule; @@ -244,7 +245,7 @@ public boolean validate() throws ContractValidateException { long fee = calcFee(); if (Arrays.equals(sellTokenID, "_".getBytes())) { - if (ownerAccount.getBalance() < Math.addExact(sellTokenQuantity, fee)) { + if (ownerAccount.getBalance() < MathWrapper.addExact(sellTokenQuantity, fee)) { throw new ContractValidateException("No enough balance !"); } } else { @@ -447,7 +448,7 @@ private void matchSingleOrder(MarketOrderCapsule takerOrderCapsule, takerOrderCapsule.setSellTokenQuantityRemain(0); MarketUtils.updateOrderState(takerOrderCapsule, State.INACTIVE, marketAccountStore); - makerOrderCapsule.setSellTokenQuantityRemain(Math.subtractExact( + makerOrderCapsule.setSellTokenQuantityRemain(MathWrapper.subtractExact( makerOrderCapsule.getSellTokenQuantityRemain(), takerBuyTokenQuantityRemain)); } else { // taker > maker @@ -475,7 +476,7 @@ private void matchSingleOrder(MarketOrderCapsule takerOrderCapsule, return; } else { makerOrderCapsule.setSellTokenQuantityRemain(0); - takerOrderCapsule.setSellTokenQuantityRemain(Math.subtractExact( + takerOrderCapsule.setSellTokenQuantityRemain(MathWrapper.subtractExact( takerOrderCapsule.getSellTokenQuantityRemain(), makerBuyTokenQuantityReceive)); } } @@ -524,7 +525,8 @@ private MarketOrderCapsule createAndSaveOrder(AccountCapsule accountCapsule, private void transferBalanceOrToken(AccountCapsule accountCapsule) { if (Arrays.equals(sellTokenID, "_".getBytes())) { - accountCapsule.setBalance(Math.subtractExact(accountCapsule.getBalance(), sellTokenQuantity)); + accountCapsule.setBalance(MathWrapper.subtractExact( + accountCapsule.getBalance(), sellTokenQuantity)); } else { accountCapsule .reduceAssetAmountV2(sellTokenID, sellTokenQuantity, dynamicStore, assetIssueStore); @@ -537,7 +539,7 @@ private void addTrxOrToken(MarketOrderCapsule orderCapsule, long num, byte[] buyTokenId = orderCapsule.getBuyTokenId(); if (Arrays.equals(buyTokenId, "_".getBytes())) { - accountCapsule.setBalance(Math.addExact(accountCapsule.getBalance(), num)); + accountCapsule.setBalance(MathWrapper.addExact(accountCapsule.getBalance(), num)); } else { accountCapsule .addAssetAmountV2(buyTokenId, num, dynamicStore, assetIssueStore); @@ -550,7 +552,7 @@ private void addTrxOrToken(MarketOrderCapsule orderCapsule, long num) { byte[] buyTokenId = orderCapsule.getBuyTokenId(); if (Arrays.equals(buyTokenId, "_".getBytes())) { - accountCapsule.setBalance(Math.addExact(accountCapsule.getBalance(), num)); + accountCapsule.setBalance(MathWrapper.addExact(accountCapsule.getBalance(), num)); } else { accountCapsule .addAssetAmountV2(buyTokenId, num, dynamicStore, assetIssueStore); diff --git a/actuator/src/main/java/org/tron/core/actuator/ParticipateAssetIssueActuator.java b/actuator/src/main/java/org/tron/core/actuator/ParticipateAssetIssueActuator.java index 77e345b2a92..3744ecdbcf0 100755 --- a/actuator/src/main/java/org/tron/core/actuator/ParticipateAssetIssueActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/ParticipateAssetIssueActuator.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.Objects; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.common.utils.Commons; import org.tron.common.utils.DecodeUtil; @@ -64,8 +65,8 @@ public boolean execute(Object object) throws ContractExeException { //subtract from owner address byte[] ownerAddress = participateAssetIssueContract.getOwnerAddress().toByteArray(); AccountCapsule ownerAccount = accountStore.get(ownerAddress); - long balance = Math.subtractExact(ownerAccount.getBalance(), cost); - balance = Math.subtractExact(balance, fee); + long balance = MathWrapper.subtractExact(ownerAccount.getBalance(), cost); + balance = MathWrapper.subtractExact(balance, fee); ownerAccount.setBalance(balance); byte[] key = participateAssetIssueContract.getAssetName().toByteArray(); @@ -74,14 +75,14 @@ public boolean execute(Object object) throws ContractExeException { assetIssueCapsule = Commons .getAssetIssueStoreFinal(dynamicStore, assetIssueStore, assetIssueV2Store).get(key); - long exchangeAmount = Math.multiplyExact(cost, assetIssueCapsule.getNum()); - exchangeAmount = Math.floorDiv(exchangeAmount, assetIssueCapsule.getTrxNum()); + long exchangeAmount = MathWrapper.multiplyExact(cost, assetIssueCapsule.getNum()); + exchangeAmount = MathWrapper.floorDiv(exchangeAmount, assetIssueCapsule.getTrxNum()); ownerAccount.addAssetAmountV2(key, exchangeAmount, dynamicStore, assetIssueStore); //add to to_address byte[] toAddress = participateAssetIssueContract.getToAddress().toByteArray(); AccountCapsule toAccount = accountStore.get(toAddress); - toAccount.setBalance(Math.addExact(toAccount.getBalance(), cost)); + toAccount.setBalance(MathWrapper.addExact(toAccount.getBalance(), cost)); if (!toAccount.reduceAssetAmountV2(key, exchangeAmount, dynamicStore, assetIssueStore)) { throw new ContractExeException("reduceAssetAmount failed !"); } @@ -156,7 +157,7 @@ public boolean validate() throws ContractValidateException { try { //Whether the balance is enough long fee = calcFee(); - if (ownerAccount.getBalance() < Math.addExact(amount, fee)) { + if (ownerAccount.getBalance() < MathWrapper.addExact(amount, fee)) { throw new ContractValidateException("No enough balance !"); } @@ -181,8 +182,8 @@ public boolean validate() throws ContractValidateException { int trxNum = assetIssueCapsule.getTrxNum(); int num = assetIssueCapsule.getNum(); - long exchangeAmount = Math.multiplyExact(amount, num); - exchangeAmount = Math.floorDiv(exchangeAmount, trxNum); + long exchangeAmount = MathWrapper.multiplyExact(amount, num); + exchangeAmount = MathWrapper.floorDiv(exchangeAmount, trxNum); if (exchangeAmount <= 0) { throw new ContractValidateException("Can not process the exchange!"); } diff --git a/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java b/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java index 284650f1ffb..25948f88f71 100644 --- a/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java @@ -12,6 +12,7 @@ import java.util.Objects; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.Commons; import org.tron.common.utils.DecodeUtil; @@ -97,7 +98,7 @@ public boolean execute(Object result) //adjust and verify total shielded pool value try { Commons.adjustTotalShieldedPoolValue( - Math.addExact(Math.subtractExact(shieldedTransferContract.getToAmount(), + MathWrapper.addExact(MathWrapper.subtractExact(shieldedTransferContract.getToAmount(), shieldedTransferContract.getFromAmount()), fee), dynamicStore); } catch (ArithmeticException | BalanceInsufficientException e) { logger.debug(e.getMessage(), e); @@ -327,9 +328,10 @@ private void checkProof(List spendDescriptions, long totalShieldedPoolValue = dynamicStore .getTotalShieldedPoolValue(); try { - valueBalance = Math.addExact(Math.subtractExact(shieldedTransferContract.getToAmount(), + valueBalance = MathWrapper.addExact( + MathWrapper.subtractExact(shieldedTransferContract.getToAmount(), shieldedTransferContract.getFromAmount()), fee); - totalShieldedPoolValue = Math.subtractExact(totalShieldedPoolValue, valueBalance); + totalShieldedPoolValue = MathWrapper.subtractExact(totalShieldedPoolValue, valueBalance); } catch (ArithmeticException e) { logger.debug(e.getMessage(), e); throw new ZkProofValidateException(e.getMessage(), true); @@ -452,7 +454,7 @@ private void validateTransparent(ShieldedTransferContract shieldedTransferContra AccountCapsule toAccount = accountStore.get(toAddress); if (toAccount != null) { try { - Math.addExact(getZenBalance(toAccount), toAmount); + MathWrapper.addExact(getZenBalance(toAccount), toAmount); } catch (ArithmeticException e) { logger.debug(e.getMessage(), e); throw new ContractValidateException(e.getMessage()); diff --git a/actuator/src/main/java/org/tron/core/actuator/TransferActuator.java b/actuator/src/main/java/org/tron/core/actuator/TransferActuator.java index 5e3d605aed7..cd0be92a849 100755 --- a/actuator/src/main/java/org/tron/core/actuator/TransferActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/TransferActuator.java @@ -7,6 +7,7 @@ import java.util.Arrays; import java.util.Objects; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.Commons; import org.tron.common.utils.DecodeUtil; import org.tron.common.utils.StringUtil; @@ -58,7 +59,7 @@ public boolean execute(Object object) throws ContractExeException { fee = fee + dynamicStore.getCreateNewAccountFeeInSystemContract(); } - Commons.adjustBalance(accountStore, ownerAddress, -(Math.addExact(fee, amount))); + Commons.adjustBalance(accountStore, ownerAddress, -(MathWrapper.addExact(fee, amount))); if (dynamicStore.supportBlackHoleOptimization()) { dynamicStore.burnTrx(fee); } else { @@ -156,7 +157,7 @@ public boolean validate() throws ContractValidateException { } } - if (balance < Math.addExact(amount, fee)) { + if (balance < MathWrapper.addExact(amount, fee)) { logger.warn("Balance is not sufficient. Account: {}, balance: {}, amount: {}, fee: {}.", StringUtil.encode58Check(ownerAddress), balance, amount, fee); throw new ContractValidateException( @@ -164,7 +165,7 @@ public boolean validate() throws ContractValidateException { } if (toAccount != null) { - Math.addExact(toAccount.getBalance(), amount); + MathWrapper.addExact(toAccount.getBalance(), amount); } } catch (ArithmeticException e) { logger.debug(e.getMessage(), e); diff --git a/actuator/src/main/java/org/tron/core/actuator/TransferAssetActuator.java b/actuator/src/main/java/org/tron/core/actuator/TransferAssetActuator.java index de2b2faec86..7eb2627028d 100644 --- a/actuator/src/main/java/org/tron/core/actuator/TransferAssetActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/TransferAssetActuator.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.Objects; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.common.utils.Commons; import org.tron.common.utils.DecodeUtil; @@ -177,7 +178,7 @@ public boolean validate() throws ContractValidateException { assetBalance = toAccount.getAsset(dynamicStore, ByteArray.toStr(assetName)); if (assetBalance != null) { try { - assetBalance = Math.addExact(assetBalance, amount); //check if overflow + assetBalance = MathWrapper.addExact(assetBalance, amount); //check if overflow } catch (Exception e) { logger.debug(e.getMessage(), e); throw new ContractValidateException(e.getMessage()); diff --git a/actuator/src/main/java/org/tron/core/actuator/UnDelegateResourceActuator.java b/actuator/src/main/java/org/tron/core/actuator/UnDelegateResourceActuator.java index 79a09664180..f3f2adaf15c 100755 --- a/actuator/src/main/java/org/tron/core/actuator/UnDelegateResourceActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/UnDelegateResourceActuator.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.Objects; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.DecodeUtil; import org.tron.common.utils.StringUtil; import org.tron.core.capsule.AccountCapsule; @@ -82,7 +83,7 @@ public boolean execute(Object result) throws ContractExeException { * ((double) (dynamicStore.getTotalNetLimit()) / dynamicStore.getTotalNetWeight())); transferUsage = (long) (receiverCapsule.getNetUsage() * ((double) (unDelegateBalance) / receiverCapsule.getAllFrozenBalanceForBandwidth())); - transferUsage = Math.min(unDelegateMaxUsage, transferUsage); + transferUsage = MathWrapper.min(unDelegateMaxUsage, transferUsage); receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForBandwidth(-unDelegateBalance); } @@ -105,7 +106,7 @@ public boolean execute(Object result) throws ContractExeException { * ((double) (dynamicStore.getTotalEnergyCurrentLimit()) / dynamicStore.getTotalEnergyWeight())); transferUsage = (long) (receiverCapsule.getEnergyUsage() * ((double) (unDelegateBalance) / receiverCapsule.getAllFrozenBalanceForEnergy())); - transferUsage = Math.min(unDelegateMaxUsage, transferUsage); + transferUsage = MathWrapper.min(unDelegateMaxUsage, transferUsage); receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForEnergy(-unDelegateBalance); } diff --git a/actuator/src/main/java/org/tron/core/actuator/VMActuator.java b/actuator/src/main/java/org/tron/core/actuator/VMActuator.java index 326e2472757..82834670f14 100644 --- a/actuator/src/main/java/org/tron/core/actuator/VMActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/VMActuator.java @@ -1,7 +1,5 @@ package org.tron.core.actuator; -import static java.lang.Math.max; -import static java.lang.Math.min; import static org.apache.commons.lang3.ArrayUtils.getLength; import static org.apache.commons.lang3.ArrayUtils.isNotEmpty; import static org.tron.protos.contract.Common.ResourceCode.ENERGY; @@ -17,6 +15,7 @@ import org.apache.commons.lang3.StringUtils; import org.bouncycastle.util.encoders.Hex; import org.tron.common.logsfilter.trigger.ContractTrigger; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.runtime.InternalTransaction; import org.tron.common.runtime.InternalTransaction.ExecutorType; @@ -123,7 +122,7 @@ public void validate(Object object) throws ContractValidateException { trx = context.getTrxCap().getInstance(); // If tx`s fee limit is set, use it to calc max energy limit for constant call if (isConstantCall && trx.getRawData().getFeeLimit() > 0) { - maxEnergyLimit = Math.min(maxEnergyLimit, trx.getRawData().getFeeLimit() + maxEnergyLimit = MathWrapper.min(maxEnergyLimit, trx.getRawData().getFeeLimit() / context.getStoreFactory().getChainBaseManager() .getDynamicPropertiesStore().getEnergyFee()); } @@ -555,8 +554,8 @@ public long getAccountEnergyLimitWithFixRatio(AccountCapsule account, long feeLi receipt.setCallerEnergyLeft(leftFrozenEnergy); } - long energyFromBalance = max(account.getBalance() - callValue, 0) / sunPerEnergy; - long availableEnergy = Math.addExact(leftFrozenEnergy, energyFromBalance); + long energyFromBalance = MathWrapper.max(account.getBalance() - callValue, 0) / sunPerEnergy; + long availableEnergy = MathWrapper.addExact(leftFrozenEnergy, energyFromBalance); long energyFromFeeLimit = feeLimit / sunPerEnergy; if (VMConfig.allowTvmFreezeV2()) { @@ -572,12 +571,13 @@ public long getAccountEnergyLimitWithFixRatio(AccountCapsule account, long feeLi receipt.setCallerEnergyWindowSizeV2(account.getWindowSizeV2(ENERGY)); account.setEnergyUsage( energyProcessor.increase(account, ENERGY, - account.getEnergyUsage(), min(leftFrozenEnergy, energyFromFeeLimit), now, now)); + account.getEnergyUsage(), MathWrapper.min( + leftFrozenEnergy, energyFromFeeLimit), now, now)); receipt.setCallerEnergyMergedUsage(account.getEnergyUsage()); receipt.setCallerEnergyMergedWindowSize(account.getWindowSize(ENERGY)); rootRepository.updateAccount(account.createDbKey(), account); } - return min(availableEnergy, energyFromFeeLimit); + return MathWrapper.min(availableEnergy, energyFromFeeLimit); } @@ -590,9 +590,9 @@ private long getAccountEnergyLimitWithFloatRatio(AccountCapsule account, long fe } // can change the calc way long leftEnergyFromFreeze = rootRepository.getAccountLeftEnergyFromFreeze(account); - callValue = max(callValue, 0); - long energyFromBalance = Math - .floorDiv(max(account.getBalance() - callValue, 0), sunPerEnergy); + callValue = MathWrapper.max(callValue, 0); + long energyFromBalance = MathWrapper.floorDiv( + MathWrapper.max(account.getBalance() - callValue, 0), sunPerEnergy); long energyFromFeeLimit; long totalBalanceForEnergyFreeze = account.getAllFrozenBalanceForEnergy(); @@ -611,13 +611,13 @@ private long getAccountEnergyLimitWithFloatRatio(AccountCapsule account, long fe .multiply(BigInteger.valueOf(feeLimit)) .divide(BigInteger.valueOf(totalBalanceForEnergyFreeze)).longValueExact(); } else { - energyFromFeeLimit = Math - .addExact(leftEnergyFromFreeze, + energyFromFeeLimit = MathWrapper.addExact(leftEnergyFromFreeze, (feeLimit - leftBalanceForEnergyFreeze) / sunPerEnergy); } } - return min(Math.addExact(leftEnergyFromFreeze, energyFromBalance), energyFromFeeLimit); + return MathWrapper.min(MathWrapper.addExact( + leftEnergyFromFreeze, energyFromBalance), energyFromFeeLimit); } public long getTotalEnergyLimit(AccountCapsule creator, AccountCapsule caller, @@ -707,18 +707,18 @@ public long getTotalEnergyLimitWithFixRatio(AccountCapsule creator, AccountCapsu } } if (consumeUserResourcePercent <= 0) { - creatorEnergyLimit = min(originEnergyLeft, originEnergyLimit); + creatorEnergyLimit = MathWrapper.min(originEnergyLeft, originEnergyLimit); } else { if (consumeUserResourcePercent < VMConstant.ONE_HUNDRED) { // creatorEnergyLimit = // min(callerEnergyLimit * (100 - percent) / percent, // creatorLeftFrozenEnergy, originEnergyLimit) - creatorEnergyLimit = min( + creatorEnergyLimit = MathWrapper.min( BigInteger.valueOf(callerEnergyLimit) .multiply(BigInteger.valueOf(VMConstant.ONE_HUNDRED - consumeUserResourcePercent)) .divide(BigInteger.valueOf(consumeUserResourcePercent)).longValueExact(), - min(originEnergyLeft, originEnergyLimit) + MathWrapper.min(originEnergyLeft, originEnergyLimit) ); } } @@ -740,7 +740,7 @@ public long getTotalEnergyLimitWithFixRatio(AccountCapsule creator, AccountCapsu receipt.setOriginEnergyMergedWindowSize(creator.getWindowSize(ENERGY)); rootRepository.updateAccount(creator.createDbKey(), creator); } - return Math.addExact(callerEnergyLimit, creatorEnergyLimit); + return MathWrapper.addExact(callerEnergyLimit, creatorEnergyLimit); } private long getTotalEnergyLimitWithFloatRatio(AccountCapsule creator, AccountCapsule caller, @@ -760,9 +760,10 @@ private long getTotalEnergyLimitWithFloatRatio(AccountCapsule creator, AccountCa if (creatorEnergyLimit * consumeUserResourcePercent > (VMConstant.ONE_HUNDRED - consumeUserResourcePercent) * callerEnergyLimit) { - return Math.floorDiv(callerEnergyLimit * VMConstant.ONE_HUNDRED, consumeUserResourcePercent); + return MathWrapper.floorDiv( + callerEnergyLimit * VMConstant.ONE_HUNDRED, consumeUserResourcePercent); } else { - return Math.addExact(callerEnergyLimit, creatorEnergyLimit); + return MathWrapper.addExact(callerEnergyLimit, creatorEnergyLimit); } } diff --git a/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java b/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java index 7044564b1e1..5d5949e10bd 100644 --- a/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java @@ -36,6 +36,7 @@ import org.tron.api.GrpcAPI.TransactionExtention; import org.tron.api.GrpcAPI.TransactionSignWeight; import org.tron.api.GrpcAPI.TransactionSignWeight.Result; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.Sha256Hash; import org.tron.core.ChainBaseManager; @@ -270,7 +271,7 @@ public static long estimateConsumeBandWidthSize(DynamicPropertiesStore dps, long DelegateResourceContract.Builder builder2 = DelegateResourceContract.newBuilder() .setBalance(TRX_PRECISION); long builder2Size = builder2.build().getSerializedSize(); - long addSize = Math.max(builderSize - builder2Size, 0L); + long addSize = MathWrapper.max(builderSize - builder2Size, 0L); return DELEGATE_COST_BASE_SIZE + addSize; } diff --git a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java index be7b9423f5c..d7789f36053 100644 --- a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java +++ b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java @@ -46,6 +46,7 @@ import org.tron.common.crypto.zksnark.Fp; import org.tron.common.crypto.zksnark.PairingCheck; import org.tron.common.es.ExecutorServiceManager; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.runtime.ProgramResult; import org.tron.common.runtime.vm.DataWord; @@ -614,14 +615,15 @@ public long getEnergyForData(byte[] data) { int expLen = parseLen(data, 1); int modLen = parseLen(data, 2); - byte[] expHighBytes = parseBytes(data, addSafely(ARGS_OFFSET, baseLen), Math.min(expLen, 32)); + byte[] expHighBytes = parseBytes(data, addSafely(ARGS_OFFSET, baseLen), MathWrapper.min( + expLen, 32)); - long multComplexity = getMultComplexity(Math.max(baseLen, modLen)); + long multComplexity = getMultComplexity(MathWrapper.max(baseLen, modLen)); long adjExpLen = getAdjustedExponentLength(expHighBytes, expLen); // use big numbers to stay safe in case of overflow BigInteger energy = BigInteger.valueOf(multComplexity) - .multiply(BigInteger.valueOf(Math.max(adjExpLen, 1))) + .multiply(BigInteger.valueOf(MathWrapper.max(adjExpLen, 1))) .divide(GQUAD_DIVISOR); return isLessThan(energy, BigInteger.valueOf(Long.MAX_VALUE)) ? energy.longValueExact() diff --git a/actuator/src/main/java/org/tron/core/vm/VMUtils.java b/actuator/src/main/java/org/tron/core/vm/VMUtils.java index abdf6c7fe4c..74b56b20afd 100644 --- a/actuator/src/main/java/org/tron/core/vm/VMUtils.java +++ b/actuator/src/main/java/org/tron/core/vm/VMUtils.java @@ -16,6 +16,7 @@ import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; import org.tron.common.utils.Commons; @@ -164,7 +165,7 @@ public static boolean validateForSmartContract(Repository deposit, byte[] ownerA "Validate InternalTransfer error, balance is not sufficient."); } - Math.addExact(toAccount.getBalance(), amount); + MathWrapper.addExact(toAccount.getBalance(), amount); } catch (ArithmeticException e) { logger.debug(e.getMessage(), e); throw new ContractValidateException(e.getMessage()); @@ -225,7 +226,7 @@ public static boolean validateForSmartContract(Repository deposit, byte[] ownerA ByteArray.toStr(tokenIdWithoutLeadingZero)); if (assetBalance != null) { try { - assetBalance = Math.addExact(assetBalance, amount); //check if overflow + assetBalance = MathWrapper.addExact(assetBalance, amount); //check if overflow } catch (Exception e) { logger.debug(e.getMessage(), e); throw new ContractValidateException(e.getMessage()); diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/UnDelegateResourceProcessor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/UnDelegateResourceProcessor.java index d521e596e3e..346d2196cc3 100644 --- a/actuator/src/main/java/org/tron/core/vm/nativecontract/UnDelegateResourceProcessor.java +++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/UnDelegateResourceProcessor.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.Objects; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.DecodeUtil; import org.tron.common.utils.StringUtil; import org.tron.core.ChainBaseManager; @@ -115,7 +116,7 @@ public void execute(UnDelegateResourceParam param, Repository repo) { * dynamicStore.getTotalNetLimit() / repo.getTotalNetWeight()); transferUsage = (long) (receiverCapsule.getNetUsage() * ((double) (unDelegateBalance) / receiverCapsule.getAllFrozenBalanceForBandwidth())); - transferUsage = Math.min(unDelegateMaxUsage, transferUsage); + transferUsage = MathWrapper.min(unDelegateMaxUsage, transferUsage); receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForBandwidth(-unDelegateBalance); } @@ -139,7 +140,7 @@ public void execute(UnDelegateResourceParam param, Repository repo) { * dynamicStore.getTotalEnergyCurrentLimit() / repo.getTotalEnergyWeight()); transferUsage = (long) (receiverCapsule.getEnergyUsage() * ((double) (unDelegateBalance) / receiverCapsule.getAllFrozenBalanceForEnergy())); - transferUsage = Math.min(unDelegateMaxUsage, transferUsage); + transferUsage = MathWrapper.min(unDelegateMaxUsage, transferUsage); receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForEnergy(-unDelegateBalance); } diff --git a/actuator/src/main/java/org/tron/core/vm/program/Memory.java b/actuator/src/main/java/org/tron/core/vm/program/Memory.java index e5cbebad2b9..6117924b508 100644 --- a/actuator/src/main/java/org/tron/core/vm/program/Memory.java +++ b/actuator/src/main/java/org/tron/core/vm/program/Memory.java @@ -1,13 +1,13 @@ package org.tron.core.vm.program; -import static java.lang.Math.ceil; -import static java.lang.Math.min; import static java.lang.String.format; import static org.tron.common.utils.ByteUtil.EMPTY_BYTE_ARRAY; import static org.tron.common.utils.ByteUtil.oneByteToHexString; import java.util.LinkedList; import java.util.List; + +import org.tron.common.math.MathWrapper; import org.tron.common.runtime.vm.DataWord; import org.tron.core.vm.program.listener.ProgramListener; import org.tron.core.vm.program.listener.ProgramListenerAware; @@ -104,16 +104,16 @@ public void extend(int address, int size) { return; } - final int newSize = Math.addExact(address, size); + final int newSize = MathWrapper.addExact(address, size); int toAllocate = newSize - internalSize(); if (toAllocate > 0) { - addChunks((int) ceil((double) toAllocate / CHUNK_SIZE)); + addChunks((int) MathWrapper.ceil((double) toAllocate / CHUNK_SIZE)); } toAllocate = newSize - softSize; if (toAllocate > 0) { - toAllocate = (int) ceil((double) toAllocate / WORD_SIZE) * WORD_SIZE; - softSize = Math.addExact(softSize, toAllocate); + toAllocate = (int) MathWrapper.ceil((double) toAllocate / WORD_SIZE) * WORD_SIZE; + softSize = MathWrapper.addExact(softSize, toAllocate); if (programListener != null) { programListener.onMemoryExtend(toAllocate); @@ -184,7 +184,7 @@ public List getChunks() { private int captureMax(int chunkIndex, int chunkOffset, int size, byte[] src, int srcPos) { byte[] chunk = chunks.get(chunkIndex); - int toCapture = min(size, chunk.length - chunkOffset); + int toCapture = MathWrapper.min(size, chunk.length - chunkOffset); System.arraycopy(src, srcPos, chunk, chunkOffset, toCapture); return toCapture; @@ -193,7 +193,7 @@ private int captureMax(int chunkIndex, int chunkOffset, int size, byte[] src, in private int grabMax(int chunkIndex, int chunkOffset, int size, byte[] dest, int destPos) { byte[] chunk = chunks.get(chunkIndex); - int toGrab = min(size, chunk.length - chunkOffset); + int toGrab = MathWrapper.min(size, chunk.length - chunkOffset); System.arraycopy(chunk, chunkOffset, dest, destPos, toGrab); diff --git a/actuator/src/main/java/org/tron/core/vm/program/Program.java b/actuator/src/main/java/org/tron/core/vm/program/Program.java index e02ba225c6b..05614b82bcf 100644 --- a/actuator/src/main/java/org/tron/core/vm/program/Program.java +++ b/actuator/src/main/java/org/tron/core/vm/program/Program.java @@ -30,6 +30,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.bouncycastle.util.encoders.Hex; import org.tron.common.crypto.Hash; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.runtime.InternalTransaction; import org.tron.common.runtime.ProgramResult; @@ -624,7 +625,7 @@ private void withdrawRewardAndCancelVote(byte[] owner, Repository repo) { long balance = ownerCapsule.getBalance(); long allowance = ownerCapsule.getAllowance(); ownerCapsule.setInstance(ownerCapsule.getInstance().toBuilder() - .setBalance(Math.addExact(balance, allowance)) + .setBalance(MathWrapper.addExact(balance, allowance)) .setAllowance(0) .setLatestWithdrawTime(getTimestamp().longValue() * 1000) .build()); @@ -1222,7 +1223,7 @@ public DataWord getContractAddress() { public DataWord getBlockHash(int index) { if (index < this.getNumber().longValue() - && index >= Math.max(256, this.getNumber().longValue()) - 256) { + && index >= MathWrapper.max(256, this.getNumber().longValue()) - 256) { BlockCapsule blockCapsule = contractState.getBlockByNum(index); @@ -2147,10 +2148,12 @@ public boolean voteWitness(int witnessArrayOffset, int witnessArrayLength, VoteWitnessParam param = new VoteWitnessParam(); param.setVoterAddress(owner); - byte[] witnessArrayData = memoryChunk(Math.addExact(witnessArrayOffset, DataWord.WORD_SIZE), - Math.multiplyExact(witnessArrayLength, DataWord.WORD_SIZE)); - byte[] amountArrayData = memoryChunk(Math.addExact(amountArrayOffset, DataWord.WORD_SIZE), - Math.multiplyExact(amountArrayLength, DataWord.WORD_SIZE)); + byte[] witnessArrayData = memoryChunk(MathWrapper.addExact( + witnessArrayOffset, DataWord.WORD_SIZE), + MathWrapper.multiplyExact(witnessArrayLength, DataWord.WORD_SIZE)); + byte[] amountArrayData = memoryChunk(MathWrapper.addExact( + amountArrayOffset, DataWord.WORD_SIZE), + MathWrapper.multiplyExact(amountArrayLength, DataWord.WORD_SIZE)); for (int i = 0; i < witnessArrayLength; i++) { DataWord witness = new DataWord(Arrays.copyOfRange(witnessArrayData, diff --git a/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java b/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java index 50fa5385c23..ad91828c02c 100644 --- a/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java +++ b/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java @@ -13,6 +13,7 @@ import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; import org.tron.common.crypto.Hash; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.runtime.vm.DataWord; import org.tron.common.utils.ByteArray; @@ -681,7 +682,7 @@ public long addBalance(byte[] address, long value) { StringUtil.createReadableString(accountCapsule.createDbKey()) + " insufficient balance"); } - accountCapsule.setBalance(Math.addExact(balance, value)); + accountCapsule.setBalance(MathWrapper.addExact(balance, value)); Key key = Key.create(address); accountCache.put(key, Value.create(accountCapsule, accountCache.get(key).getType().addType(Type.DIRTY))); @@ -841,7 +842,7 @@ private long increase(long lastUsage, long usage, long lastTime, long now, long if (lastTime + windowSize > now) { long delta = now - lastTime; double decay = (windowSize - delta) / (double) windowSize; - averageLastUsage = Math.round(averageLastUsage * decay); + averageLastUsage = MathWrapper.round(averageLastUsage * decay); } else { averageLastUsage = 0; } diff --git a/actuator/src/main/java/org/tron/core/vm/utils/FreezeV2Util.java b/actuator/src/main/java/org/tron/core/vm/utils/FreezeV2Util.java index 7bc760f9edf..596825fcea8 100644 --- a/actuator/src/main/java/org/tron/core/vm/utils/FreezeV2Util.java +++ b/actuator/src/main/java/org/tron/core/vm/utils/FreezeV2Util.java @@ -5,6 +5,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; +import org.tron.common.math.MathWrapper; import org.tron.core.actuator.UnfreezeBalanceV2Actuator; import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.DelegatedResourceCapsule; @@ -162,7 +163,7 @@ public static long queryDelegatableResource(byte[] address, long type, Repositor } long v2NetUsage = getV2NetUsage(accountCapsule, usage); - return Math.max(0L, frozenV2Resource - v2NetUsage); + return MathWrapper.max(0L, frozenV2Resource - v2NetUsage); } if (type == 1) { @@ -182,7 +183,7 @@ public static long queryDelegatableResource(byte[] address, long type, Repositor } long v2EnergyUsage = getV2EnergyUsage(accountCapsule, usage); - return Math.max(0L, frozenV2Resource - v2EnergyUsage); + return MathWrapper.max(0L, frozenV2Resource - v2EnergyUsage); } return 0L; @@ -218,7 +219,7 @@ public static Triple checkUndelegateResource(byte[] address, l return Triple.of(0L, 0L, 0L); } - amount = Math.min(amount, resourceLimit); + amount = MathWrapper.min(amount, resourceLimit); if (resourceLimit <= usagePair.getLeft()) { return Triple.of(0L, amount, usagePair.getRight()); } @@ -243,7 +244,7 @@ public static long getV2NetUsage(AccountCapsule ownerCapsule, long netUsage) { - ownerCapsule.getFrozenBalance() - ownerCapsule.getAcquiredDelegatedFrozenBalanceForBandwidth() - ownerCapsule.getAcquiredDelegatedFrozenV2BalanceForBandwidth(); - return Math.max(0, v2NetUsage); + return MathWrapper.max(0, v2NetUsage); } public static long getV2EnergyUsage(AccountCapsule ownerCapsule, long energyUsage) { @@ -251,7 +252,7 @@ public static long getV2EnergyUsage(AccountCapsule ownerCapsule, long energyUsag - ownerCapsule.getEnergyFrozenBalance() - ownerCapsule.getAcquiredDelegatedFrozenBalanceForEnergy() - ownerCapsule.getAcquiredDelegatedFrozenV2BalanceForEnergy(); - return Math.max(0, v2EnergyUsage); + return MathWrapper.max(0, v2EnergyUsage); } } diff --git a/chainbase/src/main/java/org/tron/common/utils/Commons.java b/chainbase/src/main/java/org/tron/common/utils/Commons.java index 55542d494b4..442903b55a4 100644 --- a/chainbase/src/main/java/org/tron/common/utils/Commons.java +++ b/chainbase/src/main/java/org/tron/common/utils/Commons.java @@ -2,6 +2,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.ExchangeCapsule; @@ -77,7 +78,7 @@ public static void adjustBalance(AccountStore accountStore, AccountCapsule accou String.format("%s insufficient balance, balance: %d, amount: %d", StringUtil.createReadableString(account.createDbKey()), balance, -amount)); } - account.setBalance(Math.addExact(balance, amount)); + account.setBalance(MathWrapper.addExact(balance, amount)); accountStore.put(account.getAddress().toByteArray(), account); } @@ -137,8 +138,8 @@ public static void adjustAssetBalanceV2(AccountCapsule account, String AssetID, public static void adjustTotalShieldedPoolValue(long valueBalance, DynamicPropertiesStore dynamicPropertiesStore) throws BalanceInsufficientException { - long totalShieldedPoolValue = Math - .subtractExact(dynamicPropertiesStore.getTotalShieldedPoolValue(), valueBalance); + long totalShieldedPoolValue = MathWrapper.subtractExact( + dynamicPropertiesStore.getTotalShieldedPoolValue(), valueBalance); if (totalShieldedPoolValue < 0) { throw new BalanceInsufficientException(String.format( "total shielded pool value can not below 0, actual: %d", totalShieldedPoolValue)); diff --git a/chainbase/src/main/java/org/tron/common/utils/ForkController.java b/chainbase/src/main/java/org/tron/common/utils/ForkController.java index 7cbac28e781..a289282252f 100644 --- a/chainbase/src/main/java/org/tron/common/utils/ForkController.java +++ b/chainbase/src/main/java/org/tron/common/utils/ForkController.java @@ -14,6 +14,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.core.ChainBaseManager; import org.tron.core.capsule.BlockCapsule; @@ -98,8 +99,7 @@ private boolean passNew(int version) { ++count; } } - return count >= Math - .ceil((double) versionEnum.getHardForkRate() * stats.length / 100); + return count >= MathWrapper.ceil((double) versionEnum.getHardForkRate() * stats.length / 100); } diff --git a/chainbase/src/main/java/org/tron/core/capsule/AccountCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/AccountCapsule.java index b60fed63cda..7374ccba193 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/AccountCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/AccountCapsule.java @@ -20,6 +20,7 @@ import com.google.protobuf.InvalidProtocolBufferException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.core.capsule.utils.AssetUtil; import org.tron.core.store.AssetIssueStore; @@ -42,7 +43,6 @@ import java.util.Map; import java.util.Objects; -import static java.lang.Math.ceil; import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL; import static org.tron.core.config.Parameter.ChainConstant.WINDOW_SIZE_MS; import static org.tron.core.config.Parameter.ChainConstant.WINDOW_SIZE_PRECISION; @@ -403,13 +403,15 @@ public void addAcquiredDelegatedFrozenV2BalanceForBandwidth(long balance) { public void safeAddAcquiredDelegatedFrozenBalanceForBandwidth(long balance) { this.account = this.account.toBuilder().setAcquiredDelegatedFrozenBalanceForBandwidth( - Math.max(0, this.account.getAcquiredDelegatedFrozenBalanceForBandwidth() + balance)) + MathWrapper.max( + 0, this.account.getAcquiredDelegatedFrozenBalanceForBandwidth() + balance)) .build(); } public void safeAddAcquiredDelegatedFrozenV2BalanceForBandwidth(long balance) { this.account = this.account.toBuilder().setAcquiredDelegatedFrozenV2BalanceForBandwidth( - Math.max(0, this.account.getAcquiredDelegatedFrozenV2BalanceForBandwidth() + balance)) + MathWrapper.max( + 0, this.account.getAcquiredDelegatedFrozenV2BalanceForBandwidth() + balance)) .build(); } @@ -499,7 +501,8 @@ public void addAcquiredDelegatedFrozenV2BalanceForEnergy(long balance) { public void safeAddAcquiredDelegatedFrozenBalanceForEnergy(long balance) { AccountResource newAccountResource = getAccountResource().toBuilder() .setAcquiredDelegatedFrozenBalanceForEnergy( - Math.max(0, getAccountResource().getAcquiredDelegatedFrozenBalanceForEnergy() + balance)) + MathWrapper.max( + 0, getAccountResource().getAcquiredDelegatedFrozenBalanceForEnergy() + balance)) .build(); this.account = this.account.toBuilder() @@ -509,7 +512,7 @@ public void safeAddAcquiredDelegatedFrozenBalanceForEnergy(long balance) { public void safeAddAcquiredDelegatedFrozenV2BalanceForEnergy(long balance) { AccountResource newAccountResource = getAccountResource().toBuilder() - .setAcquiredDelegatedFrozenV2BalanceForEnergy(Math.max(0, getAccountResource() + .setAcquiredDelegatedFrozenV2BalanceForEnergy(MathWrapper.max(0, getAccountResource() .getAcquiredDelegatedFrozenV2BalanceForEnergy() + balance)).build(); this.account = this.account.toBuilder().setAccountResource(newAccountResource).build(); } @@ -718,8 +721,8 @@ public boolean addAssetAmount(byte[] key, long amount) { if (currentAmount == null) { currentAmount = 0L; } - this.account = this.account.toBuilder().putAsset(nameKey, Math.addExact(currentAmount, amount)) - .build(); + this.account = this.account.toBuilder().putAsset(nameKey, MathWrapper.addExact( + currentAmount, amount)).build(); return true; } @@ -737,8 +740,8 @@ public boolean addAssetAmountV2(byte[] key, long amount, currentAmount = 0L; } this.account = this.account.toBuilder() - .putAsset(nameKey, Math.addExact(currentAmount, amount)) - .putAssetV2(tokenID, Math.addExact(currentAmount, amount)) + .putAsset(nameKey, MathWrapper.addExact(currentAmount, amount)) + .putAssetV2(tokenID, MathWrapper.addExact(currentAmount, amount)) .build(); } //key is token id @@ -750,7 +753,7 @@ public boolean addAssetAmountV2(byte[] key, long amount, currentAmount = 0L; } this.account = this.account.toBuilder() - .putAssetV2(tokenIDStr, Math.addExact(currentAmount, amount)) + .putAssetV2(tokenIDStr, MathWrapper.addExact(currentAmount, amount)) .build(); } return true; @@ -762,7 +765,7 @@ public boolean reduceAssetAmount(byte[] key, long amount) { Long currentAmount = assetMap.get(nameKey); if (amount > 0 && null != currentAmount && amount <= currentAmount) { this.account = this.account.toBuilder() - .putAsset(nameKey, Math.subtractExact(currentAmount, amount)).build(); + .putAsset(nameKey, MathWrapper.subtractExact(currentAmount, amount)).build(); return true; } @@ -781,8 +784,8 @@ public boolean reduceAssetAmountV2(byte[] key, long amount, Long currentAmount = assetMap.get(nameKey); if (amount > 0 && null != currentAmount && amount <= currentAmount) { this.account = this.account.toBuilder() - .putAsset(nameKey, Math.subtractExact(currentAmount, amount)) - .putAssetV2(tokenID, Math.subtractExact(currentAmount, amount)) + .putAsset(nameKey, MathWrapper.subtractExact(currentAmount, amount)) + .putAssetV2(tokenID, MathWrapper.subtractExact(currentAmount, amount)) .build(); return true; } @@ -794,7 +797,7 @@ public boolean reduceAssetAmountV2(byte[] key, long amount, Long currentAmount = assetMapV2.get(tokenID); if (amount > 0 && null != currentAmount && amount <= currentAmount) { this.account = this.account.toBuilder() - .putAssetV2(tokenID, Math.subtractExact(currentAmount, amount)) + .putAssetV2(tokenID, MathWrapper.subtractExact(currentAmount, amount)) .build(); return true; } diff --git a/chainbase/src/main/java/org/tron/core/capsule/ContractCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/ContractCapsule.java index cb6292ed290..30784581bac 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/ContractCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/ContractCapsule.java @@ -15,13 +15,12 @@ package org.tron.core.capsule; -import static java.lang.Math.max; -import static java.lang.Math.min; import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.core.Constant; import org.tron.protos.Protocol.Transaction; import org.tron.protos.contract.SmartContractOuterClass.CreateSmartContract; @@ -111,7 +110,7 @@ public byte[] getOriginAddress() { public long getConsumeUserResourcePercent() { long percent = this.smartContract.getConsumeUserResourcePercent(); - return max(0, min(percent, Constant.ONE_HUNDRED)); + return MathWrapper.max(0, MathWrapper.min(percent, Constant.ONE_HUNDRED)); } public long getOriginEnergyLimit() { diff --git a/chainbase/src/main/java/org/tron/core/capsule/ContractStateCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/ContractStateCapsule.java index 8633534280b..de9f5bc04a5 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/ContractStateCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/ContractStateCapsule.java @@ -5,6 +5,7 @@ import com.google.protobuf.InvalidProtocolBufferException; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.core.store.DynamicPropertiesStore; import org.tron.protos.contract.SmartContractOuterClass; import org.tron.protos.contract.SmartContractOuterClass.ContractState; @@ -106,8 +107,7 @@ public boolean catchUpToCycle( double increasePercent = 1 + (double) increaseFactor / precisionFactor; this.contractState = ContractState.newBuilder() .setUpdateCycle(lastCycle) - .setEnergyFactor(Math.min( - maxFactor, + .setEnergyFactor(MathWrapper.min(maxFactor, (long) ((getEnergyFactor() + precisionFactor) * increasePercent) - precisionFactor)) .build(); } @@ -119,7 +119,7 @@ public boolean catchUpToCycle( } // Calc the decrease percent (decrease factor [75% ~ 100%]) - double decreasePercent = Math.pow( + double decreasePercent = MathWrapper.pow( 1 - (double) increaseFactor / DYNAMIC_ENERGY_DECREASE_DIVISION / precisionFactor, cycleCount ); @@ -130,7 +130,7 @@ public boolean catchUpToCycle( // That means we merge this special case to normal cases) this.contractState = ContractState.newBuilder() .setUpdateCycle(newCycle) - .setEnergyFactor(Math.max( + .setEnergyFactor(MathWrapper.max( 0, (long) ((getEnergyFactor() + precisionFactor) * decreasePercent) - precisionFactor)) .build(); diff --git a/chainbase/src/main/java/org/tron/core/capsule/ExchangeProcessor.java b/chainbase/src/main/java/org/tron/core/capsule/ExchangeProcessor.java index e1b536b3e7a..99a0d8d2c70 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/ExchangeProcessor.java +++ b/chainbase/src/main/java/org/tron/core/capsule/ExchangeProcessor.java @@ -1,6 +1,7 @@ package org.tron.core.capsule; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; @Slf4j(topic = "capsule") public class ExchangeProcessor { @@ -16,7 +17,8 @@ private long exchangeToSupply(long balance, long quant) { long newBalance = balance + quant; logger.debug("balance + quant: " + newBalance); - double issuedSupply = -supply * (1.0 - Math.pow(1.0 + (double) quant / newBalance, 0.0005)); + double issuedSupply = -supply * (1.0 - MathWrapper.powjdk8( + 1.0 + (double) quant / newBalance, 0.0005)); logger.debug("issuedSupply: " + issuedSupply); long out = (long) issuedSupply; supply += out; @@ -28,7 +30,7 @@ private long exchangeFromSupply(long balance, long supplyQuant) { supply -= supplyQuant; double exchangeBalance = - balance * (Math.pow(1.0 + (double) supplyQuant / supply, 2000.0) - 1.0); + balance * (MathWrapper.pow(1.0 + (double) supplyQuant / supply, 2000.0) - 1.0); logger.debug("exchangeBalance: " + exchangeBalance); return (long) exchangeBalance; diff --git a/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java index 7d003b6b0e4..1aa56a1dead 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java @@ -3,6 +3,7 @@ import java.util.Objects; import lombok.Getter; import lombok.Setter; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.Commons; import org.tron.common.utils.ForkController; @@ -220,7 +221,7 @@ public void payEnergyBill(DynamicPropertiesStore dynamicPropertiesStore, payEnergyBill(dynamicPropertiesStore, accountStore, forkController, caller, receipt.getEnergyUsageTotal(), receipt.getResult(), energyProcessor, now); } else { - long originUsage = Math.multiplyExact(receipt.getEnergyUsageTotal(), percent) / 100; + long originUsage = MathWrapper.multiplyExact(receipt.getEnergyUsageTotal(), percent) / 100; originUsage = getOriginUsage(dynamicPropertiesStore, origin, originEnergyLimit, energyProcessor, originUsage); @@ -239,14 +240,14 @@ private long getOriginUsage(DynamicPropertiesStore dynamicPropertiesStore, Accou if (dynamicPropertiesStore.getAllowTvmFreeze() == 1 || dynamicPropertiesStore.supportUnfreezeDelay()) { - return Math.min(originUsage, Math.min(originEnergyLeft, originEnergyLimit)); + return MathWrapper.min(originUsage, MathWrapper.min(originEnergyLeft, originEnergyLimit)); } if (checkForEnergyLimit(dynamicPropertiesStore)) { - return Math.min(originUsage, - Math.min(energyProcessor.getAccountLeftEnergyFromFreeze(origin), originEnergyLimit)); + return MathWrapper.min(originUsage, MathWrapper.min( + energyProcessor.getAccountLeftEnergyFromFreeze(origin), originEnergyLimit)); } - return Math.min(originUsage, energyProcessor.getAccountLeftEnergyFromFreeze(origin)); + return MathWrapper.min(originUsage, energyProcessor.getAccountLeftEnergyFromFreeze(origin)); } private void payEnergyBill( diff --git a/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java b/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java index f345a96df81..f6d1327f857 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java +++ b/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java @@ -19,6 +19,7 @@ import java.math.BigInteger; import java.util.Arrays; import org.tron.common.crypto.Hash; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; import org.tron.core.capsule.AccountCapsule; @@ -230,8 +231,8 @@ public static byte[] createPairKey(byte[] sellTokenId, byte[] buyTokenId) { public static int comparePrice(long price1SellQuantity, long price1BuyQuantity, long price2SellQuantity, long price2BuyQuantity) { try { - return Long.compare(Math.multiplyExact(price1BuyQuantity, price2SellQuantity), - Math.multiplyExact(price2BuyQuantity, price1SellQuantity)); + return Long.compare(MathWrapper.multiplyExact(price1BuyQuantity, price2SellQuantity), + MathWrapper.multiplyExact(price2BuyQuantity, price1SellQuantity)); } catch (ArithmeticException ex) { // do nothing here, because we will use BigInteger to compute again @@ -299,8 +300,8 @@ public static void updateOrderState(MarketOrderCapsule orderCapsule, public static long multiplyAndDivide(long a, long b, long c) { try { - long tmp = Math.multiplyExact(a, b); - return Math.floorDiv(tmp, c); + long tmp = MathWrapper.multiplyExact(a, b); + return MathWrapper.floorDiv(tmp, c); } catch (ArithmeticException ex) { // do nothing here, because we will use BigInteger to compute again } @@ -320,7 +321,7 @@ public static void returnSellTokenRemain(MarketOrderCapsule orderCapsule, byte[] sellTokenId = orderCapsule.getSellTokenId(); long sellTokenQuantityRemain = orderCapsule.getSellTokenQuantityRemain(); if (Arrays.equals(sellTokenId, "_".getBytes())) { - accountCapsule.setBalance(Math.addExact( + accountCapsule.setBalance(MathWrapper.addExact( accountCapsule.getBalance(), sellTokenQuantityRemain)); } else { accountCapsule diff --git a/chainbase/src/main/java/org/tron/core/db/EnergyProcessor.java b/chainbase/src/main/java/org/tron/core/db/EnergyProcessor.java index 5cd0f796374..d1209380a10 100644 --- a/chainbase/src/main/java/org/tron/core/db/EnergyProcessor.java +++ b/chainbase/src/main/java/org/tron/core/db/EnergyProcessor.java @@ -5,6 +5,7 @@ import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION; import lombok.extern.slf4j.Slf4j; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.TransactionCapsule; @@ -79,8 +80,7 @@ public void updateAdaptiveTotalEnergyLimit() { // logger.info(totalEnergyAverageUsage + "<" + targetTotalEnergyLimit + "\n" + result); } - result = Math.min( - Math.max(result, totalEnergyLimit), + result = MathWrapper.min(MathWrapper.max(result, totalEnergyLimit), totalEnergyLimit * dynamicPropertiesStore.getAdaptiveResourceLimitMultiplier() ); diff --git a/chainbase/src/main/java/org/tron/core/db/ResourceProcessor.java b/chainbase/src/main/java/org/tron/core/db/ResourceProcessor.java index a7a22390958..9e81497e510 100644 --- a/chainbase/src/main/java/org/tron/core/db/ResourceProcessor.java +++ b/chainbase/src/main/java/org/tron/core/db/ResourceProcessor.java @@ -3,6 +3,7 @@ import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL; import static org.tron.core.config.Parameter.ChainConstant.WINDOW_SIZE_PRECISION; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.Commons; import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.TransactionCapsule; @@ -50,7 +51,7 @@ protected long increase(long lastUsage, long usage, long lastTime, long now, lon if (lastTime + windowSize > now) { long delta = now - lastTime; double decay = (windowSize - delta) / (double) windowSize; - averageLastUsage = Math.round(averageLastUsage * decay); + averageLastUsage = MathWrapper.round(averageLastUsage * decay); } else { averageLastUsage = 0; } @@ -78,7 +79,7 @@ public long increase(AccountCapsule accountCapsule, ResourceCode resourceCode, if (lastTime + oldWindowSize > now) { long delta = now - lastTime; double decay = (oldWindowSize - delta) / (double) oldWindowSize; - averageLastUsage = Math.round(averageLastUsage * decay); + averageLastUsage = MathWrapper.round(averageLastUsage * decay); } else { averageLastUsage = 0; } @@ -110,7 +111,7 @@ public long increaseV2(AccountCapsule accountCapsule, ResourceCode resourceCode, if (lastTime + oldWindowSize > now) { long delta = now - lastTime; double decay = (oldWindowSize - delta) / (double) oldWindowSize; - averageLastUsage = Math.round(averageLastUsage * decay); + averageLastUsage = MathWrapper.round(averageLastUsage * decay); } else { averageLastUsage = 0; } @@ -126,7 +127,7 @@ public long increaseV2(AccountCapsule accountCapsule, ResourceCode resourceCode, long remainWindowSize = oldWindowSizeV2 - (now - lastTime) * WINDOW_SIZE_PRECISION; long newWindowSize = divideCeil( remainUsage * remainWindowSize + usage * this.windowSize * WINDOW_SIZE_PRECISION, newUsage); - newWindowSize = Math.min(newWindowSize, this.windowSize * WINDOW_SIZE_PRECISION); + newWindowSize = MathWrapper.min(newWindowSize, this.windowSize * WINDOW_SIZE_PRECISION); accountCapsule.setNewWindowSizeV2(resourceCode, newWindowSize); return newUsage; } @@ -188,7 +189,8 @@ public void unDelegateIncreaseV2(AccountCapsule owner, final AccountCapsule rece divideCeil( ownerUsage * remainOwnerWindowSizeV2 + transferUsage * remainReceiverWindowSizeV2, newOwnerUsage); - newOwnerWindowSize = Math.min(newOwnerWindowSize, this.windowSize * WINDOW_SIZE_PRECISION); + newOwnerWindowSize = MathWrapper.min( + newOwnerWindowSize, this.windowSize * WINDOW_SIZE_PRECISION); owner.setNewWindowSizeV2(resourceCode, newOwnerWindowSize); owner.setUsage(resourceCode, newOwnerUsage); owner.setLatestTime(resourceCode, now); diff --git a/chainbase/src/main/java/org/tron/core/db/StorageMarket.java b/chainbase/src/main/java/org/tron/core/db/StorageMarket.java deleted file mode 100644 index 10a3b656565..00000000000 --- a/chainbase/src/main/java/org/tron/core/db/StorageMarket.java +++ /dev/null @@ -1,234 +0,0 @@ -package org.tron.core.db; - -import lombok.extern.slf4j.Slf4j; -import org.tron.core.capsule.AccountCapsule; -import org.tron.core.store.AccountStore; -import org.tron.core.store.DynamicPropertiesStore; - -@Slf4j(topic = "DB") -public class StorageMarket { - private static final String LOG_MSG = "NewTotalPool: {}, newTotalReserved: {}."; - private static final long MS_PER_YEAR = 365 * 24 * 3600 * 1000L; - private AccountStore accountStore; - private DynamicPropertiesStore dynamicPropertiesStore; - private long supply = 1_000_000_000_000_000L; - - - public StorageMarket(AccountStore accountStore, DynamicPropertiesStore dynamicPropertiesStore) { - this.accountStore = accountStore; - this.dynamicPropertiesStore = dynamicPropertiesStore; - } - - private long exchangeToSupply(boolean isTRX, long quant) { - logger.info("IsTRX: {}.", isTRX); - long balance = isTRX ? dynamicPropertiesStore.getTotalStoragePool() : - dynamicPropertiesStore.getTotalStorageReserved(); - logger.info("Balance: {}.", balance); - long newBalance = balance + quant; - logger.info("Balance + quant: {}.", balance + quant); - -// if (isTRX) { -// dbManager.getDynamicPropertiesStore().saveTotalStoragePool(newBalance); -// } else { -// dbManager.getDynamicPropertiesStore().saveTotalStorageReserved(newBalance); -// } - - double issuedSupply = -supply * (1.0 - Math.pow(1.0 + (double) quant / newBalance, 0.0005)); - logger.info("IssuedSupply: {}.", issuedSupply); - long out = (long) issuedSupply; - supply += out; - - return out; - } - - private long exchangeToSupply2(boolean isTRX, long quant) { - logger.info("IsTRX: {}.", isTRX); - long balance = isTRX ? dynamicPropertiesStore.getTotalStoragePool() : - dynamicPropertiesStore.getTotalStorageReserved(); - logger.info("Balance: {}.", balance); - long newBalance = balance - quant; - logger.info("Balance - quant: {}.", balance - quant); - -// if (isTRX) { -// dbManager.getDynamicPropertiesStore().saveTotalStoragePool(newBalance); -// } else { -// dbManager.getDynamicPropertiesStore().saveTotalStorageReserved(newBalance); -// } - - double issuedSupply = -supply * (1.0 - Math.pow(1.0 + (double) quant / newBalance, 0.0005)); - logger.info("IssuedSupply: {}.", issuedSupply); - long out = (long) issuedSupply; - supply += out; - - return out; - } - - private long exchange_from_supply(boolean isTRX, long supplyQuant) { - long balance = isTRX ? dynamicPropertiesStore.getTotalStoragePool() : - dynamicPropertiesStore.getTotalStorageReserved(); - supply -= supplyQuant; - - double exchangeBalance = - balance * (Math.pow(1.0 + (double) supplyQuant / supply, 2000.0) - 1.0); - logger.info("ExchangeBalance: {}.", exchangeBalance); - long out = (long) exchangeBalance; - - if (isTRX) { - out = Math.round(exchangeBalance / 100000) * 100000; - logger.info("Out: {}.", out); - } - - return out; - } - - public long exchange(long from, boolean isTRX) { - long relay = exchangeToSupply(isTRX, from); - return exchange_from_supply(!isTRX, relay); - } - - public long calculateTax(long duration, long limit) { - // todo: Support for change by the committee - double ratePerYear = dynamicPropertiesStore.getStorageExchangeTaxRate() / 100.0; - double millisecondPerYear = MS_PER_YEAR; - double feeRate = duration / millisecondPerYear * ratePerYear; - long storageTax = (long) (limit * feeRate); - logger.info("StorageTax: {}.", storageTax); - return storageTax; - } - - - public long tryPayTax(long duration, long limit) { - long storageTax = calculateTax(duration, limit); - long tax = exchange(storageTax, false); - logger.info("Tax: {}.", tax); - - long newTotalTax = dynamicPropertiesStore.getTotalStorageTax() + tax; - long newTotalPool = dynamicPropertiesStore.getTotalStoragePool() - tax; - long newTotalReserved = dynamicPropertiesStore.getTotalStorageReserved() - + storageTax; - logger.info("Reserved: {}.", dynamicPropertiesStore.getTotalStorageReserved()); - boolean eq = dynamicPropertiesStore.getTotalStorageReserved() - == 128L * 1024 * 1024 * 1024; - logger.info("Reserved == 128GB: {}.", eq); - logger.info("NewTotalTax: {}, newTotalPool: {}, newTotalReserved: {}.", - newTotalTax, newTotalPool, newTotalReserved); - - return storageTax; - } - - public long payTax(long duration, long limit) { - long storageTax = calculateTax(duration, limit); - long tax = exchange(storageTax, false); - logger.info("Tax: {}.", tax); - - long newTotalTax = dynamicPropertiesStore.getTotalStorageTax() + tax; - long newTotalPool = dynamicPropertiesStore.getTotalStoragePool() - tax; - long newTotalReserved = dynamicPropertiesStore.getTotalStorageReserved() - + storageTax; - logger.info("Reserved: {}.", dynamicPropertiesStore.getTotalStorageReserved()); - boolean eq = dynamicPropertiesStore.getTotalStorageReserved() - == 128L * 1024 * 1024 * 1024; - logger.info("Reserved == 128GB: {}.", eq); - logger.info("NewTotalTax: {}, newTotalPool: {}, newTotalReserved: {}.", - newTotalTax, newTotalPool, newTotalReserved); - dynamicPropertiesStore.saveTotalStorageTax(newTotalTax); - dynamicPropertiesStore.saveTotalStoragePool(newTotalPool); - dynamicPropertiesStore.saveTotalStorageReserved(newTotalReserved); - - return storageTax; - } - - public long tryBuyStorageBytes(long storageBought) { - long relay = exchangeToSupply2(false, storageBought); - return exchange_from_supply(true, relay); - } - - public long tryBuyStorage(long quant) { - return exchange(quant, true); - } - - public long trySellStorage(long bytes) { - return exchange(bytes, false); - } - - public AccountCapsule buyStorageBytes(AccountCapsule accountCapsule, long storageBought) { - long now = dynamicPropertiesStore.getLatestBlockHeaderTimestamp(); - long currentStorageLimit = accountCapsule.getStorageLimit(); - - long relay = exchangeToSupply2(false, storageBought); - long quant = exchange_from_supply(true, relay); - - long newBalance = accountCapsule.getBalance() - quant; - logger.info("New balance: {}.", newBalance); - - long newStorageLimit = currentStorageLimit + storageBought; - logger.info("StorageBought: {}, newStorageLimit: {}.", storageBought, newStorageLimit); - - accountCapsule.setLatestExchangeStorageTime(now); - accountCapsule.setStorageLimit(newStorageLimit); - accountCapsule.setBalance(newBalance); - accountStore.put(accountCapsule.createDbKey(), accountCapsule); - - long newTotalPool = dynamicPropertiesStore.getTotalStoragePool() + quant; - long newTotalReserved = dynamicPropertiesStore.getTotalStorageReserved() - - storageBought; - logger.info(LOG_MSG, newTotalPool, newTotalReserved); - dynamicPropertiesStore.saveTotalStoragePool(newTotalPool); - dynamicPropertiesStore.saveTotalStorageReserved(newTotalReserved); - return accountCapsule; - } - - - public void buyStorage(AccountCapsule accountCapsule, long quant) { - long now = dynamicPropertiesStore.getLatestBlockHeaderTimestamp(); - long currentStorageLimit = accountCapsule.getStorageLimit(); - - long newBalance = accountCapsule.getBalance() - quant; - logger.info("New balance: {}.", newBalance); - - long storageBought = exchange(quant, true); - long newStorageLimit = currentStorageLimit + storageBought; - logger.info("StorageBought: {}, newStorageLimit: {}.", storageBought, newStorageLimit); - - accountCapsule.setLatestExchangeStorageTime(now); - accountCapsule.setStorageLimit(newStorageLimit); - accountCapsule.setBalance(newBalance); - accountStore.put(accountCapsule.createDbKey(), accountCapsule); - - long newTotalPool = dynamicPropertiesStore.getTotalStoragePool() + quant; - long newTotalReserved = dynamicPropertiesStore.getTotalStorageReserved() - - storageBought; - logger.info(LOG_MSG, newTotalPool, newTotalReserved); - dynamicPropertiesStore.saveTotalStoragePool(newTotalPool); - dynamicPropertiesStore.saveTotalStorageReserved(newTotalReserved); - - } - - public void sellStorage(AccountCapsule accountCapsule, long bytes) { - long now = dynamicPropertiesStore.getLatestBlockHeaderTimestamp(); - long currentStorageLimit = accountCapsule.getStorageLimit(); - - long quant = exchange(bytes, false); - long newBalance = accountCapsule.getBalance() + quant; - - long newStorageLimit = currentStorageLimit - bytes; - logger.info("Quant: {}, newStorageLimit: {}.", quant, newStorageLimit); - - accountCapsule.setLatestExchangeStorageTime(now); - accountCapsule.setStorageLimit(newStorageLimit); - accountCapsule.setBalance(newBalance); - accountStore.put(accountCapsule.createDbKey(), accountCapsule); - - long newTotalPool = dynamicPropertiesStore.getTotalStoragePool() - quant; - long newTotalReserved = dynamicPropertiesStore.getTotalStorageReserved() - + bytes; - logger.info(LOG_MSG, newTotalPool, newTotalReserved); - dynamicPropertiesStore.saveTotalStoragePool(newTotalPool); - dynamicPropertiesStore.saveTotalStorageReserved(newTotalReserved); - - } - - public long getAccountLeftStorageInByteFromBought(AccountCapsule accountCapsule) { - return accountCapsule.getStorageLimit() - accountCapsule.getStorageUsage(); - } -} diff --git a/chainbase/src/main/java/org/tron/core/db/TransactionTrace.java b/chainbase/src/main/java/org/tron/core/db/TransactionTrace.java index e9c4feb7e18..746d5baccde 100644 --- a/chainbase/src/main/java/org/tron/core/db/TransactionTrace.java +++ b/chainbase/src/main/java/org/tron/core/db/TransactionTrace.java @@ -10,6 +10,7 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; +import org.tron.common.math.MathWrapper; import org.tron.common.runtime.InternalTransaction.TrxType; import org.tron.common.runtime.ProgramResult; import org.tron.common.runtime.Runtime; @@ -244,9 +245,9 @@ public void pay() throws BalanceInsufficientException { callerAccount = callContract.getOwnerAddress().toByteArray(); originAccount = contractCapsule.getOriginAddress(); - percent = Math - .max(Constant.ONE_HUNDRED - contractCapsule.getConsumeUserResourcePercent(), 0); - percent = Math.min(percent, Constant.ONE_HUNDRED); + percent = MathWrapper.max( + Constant.ONE_HUNDRED - contractCapsule.getConsumeUserResourcePercent(), 0); + percent = MathWrapper.min(percent, Constant.ONE_HUNDRED); originEnergyLimit = contractCapsule.getOriginEnergyLimit(); break; default: diff --git a/chainbase/src/main/java/org/tron/core/service/MortgageService.java b/chainbase/src/main/java/org/tron/core/service/MortgageService.java index 805245d53f2..b42bc89166a 100644 --- a/chainbase/src/main/java/org/tron/core/service/MortgageService.java +++ b/chainbase/src/main/java/org/tron/core/service/MortgageService.java @@ -12,6 +12,7 @@ import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.Pair; import org.tron.common.utils.StringUtil; import org.tron.core.capsule.AccountCapsule; @@ -206,7 +207,7 @@ private long computeReward(long beginCycle, long endCycle, AccountCapsule accoun .map(vote -> new Pair<>(vote.getVoteAddress().toByteArray(), vote.getVoteCount())) .collect(Collectors.toList()); if (beginCycle < newAlgorithmCycle) { - long oldEndCycle = Math.min(endCycle, newAlgorithmCycle); + long oldEndCycle = MathWrapper.min(endCycle, newAlgorithmCycle); reward = getOldReward(beginCycle, oldEndCycle, srAddresses); beginCycle = oldEndCycle; } diff --git a/chainbase/src/main/java/org/tron/core/store/AssetIssueStore.java b/chainbase/src/main/java/org/tron/core/store/AssetIssueStore.java index d38a5f0677e..4f69a6c3c66 100644 --- a/chainbase/src/main/java/org/tron/core/store/AssetIssueStore.java +++ b/chainbase/src/main/java/org/tron/core/store/AssetIssueStore.java @@ -43,13 +43,6 @@ private List getAssetIssuesPaginated(List return null; } -// return Streams.stream(iterator()) -// .map(Entry::getValue) -// .sorted(Comparator.comparing(a -> a.getName().toStringUtf8(), String::compareTo)) -// .skip(offset) -// .limit(Math.min(limit, ASSET_ISSUE_COUNT_LIMIT_MAX)) -// .collect(Collectors.toList()); - if (assetIssueList.size() <= offset) { return null; } diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index bf788232640..57858e0c81b 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -13,6 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.ByteArray; import org.tron.common.utils.Sha256Hash; @@ -2235,7 +2236,7 @@ public void addTotalNetWeight(long amount) { long totalNetWeight = getTotalNetWeight(); totalNetWeight += amount; if (allowNewReward()) { - totalNetWeight = Math.max(0, totalNetWeight); + totalNetWeight = MathWrapper.max(0, totalNetWeight); } saveTotalNetWeight(totalNetWeight); } @@ -2248,7 +2249,7 @@ public void addTotalEnergyWeight(long amount) { long totalEnergyWeight = getTotalEnergyWeight(); totalEnergyWeight += amount; if (allowNewReward()) { - totalEnergyWeight = Math.max(0, totalEnergyWeight); + totalEnergyWeight = MathWrapper.max(0, totalEnergyWeight); } saveTotalEnergyWeight(totalEnergyWeight); } @@ -2261,7 +2262,7 @@ public void addTotalTronPowerWeight(long amount) { long totalWeight = getTotalTronPowerWeight(); totalWeight += amount; if (allowNewReward()) { - totalWeight = Math.max(0, totalWeight); + totalWeight = MathWrapper.max(0, totalWeight); } saveTotalTronPowerWeight(totalWeight); } diff --git a/common/src/main/java/org/tron/common/math/MathWrapper.java b/common/src/main/java/org/tron/common/math/MathWrapper.java new file mode 100644 index 00000000000..1f55f69c48b --- /dev/null +++ b/common/src/main/java/org/tron/common/math/MathWrapper.java @@ -0,0 +1,150 @@ +package org.tron.common.math; + +import com.google.common.base.Preconditions; +import lombok.extern.slf4j.Slf4j; +import org.tron.math.TronMath; + +@Slf4j(topic = "math") +public final class MathWrapper { + + static { + TronMath.loadLib(); + } + + private MathWrapper() { + throw new UnsupportedOperationException("MathWrapper should not be instantiated"); + } + + public static int abs(int a) { + return Math.abs(a); + } + + public static long abs(long a) { + return Math.abs(a); + } + + public static float abs(float a) { + return Math.abs(a); + } + + public static double abs(double a) { + return Math.abs(a); + } + + public static int max(int a, int b) { + return Math.max(a, b); + } + + public static long max(long a, long b) { + return Math.max(a, b); + } + + public static float max(float a, float b) { + return Math.max(a, b); + } + + public static double max(double a, double b) { + return Math.max(a, b); + } + + public static int min(int a, int b) { + return Math.min(a, b); + } + + public static long min(long a, long b) { + return Math.min(a, b); + } + + public static float min(float a, float b) { + return Math.min(a, b); + } + + public static double min(double a, double b) { + return Math.min(a, b); + } + + public static double signum(double d) { + return Math.signum(d); + } + + public static float signum(float f) { + return Math.signum(f); + } + + public static double ceil(double a) { + return Math.ceil(a); + } + + public static int round(float a) { + return Math.round(a); + } + + public static long round(double a) { + return Math.round(a); + } + + public static double random() { + return Math.random(); + } + + public static int addExact(int x, int y) { + return Math.addExact(x, y); + } + + public static long addExact(long x, long y) { + return Math.addExact(x, y); + } + + public static int subtractExact(int x, int y) { + return Math.subtractExact(x, y); + } + + public static long subtractExact(long x, long y) { + return Math.subtractExact(x, y); + } + + + public static int floorDiv(int x, int y) { + return Math.floorDiv(x, y); + } + + public static long floorDiv(long x, int y) { + return Math.floorDiv(x, y); + } + + public static long floorDiv(long x, long y) { + return Math.floorDiv(x, y); + } + + public static int multiplyExact(int x, int y) { + return Math.multiplyExact(x, y); + } + + public static long multiplyExact(long x, int y) { + return Math.multiplyExact(x, y); + } + + public static long multiplyExact(long x, long y) { + return Math.multiplyExact(x, y); + } + + + public static double exp(double a) { + return Math.exp(a); + } + + public static double pow(double a, double b) { + return Math.pow(a, b); + } + + /** + * NOTE: This is a partial implementation, only operates on 1 -1 && b < 1, "b must be -1 1 && a < 2, "a must be 1 0) { try { totalToAmount = receiveSize == 0 ? scaledToAmount - : (Math.addExact(scaledToAmount, shieldedReceives.get(0).getNote().getValue())); + : (MathWrapper.addExact(scaledToAmount, shieldedReceives.get(0).getNote().getValue())); } catch (ArithmeticException e) { throw new ZksnarkException("Unbalanced burn!"); } @@ -3648,7 +3649,7 @@ public ShieldedTRC20Parameters createShieldedContractParametersWithoutAsk( if (scaledToAmount > 0) { try { totalToAmount = receiveSize == 0 ? scaledToAmount - : Math.addExact(scaledToAmount, shieldedReceives.get(0).getNote().getValue()); + : MathWrapper.addExact(scaledToAmount, shieldedReceives.get(0).getNote().getValue()); } catch (ArithmeticException e) { throw new ZksnarkException("Unbalanced burn!"); } diff --git a/framework/src/main/java/org/tron/core/capsule/utils/RLP.java b/framework/src/main/java/org/tron/core/capsule/utils/RLP.java index 60a84cfd3d3..a37c9ad8779 100644 --- a/framework/src/main/java/org/tron/core/capsule/utils/RLP.java +++ b/framework/src/main/java/org/tron/core/capsule/utils/RLP.java @@ -17,6 +17,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tron.common.crypto.Hash; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteUtil; import org.tron.common.utils.Value; import org.tron.core.db.ByteArrayWrapper; @@ -49,7 +50,7 @@ public class RLP { /** * Allow for content up to size of 2^64 bytes * */ - private static final double MAX_ITEM_LENGTH = Math.pow(256, 8); + private static final double MAX_ITEM_LENGTH = MathWrapper.pow(256, 8); /** * Reason for threshold according to Vitalik Buterin: - 56 bytes maximizes the benefit of both * options - if we went with 60 then we would have only had 4 slots for long strings so RLP would diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index a8547b73948..2c0f047cbdf 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -1,6 +1,5 @@ package org.tron.core.config.args; -import static java.lang.Math.max; import static java.lang.System.exit; import static org.tron.core.Constant.ADD_PRE_FIX_BYTE_MAINNET; import static org.tron.core.Constant.DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE; @@ -54,6 +53,7 @@ import org.tron.common.logsfilter.TriggerConfig; import org.tron.common.logsfilter.trigger.ContractEventTrigger; import org.tron.common.logsfilter.trigger.ContractLogTrigger; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.parameter.RateLimiterInitialization; import org.tron.common.setting.RocksDbSettings; @@ -440,7 +440,7 @@ public static void setParam(final String[] args, final String confFileName) { if (config.hasPath(Constant.VM_MAX_ENERGY_LIMIT_FOR_CONSTANT)) { long configLimit = config.getLong(Constant.VM_MAX_ENERGY_LIMIT_FOR_CONSTANT); - PARAMETER.maxEnergyLimitForConstant = max(3_000_000L, configLimit); + PARAMETER.maxEnergyLimitForConstant = MathWrapper.max(3_000_000L, configLimit); } if (config.hasPath(Constant.VM_LRU_CACHE_SIZE)) { @@ -1124,8 +1124,8 @@ public static void setParam(final String[] args, final String confFileName) { if (config.hasPath(Constant.ALLOW_DELEGATE_OPTIMIZATION)) { PARAMETER.allowDelegateOptimization = config.getLong(Constant.ALLOW_DELEGATE_OPTIMIZATION); - PARAMETER.allowDelegateOptimization = Math.min(PARAMETER.allowDelegateOptimization, 1); - PARAMETER.allowDelegateOptimization = Math.max(PARAMETER.allowDelegateOptimization, 0); + PARAMETER.allowDelegateOptimization = MathWrapper.min(PARAMETER.allowDelegateOptimization, 1); + PARAMETER.allowDelegateOptimization = MathWrapper.max(PARAMETER.allowDelegateOptimization, 0); } if (config.hasPath(Constant.COMMITTEE_UNFREEZE_DELAY_DAYS)) { @@ -1140,33 +1140,33 @@ public static void setParam(final String[] args, final String confFileName) { if (config.hasPath(Constant.ALLOW_DYNAMIC_ENERGY)) { PARAMETER.allowDynamicEnergy = config.getLong(Constant.ALLOW_DYNAMIC_ENERGY); - PARAMETER.allowDynamicEnergy = Math.min(PARAMETER.allowDynamicEnergy, 1); - PARAMETER.allowDynamicEnergy = Math.max(PARAMETER.allowDynamicEnergy, 0); + PARAMETER.allowDynamicEnergy = MathWrapper.min(PARAMETER.allowDynamicEnergy, 1); + PARAMETER.allowDynamicEnergy = MathWrapper.max(PARAMETER.allowDynamicEnergy, 0); } if (config.hasPath(Constant.DYNAMIC_ENERGY_THRESHOLD)) { PARAMETER.dynamicEnergyThreshold = config.getLong(Constant.DYNAMIC_ENERGY_THRESHOLD); PARAMETER.dynamicEnergyThreshold - = Math.min(PARAMETER.dynamicEnergyThreshold, 100_000_000_000_000_000L); - PARAMETER.dynamicEnergyThreshold = Math.max(PARAMETER.dynamicEnergyThreshold, 0); + = MathWrapper.min(PARAMETER.dynamicEnergyThreshold, 100_000_000_000_000_000L); + PARAMETER.dynamicEnergyThreshold = MathWrapper.max(PARAMETER.dynamicEnergyThreshold, 0); } if (config.hasPath(Constant.DYNAMIC_ENERGY_INCREASE_FACTOR)) { PARAMETER.dynamicEnergyIncreaseFactor = config.getLong(Constant.DYNAMIC_ENERGY_INCREASE_FACTOR); PARAMETER.dynamicEnergyIncreaseFactor = - Math.min(PARAMETER.dynamicEnergyIncreaseFactor, DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE); + MathWrapper.min(PARAMETER.dynamicEnergyIncreaseFactor, DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE); PARAMETER.dynamicEnergyIncreaseFactor = - Math.max(PARAMETER.dynamicEnergyIncreaseFactor, 0); + MathWrapper.max(PARAMETER.dynamicEnergyIncreaseFactor, 0); } if (config.hasPath(Constant.DYNAMIC_ENERGY_MAX_FACTOR)) { PARAMETER.dynamicEnergyMaxFactor = config.getLong(Constant.DYNAMIC_ENERGY_MAX_FACTOR); PARAMETER.dynamicEnergyMaxFactor = - Math.min(PARAMETER.dynamicEnergyMaxFactor, DYNAMIC_ENERGY_MAX_FACTOR_RANGE); + MathWrapper.min(PARAMETER.dynamicEnergyMaxFactor, DYNAMIC_ENERGY_MAX_FACTOR_RANGE); PARAMETER.dynamicEnergyMaxFactor = - Math.max(PARAMETER.dynamicEnergyMaxFactor, 0); + MathWrapper.max(PARAMETER.dynamicEnergyMaxFactor, 0); } PARAMETER.dynamicConfigEnable = config.hasPath(Constant.DYNAMIC_CONFIG_ENABLE) @@ -1569,7 +1569,7 @@ private static void initRocksDbSettings(Config config) { ? config.getInt(prefix + "levelNumber") : 7; int compactThreads = config.hasPath(prefix + "compactThreads") ? config.getInt(prefix + "compactThreads") - : max(Runtime.getRuntime().availableProcessors(), 1); + : MathWrapper.max(Runtime.getRuntime().availableProcessors(), 1); int blocksize = config.hasPath(prefix + "blocksize") ? config.getInt(prefix + "blocksize") : 16; long maxBytesForLevelBase = config.hasPath(prefix + "maxBytesForLevelBase") diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 63bbef9ff7f..3fd1839f83e 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -63,6 +63,7 @@ import org.tron.common.logsfilter.trigger.ContractLogTrigger; import org.tron.common.logsfilter.trigger.ContractTrigger; import org.tron.common.logsfilter.trigger.Trigger; +import org.tron.common.math.MathWrapper; import org.tron.common.overlay.message.Message; import org.tron.common.parameter.CommonParameter; import org.tron.common.prometheus.MetricKeys; @@ -1803,8 +1804,8 @@ private void payReward(BlockCapsule block) { mortgageService.payStandbyWitness(); if (chainBaseManager.getDynamicPropertiesStore().supportTransactionFeePool()) { - long transactionFeeReward = Math - .floorDiv(chainBaseManager.getDynamicPropertiesStore().getTransactionFeePool(), + long transactionFeeReward = MathWrapper.floorDiv( + chainBaseManager.getDynamicPropertiesStore().getTransactionFeePool(), Constant.TRANSACTION_FEE_POOL_PERIOD); mortgageService.payTransactionFeeReward(witnessCapsule.getAddress().toByteArray(), transactionFeeReward); @@ -1819,8 +1820,8 @@ private void payReward(BlockCapsule block) { + chainBaseManager.getDynamicPropertiesStore().getWitnessPayPerBlock()); if (chainBaseManager.getDynamicPropertiesStore().supportTransactionFeePool()) { - long transactionFeeReward = Math - .floorDiv(chainBaseManager.getDynamicPropertiesStore().getTransactionFeePool(), + long transactionFeeReward = MathWrapper.floorDiv( + chainBaseManager.getDynamicPropertiesStore().getTransactionFeePool(), Constant.TRANSACTION_FEE_POOL_PERIOD); account.setAllowance(account.getAllowance() + transactionFeeReward); chainBaseManager.getDynamicPropertiesStore().saveTransactionFeePool( @@ -2402,8 +2403,8 @@ private void initLiteNode() { } transactionCount += trx.getTransactionIds().size(); long blockNum = trx.getNum(); - maxBlock = Math.max(maxBlock, blockNum); - minBlock = Math.min(minBlock, blockNum); + maxBlock = MathWrapper.max(maxBlock, blockNum); + minBlock = MathWrapper.min(minBlock, blockNum); item.setBlockNum(blockNum); trx.getTransactionIds().forEach( tid -> chainBaseManager.getTransactionStore().put(Hex.decode(tid), item)); diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java index 958ebfe5561..b09c4d2b457 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java @@ -6,6 +6,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.tron.common.math.MathWrapper; import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.config.Parameter.NetConstants; import org.tron.core.exception.P2pException; @@ -118,7 +119,7 @@ private BlockId getUnForkId(List blockIds) throws P2pException { private LinkedList getBlockIds(Long unForkNum, BlockId headID) throws P2pException { long headNum = headID.getNum(); - long len = Math.min(headNum, unForkNum + NetConstants.SYNC_FETCH_BATCH_NUM); + long len = MathWrapper.min(headNum, unForkNum + NetConstants.SYNC_FETCH_BATCH_NUM); LinkedList ids = new LinkedList<>(); for (long i = unForkNum; i <= len; i++) { diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterWrapper.java b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterWrapper.java index cf04a1769aa..eca1636b024 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterWrapper.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterWrapper.java @@ -3,6 +3,7 @@ import com.google.protobuf.ByteString; import lombok.Getter; import org.apache.commons.lang3.StringUtils; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; import org.tron.core.exception.JsonRpcInvalidParamsException; @@ -55,7 +56,7 @@ public LogFilterWrapper(FilterRequest fr, long currentMaxBlockNum, Wallet wallet if (toBlockSrc == -1) { toBlockSrc = Long.MAX_VALUE; } - fromBlockSrc = Math.min(toBlockSrc, currentMaxBlockNum); + fromBlockSrc = MathWrapper.min(toBlockSrc, currentMaxBlockNum); } else if (StringUtils.isNotEmpty(fr.getFromBlock()) && StringUtils.isEmpty(fr.getToBlock())) { diff --git a/framework/src/main/java/org/tron/program/DBConvert.java b/framework/src/main/java/org/tron/program/DBConvert.java index a13a2ffefb5..8afdd0f586b 100644 --- a/framework/src/main/java/org/tron/program/DBConvert.java +++ b/framework/src/main/java/org/tron/program/DBConvert.java @@ -31,6 +31,7 @@ import org.rocksdb.RocksDBException; import org.rocksdb.RocksIterator; import org.rocksdb.Status; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.FileUtil; import org.tron.common.utils.MarketOrderPriceComparatorForLevelDB; import org.tron.common.utils.MarketOrderPriceComparatorForRockDB; @@ -190,7 +191,8 @@ private Options newDefaultRocksDbOptions() { options.setTargetFileSizeBase(64 * 1024 * 1024); options.setTargetFileSizeMultiplier(1); options.setMaxBytesForLevelBase(512 * 1024 * 1024); - options.setMaxBackgroundCompactions(Math.max(1, Runtime.getRuntime().availableProcessors())); + options.setMaxBackgroundCompactions(MathWrapper.max( + 1, Runtime.getRuntime().availableProcessors())); options.setLevel0FileNumCompactionTrigger(4); options.setLevelCompactionDynamicLevelBytes(true); if ("market_pair_price_to_order".equalsIgnoreCase(this.dbName)) { diff --git a/framework/src/test/java/org/tron/common/runtime/vm/FreezeV2Test.java b/framework/src/test/java/org/tron/common/runtime/vm/FreezeV2Test.java index 9558c701109..de0cd78ab24 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/FreezeV2Test.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/FreezeV2Test.java @@ -22,6 +22,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.tron.common.application.TronApplicationContext; +import org.tron.common.math.MathWrapper; import org.tron.common.runtime.Runtime; import org.tron.common.runtime.RuntimeImpl; import org.tron.common.runtime.TVMTestResult; @@ -856,7 +857,7 @@ private TVMTestResult unDelegateResource( transferUsage = (long) (oldReceiver.getEnergyUsage() * ((double) (amount) / oldReceiver.getAllFrozenBalanceForEnergy())); } - transferUsage = Math.min(unDelegateMaxUsage, transferUsage); + transferUsage = MathWrapper.min(unDelegateMaxUsage, transferUsage); } DelegatedResourceStore delegatedResourceStore = manager.getDelegatedResourceStore(); diff --git a/framework/src/test/java/org/tron/common/runtime/vm/MemoryTest.java b/framework/src/test/java/org/tron/common/runtime/vm/MemoryTest.java index b6e9cfe109c..7d4d9a85f23 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/MemoryTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/MemoryTest.java @@ -18,7 +18,6 @@ package org.tron.common.runtime.vm; -import static java.lang.Math.ceil; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertTrue; @@ -27,6 +26,7 @@ import org.bouncycastle.util.encoders.Hex; import org.junit.Assert; import org.junit.Test; +import org.tron.common.math.MathWrapper; import org.tron.core.vm.program.Memory; @Slf4j @@ -43,7 +43,7 @@ private static void checkMemoryExtend(int dataSize) { } private static int calcSize(int dataSize, int chunkSize) { - return (int) ceil((double) dataSize / chunkSize) * chunkSize; + return (int) MathWrapper.ceil((double) dataSize / chunkSize) * chunkSize; } @Test diff --git a/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsVerifyProofTest.java b/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsVerifyProofTest.java index 833ed6b0ac3..924bdbc2811 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsVerifyProofTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsVerifyProofTest.java @@ -13,6 +13,7 @@ import org.junit.Test; import org.tron.api.GrpcAPI.ShieldedTRC20Parameters; import org.tron.common.BaseTest; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; import org.tron.common.utils.FileUtil; @@ -4504,7 +4505,7 @@ private byte[] longTo32Bytes(long value) { } private long randomLong() { - return Math.round(Math.random() * Long.MAX_VALUE / 2); + return MathWrapper.round(MathWrapper.random() * Long.MAX_VALUE / 2); } } diff --git a/framework/src/test/java/org/tron/common/runtime/vm/VoteTest.java b/framework/src/test/java/org/tron/common/runtime/vm/VoteTest.java index 1d85e9a7eab..4bb82a04b1d 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/VoteTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/VoteTest.java @@ -23,6 +23,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.tron.common.application.TronApplicationContext; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.runtime.Runtime; import org.tron.common.runtime.RuntimeImpl; @@ -863,7 +864,7 @@ private void checkRewardAndWithdraw(byte[] contract, boolean isZero) throws Exce long rewardBySystem = mortgageService.queryReward(contract); long beginCycle = manager.getDelegationStore().getBeginCycle(contract); long currentCycle = manager.getDynamicPropertiesStore().getCurrentCycleNumber(); - long passedCycle = Math.max(0, currentCycle - beginCycle); + long passedCycle = MathWrapper.max(0, currentCycle - beginCycle); Assert.assertTrue(isZero ? rewardBySystem == 0 : rewardBySystem > 0); triggerContract(contract, SUCCESS, getConsumer(">=", rewardBySystem) diff --git a/framework/src/test/java/org/tron/common/utils/client/utils/AbiUtil.java b/framework/src/test/java/org/tron/common/utils/client/utils/AbiUtil.java index 976490b8c80..b8fed1269f4 100644 --- a/framework/src/test/java/org/tron/common/utils/client/utils/AbiUtil.java +++ b/framework/src/test/java/org/tron/common/utils/client/utils/AbiUtil.java @@ -9,6 +9,7 @@ import org.apache.commons.lang3.StringUtils; import org.bouncycastle.util.encoders.Hex; import org.tron.common.crypto.Hash; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteUtil; import org.tron.common.utils.Commons; @@ -340,7 +341,7 @@ static class CoderNumber extends Coder { @Override byte[] encode(String value) { long n = Long.valueOf(value); - DataWord word = new DataWord(Math.abs(n)); + DataWord word = new DataWord(MathWrapper.abs(n)); if (n < 0) { word.negate(); } diff --git a/framework/src/test/java/org/tron/common/utils/client/utils/DataWord.java b/framework/src/test/java/org/tron/common/utils/client/utils/DataWord.java index 6c36f4d636a..2d8bcb33b7c 100644 --- a/framework/src/test/java/org/tron/common/utils/client/utils/DataWord.java +++ b/framework/src/test/java/org/tron/common/utils/client/utils/DataWord.java @@ -25,6 +25,7 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteUtil; import org.tron.common.utils.FastByteComparisons; @@ -466,7 +467,7 @@ public int compareTo(DataWord o) { data, 0, data.length, o.getData(), 0, o.getData().length); // Convert result into -1, 0 or 1 as is the convention - return (int) Math.signum(result); + return (int) MathWrapper.signum(result); } /** diff --git a/framework/src/test/java/org/tron/core/StorageMarketTest.java b/framework/src/test/java/org/tron/core/StorageMarketTest.java deleted file mode 100644 index 8b6e90e1c67..00000000000 --- a/framework/src/test/java/org/tron/core/StorageMarketTest.java +++ /dev/null @@ -1,256 +0,0 @@ -package org.tron.core; - -import static org.tron.core.config.Parameter.ChainConstant.TRANSFER_FEE; - -import com.google.protobuf.Any; -import com.google.protobuf.ByteString; -import lombok.extern.slf4j.Slf4j; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.tron.common.BaseTest; -import org.tron.common.utils.ByteArray; -import org.tron.core.capsule.AccountCapsule; -import org.tron.core.config.args.Args; -import org.tron.core.db.StorageMarket; -import org.tron.protos.Protocol.AccountType; -import org.tron.protos.contract.StorageContract.BuyStorageContract; - -@Slf4j -public class StorageMarketTest extends BaseTest { - - private static final String OWNER_ADDRESS; - private static final long initBalance = 10_000_000_000_000_000L; - private static StorageMarket storageMarket; - - static { - Args.setParam(new String[]{"--output-directory", dbPath()}, Constant.TEST_CONF); - OWNER_ADDRESS = Wallet.getAddressPreFixString() + "548794500882809695a8a687866e76d4271a1abc"; - } - - /** - * create temp Capsule test need. - */ - @Before - public void createAccountCapsule() { - storageMarket = new StorageMarket(dbManager.getAccountStore(), - dbManager.getDynamicPropertiesStore()); - - AccountCapsule ownerCapsule = - new AccountCapsule( - ByteString.copyFromUtf8("owner"), - ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS)), - AccountType.Normal, - initBalance); - dbManager.getAccountStore().put(ownerCapsule.getAddress().toByteArray(), ownerCapsule); - - dbManager.getDynamicPropertiesStore().saveTotalStorageReserved( - 128L * 1024 * 1024 * 1024); - dbManager.getDynamicPropertiesStore().saveTotalStoragePool(100_000_000_000000L); - dbManager.getDynamicPropertiesStore().saveTotalStorageTax(0); - - dbManager.getDynamicPropertiesStore().saveLatestBlockHeaderTimestamp(0); - } - - private Any getContract(String ownerAddress, long quant) { - return Any.pack( - BuyStorageContract.newBuilder() - .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString(ownerAddress))) - .setQuant(quant) - .build()); - } - - @Test - public void testBuyStorage() { - long currentPool = dbManager.getDynamicPropertiesStore().getTotalStoragePool(); - long currentReserved = dbManager.getDynamicPropertiesStore().getTotalStorageReserved(); - Assert.assertEquals(currentPool, 100_000_000_000000L); - Assert.assertEquals(currentReserved, 128L * 1024 * 1024 * 1024); - - AccountCapsule owner = - dbManager.getAccountStore().get(ByteArray.fromHexString(OWNER_ADDRESS)); - - long quant = 2_000_000_000_000L; // 2 million trx - storageMarket.buyStorage(owner, quant); - - Assert.assertEquals(owner.getBalance(), initBalance - quant - - TRANSFER_FEE); - Assert.assertEquals(2694881440L, owner.getStorageLimit()); - Assert.assertEquals(currentReserved - 2694881440L, - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(currentPool + quant, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - } - - @Test - public void testBuyStorage2() { - long currentPool = dbManager.getDynamicPropertiesStore().getTotalStoragePool(); - long currentReserved = dbManager.getDynamicPropertiesStore().getTotalStorageReserved(); - Assert.assertEquals(currentPool, 100_000_000_000000L); - Assert.assertEquals(currentReserved, 128L * 1024 * 1024 * 1024); - - AccountCapsule owner = - dbManager.getAccountStore().get(ByteArray.fromHexString(OWNER_ADDRESS)); - - long quant = 1_000_000_000_000L; // 1 million trx - - storageMarket.buyStorage(owner, quant); - - Assert.assertEquals(owner.getBalance(), initBalance - quant - - TRANSFER_FEE); - Assert.assertEquals(1360781717L, owner.getStorageLimit()); - Assert.assertEquals(currentReserved - 1360781717L, - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(currentPool + quant, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - storageMarket.buyStorage(owner, quant); - - Assert.assertEquals(owner.getBalance(), initBalance - 2 * quant - - TRANSFER_FEE); - Assert.assertEquals(2694881439L, owner.getStorageLimit()); - Assert.assertEquals(currentReserved - 2694881439L, - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(currentPool + 2 * quant, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - } - - - @Test - public void testBuyStorageBytes() { - long currentPool = dbManager.getDynamicPropertiesStore().getTotalStoragePool(); - long currentReserved = dbManager.getDynamicPropertiesStore().getTotalStorageReserved(); - Assert.assertEquals(currentPool, 100_000_000_000000L); - Assert.assertEquals(currentReserved, 128L * 1024 * 1024 * 1024); - - AccountCapsule owner = - dbManager.getAccountStore().get(ByteArray.fromHexString(OWNER_ADDRESS)); - - long bytes = 2694881440L; // 2 million trx - storageMarket.buyStorageBytes(owner, bytes); - - Assert.assertEquals(owner.getBalance(), initBalance - 2_000_000_000_000L - - TRANSFER_FEE); - Assert.assertEquals(bytes, owner.getStorageLimit()); - Assert.assertEquals(currentReserved - bytes, - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(currentPool + 2_000_000_000_000L, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - } - - @Test - public void testBuyStorageBytes2() { - long currentPool = dbManager.getDynamicPropertiesStore().getTotalStoragePool(); - long currentReserved = dbManager.getDynamicPropertiesStore().getTotalStorageReserved(); - Assert.assertEquals(currentPool, 100_000_000_000000L); - Assert.assertEquals(currentReserved, 128L * 1024 * 1024 * 1024); - - AccountCapsule owner = - dbManager.getAccountStore().get(ByteArray.fromHexString(OWNER_ADDRESS)); - - long bytes1 = 1360781717L; - - storageMarket.buyStorageBytes(owner, bytes1); - - Assert.assertEquals(owner.getBalance(), initBalance - 1_000_000_000_000L - - TRANSFER_FEE); - Assert.assertEquals(bytes1, owner.getStorageLimit()); - Assert.assertEquals(currentReserved - bytes1, - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(currentPool + 1_000_000_000_000L, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - long bytes2 = 1334099723L; - storageMarket.buyStorageBytes(owner, bytes2); - Assert.assertEquals(owner.getBalance(), initBalance - 2 * 1_000_000_000_000L - - TRANSFER_FEE); - Assert.assertEquals(bytes1 + bytes2, owner.getStorageLimit()); - Assert.assertEquals(currentReserved - (bytes1 + bytes2), - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(currentPool + 2 * 1_000_000_000_000L, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - } - - @Test - public void testSellStorage() { - long currentPool = dbManager.getDynamicPropertiesStore().getTotalStoragePool(); - long currentReserved = dbManager.getDynamicPropertiesStore().getTotalStorageReserved(); - Assert.assertEquals(currentPool, 100_000_000_000000L); - Assert.assertEquals(currentReserved, 128L * 1024 * 1024 * 1024); - - AccountCapsule owner = - dbManager.getAccountStore().get(ByteArray.fromHexString(OWNER_ADDRESS)); - - long quant = 2_000_000_000_000L; // 2 million trx - storageMarket.buyStorage(owner, quant); - - Assert.assertEquals(owner.getBalance(), initBalance - quant - - TRANSFER_FEE); - Assert.assertEquals(2694881440L, owner.getStorageLimit()); - Assert.assertEquals(currentReserved - 2694881440L, - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(currentPool + quant, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - long bytes = 2694881440L; - storageMarket.sellStorage(owner, bytes); - - Assert.assertEquals(owner.getBalance(), initBalance); - Assert.assertEquals(0, owner.getStorageLimit()); - Assert.assertEquals(currentReserved, - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(100_000_000_000_000L, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - } - - @Test - public void testSellStorage2() { - long currentPool = dbManager.getDynamicPropertiesStore().getTotalStoragePool(); - long currentReserved = dbManager.getDynamicPropertiesStore().getTotalStorageReserved(); - Assert.assertEquals(currentPool, 100_000_000_000000L); - Assert.assertEquals(currentReserved, 128L * 1024 * 1024 * 1024); - - AccountCapsule owner = - dbManager.getAccountStore().get(ByteArray.fromHexString(OWNER_ADDRESS)); - - long quant = 2_000_000_000_000L; // 2 million trx - storageMarket.buyStorage(owner, quant); - - Assert.assertEquals(owner.getBalance(), initBalance - quant - - TRANSFER_FEE); - Assert.assertEquals(2694881440L, owner.getStorageLimit()); - Assert.assertEquals(currentReserved - 2694881440L, - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(currentPool + quant, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - long bytes1 = 2694881440L - 1360781717L; // 1 million trx - long bytes2 = 1360781717L; // 1 million trx - - storageMarket.sellStorage(owner, bytes1); - - Assert.assertEquals(owner.getBalance(), initBalance - 1_000_000_000_000L); - Assert.assertEquals(1360781717L, owner.getStorageLimit()); - Assert.assertEquals(currentReserved - 1360781717L, - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(currentPool + 1_000_000_000_000L, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - storageMarket.sellStorage(owner, bytes2); - - Assert.assertEquals(owner.getBalance(), initBalance); - Assert.assertEquals(0, owner.getStorageLimit()); - Assert.assertEquals(currentReserved, - dbManager.getDynamicPropertiesStore().getTotalStorageReserved()); - Assert.assertEquals(currentPool, - dbManager.getDynamicPropertiesStore().getTotalStoragePool()); - - } - - -} diff --git a/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java b/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java index 3346a1aead5..eb44e9568bc 100644 --- a/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java +++ b/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java @@ -22,6 +22,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.tron.common.BaseTest; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.core.ChainBaseManager; import org.tron.core.Constant; @@ -114,7 +115,7 @@ public static long estimateConsumeBandWidthSize(final AccountCapsule ownerCapsul TransactionCapsule fakeTransactionCapsule2 = new TransactionCapsule(builder2.build(), ContractType.DelegateResourceContract); long size2 = consumeBandWidthSize(fakeTransactionCapsule2, chainBaseManager); - long addSize = Math.max(size1 - size2, 0L); + long addSize = MathWrapper.max(size1 - size2, 0L); return DELEGATE_COST_BASE_SIZE + addSize; } @@ -135,7 +136,7 @@ public static long estimateConsumeBandWidthSizeOld( TransactionCapsule fakeTransactionCapsule2 = new TransactionCapsule(builder2.build(), ContractType.DelegateResourceContract); long size2 = consumeBandWidthSize(fakeTransactionCapsule2, chainBaseManager); - long addSize = Math.max(size1 - size2, 0L); + long addSize = MathWrapper.max(size1 - size2, 0L); return DELEGATE_COST_BASE_SIZE + addSize; } @@ -373,7 +374,7 @@ public void estimateConsumeBandWidthSizePositive() { DelegateResourceContract.newBuilder() .setBalance(TRX_PRECISION); - long expected = DELEGATE_COST_BASE_SIZE + Math.max( + long expected = DELEGATE_COST_BASE_SIZE + MathWrapper.max( builder.build().getSerializedSize() - builder2.build().getSerializedSize(), 0L); long actual = TransactionUtil.estimateConsumeBandWidthSize(dps, balance); Assert.assertEquals(expected, actual); @@ -391,7 +392,7 @@ public void estimateConsumeBandWidthSizeBoundary() { DelegateResourceContract.newBuilder() .setBalance(TRX_PRECISION); - long expected = DELEGATE_COST_BASE_SIZE + Math.max( + long expected = DELEGATE_COST_BASE_SIZE + MathWrapper.max( builder.build().getSerializedSize() - builder2.build().getSerializedSize(), 0L); long actual = TransactionUtil.estimateConsumeBandWidthSize(dps, balance); Assert.assertEquals(expected, actual); @@ -409,7 +410,7 @@ public void estimateConsumeBandWidthSizeEdge() { DelegateResourceContract.newBuilder() .setBalance(TRX_PRECISION); - long expected = DELEGATE_COST_BASE_SIZE + Math.max( + long expected = DELEGATE_COST_BASE_SIZE + MathWrapper.max( builder.build().getSerializedSize() - builder2.build().getSerializedSize(), 0L); long actual = TransactionUtil.estimateConsumeBandWidthSize(dps, balance); Assert.assertEquals(expected, actual); @@ -427,7 +428,7 @@ public void estimateConsumeBandWidthSizeCorner() { DelegateResourceContract.newBuilder() .setBalance(TRX_PRECISION); - long expected = DELEGATE_COST_BASE_SIZE + Math.max( + long expected = DELEGATE_COST_BASE_SIZE + MathWrapper.max( builder.build().getSerializedSize() - builder2.build().getSerializedSize(), 0L); long actual = TransactionUtil.estimateConsumeBandWidthSize(dps, balance); Assert.assertEquals(expected, actual); diff --git a/framework/src/test/java/org/tron/core/capsule/utils/ExchangeProcessorTest.java b/framework/src/test/java/org/tron/core/capsule/utils/ExchangeProcessorTest.java index 3437eb0ea42..0bcd0b4ce7a 100644 --- a/framework/src/test/java/org/tron/core/capsule/utils/ExchangeProcessorTest.java +++ b/framework/src/test/java/org/tron/core/capsule/utils/ExchangeProcessorTest.java @@ -55,6 +55,12 @@ public void testExchange2() { } + @Test + public void testExchange3() { + long result = processor.exchange(4732214, 2202692725330L, 29218); + Assert.assertEquals(13516579896L, result); + } + @Test public void testSellAndBuy() { diff --git a/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java b/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java index 3662fb524b8..7465a36a960 100644 --- a/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java +++ b/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java @@ -11,6 +11,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.tron.common.math.MathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.ByteArray; import org.tron.common.utils.MerkleRoot; @@ -85,7 +86,7 @@ private static int getRank(int num) { num = num >> 1; rank++; } - if (temp == Math.pow(2, rank - 1)) { + if (temp == MathWrapper.pow(2, rank - 1)) { rank -= 1; } return rank; diff --git a/framework/src/test/java/org/tron/core/db/MarketPairPriceToOrderStoreTest.java b/framework/src/test/java/org/tron/core/db/MarketPairPriceToOrderStoreTest.java index 1f453cb9b41..2a388070dc2 100755 --- a/framework/src/test/java/org/tron/core/db/MarketPairPriceToOrderStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/MarketPairPriceToOrderStoreTest.java @@ -6,6 +6,7 @@ import org.junit.Assert; import org.junit.Test; import org.tron.common.BaseTest; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; import org.tron.core.ChainBaseManager; @@ -43,7 +44,7 @@ public void cleanDb() { } private static int randomInt(int minInt, int maxInt) { - return (int) Math.round(Math.random() * (maxInt - minInt) + minInt); + return (int) MathWrapper.round(MathWrapper.random() * (maxInt - minInt) + minInt); } @Test diff --git a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java index ea0c0354bb0..ddee5a46a7c 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java @@ -8,6 +8,7 @@ import org.junit.Assert; import org.junit.Test; import org.tron.common.logsfilter.capsule.BlockFilterCapsule; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.core.exception.ItemNotFoundException; import org.tron.core.services.jsonrpc.TronJsonRpcImpl; @@ -17,7 +18,7 @@ public class ConcurrentHashMapTest { private static int randomInt(int minInt, int maxInt) { - return (int) Math.round(Math.random() * (maxInt - minInt) + minInt); + return (int) MathWrapper.round(MathWrapper.random() * (maxInt - minInt) + minInt); } /** diff --git a/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java b/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java index 67353eb24b1..37a21e107ad 100644 --- a/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java @@ -27,6 +27,7 @@ import org.junit.Ignore; import org.junit.Test; import org.tron.common.BaseTest; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; import org.tron.common.zksnark.IncrementalMerkleTreeContainer; @@ -87,7 +88,7 @@ public static void init() { } private static int randomInt(int minInt, int maxInt) { - return (int) Math.round(Math.random() * (maxInt - minInt) + minInt); + return (int) MathWrapper.round(MathWrapper.random() * (maxInt - minInt) + minInt); } public static void test(byte[] K, byte[] ovk, byte[] cv, byte[] cm, byte[] epk) diff --git a/framework/src/test/java/org/tron/core/zksnark/SaplingNoteTest.java b/framework/src/test/java/org/tron/core/zksnark/SaplingNoteTest.java index 60f6c8c0826..ca5235d234e 100644 --- a/framework/src/test/java/org/tron/core/zksnark/SaplingNoteTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/SaplingNoteTest.java @@ -4,6 +4,7 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import org.tron.common.math.MathWrapper; import org.tron.common.utils.ByteArray; import org.tron.core.config.args.Args; import org.tron.core.exception.BadItemException; @@ -30,7 +31,7 @@ public static void removeDb() { } private static int randomInt(int minInt, int maxInt) { - return (int) Math.round(Math.random() * (maxInt - minInt) + minInt); + return (int) MathWrapper.round(MathWrapper.random() * (maxInt - minInt) + minInt); } @Test diff --git a/framework/src/test/java/org/tron/keystroe/CredentialsTest.java b/framework/src/test/java/org/tron/keystroe/CredentialsTest.java index ce992c3443f..dd2745d18c9 100644 --- a/framework/src/test/java/org/tron/keystroe/CredentialsTest.java +++ b/framework/src/test/java/org/tron/keystroe/CredentialsTest.java @@ -1,6 +1,5 @@ package org.tron.keystroe; -import lombok.var; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; From ed85d802b826d3852b148d68ca08010f515591d0 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Thu, 23 May 2024 17:32:17 +0800 Subject: [PATCH 04/15] feat(log): add log for powjdk8. --- .../org/tron/common/math/MathWrapper.java | 8 +++++++- framework/src/main/resources/logback.xml | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/org/tron/common/math/MathWrapper.java b/common/src/main/java/org/tron/common/math/MathWrapper.java index 1f55f69c48b..110613a52d8 100644 --- a/common/src/main/java/org/tron/common/math/MathWrapper.java +++ b/common/src/main/java/org/tron/common/math/MathWrapper.java @@ -145,6 +145,12 @@ public static double pow(double a, double b) { public static double powjdk8(double a, double b) { Preconditions.checkArgument(b > -1 && b < 1, "b must be -1 1 && a < 2, "a must be 1 + + ./logs/db/math.log + + ./logs/math/math.%i.log.gz + 500MB + 50GB + + + %m + UTF-8 + + + TRACE + + + 0 @@ -92,6 +108,9 @@ + + + From f5bcffa558d513e6e0ff115aafd91d8dd9b51f9c Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Mon, 13 May 2024 21:31:51 +0800 Subject: [PATCH 05/15] feat(api): optimize api service startup --- .../common/parameter/CommonParameter.java | 18 ++++ .../src/main/java/org/tron/core/Constant.java | 8 +- .../common/application/AbstractService.java | 66 +++++++++++++ .../common/application/ApplicationImpl.java | 19 +++- .../tron/common/application/HttpService.java | 52 +---------- .../tron/common/application/RpcService.java | 54 +---------- .../org/tron/common/application/Service.java | 13 +-- .../common/application/ServiceContainer.java | 56 ++++------- .../java/org/tron/core/config/args/Args.java | 31 ++++++- .../org/tron/core/services/RpcApiService.java | 9 +- .../services/http/FullNodeHttpApiService.java | 9 +- .../solidity/SolidityNodeHttpApiService.java | 9 +- .../JsonRpcServiceOnPBFT.java | 14 +-- .../JsonRpcServiceOnSolidity.java | 14 +-- .../interfaceOnPBFT/RpcApiServiceOnPBFT.java | 8 +- .../http/PBFT/HttpApiOnPBFTService.java | 10 +- .../RpcApiServiceOnSolidity.java | 8 +- .../solidity/HttpApiOnSolidityService.java | 10 +- .../jsonrpc/FullNodeJsonRpcHttpService.java | 14 +-- .../org/tron/core/zen/ZksnarkInitService.java | 2 - .../main/java/org/tron/program/FullNode.java | 59 ------------ .../java/org/tron/program/SolidityNode.java | 12 --- .../org/tron/core/config/args/ArgsTest.java | 93 +++++++++++++++++++ .../tron/core/jsonrpc/JsonrpcServiceTest.java | 3 - .../java/org/tron/core/pbft/PbftApiTest.java | 1 - .../org/tron/program/SolidityNodeTest.java | 41 +------- 26 files changed, 289 insertions(+), 344 deletions(-) create mode 100644 framework/src/main/java/org/tron/common/application/AbstractService.java diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index f9562e580e3..4b523a3eb7b 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -453,12 +453,30 @@ public class CommonParameter { @Getter @Setter public String cryptoEngine = Constant.ECKey_ENGINE; + + @Getter + @Setter + public boolean rpcEnable = true; + + @Getter + @Setter + public boolean rpcSolidityEnable = true; + + @Getter + @Setter + public boolean rpcPBFTEnable = true; + @Getter @Setter public boolean fullNodeHttpEnable = true; @Getter @Setter public boolean solidityNodeHttpEnable = true; + + @Getter + @Setter + public boolean pBFTHttpEnable = true; + @Getter @Setter public boolean jsonRpcHttpFullNodeEnable = false; diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index 2cd9ea95f15..93bc5dca917 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -118,15 +118,21 @@ public class Constant { public static final String NODE_DNS_AWS_REGION = "node.dns.awsRegion"; public static final String NODE_DNS_AWS_HOST_ZONE_ID = "node.dns.awsHostZoneId"; + // config for rpc public static final String NODE_RPC_PORT = "node.rpc.port"; public static final String NODE_RPC_SOLIDITY_PORT = "node.rpc.solidityPort"; public static final String NODE_RPC_PBFT_PORT = "node.rpc.PBFTPort"; + public static final String NODE_RPC_ENABLE = "node.rpc.enable"; + public static final String NODE_RPC_SOLIDITY_ENABLE = "node.rpc.solidityEnable"; + public static final String NODE_RPC_PBFT_ENABLE = "node.rpc.PBFTEnable"; + // config for http public static final String NODE_HTTP_FULLNODE_PORT = "node.http.fullNodePort"; public static final String NODE_HTTP_SOLIDITY_PORT = "node.http.solidityPort"; public static final String NODE_HTTP_FULLNODE_ENABLE = "node.http.fullNodeEnable"; public static final String NODE_HTTP_SOLIDITY_ENABLE = "node.http.solidityEnable"; + public static final String NODE_HTTP_PBFT_ENABLE = "node.http.PBFTEnable"; public static final String NODE_HTTP_PBFT_PORT = "node.http.PBFTPort"; - + // config for jsonrpc public static final String NODE_JSONRPC_HTTP_FULLNODE_ENABLE = "node.jsonrpc.httpFullNodeEnable"; public static final String NODE_JSONRPC_HTTP_FULLNODE_PORT = "node.jsonrpc.httpFullNodePort"; public static final String NODE_JSONRPC_HTTP_SOLIDITY_ENABLE = "node.jsonrpc.httpSolidityEnable"; diff --git a/framework/src/main/java/org/tron/common/application/AbstractService.java b/framework/src/main/java/org/tron/common/application/AbstractService.java new file mode 100644 index 00000000000..025be1d471c --- /dev/null +++ b/framework/src/main/java/org/tron/common/application/AbstractService.java @@ -0,0 +1,66 @@ +package org.tron.common.application; + +import com.google.common.base.Objects; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.tron.core.config.args.Args; + + +@Slf4j(topic = "service") +public abstract class AbstractService implements Service { + + protected int port; + @Getter + protected boolean enable; + protected final String name = this.getClass().getSimpleName(); + + + @Override + public void start() { + try { + innerStart(); + logger.info("{} started, listening on {}", name, port); + } catch (Exception e) { + logger.error("{}", name, e); + } + } + + @Override + public void stop() { + logger.info("{} shutdown...", name); + try { + innerStop(); + } catch (Exception e) { + logger.warn("{}", name, e); + } + logger.info("{} shutdown complete", name); + + + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AbstractService that = (AbstractService) o; + return port == that.port; + } + + @Override + public int hashCode() { + return Objects.hashCode(name, port); + } + + public abstract void innerStart() throws Exception; + + public abstract void innerStop() throws Exception; + + protected boolean isFullNode() { + return !Args.getInstance().isSolidityNode(); + } + +} diff --git a/framework/src/main/java/org/tron/common/application/ApplicationImpl.java b/framework/src/main/java/org/tron/common/application/ApplicationImpl.java index 3cb75cb1e24..b4e248b2c02 100644 --- a/framework/src/main/java/org/tron/common/application/ApplicationImpl.java +++ b/framework/src/main/java/org/tron/common/application/ApplicationImpl.java @@ -1,5 +1,6 @@ package org.tron.common.application; +import java.util.concurrent.CountDownLatch; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -15,6 +16,7 @@ @Component public class ApplicationImpl implements Application { + @Autowired private ServiceContainer services; @Autowired @@ -29,6 +31,8 @@ public class ApplicationImpl implements Application { @Autowired private ConsensusService consensusService; + private final CountDownLatch shutdown = new CountDownLatch(1); + @Override public void setOptions(Args args) { // not used @@ -37,24 +41,23 @@ public void setOptions(Args args) { @Override @Autowired public void init(CommonParameter parameter) { - services = new ServiceContainer(); + // not used } @Override public void addService(Service service) { - services.add(service); + // used by test } @Override public void initServices(CommonParameter parameter) { - services.init(parameter); + // not used } /** * start up the app. */ public void startup() { - this.initServices(Args.getInstance()); this.startServices(); if ((!Args.getInstance().isSolidityNode()) && (!Args.getInstance().isP2pDisable())) { tronNetService.start(); @@ -71,6 +74,7 @@ public void shutdown() { tronNetService.close(); } dbManager.close(); + shutdown.countDown(); } @Override @@ -80,7 +84,12 @@ public void startServices() { @Override public void blockUntilShutdown() { - services.blockUntilShutdown(); + try { + shutdown.await(); + } catch (final InterruptedException e) { + logger.debug("Interrupted, exiting", e); + Thread.currentThread().interrupt(); + } } @Override diff --git a/framework/src/main/java/org/tron/common/application/HttpService.java b/framework/src/main/java/org/tron/common/application/HttpService.java index 76f8e74d65c..f9eef50461d 100644 --- a/framework/src/main/java/org/tron/common/application/HttpService.java +++ b/framework/src/main/java/org/tron/common/application/HttpService.java @@ -15,67 +15,25 @@ package org.tron.common.application; -import com.google.common.base.Objects; import lombok.extern.slf4j.Slf4j; import org.eclipse.jetty.server.Server; @Slf4j(topic = "rpc") -public abstract class HttpService implements Service { +public abstract class HttpService extends AbstractService { protected Server apiServer; - protected int port; @Override - public void blockUntilShutdown() { + public void innerStart() throws Exception { if (apiServer != null) { - try { - apiServer.join(); - } catch (InterruptedException e) { - logger.warn("{}", e.getMessage()); - Thread.currentThread().interrupt(); - } + apiServer.start(); } } @Override - public void start() { + public void innerStop() throws Exception { if (apiServer != null) { - try { - apiServer.start(); - logger.info("{} started, listening on {}", this.getClass().getSimpleName(), port); - } catch (Exception e) { - logger.error("{}", this.getClass().getSimpleName(), e); - } + apiServer.stop(); } } - - @Override - public void stop() { - if (apiServer != null) { - logger.info("{} shutdown...", this.getClass().getSimpleName()); - try { - apiServer.stop(); - } catch (Exception e) { - logger.warn("{}", this.getClass().getSimpleName(), e); - } - logger.info("{} shutdown complete", this.getClass().getSimpleName()); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - HttpService that = (HttpService) o; - return port == that.port; - } - - @Override - public int hashCode() { - return Objects.hashCode(getClass().getSimpleName(), port); - } } diff --git a/framework/src/main/java/org/tron/common/application/RpcService.java b/framework/src/main/java/org/tron/common/application/RpcService.java index cb89441174a..129348f12e7 100644 --- a/framework/src/main/java/org/tron/common/application/RpcService.java +++ b/framework/src/main/java/org/tron/common/application/RpcService.java @@ -15,71 +15,27 @@ package org.tron.common.application; -import com.google.common.base.Objects; import io.grpc.Server; -import java.io.IOException; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; @Slf4j(topic = "rpc") -public abstract class RpcService implements Service { +public abstract class RpcService extends AbstractService { protected Server apiServer; - protected int port; @Override - public void blockUntilShutdown() { + public void innerStart() throws Exception { if (apiServer != null) { - try { - apiServer.awaitTermination(); - } catch (InterruptedException e) { - logger.warn("{}", e.getMessage()); - Thread.currentThread().interrupt(); - } + apiServer.start(); } } @Override - public void start() { + public void innerStop() throws Exception { if (apiServer != null) { - try { - apiServer.start(); - logger.info("{} started, listening on {}", this.getClass().getSimpleName(), port); - } catch (IOException e) { - logger.error("{}", this.getClass().getSimpleName(), e); - } + apiServer.shutdown().awaitTermination(5, TimeUnit.SECONDS); } } - @Override - public void stop() { - if (apiServer != null) { - logger.info("{} shutdown...", this.getClass().getSimpleName()); - try { - apiServer.shutdown().awaitTermination(5, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - logger.warn("{}", this.getClass().getSimpleName(), e); - } - logger.info("{} shutdown complete", this.getClass().getSimpleName()); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - RpcService that = (RpcService) o; - return port == that.port; - } - - @Override - public int hashCode() { - return Objects.hashCode(getClass().getSimpleName(), port); - } - } diff --git a/framework/src/main/java/org/tron/common/application/Service.java b/framework/src/main/java/org/tron/common/application/Service.java index 67b4e3ce9ae..7255391108c 100644 --- a/framework/src/main/java/org/tron/common/application/Service.java +++ b/framework/src/main/java/org/tron/common/application/Service.java @@ -15,22 +15,11 @@ package org.tron.common.application; -import org.tron.common.parameter.CommonParameter; - public interface Service { - void init(); - - void init(CommonParameter parameter); - - /** - * Start the service. - * {@link Service#init(CommonParameter parameter) init(CommonParameter parameter)} must be called - * before this method. - */ void start(); void stop(); - void blockUntilShutdown(); + boolean isEnable(); } diff --git a/framework/src/main/java/org/tron/common/application/ServiceContainer.java b/framework/src/main/java/org/tron/common/application/ServiceContainer.java index 2951596add7..bd00a2584af 100644 --- a/framework/src/main/java/org/tron/common/application/ServiceContainer.java +++ b/framework/src/main/java/org/tron/common/application/ServiceContainer.java @@ -15,59 +15,41 @@ package org.tron.common.application; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; -import org.tron.common.parameter.CommonParameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; @Slf4j(topic = "app") +@Component public class ServiceContainer { - private final Set services; + @Autowired + private List services; - public ServiceContainer() { - this.services = Collections.synchronizedSet(new LinkedHashSet<>()); - } - - public void add(Service service) { - this.services.add(service); - } + private List enabledServices; - - public void init() { - this.services.forEach(service -> { - logger.debug("Initing {}.", service.getClass().getSimpleName()); - service.init(); - }); + public ServiceContainer() { } - public void init(CommonParameter parameter) { - this.services.forEach(service -> { - logger.debug("Initing {}.", service.getClass().getSimpleName()); - service.init(parameter); - }); + @PostConstruct + private void initEnabledServices() { + this.enabledServices = this.services.stream() + .filter(Service::isEnable) + .collect(Collectors.toList()); } - public void start() { + void start() { logger.info("Starting api services."); - this.services.forEach(service -> { - logger.debug("Starting {}.", service.getClass().getSimpleName()); - service.start(); - }); + this.enabledServices.forEach(Service::start); logger.info("All api services started."); } - public void stop() { + void stop() { logger.info("Stopping api services."); - this.services.forEach(service -> { - logger.debug("Stopping {}.", service.getClass().getSimpleName()); - service.stop(); - }); + this.enabledServices.forEach(Service::stop); logger.info("All api services stopped."); } - - public void blockUntilShutdown() { - this.services.stream().findFirst().ifPresent(Service::blockUntilShutdown); - } } diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 2c0f047cbdf..55c54794fcc 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -189,8 +189,12 @@ public static void clearParam() { PARAMETER.validContractProtoThreadNum = 1; PARAMETER.shieldedTransInPendingMaxCounts = 10; PARAMETER.changedDelegation = 0; + PARAMETER.rpcEnable = true; + PARAMETER.rpcSolidityEnable = true; + PARAMETER.rpcPBFTEnable = true; PARAMETER.fullNodeHttpEnable = true; PARAMETER.solidityNodeHttpEnable = true; + PARAMETER.pBFTHttpEnable = true; PARAMETER.jsonRpcHttpFullNodeEnable = false; PARAMETER.jsonRpcHttpSolidityNodeEnable = false; PARAMETER.jsonRpcHttpPBFTNodeEnable = false; @@ -353,14 +357,21 @@ private static Map getOptionGroup() { * set parameters. */ public static void setParam(final String[] args, final String confFileName) { + Config config = Configuration.getByFileName(PARAMETER.shellConfFileName, confFileName); + setParam(args, config); + } + + /** + * set parameters. + */ + public static void setParam(final String[] args, final Config config) { + JCommander.newBuilder().addObject(PARAMETER).build().parse(args); if (PARAMETER.version) { printVersion(); exit(0); } - Config config = Configuration.getByFileName(PARAMETER.shellConfFileName, confFileName); - if (config.hasPath(Constant.NET_TYPE) && Constant.TESTNET.equalsIgnoreCase(config.getString(Constant.NET_TYPE))) { Wallet.setAddressPreFixByte(Constant.ADD_PRE_FIX_BYTE_TESTNET); @@ -447,6 +458,18 @@ public static void setParam(final String[] args, final String confFileName) { PARAMETER.lruCacheSize = config.getInt(Constant.VM_LRU_CACHE_SIZE); } + if (config.hasPath(Constant.NODE_RPC_ENABLE)) { + PARAMETER.rpcEnable = config.getBoolean(Constant.NODE_RPC_ENABLE); + } + + if (config.hasPath(Constant.NODE_RPC_SOLIDITY_ENABLE)) { + PARAMETER.rpcSolidityEnable = config.getBoolean(Constant.NODE_RPC_SOLIDITY_ENABLE); + } + + if (config.hasPath(Constant.NODE_RPC_PBFT_ENABLE)) { + PARAMETER.rpcPBFTEnable = config.getBoolean(Constant.NODE_RPC_PBFT_ENABLE); + } + if (config.hasPath(Constant.NODE_HTTP_FULLNODE_ENABLE)) { PARAMETER.fullNodeHttpEnable = config.getBoolean(Constant.NODE_HTTP_FULLNODE_ENABLE); } @@ -455,6 +478,10 @@ public static void setParam(final String[] args, final String confFileName) { PARAMETER.solidityNodeHttpEnable = config.getBoolean(Constant.NODE_HTTP_SOLIDITY_ENABLE); } + if (config.hasPath(Constant.NODE_HTTP_PBFT_ENABLE)) { + PARAMETER.pBFTHttpEnable = config.getBoolean(Constant.NODE_HTTP_PBFT_ENABLE); + } + if (config.hasPath(Constant.NODE_JSONRPC_HTTP_FULLNODE_ENABLE)) { PARAMETER.jsonRpcHttpFullNodeEnable = config.getBoolean(Constant.NODE_JSONRPC_HTTP_FULLNODE_ENABLE); diff --git a/framework/src/main/java/org/tron/core/services/RpcApiService.java b/framework/src/main/java/org/tron/core/services/RpcApiService.java index 85ad2dd18dd..1e6162c8532 100755 --- a/framework/src/main/java/org/tron/core/services/RpcApiService.java +++ b/framework/src/main/java/org/tron/core/services/RpcApiService.java @@ -199,14 +199,9 @@ public class RpcApiService extends RpcService { private final String executorName = "rpc-full-executor"; - @Override - public void init() { - - } - - @Override - public void init(CommonParameter args) { + public RpcApiService() { port = Args.getInstance().getRpcPort(); + enable = Args.getInstance().isRpcEnable(); } @Override diff --git a/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java b/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java index 55e6e07b5ec..81ffaa0da72 100644 --- a/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java +++ b/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java @@ -13,7 +13,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.common.application.HttpService; -import org.tron.common.parameter.CommonParameter; import org.tron.core.config.args.Args; import org.tron.core.services.filter.HttpApiAccessFilter; import org.tron.core.services.filter.HttpInterceptor; @@ -294,13 +293,9 @@ public class FullNodeHttpApiService extends HttpService { @Autowired private CancelAllUnfreezeV2Servlet cancelAllUnfreezeV2Servlet; - @Override - public void init() { - } - - @Override - public void init(CommonParameter args) { + public FullNodeHttpApiService() { port = Args.getInstance().getFullNodeHttpPort(); + enable = isFullNode() && Args.getInstance().isFullNodeHttpEnable(); } @Override diff --git a/framework/src/main/java/org/tron/core/services/http/solidity/SolidityNodeHttpApiService.java b/framework/src/main/java/org/tron/core/services/http/solidity/SolidityNodeHttpApiService.java index 0c66b220e09..dbe54f282a3 100644 --- a/framework/src/main/java/org/tron/core/services/http/solidity/SolidityNodeHttpApiService.java +++ b/framework/src/main/java/org/tron/core/services/http/solidity/SolidityNodeHttpApiService.java @@ -11,7 +11,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.common.application.HttpService; -import org.tron.common.parameter.CommonParameter; import org.tron.core.config.args.Args; import org.tron.core.services.filter.HttpApiAccessFilter; import org.tron.core.services.http.EstimateEnergyServlet; @@ -166,13 +165,9 @@ public class SolidityNodeHttpApiService extends HttpService { private GetEnergyPricesServlet getEnergyPricesServlet; - @Override - public void init() { - } - - @Override - public void init(CommonParameter args) { + public SolidityNodeHttpApiService() { port = Args.getInstance().getSolidityHttpPort(); + enable = !isFullNode() && Args.getInstance().isSolidityNodeHttpEnable(); } @Override diff --git a/framework/src/main/java/org/tron/core/services/interfaceJsonRpcOnPBFT/JsonRpcServiceOnPBFT.java b/framework/src/main/java/org/tron/core/services/interfaceJsonRpcOnPBFT/JsonRpcServiceOnPBFT.java index 1893a46045a..d251ba1f276 100644 --- a/framework/src/main/java/org/tron/core/services/interfaceJsonRpcOnPBFT/JsonRpcServiceOnPBFT.java +++ b/framework/src/main/java/org/tron/core/services/interfaceJsonRpcOnPBFT/JsonRpcServiceOnPBFT.java @@ -8,7 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.common.application.HttpService; -import org.tron.common.parameter.CommonParameter; +import org.tron.core.config.args.Args; @Component @Slf4j(topic = "API") @@ -17,13 +17,9 @@ public class JsonRpcServiceOnPBFT extends HttpService { @Autowired private JsonRpcOnPBFTServlet jsonRpcOnPBFTServlet; - @Override - public void init() { - } - - @Override - public void init(CommonParameter args) { - port = CommonParameter.getInstance().getJsonRpcHttpPBFTPort(); + public JsonRpcServiceOnPBFT() { + port = Args.getInstance().getJsonRpcHttpPBFTPort(); + enable = isFullNode() && Args.getInstance().isJsonRpcHttpPBFTNodeEnable(); } @Override @@ -36,7 +32,7 @@ public void start() { context.addServlet(new ServletHolder(jsonRpcOnPBFTServlet), "/jsonrpc"); - int maxHttpConnectNumber = CommonParameter.getInstance().getMaxHttpConnectNumber(); + int maxHttpConnectNumber = Args.getInstance().getMaxHttpConnectNumber(); if (maxHttpConnectNumber > 0) { apiServer.addBean(new ConnectionLimit(maxHttpConnectNumber, apiServer)); } diff --git a/framework/src/main/java/org/tron/core/services/interfaceJsonRpcOnSolidity/JsonRpcServiceOnSolidity.java b/framework/src/main/java/org/tron/core/services/interfaceJsonRpcOnSolidity/JsonRpcServiceOnSolidity.java index 52f5b761ae2..d29b598d285 100644 --- a/framework/src/main/java/org/tron/core/services/interfaceJsonRpcOnSolidity/JsonRpcServiceOnSolidity.java +++ b/framework/src/main/java/org/tron/core/services/interfaceJsonRpcOnSolidity/JsonRpcServiceOnSolidity.java @@ -8,7 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.common.application.HttpService; -import org.tron.common.parameter.CommonParameter; +import org.tron.core.config.args.Args; @Component @Slf4j(topic = "API") @@ -17,13 +17,9 @@ public class JsonRpcServiceOnSolidity extends HttpService { @Autowired private JsonRpcOnSolidityServlet jsonRpcOnSolidityServlet; - @Override - public void init() { - } - - @Override - public void init(CommonParameter args) { - port = CommonParameter.getInstance().getJsonRpcHttpSolidityPort(); + public JsonRpcServiceOnSolidity() { + port = Args.getInstance().getJsonRpcHttpSolidityPort(); + enable = isFullNode() && Args.getInstance().isJsonRpcHttpSolidityNodeEnable(); } @Override @@ -36,7 +32,7 @@ public void start() { context.addServlet(new ServletHolder(jsonRpcOnSolidityServlet), "/jsonrpc"); - int maxHttpConnectNumber = CommonParameter.getInstance().getMaxHttpConnectNumber(); + int maxHttpConnectNumber = Args.getInstance().getMaxHttpConnectNumber(); if (maxHttpConnectNumber > 0) { apiServer.addBean(new ConnectionLimit(maxHttpConnectNumber, apiServer)); } diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnPBFT/RpcApiServiceOnPBFT.java b/framework/src/main/java/org/tron/core/services/interfaceOnPBFT/RpcApiServiceOnPBFT.java index 4d801f20e5c..9117ea1b1b7 100755 --- a/framework/src/main/java/org/tron/core/services/interfaceOnPBFT/RpcApiServiceOnPBFT.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnPBFT/RpcApiServiceOnPBFT.java @@ -81,13 +81,9 @@ public class RpcApiServiceOnPBFT extends RpcService { private final String executorName = "rpc-pbft-executor"; - @Override - public void init() { - } - - @Override - public void init(CommonParameter parameter) { + public RpcApiServiceOnPBFT() { port = Args.getInstance().getRpcOnPBFTPort(); + enable = isFullNode() && Args.getInstance().isRpcPBFTEnable(); } @Override diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnPBFT/http/PBFT/HttpApiOnPBFTService.java b/framework/src/main/java/org/tron/core/services/interfaceOnPBFT/http/PBFT/HttpApiOnPBFTService.java index 7a5fd0cbcde..654526b7318 100644 --- a/framework/src/main/java/org/tron/core/services/interfaceOnPBFT/http/PBFT/HttpApiOnPBFTService.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnPBFT/http/PBFT/HttpApiOnPBFTService.java @@ -10,7 +10,6 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.springframework.beans.factory.annotation.Autowired; import org.tron.common.application.HttpService; -import org.tron.common.parameter.CommonParameter; import org.tron.core.config.args.Args; import org.tron.core.services.filter.HttpApiAccessFilter; import org.tron.core.services.filter.LiteFnQueryHttpFilter; @@ -172,14 +171,9 @@ public class HttpApiOnPBFTService extends HttpService { @Autowired private GetDelegatedResourceV2OnPBFTServlet getDelegatedResourceV2OnPBFTServlet; - @Override - public void init() { - - } - - @Override - public void init(CommonParameter parameter) { + public HttpApiOnPBFTService() { port = Args.getInstance().getPBFTHttpPort(); + enable = isFullNode() && Args.getInstance().isPBFTHttpEnable(); } @Override diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java index 6bdfc824163..aeab78b04eb 100755 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java @@ -83,13 +83,9 @@ public class RpcApiServiceOnSolidity extends RpcService { private final String executorName = "rpc-solidity-executor"; - @Override - public void init() { - } - - @Override - public void init(CommonParameter args) { + public RpcApiServiceOnSolidity() { port = Args.getInstance().getRpcOnSolidityPort(); + enable = isFullNode() && Args.getInstance().isRpcSolidityEnable(); } @Override diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java index f89be80c71b..5a1328d1a5e 100644 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java @@ -10,7 +10,6 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.springframework.beans.factory.annotation.Autowired; import org.tron.common.application.HttpService; -import org.tron.common.parameter.CommonParameter; import org.tron.core.config.args.Args; import org.tron.core.services.filter.HttpApiAccessFilter; import org.tron.core.services.filter.LiteFnQueryHttpFilter; @@ -178,14 +177,9 @@ public class HttpApiOnSolidityService extends HttpService { @Autowired private GetBlockOnSolidityServlet getBlockOnSolidityServlet; - @Override - public void init() { - - } - - @Override - public void init(CommonParameter args) { + public HttpApiOnSolidityService() { port = Args.getInstance().getSolidityHttpPort(); + enable = isFullNode() && Args.getInstance().isSolidityNodeHttpEnable(); } @Override diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/FullNodeJsonRpcHttpService.java b/framework/src/main/java/org/tron/core/services/jsonrpc/FullNodeJsonRpcHttpService.java index ff017f9562e..1b6e921a387 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/FullNodeJsonRpcHttpService.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/FullNodeJsonRpcHttpService.java @@ -12,7 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.common.application.HttpService; -import org.tron.common.parameter.CommonParameter; +import org.tron.core.config.args.Args; import org.tron.core.services.filter.HttpInterceptor; @Component @@ -22,13 +22,9 @@ public class FullNodeJsonRpcHttpService extends HttpService { @Autowired private JsonRpcServlet jsonRpcServlet; - @Override - public void init() { - } - - @Override - public void init(CommonParameter args) { - port = CommonParameter.getInstance().getJsonRpcHttpFullNodePort(); + public FullNodeJsonRpcHttpService() { + port = Args.getInstance().getJsonRpcHttpFullNodePort(); + enable = isFullNode() && Args.getInstance().isJsonRpcHttpFullNodeEnable(); } @Override @@ -41,7 +37,7 @@ public void start() { context.addServlet(new ServletHolder(jsonRpcServlet), "/jsonrpc"); - int maxHttpConnectNumber = CommonParameter.getInstance().getMaxHttpConnectNumber(); + int maxHttpConnectNumber = Args.getInstance().getMaxHttpConnectNumber(); if (maxHttpConnectNumber > 0) { apiServer.addBean(new ConnectionLimit(maxHttpConnectNumber, apiServer)); } diff --git a/framework/src/main/java/org/tron/core/zen/ZksnarkInitService.java b/framework/src/main/java/org/tron/core/zen/ZksnarkInitService.java index 8b9aafe4715..04405dfc758 100644 --- a/framework/src/main/java/org/tron/core/zen/ZksnarkInitService.java +++ b/framework/src/main/java/org/tron/core/zen/ZksnarkInitService.java @@ -6,7 +6,6 @@ import javax.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; -import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import org.tron.common.zksnark.JLibrustzcash; import org.tron.common.zksnark.LibrustzcashParam; @@ -14,7 +13,6 @@ @Slf4j(topic = "API") @Component -@DependsOn("fullNodeHttpApiService") public class ZksnarkInitService { @PostConstruct diff --git a/framework/src/main/java/org/tron/program/FullNode.java b/framework/src/main/java/org/tron/program/FullNode.java index 0fd87eb5de0..b7adf0ffe1a 100644 --- a/framework/src/main/java/org/tron/program/FullNode.java +++ b/framework/src/main/java/org/tron/program/FullNode.java @@ -15,16 +15,6 @@ import org.tron.core.Constant; import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; -import org.tron.core.net.P2pEventHandlerImpl; -import org.tron.core.services.RpcApiService; -import org.tron.core.services.http.FullNodeHttpApiService; -import org.tron.core.services.interfaceJsonRpcOnPBFT.JsonRpcServiceOnPBFT; -import org.tron.core.services.interfaceJsonRpcOnSolidity.JsonRpcServiceOnSolidity; -import org.tron.core.services.interfaceOnPBFT.RpcApiServiceOnPBFT; -import org.tron.core.services.interfaceOnPBFT.http.PBFT.HttpApiOnPBFTService; -import org.tron.core.services.interfaceOnSolidity.RpcApiServiceOnSolidity; -import org.tron.core.services.interfaceOnSolidity.http.solidity.HttpApiOnSolidityService; -import org.tron.core.services.jsonrpc.FullNodeJsonRpcHttpService; @Slf4j(topic = "app") public class FullNode { @@ -80,55 +70,6 @@ public static void main(String[] args) { context.refresh(); Application appT = ApplicationFactory.create(context); context.registerShutdownHook(); - - // grpc api server - RpcApiService rpcApiService = context.getBean(RpcApiService.class); - appT.addService(rpcApiService); - - // http api server - FullNodeHttpApiService httpApiService = context.getBean(FullNodeHttpApiService.class); - if (CommonParameter.getInstance().fullNodeHttpEnable) { - appT.addService(httpApiService); - } - - // JSON-RPC http server - if (CommonParameter.getInstance().jsonRpcHttpFullNodeEnable) { - FullNodeJsonRpcHttpService jsonRpcHttpService = - context.getBean(FullNodeJsonRpcHttpService.class); - appT.addService(jsonRpcHttpService); - } - - // full node and solidity node fuse together - // provide solidity rpc and http server on the full node. - RpcApiServiceOnSolidity rpcApiServiceOnSolidity = context - .getBean(RpcApiServiceOnSolidity.class); - appT.addService(rpcApiServiceOnSolidity); - HttpApiOnSolidityService httpApiOnSolidityService = context - .getBean(HttpApiOnSolidityService.class); - if (CommonParameter.getInstance().solidityNodeHttpEnable) { - appT.addService(httpApiOnSolidityService); - } - - // JSON-RPC on solidity - if (CommonParameter.getInstance().jsonRpcHttpSolidityNodeEnable) { - JsonRpcServiceOnSolidity jsonRpcServiceOnSolidity = context - .getBean(JsonRpcServiceOnSolidity.class); - appT.addService(jsonRpcServiceOnSolidity); - } - - // PBFT API (HTTP and GRPC) - RpcApiServiceOnPBFT rpcApiServiceOnPBFT = context - .getBean(RpcApiServiceOnPBFT.class); - appT.addService(rpcApiServiceOnPBFT); - HttpApiOnPBFTService httpApiOnPBFTService = context - .getBean(HttpApiOnPBFTService.class); - appT.addService(httpApiOnPBFTService); - - // JSON-RPC on PBFT - if (CommonParameter.getInstance().jsonRpcHttpPBFTNodeEnable) { - JsonRpcServiceOnPBFT jsonRpcServiceOnPBFT = context.getBean(JsonRpcServiceOnPBFT.class); - appT.addService(jsonRpcServiceOnPBFT); - } appT.startup(); appT.blockUntilShutdown(); } diff --git a/framework/src/main/java/org/tron/program/SolidityNode.java b/framework/src/main/java/org/tron/program/SolidityNode.java index 4cf71177803..fd5f7d56ef5 100644 --- a/framework/src/main/java/org/tron/program/SolidityNode.java +++ b/framework/src/main/java/org/tron/program/SolidityNode.java @@ -6,9 +6,7 @@ import java.util.concurrent.atomic.AtomicLong; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.BooleanUtils; -import org.springframework.context.ApplicationContext; import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; @@ -21,8 +19,6 @@ import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; import org.tron.core.db.Manager; -import org.tron.core.services.RpcApiService; -import org.tron.core.services.http.solidity.SolidityNodeHttpApiService; import org.tron.protos.Protocol.Block; @Slf4j(topic = "app") @@ -82,14 +78,6 @@ public static void main(String[] args) { Metrics.init(); Application appT = ApplicationFactory.create(context); - RpcApiService rpcApiService = context.getBean(RpcApiService.class); - appT.addService(rpcApiService); - //http - SolidityNodeHttpApiService httpApiService = context.getBean(SolidityNodeHttpApiService.class); - if (CommonParameter.getInstance().solidityNodeHttpEnable) { - appT.addService(httpApiService); - } - SolidityNode node = new SolidityNode(appT.getDbManager()); node.start(); appT.startup(); diff --git a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java index 52307d9d294..968e5bc3c18 100644 --- a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java +++ b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java @@ -17,11 +17,15 @@ import com.google.common.collect.Lists; import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; import io.grpc.internal.GrpcUtil; import io.grpc.netty.NettyServerBuilder; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; @@ -149,5 +153,94 @@ public void testOldRewardOpt() { thrown.expect(IllegalArgumentException.class); Args.setParam(new String[] {"-w"}, "args-test.conf"); } + + @Test + public void testInitService() { + Map storage = new HashMap<>(); + // avoid the exception for the missing storage + storage.put("storage.db.directory", "database"); + Config config = ConfigFactory.defaultOverrides().withFallback(ConfigFactory.parseMap(storage)); + // test default value + Args.setParam(new String[] {}, config); + Assert.assertTrue(Args.getInstance().isRpcEnable()); + Assert.assertTrue(Args.getInstance().isRpcSolidityEnable()); + Assert.assertTrue(Args.getInstance().isRpcPBFTEnable()); + Assert.assertTrue(Args.getInstance().isFullNodeHttpEnable()); + Assert.assertTrue(Args.getInstance().isSolidityNodeHttpEnable()); + Assert.assertTrue(Args.getInstance().isPBFTHttpEnable()); + Assert.assertFalse(Args.getInstance().isJsonRpcHttpFullNodeEnable()); + Assert.assertFalse(Args.getInstance().isJsonRpcHttpSolidityNodeEnable()); + Assert.assertFalse(Args.getInstance().isJsonRpcHttpPBFTNodeEnable()); + Args.clearParam(); + // test set all true value + storage.put("node.rpc.enable", "true"); + storage.put("node.rpc.solidityEnable", "true"); + storage.put("node.rpc.PBFTEnable", "true"); + storage.put("node.http.fullNodeEnable", "true"); + storage.put("node.http.solidityEnable", "true"); + storage.put("node.http.PBFTEnable", "true"); + storage.put("node.jsonrpc.httpFullNodeEnable", "true"); + storage.put("node.jsonrpc.httpSolidityEnable", "true"); + storage.put("node.jsonrpc.httpPBFTEnable", "true"); + config = ConfigFactory.defaultOverrides().withFallback(ConfigFactory.parseMap(storage)); + // test value + Args.setParam(new String[] {}, config); + Assert.assertTrue(Args.getInstance().isRpcEnable()); + Assert.assertTrue(Args.getInstance().isRpcSolidityEnable()); + Assert.assertTrue(Args.getInstance().isRpcPBFTEnable()); + Assert.assertTrue(Args.getInstance().isFullNodeHttpEnable()); + Assert.assertTrue(Args.getInstance().isSolidityNodeHttpEnable()); + Assert.assertTrue(Args.getInstance().isPBFTHttpEnable()); + Assert.assertTrue(Args.getInstance().isJsonRpcHttpFullNodeEnable()); + Assert.assertTrue(Args.getInstance().isJsonRpcHttpSolidityNodeEnable()); + Assert.assertTrue(Args.getInstance().isJsonRpcHttpPBFTNodeEnable()); + Args.clearParam(); + // test set all false value + storage.put("node.rpc.enable", "false"); + storage.put("node.rpc.solidityEnable", "false"); + storage.put("node.rpc.PBFTEnable", "false"); + storage.put("node.http.fullNodeEnable", "false"); + storage.put("node.http.solidityEnable", "false"); + storage.put("node.http.PBFTEnable", "false"); + storage.put("node.jsonrpc.httpFullNodeEnable", "false"); + storage.put("node.jsonrpc.httpSolidityEnable", "false"); + storage.put("node.jsonrpc.httpPBFTEnable", "false"); + config = ConfigFactory.defaultOverrides().withFallback(ConfigFactory.parseMap(storage)); + // test value + Args.setParam(new String[] {}, config); + Assert.assertFalse(Args.getInstance().isRpcEnable()); + Assert.assertFalse(Args.getInstance().isRpcSolidityEnable()); + Assert.assertFalse(Args.getInstance().isRpcPBFTEnable()); + Assert.assertFalse(Args.getInstance().isFullNodeHttpEnable()); + Assert.assertFalse(Args.getInstance().isSolidityNodeHttpEnable()); + Assert.assertFalse(Args.getInstance().isPBFTHttpEnable()); + Assert.assertFalse(Args.getInstance().isJsonRpcHttpFullNodeEnable()); + Assert.assertFalse(Args.getInstance().isJsonRpcHttpSolidityNodeEnable()); + Assert.assertFalse(Args.getInstance().isJsonRpcHttpPBFTNodeEnable()); + Args.clearParam(); + // test set random value + storage.put("node.rpc.enable", "false"); + storage.put("node.rpc.solidityEnable", "false"); + storage.put("node.rpc.PBFTEnable", "true"); + storage.put("node.http.fullNodeEnable", "false"); + storage.put("node.http.solidityEnable", "true"); + storage.put("node.http.PBFTEnable", "false"); + storage.put("node.jsonrpc.httpFullNodeEnable", "true"); + storage.put("node.jsonrpc.httpSolidityEnable", "false"); + storage.put("node.jsonrpc.httpPBFTEnable", "true"); + config = ConfigFactory.defaultOverrides().withFallback(ConfigFactory.parseMap(storage)); + // test value + Args.setParam(new String[] {}, config); + Assert.assertFalse(Args.getInstance().isRpcEnable()); + Assert.assertFalse(Args.getInstance().isRpcSolidityEnable()); + Assert.assertTrue(Args.getInstance().isRpcPBFTEnable()); + Assert.assertFalse(Args.getInstance().isFullNodeHttpEnable()); + Assert.assertTrue(Args.getInstance().isSolidityNodeHttpEnable()); + Assert.assertFalse(Args.getInstance().isPBFTHttpEnable()); + Assert.assertTrue(Args.getInstance().isJsonRpcHttpFullNodeEnable()); + Assert.assertFalse(Args.getInstance().isJsonRpcHttpSolidityNodeEnable()); + Assert.assertTrue(Args.getInstance().isJsonRpcHttpPBFTNodeEnable()); + Args.clearParam(); + } } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index b3ed26b591f..98631210374 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -265,7 +265,6 @@ public void testGetTransactionByHash() { @Test public void testGetBlockByNumber2() { - fullNodeJsonRpcHttpService.init(Args.getInstance()); fullNodeJsonRpcHttpService.start(); JsonArray params = new JsonArray(); params.add(ByteArray.toJsonHex(blockCapsule.getNum())); @@ -303,9 +302,7 @@ public void testGetBlockByNumber2() { @Test public void testServicesInit() { try { - jsonRpcServiceOnPBFT.init(Args.getInstance()); jsonRpcServiceOnPBFT.start(); - jsonRpcServiceOnSolidity.init(Args.getInstance()); jsonRpcServiceOnSolidity.start(); } finally { jsonRpcServiceOnPBFT.stop(); diff --git a/framework/src/test/java/org/tron/core/pbft/PbftApiTest.java b/framework/src/test/java/org/tron/core/pbft/PbftApiTest.java index 9bc942d6684..84fb632b93d 100755 --- a/framework/src/test/java/org/tron/core/pbft/PbftApiTest.java +++ b/framework/src/test/java/org/tron/core/pbft/PbftApiTest.java @@ -58,7 +58,6 @@ public void pbftapi() throws IOException { Assert.assertTrue(dynamicPropertiesStore.getLatestBlockHeaderNumber() >= 10); commonDataBase.saveLatestPbftBlockNum(6); - httpApiOnPBFTService.init(Args.getInstance()); httpApiOnPBFTService.start(); CloseableHttpResponse response; try (CloseableHttpClient httpClient = HttpClients.createDefault()) { diff --git a/framework/src/test/java/org/tron/program/SolidityNodeTest.java b/framework/src/test/java/org/tron/program/SolidityNodeTest.java index a77fa2fa8c6..a95d07c0c11 100755 --- a/framework/src/test/java/org/tron/program/SolidityNodeTest.java +++ b/framework/src/test/java/org/tron/program/SolidityNodeTest.java @@ -1,12 +1,8 @@ package org.tron.program; -import java.io.File; -import java.io.IOException; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.junit.AfterClass; import org.junit.Assert; -import org.junit.BeforeClass; import org.junit.Test; import org.tron.common.BaseTest; import org.tron.common.client.DatabaseGrpcClient; @@ -26,41 +22,10 @@ public class SolidityNodeTest extends BaseTest { SolidityNodeHttpApiService solidityNodeHttpApiService; static { - try { - Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); - } catch (IOException e) { - Assert.fail("create temp directory failed."); - } + Args.setParam(new String[]{"-d", dbPath()}, Constant.TEST_CONF); Args.getInstance().setSolidityNode(true); } - /** - * init db. - */ - @BeforeClass - public static void init() { - } - - /** - * remo db when after test. - */ - @AfterClass - public static void removeDb() { - Args.clearParam(); - } - - private static Boolean deleteFolder(File index) { - if (!index.isDirectory() || index.listFiles().length <= 0) { - return index.delete(); - } - for (File file : index.listFiles()) { - if (null != file && !deleteFolder(file)) { - return false; - } - } - return index.delete(); - } - @Test public void testSolidityArgs() { Assert.assertNotNull(Args.getInstance().getTrustNodeAddr()); @@ -69,7 +34,6 @@ public void testSolidityArgs() { @Test public void testSolidityGrpcCall() { - rpcApiService.init(Args.getInstance()); rpcApiService.start(); DatabaseGrpcClient databaseGrpcClient = null; String address = Args.getInstance().getTrustNodeAddr(); @@ -99,7 +63,8 @@ public void testSolidityGrpcCall() { @Test public void testSolidityNodeHttpApiService() { - solidityNodeHttpApiService.init(Args.getInstance()); + solidityNodeHttpApiService.start(); + // start again solidityNodeHttpApiService.start(); solidityNodeHttpApiService.stop(); Assert.assertTrue(true); From c2d71cb51f089bab21f2ef8d7d698161ad23e376 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Thu, 23 May 2024 17:45:01 +0800 Subject: [PATCH 06/15] feat(block): skip check when blockCap.generatedByMyself. --- .../main/java/org/tron/core/db/Manager.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 3fd1839f83e..764ad745d36 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -1419,29 +1419,31 @@ public TransactionInfo processTransaction(final TransactionCapsule trxCap, Block Objects.nonNull(blockCap) ? MetricLabels.BLOCK : MetricLabels.TRX, contract.getType().name()); - long start = System.currentTimeMillis(); + final long start = System.currentTimeMillis(); if (Objects.nonNull(blockCap)) { chainBaseManager.getBalanceTraceStore().initCurrentTransactionBalanceTrace(trxCap); } + if (Objects.nonNull(blockCap) && !blockCap.generatedByMyself) { + validateTapos(trxCap); + validateCommon(trxCap); + if (trxCap.getInstance().getRawData().getContractList().size() != 1) { + throw new ContractSizeNotEqualToOneException( + String.format( + "tx %s contract size should be exactly 1, this is extend feature ,actual :%d", + txId, trxCap.getInstance().getRawData().getContractList().size())); + } - validateTapos(trxCap); - validateCommon(trxCap); + validateDup(trxCap); - if (trxCap.getInstance().getRawData().getContractList().size() != 1) { - throw new ContractSizeNotEqualToOneException( - String.format( - "tx %s contract size should be exactly 1, this is extend feature ,actual :%d", - txId, trxCap.getInstance().getRawData().getContractList().size())); + if (!trxCap.validateSignature(chainBaseManager.getAccountStore(), + chainBaseManager.getDynamicPropertiesStore())) { + throw new ValidateSignatureException( + String.format(" %s transaction signature validate failed", txId)); + } } - validateDup(trxCap); - if (!trxCap.validateSignature(chainBaseManager.getAccountStore(), - chainBaseManager.getDynamicPropertiesStore())) { - throw new ValidateSignatureException( - String.format(" %s transaction signature validate failed", txId)); - } TransactionTrace trace = new TransactionTrace(trxCap, StoreFactory.getInstance(), new RuntimeImpl()); From 5d31bbcf3b656b99dc38ffb0f4d1575bd253fa44 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Mon, 20 May 2024 14:20:31 +0800 Subject: [PATCH 07/15] test(plugins/DbCopy): solve directory creation failure due to limited permissions --- plugins/src/test/java/org/tron/plugins/DbCopyTest.java | 6 +++--- plugins/src/test/java/org/tron/plugins/DbTest.java | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/plugins/src/test/java/org/tron/plugins/DbCopyTest.java b/plugins/src/test/java/org/tron/plugins/DbCopyTest.java index c5cc8f2bb31..9e488a592aa 100644 --- a/plugins/src/test/java/org/tron/plugins/DbCopyTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbCopyTest.java @@ -11,7 +11,7 @@ public class DbCopyTest extends DbTest { @Test public void testRun() { String[] args = new String[] { "db", "cp", INPUT_DIRECTORY, - tmpDir + UUID.randomUUID()}; + genarateTmpDir()}; Assert.assertEquals(0, cli.execute(args)); } @@ -32,7 +32,7 @@ public void testNotExist() { @Test public void testEmpty() throws IOException { String[] args = new String[] {"db", "cp", temporaryFolder.newFolder().toString(), - tmpDir + UUID.randomUUID()}; + genarateTmpDir()}; Assert.assertEquals(0, cli.execute(args)); } @@ -46,7 +46,7 @@ public void testDestIsExist() throws IOException { @Test public void testSrcIsFile() throws IOException { String[] args = new String[] {"db", "cp", temporaryFolder.newFile().toString(), - tmpDir + UUID.randomUUID()}; + genarateTmpDir()}; Assert.assertEquals(403, cli.execute(args)); } diff --git a/plugins/src/test/java/org/tron/plugins/DbTest.java b/plugins/src/test/java/org/tron/plugins/DbTest.java index 8a5f9de4a67..8605fa18d50 100644 --- a/plugins/src/test/java/org/tron/plugins/DbTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbTest.java @@ -86,4 +86,14 @@ private static void initDB(File file) throws IOException { } } } + + /** + * Generate a not-exist temporary directory path. + * @return temporary path + */ + public String genarateTmpDir() { + File dir = Paths.get(tmpDir, UUID.randomUUID().toString()).toFile(); + dir.deleteOnExit(); + return dir.getPath(); + } } From e6c19966d3433d51daf63c59b7b6d787296f6c04 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Wed, 20 Mar 2024 13:31:53 +0800 Subject: [PATCH 08/15] fix(backup/backupServer): make the thread pool close after the netty channel closes --- .../java/org/tron/common/backup/socket/BackupServer.java | 2 +- .../test/java/org/tron/common/backup/BackupServerTest.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/common/backup/socket/BackupServer.java b/framework/src/main/java/org/tron/common/backup/socket/BackupServer.java index 2acf1e12633..67739ac50d2 100644 --- a/framework/src/main/java/org/tron/common/backup/socket/BackupServer.java +++ b/framework/src/main/java/org/tron/common/backup/socket/BackupServer.java @@ -95,7 +95,6 @@ public void initChannel(NioDatagramChannel ch) public void close() { logger.info("Closing backup server..."); shutdown = true; - ExecutorServiceManager.shutdownAndAwaitTermination(executor, name); backupManager.stop(); if (channel != null) { try { @@ -104,6 +103,7 @@ public void close() { logger.warn("Closing backup server failed.", e); } } + ExecutorServiceManager.shutdownAndAwaitTermination(executor, name); logger.info("Backup server closed."); } } diff --git a/framework/src/test/java/org/tron/common/backup/BackupServerTest.java b/framework/src/test/java/org/tron/common/backup/BackupServerTest.java index 34b17ec186f..9bff6eed677 100644 --- a/framework/src/test/java/org/tron/common/backup/BackupServerTest.java +++ b/framework/src/test/java/org/tron/common/backup/BackupServerTest.java @@ -7,6 +7,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.junit.rules.Timeout; import org.tron.common.backup.socket.BackupServer; import org.tron.common.parameter.CommonParameter; import org.tron.core.Constant; @@ -17,6 +18,9 @@ public class BackupServerTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Rule + public Timeout globalTimeout = Timeout.seconds(60); private BackupServer backupServer; @Before @@ -40,5 +44,7 @@ public void tearDown() { @Test public void test() throws InterruptedException { backupServer.initServer(); + // wait for the server to start + Thread.sleep(1000); } } From 66f13e1714ea7d9fb9402348ab9b98f0fd26bfaf Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Thu, 23 May 2024 19:08:18 +0800 Subject: [PATCH 09/15] fix(block): skip check when blockCap.generatedByMyself. --- framework/src/main/java/org/tron/core/db/Manager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 764ad745d36..4e3a44caa79 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -1424,7 +1424,7 @@ public TransactionInfo processTransaction(final TransactionCapsule trxCap, Block if (Objects.nonNull(blockCap)) { chainBaseManager.getBalanceTraceStore().initCurrentTransactionBalanceTrace(trxCap); } - if (Objects.nonNull(blockCap) && !blockCap.generatedByMyself) { + if (Objects.isNull(blockCap) || !blockCap.generatedByMyself) { validateTapos(trxCap); validateCommon(trxCap); if (trxCap.getInstance().getRawData().getContractList().size() != 1) { From 02455446fb2e09b673b78b552cf65900dc4dd297 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Thu, 23 May 2024 21:11:58 +0800 Subject: [PATCH 10/15] log(math): roll back. --- framework/src/main/resources/logback.xml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/framework/src/main/resources/logback.xml b/framework/src/main/resources/logback.xml index a5e553bbb56..39c7f463172 100644 --- a/framework/src/main/resources/logback.xml +++ b/framework/src/main/resources/logback.xml @@ -51,22 +51,6 @@ - - ./logs/db/math.log - - ./logs/math/math.%i.log.gz - 500MB - 50GB - - - %m - UTF-8 - - - TRACE - - - 0 @@ -108,9 +92,6 @@ - - - From f02739f9bf1046998a6f9a30595891ac57bef259 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Fri, 24 May 2024 17:46:56 +0800 Subject: [PATCH 11/15] log(math): format double to hex. --- .../main/java/org/tron/common/math/MathWrapper.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/org/tron/common/math/MathWrapper.java b/common/src/main/java/org/tron/common/math/MathWrapper.java index 110613a52d8..129b9866e6d 100644 --- a/common/src/main/java/org/tron/common/math/MathWrapper.java +++ b/common/src/main/java/org/tron/common/math/MathWrapper.java @@ -148,9 +148,15 @@ public static double powjdk8(double a, double b) { double jdk8 = TronMath.pow(a, b); double strict = StrictMathWrapper.pow(a, b); if (Double.compare(jdk8, strict) != 0) { - logger.info("{}\t{}\t{}", String.format("%.16f", a), - String.format("%.16f", jdk8), String.format("%.16f", strict)); + logger.info("{}\t{}\t{}", doubleToHex(a), doubleToHex(jdk8), doubleToHex(strict)); } return jdk8; } + + public static String doubleToHex(double input) { + // Convert the starting value to the equivalent value in a long + long doubleAsLong = Double.doubleToRawLongBits(input); + // and then convert the long to a hex string + return Long.toHexString(doubleAsLong); + } } From e933eff9487beb620226b7b23eb770be0306dacd Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Fri, 24 May 2024 17:54:43 +0800 Subject: [PATCH 12/15] log(config): add config load. --- .../java/org/tron/program/SolidityNode.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/framework/src/main/java/org/tron/program/SolidityNode.java b/framework/src/main/java/org/tron/program/SolidityNode.java index fd5f7d56ef5..7b158669816 100644 --- a/framework/src/main/java/org/tron/program/SolidityNode.java +++ b/framework/src/main/java/org/tron/program/SolidityNode.java @@ -2,10 +2,13 @@ import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import java.io.File; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.atomic.AtomicLong; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.BooleanUtils; +import org.slf4j.LoggerFactory; import org.springframework.util.ObjectUtils; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; @@ -49,6 +52,23 @@ public SolidityNode(Manager dbManager) { remoteBlockNum.set(getLastSolidityBlockNum()); } + private static void load(String path) { + try { + File file = new File(path); + if (!file.exists() || !file.isFile() || !file.canRead()) { + return; + } + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(lc); + lc.reset(); + configurator.doConfigure(file); + logger.info("load logback configure file success"); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + /** * Start the SolidityNode. */ @@ -56,10 +76,7 @@ public static void main(String[] args) { logger.info("Solidity node is running."); Args.setParam(args, Constant.TESTNET_CONF); CommonParameter parameter = CommonParameter.getInstance(); - - logger.info("index switch is {}", - BooleanUtils.toStringOnOff(BooleanUtils - .toBoolean(parameter.getStorage().getIndexSwitch()))); + load(parameter.getLogbackPath()); if (ObjectUtils.isEmpty(parameter.getTrustNodeAddr())) { logger.error("Trust node is not set."); From d4b1cc4e4236c8c5cac40401b3600d3c61db2872 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Mon, 3 Jun 2024 17:42:41 +0800 Subject: [PATCH 13/15] feat(db/leveldb): update leveldbjni --- common/build.gradle | 4 ++-- plugins/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/build.gradle b/common/build.gradle index 6c1545e5d13..946a5fc855e 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -23,9 +23,9 @@ if (isWindows()) { } } else { ext { - leveldbGroup = "org.fusesource.leveldbjni" + leveldbGroup = "io.github.tronprotocol" leveldbName = "leveldbjni-all" - leveldbVersion = "1.8" + leveldbVersion = "1.18.3" } } diff --git a/plugins/build.gradle b/plugins/build.gradle index 7f226d7099c..54ae7cba1c6 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -33,8 +33,8 @@ dependencies { compile group: 'me.tongfei', name: 'progressbar', version: '0.9.3' compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' compile group: 'org.rocksdb', name: 'rocksdbjni', version: '5.15.10' - compile 'io.github.tronprotocol:leveldbjni-all:1.18.2' - compile 'io.github.tronprotocol:leveldb:1.18.2' + compile 'io.github.tronprotocol:leveldbjni-all:1.18.3' + compile 'io.github.tronprotocol:leveldb:1.18.3' compile project(":protocol") } From db56a8eb6a1711dfe78c5ec2c1f47a17f0878a93 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Mon, 3 Jun 2024 17:45:36 +0800 Subject: [PATCH 14/15] feat(db/rocksdb): update rocksdbjni --- .../rocksdb/RocksDbDataSourceImpl.java | 6 +++--- .../MarketOrderPriceComparatorForRockDB.java | 19 ++++++++----------- .../java/org/tron/core/db/TronDatabase.java | 4 ++-- .../tron/core/db/TronStoreWithRevoking.java | 4 ++-- .../store/MarketPairPriceToOrderStore.java | 4 ++-- common/build.gradle | 4 ++-- plugins/README.md | 4 +--- plugins/build.gradle | 6 +++--- .../main/java/org/tron/plugins/DbConvert.java | 8 +------- .../MarketOrderPriceComparatorForRockDB.java | 19 +++++++------------ .../java/org/tron/plugins/DbConvertTest.java | 2 +- 11 files changed, 32 insertions(+), 48 deletions(-) diff --git a/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java index 6c5d8018487..3b8afd7f35d 100644 --- a/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java +++ b/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java @@ -20,10 +20,10 @@ import java.util.stream.Collectors; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.rocksdb.AbstractComparator; import org.rocksdb.BlockBasedTableConfig; import org.rocksdb.BloomFilter; import org.rocksdb.Checkpoint; -import org.rocksdb.DirectComparator; import org.rocksdb.InfoLogLevel; import org.rocksdb.Logger; import org.rocksdb.Options; @@ -60,11 +60,11 @@ public class RocksDbDataSourceImpl extends DbStat implements DbSourceInter`: Input path for leveldb, default: output-directory/database. - ``: Output path for rocksdb, default: output-directory-dst/database. -- `--safe`: In safe mode, read data from leveldb then put into rocksdb, it's a very time-consuming procedure. If not, just change engine.properties from leveldb to rocksdb, rocksdb - is compatible with leveldb for the current version. This may not be the case in the future, default: false. - `-h | --help`: Provide the help info. ### Examples: ```shell script # full command - java -jar Toolkit.jar db convert [-h] [--safe] + java -jar Toolkit.jar db convert [-h] # examples java -jar Toolkit.jar db convert output-directory/database /tmp/database ``` diff --git a/plugins/build.gradle b/plugins/build.gradle index 54ae7cba1c6..b1108d8324d 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -32,9 +32,9 @@ dependencies { compile group: 'com.typesafe', name: 'config', version: '1.3.2' compile group: 'me.tongfei', name: 'progressbar', version: '0.9.3' compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' - compile group: 'org.rocksdb', name: 'rocksdbjni', version: '5.15.10' - compile 'io.github.tronprotocol:leveldbjni-all:1.18.3' - compile 'io.github.tronprotocol:leveldb:1.18.3' + compile group: 'org.rocksdb', name: 'rocksdbjni', version: '7.7.3' + compile 'com.halibobor:leveldbjni-all:1.18.3' + compile 'com.halibobor:leveldb:1.18.3' compile project(":protocol") } diff --git a/plugins/src/main/java/org/tron/plugins/DbConvert.java b/plugins/src/main/java/org/tron/plugins/DbConvert.java index a75b235bbcf..c5e69b74418 100644 --- a/plugins/src/main/java/org/tron/plugins/DbConvert.java +++ b/plugins/src/main/java/org/tron/plugins/DbConvert.java @@ -49,13 +49,7 @@ public class DbConvert implements Callable { description = "Output path for rocksdb. Default: ${DEFAULT-VALUE}") private File dest; - @CommandLine.Option(names = {"--safe"}, - description = "In safe mode, read data from leveldb then put rocksdb." - + "If not, just change engine.properties from leveldb to rocksdb," - + "rocksdb is compatible with leveldb for current version." - + "This may not be the case in the future." - + "Default: ${DEFAULT-VALUE}") - private boolean safe; + private final boolean safe = true; @CommandLine.Option(names = {"-h", "--help"}) private boolean help; diff --git a/plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForRockDB.java b/plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForRockDB.java index cd718bdd2d7..388711f99c6 100644 --- a/plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForRockDB.java +++ b/plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForRockDB.java @@ -1,11 +1,11 @@ package org.tron.plugins.comparator; +import java.nio.ByteBuffer; +import org.rocksdb.AbstractComparator; import org.rocksdb.ComparatorOptions; -import org.rocksdb.DirectSlice; -import org.rocksdb.util.DirectBytewiseComparator; import org.tron.plugins.utils.MarketUtils; -public class MarketOrderPriceComparatorForRockDB extends DirectBytewiseComparator { +public class MarketOrderPriceComparatorForRockDB extends AbstractComparator { public MarketOrderPriceComparatorForRockDB(final ComparatorOptions copt) { super(copt); @@ -17,21 +17,16 @@ public String name() { } @Override - public int compare(final DirectSlice a, final DirectSlice b) { + public int compare(final ByteBuffer a, final ByteBuffer b) { return MarketUtils.comparePriceKey(convertDataToBytes(a), convertDataToBytes(b)); } /** * DirectSlice.data().array will throw UnsupportedOperationException. * */ - public byte[] convertDataToBytes(DirectSlice directSlice) { - int capacity = directSlice.data().capacity(); - byte[] bytes = new byte[capacity]; - - for (int i = 0; i < capacity; i++) { - bytes[i] = directSlice.get(i); - } - + public byte[] convertDataToBytes(ByteBuffer buf) { + byte[] bytes = new byte[buf.remaining()]; + buf.get(bytes); return bytes; } diff --git a/plugins/src/test/java/org/tron/plugins/DbConvertTest.java b/plugins/src/test/java/org/tron/plugins/DbConvertTest.java index 150e47c9f65..0117a445418 100644 --- a/plugins/src/test/java/org/tron/plugins/DbConvertTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbConvertTest.java @@ -18,7 +18,7 @@ public void testRun() throws IOException { @Test public void testRunWithSafe() throws IOException { String[] args = new String[] { "db", "convert", INPUT_DIRECTORY, - temporaryFolder.newFolder().toString(),"--safe" }; + temporaryFolder.newFolder().toString()}; Assert.assertEquals(0, cli.execute(args)); } From 2a60ed9e20231505fc02560daf087504b0c55618 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Wed, 12 Jun 2024 18:27:24 +0800 Subject: [PATCH 15/15] feat(leveldb): add bloom filter . --- .../org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java index 43a24ff4416..b66eb5694f8 100644 --- a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java +++ b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java @@ -143,6 +143,7 @@ private void openDatabase(Options dbOptions) throws IOException { Files.createDirectories(dbPath.getParent()); } try { + dbOptions.bitsPerKey(10); database = factory.open(dbPath.toFile(), dbOptions); if (!this.getDBName().startsWith("checkpoint")) { logger