diff --git a/.jenkinsci/artifacts.groovy b/.jenkinsci/artifacts.groovy new file mode 100644 index 0000000000..3a5aa0a68b --- /dev/null +++ b/.jenkinsci/artifacts.groovy @@ -0,0 +1,44 @@ +#!/usr/bin/env groovy + +def uploadArtifacts(filePaths, uploadPath, artifactServers=['artifact.soramitsu.co.jp']) { + def baseUploadPath = 'files' + def filePathsConverted = [] + agentType = sh(script: 'uname', returnStdout: true).trim() + uploadPath = baseUploadPath + uploadPath + filePaths.each { + fp = sh(script: "ls -d ${it} | tr '\n' ','", returnStdout: true).trim() + filePathsConverted.addAll(fp.split(',')) + } + def shaSumBinary = 'sha256sum' + def md5SumBinary = 'md5sum' + if (agentType == 'Darwin') { + shaSumBinary = 'shasum -a 256' + md5SumBinary = 'md5 -r' + } + sh "> \$(pwd)/batch.txt" + filePathsConverted.each { + sh "echo put ${it} $uploadPath >> \$(pwd)/batch.txt;" + sh "$shaSumBinary ${it} | cut -d' ' -f1 > \$(pwd)/\$(basename ${it}).sha256" + sh "$md5SumBinary ${it} | cut -d' ' -f1 > \$(pwd)/\$(basename ${it}).md5" + sh "echo put \$(pwd)/\$(basename ${it}).sha256 $uploadPath >> \$(pwd)/batch.txt;" + sh "echo put \$(pwd)/\$(basename ${it}).md5 $uploadPath >> \$(pwd)/batch.txt;" + } + // mkdirs recursively + uploadPath = uploadPath.split('/') + def p = '' + sh "> \$(pwd)/mkdirs.txt" + uploadPath.each { + p += "/${it}" + sh("echo -mkdir $p >> \$(pwd)/mkdirs.txt") + } + + sshagent(['jenkins-artifact']) { + sh "ssh-agent" + artifactServers.each { + sh "sftp -b \$(pwd)/mkdirs.txt jenkins@${it} || true" + sh "sftp -b \$(pwd)/batch.txt jenkins@${it}" + } + } +} + +return this diff --git a/.jenkinsci/bindings.groovy b/.jenkinsci/bindings.groovy index 4f20c09bb0..afc89afc82 100644 --- a/.jenkinsci/bindings.groovy +++ b/.jenkinsci/bindings.groovy @@ -1,27 +1,74 @@ #!/usr/bin/env groovy -def doBindings() { - def cmake_options = "" - if (params.JavaBindings) { - cmake_options += " -DSWIG_JAVA=ON " - } - if (params.PythonBindings) { - cmake_options += " -DSWIG_PYTHON=ON " - } - // In case language specific options were not set, - // build for each language - if (!params.JavaBindings && !params.PythonBindings) { - cmake_options += " -DSWIG_JAVA=ON -DSWIG_PYTHON=ON " - } +def doJavaBindings(buildType=Release) { + def currentPath = sh(script: "pwd", returnStdout: true).trim() + def commit = env.GIT_COMMIT + def artifactsPath = sprintf('%1$s/java-bindings-%2$s-%3$s-%4$s.zip', + [currentPath, buildType, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)]) sh """ cmake \ -H. \ -Bbuild \ - -DCMAKE_BUILD_TYPE=Release \ - ${cmake_options} + -DCMAKE_BUILD_TYPE=$buildType \ + -DSWIG_JAVA=ON + """ + sh "cd build; make -j${params.PARALLELISM} irohajava" + sh "zip -j $artifactsPath build/shared_model/bindings/*.java build/shared_model/bindings/libirohajava.so" + sh "cp $artifactsPath /tmp/bindings-artifact" + return artifactsPath +} + +def doPythonBindings(buildType=Release) { + def currentPath = sh(script: "pwd", returnStdout: true).trim() + def commit = env.GIT_COMMIT + def supportPython2 = "OFF" + def artifactsPath = sprintf('%1$s/python-bindings-%2$s-%3$s-%4$s-%5$s.zip', + [currentPath, env.PBVersion, buildType, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)]) + // do not use preinstalled libed25519 + sh "rm -rf /usr/local/include/ed25519*; unlink /usr/local/lib/libed25519.so; rm -f /usr/local/lib/libed25519.so.1.2.2" + if (env.PBVersion == "python2") { supportPython2 = "ON" } + sh """ + cmake \ + -H. \ + -Bbuild \ + -DCMAKE_BUILD_TYPE=$buildType \ + -DSWIG_PYTHON=ON \ + -DSUPPORT_PYTHON2=$supportPython2 """ sh "cmake --build build --target python_tests" - sh "cd build; make -j${params.PARALLELISM} irohajava irohapy" + sh "cd build; make -j${params.PARALLELISM} irohapy" + sh "protoc --proto_path=schema --python_out=build/shared_model/bindings block.proto primitive.proto commands.proto queries.proto responses.proto endpoint.proto" + sh "${env.PBVersion} -m grpc_tools.protoc --proto_path=schema --python_out=build/shared_model/bindings --grpc_python_out=build/shared_model/bindings endpoint.proto yac.proto ordering.proto loader.proto" + sh "zip -j $artifactsPath build/shared_model/bindings/*.py build/shared_model/bindings/*.so" + sh "cp $artifactsPath /tmp/bindings-artifact" + return artifactsPath +} + +def doAndroidBindings(abiVersion) { + def currentPath = sh(script: "pwd", returnStdout: true).trim() + def commit = env.GIT_COMMIT + def artifactsPath = sprintf('%1$s/android-bindings-%2$s-%3$s-%4$s-%5$s-%6$s.zip', + [currentPath, "\$PLATFORM", abiVersion, "\$BUILD_TYPE_A", sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)]) + sh """ + (cd /iroha; git init; git remote add origin https://github.com/hyperledger/iroha.git; \ + git fetch --depth 1 origin develop; git checkout -t origin/develop) + """ + sh """ + . /entrypoint.sh; \ + sed -i.bak "s~find_package(JNI REQUIRED)~SET(CMAKE_SWIG_FLAGS \\\${CMAKE_SWIG_FLAGS} -package \${PACKAGE})~" /iroha/shared_model/bindings/CMakeLists.txt; \ + # TODO: might not be needed in the future + sed -i.bak "/target_include_directories(\\\${SWIG_MODULE_irohajava_REAL_NAME} PUBLIC/,+3d" /iroha/shared_model/bindings/CMakeLists.txt; \ + sed -i.bak "s~swig_link_libraries(irohajava~swig_link_libraries(irohajava \"/protobuf/.build/lib\${PROTOBUF_LIB_NAME}.a\" \"\${NDK_PATH}/platforms/android-$abiVersion/\${ARCH}/usr/\${LIBP}/liblog.so\"~" /iroha/shared_model/bindings/CMakeLists.txt; \ + sed -i.bak "s~find_library(protobuf_LIBRARY protobuf)~find_library(protobuf_LIBRARY \${PROTOBUF_LIB_NAME})~" /iroha/cmake/Modules/Findprotobuf.cmake; \ + sed -i.bak "s~find_program(protoc_EXECUTABLE protoc~set(protoc_EXECUTABLE \"/protobuf/host_build/protoc\"~" /iroha/cmake/Modules/Findprotobuf.cmake; \ + cmake -H/iroha/shared_model -B/iroha/shared_model/build -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=$abiVersion -DCMAKE_ANDROID_ARCH_ABI=\$PLATFORM \ + -DANDROID_NDK=\$NDK_PATH -DCMAKE_ANDROID_STL_TYPE=c++_static -DCMAKE_BUILD_TYPE=\$BUILD_TYPE_A -DTESTING=OFF \ + -DSHARED_MODEL_DISABLE_COMPATIBILITY=ON -DSWIG_JAVA=ON -DCMAKE_PREFIX_PATH=\$DEPS_DIR + """ + sh "cmake --build /iroha/shared_model/build --target irohajava -- -j${params.PARALLELISM}" + sh "zip -j $artifactsPath /iroha/shared_model/build/bindings/*.java /iroha/shared_model/build/bindings/libirohajava.so" + sh "cp $artifactsPath /tmp/bindings-artifact" + return artifactsPath } return this diff --git a/.jenkinsci/debug-build.groovy b/.jenkinsci/debug-build.groovy index dd430e67d6..8616097d6b 100644 --- a/.jenkinsci/debug-build.groovy +++ b/.jenkinsci/debug-build.groovy @@ -1,106 +1,115 @@ #!/usr/bin/env groovy def doDebugBuild(coverageEnabled=false) { + def dPullOrBuild = load ".jenkinsci/docker-pull-or-build.groovy" + def manifest = load ".jenkinsci/docker-manifest.groovy" + def pCommit = load ".jenkinsci/previous-commit.groovy" def parallelism = params.PARALLELISM + def platform = sh(script: 'uname -m', returnStdout: true).trim() + def previousCommit = pCommit.previousCommitOrCurrent() // params are always null unless job is started // this is the case for the FIRST build only. // So just set this to same value as default. // This is a known bug. See https://issues.jenkins-ci.org/browse/JENKINS-41929 - if (parallelism == null) { + if (!parallelism) { parallelism = 4 } - if ("arm7" in env.NODE_NAME) { + if (env.NODE_NAME.contains('arm7')) { parallelism = 1 } sh "docker network create ${env.IROHA_NETWORK}" + def iC = dPullOrBuild.dockerPullOrUpdate("${platform}-develop-build", + "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/develop/Dockerfile", + "${env.GIT_RAW_BASE_URL}/${previousCommit}/docker/develop/Dockerfile", + "${env.GIT_RAW_BASE_URL}/develop/docker/develop/Dockerfile", + ['PARALLELISM': parallelism]) - docker.image('postgres:9.5').run("" + if (GIT_LOCAL_BRANCH == 'develop' && manifest.manifestSupportEnabled()) { + manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop-build", + ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", + "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", + "${DOCKER_REGISTRY_BASENAME}:aarch64-develop-build"]) + manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:develop-build", + [ + [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", + arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], + [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", + arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], + [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-develop-build", + arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] + ]) + withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { + manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop-build", login, password) + } + } + docker.image('postgres:9.5').withRun("" + " -e POSTGRES_USER=${env.IROHA_POSTGRES_USER}" + " -e POSTGRES_PASSWORD=${env.IROHA_POSTGRES_PASSWORD}" + " --name ${env.IROHA_POSTGRES_HOST}" - + " --network=${env.IROHA_NETWORK}") - - def platform = sh(script: 'uname -m', returnStdout: true).trim() - sh "curl -L -o /tmp/${env.GIT_COMMIT}/Dockerfile --create-dirs https://raw.githubusercontent.com/hyperledger/iroha/${env.GIT_COMMIT}/docker/develop/${platform}/Dockerfile" - // pull docker image in case we don't have one - // speeds up consequent image builds as we simply tag them - sh "docker pull ${DOCKER_BASE_IMAGE_DEVELOP}" - if (env.BRANCH_NAME == 'develop') { - iC = docker.build("hyperledger/iroha:${GIT_COMMIT}-${BUILD_NUMBER}", "--build-arg PARALLELISM=${parallelism} -f /tmp/${env.GIT_COMMIT}/Dockerfile /tmp/${env.GIT_COMMIT}") - docker.withRegistry('https://registry.hub.docker.com', 'docker-hub-credentials') { - iC.push("${platform}-develop") - } - } - else { - iC = docker.build("hyperledger/iroha-workflow:${GIT_COMMIT}-${BUILD_NUMBER}", "-f /tmp/${env.GIT_COMMIT}/Dockerfile /tmp/${env.GIT_COMMIT} --build-arg PARALLELISM=${parallelism}") - } - iC.inside("" - + " -e IROHA_POSTGRES_HOST=${env.IROHA_POSTGRES_HOST}" - + " -e IROHA_POSTGRES_PORT=${env.IROHA_POSTGRES_PORT}" - + " -e IROHA_POSTGRES_USER=${env.IROHA_POSTGRES_USER}" - + " -e IROHA_POSTGRES_PASSWORD=${env.IROHA_POSTGRES_PASSWORD}" - + " --network=${env.IROHA_NETWORK}" - + " -v /var/jenkins/ccache:${CCACHE_DIR}") { - - def scmVars = checkout scm - def cmakeOptions = "" - if ( coverageEnabled ) { - cmakeOptions = " -DCOVERAGE=ON " - } - env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" - env.IROHA_HOME = "/opt/iroha" - env.IROHA_BUILD = "${env.IROHA_HOME}/build" + + " --network=${env.IROHA_NETWORK}") { + iC.inside("" + + " -e IROHA_POSTGRES_HOST=${env.IROHA_POSTGRES_HOST}" + + " -e IROHA_POSTGRES_PORT=${env.IROHA_POSTGRES_PORT}" + + " -e IROHA_POSTGRES_USER=${env.IROHA_POSTGRES_USER}" + + " -e IROHA_POSTGRES_PASSWORD=${env.IROHA_POSTGRES_PASSWORD}" + + " --network=${env.IROHA_NETWORK}" + + " -v /var/jenkins/ccache:${CCACHE_DIR}" + + " -v /tmp/${GIT_COMMIT}-${BUILD_NUMBER}:/tmp/${GIT_COMMIT}") { - sh """ - ccache --version - ccache --show-stats - ccache --zero-stats - ccache --max-size=5G - """ - sh """ - cmake \ - -DTESTING=ON \ - -H. \ - -Bbuild \ - -DCMAKE_BUILD_TYPE=Debug \ - -DIROHA_VERSION=${env.IROHA_VERSION} \ - ${cmakeOptions} - """ - sh "cmake --build build -- -j${parallelism}" - sh "ccache --show-stats" - if ( coverageEnabled ) { - sh "cmake --build build --target coverage.init.info" - } - def testExitCode = sh(script: 'cmake --build build --target test', returnStatus: true) - if (testExitCode != 0) { - currentBuild.result = "UNSTABLE" - } - if ( coverageEnabled ) { - sh "cmake --build build --target cppcheck" - // Sonar - if (env.CHANGE_ID != null) { - sh """ - sonar-scanner \ - -Dsonar.github.disableInlineComments \ - -Dsonar.github.repository='hyperledger/iroha' \ - -Dsonar.analysis.mode=preview \ - -Dsonar.login=${SONAR_TOKEN} \ - -Dsonar.projectVersion=${BUILD_TAG} \ - -Dsonar.github.oauth=${SORABOT_TOKEN} \ - -Dsonar.github.pullRequest=${CHANGE_ID} - """ + def scmVars = checkout scm + def cmakeOptions = "" + if ( coverageEnabled ) { + cmakeOptions = " -DCOVERAGE=ON " } + env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" + env.IROHA_HOME = "/opt/iroha" + env.IROHA_BUILD = "${env.IROHA_HOME}/build" - sh "cmake --build build --target coverage.info" - sh "python /tmp/lcov_cobertura.py build/reports/coverage.info -o build/reports/coverage.xml" - cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: '**/build/reports/coverage.xml', conditionalCoverageTargets: '75, 50, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '75, 50, 0', maxNumberOfBuilds: 50, methodCoverageTargets: '75, 50, 0', onlyStable: false, zoomCoverageChart: false - } - - // TODO: replace with upload to artifactory server - // develop branch only - if ( env.BRANCH_NAME == "develop" ) { - //archive(includes: 'build/bin/,compile_commands.json') + sh """ + ccache --version + ccache --show-stats + ccache --zero-stats + ccache --max-size=5G + """ + sh """ + cmake \ + -DTESTING=ON \ + -H. \ + -Bbuild \ + -DCMAKE_BUILD_TYPE=Debug \ + -DIROHA_VERSION=${env.IROHA_VERSION} \ + ${cmakeOptions} + """ + sh "cmake --build build -- -j${parallelism}" + sh "ccache --show-stats" + if ( coverageEnabled ) { + sh "cmake --build build --target coverage.init.info" + } + def testExitCode = sh(script: 'CTEST_OUTPUT_ON_FAILURE=1 cmake --build build --target test', returnStatus: true) + if (testExitCode != 0) { + currentBuild.result = "UNSTABLE" + } + if ( coverageEnabled ) { + sh "cmake --build build --target cppcheck" + // Sonar + if (env.CHANGE_ID != null) { + sh """ + sonar-scanner \ + -Dsonar.github.disableInlineComments \ + -Dsonar.github.repository='${DOCKER_REGISTRY_BASENAME}' \ + -Dsonar.analysis.mode=preview \ + -Dsonar.login=${SONAR_TOKEN} \ + -Dsonar.projectVersion=${BUILD_TAG} \ + -Dsonar.github.oauth=${SORABOT_TOKEN} \ + -Dsonar.github.pullRequest=${CHANGE_ID} + """ + } + sh "cmake --build build --target coverage.info" + sh "python /tmp/lcov_cobertura.py build/reports/coverage.info -o build/reports/coverage.xml" + cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: '**/build/reports/coverage.xml', conditionalCoverageTargets: '75, 50, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '75, 50, 0', maxNumberOfBuilds: 50, methodCoverageTargets: '75, 50, 0', onlyStable: false, zoomCoverageChart: false + } } } } + return this diff --git a/.jenkinsci/docker-manifest.groovy b/.jenkinsci/docker-manifest.groovy new file mode 100644 index 0000000000..c7b7c7c73b --- /dev/null +++ b/.jenkinsci/docker-manifest.groovy @@ -0,0 +1,28 @@ +#!/usr/bin/env groovy + +def manifestSupportEnabled() { + def dockerVersion = sh(script: "docker -v", returnStdout: true).trim() + def experimentalEnabled = sh(script: "grep -i experimental ~/.docker/config.json", returnStatus: true) + return experimentalEnabled == 0 && dockerVersion ==~ /^Docker version 18.*$/ + +} + +def manifestCreate(manifestListName, manifests) { + sh "docker manifest create ${manifestListName} ${manifests.join(' ')}" +} + +def manifestAnnotate(manifestListName, manifestsWithFeatures) { + manifestsWithFeatures.each { + sh """ + docker manifest annotate ${manifestListName} ${it['manifest']} --arch "${it['arch']}" \ + --os "${it['os']}" --os-features "${it['osfeatures'].join(',')}" --variant "${it['variant']}" + """ + } +} + +def manifestPush(manifestListName, dockerRegistryLogin, dockerRegistryPassword) { + sh "docker login -u '${dockerRegistryLogin}' -p '${dockerRegistryPassword}'" + sh "docker manifest push --purge ${manifestListName}" +} + +return this diff --git a/.jenkinsci/docker-pull-or-build.groovy b/.jenkinsci/docker-pull-or-build.groovy new file mode 100644 index 0000000000..d699ac40bb --- /dev/null +++ b/.jenkinsci/docker-pull-or-build.groovy @@ -0,0 +1,57 @@ +#!/usr/bin/env groovy + +def remoteFilesDiffer(f1, f2) { + sh "curl -L -o /tmp/${env.GIT_COMMIT}/f1 --create-dirs ${f1}" + sh "curl -L -o /tmp/${env.GIT_COMMIT}/f2 ${f2}" + diffExitCode = sh(script: "diff -q /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}/f2", returnStatus: true) + return diffExitCode != 0 +} + +def buildOptionsString(options) { + def s = '' + if (options) { + options.each { k, v -> + s += "--build-arg ${k}=${v} " + } + } + return s +} + +def dockerPullOrUpdate(imageName, currentDockerfileURL, previousDockerfileURL, referenceDockerfileURL, buildOptions=null) { + buildOptions = buildOptionsString(buildOptions) + def commit = sh(script: "echo ${GIT_LOCAL_BRANCH} | md5sum | cut -c 1-8", returnStdout: true).trim() + if (remoteFilesDiffer(currentDockerfileURL, previousDockerfileURL)) { + // Dockerfile has been changed compared to the previous commit + // Worst case scenario. We cannot count on the local cache + // because Dockerfile may contain apt-get entries that would try to update + // from invalid (stale) addresses + iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "${buildOptions} --no-cache -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") + } + else { + // first commit in this branch or Dockerfile modified + if (remoteFilesDiffer(currentDockerfileURL, referenceDockerfileURL)) { + // if we're lucky to build on the same agent, image will be built using cache + iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "$buildOptions -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") + } + else { + // try pulling image from Dockerhub, probably image is already there + def testExitCode = sh(script: "docker pull ${DOCKER_REGISTRY_BASENAME}:${imageName}", returnStatus: true) + if (testExitCode != 0) { + // image does not (yet) exist on Dockerhub. Build it + iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "$buildOptions --no-cache -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") + } + else { + // no difference found compared to both previous and reference Dockerfile + iC = docker.image("${DOCKER_REGISTRY_BASENAME}:${imageName}") + } + } + } + if (GIT_LOCAL_BRANCH ==~ /develop|master/) { + docker.withRegistry('https://registry.hub.docker.com', 'docker-hub-credentials') { + iC.push(imageName) + } + } + return iC +} + +return this diff --git a/.jenkinsci/linux-post-step.groovy b/.jenkinsci/linux-post-step.groovy new file mode 100644 index 0000000000..2cd92a675b --- /dev/null +++ b/.jenkinsci/linux-post-step.groovy @@ -0,0 +1,22 @@ +def linuxPostStep() { + timeout(time: 600, unit: "SECONDS") { + try { + if (currentBuild.currentResult == "SUCCESS" && GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + def artifacts = load ".jenkinsci/artifacts.groovy" + def commit = env.GIT_COMMIT + def platform = sh(script: 'uname -m', returnStdout: true).trim() + filePaths = [ '/tmp/${GIT_COMMIT}-${BUILD_NUMBER}/*' ] + artifacts.uploadArtifacts(filePaths, sprintf('/iroha/linux/%4$s/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6), platform])) + } + } + finally { + if (env.BUILD_TYPE == 'Debug') { + def cleanup = load ".jenkinsci/docker-cleanup.groovy" + cleanup.doDockerCleanup() + } + cleanWs() + } + } +} + +return this diff --git a/.jenkinsci/mac-release-build.groovy b/.jenkinsci/mac-release-build.groovy new file mode 100644 index 0000000000..02125d2632 --- /dev/null +++ b/.jenkinsci/mac-release-build.groovy @@ -0,0 +1,29 @@ +#!/usr/bin/env groovy + +def doReleaseBuild(coverageEnabled=false) { + def scmVars = checkout scm + env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" + env.IROHA_HOME = "/opt/iroha" + env.IROHA_BUILD = "${env.IROHA_HOME}/build" + + sh """ + export CCACHE_DIR=${CCACHE_RELEASE_DIR} + ccache --version + ccache --show-stats + ccache --zero-stats + ccache --max-size=1G + + cmake -H. \ + -Bbuild \ + -DCOVERAGE=OFF \ + -DPACKAGE_TGZ=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DIROHA_VERSION=${env.IROHA_VERSION} + + cmake --build build --target package -- -j${params.PARALLELISM} + mv ./build/iroha-${env.IROHA_VERSION}-*.tar.gz ./build/iroha.tar.gz + ccache --show-stats + """ +} + +return this diff --git a/.jenkinsci/previous-commit.groovy b/.jenkinsci/previous-commit.groovy new file mode 100644 index 0000000000..5a3916005e --- /dev/null +++ b/.jenkinsci/previous-commit.groovy @@ -0,0 +1,9 @@ +#!/usr/bin/env groovy + +def previousCommitOrCurrent() { + // GIT_PREVIOUS_COMMIT is null on first PR build + // regardless Jenkins docs saying it equals the current one on first build in branch + return !env.GIT_PREVIOUS_COMMIT ? env.GIT_COMMIT : env.GIT_PREVIOUS_COMMIT +} + +return this diff --git a/.jenkinsci/release-build.groovy b/.jenkinsci/release-build.groovy index 05d6613917..5e92053772 100644 --- a/.jenkinsci/release-build.groovy +++ b/.jenkinsci/release-build.groovy @@ -2,24 +2,21 @@ def doReleaseBuild() { def parallelism = params.PARALLELISM + def manifest = load ".jenkinsci/docker-manifest.groovy" // params are always null unless job is started // this is the case for the FIRST build only. // So just set this to same value as default. // This is a known bug. See https://issues.jenkins-ci.org/browse/JENKINS-41929 - if (parallelism == null) { + if (!parallelism) { parallelism = 4 } - if ("arm7" in env.NODE_NAME) { + if (env.NODE_NAME.contains('arm7')) { parallelism = 1 } def platform = sh(script: 'uname -m', returnStdout: true).trim() - sh "curl -L -o /tmp/${env.GIT_COMMIT}/Dockerfile --create-dirs https://raw.githubusercontent.com/hyperledger/iroha/${env.GIT_COMMIT}/docker/develop/${platform}/Dockerfile" - // pull docker image for building release package of Iroha - // speeds up consequent image builds as we simply tag them - sh "docker pull ${DOCKER_BASE_IMAGE_DEVELOP}" - iC = docker.build("hyperledger/iroha:${GIT_COMMIT}-${BUILD_NUMBER}", "--build-arg PARALLELISM=${parallelism} -f /tmp/${env.GIT_COMMIT}/Dockerfile /tmp/${env.GIT_COMMIT}") - sh "mkdir /tmp/${env.GIT_COMMIT}-${BUILD_NUMBER} || true" + iC = docker.image("${DOCKER_REGISTRY_BASENAME}:${platform}-develop-build") + iC.pull() iC.inside("" + " -v /tmp/${GIT_COMMIT}-${BUILD_NUMBER}:/tmp/${GIT_COMMIT}" + " -v /var/jenkins/ccache:${CCACHE_RELEASE_DIR}") { @@ -49,21 +46,58 @@ def doReleaseBuild() { sh "cmake --build build --target package -- -j${parallelism}" sh "ccache --show-stats" - // copy build package to the volume - sh "cp ./build/iroha-*.deb /tmp/${GIT_COMMIT}/iroha.deb" + // move build package to the volume + sh "mv ./build/iroha-*.deb /tmp/${GIT_COMMIT}/iroha.deb" + sh "mv ./build/*.tar.gz /tmp/${GIT_COMMIT}/iroha.tar.gz" } - sh "curl -L -o /tmp/${env.GIT_COMMIT}/Dockerfile --create-dirs https://raw.githubusercontent.com/hyperledger/iroha/${env.GIT_COMMIT}/docker/release/${platform}/Dockerfile" - sh "curl -L -o /tmp/${env.GIT_COMMIT}/entrypoint.sh https://raw.githubusercontent.com/hyperledger/iroha/${env.GIT_COMMIT}/docker/release/${platform}/entrypoint.sh" - sh "cp /tmp/${GIT_COMMIT}-${BUILD_NUMBER}/iroha.deb /tmp/${env.GIT_COMMIT}" + sh "curl -L -o /tmp/${env.GIT_COMMIT}/Dockerfile --create-dirs ${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/release/Dockerfile" + sh "curl -L -o /tmp/${env.GIT_COMMIT}/entrypoint.sh ${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/release/entrypoint.sh" + sh "mv /tmp/${GIT_COMMIT}-${BUILD_NUMBER}/iroha.deb /tmp/${env.GIT_COMMIT}" sh "chmod +x /tmp/${env.GIT_COMMIT}/entrypoint.sh" - iCRelease = docker.build("hyperledger/iroha:${GIT_COMMIT}-${BUILD_NUMBER}-release", "-f /tmp/${env.GIT_COMMIT}/Dockerfile /tmp/${env.GIT_COMMIT}") + iCRelease = docker.build("${DOCKER_REGISTRY_BASENAME}:${GIT_COMMIT}-${BUILD_NUMBER}-release", "--no-cache -f /tmp/${env.GIT_COMMIT}/Dockerfile /tmp/${env.GIT_COMMIT}") docker.withRegistry('https://registry.hub.docker.com', 'docker-hub-credentials') { - if (env.BRANCH_NAME == 'develop') { - iCRelease.push("${platform}-develop-latest") + if (env.GIT_LOCAL_BRANCH == 'develop') { + iCRelease.push("${platform}-develop") + if (manifest.manifestSupportEnabled()) { + manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop", + ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop", + "${DOCKER_REGISTRY_BASENAME}:armv7l-develop", + "${DOCKER_REGISTRY_BASENAME}:aarch64-develop"]) + manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:develop", + [ + [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-develop", + arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], + [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-develop", + arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], + [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-develop", + arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] + ]) + withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { + manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop", login, password) + } + } } - else if (env.BRANCH_NAME == 'master') { + else if (env.GIT_LOCAL_BRANCH == 'master') { iCRelease.push("${platform}-latest") + if (manifest.manifestSupportEnabled()) { + manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:latest", + ["${DOCKER_REGISTRY_BASENAME}:x86_64-latest", + "${DOCKER_REGISTRY_BASENAME}:armv7l-latest", + "${DOCKER_REGISTRY_BASENAME}:aarch64-latest"]) + manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:latest", + [ + [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-latest", + arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], + [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-latest", + arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], + [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-latest", + arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] + ]) + withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { + manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:latest", login, password) + } + } } } sh "docker rmi ${iCRelease.id}" diff --git a/.jenkinsci/remote-files-differ.groovy b/.jenkinsci/remote-files-differ.groovy new file mode 100644 index 0000000000..6ea43fdf27 --- /dev/null +++ b/.jenkinsci/remote-files-differ.groovy @@ -0,0 +1,10 @@ +#!/usr/bin/env groovy + +def remoteFilesDiffer(f1, f2) { + sh "curl -sSL -o /tmp/${env.GIT_COMMIT}/f1 --create-dirs ${f1}" + sh "curl -sSL -o /tmp/${env.GIT_COMMIT}/f2 ${f2}" + diffExitCode = sh(script: "diff -q /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}/f2", returnStatus: true) + return diffExitCode != 0 +} + +return this diff --git a/.jenkinsci/selected-branches-coverage.groovy b/.jenkinsci/selected-branches-coverage.groovy new file mode 100644 index 0000000000..1dd04792dd --- /dev/null +++ b/.jenkinsci/selected-branches-coverage.groovy @@ -0,0 +1,13 @@ +#!/usr/bin/env groovy + +def selectedBranchesCoverage(branches, PRCoverage=true) { + // trigger coverage if branch is either develop or master, or it is a PR + if (PRCoverage) { + return env.GIT_LOCAL_BRANCH in branches || env.CHANGE_ID != null + } + else { + return env.GIT_LOCAL_BRANCH in branches + } +} + +return this \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index 6a6d71bf16..c4362644e0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -16,11 +16,16 @@ properties([parameters([ booleanParam(defaultValue: false, description: '', name: 'ARMv7'), booleanParam(defaultValue: false, description: '', name: 'ARMv8'), booleanParam(defaultValue: true, description: '', name: 'MacOS'), - booleanParam(defaultValue: false, description: 'Whether it is a triggered build', name: 'Nightly'), booleanParam(defaultValue: false, description: 'Whether build docs or not', name: 'Doxygen'), booleanParam(defaultValue: false, description: 'Whether build Java bindings', name: 'JavaBindings'), + choice(choices: 'Release\nDebug', description: 'Java Bindings Build Type', name: 'JBBuildType'), booleanParam(defaultValue: false, description: 'Whether build Python bindings', name: 'PythonBindings'), - booleanParam(defaultValue: false, description: 'Whether build bindings only w/o Iroha itself', name: 'BindingsOnly'), + choice(choices: 'Release\nDebug', description: 'Python Bindings Build Type', name: 'PBBuildType'), + choice(choices: 'python3\npython2', description: 'Python Bindings Version', name: 'PBVersion'), + booleanParam(defaultValue: false, description: 'Whether build Android bindings', name: 'AndroidBindings'), + choice(choices: '26\n25\n24\n23\n22\n21\n20\n19\n18\n17\n16\n15\n14', description: 'Android Bindings ABI Version', name: 'ABABIVersion'), + choice(choices: 'Release\nDebug', description: 'Android Bindings Build Type', name: 'ABBuildType'), + choice(choices: 'arm64-v8a\narmeabi-v7a\narmeabi\nx86_64\nx86', description: 'Android Bindings Platform', name: 'ABPlatform'), string(defaultValue: '4', description: 'How much parallelism should we exploit. "4" is optimal for machines with modest amount of memory and at least 4 cores', name: 'PARALLELISM')])]) @@ -30,10 +35,8 @@ pipeline { CCACHE_RELEASE_DIR = '/opt/.ccache-release' SORABOT_TOKEN = credentials('SORABOT_TOKEN') SONAR_TOKEN = credentials('SONAR_TOKEN') - CODECOV_TOKEN = credentials('CODECOV_TOKEN') - DOCKERHUB = credentials('DOCKERHUB') - DOCKER_BASE_IMAGE_DEVELOP = 'hyperledger/iroha:develop' - DOCKER_BASE_IMAGE_RELEASE = 'hyperledger/iroha:latest' + GIT_RAW_BASE_URL = "https://raw.githubusercontent.com/hyperledger/iroha" + DOCKER_REGISTRY_BASENAME = "hyperledger/iroha" IROHA_NETWORK = "iroha-0${CHANGE_ID}-${GIT_COMMIT}-${BUILD_NUMBER}" IROHA_POSTGRES_HOST = "pg-0${CHANGE_ID}-${GIT_COMMIT}-${BUILD_NUMBER}" @@ -42,59 +45,45 @@ pipeline { IROHA_POSTGRES_PORT = 5432 } - triggers { - parameterizedCron(''' -0 23 * * * %BUILD_TYPE=Release; Linux=True; MacOS=True; ARMv7=False; ARMv8=True; Nightly=True; Doxygen=False; JavaBindings=False; PythonBindings=False; BindingsOnly=False; PARALLELISM=4 - ''') - } options { buildDiscarder(logRotator(numToKeepStr: '20')) } agent any stages { - stage ('Stop bad job builds') { + stage ('Stop same job builds') { agent { label 'master' } steps { script { - if (BRANCH_NAME != "develop") { - if (params.Nightly) { - // Stop this job running if it is nightly but not the develop it should be - def tmp = load ".jenkinsci/cancel-nightly-except-develop.groovy" - tmp.cancelThisJob() - } - else { - // Stop same job running builds if it is commit/PR build and not triggered as nightly - def builds = load ".jenkinsci/cancel-builds-same-job.groovy" - builds.cancelSameJobBuilds() - } - } - else { - if (!params.Nightly) { - // Stop same job running builds if it is develop but it is not nightly - def builds = load ".jenkinsci/cancel-builds-same-job.groovy" - builds.cancelSameJobBuilds() - } + if (GIT_LOCAL_BRANCH != "develop") { + def builds = load ".jenkinsci/cancel-builds-same-job.groovy" + builds.cancelSameJobBuilds() } } } } stage('Build Debug') { when { - allOf { - expression { params.BUILD_TYPE == 'Debug' } - expression { return !params.BindingsOnly } - } + expression { params.BUILD_TYPE == 'Debug' } } parallel { stage ('Linux') { - when { expression { return params.Linux } } + when { + beforeAgent true + expression { return params.Linux } + } agent { label 'x86_64' } steps { script { debugBuild = load ".jenkinsci/debug-build.groovy" - debugBuild.doDebugBuild(true) - if (BRANCH_NAME ==~ /(master|develop)/) { + coverage = load ".jenkinsci/selected-branches-coverage.groovy" + if (coverage.selectedBranchesCoverage(['develop', 'master'])) { + debugBuild.doDebugBuild(true) + } + else { + debugBuild.doDebugBuild() + } + if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { releaseBuild = load ".jenkinsci/release-build.groovy" releaseBuild.doReleaseBuild() } @@ -103,28 +92,29 @@ pipeline { post { always { script { - timeout(time: 60, unit: "SECONDS") { - def cleanup = load ".jenkinsci/docker-cleanup.groovy" - cleanup.doDockerCleanup() - cleanWs() - } + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } } } stage('ARMv7') { - when { expression { return params.ARMv7 } } + when { + beforeAgent true + expression { return params.ARMv7 } + } agent { label 'armv7' } steps { script { - def debugBuild = load ".jenkinsci/debug-build.groovy" - if (!params.Linux && !params.ARMv8 && !params.MacOS) { + debugBuild = load ".jenkinsci/debug-build.groovy" + coverage = load ".jenkinsci/selected-branches-coverage.groovy" + if (!params.Linux && !params.ARMv8 && !params.MacOS && (coverage.selectedBranchesCoverage(['develop', 'master']))) { debugBuild.doDebugBuild(true) } else { debugBuild.doDebugBuild() } - if (BRANCH_NAME ==~ /(master|develop)/) { + if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { releaseBuild = load ".jenkinsci/release-build.groovy" releaseBuild.doReleaseBuild() } @@ -133,28 +123,29 @@ pipeline { post { always { script { - timeout(time: 60, unit: "SECONDS") { - def cleanup = load ".jenkinsci/docker-cleanup.groovy" - cleanup.doDockerCleanup() - cleanWs() - } + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } } } stage('ARMv8') { - when { expression { return params.ARMv8 } } + when { + beforeAgent true + expression { return params.ARMv8 } + } agent { label 'armv8' } steps { script { - def debugBuild = load ".jenkinsci/debug-build.groovy" - if (!params.Linux && !params.MacOS) { + debugBuild = load ".jenkinsci/debug-build.groovy" + coverage = load ".jenkinsci/selected-branches-coverage.groovy" + if (!params.Linux && !params.MacOS && (coverage.selectedBranchesCoverage(['develop', 'master']))) { debugBuild.doDebugBuild(true) } else { debugBuild.doDebugBuild() } - if (BRANCH_NAME ==~ /(master|develop)/) { + if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { releaseBuild = load ".jenkinsci/release-build.groovy" releaseBuild.doReleaseBuild() } @@ -163,23 +154,24 @@ pipeline { post { always { script { - timeout(time: 60, unit: "SECONDS") { - def cleanup = load ".jenkinsci/docker-cleanup.groovy" - cleanup.doDockerCleanup() - cleanWs() - } + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } } } stage('MacOS'){ - when { expression { return params.MacOS } } + when { + beforeAgent true + expression { return params.MacOS } + } agent { label 'mac' } steps { script { def coverageEnabled = false def cmakeOptions = "" - if (!params.Linux) { + coverage = load ".jenkinsci/selected-branches-coverage.groovy" + if (!params.Linux && (coverage.selectedBranchesCoverage(['develop', 'master']))) { coverageEnabled = true cmakeOptions = " -DCOVERAGE=ON " } @@ -218,7 +210,7 @@ pipeline { """ def testExitCode = sh(script: 'IROHA_POSTGRES_HOST=localhost IROHA_POSTGRES_PORT=5433 cmake --build build --target test', returnStatus: true) if (testExitCode != 0) { - currentBuild.result = "UNSTABLE" + currentBuild.currentResult = "UNSTABLE" } if ( coverageEnabled ) { sh "cmake --build build --target cppcheck" @@ -237,25 +229,32 @@ pipeline { sh "cmake --build build --target coverage.info" sh "python /usr/local/bin/lcov_cobertura.py build/reports/coverage.info -o build/reports/coverage.xml" cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: '**/build/reports/coverage.xml', conditionalCoverageTargets: '75, 50, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '75, 50, 0', maxNumberOfBuilds: 50, methodCoverageTargets: '75, 50, 0', onlyStable: false, zoomCoverageChart: false - } - - // TODO: replace with upload to artifactory server - // only develop branch - if ( env.BRANCH_NAME == "develop" ) { - //archive(includes: 'build/bin/,compile_commands.json') + if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + releaseBuild = load ".jenkinsci/mac-release-build.groovy" + releaseBuild.doReleaseBuild() } } } post { always { script { - timeout(time: 60, unit: "SECONDS") { - cleanWs() - sh """ - pg_ctl -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ stop && \ - rm -rf /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ - """ + timeout(time: 600, unit: "SECONDS") { + try { + if (currentBuild.currentResult == "SUCCESS" && GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + def artifacts = load ".jenkinsci/artifacts.groovy" + def commit = env.GIT_COMMIT + filePaths = [ '\$(pwd)/build/*.tar.gz' ] + artifacts.uploadArtifacts(filePaths, sprintf('/iroha/macos/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)])) + } + } + finally { + cleanWs() + sh """ + pg_ctl -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ stop && \ + rm -rf /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ + """ + } } } } @@ -265,15 +264,15 @@ pipeline { } stage('Build Release') { when { - allOf { - expression { params.BUILD_TYPE == 'Release' } - expression { return ! params.BindingsOnly } - } + expression { params.BUILD_TYPE == 'Release' } } parallel { stage('Linux') { - when { expression { return params.Linux } } - agent { label 'linux && x86_64' } + when { + beforeAgent true + expression { return params.Linux } + } + agent { label 'x86_64' } steps { script { def releaseBuild = load ".jenkinsci/release-build.groovy" @@ -283,17 +282,17 @@ pipeline { post { always { script { - timeout(time: 60, unit: "SECONDS") { - def cleanup = load ".jenkinsci/docker-cleanup.groovy" - cleanup.doDockerCleanup() - cleanWs() - } + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } } } stage('ARMv7') { - when { expression { return params.ARMv7 } } + when { + beforeAgent true + expression { return params.ARMv7 } + } agent { label 'armv7' } steps { script { @@ -304,17 +303,17 @@ pipeline { post { always { script { - timeout(time: 60, unit: "SECONDS") { - def cleanup = load ".jenkinsci/docker-cleanup.groovy" - cleanup.doDockerCleanup() - cleanWs() - } + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } - } + } } stage('ARMv8') { - when { expression { return params.ARMv8 } } + when { + beforeAgent true + expression { return params.ARMv8 } + } agent { label 'armv8' } steps { script { @@ -325,44 +324,40 @@ pipeline { post { always { script { - timeout(time: 60, unit: "SECONDS") { - def cleanup = load ".jenkinsci/docker-cleanup.groovy" - cleanup.doDockerCleanup() - cleanWs() - } + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } - } + } } stage('MacOS') { - when { expression { return params.MacOS } } + when { + beforeAgent true + expression { return params.MacOS } + } + agent { label 'mac' } steps { script { - def scmVars = checkout scm - env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" - env.IROHA_HOME = "/opt/iroha" - env.IROHA_BUILD = "${env.IROHA_HOME}/build" - - sh """ - ccache --version - ccache --show-stats - ccache --zero-stats - ccache --max-size=5G - """ - sh """ - cmake \ - -H. \ - -Bbuild \ - -DCMAKE_BUILD_TYPE=${params.BUILD_TYPE} \ - -DIROHA_VERSION=${env.IROHA_VERSION} - """ - sh "cmake --build build -- -j${params.PARALLELISM}" - sh "ccache --show-stats" - - // TODO: replace with upload to artifactory server - // only develop branch - if ( env.BRANCH_NAME == "develop" ) { - //archive(includes: 'build/bin/,compile_commands.json') + def releaseBuild = load ".jenkinsci/mac-release-build.groovy" + releaseBuild.doReleaseBuild() + } + } + post { + always { + script { + timeout(time: 600, unit: "SECONDS") { + try { + if (currentBuild.currentResult == "SUCCESS" && GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + def artifacts = load ".jenkinsci/artifacts.groovy" + def commit = env.GIT_COMMIT + filePaths = [ '\$(pwd)/build/*.tar.gz' ] + artifacts.uploadArtifacts(filePaths, sprintf('/iroha/macos/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)])) + } + } + finally { + cleanWs() + } + } } } } @@ -371,9 +366,10 @@ pipeline { } stage('Build docs') { when { + beforeAgent true allOf { expression { return params.Doxygen } - expression { BRANCH_NAME ==~ /(master|develop)/ } + expression { GIT_LOCAL_BRANCH ==~ /(master|develop)/ } } } // build docs on any vacant node. Prefer `x86_64` over @@ -391,23 +387,85 @@ pipeline { } stage('Build bindings') { when { + beforeAgent true anyOf { - expression { return params.BindingsOnly } expression { return params.PythonBindings } expression { return params.JavaBindings } + expression { return params.AndroidBindings } } } agent { label 'x86_64' } + environment { + JAVA_HOME = '/usr/lib/jvm/java-8-oracle' + } steps { script { def bindings = load ".jenkinsci/bindings.groovy" + def dPullOrBuild = load ".jenkinsci/docker-pull-or-build.groovy" + def pCommit = load ".jenkinsci/previous-commit.groovy" def platform = sh(script: 'uname -m', returnStdout: true).trim() - sh "curl -L -o /tmp/${env.GIT_COMMIT}/Dockerfile --create-dirs https://raw.githubusercontent.com/hyperledger/iroha/${env.GIT_COMMIT}/docker/develop/${platform}/Dockerfile" - iC = docker.build("hyperledger/iroha-develop:${GIT_COMMIT}-${BUILD_NUMBER}", "-f /tmp/${env.GIT_COMMIT}/Dockerfile /tmp/${env.GIT_COMMIT} --build-arg PARALLELISM=${PARALLELISM}") - sh "rm -rf /tmp/${env.GIT_COMMIT}" - iC.inside { - def scmVars = checkout scm - bindings.doBindings() + def previousCommit = pCommit.previousCommitOrCurrent() + if (params.JavaBindings) { + iC = dPullOrBuild.dockerPullOrUpdate("$platform-develop-build", + "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/develop/Dockerfile", + "${env.GIT_RAW_BASE_URL}/${previousCommit}/docker/develop/Dockerfile", + "${env.GIT_RAW_BASE_URL}/develop/docker/develop/Dockerfile", + ['PARALLELISM': params.PARALLELISM]) + iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact") { + bindings.doJavaBindings(params.JBBuildType) + } + } + if (params.PythonBindings) { + iC = dPullOrBuild.dockerPullOrUpdate("$platform-develop-build", + "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/develop/Dockerfile", + "${env.GIT_RAW_BASE_URL}/${previousCommit}/docker/develop/Dockerfile", + "${env.GIT_RAW_BASE_URL}/develop/docker/develop/Dockerfile", + ['PARALLELISM': params.PARALLELISM]) + iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact") { + bindings.doPythonBindings(params.PBBuildType) + } + } + if (params.AndroidBindings) { + iC = dPullOrBuild.dockerPullOrUpdate("android-${params.ABPlatform}-${params.ABBuildType}", + "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/android/Dockerfile", + "${env.GIT_RAW_BASE_URL}/${previousCommit}/docker/android/Dockerfile", + "${env.GIT_RAW_BASE_URL}/develop/docker/android/Dockerfile", + ['PARALLELISM': params.PARALLELISM, 'PLATFORM': params.ABPlatform, 'BUILD_TYPE': params.ABBuildType]) + sh "curl -L -o /tmp/${env.GIT_COMMIT}/entrypoint.sh ${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/android/entrypoint.sh" + sh "chmod +x /tmp/${env.GIT_COMMIT}/entrypoint.sh" + iC.inside("-v /tmp/${env.GIT_COMMIT}/entrypoint.sh:/entrypoint.sh:ro -v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact") { + bindings.doAndroidBindings(params.ABABIVersion) + } + } + } + } + post { + always { + timeout(time: 600, unit: "SECONDS") { + script { + try { + if (currentBuild.currentResult == "SUCCESS") { + def artifacts = load ".jenkinsci/artifacts.groovy" + def commit = env.GIT_COMMIT + if (params.JavaBindings) { + javaBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/java-bindings-*.zip' ] + artifacts.uploadArtifacts(javaBindingsFilePaths, '/iroha/bindings/java') + } + if (params.PythonBindings) { + pythonBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/python-bindings-*.zip' ] + artifacts.uploadArtifacts(pythonBindingsFilePaths, '/iroha/bindings/python') + } + if (params.AndroidBindings) { + androidBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/android-bindings-*.zip' ] + artifacts.uploadArtifacts(androidBindingsFilePaths, '/iroha/bindings/android') + } + } + } + finally { + sh "rm -rf /tmp/${env.GIT_COMMIT}" + cleanWs() + } + } } } } diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 0bee5ed289..1885c6f9c2 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,21 +1,30 @@ ## Maintainers -| Name | GitHub Id | email | -|---|---|---| -| Makoto Takemiya | takemiyamakoto | takemiya@soramitsu.co.jp | -| Ryu Okada | ryuo88 | okada@soramitsu.co.jp | -| Taisei Igarashi | MizukiSonoko | igarashi@soramitsu.co.jp | -| Motohiko Abe | motxx | abe@soramitsu.co.jp | -| Daisuke Shimada | cimadai | dice.k1984@gmail.com | -| Sushant D. Mayekar | Sushantdm | Sushantdm@gmail.com | -| Yanno Ban | yannoban | ban.yanno@nbc.org.kh -| Hiroshi Sasagawa | SasagawaHiroshi | sasagawa_hiroshi@intec.co.jp | -| Takumi Yamashita | satelliteyes | yamashita@soramitsu.co.jp | -| Bogdan Vaneev | Warchant | bogdan@soramitsu.co.jp | -| Fyodor Muratov | muratovv | fyodor@soramitsu.co.jp | -| Andrei Lebedev | lebdron | andrei@soramitsu.co.jp | -| Bulat Nasrulin | grimadas | bulat@soramitsu.co.jp | -| Kamil Salakhiev | kamilsa | kamil@soramitsu.co.jp | -| Konstantin Munichev | luckychess | konstantin@soramitsu.co.jp | -| Evgenii Mininbaev | l4l | evgenii@soramitsu.co.jp | -| Nikolay Yushkevich | neewy | nikolai@soramitsu.co.jp | +Maintainers of Hyperledger Iroha project +are supposed to help contributors by explain them project details, +such as architecture, process, existing issues. + +This is the list of maintainers, including their email address for direct communications: + +| Name | GitHub Id | email | Area of expertise | +|------------------------|--------------------------|--------------------------------|---------------------------------| +| Makoto Takemiya | @takemiyamakoto | takemiya@soramitsu.co.jp | Product vision | +| Ryu Okada | @ryuo88 | okada@soramitsu.co.jp | Product vision | +| Nikolay Yushkevich | @neewy | nikolai@soramitsu.co.jp | Project state | +| Fyodor Muratov | @muratovv | fyodor@soramitsu.co.jp | Architecture, Java library, QA | +| Andrei Lebedev | @lebdron | andrei@soramitsu.co.jp | Research | +| Sergei Solonets | @Solonets | ssolonets@gmail.com | Development | +| Yanno Ban | @yannoban | ban.yanno@nbc.org.kh | Development | +| Dumitru Savva | @x3medima17 | savva@soramitsu.co.jp | Development | +| Nikita Alekseev | @nickaleks | alekseev@soramitsu.co.jp | Development | +| Victor Drobny | @victordrobny | drobny@soramitsu.co.jp | Development | +| Bulat Nasrulin | @grimadas | bulat@soramitsu.co.jp | Development | +| Kamil Salakhiev | @kamilsa | kamil@soramitsu.co.jp | Development | +| Igor Egorov | @igor-egorov | igor@soramitsu.co.jp | Development, Android library | +| Konstantin Munichev | @luckychess | konstantin@soramitsu.co.jp | Security | +| Evgenii Mininbaev | @l4l | evgenii@soramitsu.co.jp | Security, Python library | +| Vyacheslav Bikbaev | @laSinteZ | viacheslav@soramitsu.co.jp | Documentation, NodeJS library | +| Arseniy Fokin | @stinger112 | stinger112@gmail.com | NodeJS library | +| Alexey Chernyshov | @Alexey-N-Chernyshov | chernyshov@soramitsu.co.jp | Development | +| Artyom Bakhtin | @bakhtin | a@bakhtin.net | Ansible, Jenkins, artifacts | +| Anatoly Tyukushin | @tyvision | tyukushin@soramitsu.co.jp | Ansible, Jenkins | diff --git a/README.md b/README.md index e9b321ed34..50ca4195ab 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Blockchain platform Hyperledger Iroha is designed for simple creation and management of assets. This is a distributed ledger of transactions. -Check [overview](http://iroha.readthedocs.io/en/latest/overview/) page of our documentation. +Check [overview](http://iroha.readthedocs.io/en/latest/overview.html) page of our documentation. Iroha logo @@ -33,24 +33,25 @@ For more information, such as how to use client libraries in your target program ## Need help? -* Join [telegram chat](https://t.me/joinchat/AgzrTUCZ6edlj6V612n5JQ) where the maintainers team is able to help you -* Communicate in Gitter chat with our development community [![Join the chat at https://gitter.im/hyperledger-iroha/Lobby](https://badges.gitter.im/hyperledger-iroha/Lobby.svg)](https://gitter.im/hyperledger-iroha/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +* Join [telegram chat](https://t.me/hyperledgeriroha) where the maintainers team is able to help you +* Communicate in Gitter chat with our development community [![Join the chat at https://gitter.im/hyperledger-iroha/Lobby](https://badges.gitter.im/hyperledger-iroha/Lobby.svg)](https://gitter.im/hyperledger-iroha/Lobby) * Submit issues via GitHub Iroha repository * Join [HyperLedger RocketChat](https://chat.hyperledger.org) #iroha channel to discuss your concerns and proposals * Use mailing list to spread your word within Iroha development community [hyperledger-iroha@lists.hyperledger.org](mailto:hyperledger-iroha@lists.hyperledger.org) ## License -Copyright 2016 – 2018 Soramitsu Co., Ltd. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 +Iroha codebase is 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 http://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. + +Iroha documentation files are made available under the Creative Commons +Attribution 4.0 International License (CC-BY-4.0), available at +http://creativecommons.org/licenses/by/4.0/ diff --git a/cmake/Modules/Finded25519.cmake b/cmake/Modules/Finded25519.cmake index bb50631f50..23414de25f 100644 --- a/cmake/Modules/Finded25519.cmake +++ b/cmake/Modules/Finded25519.cmake @@ -12,15 +12,26 @@ find_package_handle_standard_args(ed25519 DEFAULT_MSG ) set(URL https://github.com/hyperledger/iroha-ed25519) -set(VERSION e7188b8393dbe5ac54378610d53630bd4a180038) +if (MSVC) + # trunk/1.2 with windows-specific changes + set(VERSION 31bb9b50e01b21ea2c21d33929e20934be4665b4) +else() + set(VERSION e7188b8393dbe5ac54378610d53630bd4a180038) +endif() set_target_description(ed25519 "Digital signature algorithm" ${URL} ${VERSION}) if (NOT ed25519_FOUND) + if (NOT WIN32) + find_package(Git REQUIRED) + set(PATCH_RANDOM ${GIT_EXECUTABLE} apply ${PROJECT_SOURCE_DIR}/patch/close.patch || true) + endif () + externalproject_add(hyperledger_ed25519 GIT_REPOSITORY ${URL} GIT_TAG ${VERSION} CMAKE_ARGS -DTESTING=OFF -DBUILD=STATIC - BUILD_BYPRODUCTS ${EP_PREFIX}/src/hyperledger_ed25519-build/libed25519.a + PATCH_COMMAND ${PATCH_RANDOM} + BUILD_BYPRODUCTS ${EP_PREFIX}/src/hyperledger_ed25519-build/${CMAKE_STATIC_LIBRARY_PREFIX}ed25519${CMAKE_STATIC_LIBRARY_SUFFIX} INSTALL_COMMAND "" # remove install step TEST_COMMAND "" # remove test step UPDATE_COMMAND "" # remove update step @@ -32,6 +43,13 @@ if (NOT ed25519_FOUND) file(MAKE_DIRECTORY ${ed25519_INCLUDE_DIR}) link_directories(${binary_dir}) + if(CMAKE_GENERATOR MATCHES "Visual Studio") + set_target_properties(ed25519 PROPERTIES + IMPORTED_LOCATION_DEBUG ${binary_dir}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}ed25519${CMAKE_STATIC_LIBRARY_SUFFIX} + IMPORTED_LOCATION_RELEASE ${binary_dir}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}ed25519${CMAKE_STATIC_LIBRARY_SUFFIX} + ) + endif() + add_dependencies(ed25519 hyperledger_ed25519) endif () diff --git a/cmake/Modules/Findgrpc.cmake b/cmake/Modules/Findgrpc.cmake index cc32a12ff0..2dde985c0f 100644 --- a/cmake/Modules/Findgrpc.cmake +++ b/cmake/Modules/Findgrpc.cmake @@ -15,6 +15,9 @@ mark_as_advanced(grpc_grpc++_LIBRARY) find_library(gpr_LIBRARY gpr) mark_as_advanced(gpr_LIBRARY) +find_library(address_sorting_LIBRARY address_sorting) +mark_as_advanced(address_sorting_LIBRARY) + find_program(grpc_CPP_PLUGIN grpc_cpp_plugin) mark_as_advanced(grpc_CPP_PLUGIN) @@ -26,7 +29,7 @@ find_package_handle_standard_args(grpc DEFAULT_MSG ) set(URL https://github.com/grpc/grpc) -set(VERSION bfcbad3b86c7912968dc8e64f2121c920dad4dfb) +set(VERSION bd44e485f69d70ca4095cea92decd98de3892aa6) # Release 1.11.0 set_target_description(grpc "Remote Procedure Call library" ${URL} ${VERSION}) if (NOT grpc_FOUND) @@ -40,8 +43,6 @@ if (NOT grpc_FOUND) -DProtobuf_DIR=${EP_PREFIX}/src/google_protobuf-build/lib/cmake/protobuf -DgRPC_ZLIB_PROVIDER=package -DBUILD_SHARED_LIBS=ON - PATCH_COMMAND - ${GIT_EXECUTABLE} apply ${PROJECT_SOURCE_DIR}/patch/fix-protobuf-package-include.patch || true BUILD_BYPRODUCTS ${EP_PREFIX}/src/grpc_grpc-build/grpc_cpp_plugin ${EP_PREFIX}/src/grpc_grpc-build/${CMAKE_SHARED_LIBRARY_PREFIX}gpr${CMAKE_SHARED_LIBRARY_SUFFIX} @@ -56,6 +57,7 @@ if (NOT grpc_FOUND) set(gpr_LIBRARY ${binary_dir}/${CMAKE_SHARED_LIBRARY_PREFIX}gpr${CMAKE_SHARED_LIBRARY_SUFFIX}) set(grpc_LIBRARY ${binary_dir}/${CMAKE_SHARED_LIBRARY_PREFIX}grpc${CMAKE_SHARED_LIBRARY_SUFFIX}) set(grpc_grpc++_LIBRARY ${binary_dir}/${CMAKE_SHARED_LIBRARY_PREFIX}grpc++${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(address_sorting_LIBRARY ${binary_dir}/${CMAKE_SHARED_LIBRARY_PREFIX}address_sorting${CMAKE_SHARED_LIBRARY_SUFFIX}) set(grpc_CPP_PLUGIN ${binary_dir}/grpc_cpp_plugin) file(MAKE_DIRECTORY ${grpc_INCLUDE_DIR}) link_directories(${binary_dir}) @@ -90,4 +92,5 @@ if(ENABLE_LIBS_PACKAGING) add_install_step_for_lib(${grpc_LIBRARY}) add_install_step_for_lib(${grpc_grpc++_LIBRARY}) add_install_step_for_lib(${gpr_LIBRARY}) + add_install_step_for_lib(${address_sorting_LIBRARY}) endif() diff --git a/cmake/Modules/Findprotobuf.cmake b/cmake/Modules/Findprotobuf.cmake index ac033a5391..0b7d61e338 100644 --- a/cmake/Modules/Findprotobuf.cmake +++ b/cmake/Modules/Findprotobuf.cmake @@ -19,7 +19,7 @@ find_package_handle_standard_args(protobuf DEFAULT_MSG ) set(URL https://github.com/google/protobuf.git) -set(VERSION 80a37e0782d2d702d52234b62dd4b9ec74fd2c95) +set(VERSION 106ffc04be1abf3ff3399f54ccf149815b287dd9) # Protocol Buffers v3.5.1 set_target_description(protobuf "Protocol buffers library" ${URL} ${VERSION}) if (NOT protobuf_FOUND) diff --git a/cmake/Modules/Findswig.cmake b/cmake/Modules/Findswig.cmake index 449a957902..e012686edf 100644 --- a/cmake/Modules/Findswig.cmake +++ b/cmake/Modules/Findswig.cmake @@ -8,8 +8,9 @@ find_package_handle_standard_args(SWIG DEFAULT_MSG if(NOT SWIG_EXECUTABLE) find_package(Git REQUIRED) - set(URL ftp://www.mirrorservice.org/sites/ftp.sourceforge.net/pub/sourceforge/s/sw/swig/swig/swig-3.0.12/swig-3.0.12.tar.gz) - set_target_description(swig "Simplified Wrapper and Interface Generator (SWIG)" ${URL} 3.0.12) + set(SWIG_VERSION 3.0.12) + set(URL ftp://www.mirrorservice.org/sites/ftp.sourceforge.net/pub/sourceforge/s/sw/swig/swig/swig-${SWIG_VERSION}/swig-${SWIG_VERSION}.tar.gz) + set_target_description(swig "Simplified Wrapper and Interface Generator (SWIG)" ${URL} ${SWIG_VERSION}) ExternalProject_Add(swig_swig URL ${URL} @@ -20,12 +21,13 @@ if(NOT SWIG_EXECUTABLE) BUILD_IN_SOURCE ON BUILD_COMMAND ${MAKE} swig TEST_COMMAND "" # remove test step + UPDATE_COMMAND "" # remove update step ) ExternalProject_Get_Property(swig_swig source_dir) # Predefined vars for local installed SWIG set(SWIG_EXECUTABLE ${source_dir}/swig) - set(SWIG_DIR ${source_dir}) + set(SWIG_DIR ${source_dir}/share/swig/${SWIG_VERSION}) add_dependencies(swig swig_swig) diff --git a/cmake/functions.cmake b/cmake/functions.cmake index 2b9368c6c8..00d73b9540 100644 --- a/cmake/functions.cmake +++ b/cmake/functions.cmake @@ -27,15 +27,27 @@ function(addtest test_name SOURCES) add_executable(${test_name} ${SOURCES}) target_link_libraries(${test_name} gtest gmock) target_include_directories(${test_name} PUBLIC ${PROJECT_SOURCE_DIR}/test) + + # fetch directory after test in source dir call + # for example: + # "/Users/user/iroha/test/integration/acceptance" + # match to "integration" + string(REGEX REPLACE ".*test\\/([a-zA-Z]+).*" "\\1" output ${CMAKE_CURRENT_SOURCE_DIR}) + add_test( - NAME ${test_name} + NAME "${output}_${test_name}" COMMAND $ ${test_xml_output} ) - strictmode(${test_name}) + if (NOT MSVC) + # protobuf generates warnings at the moment + strictmode(${test_name}) + endif () if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) target_compile_options(${test_name} PRIVATE -Wno-inconsistent-missing-override) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # do nothing, but also don't spam warning on each test else () message(AUTHOR_WARNING "Unknown compiler: building target ${target} with default options") endif () @@ -51,10 +63,17 @@ endfunction() function(compile_proto_to_cpp PROTO) string(REGEX REPLACE "\\.proto$" ".pb.h" GEN_PB_HEADER ${PROTO}) string(REGEX REPLACE "\\.proto$" ".pb.cc" GEN_PB ${PROTO}) + if (MSVC) + set(GEN_COMMAND "${Protobuf_PROTOC_EXECUTABLE}") + set(GEN_ARGS ${Protobuf_INCLUDE_DIR}) + else() + set(GEN_COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=${protobuf_LIBRARY_DIR}:$ENV{LD_LIBRARY_PATH} "${protoc_EXECUTABLE}") + set(GEN_ARGS ${protobuf_INCLUDE_DIR}) + endif() add_custom_command( OUTPUT ${IROHA_SCHEMA_DIR}/${GEN_PB_HEADER} ${IROHA_SCHEMA_DIR}/${GEN_PB} - COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=${protobuf_LIBRARY_DIR}:$ENV{LD_LIBRARY_PATH} "${protoc_EXECUTABLE}" - ARGS -I${protobuf_INCLUDE_DIR} -I. --cpp_out=${IROHA_SCHEMA_DIR} ${PROTO} + COMMAND ${GEN_COMMAND} + ARGS -I${GEN_ARGS} -I. --cpp_out=${IROHA_SCHEMA_DIR} ${PROTO} DEPENDS protoc WORKING_DIRECTORY ${IROHA_SCHEMA_DIR} ) diff --git a/deploy/ansible/playbooks/iroha-standalone-nodes/iroha-deploy.yml b/deploy/ansible/playbooks/iroha-standalone-nodes/iroha-deploy.yml index be332ff798..42a106b882 100644 --- a/deploy/ansible/playbooks/iroha-standalone-nodes/iroha-deploy.yml +++ b/deploy/ansible/playbooks/iroha-standalone-nodes/iroha-deploy.yml @@ -12,4 +12,4 @@ changed_when: False roles: - { role: docker, tags: docker } - - { role: iroha-standalone-deploy-node } + - { role: iroha-standalone-deploy-node, tags: iroha-standalone-deploy-node } diff --git a/deploy/ansible/roles/iroha-cluster-deploy-node/defaults/main.yml b/deploy/ansible/roles/iroha-cluster-deploy-node/defaults/main.yml index cdcda7679c..1804f0a11b 100644 --- a/deploy/ansible/roles/iroha-cluster-deploy-node/defaults/main.yml +++ b/deploy/ansible/roles/iroha-cluster-deploy-node/defaults/main.yml @@ -7,7 +7,7 @@ iroha_net: iroha_network # name of docker network containerConfPath: /opt/iroha_data # path to folder with config files inside docker container - irohaDockerImage: hyperledger/iroha-docker # docker image name - irohaDockerImageTag: develop_latest # docker image tag + irohaDockerImage: hyperledger/iroha # docker image name + irohaDockerImageTag: develop # docker image tag dbDockerImage: postgres dbDockerImageTag: 9.5 diff --git a/deploy/ansible/roles/iroha-standalone-deploy-node/defaults/main.yml b/deploy/ansible/roles/iroha-standalone-deploy-node/defaults/main.yml index 15a0c7c26d..b719450d0e 100644 --- a/deploy/ansible/roles/iroha-standalone-deploy-node/defaults/main.yml +++ b/deploy/ansible/roles/iroha-standalone-deploy-node/defaults/main.yml @@ -7,7 +7,7 @@ iroha_net: iroha_network # name of docker network containerConfPath: /opt/iroha_data - irohaDockerImage: hyperledger/iroha-docker # docker image name - irohaDockerImageTag: develop_latest # docker image tag + irohaDockerImage: hyperledger/iroha # docker image name + irohaDockerImageTag: develop # docker image tag dbDockerImage: postgres dbDockerImageTag: 9.5 diff --git a/docker/android/Dockerfile b/docker/android/Dockerfile new file mode 100644 index 0000000000..eb70b69ddb --- /dev/null +++ b/docker/android/Dockerfile @@ -0,0 +1,52 @@ +# using fresh 18.04 as it contains suitable `cmake` in repos +FROM ubuntu:18.04 + +# number of concurrent threads during build +# usage: docker build --build-arg PARALLELISM=8 -t name/name . +ARG PARALLELISM=1 +ARG BUILD_TYPE_A +ENV BUILD_TYPE_A=${BUILD_TYPE_A:-Release} +ARG VERSION +ENV VERSION=${VERSION:-26} +ARG PACKAGE +ENV PACKAGE=${PACKAGE:-jp.co.soramitsu.iroha.android} +# valid platforms: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64 +ARG PLATFORM +ENV PLATFORM=${PLATFORM:-x86_64} + +ENV NDK_PATH="/android-ndk/android-ndk-r16b" +ENV DEPS_DIR="/iroha/dependencies" + +RUN apt-get update && \ + apt-get -y install --no-install-recommends git curl apt-utils software-properties-common libpthread-stubs0-dev libpcre3-dev \ + unzip zip build-essential automake libtool ca-certificates ccache zlib1g-dev libcurl4-openssl-dev libc6-dbg cmake; \ + rm -rf /var/lib/apt/lists/* + +RUN set -e; mkdir -p $DEPS_DIR/include $DEPS_DIR/lib + +# boost 1.66 +RUN set -e; \ + curl -L -o /tmp/boost_1_66_0.tar.gz https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.gz; \ + tar -zxf /tmp/boost_1_66_0.tar.gz -C /tmp; mv /tmp/boost_1_66_0/boost $DEPS_DIR/include; rm -f /tmp/boost_1_66_0.tar.gz + +# install android-ndk-r16b +RUN set -e; \ + curl -L -o /tmp/android-ndk.zip https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip; unzip -q /tmp/android-ndk.zip -d /android-ndk; rm -f /tmp/android-ndk.zip + +# protobuf +RUN set -ex; \ + git clone https://github.com/google/protobuf; \ + (cd ./protobuf ; git checkout b5fbb742af122b565925987e65c08957739976a7); \ + cmake -Dprotobuf_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=$BUILD_TYPE_A -H./protobuf/cmake -B./protobuf/host_build; \ + VERBOSE=1 cmake --build ./protobuf/host_build -- -j$PARALLELISM; \ + sed -i.bak "s~COMMAND js_embed~COMMAND \"$PWD/protobuf/host_build/js_embed\"~" ./protobuf/cmake/libprotoc.cmake; \ + LDFLAGS="-llog -landroid" cmake -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=$VERSION -DCMAKE_ANDROID_ARCH_ABI=$PLATFORM -DANDROID_NDK=$NDK_PATH -DCMAKE_ANDROID_STL_TYPE=c++_static -DCMAKE_INSTALL_PREFIX=$DEPS_DIR -Dprotobuf_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE="$BUILD_TYPE_A" -H./protobuf/cmake -B./protobuf/.build; \ + VERBOSE=1 cmake --build ./protobuf/.build --target install -- -j$PARALLELISM + +# ed25519 +RUN set -e; \ + git clone git://github.com/hyperledger/iroha-ed25519; \ + (cd ./iroha-ed25519 ; git checkout e7188b8393dbe5ac54378610d53630bd4a180038); \ + cmake -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=$VERSION -DCMAKE_ANDROID_ARCH_ABI=$PLATFORM -DANDROID_NDK=$NDK_PATH -DCMAKE_ANDROID_STL_TYPE=c++_static -DCMAKE_INSTALL_PREFIX=$DEPS_DIR -DTESTING=OFF -DCMAKE_BUILD_TYPE=$BUILD_TYPE_A -DBUILD=STATIC -H./iroha-ed25519 -B./iroha-ed25519/build; \ + VERBOSE=1 cmake --build ./iroha-ed25519/build --target install -- -j$PARALLELISM; \ + mv "$DEPS_DIR"/lib/static/libed25519.a "$DEPS_DIR"/lib; rmdir "$DEPS_DIR"/lib/static/ diff --git a/docker/android/entrypoint.sh b/docker/android/entrypoint.sh new file mode 100644 index 0000000000..10ff2ba2d4 --- /dev/null +++ b/docker/android/entrypoint.sh @@ -0,0 +1,33 @@ +#!/bin/bash +export LIBP=lib +case "$PLATFORM" in + armeabi) + export ARCH=arch-arm + ;; + armeabi-v7a) + export ARCH=arch-arm + ;; + arm64-v8a) + export ARCH=arch-arm64 + ;; + x86) + export ARCH=arch-x86 + ;; + x86_64) + export ARCH=arch-x86_64 + export LIBP=lib64 + ;; + *) + echo Wrong ABI name: "$PLATFORM" + exit 1 + ;; +esac + +if [ "$BUILD_TYPE_A" = "Release" ]; then + export PROTOBUF_LIB_NAME=protobuf +elif [ "$BUILD_TYPE_A" = "Debug" ]; then + export PROTOBUF_LIB_NAME=protobufd +else + echo "Unknown build type: $BUILD_TYPE_A" + exit 1 +fi diff --git a/docker/develop/aarch64/Dockerfile b/docker/develop/Dockerfile similarity index 82% rename from docker/develop/aarch64/Dockerfile rename to docker/develop/Dockerfile index 15d79ff37c..5d480fc386 100644 --- a/docker/develop/aarch64/Dockerfile +++ b/docker/develop/Dockerfile @@ -8,14 +8,13 @@ ARG CMAKE_BUILD_TYPE=Release ENV IROHA_HOME /opt/iroha ENV IROHA_BUILD /opt/iroha/build -RUN apt-get update; \ - apt-get -y upgrade; \ +RUN apt-get update && \ apt-get -y --no-install-recommends install apt-utils software-properties-common; \ apt-get -y clean # add git repository -RUN add-apt-repository -y ppa:git-core/ppa; \ +RUN add-apt-repository -y ppa:git-core/ppa && \ apt-get update @@ -24,17 +23,21 @@ RUN set -e; \ automake libtool \ # dev dependencies libssl-dev zlib1g-dev libcurl4-openssl-dev libc6-dbg golang \ - # CircleCI dependencies - git ssh tar gzip ca-certificates python3 python3-pip python3-setuptools \ + # CI dependencies + git ssh tar gzip ca-certificates \ + # Pythons + python-pip python3-pip python3-setuptools python-dev \ + # SWIG dependencies + libpcre3-dev autoconf bison \ # other - wget curl file unzip gdb iputils-ping vim ccache \ - gcovr cppcheck doxygen graphviz graphviz-dev; \ + wget curl file gdb ccache \ + gcovr cppcheck doxygen graphviz graphviz-dev unzip zip; \ apt-get -y clean -# install cmake 3.7.2 +# install cmake 3.10.2 RUN set -e; \ git clone https://gitlab.kitware.com/cmake/cmake.git /tmp/cmake; \ - (cd /tmp/cmake ; git checkout 35413bf2c1b33980afd418030af27f184872af6b); \ + (cd /tmp/cmake ; git checkout c1e087a9d3af74299d7681c9f9de59e5977a1539); \ (cd /tmp/cmake ; /tmp/cmake/bootstrap --system-curl --parallel=${PARALLELISM} --enable-ccache); \ make -j${PARALLELISM} -C /tmp/cmake; \ make -C /tmp/cmake install; \ @@ -52,10 +55,10 @@ RUN set -e; \ ldconfig; \ rm -rf /tmp/boost -# install protobuf +# install protobuf v3.5.1 RUN set -e; \ git clone https://github.com/google/protobuf /tmp/protobuf; \ - (cd /tmp/protobuf ; git checkout 80a37e0782d2d702d52234b62dd4b9ec74fd2c95); \ + (cd /tmp/protobuf ; git checkout 106ffc04be1abf3ff3399f54ccf149815b287dd9); \ cmake \ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ -Dprotobuf_BUILD_TESTS=OFF \ @@ -90,11 +93,10 @@ RUN set -e; \ ldconfig; \ rm -rf /tmp/c-ares -# install grpc +# install grpc 1.11.0 RUN set -e; \ git clone https://github.com/grpc/grpc /tmp/grpc; \ - cd /tmp/grpc; \ - git checkout bfcbad3b86c7912968dc8e64f2121c920dad4dfb; \ + (cd /tmp/grpc ; git checkout bd44e485f69d70ca4095cea92decd98de3892aa6); \ cmake \ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ -DgRPC_BENCHMARK_PROVIDER="" \ @@ -106,12 +108,7 @@ RUN set -e; \ -DBUILD_SHARED_LIBS=ON \ -H/tmp/grpc \ -B/tmp/grpc/.build; \ - cd /tmp/grpc/.build; \ - make -j${PARALLELISM} gpr grpc grpc++ grpc_cpp_plugin; \ - # copy libs to /usr/local/lib - cp libgpr.so libgrpc.so libgrpc++.so libgrpc_plugin_support.so /usr/local/lib; \ - cp grpc_cpp_plugin /usr/local/bin; \ - cp -R ../include /usr/local; \ + cmake --build /tmp/grpc/.build --target install -- -j${PARALLELISM}; \ ldconfig; \ rm -rf /tmp/grpc @@ -203,15 +200,6 @@ RUN set -e; \ ldconfig; \ rm -rf /tmp/tbb -# install docker -ENV DOCKER_VERSION=17.06.0-ce -RUN set -e; \ - curl -L -o /tmp/docker-${DOCKER_VERSION}.tgz https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz; \ - tar -xz -C /tmp -f /tmp/docker-${DOCKER_VERSION}.tgz; \ - mv /tmp/docker/* /usr/bin; \ - rm /tmp/docker-${DOCKER_VERSION}.tgz; \ - rm -rf /tmp/docker - # install sonar cli ENV SONAR_CLI_VERSION=3.0.3.778 RUN set -e; \ @@ -236,10 +224,38 @@ RUN set -e; \ rm -rf /tmp/ed25519 # fetch lcov reports converter -RUN curl -L -o /tmp/lcov_cobertura.py https://raw.githubusercontent.com/eriwen/lcov-to-cobertura-xml/8c55cd11f80a21e7e46f20f8c81fcde0bf11f5e5/lcov_cobertura/lcov_cobertura.py +RUN set -e; \ + curl -L -o /tmp/lcov_cobertura.py https://raw.githubusercontent.com/eriwen/lcov-to-cobertura-xml/8c55cd11f80a21e7e46f20f8c81fcde0bf11f5e5/lcov_cobertura/lcov_cobertura.py + +RUN set -e; \ + add-apt-repository -y ppa:webupd8team/java; \ + apt-get update; \ + echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections; \ + apt-get -y install oracle-java8-installer; \ + java -version + +# Build SWIG +RUN set -e; \ + curl -L -o /tmp/swig-3.0.12.tar.gz https://github.com/swig/swig/archive/rel-3.0.12.tar.gz; \ + tar -C /tmp -zxf /tmp/swig-3.0.12.tar.gz; \ + cd /tmp/swig-rel-3.0.12; \ + ./autogen.sh && ./configure && make -j${PARALLELISM}; \ + make install; \ + rm -rf /tmp/swig-rel-3.0.12 + +RUN set -e; \ + add-apt-repository -y ppa:jonathonf/python-3.6; \ + apt-get update; \ + apt-get -y install python3.6-dev + +# python bindings dependencies +RUN set -e; \ + pip install grpcio_tools; \ + pip3 install grpcio_tools # install lcov -RUN curl -L -o /tmp/lcov-1.13.tar.gz https://github.com/linux-test-project/lcov/releases/download/v1.13/lcov-1.13.tar.gz; cd /tmp; tar zxf lcov-1.13.tar.gz; cd lcov-1.13; make install +RUN set -e; \ + curl -L -o /tmp/lcov-1.13.tar.gz https://github.com/linux-test-project/lcov/releases/download/v1.13/lcov-1.13.tar.gz; cd /tmp; tar zxf lcov-1.13.tar.gz; cd lcov-1.13; make install # non-interactive adduser # -m = create home dir diff --git a/docker/develop/armv7l/Dockerfile b/docker/develop/armv7l/Dockerfile deleted file mode 100644 index 11e52a94c1..0000000000 --- a/docker/develop/armv7l/Dockerfile +++ /dev/null @@ -1,261 +0,0 @@ -FROM ubuntu:16.04 - -# number of concurrent threads during build -# usage: docker build --build-arg PARALLELISM=8 -t name/name . -ARG PARALLELISM=1 -ARG CMAKE_BUILD_TYPE=Release - -ENV IROHA_HOME /opt/iroha -ENV IROHA_BUILD /opt/iroha/build - -RUN apt-get update; \ - apt-get -y upgrade; \ - apt-get -y --no-install-recommends install apt-utils software-properties-common; \ - apt-get -y clean - - -# add git repository -RUN add-apt-repository -y ppa:git-core/ppa; \ - apt-get update - - -RUN set -e; \ - apt-get -y --no-install-recommends install build-essential python-software-properties \ - automake libtool \ - # dev dependencies - libssl-dev zlib1g-dev libcurl4-openssl-dev libc6-dbg golang \ - # CircleCI dependencies - git ssh tar gzip ca-certificates python3 python3-pip python3-setuptools \ - # other - wget curl file unzip gdb iputils-ping vim ccache \ - gcovr cppcheck doxygen graphviz graphviz-dev; \ - apt-get -y clean - -# install cmake 3.7.2 -RUN set -e; \ - git clone https://gitlab.kitware.com/cmake/cmake.git /tmp/cmake; \ - (cd /tmp/cmake ; git checkout 35413bf2c1b33980afd418030af27f184872af6b); \ - (cd /tmp/cmake ; /tmp/cmake/bootstrap --system-curl --parallel=${PARALLELISM} --enable-ccache); \ - make -j${PARALLELISM} -C /tmp/cmake; \ - make -C /tmp/cmake install; \ - ldconfig; \ - rm -rf /tmp/cmake - -# install boost 1.65.1 -RUN set -e; \ - git clone https://github.com/boostorg/boost /tmp/boost; \ - (cd /tmp/boost ; git checkout 436ad1dfcfc7e0246141beddd11c8a4e9c10b146); \ - (cd /tmp/boost ; git submodule update --init --recursive); \ - (cd /tmp/boost ; /tmp/boost/bootstrap.sh --with-libraries=system,filesystem); \ - (cd /tmp/boost ; /tmp/boost/b2 headers); \ - (cd /tmp/boost ; /tmp/boost/b2 cxxflags="-std=c++14" -j ${PARALLELISM} install); \ - ldconfig; \ - rm -rf /tmp/boost - -# install protobuf -RUN set -e; \ - git clone https://github.com/google/protobuf /tmp/protobuf; \ - (cd /tmp/protobuf ; git checkout 80a37e0782d2d702d52234b62dd4b9ec74fd2c95); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -Dprotobuf_BUILD_TESTS=OFF \ - -Dprotobuf_BUILD_SHARED_LIBS=ON \ - -H/tmp/protobuf/cmake \ - -B/tmp/protobuf/.build; \ - cmake --build /tmp/protobuf/.build --target install -- -j${PARALLELISM}; \ - ldconfig; \ - rm -rf /tmp/protobuf - -# install gflags -RUN set -e; \ - git clone https://github.com/gflags/gflags /tmp/gflags; \ - (cd /tmp/gflags ; git checkout f8a0efe03aa69b3336d8e228b37d4ccb17324b88); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -H/tmp/gflags \ - -B/tmp/gflags/build; \ - cmake --build /tmp/gflags/build --target install -- -j${PARALLELISM}; \ - ldconfig; \ - rm -rf /tmp/gflags - -# install c-ares -RUN set -e; \ - git clone https://github.com/c-ares/c-ares /tmp/c-ares; \ - (cd /tmp/c-ares ; git checkout 3be1924221e1326df520f8498d704a5c4c8d0cce); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -H/tmp/c-ares \ - -B/tmp/c-ares/build; \ - cmake --build /tmp/c-ares/build --target install -- -j${PARALLELISM}; \ - ldconfig; \ - rm -rf /tmp/c-ares - -# install grpc -RUN set -e; \ - git clone https://github.com/grpc/grpc /tmp/grpc; \ - cd /tmp/grpc; \ - git checkout bfcbad3b86c7912968dc8e64f2121c920dad4dfb; \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -DgRPC_BENCHMARK_PROVIDER="" \ - -DgRPC_ZLIB_PROVIDER=package \ - -DgRPC_CARES_PROVIDER=package \ - -DgRPC_SSL_PROVIDER=package \ - -DgRPC_PROTOBUF_PROVIDER=package \ - -DgRPC_GFLAGS_PROVIDER=package \ - -DBUILD_SHARED_LIBS=ON \ - -H/tmp/grpc \ - -B/tmp/grpc/.build; \ - cd /tmp/grpc/.build; \ - make -j${PARALLELISM} gpr grpc grpc++ grpc_cpp_plugin; \ - # copy libs to /usr/local/lib - cp libgpr.so libgrpc.so libgrpc++.so libgrpc_plugin_support.so /usr/local/lib; \ - cp grpc_cpp_plugin /usr/local/bin; \ - cp -R ../include /usr/local; \ - ldconfig; \ - rm -rf /tmp/grpc - -# install gtest -RUN set -e; \ - git clone https://github.com/google/googletest /tmp/googletest; \ - (cd /tmp/googletest ; git checkout ec44c6c1675c25b9827aacd08c02433cccde7780); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -H/tmp/googletest \ - -B/tmp/googletest/build; \ - cmake --build /tmp/googletest/build --target install -- -j${PARALLELISM}; \ - ldconfig; \ - rm -rf /tmp/googletest - -# install spdlog v0.16.3 -RUN set -e; \ - git clone https://github.com/gabime/spdlog /tmp/spdlog; \ - (cd /tmp/spdlog ; git checkout ccd675a286f457068ee8c823f8207f13c2325b26); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -DSPDLOG_BUILD_TESTING=OFF -H/tmp/spdlog -B/tmp/spdlog/build; \ - cmake --build /tmp/spdlog/build --target install; \ - rm -rf /tmp/spdlog - -# install rxcpp -RUN set -e; \ - git clone https://github.com/Reactive-Extensions/RxCpp /tmp/RxCpp; \ - (cd /tmp/RxCpp ; git checkout 1b2e0589f19cb34d8cd58803677701dcf2161876); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -H/tmp/RxCpp \ - -B/tmp/RxCpp/build; \ - cmake --build /tmp/RxCpp/build --target install; \ - rm -rf /tmp/RxCpp - -# install rapidjson -RUN set -e; \ - git clone https://github.com/miloyip/rapidjson /tmp/rapidjson; \ - (cd /tmp/rapidjson ; git checkout f54b0e47a08782a6131cc3d60f94d038fa6e0a51); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -DRAPIDJSON_BUILD_EXAMPLES=OFF \ - -H/tmp/rapidjson \ - -B/tmp/rapidjson/build; \ - cmake --build /tmp/rapidjson/build --target install; \ - ldconfig; \ - rm -rf /tmp/rapidjson - -# install libpq -RUN set -e; \ - git clone --progress https://git.postgresql.org/git/postgresql.git /tmp/postgresql; \ - cd /tmp/postgresql; \ - git checkout 029386ccbddd0a33d481b94e511f5219b03e6636; \ - ./configure --without-readline --prefix=/usr/local; \ - # build - make -j${PARALLELISM} -C src/bin/pg_config; \ - make -j${PARALLELISM} -C src/interfaces/libpq; \ - make -j${PARALLELISM} -C src/backend/utils fmgroids.h; \ - cp src/backend/utils/fmgroids.h src/include/utils/fmgroids.h; \ - # install - make -C src/bin/pg_config install; \ - make -C src/interfaces/libpq install; \ - make -C src/include install; \ - ldconfig; \ - # remove - rm -rf /tmp/postgresql - -# install pqxx -RUN set -e; \ - git clone https://github.com/jtv/libpqxx /tmp/libpqxx; \ - (cd /tmp/libpqxx ; git checkout 5b17abce5ac2b1a2f8278718405b7ade8bb30ae9); \ - curl -L -o /tmp/libpqxx/config/config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=6b2374c79506ee82a8b440f6d1ca293e2e2e2463'; \ - curl -L -o /tmp/libpqxx/config/config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=6b2374c79506ee82a8b440f6d1ca293e2e2e2463'; \ - (cd /tmp/libpqxx ; /tmp/libpqxx/configure --disable-documentation --with-pic); \ - make -j${PARALLELISM} -C /tmp/libpqxx; \ - make -C /tmp/libpqxx install; \ - ldconfig; \ - rm -rf /tmp/libpqxx - -# install tbb -RUN set -e; \ - git clone https://github.com/01org/tbb /tmp/tbb; \ - (cd /tmp/tbb ; git checkout eb6336ad29450f2a64af5123ca1b9429ff6bc11d); \ - make -j${PARALLELISM} -C /tmp/tbb tbb_build_prefix=build; \ - cp /tmp/tbb/build/build_debug/*.so* /usr/local/lib; \ - cp /tmp/tbb/build/build_release/*.so* /usr/local/lib; \ - cp -r /tmp/tbb/include/* /usr/local/include; \ - ldconfig; \ - rm -rf /tmp/tbb - -# install docker -ENV DOCKER_VERSION=17.06.0-ce -RUN set -e; \ - curl -L -o /tmp/docker-${DOCKER_VERSION}.tgz https://download.docker.com/linux/static/stable/armhf/docker-${DOCKER_VERSION}.tgz; \ - tar -xz -C /tmp -f /tmp/docker-${DOCKER_VERSION}.tgz; \ - mv /tmp/docker/* /usr/bin; \ - rm /tmp/docker-${DOCKER_VERSION}.tgz; \ - rm -rf /tmp/docker - -# install sonar cli -ENV SONAR_CLI_VERSION=3.0.3.778 -RUN set -e; \ - mkdir -p /opt/sonar; \ - curl -L -o /tmp/sonar.zip https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_CLI_VERSION}-linux.zip; \ - unzip -o -d /tmp/sonar-scanner /tmp/sonar.zip; \ - mv /tmp/sonar-scanner/sonar-scanner-${SONAR_CLI_VERSION}-linux /opt/sonar/scanner; \ - ln -s -f /opt/sonar/scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner; \ - rm -rf /tmp/sonar* - -# install ed25519 -RUN set -e; \ - git clone git://github.com/hyperledger/iroha-ed25519.git /tmp/ed25519; \ - (cd /tmp/ed25519 ; git checkout e7188b8393dbe5ac54378610d53630bd4a180038); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -DTESTING=OFF \ - -H/tmp/ed25519 \ - -B/tmp/ed25519/build; \ - cmake --build /tmp/ed25519/build --target install -- -j${PARALLELISM}; \ - ldconfig; \ - rm -rf /tmp/ed25519 - -# fetch lcov reports converter -RUN curl -L -o /tmp/lcov_cobertura.py https://raw.githubusercontent.com/eriwen/lcov-to-cobertura-xml/8c55cd11f80a21e7e46f20f8c81fcde0bf11f5e5/lcov_cobertura/lcov_cobertura.py - -# install lcov -RUN curl -L -o /tmp/lcov-1.13.tar.gz https://github.com/linux-test-project/lcov/releases/download/v1.13/lcov-1.13.tar.gz; cd /tmp; tar zxf lcov-1.13.tar.gz; cd lcov-1.13; make install - -# non-interactive adduser -# -m = create home dir -# -s = set default shell -# iroha-ci = username -# -u = userid, default for Ubuntu is 1000 -# -U = create a group same as username -# no password -RUN useradd -ms /bin/bash iroha-ci -u 1000 -U - -WORKDIR /opt/iroha -RUN set -e; \ - chmod -R 777 /opt/iroha; \ - mkdir -p /tmp/ccache -m 777; \ - ccache --clear - - -USER iroha-ci -CMD ["/bin/bash"] diff --git a/docker/develop/x86_64/Dockerfile b/docker/develop/x86_64/Dockerfile deleted file mode 100644 index 15d79ff37c..0000000000 --- a/docker/develop/x86_64/Dockerfile +++ /dev/null @@ -1,261 +0,0 @@ -FROM ubuntu:16.04 - -# number of concurrent threads during build -# usage: docker build --build-arg PARALLELISM=8 -t name/name . -ARG PARALLELISM=1 -ARG CMAKE_BUILD_TYPE=Release - -ENV IROHA_HOME /opt/iroha -ENV IROHA_BUILD /opt/iroha/build - -RUN apt-get update; \ - apt-get -y upgrade; \ - apt-get -y --no-install-recommends install apt-utils software-properties-common; \ - apt-get -y clean - - -# add git repository -RUN add-apt-repository -y ppa:git-core/ppa; \ - apt-get update - - -RUN set -e; \ - apt-get -y --no-install-recommends install build-essential python-software-properties \ - automake libtool \ - # dev dependencies - libssl-dev zlib1g-dev libcurl4-openssl-dev libc6-dbg golang \ - # CircleCI dependencies - git ssh tar gzip ca-certificates python3 python3-pip python3-setuptools \ - # other - wget curl file unzip gdb iputils-ping vim ccache \ - gcovr cppcheck doxygen graphviz graphviz-dev; \ - apt-get -y clean - -# install cmake 3.7.2 -RUN set -e; \ - git clone https://gitlab.kitware.com/cmake/cmake.git /tmp/cmake; \ - (cd /tmp/cmake ; git checkout 35413bf2c1b33980afd418030af27f184872af6b); \ - (cd /tmp/cmake ; /tmp/cmake/bootstrap --system-curl --parallel=${PARALLELISM} --enable-ccache); \ - make -j${PARALLELISM} -C /tmp/cmake; \ - make -C /tmp/cmake install; \ - ldconfig; \ - rm -rf /tmp/cmake - -# install boost 1.65.1 -RUN set -e; \ - git clone https://github.com/boostorg/boost /tmp/boost; \ - (cd /tmp/boost ; git checkout 436ad1dfcfc7e0246141beddd11c8a4e9c10b146); \ - (cd /tmp/boost ; git submodule update --init --recursive); \ - (cd /tmp/boost ; /tmp/boost/bootstrap.sh --with-libraries=system,filesystem); \ - (cd /tmp/boost ; /tmp/boost/b2 headers); \ - (cd /tmp/boost ; /tmp/boost/b2 cxxflags="-std=c++14" -j ${PARALLELISM} install); \ - ldconfig; \ - rm -rf /tmp/boost - -# install protobuf -RUN set -e; \ - git clone https://github.com/google/protobuf /tmp/protobuf; \ - (cd /tmp/protobuf ; git checkout 80a37e0782d2d702d52234b62dd4b9ec74fd2c95); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -Dprotobuf_BUILD_TESTS=OFF \ - -Dprotobuf_BUILD_SHARED_LIBS=ON \ - -H/tmp/protobuf/cmake \ - -B/tmp/protobuf/.build; \ - cmake --build /tmp/protobuf/.build --target install -- -j${PARALLELISM}; \ - ldconfig; \ - rm -rf /tmp/protobuf - -# install gflags -RUN set -e; \ - git clone https://github.com/gflags/gflags /tmp/gflags; \ - (cd /tmp/gflags ; git checkout f8a0efe03aa69b3336d8e228b37d4ccb17324b88); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -H/tmp/gflags \ - -B/tmp/gflags/build; \ - cmake --build /tmp/gflags/build --target install -- -j${PARALLELISM}; \ - ldconfig; \ - rm -rf /tmp/gflags - -# install c-ares -RUN set -e; \ - git clone https://github.com/c-ares/c-ares /tmp/c-ares; \ - (cd /tmp/c-ares ; git checkout 3be1924221e1326df520f8498d704a5c4c8d0cce); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -H/tmp/c-ares \ - -B/tmp/c-ares/build; \ - cmake --build /tmp/c-ares/build --target install -- -j${PARALLELISM}; \ - ldconfig; \ - rm -rf /tmp/c-ares - -# install grpc -RUN set -e; \ - git clone https://github.com/grpc/grpc /tmp/grpc; \ - cd /tmp/grpc; \ - git checkout bfcbad3b86c7912968dc8e64f2121c920dad4dfb; \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -DgRPC_BENCHMARK_PROVIDER="" \ - -DgRPC_ZLIB_PROVIDER=package \ - -DgRPC_CARES_PROVIDER=package \ - -DgRPC_SSL_PROVIDER=package \ - -DgRPC_PROTOBUF_PROVIDER=package \ - -DgRPC_GFLAGS_PROVIDER=package \ - -DBUILD_SHARED_LIBS=ON \ - -H/tmp/grpc \ - -B/tmp/grpc/.build; \ - cd /tmp/grpc/.build; \ - make -j${PARALLELISM} gpr grpc grpc++ grpc_cpp_plugin; \ - # copy libs to /usr/local/lib - cp libgpr.so libgrpc.so libgrpc++.so libgrpc_plugin_support.so /usr/local/lib; \ - cp grpc_cpp_plugin /usr/local/bin; \ - cp -R ../include /usr/local; \ - ldconfig; \ - rm -rf /tmp/grpc - -# install gtest -RUN set -e; \ - git clone https://github.com/google/googletest /tmp/googletest; \ - (cd /tmp/googletest ; git checkout ec44c6c1675c25b9827aacd08c02433cccde7780); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -H/tmp/googletest \ - -B/tmp/googletest/build; \ - cmake --build /tmp/googletest/build --target install -- -j${PARALLELISM}; \ - ldconfig; \ - rm -rf /tmp/googletest - -# install spdlog v0.16.3 -RUN set -e; \ - git clone https://github.com/gabime/spdlog /tmp/spdlog; \ - (cd /tmp/spdlog ; git checkout ccd675a286f457068ee8c823f8207f13c2325b26); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -DSPDLOG_BUILD_TESTING=OFF -H/tmp/spdlog -B/tmp/spdlog/build; \ - cmake --build /tmp/spdlog/build --target install; \ - rm -rf /tmp/spdlog - -# install rxcpp -RUN set -e; \ - git clone https://github.com/Reactive-Extensions/RxCpp /tmp/RxCpp; \ - (cd /tmp/RxCpp ; git checkout 1b2e0589f19cb34d8cd58803677701dcf2161876); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -H/tmp/RxCpp \ - -B/tmp/RxCpp/build; \ - cmake --build /tmp/RxCpp/build --target install; \ - rm -rf /tmp/RxCpp - -# install rapidjson -RUN set -e; \ - git clone https://github.com/miloyip/rapidjson /tmp/rapidjson; \ - (cd /tmp/rapidjson ; git checkout f54b0e47a08782a6131cc3d60f94d038fa6e0a51); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -DRAPIDJSON_BUILD_EXAMPLES=OFF \ - -H/tmp/rapidjson \ - -B/tmp/rapidjson/build; \ - cmake --build /tmp/rapidjson/build --target install; \ - ldconfig; \ - rm -rf /tmp/rapidjson - -# install libpq -RUN set -e; \ - git clone --progress https://git.postgresql.org/git/postgresql.git /tmp/postgresql; \ - cd /tmp/postgresql; \ - git checkout 029386ccbddd0a33d481b94e511f5219b03e6636; \ - ./configure --without-readline --prefix=/usr/local; \ - # build - make -j${PARALLELISM} -C src/bin/pg_config; \ - make -j${PARALLELISM} -C src/interfaces/libpq; \ - make -j${PARALLELISM} -C src/backend/utils fmgroids.h; \ - cp src/backend/utils/fmgroids.h src/include/utils/fmgroids.h; \ - # install - make -C src/bin/pg_config install; \ - make -C src/interfaces/libpq install; \ - make -C src/include install; \ - ldconfig; \ - # remove - rm -rf /tmp/postgresql - -# install pqxx -RUN set -e; \ - git clone https://github.com/jtv/libpqxx /tmp/libpqxx; \ - (cd /tmp/libpqxx ; git checkout 5b17abce5ac2b1a2f8278718405b7ade8bb30ae9); \ - curl -L -o /tmp/libpqxx/config/config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=6b2374c79506ee82a8b440f6d1ca293e2e2e2463'; \ - curl -L -o /tmp/libpqxx/config/config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=6b2374c79506ee82a8b440f6d1ca293e2e2e2463'; \ - (cd /tmp/libpqxx ; /tmp/libpqxx/configure --disable-documentation --with-pic); \ - make -j${PARALLELISM} -C /tmp/libpqxx; \ - make -C /tmp/libpqxx install; \ - ldconfig; \ - rm -rf /tmp/libpqxx - -# install tbb -RUN set -e; \ - git clone https://github.com/01org/tbb /tmp/tbb; \ - (cd /tmp/tbb ; git checkout eb6336ad29450f2a64af5123ca1b9429ff6bc11d); \ - make -j${PARALLELISM} -C /tmp/tbb tbb_build_prefix=build; \ - cp /tmp/tbb/build/build_debug/*.so* /usr/local/lib; \ - cp /tmp/tbb/build/build_release/*.so* /usr/local/lib; \ - cp -r /tmp/tbb/include/* /usr/local/include; \ - ldconfig; \ - rm -rf /tmp/tbb - -# install docker -ENV DOCKER_VERSION=17.06.0-ce -RUN set -e; \ - curl -L -o /tmp/docker-${DOCKER_VERSION}.tgz https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz; \ - tar -xz -C /tmp -f /tmp/docker-${DOCKER_VERSION}.tgz; \ - mv /tmp/docker/* /usr/bin; \ - rm /tmp/docker-${DOCKER_VERSION}.tgz; \ - rm -rf /tmp/docker - -# install sonar cli -ENV SONAR_CLI_VERSION=3.0.3.778 -RUN set -e; \ - mkdir -p /opt/sonar; \ - curl -L -o /tmp/sonar.zip https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_CLI_VERSION}-linux.zip; \ - unzip -o -d /tmp/sonar-scanner /tmp/sonar.zip; \ - mv /tmp/sonar-scanner/sonar-scanner-${SONAR_CLI_VERSION}-linux /opt/sonar/scanner; \ - ln -s -f /opt/sonar/scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner; \ - rm -rf /tmp/sonar* - -# install ed25519 -RUN set -e; \ - git clone git://github.com/hyperledger/iroha-ed25519.git /tmp/ed25519; \ - (cd /tmp/ed25519 ; git checkout e7188b8393dbe5ac54378610d53630bd4a180038); \ - cmake \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -DTESTING=OFF \ - -H/tmp/ed25519 \ - -B/tmp/ed25519/build; \ - cmake --build /tmp/ed25519/build --target install -- -j${PARALLELISM}; \ - ldconfig; \ - rm -rf /tmp/ed25519 - -# fetch lcov reports converter -RUN curl -L -o /tmp/lcov_cobertura.py https://raw.githubusercontent.com/eriwen/lcov-to-cobertura-xml/8c55cd11f80a21e7e46f20f8c81fcde0bf11f5e5/lcov_cobertura/lcov_cobertura.py - -# install lcov -RUN curl -L -o /tmp/lcov-1.13.tar.gz https://github.com/linux-test-project/lcov/releases/download/v1.13/lcov-1.13.tar.gz; cd /tmp; tar zxf lcov-1.13.tar.gz; cd lcov-1.13; make install - -# non-interactive adduser -# -m = create home dir -# -s = set default shell -# iroha-ci = username -# -u = userid, default for Ubuntu is 1000 -# -U = create a group same as username -# no password -RUN useradd -ms /bin/bash iroha-ci -u 1000 -U - -WORKDIR /opt/iroha -RUN set -e; \ - chmod -R 777 /opt/iroha; \ - mkdir -p /tmp/ccache -m 777; \ - ccache --clear - - -USER iroha-ci -CMD ["/bin/bash"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8bc958647b..95803e4084 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: node: - image: hyperledger/iroha-docker-develop:v1 + image: hyperledger/iroha:develop-build ports: - "${IROHA_PORT}:50051" - "${DEBUGGER_PORT}:20000" diff --git a/docker/manifest.yaml b/docker/manifest.yaml index f7ece5d19f..f43a6e1eee 100644 --- a/docker/manifest.yaml +++ b/docker/manifest.yaml @@ -2,20 +2,20 @@ image: hyperledger/iroha:latest manifests: - - image: hyperledger/iroha:x86_64 + image: hyperledger/iroha:x86_64-latest platform: architecture: amd64 os: linux features: - sse - - image: hyperledger/iroha:armv7l + image: hyperledger/iroha:armv7l-latest platform: architecture: arm os: linux variant: v7 - - image: hyperledger/iroha:aarch64 + image: hyperledger/iroha:aarch64-latest platform: architecture: arm64 os: linux @@ -41,3 +41,25 @@ manifests: platform: architecture: arm64 os: linux + +# develop branch, build env +image: hyperledger/iroha:develop-build +manifests: + - + image: hyperledger/iroha:x86_64-develop-build + platform: + architecture: amd64 + os: linux + features: + - sse + - + image: hyperledger/iroha:armv7l-develop-build + platform: + architecture: arm + os: linux + variant: v7 + - + image: hyperledger/iroha:aarch64-develop-build + platform: + architecture: arm64 + os: linux diff --git a/docker/release/x86_64/Dockerfile b/docker/release/Dockerfile similarity index 51% rename from docker/release/x86_64/Dockerfile rename to docker/release/Dockerfile index 53f05883fd..c07120cb32 100644 --- a/docker/release/x86_64/Dockerfile +++ b/docker/release/Dockerfile @@ -1,15 +1,12 @@ FROM ubuntu:16.04 -RUN apt-get update;\ - apt-get upgrade -y; \ - apt-get install -y \ - libc-ares-dev +RUN apt-get update; \ + apt-get install -y libc-ares-dev #Install iroha COPY iroha.deb /tmp/iroha.deb -RUN dpkg -i /tmp/iroha.deb - -RUN apt-get -fy install;rm -f /tmp/iroha.deb +RUN apt-get install -y /tmp/iroha.deb; \ + rm -f /tmp/iroha.deb WORKDIR /opt/iroha_data diff --git a/docker/release/aarch64/Dockerfile b/docker/release/aarch64/Dockerfile deleted file mode 120000 index d810e95633..0000000000 --- a/docker/release/aarch64/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -../x86_64/Dockerfile \ No newline at end of file diff --git a/docker/release/aarch64/entrypoint.sh b/docker/release/aarch64/entrypoint.sh deleted file mode 120000 index d87c399ff9..0000000000 --- a/docker/release/aarch64/entrypoint.sh +++ /dev/null @@ -1 +0,0 @@ -../x86_64/entrypoint.sh \ No newline at end of file diff --git a/docker/release/armv7l/Dockerfile b/docker/release/armv7l/Dockerfile deleted file mode 120000 index d810e95633..0000000000 --- a/docker/release/armv7l/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -../x86_64/Dockerfile \ No newline at end of file diff --git a/docker/release/armv7l/entrypoint.sh b/docker/release/armv7l/entrypoint.sh deleted file mode 120000 index d87c399ff9..0000000000 --- a/docker/release/armv7l/entrypoint.sh +++ /dev/null @@ -1 +0,0 @@ -../x86_64/entrypoint.sh \ No newline at end of file diff --git a/docker/release/x86_64/entrypoint.sh b/docker/release/entrypoint.sh similarity index 70% rename from docker/release/x86_64/entrypoint.sh rename to docker/release/entrypoint.sh index 85595eb18a..709e3d9bbc 100755 --- a/docker/release/x86_64/entrypoint.sh +++ b/docker/release/entrypoint.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash echo key=$KEY echo $PWD -iroha-cli --genesis_block --peers_address peers.list irohad --genesis_block genesis.block --config config.sample --keypair_name $KEY diff --git a/docs/README.md b/docs/README.md index 7883d2e54a..a57bff90f7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # Iroha reStructuredTest documentation -The purpose of this documentation is to convey design and architecture aspects of Iroha ledger in a structured approach, as well as operational side: how-tos, guides, and examples. Docs are accessible via ReadTheDocs website, and can be generated to a lot of formats, available in Sphinx. In order to contribute, one should to be familiar with [reStructuredTest](docutils.sourceforge.net/rst.html) syntax, and follow principles described in this file. +The purpose of this documentation is to convey design and architecture aspects of Iroha ledger in a structured approach, as well as operational side: how-tos, guides, and examples. Docs are accessible via ReadTheDocs website, and can be generated to a lot of formats, available in Sphinx. In order to contribute, one should be familiar with [reStructuredTest](http://docutils.sourceforge.net/rst.html) syntax, and follow principles described in this file. ## Principles diff --git a/docs/image_assets/iroha_swift_guide/iroha_swift_guide_001.png b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_001.png new file mode 100644 index 0000000000..54ba9316eb Binary files /dev/null and b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_001.png differ diff --git a/docs/image_assets/iroha_swift_guide/iroha_swift_guide_002.png b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_002.png new file mode 100644 index 0000000000..0110bda65e Binary files /dev/null and b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_002.png differ diff --git a/docs/image_assets/iroha_swift_guide/iroha_swift_guide_003.png b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_003.png new file mode 100644 index 0000000000..a1fe925e9b Binary files /dev/null and b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_003.png differ diff --git a/docs/image_assets/iroha_swift_guide/iroha_swift_guide_004.png b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_004.png new file mode 100644 index 0000000000..1bc7aba4a4 Binary files /dev/null and b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_004.png differ diff --git a/docs/image_assets/iroha_swift_guide/iroha_swift_guide_005.png b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_005.png new file mode 100644 index 0000000000..fd8ea8bbd3 Binary files /dev/null and b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_005.png differ diff --git a/docs/image_assets/iroha_swift_guide/iroha_swift_guide_007.png b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_007.png new file mode 100644 index 0000000000..f0e7d53101 Binary files /dev/null and b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_007.png differ diff --git a/docs/image_assets/iroha_swift_guide/iroha_swift_guide_008.png b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_008.png new file mode 100644 index 0000000000..05dbf506f1 Binary files /dev/null and b/docs/image_assets/iroha_swift_guide/iroha_swift_guide_008.png differ diff --git a/docs/source/api/commands.rst b/docs/source/api/commands.rst index 7736cef7b4..094d73994e 100644 --- a/docs/source/api/commands.rst +++ b/docs/source/api/commands.rst @@ -462,6 +462,8 @@ Purpose Purpose of set account detail command is to set key-value information for a given account +.. warning:: If there was a value for a given key already in the storage then it will be replaced with the new value + Schema ^^^^^^ diff --git a/docs/source/api/queries.rst b/docs/source/api/queries.rst index da3cb1e1c5..fd1a17f06b 100644 --- a/docs/source/api/queries.rst +++ b/docs/source/api/queries.rst @@ -300,7 +300,7 @@ Response Structure "Balance", "balance of the asset", "Not less than 0", "200.20" Get Asset Info --------------- +^^^^^^^^^^^^^^ Purpose ------- diff --git a/docs/source/conf.py b/docs/source/conf.py index d2b982d7e6..741f702a2d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -52,9 +52,9 @@ # General information about the project. project = u'Iroha' documentation = u'Iroha Documentation' -description = u'Distributed ledger technology platworm, written in C++' -copyright = u'Creative Commons Attribution-NonCommercial 3.0 Unported' -author = u'Nikolay Yushkevich at Soramitsu Co Ltd' +description = u'Distributed ledger technology platform, written in C++' +copyright = u'2018 Soramitsu Co., Ltd.' +author = u'Nikolay Yushkevich at Soramitsu Co., Ltd.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/source/getting_started/index.rst b/docs/source/getting_started/index.rst index 627eb9ef18..4d7b8cb580 100644 --- a/docs/source/getting_started/index.rst +++ b/docs/source/getting_started/index.rst @@ -15,7 +15,7 @@ things simple, we will use Docker. Prerequisites ------------- For this guide, you need a computer running Unix-like system with ``docker`` -installed. You can read how to install it on a +installed. You can read how to install it on a `Docker's website `_. .. note:: Please note that you can use Iroha without ``docker`` as well. You @@ -76,17 +76,17 @@ Configuring Iroha Network this guide. Now we need to configure our Iroha network. This includes creating a -configuration file, generating keypairs for a users, writing a list of peers +configuration file, generating keypairs for a users, writing a list of peers and creating a genesis block. However, we have prepared an example -configuration for this guide, so you can start playing with Iroha faster. -In order to get those files, you need to clone the +configuration for this guide, so you can start playing with Iroha faster. +In order to get those files, you need to clone the `Iroha repository `_ from Github. .. code-block:: shell git clone -b develop https://github.com/hyperledger/iroha --depth=1 -.. hint:: ``--depth-1`` option allows us to download only latest commit and +.. hint:: ``--depth=1`` option allows us to download only latest commit and save some time and bandwidth. If you want to get a full commit history, you can omit this option. @@ -103,7 +103,7 @@ command -v blockstore:/tmp/block_store \ --network=iroha-network \ --entrypoint=/bin/bash \ - hyperledger/iroha-docker:develop + hyperledger/iroha:develop Let's look in detail what this command does: @@ -116,10 +116,10 @@ Let's look in detail what this command does: the container - ``--network=iroha-network \`` adds our container to previously created ``iroha-network``, so Iroha and Postgres could see each other. -- ``--entrypoint=/bin/bash \`` Because ``hyperledger/iroha-docker`` has +- ``--entrypoint=/bin/bash \`` Because ``hyperledger/iroha`` has the custom script which runs after starting the container, we want to override it so we can start Iroha Daemon manually. -- ``hyperledger/iroha-docker:develop`` is the image which has the ``develop`` +- ``hyperledger/iroha:develop`` is the image which has the ``develop`` branch. Launching Iroha Daemon @@ -178,7 +178,7 @@ account to work with Iroha. .. note:: Full account name has a ``@`` symbol between name and domain. Note that the keypair has the same name. - + Creating the First Transaction ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,7 +206,7 @@ Quantity (add_ast_qty)``, enter Account ID – ``admin@test``, asset ID – .. note:: Full asset name has a ``#`` symbol between name and domain. -Let's transfer 100.50 ``coolcoins`` from ``admin@test`` to ``test@test`` +Let's transfer 100.50 ``coolcoins`` from ``admin@test`` to ``test@test`` by adding one more command and choosing ``5. Transfer Assets (tran_ast)``. Enter Source Account and Destination Account, in our case ``admin@test`` and ``test@test``, Asset ID (``coolcoin#test``), integer part and precision @@ -225,7 +225,7 @@ Congratulations! You have submitted your first transaction to Iroha. Creating the First Query ^^^^^^^^^^^^^^^^^^^^^^^^ -Now let's check if ``coolcoins`` were successfully transferred from +Now let's check if ``coolcoins`` were successfully transferred from ``admin@test`` to ``test@test``. Choose ``2. New query (qry)``. ``7. Get Account's Assets (get_acc_ast)`` can help you to check if ``test@test`` now has ``coolcoin``. Form a query in a similar way you did with @@ -257,7 +257,7 @@ Account, in our case ``admin@test`` and ``test@test``, Asset ID (``coolcoin#test``), integer part and precision (``10000000`` and ``2`` accordingly). Send a transaction to Iroha peer as you did before. Well, it says -.. code:: +.. code:: [2018-03-21 12:58:40.791297963][th:520][info] TransactionResponseHandler Transaction successfully sent Congratulation, your transaction was accepted for processing. @@ -269,11 +269,11 @@ had successfully cheated Iroha? Let's try to see transaction's status. Choose you can get in the console after the previous command. Let's send it to Iroha. It replies with: -.. code:: +.. code:: Transaction has not passed stateful validation. Apparently no. Our transaction was not accepted because it did not pass stateful validation and ``coolcoins`` were not transferred. You can check -the status of ``admin@test`` and ``test@test`` with queries to be sure +the status of ``admin@test`` and ``test@test`` with queries to be sure (like we did earlier). diff --git a/docs/source/guides/build.rst b/docs/source/guides/build.rst index 4b3657f023..2a46387808 100644 --- a/docs/source/guides/build.rst +++ b/docs/source/guides/build.rst @@ -38,7 +38,7 @@ to the directory of your choice. git clone -b develop https://github.com/hyperledger/iroha --depth=1 -.. hint:: ``--depth-1`` option allows us to download only latest commit and +.. hint:: ``--depth=1`` option allows us to download only latest commit and save some time and bandwidth. If you want to get a full commit history, you can omit this option. @@ -57,8 +57,8 @@ After you execute this script, following things happen: 1. The script checks if you don't have containers with Iroha already running. Successful completion finishes with the new container shell. -2. The script will download ``iroha-docker-develop`` and ``postgres`` images. -``iroha-docker-develop`` image contains all development dependencies and is +2. The script will download ``hyperledger/iroha:develop-build`` and ``postgres`` images. +``hyperledger/iroha:develop-build`` image contains all development dependencies and is based on top of ``ubuntu:16.04``. ``postgres`` image is required for starting and running Iroha. 3. Two containers are created and launched. diff --git a/docs/source/guides/dependencies.rst b/docs/source/guides/dependencies.rst index 6fdc99c511..b959ec9c2f 100644 --- a/docs/source/guides/dependencies.rst +++ b/docs/source/guides/dependencies.rst @@ -226,7 +226,7 @@ Installation on macOS CMAKE_BUILD_TYPE="Release" git clone https://github.com/google/protobuf /tmp/protobuf; - (cd /tmp/protobuf ; git checkout 80a37e0782d2d702d52234b62dd4b9ec74fd2c95); + (cd /tmp/protobuf ; git checkout 106ffc04be1abf3ff3399f54ccf149815b287dd9); cmake \ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ -Dprotobuf_BUILD_TESTS=OFF \ diff --git a/docs/source/guides/deployment.rst b/docs/source/guides/deployment.rst index 04ab566ebe..40dd732a5a 100644 --- a/docs/source/guides/deployment.rst +++ b/docs/source/guides/deployment.rst @@ -70,7 +70,7 @@ In order to run Iroha peer as a single instance in Docker, you should pull the i .. code-block:: shell - docker pull hyperledger/iroha-docker:latest + docker pull hyperledger/iroha:latest .. Hint:: Use *latest* tag for latest stable release, and *develop* for latest development version @@ -138,7 +138,7 @@ If they are met, you can move forward with the following command: -e KEY='node0' \ # Docker network name --network=iroha-network \ - hyperledger/iroha-docker:latest + hyperledger/iroha:latest Running multiple instances (peer network) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/guides/libraries.rst b/docs/source/guides/libraries.rst index 4a4b563923..65860a742b 100644 --- a/docs/source/guides/libraries.rst +++ b/docs/source/guides/libraries.rst @@ -9,4 +9,5 @@ Client Libraries libraries/java.rst libraries/nodejs.rst libraries/python.rst - + libraries/swift_ios.rst + diff --git a/docs/source/guides/libraries/nodejs.rst b/docs/source/guides/libraries/nodejs.rst index 997135ea18..90451d83e7 100644 --- a/docs/source/guides/libraries/nodejs.rst +++ b/docs/source/guides/libraries/nodejs.rst @@ -67,7 +67,7 @@ Go to the NPM package directory and start the build process .. code-block:: shell cd iroha/shared_model/packages/javascript - npm install + npm install --build-from-source=iroha-lib That's all. You can use the library now. diff --git a/docs/source/guides/libraries/python.rst b/docs/source/guides/libraries/python.rst index db61ddc66c..15f89c2e35 100644 --- a/docs/source/guides/libraries/python.rst +++ b/docs/source/guides/libraries/python.rst @@ -230,7 +230,6 @@ Create domain and asset: .. code:: python tx = tx_builder.creatorAccountId(creator) \ - .txCounter(tx_counter) \ .createdTime(current_time) \ .createDomain("domain", "user") \ .createAsset("coin", "domain", 2).build() @@ -243,7 +242,6 @@ Create asset quantity: .. code:: python tx = tx_builder.creatorAccountId(creator) \ - .txCounter(tx_counter) \ .createdTime(current_time) \ .addAssetQuantity("admin@test", "coin#domain", "1000.2").build() @@ -257,7 +255,6 @@ Create account: user1_kp = crypto.generateKeypair() tx = tx_builder.creatorAccountId(creator) \ - .txCounter(tx_counter) \ .createdTime(current_time) \ .createAccount("userone", "domain", user1_kp.publicKey()).build() @@ -269,7 +266,6 @@ Send asset: .. code:: python tx = tx_builder.creatorAccountId(creator) \ - .txCounter(tx_counter) \ .createdTime(current_time) \ .transferAsset("admin@test", "userone@domain", "coin#domain", "Some message", "2.0").build() diff --git a/docs/source/guides/libraries/swift_ios.rst b/docs/source/guides/libraries/swift_ios.rst new file mode 100644 index 0000000000..8443bbfb79 --- /dev/null +++ b/docs/source/guides/libraries/swift_ios.rst @@ -0,0 +1,230 @@ +iOS Swift Library +----------------- + +Objectives +^^^^^^^^^^ + +In this guide you will learn: + +- How to build client library for iOS +- How to configure test application +- How to interact with Iroha blockchain from the mobile device + +Video Guide +^^^^^^^^^^^ + +For more details please visit the video below which fully describes all the steps. + +.. raw:: html + + + +Prerequisites +^^^^^^^^^^^^^ + +Before starting you need to install the following software on your mac: + +- XCode +- Carthage +- Git +- Cmake +- Postgresql + +This tutorial was tested with the following environment: + +- MacOS Sierra 10.12.6 +- Xcode 9.2 +- carthage 0.29.0 +- cmake 3.11.0 +- iPhone 7 iOS 11.2 Simulator +  +Hyperledger Iroha iOS library +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Iroha has the following features: + +1. Creation and management of custom complex assets, such as currencies or indivisible rights, serial numbers, patents, etc. +2. Management of user accounts +3. Taxonomy of accounts based on domains — or sub-ledgers in the system +4. The system of rights and verification of user permissions for the execution of transactions and queries in the system +5. Validation of business rules for transactions and queries in the system + +Among the non-functional requirements can be noted a high degree of network fault tolerance (Byzantine Fault Tolerant). +Iroha iOS library gives the ability to provide key generation and signing logic for queries and transactions passed to Iroha blockchain. +Let's start with the detailed instructions how to install Iroha on the local machine. + + +  +Instruction +^^^^^^^^^^^ + +1. Open the terminal and go to the folder where you want to install all artifacts: + + .. code-block:: bash + + cd path/to/your/folder/for/example/iroha-ios/project/ + +2. Clone the repository for the iOS client: + + .. code-block:: bash + + git clone https://github.com/hyperledger/iroha-ios.git + +3. Go to the Iroha-ios folder: + + .. code-block:: bash + + cd iroha-ios/ + +4. Update dependencies: + + .. code-block:: bash + + carthage update --platform iOS + +5. Go to sample project directory: + + .. code-block:: bash + + cd SwiftyIrohaExample + +6. Update dependencies for the sample: + + .. code-block:: bash + + carthage update --platform iOS + +7. Go to GRPC library source's location: + + .. code-block:: bash + + cd grpc-swift/ + +8. Remove old library sources: + +.. note:: Make sure you are located in ``grpc-swift/`` subfolder + + .. code-block:: bash + + # removes all files from the current directory + rm -rf ./* + # removes all hidden files too (so clean build can be done) + rm -rf ./.* +  +9. Download release version of GRCP from git to the current directory: + + .. code-block:: bash + + git clone --branch 0.3.3 https://github.com/grpc/grpc-swift.git . + +10. Build library: + + .. code-block:: bash + + make + +11. Go to the root of your playground folder (from the first step - path/to/your/folder/for/example/iroha-ios/project/): + + .. code-block:: bash + + cd ../../.. + +.. note:: Make sure now you are located in ``path/to/your/folder/for/example/iroha-ios/project/`` folder + +12. This step downloads script for client library which is needed to build client library. Clone it from the repository: + + .. code-block:: bash + + curl https://raw.githubusercontent.com/hyperledger/iroha/master/shared_model/packages/ios/ios-build.sh > ios-build.sh + +13. Optional step. If you have issues with cloning during ios-build.sh execution do the following command before the script invocation: + + .. code-block:: bash + + sed -i '' 's|git://github.com/hyperledger/iroha-ed25519|https://github.com/hyperledger/iroha-ed25519.git|g' ios-build.sh + +14. Make downloaded script executable: + + .. code-block:: bash + + chmod +x ios-build.sh + +15. Finally, build the client iOS library with proper options - platform: OS | SIMULATOR | SIMULATOR64; build: Debug | Release : + + .. code-block:: bash + + ./ios-build.sh SIMULATOR64 Debug + +16. The generated artifacts should be copied to the proper location (let's create it first): + + .. code-block:: bash + + # this command shows location for simulator artifacts + # use this command for device instead: + # mkdir -p iroha-ios/libs/iOS/ + mkdir -p iroha-ios/libs/Simulator/ +  +17. Copy generated binaries: + + .. code-block:: bash + + # this command shows location for simulator artifacts + # use this command for device instead: + # cp lib/* iroha-ios/libs/iOS/ + cp lib/* iroha-ios/libs/Simulator/ + +18. Do not forget to copy generated headers: + + .. code-block:: bash + + cp -a include/. iroha-ios/headers/ + +19. Now it's time to manually config Xcode project for the sample application. Open SwiftyIroha.xcodeproj: + +.. image:: https://github.com/hyperledger/iroha/raw/develop/docs/image_assets/iroha_swift_guide/iroha_swift_guide_001.png +  +20. Select SwiftyIrohaExample.xcodeproj general tab and link SwiftProtobuf framework from iroha-ios/SwiftProtobuf.framework location + +.. image:: https://github.com/hyperledger/iroha/raw/develop/docs/image_assets/iroha_swift_guide/iroha_swift_guide_002.png + +21. Select SwiftGRPC.xcodeproj project and remove zlib-example target from it: + +.. image:: https://github.com/hyperledger/iroha/raw/develop/docs/image_assets/iroha_swift_guide/iroha_swift_guide_003.png +  +22. Go to Proto group and remove it (In future this step will be removed, but for now it's needed for sample app to be built): + +.. image:: https://github.com/hyperledger/iroha/raw/develop/docs/image_assets/iroha_swift_guide/iroha_swift_guide_004.png + +23. Congratulations! We are done. Select SwiftyIrohaExample target, choose iPhone simulator device and build the application to make sure we have done everything correctly: + +.. image:: https://github.com/hyperledger/iroha/raw/develop/docs/image_assets/iroha_swift_guide/iroha_swift_guide_005.png + +Before we launch the application and test it we should deploy Iroha peer on our local machine and launch it. + +There is good news - steps 1-18 should not be done manually every time - here is the script which does it automatically. + +The script for iOS client installation and setup +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +All you need now is to download `build script `__ +``iroha-preparation.sh`` and launch it from ``path/to/your/folder/for/example/iroha-ios/project/``. + +Starting Iroha Node +^^^^^^^^^^^^^^^^^^^ + +To run this example, you need an Iroha node up and running. Please check out +:ref:`getting-started` if you want to learn how to start it. + +Launching Iroha iOS sample +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Now it's time to switch back to SwiftyIrohaSample application and launch it on the simulator. Open Xcode project, select proper sample target and run. +The sample will send test transaction to our node and query the result from blockchain. Successful operations will look similar to this Xcode console output: + +.. image:: https://github.com/hyperledger/iroha/raw/develop/docs/image_assets/iroha_swift_guide/iroha_swift_guide_007.png + +The output from Iroha terminal window (where the node is running): + +.. image:: https://github.com/hyperledger/iroha/raw/develop/docs/image_assets/iroha_swift_guide/iroha_swift_guide_008.png + +Great! We have sent our transaction and verified its presence in blockchain. diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 51783ee7cf..a2a8462a9c 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -49,11 +49,6 @@ Finally, Iroha is the only ledger that has a robust permission system, allowing .. [#f1] Yet Another Consensus -Is it fast? ------------ - -As per the latest review date of these docs, according to `Huawei Caliper `_ testing tool, Iroha is capable of processing 45 transactions per second. Theoretically, this is not even close to the limit of the system, and we will continue constant optimizations in order to improve stability and performance. - How to create applications around Iroha? ---------------------------------------- diff --git a/example/admin@test.priv b/example/admin@test.priv index ef24e1430d..87ff52af36 100644 --- a/example/admin@test.priv +++ b/example/admin@test.priv @@ -1 +1 @@ -1d7e0a32ee0affeb4d22acd73c2c6fb6bd58e266c8c2ce4fa0ffe3dd6a253ffb \ No newline at end of file +0f0ce16d2afbb8eca23c7d8c2724f0c257a800ee2bbd54688cec6b898e3f7e33 \ No newline at end of file diff --git a/example/admin@test.pub b/example/admin@test.pub index d80b9b299f..1dfda5428f 100644 --- a/example/admin@test.pub +++ b/example/admin@test.pub @@ -1 +1 @@ -407e57f50ca48969b08ba948171bb2435e035d82cec417e18e4a38f5fb113f83 \ No newline at end of file +889f6b881e331be21487db77dcf32c5f8d3d5e8066e78d2feac4239fe91d416f \ No newline at end of file diff --git a/example/genesis.block b/example/genesis.block index a8f665dd13..bb30df3c22 100644 --- a/example/genesis.block +++ b/example/genesis.block @@ -1,116 +1,120 @@ { - "signatures": [], - "created_ts": 0, - "hash": "ef9781aadf6d4d5ac8d01e14f7bbc805278ed9bb7200dafe8335bfc3a254e82b", - "prev_hash": "0000000000000000000000000000000000000000000000000000000000000000", - "height": 1, - "txs_number": 1, - "transactions": [ - { - "signatures": [], - "created_ts": 0, - "creator_account_id": "", - "tx_counter": 0, - "commands": [ - { - "command_type": "AddPeer", - "peer": { - "address": "localhost:10001", - "peer_key": "292a8714694095edce6be799398ed5d6244cd7be37eb813106b217d850d261f2" - } - }, - { - "command_type": "CreateRole", - "role_name": "admin", - "permissions": [ - "can_add_peer", - "can_add_signatory", - "can_create_account", - "can_create_domain", - "can_get_my_acc_detail", - "can_get_all_acc_detail", - "can_get_all_acc_ast", - "can_get_all_acc_ast_txs", - "can_get_all_acc_txs", - "can_get_all_accounts", - "can_get_all_signatories", - "can_get_all_txs", - "can_get_roles", - "can_read_assets", - "can_remove_signatory", - "can_set_detail", - "can_set_quorum" - ] - }, - { - "command_type": "CreateRole", - "role_name": "user", - "permissions": [ - "can_add_signatory", - "can_get_my_account", - "can_get_my_acc_detail", - "can_get_domain_acc_detail", - "can_get_my_acc_ast", - "can_get_my_acc_ast_txs", - "can_get_my_acc_txs", - "can_get_my_signatories", - "can_get_my_txs", - "can_grant_can_add_signatory", - "can_grant_can_remove_signatory", - "can_grant_can_set_detail", - "can_grant_can_set_quorum", - "can_grant_can_transfer", - "can_receive", - "can_remove_signatory", - "can_set_quorum", - "can_set_detail", - "can_transfer" - ] - }, - { - "command_type": "CreateRole", - "role_name": "money_creator", - "permissions": [ - "can_add_asset_qty", - "can_create_asset", - "can_receive", - "can_transfer" - ] - }, - { - "command_type": "CreateDomain", - "domain_id": "test", - "user_default_role": "user" - }, - { - "command_type": "CreateAsset", - "asset_name": "coin", - "domain_id": "test", - "precision": 2 - }, - { - "command_type": "CreateAccount", - "account_name": "admin", - "domain_id": "test", - "pubkey": "407e57f50ca48969b08ba948171bb2435e035d82cec417e18e4a38f5fb113f83" - }, - { - "command_type": "CreateAccount", - "account_name": "test", - "domain_id": "test", - "pubkey": "359f925e4eeecfdd6aa1abc0b79a6a121a5dd63bb612b603247ea4f8ad160156" - }, - { - "command_type": "AppendRole", - "account_id": "admin@test", - "role_name": "admin" - }, - { - "command_type": "AppendRole", - "account_id": "admin@test", - "role_name": "money_creator" - } - ] - } - ] + "payload":{ + "transactions":[ + { + "payload":{ + "commands":[ + { + "addPeer":{ + "peer":{ + "address":"localhost:10001", + "peerKey":"0E2icbV/5jQmrh3Jf2lSEEA3QR/PTztzncIX9F5fyZs=" + } + } + }, + { + "createRole":{ + "roleName":"admin", + "permissions":[ + "can_add_peer", + "can_add_signatory", + "can_create_account", + "can_create_domain", + "can_get_all_acc_ast", + "can_get_all_acc_ast_txs", + "can_get_all_acc_detail", + "can_get_all_acc_txs", + "can_get_all_accounts", + "can_get_all_signatories", + "can_get_all_txs", + "can_get_roles", + "can_read_assets", + "can_remove_signatory", + "can_set_quorum", + "can_get_blocks" + ] + } + }, + { + "createRole":{ + "roleName":"user", + "permissions":[ + "can_add_signatory", + "can_get_my_acc_ast", + "can_get_my_acc_ast_txs", + "can_get_my_acc_detail", + "can_get_my_acc_txs", + "can_get_my_account", + "can_get_my_signatories", + "can_get_my_txs", + "can_grant_can_add_my_signatory", + "can_grant_can_remove_my_signatory", + "can_grant_can_set_my_account_detail", + "can_grant_can_set_my_quorum", + "can_grant_can_transfer_my_assets", + "can_receive", + "can_remove_signatory", + "can_set_quorum", + "can_transfer" + ] + } + }, + { + "createRole":{ + "roleName":"money_creator", + "permissions":[ + "can_add_asset_qty", + "can_create_asset", + "can_receive", + "can_transfer" + ] + } + }, + { + "createDomain":{ + "domainId":"test", + "defaultRole":"user" + } + }, + { + "createAsset":{ + "assetName":"coin", + "domainId":"test", + "precision":2 + } + }, + { + "createAccount":{ + "accountName":"admin", + "domainId":"test", + "mainPubkey":"iJ9riB4zG+IUh9t33PMsX409XoBm540v6sQjn+kdQW8=" + } + }, + { + "createAccount":{ + "accountName":"test", + "domainId":"test", + "mainPubkey":"3MfszkSPLhkKSBrsfKIe5S7aVG5mC0gg9JdtATIVcJc=" + } + }, + { + "appendRole":{ + "accountId":"admin@test", + "roleName":"admin" + } + }, + { + "appendRole":{ + "accountId":"admin@test", + "roleName":"money_creator" + } + } + ] + } + } + ], + "txNumber":1, + "height":"1", + "prevBlockHash":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + } } diff --git a/example/java/TransactionExample.java b/example/java/TransactionExample.java index 19481053d6..08ab4b30a0 100644 --- a/example/java/TransactionExample.java +++ b/example/java/TransactionExample.java @@ -69,12 +69,11 @@ public static void main(String[] args) { long currentTime = System.currentTimeMillis(); String creator = "admin@test"; - long startTxCounter = 1, startQueryCounter = 1; + long startQueryCounter = 1; // build transaction (still unsigned) UnsignedTx utx = txBuilder.creatorAccountId(creator) .createdTime(BigInteger.valueOf(currentTime)) - .txCounter(BigInteger.valueOf(startTxCounter)) .createDomain("ru", "user") .createAsset("dollar", "ru", (short)2).build(); diff --git a/example/java/build_library.sh b/example/java/build_library.sh old mode 100644 new mode 100755 index ed2f5aef9a..2c4b0a7d28 --- a/example/java/build_library.sh +++ b/example/java/build_library.sh @@ -6,8 +6,13 @@ mkdir dist # build native library ./prepare.sh -cp build/shared_model/bindings/libirohajava.jnilib dist/libirohajava.jnilib +unamestr=`uname` +if [[ "$unamestr" == 'Linux' ]]; then + cp build/shared_model/bindings/libirohajava.so dist/libirohajava.so +elif [[ "$unamestr" == 'Darwin' ]]; then + cp build/shared_model/bindings/libirohajava.jnilib dist/libirohajava.jnilib +fi # build jar gradle jar diff --git a/example/node/index.js b/example/node/index.js index 41d4ce3b86..833ecae984 100644 --- a/example/node/index.js +++ b/example/node/index.js @@ -28,14 +28,12 @@ var adminPub = fs.readFileSync('../admin@test.pub').toString() var keys = crypto.convertFromExisting(adminPub, adminPriv) var currentTime = Date.now() -var startTxCounter = 1 var startQueryCounter = 1 var creator = 'admin@test' // build transaction var tx = txBuilder .creatorAccountId(creator) - .txCounter(startTxCounter) .createdTime(currentTime) .createDomain('ru', 'user') .createAsset('dollar', 'ru', 2) diff --git a/example/node0.priv b/example/node0.priv index 4ee1612c19..1362af9c39 100644 --- a/example/node0.priv +++ b/example/node0.priv @@ -1 +1 @@ -8316fe25fda2bb3964ae756251b5f1fe010fafe56443978d524dc6485548be76 \ No newline at end of file +41209bd907789fd5a796ac6bdff908bac2f7abcf7a1d0b99a18290f285f6e965 \ No newline at end of file diff --git a/example/node0.pub b/example/node0.pub index e3e0793df3..bb7c1c9dcd 100644 --- a/example/node0.pub +++ b/example/node0.pub @@ -1 +1 @@ -292a8714694095edce6be799398ed5d6244cd7be37eb813106b217d850d261f2 \ No newline at end of file +d04da271b57fe63426ae1dc97f6952104037411fcf4f3b739dc217f45e5fc99b \ No newline at end of file diff --git a/example/python/tx-example.py b/example/python/tx-example.py index e1acc0691f..10c310d03a 100644 --- a/example/python/tx-example.py +++ b/example/python/tx-example.py @@ -20,9 +20,13 @@ admin_pub = open("../admin@test.pub", "r").read() key_pair = crypto.convertFromExisting(admin_pub, admin_priv) +user1_kp = crypto.generateKeypair() + current_time = int(round(time.time() * 1000)) - 10**5 creator = "admin@test" +query_counter = 1 + def get_status(tx): # Create status request @@ -114,9 +118,11 @@ def send_query(query, key_pair): return query_response -def tx1(): +def create_asset_coin(): + """ + Create domain "domain" and asset "coin#domain" with precision 2 + """ tx = tx_builder.creatorAccountId(creator) \ - .txCounter(1) \ .createdTime(current_time) \ .createDomain("domain", "user") \ .createAsset("coin", "domain", 2).build() @@ -125,9 +131,11 @@ def tx1(): print_status_streaming(tx) -def tx2(): +def add_coin_to_admin(): + """ + Add 1000.00 asset quantity of asset coin to admin + """ tx = tx_builder.creatorAccountId(creator) \ - .txCounter(2) \ .createdTime(current_time) \ .addAssetQuantity("admin@test", "coin#domain", "1000.00").build() @@ -135,32 +143,61 @@ def tx2(): print_status_streaming(tx) -def tx3(): - user1_kp = crypto.generateKeypair() - +def create_account_userone(): + """ + Create account "userone@domain" + """ tx = tx_builder.creatorAccountId(creator) \ - .txCounter(3) \ .createdTime(current_time) \ .createAccount("userone", "domain", user1_kp.publicKey()).build() send_tx(tx, key_pair) print_status_streaming(tx) - -def tx4(): +def transfer_coin_from_admin_to_userone(): + """ + Transfer 2.00 of coin from admin@test to userone@domain + """ tx = tx_builder.creatorAccountId(creator) \ - .txCounter(4) \ .createdTime(current_time) \ .transferAsset("admin@test", "userone@domain", "coin#domain", "Some message", "2.00").build() send_tx(tx, key_pair) print_status_streaming(tx) +def grant_admin_to_add_detail_to_userone(): + """ + Grant admin@test to be able to set details information to userone@domain + """ + tx = tx_builder.creatorAccountId("userone@domain") \ + .createdTime(current_time) \ + .grantPermission(creator, "can_set_my_account_detail") \ + .build() + + send_tx(tx, user1_kp) + print_status_streaming(tx) + +def set_age_to_userone_by_admin(): + """ + Set age to userone@domain by admin@test + """ + tx = tx_builder.creatorAccountId(creator) \ + .createdTime(current_time) \ + .setAccountDetail("userone@domain", "age", "18") \ + .build() + + send_tx(tx, key_pair) + print_status_streaming(tx) -def get_asset(): +def get_coin_info(): + """ + Get information about asset coin#domain + """ + global query_counter + query_counter += 1 query = query_builder.creatorAccountId(creator) \ .createdTime(current_time) \ - .queryCounter(1) \ + .queryCounter(query_counter) \ .getAssetInfo("coin#domain") \ .build() @@ -178,9 +215,14 @@ def get_asset(): def get_account_asset(): + """ + Get list of transactions done by userone@domain with asset coin#domain + """ + global query_counter + query_counter += 1 query = query_builder.creatorAccountId(creator) \ .createdTime(current_time) \ - .queryCounter(11) \ + .queryCounter(query_counter) \ .getAccountAssets("userone@domain", "coin#domain") \ .build() @@ -188,11 +230,30 @@ def get_account_asset(): print(query_response) +def get_userone_info(): + """ + Get userone's key value information + """ + global query_counter + query_counter += 1 + query = query_builder.creatorAccountId(creator) \ + .createdTime(current_time) \ + .queryCounter(query_counter) \ + .getAccountDetail("userone@domain") \ + .build() + + query_response = send_query(query, key_pair) + print(query_response.account_detail_response.detail) + + -tx1() -tx2() -tx3() -tx4() -get_asset() +create_asset_coin() +add_coin_to_admin() +create_account_userone() +transfer_coin_from_admin_to_userone() +grant_admin_to_add_detail_to_userone() +set_age_to_userone_by_admin() +get_coin_info() get_account_asset() +get_userone_info() print("done!") diff --git a/example/test@test.priv b/example/test@test.priv index 7e6f039b91..f41898d68e 100644 --- a/example/test@test.priv +++ b/example/test@test.priv @@ -1 +1 @@ -4209ba343a92f4d086921a3f3c1eb26f50f2fece610ec3524058de79281564c2 \ No newline at end of file +2e5b37fd881f260323dca4f0776e6e1e969bef7dab14673858f82663e7cd8556 \ No newline at end of file diff --git a/example/test@test.pub b/example/test@test.pub index 556317b45f..95419850c9 100644 --- a/example/test@test.pub +++ b/example/test@test.pub @@ -1 +1 @@ -359f925e4eeecfdd6aa1abc0b79a6a121a5dd63bb612b603247ea4f8ad160156 \ No newline at end of file +dcc7ecce448f2e190a481aec7ca21ee52eda546e660b4820f4976d0132157097 \ No newline at end of file diff --git a/iroha-cli/client.cpp b/iroha-cli/client.cpp index 0e4a0f86af..a9cb123342 100644 --- a/iroha-cli/client.cpp +++ b/iroha-cli/client.cpp @@ -16,6 +16,9 @@ */ #include "client.hpp" + +#include "backend/protobuf/queries/proto_query.hpp" +#include "backend/protobuf/transaction.hpp" #include "model/converters/json_query_factory.hpp" #include "model/converters/json_transaction_factory.hpp" #include "model/converters/pb_query_factory.hpp" @@ -27,13 +30,12 @@ namespace iroha_cli { : command_client_(target_ip, port), query_client_(target_ip, port) {} CliClient::Response CliClient::sendTx( - iroha::model::Transaction tx) { + const shared_model::interface::Transaction &tx) { + const auto proto_tx = + static_cast(tx); CliClient::Response response; - // Convert to protobuf - iroha::model::converters::PbTransactionFactory factory; - auto pb_tx = factory.serialize(tx); // Send to iroha: - response.status = command_client_.Torii(pb_tx); + response.status = command_client_.Torii(proto_tx.getTransport()); // TODO 12/10/2017 neewy implement return of real transaction status IR-494 response.answer = TxStatus::OK; @@ -55,13 +57,14 @@ namespace iroha_cli { } CliClient::Response CliClient::sendQuery( - std::shared_ptr query) { + const shared_model::interface::Query &query) { CliClient::Response response; // Convert to proto and send to Iroha iroha::model::converters::PbQueryFactory pb_factory; - auto pb_query = pb_factory.serialize(query); + auto proto_query = static_cast(query); iroha::protocol::QueryResponse query_response; - response.status = query_client_.Find(pb_query.value(), query_response); + response.status = + query_client_.Find(proto_query.getTransport(), query_response); response.answer = query_response; return response; } diff --git a/iroha-cli/client.hpp b/iroha-cli/client.hpp index 1458b86923..16c41c4abd 100644 --- a/iroha-cli/client.hpp +++ b/iroha-cli/client.hpp @@ -23,12 +23,12 @@ #include "torii/command_client.hpp" #include "torii/query_client.hpp" -namespace iroha { - namespace model { - struct Query; - struct Transaction; - } -} +namespace shared_model { + namespace interface { + class Transaction; + class Query; + } // namespace interface +} // namespace shared_model namespace iroha_cli { @@ -50,7 +50,7 @@ namespace iroha_cli { * @return */ CliClient::Response sendTx( - iroha::model::Transaction tx); + const shared_model::interface::Transaction &tx); /** * Send Query to Iroha Peer, i.e. target_ip:port @@ -58,7 +58,7 @@ namespace iroha_cli { * @return */ CliClient::Response sendQuery( - std::shared_ptr query); + const shared_model::interface::Query &query); CliClient::Response getTxStatus( std::string tx_hash); diff --git a/iroha-cli/interactive/impl/interactive_cli.cpp b/iroha-cli/interactive/impl/interactive_cli.cpp index e5fb118eb3..222fa7b8e2 100644 --- a/iroha-cli/interactive/impl/interactive_cli.cpp +++ b/iroha-cli/interactive/impl/interactive_cli.cpp @@ -44,12 +44,11 @@ namespace iroha_cli { const std::string &account_name, const std::string &default_peer_ip, int default_port, - uint64_t tx_counter, uint64_t qry_counter, const std::shared_ptr &provider) : creator_(account_name), tx_cli_( - creator_, default_peer_ip, default_port, tx_counter, provider), + creator_, default_peer_ip, default_port, provider), query_cli_( creator_, default_peer_ip, default_port, qry_counter, provider), statusCli_(default_peer_ip, default_port) { diff --git a/iroha-cli/interactive/impl/interactive_query_cli.cpp b/iroha-cli/interactive/impl/interactive_query_cli.cpp index 3c1f6a362f..8a9b6047b7 100644 --- a/iroha-cli/interactive/impl/interactive_query_cli.cpp +++ b/iroha-cli/interactive/impl/interactive_query_cli.cpp @@ -18,6 +18,8 @@ #include #include +#include "backend/protobuf/queries/proto_query.hpp" + #include "client.hpp" #include "common/byteutils.hpp" #include "crypto/keys_manager_impl.hpp" @@ -26,6 +28,7 @@ #include "grpc_response_handler.hpp" #include "interactive/interactive_query_cli.hpp" #include "model/converters/json_query_factory.hpp" +#include "model/converters/pb_query_factory.hpp" #include "model/model_crypto_provider.hpp" // for ModelCryptoProvider #include "model/queries/get_asset_info.hpp" #include "model/queries/get_roles.hpp" @@ -194,9 +197,8 @@ namespace iroha_cli { GetTransactions::TxHashCollectionType tx_hashes; std::for_each( params.begin(), params.end(), [&tx_hashes](auto const &hex_hash) { - if (auto opt = iroha:: - hexstringToArray( - hex_hash)) { + if (auto opt = iroha::hexstringToArray< + GetTransactions::TxHashType::size()>(hex_hash)) { tx_hashes.push_back(*opt); } }); @@ -262,7 +264,9 @@ namespace iroha_cli { provider_->sign(*query_); CliClient client(address.value().first, address.value().second); - GrpcResponseHandler{}.handle(client.sendQuery(query_)); + auto query = shared_model::proto::Query( + *iroha::model::converters::PbQueryFactory().serialize(query_)); + GrpcResponseHandler{}.handle(client.sendQuery(query)); printEnd(); // Stop parsing return false; diff --git a/iroha-cli/interactive/impl/interactive_transaction_cli.cpp b/iroha-cli/interactive/impl/interactive_transaction_cli.cpp index 386629218c..02b095a6e1 100644 --- a/iroha-cli/interactive/impl/interactive_transaction_cli.cpp +++ b/iroha-cli/interactive/impl/interactive_transaction_cli.cpp @@ -19,6 +19,8 @@ #include #include + +#include "backend/protobuf/transaction.hpp" #include "client.hpp" #include "grpc_response_handler.hpp" #include "model/commands/append_role.hpp" @@ -30,12 +32,14 @@ #include "model/converters/json_common.hpp" #include "model/converters/json_transaction_factory.hpp" #include "model/converters/pb_common.hpp" +#include "model/converters/pb_transaction_factory.hpp" #include "model/model_crypto_provider.hpp" // for ModelCryptoProvider -#include "validators/permissions.hpp" #include "model/sha3_hash.hpp" #include "parser/parser.hpp" // for parser::ParseValue +#include "validators/permissions.hpp" using namespace iroha::model; +using namespace shared_model::permissions; namespace iroha_cli { namespace interactive { @@ -177,13 +181,11 @@ namespace iroha_cli { const std::string &creator_account, const std::string &default_peer_ip, int default_port, - uint64_t tx_counter, const std::shared_ptr &provider) : current_context_(MAIN), creator_(creator_account), default_peer_ip_(default_peer_ip), default_port_(default_port), - tx_counter_(tx_counter), provider_(provider) { log_ = logger::log("InteractiveTransactionCli"); createCommandMenu(); @@ -195,8 +197,7 @@ namespace iroha_cli { current_context_ = MAIN; printMenu("Forming a new transactions, choose command to add: ", commands_menu_); - // Creating a new transaction, increment local tx_counter - ++tx_counter_; + // Creating a new transaction while (is_parsing) { auto line = promptString("> "); if (not line) { @@ -253,9 +254,7 @@ namespace iroha_cli { auto create_account = parser::parseValue(params[8]); if (not(read_self and edit_self and read_all and transfer_receive - and asset_create - and create_domain - and roles + and asset_create and create_domain and roles and create_account)) { std::cout << "Wrong format for permission" << std::endl; return nullptr; @@ -473,17 +472,18 @@ namespace iroha_cli { // Forming a transaction - auto tx = - tx_generator_.generateTransaction(creator_, tx_counter_, commands_); + auto tx = tx_generator_.generateTransaction(creator_, commands_); // clear commands so that we can start creating new tx commands_.clear(); provider_->sign(tx); GrpcResponseHandler response_handler; - + auto shared_tx = shared_model::proto::Transaction( + iroha::model::converters::PbTransactionFactory().serialize(tx)); response_handler.handle( - CliClient(address.value().first, address.value().second).sendTx(tx)); + CliClient(address.value().first, address.value().second) + .sendTx(shared_tx)); printTxHash(tx); printEnd(); @@ -502,8 +502,7 @@ namespace iroha_cli { } // Forming a transaction - auto tx = - tx_generator_.generateTransaction(creator_, tx_counter_, commands_); + auto tx = tx_generator_.generateTransaction(creator_, commands_); // clear commands so that we can start creating new tx commands_.clear(); diff --git a/iroha-cli/interactive/interactive_cli.hpp b/iroha-cli/interactive/interactive_cli.hpp index 8f0b00a915..0756746042 100644 --- a/iroha-cli/interactive/interactive_cli.hpp +++ b/iroha-cli/interactive/interactive_cli.hpp @@ -32,7 +32,6 @@ namespace iroha_cli { * @param account_name registered in Iroha network * @param default_peer_ip default peer ip to send transactions/query * @param default_port default port of peer's Iroha Torii - * @param tx_counter synchronized nonce for sending transaction * @param qry_counter synchronized nonce for sending queries * @param provider crypto provider to make signatures */ @@ -40,7 +39,6 @@ namespace iroha_cli { const std::string &account_name, const std::string &default_peer_ip, int default_port, - uint64_t tx_counter, uint64_t qry_counter, const std::shared_ptr &provider); /** diff --git a/iroha-cli/interactive/interactive_transaction_cli.hpp b/iroha-cli/interactive/interactive_transaction_cli.hpp index a9a5abb572..5e8f2a9597 100644 --- a/iroha-cli/interactive/interactive_transaction_cli.hpp +++ b/iroha-cli/interactive/interactive_transaction_cli.hpp @@ -40,14 +40,12 @@ namespace iroha_cli { * @param creator_account user Iroha account * @param default_peer_ip of Iroha peer * @param default_port of Iroha peer - * @param tx_counter synchronized with Iroha network * @param provider for signing transactions */ InteractiveTransactionCli( const std::string &creator_account, const std::string &default_peer_ip, int default_port, - uint64_t tx_counter, const std::shared_ptr &provider); /** * Run interactive query command line @@ -180,9 +178,6 @@ namespace iroha_cli { std::string default_peer_ip_; int default_port_; - // Transaction counter specific for account creator - uint64_t tx_counter_; - // Builder for new commands iroha::model::generators::CommandGenerator generator_; diff --git a/iroha-cli/main.cpp b/iroha-cli/main.cpp index cc9d533804..364bbeca64 100644 --- a/iroha-cli/main.cpp +++ b/iroha-cli/main.cpp @@ -16,26 +16,29 @@ */ #include +#include +#include #include -#include #include +#include "backend/protobuf/queries/proto_query.hpp" #include "client.hpp" -#include "common/assert_config.hpp" +#include "converters/protobuf/json_proto_converter.hpp" #include "crypto/keys_manager_impl.hpp" #include "grpc_response_handler.hpp" #include "interactive/interactive_cli.hpp" #include "model/converters/json_block_factory.hpp" #include "model/converters/json_query_factory.hpp" +#include "model/converters/pb_block_factory.hpp" +#include "model/converters/pb_query_factory.hpp" +#include "model/converters/pb_transaction_factory.hpp" #include "model/generators/block_generator.hpp" #include "model/model_crypto_provider_impl.hpp" -#include "validators.hpp" // Account information -DEFINE_bool( - new_account, - false, - "Generate and save locally new public/private keys"); +DEFINE_bool(new_account, + false, + "Generate and save locally new public/private keys"); DEFINE_string(account_name, "", "Name of the account. Must be unique in iroha network"); @@ -55,6 +58,9 @@ DEFINE_string(json_query, "", "Query in json format"); DEFINE_bool(genesis_block, false, "Generate genesis block for new Iroha network"); +DEFINE_string(genesis_transaction, + "", + "File with transaction in json format for the genesis block"); DEFINE_string(peers_address, "", "File with peers address for new Iroha network"); @@ -62,13 +68,20 @@ DEFINE_string(peers_address, // Run iroha-cli in interactive mode DEFINE_bool(interactive, true, "Run iroha-cli in interactive mode"); - using namespace iroha::protocol; using namespace iroha::model::generators; using namespace iroha::model::converters; using namespace iroha_cli::interactive; namespace fs = boost::filesystem; +iroha::keypair_t *makeOldModel(const shared_model::crypto::Keypair &keypair) { + return new iroha::keypair_t{ + shared_model::crypto::PublicKey::OldPublicKeyType::from_string( + toBinaryString(keypair.publicKey())), + shared_model::crypto::PrivateKey::OldPrivateKeyType::from_string( + toBinaryString(keypair.privateKey()))}; +} + int main(int argc, char *argv[]) { gflags::ParseCommandLineFlags(&argc, &argv, true); gflags::ShutDownCommandLineFlags(); @@ -76,25 +89,42 @@ int main(int argc, char *argv[]) { // Generate new genesis block now Iroha network if (FLAGS_genesis_block) { BlockGenerator generator; - - if (FLAGS_peers_address.empty()) { - logger->error("--peers_address is empty"); - return EXIT_FAILURE; + iroha::model::Transaction transaction; + if (FLAGS_genesis_transaction.empty()) { + if (FLAGS_peers_address.empty()) { + logger->error("--peers_address is empty"); + return EXIT_FAILURE; + } + std::ifstream file(FLAGS_peers_address); + std::vector peers_address; + std::copy(std::istream_iterator(file), + std::istream_iterator(), + std::back_inserter(peers_address)); + // Generate genesis block + transaction = TransactionGenerator().generateGenesisTransaction( + 0, std::move(peers_address)); + } else { + rapidjson::Document doc; + std::ifstream file(FLAGS_genesis_transaction); + rapidjson::IStreamWrapper isw(file); + doc.ParseStream(isw); + auto some_tx = JsonTransactionFactory{}.deserialize(doc); + if (some_tx) { + transaction = *some_tx; + } else { + logger->error( + "Cannot deserialize genesis transaction (problem with file reading " + "or illformed json?)"); + return EXIT_FAILURE; + } } - std::ifstream file(FLAGS_peers_address); - std::vector peers_address; - std::copy(std::istream_iterator(file), - std::istream_iterator(), - std::back_inserter(peers_address)); - // Generate genesis block - auto transaction = TransactionGenerator().generateGenesisTransaction( - 0, std::move(peers_address)); + auto block = generator.generateGenesisBlock(0, {transaction}); // Convert to json - JsonBlockFactory json_factory; - auto doc = json_factory.serialize(block); std::ofstream output_file("genesis.block"); - output_file << jsonToString(doc); + auto bl = shared_model::proto::Block( + iroha::model::converters::PbBlockFactory().serialize(block)); + output_file << shared_model::converters::protobuf::modelToJson(bl); logger->info("File saved to genesis.block"); } // Create new pub/priv key, register in Iroha Network @@ -129,7 +159,10 @@ int main(int argc, char *argv[]) { if (not tx_opt) { logger->error("Json transaction has wrong format."); } else { - response_handler.handle(client.sendTx(tx_opt.value())); + auto tx = shared_model::proto::Transaction( + iroha::model::converters::PbTransactionFactory().serialize( + *tx_opt)); + response_handler.handle(client.sendTx(tx)); } } if (not FLAGS_json_query.empty()) { @@ -142,7 +175,10 @@ int main(int argc, char *argv[]) { if (not query_opt) { logger->error("Json has wrong format."); } else { - response_handler.handle(client.sendQuery(query_opt.value())); + auto query = shared_model::proto::Query( + *iroha::model::converters::PbQueryFactory().serialize(*query_opt)); + auto response = client.sendQuery(query); + response_handler.handle(response); } } } @@ -158,20 +194,17 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } iroha::KeysManagerImpl manager((path / FLAGS_account_name).string()); - boost::optional keypair; - if (FLAGS_pass_phrase.size() != 0) { - keypair = manager.loadKeys(FLAGS_pass_phrase); - } else { - keypair = manager.loadKeys(); - } + auto keypair = FLAGS_pass_phrase.size() != 0 + ? manager.loadKeys(FLAGS_pass_phrase) + : manager.loadKeys(); if (not keypair) { logger->error( "Cannot load specified keypair, or keypair is invalid. Path: {}, " - "keypair name: {}. Use --key_path to path to your keypair. \nMaybe wrong pass phrase (\"{}\")?", + "keypair name: {}. Use --key_path with path of your keypair. \n" + "Maybe wrong pass phrase (\"{}\")?", path.string(), FLAGS_account_name, - FLAGS_pass_phrase - ); + FLAGS_pass_phrase); return EXIT_FAILURE; } // TODO 13/09/17 grimadas: Init counters from Iroha, or read from disk? @@ -181,9 +214,8 @@ int main(int argc, char *argv[]) { FLAGS_peer_ip, FLAGS_torii_port, 0, - 0, std::make_shared( - *keypair)); + *std::unique_ptr(makeOldModel(*keypair)))); interactiveCli.run(); } else { logger->error("Invalid flags"); diff --git a/irohad/ametsuchi/CMakeLists.txt b/irohad/ametsuchi/CMakeLists.txt index 8806c4ce01..4f37c62781 100644 --- a/irohad/ametsuchi/CMakeLists.txt +++ b/irohad/ametsuchi/CMakeLists.txt @@ -13,15 +13,13 @@ add_library(ametsuchi ) target_link_libraries(ametsuchi - json_model_converters logger rxcpp pqxx libs_common command_execution - boost - model_interfaces + query_execution + shared_model_interfaces shared_model_proto_backend - shared_model_proto_builders shared_model_stateless_validation ) diff --git a/irohad/ametsuchi/block_query.hpp b/irohad/ametsuchi/block_query.hpp index cac9647d5c..47780171fd 100644 --- a/irohad/ametsuchi/block_query.hpp +++ b/irohad/ametsuchi/block_query.hpp @@ -96,6 +96,14 @@ namespace iroha { */ virtual boost::optional getTxByHashSync( const shared_model::crypto::Hash &hash) = 0; + + /** + * Synchronously checks whether transaction + * with given hash is present in any block + * @param hash - transaction hash + * @return true if transaction exists, false otherwise + */ + virtual bool hasTxWithHash(const shared_model::crypto::Hash &hash) = 0; }; } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/mutable_storage_impl.cpp b/irohad/ametsuchi/impl/mutable_storage_impl.cpp index 45a34b4c20..2baaf5e8e3 100644 --- a/irohad/ametsuchi/impl/mutable_storage_impl.cpp +++ b/irohad/ametsuchi/impl/mutable_storage_impl.cpp @@ -23,7 +23,6 @@ #include "ametsuchi/impl/postgres_wsv_command.hpp" #include "ametsuchi/impl/postgres_wsv_query.hpp" #include "ametsuchi/wsv_command.hpp" -#include "backend/protobuf/from_old_model.hpp" #include "model/sha3_hash.hpp" namespace iroha { @@ -76,9 +75,7 @@ namespace iroha { execute_transaction); if (result) { - block_store_.insert(std::make_pair( - block.height(), - clone(block))); + block_store_.insert(std::make_pair(block.height(), clone(block))); block_index_->index(block); top_hash_ = block.hash(); diff --git a/irohad/ametsuchi/impl/postgres_block_query.cpp b/irohad/ametsuchi/impl/postgres_block_query.cpp index d17aa25c41..ac2529d02c 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.cpp +++ b/irohad/ametsuchi/impl/postgres_block_query.cpp @@ -16,9 +16,11 @@ */ #include "ametsuchi/impl/postgres_block_query.hpp" + #include #include -#include "backend/protobuf/from_old_model.hpp" + +#include "converters/protobuf/json_proto_converter.hpp" namespace iroha { namespace ametsuchi { @@ -38,26 +40,25 @@ namespace iroha { if (height > to or count == 0) { return rxcpp::observable<>::empty(); } - return rxcpp::observable<>::range(height, to).flat_map([this](auto i) { - // TODO IR-975 victordrobny 12.02.2018 convert directly to - // shared_model::proto::Block after FlatFile will be reworked to new - // model - auto block = block_store_.get(i) | [](const auto &bytes) { - return model::converters::stringToJson(bytesToString(bytes)); - } | [this](const auto &d) { - return serializer_.deserialize(d); - } | [](const auto &block_old) { - return std::make_shared( - shared_model::proto::from_old(block_old)); - }; - return rxcpp::observable<>::create( - [block{std::move(block)}](auto s) { - if (block) { - s.on_next(block); - } - s.on_completed(); - }); - }); + return rxcpp::observable<>::range(height, to) + .flat_map([this](const auto &i) { + auto block = block_store_.get(i) | [](const auto &bytes) { + return shared_model::converters::protobuf::jsonToModel< + shared_model::proto::Block>(bytesToString(bytes)); + }; + if (not block) { + log_->error("error while converting from JSON"); + } + + return rxcpp::observable<>::create( + [block{std::move(block)}](const auto &s) { + if (block) { + s.on_next(std::make_shared( + block.value())); + } + s.on_completed(); + }); + }); } rxcpp::observable PostgresBlockQuery::getBlocksFrom( @@ -100,6 +101,7 @@ namespace iroha { -> boost::optional< shared_model::interface::types::HeightType> { if (result.size() == 0) { + log_->info("No block with transaction {}", hash.toString()); return boost::none; } return result[0] @@ -111,19 +113,20 @@ namespace iroha { std::function PostgresBlockQuery::callback( const rxcpp::subscriber &subscriber, uint64_t block_id) { return [this, &subscriber, block_id](pqxx::result &result) { - auto block = block_store_.get(block_id) | [this](auto bytes) { - // TODO IR-975 victordrobny 12.02.2018 convert directly to - // shared_model::proto::Block after FlatFile will be reworked to new - // model - return boost::optional( - shared_model::proto::from_old(*serializer_.deserialize( - *model::converters::stringToJson(bytesToString(bytes))))); + auto block = block_store_.get(block_id) | [](const auto &bytes) { + return shared_model::converters::protobuf::jsonToModel< + shared_model::proto::Block>(bytesToString(bytes)); }; + if (not block) { + log_->error("error while converting from JSON"); + return; + } + boost::for_each( result | boost::adaptors::transformed([](const auto &x) { return x.at("index").template as(); }), - [&](auto x) { + [&](const auto &x) { subscriber.on_next(PostgresBlockQuery::wTransaction( clone(*block->transactions().at(x)))); }); @@ -134,7 +137,7 @@ namespace iroha { PostgresBlockQuery::getAccountTransactions( const shared_model::interface::types::AccountIdType &account_id) { return rxcpp::observable<>::create( - [this, account_id](auto subscriber) { + [this, account_id](const auto &subscriber) { auto block_ids = this->getBlockIds(account_id); if (block_ids.empty()) { subscriber.on_completed(); @@ -183,10 +186,10 @@ namespace iroha { PostgresBlockQuery::getTransactions( const std::vector &tx_hashes) { return rxcpp::observable<>::create>( - [this, tx_hashes](auto subscriber) { + [this, tx_hashes](const auto &subscriber) { std::for_each(tx_hashes.begin(), tx_hashes.end(), - [that = this, &subscriber](auto tx_hash) { + [that = this, &subscriber](const auto &tx_hash) { subscriber.on_next(that->getTxByHashSync(tx_hash)); }); subscriber.on_completed(); @@ -196,30 +199,32 @@ namespace iroha { boost::optional PostgresBlockQuery::getTxByHashSync( const shared_model::crypto::Hash &hash) { - return getBlockId(hash) | - [this](auto blockId) { return block_store_.get(blockId); } | - [](auto bytes) { - // TODO IR-975 victordrobny 12.02.2018 convert directly to - // shared_model::proto::Block after FlatFile will be reworked to new - // model - return model::converters::stringToJson(bytesToString(bytes)); - } - | [&](const auto &json) { return serializer_.deserialize(json); } | - [](const auto &block) { - return boost::optional( - shared_model::proto::from_old(block)); - } - | [&](const auto &block) { - boost::optional result; - auto it = - std::find_if(block.transactions().begin(), - block.transactions().end(), - [&hash](auto tx) { return tx->hash() == hash; }); - if (it != block.transactions().end()) { - result = boost::optional(clone(**it)); - } - return result; - }; + auto block = getBlockId(hash) | [this](const auto &block_id) { + return block_store_.get(block_id); + } | [](const auto &bytes) { + return shared_model::converters::protobuf::jsonToModel< + shared_model::proto::Block>(bytesToString(bytes)); + }; + if (not block) { + log_->error("error while converting from JSON"); + return boost::none; + } + + boost::optional result; + auto it = + std::find_if(block->transactions().begin(), + block->transactions().end(), + [&hash](const auto &tx) { return tx->hash() == hash; }); + if (it != block->transactions().end()) { + result = boost::optional( + PostgresBlockQuery::wTransaction(clone(**it))); + } + return result; + } + + bool PostgresBlockQuery::hasTxWithHash( + const shared_model::crypto::Hash &hash) { + return getBlockId(hash) != boost::none; } } // namespace ametsuchi diff --git a/irohad/ametsuchi/impl/postgres_block_query.hpp b/irohad/ametsuchi/impl/postgres_block_query.hpp index 7343331e4a..c2eab7f51e 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.hpp +++ b/irohad/ametsuchi/impl/postgres_block_query.hpp @@ -24,7 +24,6 @@ #include "ametsuchi/block_query.hpp" #include "ametsuchi/impl/flat_file/flat_file.hpp" #include "logger/logger.hpp" -#include "model/converters/json_block_factory.hpp" #include "postgres_wsv_common.hpp" namespace iroha { @@ -63,6 +62,8 @@ namespace iroha { rxcpp::observable getTopBlocks(uint32_t count) override; + bool hasTxWithHash(const shared_model::crypto::Hash &hash) override; + private: /** * Returns all blocks' ids containing given account id @@ -95,7 +96,6 @@ namespace iroha { logger::Logger log_; using ExecuteType = decltype(makeExecuteOptional(transaction_, log_)); ExecuteType execute_; - model::converters::JsonBlockFactory serializer_; }; } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/postgres_wsv_command.cpp b/irohad/ametsuchi/impl/postgres_wsv_command.cpp index d0b30339a5..32a53a54ee 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_command.cpp +++ b/irohad/ametsuchi/impl/postgres_wsv_command.cpp @@ -162,13 +162,11 @@ namespace iroha { const shared_model::interface::Account &account) { auto result = execute_( "INSERT INTO account(account_id, domain_id, quorum, " - "transaction_count, data) VALUES (" + "data) VALUES (" + transaction_.quote(account.accountId()) + ", " + transaction_.quote(account.domainId()) + ", " + transaction_.quote(account.quorum()) + ", " - // Transaction counter - + transaction_.quote(default_tx_counter) + ", " + transaction_.quote(account.jsonData()) + ");"); auto message_gen = [&] { @@ -176,10 +174,9 @@ namespace iroha { "account id: '%s', " "domain id: '%s', " "quorum: '%d', " - "transaction counter: '%d', " "json_data: %s") % account.accountId() % account.domainId() % account.quorum() - % default_tx_counter % account.jsonData()) + % account.jsonData()) .str(); }; @@ -359,8 +356,6 @@ namespace iroha { "UPDATE account\n" " SET quorum=" + transaction_.quote(account.quorum()) + - ", transaction_count=" + - /*account.transaction_count*/ transaction_.quote(default_tx_counter) + "\n" " WHERE account_id=" + transaction_.quote(account.accountId()) + ";"); diff --git a/irohad/ametsuchi/impl/postgres_wsv_command.hpp b/irohad/ametsuchi/impl/postgres_wsv_command.hpp index 4290db4365..2f251a3599 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_command.hpp +++ b/irohad/ametsuchi/impl/postgres_wsv_command.hpp @@ -91,8 +91,6 @@ namespace iroha { &permission_id) override; private: - const size_t default_tx_counter = 0; - pqxx::nontransaction &transaction_; using ExecuteType = decltype(makeExecuteResult(transaction_)); diff --git a/irohad/ametsuchi/impl/storage_impl.cpp b/irohad/ametsuchi/impl/storage_impl.cpp index d020533ab3..564187c53f 100644 --- a/irohad/ametsuchi/impl/storage_impl.cpp +++ b/irohad/ametsuchi/impl/storage_impl.cpp @@ -22,13 +22,9 @@ #include "ametsuchi/impl/postgres_block_query.hpp" #include "ametsuchi/impl/postgres_wsv_query.hpp" #include "ametsuchi/impl/temporary_wsv_impl.hpp" -#include "model/converters/json_common.hpp" +#include "converters/protobuf/json_proto_converter.hpp" #include "postgres_ordering_service_persistent_state.hpp" -// TODO: 14-02-2018 Alexey Chernyshov remove this after relocation to -// shared_model https://soramitsu.atlassian.net/browse/IR-887 -#include "backend/protobuf/from_old_model.hpp" - namespace iroha { namespace ametsuchi { @@ -130,6 +126,8 @@ namespace iroha { const auto &top_hash) { return true; }); log_->info("block inserted: {}", inserted); commit(std::move(storage.value)); + notifier_.get_subscriber().on_next( + std::shared_ptr(clone(block))); }, [&](expected::Error &error) { log_->error(error.error); @@ -257,13 +255,11 @@ DROP TABLE IF EXISTS index_by_id_height_asset; auto storage_ptr = std::move(mutableStorage); // get ownership of storage auto storage = static_cast(storage_ptr.get()); for (const auto &block : storage->block_store_) { - // TODO: rework to shared model converters once they are available - // IR-1084 Nikita Alekseev - auto old_block = - *std::unique_ptr(block.second->makeOldModel()); - block_store_->add(block.first, - stringToBytes(model::converters::jsonToString( - serializer_.serialize(old_block)))); + block_store_->add( + block.first, + stringToBytes(shared_model::converters::protobuf::modelToJson( + *std::static_pointer_cast( + block.second)))); } storage->transaction_->exec("COMMIT;"); @@ -289,5 +285,9 @@ DROP TABLE IF EXISTS index_by_id_height_asset; std::shared_ptr StorageImpl::getBlockQuery() const { return blocks_; } + rxcpp::observable> + StorageImpl::on_commit() { + return notifier_.get_observable(); + } } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/storage_impl.hpp b/irohad/ametsuchi/impl/storage_impl.hpp index c9838da7fc..5ea53adb95 100644 --- a/irohad/ametsuchi/impl/storage_impl.hpp +++ b/irohad/ametsuchi/impl/storage_impl.hpp @@ -20,12 +20,11 @@ #include "ametsuchi/storage.hpp" -#include #include +#include #include #include #include "logger/logger.hpp" -#include "model/converters/json_block_factory.hpp" namespace iroha { namespace ametsuchi { @@ -62,7 +61,8 @@ namespace iroha { * @param blocks - block for insertion * @return true if all blocks are inserted */ - virtual bool insertBlock(const shared_model::interface::Block &block) override; + virtual bool insertBlock( + const shared_model::interface::Block &block) override; /** * Insert blocks without validation @@ -70,7 +70,8 @@ namespace iroha { * @return true if inserted */ virtual bool insertBlocks( - const std::vector> &blocks) override; + const std::vector> + &blocks) override; virtual void dropStorage() override; @@ -80,6 +81,9 @@ namespace iroha { std::shared_ptr getBlockQuery() const override; + rxcpp::observable> + on_commit() override; + ~StorageImpl() override; protected: @@ -111,13 +115,14 @@ namespace iroha { std::shared_ptr blocks_; - model::converters::JsonBlockFactory serializer_; - // Allows multiple readers and a single writer std::shared_timed_mutex rw_lock_; logger::Logger log_; + rxcpp::subjects::subject> + notifier_; + protected: const std::string init_ = R"( CREATE TABLE IF NOT EXISTS role ( @@ -137,7 +142,6 @@ CREATE TABLE IF NOT EXISTS account ( account_id character varying(288), domain_id character varying(255) NOT NULL REFERENCES domain, quorum int NOT NULL, - transaction_count int NOT NULL DEFAULT 0, data JSONB, PRIMARY KEY (account_id) ); diff --git a/irohad/ametsuchi/storage.hpp b/irohad/ametsuchi/storage.hpp index 0678591684..f9d84b2959 100644 --- a/irohad/ametsuchi/storage.hpp +++ b/irohad/ametsuchi/storage.hpp @@ -18,6 +18,7 @@ #ifndef IROHA_AMETSUCHI_H #define IROHA_AMETSUCHI_H +#include #include #include "ametsuchi/mutable_factory.hpp" #include "ametsuchi/temporary_factory.hpp" @@ -27,7 +28,7 @@ namespace shared_model { namespace interface { class Block; } -} +} // namespace shared_model namespace iroha { @@ -58,7 +59,16 @@ namespace iroha { * @param blocks - collection of blocks for insertion * @return true if inserted */ - virtual bool insertBlocks(const std::vector> &blocks) = 0; + virtual bool insertBlocks( + const std::vector> + &blocks) = 0; + + /** + * method called when block is written to the storage + * @return observable with the Block committed + */ + virtual rxcpp::observable> + on_commit() = 0; /** * Remove all information from ledger diff --git a/irohad/consensus/yac/CMakeLists.txt b/irohad/consensus/yac/CMakeLists.txt index 4eac54002d..5bb0fa0b12 100644 --- a/irohad/consensus/yac/CMakeLists.txt +++ b/irohad/consensus/yac/CMakeLists.txt @@ -19,7 +19,7 @@ add_library(supermajority_check impl/supermajority_checker_impl.cpp ) target_link_libraries(supermajority_check - model + shared_model_interfaces ) add_library(yac @@ -43,4 +43,5 @@ target_link_libraries(yac yac_grpc logger hash + shared_model_proto_backend ) diff --git a/irohad/consensus/yac/impl/supermajority_checker_impl.cpp b/irohad/consensus/yac/impl/supermajority_checker_impl.cpp index 597be726fd..e40ba8658a 100644 --- a/irohad/consensus/yac/impl/supermajority_checker_impl.cpp +++ b/irohad/consensus/yac/impl/supermajority_checker_impl.cpp @@ -17,16 +17,17 @@ #include "consensus/yac/impl/supermajority_checker_impl.hpp" #include "interfaces/common_objects/peer.hpp" +#include "interfaces/common_objects/signature.hpp" namespace iroha { namespace consensus { namespace yac { bool SupermajorityCheckerImpl::hasSupermajority( - const shared_model::interface::SignatureSetType &signatures, + const shared_model::interface::types::SignatureRangeType &signatures, const std::vector> &peers) const { - return checkSize(signatures.size(), peers.size()) + return checkSize(boost::size(signatures), peers.size()) and peersSubset(signatures, peers); } @@ -40,17 +41,19 @@ namespace iroha { } bool SupermajorityCheckerImpl::peersSubset( - const shared_model::interface::SignatureSetType &signatures, + const shared_model::interface::types::SignatureRangeType &signatures, const std::vector> &peers) const { return std::all_of( - signatures.begin(), signatures.end(), [&peers](auto signature) { + signatures.begin(), + signatures.end(), + [&peers](const auto &signature) { return std::find_if( peers.begin(), peers.end(), [&signature](const std::shared_ptr< shared_model::interface::Peer> &peer) { - return signature->publicKey() == peer->pubkey(); + return signature.publicKey() == peer->pubkey(); }) != peers.end(); }); diff --git a/irohad/consensus/yac/impl/supermajority_checker_impl.hpp b/irohad/consensus/yac/impl/supermajority_checker_impl.hpp index a1ab775c31..894df51ac8 100644 --- a/irohad/consensus/yac/impl/supermajority_checker_impl.hpp +++ b/irohad/consensus/yac/impl/supermajority_checker_impl.hpp @@ -35,14 +35,16 @@ namespace iroha { * @return true on supermajority is achieved or false otherwise */ virtual bool hasSupermajority( - const shared_model::interface::SignatureSetType &signatures, - const std::vector> &peers) - const override; + const shared_model::interface::types::SignatureRangeType + &signatures, + const std::vector> + &peers) const override; virtual bool checkSize(uint64_t current, uint64_t all) const override; virtual bool peersSubset( - const shared_model::interface::SignatureSetType &signatures, + const shared_model::interface::types::SignatureRangeType + &signatures, const std::vector> &peers) const override; diff --git a/irohad/consensus/yac/impl/timer_impl.cpp b/irohad/consensus/yac/impl/timer_impl.cpp index 429866f25b..cc152018e8 100644 --- a/irohad/consensus/yac/impl/timer_impl.cpp +++ b/irohad/consensus/yac/impl/timer_impl.cpp @@ -21,17 +21,18 @@ namespace iroha { namespace consensus { namespace yac { - void TimerImpl::invokeAfterDelay(uint64_t millis, - std::function handler) { + TimerImpl::TimerImpl( + std::function()> invoke_delay) + : invoke_delay_(std::move(invoke_delay)) {} + + void TimerImpl::invokeAfterDelay(std::function handler) { deny(); - handler_ = std::move(handler); - timer = rxcpp::observable<>::timer(std::chrono::milliseconds(millis)); - handle = timer.subscribe_on(rxcpp::observe_on_new_thread()) - .subscribe([this](auto) { handler_(); }); + handle_ = invoke_delay_().subscribe( + [handler{std::move(handler)}](auto) { handler(); }); } void TimerImpl::deny() { - handle.unsubscribe(); + handle_.unsubscribe(); } TimerImpl::~TimerImpl() { diff --git a/irohad/consensus/yac/impl/timer_impl.hpp b/irohad/consensus/yac/impl/timer_impl.hpp index 9304d268c3..e38081a7bf 100644 --- a/irohad/consensus/yac/impl/timer_impl.hpp +++ b/irohad/consensus/yac/impl/timer_impl.hpp @@ -26,21 +26,26 @@ namespace iroha { namespace yac { class TimerImpl : public Timer { public: - TimerImpl() = default; + /// Delay observable type + using TimeoutType = long; + + /** + * Constructor + * @param invoke_delay cold observable which specifies invoke strategy + */ + explicit TimerImpl( + std::function()> invoke_delay); TimerImpl(const TimerImpl &) = delete; TimerImpl &operator=(const TimerImpl &) = delete; - void invokeAfterDelay(uint64_t millis, - std::function handler) override; + void invokeAfterDelay(std::function handler) override; void deny() override; ~TimerImpl() override; private: - std::function handler_; - - rxcpp::observable timer; - rxcpp::composite_subscription handle; + std::function()> invoke_delay_; + rxcpp::composite_subscription handle_; }; } // namespace yac } // namespace consensus diff --git a/irohad/consensus/yac/impl/yac.cpp b/irohad/consensus/yac/impl/yac.cpp index a663f4e13a..68d5539c15 100644 --- a/irohad/consensus/yac/impl/yac.cpp +++ b/irohad/consensus/yac/impl/yac.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,9 +35,9 @@ namespace iroha { "Crypto verification failed for message.\n Votes: "; result += logger::to_string(votes, [](const auto &vote) { std::string result = "(Public key: "; - result += vote.signature.pubkey.to_hexstring(); + result += vote.signature->publicKey().hex(); result += ", Signature: "; - result += vote.signature.signature.to_hexstring(); + result += vote.signature->signedData().hex(); result += ")\n"; return result; }); @@ -54,24 +54,21 @@ namespace iroha { std::shared_ptr network, std::shared_ptr crypto, std::shared_ptr timer, - ClusterOrdering order, - uint64_t delay) { + ClusterOrdering order) { return std::make_shared( - vote_storage, network, crypto, timer, order, delay); + vote_storage, network, crypto, timer, order); } Yac::Yac(YacVoteStorage vote_storage, std::shared_ptr network, std::shared_ptr crypto, std::shared_ptr timer, - ClusterOrdering order, - uint64_t delay) + ClusterOrdering order) : vote_storage_(std::move(vote_storage)), network_(std::move(network)), crypto_(std::move(crypto)), timer_(std::move(timer)), - cluster_order_(order), - delay_(delay) { + cluster_order_(order){ log_ = logger::log("YAC"); } @@ -137,8 +134,7 @@ namespace iroha { network_->send_vote(cluster_order_.currentLeader(), vote); cluster_order_.switchToNext(); if (cluster_order_.hasNext()) { - timer_->invokeAfterDelay(delay_, - [this, vote] { this->votingStep(vote); }); + timer_->invokeAfterDelay([this, vote] { this->votingStep(vote); }); } } @@ -151,11 +147,7 @@ namespace iroha { auto peers = cluster_order_.getPeers(); auto it = std::find_if(peers.begin(), peers.end(), [&](const auto &peer) { - // TODO: 24/03/2018 x3medima17, remove makeOldModel after - // migrating VoteMessage to the new model - auto old_peer = - *std::unique_ptr(peer->makeOldModel()); - return old_peer.pubkey == vote.signature.pubkey; + return peer->pubkey() == vote.signature->publicKey(); }); return it != peers.end() ? boost::make_optional(std::move(*it)) : boost::none; @@ -210,8 +202,8 @@ namespace iroha { // IR-497 }, [&](const CommitMessage &commit) { - notifier_.get_subscriber().on_next(commit); this->propagateCommit(commit); + notifier_.get_subscriber().on_next(commit); }); } this->closeRound(); @@ -228,7 +220,7 @@ namespace iroha { } else { log_->info("Apply vote: {} from unknown peer {}", vote.hash.block_hash, - vote.signature.pubkey.to_hexstring()); + vote.signature->publicKey().hex()); } auto answer = @@ -246,8 +238,8 @@ namespace iroha { // propagate for all log_->info("Propagate commit {} to whole network", vote.hash.block_hash); - notifier_.get_subscriber().on_next(commit); this->propagateCommit(commit); + notifier_.get_subscriber().on_next(commit); }, [&](const RejectMessage &reject) { // propagate reject for all diff --git a/irohad/consensus/yac/impl/yac_crypto_provider_impl.cpp b/irohad/consensus/yac/impl/yac_crypto_provider_impl.cpp index 77401f3399..a5850925e4 100644 --- a/irohad/consensus/yac/impl/yac_crypto_provider_impl.cpp +++ b/irohad/consensus/yac/impl/yac_crypto_provider_impl.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,13 +17,14 @@ #include "consensus/yac/impl/yac_crypto_provider_impl.hpp" #include "consensus/yac/transport/yac_pb_converters.hpp" -#include "cryptography/ed25519_sha3_impl/internal/ed25519_impl.hpp" -#include "cryptography/ed25519_sha3_impl/internal/sha3_hash.hpp" +#include "cryptography/crypto_provider/crypto_signer.hpp" +#include "cryptography/crypto_provider/crypto_verifier.hpp" namespace iroha { namespace consensus { namespace yac { - CryptoProviderImpl::CryptoProviderImpl(const keypair_t &keypair) + CryptoProviderImpl::CryptoProviderImpl( + const shared_model::crypto::Keypair &keypair) : keypair_(keypair) {} bool CryptoProviderImpl::verify(CommitMessage msg) { @@ -41,25 +42,38 @@ namespace iroha { } bool CryptoProviderImpl::verify(VoteMessage msg) { - return iroha::verify( - iroha::sha3_256( - PbConverters::serializeVote(msg).hash().SerializeAsString()) - .to_string(), - msg.signature.pubkey, - msg.signature.signature); + auto serialized = + PbConverters::serializeVote(msg).hash().SerializeAsString(); + auto blob = shared_model::crypto::Blob(serialized); + + return shared_model::crypto::CryptoVerifier<>::verify( + msg.signature->signedData(), blob, msg.signature->publicKey()); } VoteMessage CryptoProviderImpl::getVote(YacHash hash) { VoteMessage vote; vote.hash = hash; - auto signature = iroha::sign( - iroha::sha3_256( - PbConverters::serializeVote(vote).hash().SerializeAsString()) - .to_string(), - keypair_.pubkey, - keypair_.privkey); - vote.signature.signature = signature; - vote.signature.pubkey = keypair_.pubkey; + auto serialized = + PbConverters::serializeVotePayload(vote).hash().SerializeAsString(); + auto blob = shared_model::crypto::Blob(serialized); + const auto &pubkey = keypair_.publicKey(); + const auto &privkey = keypair_.privateKey(); + auto signature = shared_model::crypto::CryptoSigner<>::sign( + blob, shared_model::crypto::Keypair(pubkey, privkey)); + + shared_model::builder::DefaultSignatureBuilder() + .publicKey(pubkey) + .signedData(signature) + .build() + .match([&vote](iroha::expected::Value< + std::shared_ptr> + &sig) { vote.signature = sig.value; }, + [](iroha::expected::Error> + &reason) { + logger::log("YacCryptoProvider::getVote") + ->error("Cannot build vote signature: {}", + *reason.error); + }); return vote; } diff --git a/irohad/consensus/yac/impl/yac_crypto_provider_impl.hpp b/irohad/consensus/yac/impl/yac_crypto_provider_impl.hpp index d55288b087..9e04a46e0e 100644 --- a/irohad/consensus/yac/impl/yac_crypto_provider_impl.hpp +++ b/irohad/consensus/yac/impl/yac_crypto_provider_impl.hpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,13 +19,15 @@ #define IROHA_YAC_CRYPTO_PROVIDER_IMPL_HPP #include "consensus/yac/yac_crypto_provider.hpp" +#include "cryptography/keypair.hpp" namespace iroha { namespace consensus { namespace yac { class CryptoProviderImpl : public YacCryptoProvider { public: - explicit CryptoProviderImpl(const keypair_t &keypair); + explicit CryptoProviderImpl( + const shared_model::crypto::Keypair &keypair); bool verify(CommitMessage msg) override; @@ -36,7 +38,7 @@ namespace iroha { VoteMessage getVote(YacHash hash) override; private: - keypair_t keypair_; + shared_model::crypto::Keypair keypair_; }; } // namespace yac } // namespace consensus diff --git a/irohad/consensus/yac/impl/yac_gate_impl.cpp b/irohad/consensus/yac/impl/yac_gate_impl.cpp index b472fea6b0..d22e561b9d 100644 --- a/irohad/consensus/yac/impl/yac_gate_impl.cpp +++ b/irohad/consensus/yac/impl/yac_gate_impl.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -103,9 +103,7 @@ namespace iroha { std::shared_ptr>( [this, model_hash, vote](auto subscriber) { auto block = block_loader_->retrieveBlock( - shared_model::crypto::PublicKey( - {vote.signature.pubkey.begin(), - vote.signature.pubkey.end()}), + vote.signature->publicKey(), shared_model::crypto::Hash(model_hash)); // if load is successful if (block) { @@ -116,6 +114,7 @@ namespace iroha { }) // need only the first .first() + .retry() .subscribe( // if load is successful from at least one node [subscriber](auto block) { @@ -132,7 +131,6 @@ namespace iroha { } void YacGateImpl::copySignatures(const CommitMessage &commit) { - current_block_.second->clearSignatures(); for (const auto &vote : commit.votes) { auto sig = vote.hash.block_signature; current_block_.second->addSignature(sig->signedData(), diff --git a/irohad/consensus/yac/impl/yac_gate_impl.hpp b/irohad/consensus/yac/impl/yac_gate_impl.hpp index 2070befbe0..3b70fd834d 100644 --- a/irohad/consensus/yac/impl/yac_gate_impl.hpp +++ b/irohad/consensus/yac/impl/yac_gate_impl.hpp @@ -49,6 +49,11 @@ namespace iroha { std::shared_ptr block_loader, uint64_t delay); void vote(const shared_model::interface::Block &) override; + /** + * method called when commit recived + * assumes to retrieve a block eventually + * @return observable with the Block commited + */ rxcpp::observable> on_commit() override; diff --git a/irohad/consensus/yac/impl/yac_hash_provider_impl.cpp b/irohad/consensus/yac/impl/yac_hash_provider_impl.cpp index 4198774c41..59e602929e 100644 --- a/irohad/consensus/yac/impl/yac_hash_provider_impl.cpp +++ b/irohad/consensus/yac/impl/yac_hash_provider_impl.cpp @@ -30,7 +30,7 @@ namespace iroha { result.proposal_hash = hex_hash; result.block_hash = hex_hash; const auto &sig = *block.signatures().begin(); - result.block_signature = clone(*sig); + result.block_signature = clone(sig); return result; } diff --git a/irohad/consensus/yac/messages.hpp b/irohad/consensus/yac/messages.hpp index 53ec70bbef..6a9174a8c8 100644 --- a/irohad/consensus/yac/messages.hpp +++ b/irohad/consensus/yac/messages.hpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,7 +21,7 @@ #include #include "consensus/yac/yac_hash_provider.hpp" // for YacHash -#include "model/signature.hpp" // for model::Signature +#include "interfaces/common_objects/signature.hpp" namespace iroha { namespace consensus { @@ -32,10 +32,10 @@ namespace iroha { */ struct VoteMessage { YacHash hash; - model::Signature signature; + std::shared_ptr signature; bool operator==(const VoteMessage &rhs) const { - return hash == rhs.hash and signature == rhs.signature; + return hash == rhs.hash and *signature == *rhs.signature; } bool operator!=(const VoteMessage &rhs) const { diff --git a/irohad/consensus/yac/supermajority_checker.hpp b/irohad/consensus/yac/supermajority_checker.hpp index ad6d158547..5226a98605 100644 --- a/irohad/consensus/yac/supermajority_checker.hpp +++ b/irohad/consensus/yac/supermajority_checker.hpp @@ -19,7 +19,7 @@ #define IROHA_CONSENSUS_SUPERMAJORITY_CHECKER_HPP #include -#include "interfaces/common_objects/signable_hash.hpp" +#include "interfaces/common_objects/types.hpp" namespace shared_model { namespace interface { @@ -46,7 +46,8 @@ namespace iroha { * @return true on supermajority is achieved or false otherwise */ virtual bool hasSupermajority( - const shared_model::interface::SignatureSetType &signatures, + const shared_model::interface::types::SignatureRangeType + &signatures, const std::vector> &peers) const = 0; @@ -65,7 +66,8 @@ namespace iroha { * @return true if is subset or false otherwise */ virtual bool peersSubset( - const shared_model::interface::SignatureSetType &signatures, + const shared_model::interface::types::SignatureRangeType + &signatures, const std::vector> &peers) const = 0; diff --git a/irohad/consensus/yac/timer.hpp b/irohad/consensus/yac/timer.hpp index a99cfc4c39..8ee95b9312 100644 --- a/irohad/consensus/yac/timer.hpp +++ b/irohad/consensus/yac/timer.hpp @@ -30,12 +30,10 @@ namespace iroha { class Timer { public: /** - * Invoke handler after delay - * @param millis - number of milliseconds before invoking + * Invoke handler with class-specific strategy * @param handler - function, that will be invoked */ - virtual void invokeAfterDelay(uint64_t millis, - std::function handler) = 0; + virtual void invokeAfterDelay(std::function handler) = 0; /** * Stop timer diff --git a/irohad/consensus/yac/transport/impl/network_impl.cpp b/irohad/consensus/yac/transport/impl/network_impl.cpp index 13709f8385..fef94eb2b4 100644 --- a/irohad/consensus/yac/transport/impl/network_impl.cpp +++ b/irohad/consensus/yac/transport/impl/network_impl.cpp @@ -24,15 +24,16 @@ #include "consensus/yac/transport/yac_pb_converters.hpp" #include "interfaces/common_objects/peer.hpp" #include "logger/logger.hpp" +#include "network/impl/grpc_channel_builder.hpp" namespace iroha { namespace consensus { namespace yac { // ----------| Public API |---------- - NetworkImpl::NetworkImpl() { - log_ = logger::log("YacNetwork"); - } + NetworkImpl::NetworkImpl() + : network::AsyncGrpcClient( + logger::log("YacNetwork")) {} void NetworkImpl::subscribe( std::shared_ptr handler) { @@ -154,8 +155,8 @@ namespace iroha { void NetworkImpl::createPeerConnection( const shared_model::interface::Peer &peer) { if (peers_.count(peer.address()) == 0) { - peers_[peer.address()] = proto::Yac::NewStub(grpc::CreateChannel( - peer.address(), grpc::InsecureChannelCredentials())); + peers_[peer.address()] = + network::createClient(peer.address()); } } diff --git a/irohad/consensus/yac/transport/impl/network_impl.hpp b/irohad/consensus/yac/transport/impl/network_impl.hpp index d75c833a60..b2d3ab46c5 100644 --- a/irohad/consensus/yac/transport/impl/network_impl.hpp +++ b/irohad/consensus/yac/transport/impl/network_impl.hpp @@ -101,11 +101,6 @@ namespace iroha { * Subscriber of network messages */ std::weak_ptr handler_; - - /** - * Internal logger - */ - logger::Logger log_; }; } // namespace yac diff --git a/irohad/consensus/yac/transport/yac_pb_converters.hpp b/irohad/consensus/yac/transport/yac_pb_converters.hpp index ac963f0b04..1ed047b955 100644 --- a/irohad/consensus/yac/transport/yac_pb_converters.hpp +++ b/irohad/consensus/yac/transport/yac_pb_converters.hpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +23,7 @@ #include "consensus/yac/messages.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" #include "interfaces/common_objects/signature.hpp" +#include "logger/logger.hpp" #include "yac.pb.h" namespace iroha { @@ -30,7 +31,7 @@ namespace iroha { namespace yac { class PbConverters { public: - static proto::Vote serializeVote(const VoteMessage &vote) { + static proto::Vote serializeVotePayload(const VoteMessage &vote) { proto::Vote pb_vote; auto hash = pb_vote.mutable_hash(); @@ -39,24 +40,23 @@ namespace iroha { auto block_signature = hash->mutable_block_signature(); - // Will fix it in the next PR, very soon, don't worry - if (vote.hash.block_signature == nullptr) { - auto peer_key = shared_model::crypto::DefaultCryptoAlgorithmType:: - generateKeypair() - .publicKey(); - shared_model::builder::DefaultSignatureBuilder() - .publicKey(peer_key) - .signedData(shared_model::crypto::Signed("")) - .build() - .match( - [&](iroha::expected::Value> &sig) { - const_cast(vote).hash.block_signature = - sig.value; - }, - [](iroha::expected::Error>) { - }); - } + block_signature->set_signature(shared_model::crypto::toBinaryString( + vote.hash.block_signature->signedData())); + + block_signature->set_pubkey(shared_model::crypto::toBinaryString( + vote.hash.block_signature->publicKey())); + + return pb_vote; + } + + static proto::Vote serializeVote(const VoteMessage &vote) { + proto::Vote pb_vote; + + auto hash = pb_vote.mutable_hash(); + hash->set_block(vote.hash.block_hash); + hash->set_proposal(vote.hash.proposal_hash); + + auto block_signature = hash->mutable_block_signature(); block_signature->set_signature(shared_model::crypto::toBinaryString( vote.hash.block_signature->signedData())); @@ -65,8 +65,11 @@ namespace iroha { vote.hash.block_signature->publicKey())); auto signature = pb_vote.mutable_signature(); - signature->set_signature(vote.signature.signature.to_string()); - signature->set_pubkey(vote.signature.pubkey.to_string()); + const auto &sig = *vote.signature; + signature->set_signature( + shared_model::crypto::toBinaryString(sig.signedData())); + signature->set_pubkey( + shared_model::crypto::toBinaryString(sig.publicKey())); return pb_vote; } @@ -84,15 +87,35 @@ namespace iroha { pb_vote.hash().block_signature().signature())) .build() .match( - [&](iroha::expected::Value< - std::shared_ptr> - &sig) { vote.hash.block_signature = sig.value; }, - [](iroha::expected::Error>) {}); - - vote.signature.signature = *stringToBlob( - pb_vote.signature().signature()); - vote.signature.pubkey = *stringToBlob( - pb_vote.signature().pubkey()); + [&vote](iroha::expected::Value< + std::shared_ptr> + &sig) { vote.hash.block_signature = sig.value; }, + [](iroha::expected::Error> + &reason) { + logger::log("YacPbConverter::deserializeVote") + ->error("Cannot build vote hash block signature: {}", + *reason.error); + }); + + const auto &pubkey = + shared_model::crypto::PublicKey(pb_vote.signature().pubkey()); + const auto &signed_data = + shared_model::crypto::Signed(pb_vote.signature().signature()); + + shared_model::builder::DefaultSignatureBuilder() + .publicKey(pubkey) + .signedData(signed_data) + .build() + .match( + [&vote](iroha::expected::Value< + std::shared_ptr> + &sig) { vote.signature = sig.value; }, + [](iroha::expected::Error> + &reason) { + logger::log("YacPbConverter::deserializeVote") + ->error("Cannot build vote signature: {}", + *reason.error); + }); return vote; } diff --git a/irohad/consensus/yac/yac.hpp b/irohad/consensus/yac/yac.hpp index bdc499c06e..b3d3e92cb6 100644 --- a/irohad/consensus/yac/yac.hpp +++ b/irohad/consensus/yac/yac.hpp @@ -48,15 +48,13 @@ namespace iroha { std::shared_ptr network, std::shared_ptr crypto, std::shared_ptr timer, - ClusterOrdering order, - uint64_t delay); + ClusterOrdering order); Yac(YacVoteStorage vote_storage, std::shared_ptr network, std::shared_ptr crypto, std::shared_ptr timer, - ClusterOrdering order, - uint64_t delay); + ClusterOrdering order); // ------|Hash gate|------ @@ -133,9 +131,6 @@ namespace iroha { // ------|One round|------ ClusterOrdering cluster_order_; - // ------|Constants|------ - const uint64_t delay_; - // ------|Logger|------ logger::Logger log_; }; diff --git a/irohad/execution/CMakeLists.txt b/irohad/execution/CMakeLists.txt index 8ba23e30ec..c875c2b3d4 100644 --- a/irohad/execution/CMakeLists.txt +++ b/irohad/execution/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(common_execution ) target_link_libraries(common_execution rxcpp + boost ) add_library(command_execution @@ -28,4 +29,15 @@ target_link_libraries(command_execution common_execution rxcpp shared_model_default_builders + shared_model_amount_utils + ) + +add_library(query_execution + impl/query_execution.cpp + ) + +target_link_libraries(query_execution + rxcpp + shared_model_default_builders + common_execution ) diff --git a/irohad/execution/command_executor.hpp b/irohad/execution/command_executor.hpp index 7e7731967c..c8fbdb69be 100644 --- a/irohad/execution/command_executor.hpp +++ b/irohad/execution/command_executor.hpp @@ -141,7 +141,7 @@ namespace iroha { std::shared_ptr commands; shared_model::interface::types::AccountIdType creator_account_id; - shared_model::builder::DefaultAmountBuilder amount_builder_; + shared_model::builder::AmountBuilderWithoutValidator amount_builder_; shared_model::builder::DefaultAccountAssetBuilder account_asset_builder_; shared_model::builder::DefaultAccountBuilder account_builder_; shared_model::builder::DefaultAssetBuilder asset_builder_; diff --git a/irohad/execution/impl/command_executor.cpp b/irohad/execution/impl/command_executor.cpp index a6bd3eda6e..90475c8f04 100644 --- a/irohad/execution/impl/command_executor.cpp +++ b/irohad/execution/impl/command_executor.cpp @@ -21,8 +21,10 @@ #include "execution/common_executor.hpp" #include "interfaces/commands/command.hpp" -#include "validators/permissions.hpp" #include "utils/amount_utils.hpp" +#include "validators/permissions.hpp" + +using namespace shared_model::detail; namespace iroha { @@ -62,16 +64,18 @@ namespace iroha { (boost::format("asset %s is absent") % command->assetId()).str(), command_name); } - auto precision = asset.value()->precision(); - if (command->amount().precision() != precision) { + auto precision = asset.value()->precision(); + if (command->amount().precision() > precision) { return makeExecutionError( - (boost::format("precision mismatch: expected %d, but got %d") + (boost::format("command precision is greater than asset precision: " + "expected %d, but got %d") % precision % command->amount().precision()) .str(), command_name); } - + auto command_amount = + makeAmountWithPrecision(command->amount(), asset.value()->precision()); if (not queries->getAccount(command->accountId())) { return makeExecutionError( (boost::format("account %s is absent") % command->accountId()).str(), @@ -80,9 +84,11 @@ namespace iroha { auto account_asset = queries->getAccountAsset(command->accountId(), command->assetId()); - auto new_balance = amount_builder_.precision(command->amount().precision()) - .intValue(command->amount().intValue()) - .build(); + auto new_balance = command_amount | [this](const auto &amount) { + return amount_builder_.precision(amount->precision()) + .intValue(amount->intValue()) + .build(); + }; using AccountAssetResult = expected::Result, iroha::ExecutionError>; @@ -354,30 +360,34 @@ namespace iroha { command_name); } auto precision = asset.value()->precision(); - - if (command->amount().precision() != precision) { + if (command->amount().precision() > precision) { return makeExecutionError( - (boost::format("precision mismatch: expected %d, but got %d") + (boost::format("command precision is greater than asset precision: " + "expected %d, but got %d") % precision % command->amount().precision()) .str(), command_name); } - auto account_asset = queries->getAccountAsset( - command->accountId(), command->assetId()); + auto command_amount = + makeAmountWithPrecision(command->amount(), asset.value()->precision()); + auto account_asset = + queries->getAccountAsset(command->accountId(), command->assetId()); if (not account_asset) { return makeExecutionError((boost::format("%s do not have %s") % command->accountId() % command->assetId()) .str(), command_name); } - auto account_asset_new = - (account_asset.value()->balance() - command->amount()) | - [this, &account_asset](const auto &new_balance) { - return account_asset_builder_.balance(*new_balance) - .accountId(account_asset.value()->accountId()) - .assetId(account_asset.value()->assetId()) - .build(); - }; + auto account_asset_new = command_amount | + [&account_asset](const auto &amount) { + return account_asset.value()->balance() - *amount; + } + | [this, &account_asset](const auto &new_balance) { + return account_asset_builder_.balance(*new_balance) + .accountId(account_asset.value()->accountId()) + .assetId(account_asset.value()->assetId()) + .build(); + }; return account_asset_new.match( [&](const expected::Value< @@ -417,23 +427,29 @@ namespace iroha { .str(), command_name); } - // Precision for both wallets auto precision = asset.value()->precision(); - if (command->amount().precision() != precision) { + if (command->amount().precision() > precision) { return makeExecutionError( - (boost::format("precision %d is wrong") % precision).str(), + (boost::format("command precision is greater than asset precision: " + "expected %d, but got %d") + % precision % command->amount().precision()) + .str(), command_name); } + auto command_amount = + makeAmountWithPrecision(command->amount(), asset.value()->precision()); // Set new balance for source account - auto src_account_asset_new = - (src_account_asset.value()->balance() - command->amount()) | - [this, &src_account_asset](const auto &new_src_balance) { - return account_asset_builder_ - .assetId(src_account_asset.value()->assetId()) - .accountId(src_account_asset.value()->accountId()) - .balance(*new_src_balance) - .build(); - }; + auto src_account_asset_new = command_amount | + [&src_account_asset](const auto &amount) { + return src_account_asset.value()->balance() - *amount; + } + | [this, &src_account_asset](const auto &new_src_balance) { + return account_asset_builder_ + .assetId(src_account_asset.value()->assetId()) + .accountId(src_account_asset.value()->accountId()) + .balance(*new_src_balance) + .build(); + }; return src_account_asset_new.match( [&](const expected::Value< std::shared_ptr> @@ -506,7 +522,9 @@ namespace iroha { // any asset return creator_account_id == command.accountId() and checkAccountRolePermission( - creator_account_id, queries, model::can_add_asset_qty); + creator_account_id, + queries, + shared_model::permissions::can_add_asset_qty); } bool CommandValidator::hasPermissions( @@ -514,7 +532,7 @@ namespace iroha { ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { return checkAccountRolePermission( - creator_account_id, queries, model::can_add_peer); + creator_account_id, queries, shared_model::permissions::can_add_peer); } bool CommandValidator::hasPermissions( @@ -526,11 +544,15 @@ namespace iroha { // account and he has permission CanAddSignatory (command.accountId() == creator_account_id and checkAccountRolePermission( - creator_account_id, queries, model::can_add_signatory)) + creator_account_id, + queries, + shared_model::permissions::can_add_signatory)) or // Case 2. Creator has granted permission for it (queries.hasAccountGrantablePermission( - creator_account_id, command.accountId(), model::can_add_signatory)); + creator_account_id, + command.accountId(), + shared_model::permissions::can_add_my_signatory)); } bool CommandValidator::hasPermissions( @@ -538,7 +560,9 @@ namespace iroha { ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { return checkAccountRolePermission( - creator_account_id, queries, model::can_append_role); + creator_account_id, + queries, + shared_model::permissions::can_append_role); } bool CommandValidator::hasPermissions( @@ -546,7 +570,9 @@ namespace iroha { ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { return checkAccountRolePermission( - creator_account_id, queries, model::can_create_account); + creator_account_id, + queries, + shared_model::permissions::can_create_account); } bool CommandValidator::hasPermissions( @@ -554,7 +580,9 @@ namespace iroha { ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { return checkAccountRolePermission( - creator_account_id, queries, model::can_create_asset); + creator_account_id, + queries, + shared_model::permissions::can_create_asset); } bool CommandValidator::hasPermissions( @@ -562,7 +590,9 @@ namespace iroha { ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { return checkAccountRolePermission( - creator_account_id, queries, model::can_create_domain); + creator_account_id, + queries, + shared_model::permissions::can_create_domain); } bool CommandValidator::hasPermissions( @@ -570,7 +600,9 @@ namespace iroha { ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { return checkAccountRolePermission( - creator_account_id, queries, model::can_create_role); + creator_account_id, + queries, + shared_model::permissions::can_create_role); } bool CommandValidator::hasPermissions( @@ -578,7 +610,9 @@ namespace iroha { ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { return checkAccountRolePermission( - creator_account_id, queries, model::can_detach_role); + creator_account_id, + queries, + shared_model::permissions::can_detach_role); } bool CommandValidator::hasPermissions( @@ -588,7 +622,7 @@ namespace iroha { return checkAccountRolePermission( creator_account_id, queries, - model::can_grant + command.permissionName()); + shared_model::permissions::can_grant + command.permissionName()); } bool CommandValidator::hasPermissions( @@ -600,11 +634,14 @@ namespace iroha { // permission on it (creator_account_id == command.accountId() and checkAccountRolePermission( - creator_account_id, queries, model::can_remove_signatory)) + creator_account_id, + queries, + shared_model::permissions::can_remove_signatory)) // 2. Creator has granted permission on removal - or (queries.hasAccountGrantablePermission(creator_account_id, - command.accountId(), - model::can_remove_signatory)); + or (queries.hasAccountGrantablePermission( + creator_account_id, + command.accountId(), + shared_model::permissions::can_remove_my_signatory)); } bool CommandValidator::hasPermissions( @@ -624,7 +661,9 @@ namespace iroha { creator_account_id == command.accountId() or // Case 2. Creator has grantable permission to set account key/value queries.hasAccountGrantablePermission( - creator_account_id, command.accountId(), model::can_set_detail); + creator_account_id, + command.accountId(), + shared_model::permissions::can_set_my_account_detail); } bool CommandValidator::hasPermissions( @@ -635,10 +674,14 @@ namespace iroha { // 1. Creator set quorum for his account -> must have permission (creator_account_id == command.accountId() and checkAccountRolePermission( - creator_account_id, queries, model::can_set_quorum)) + creator_account_id, + queries, + shared_model::permissions::can_set_quorum)) // 2. Creator has granted permission on it or (queries.hasAccountGrantablePermission( - creator_account_id, command.accountId(), model::can_set_quorum)); + creator_account_id, + command.accountId(), + shared_model::permissions::can_set_my_quorum)); } bool CommandValidator::hasPermissions( @@ -647,7 +690,9 @@ namespace iroha { const shared_model::interface::types::AccountIdType &creator_account_id) { return creator_account_id == command.accountId() and checkAccountRolePermission( - creator_account_id, queries, model::can_subtract_asset_qty); + creator_account_id, + queries, + shared_model::permissions::can_subtract_asset_qty); } bool CommandValidator::hasPermissions( @@ -660,15 +705,18 @@ namespace iroha { and queries.hasAccountGrantablePermission( creator_account_id, command.srcAccountId(), - model::can_transfer)) + shared_model::permissions::can_transfer_my_assets)) or // 2. Creator transfer from their account (creator_account_id == command.srcAccountId() and checkAccountRolePermission( - creator_account_id, queries, model::can_transfer))) + creator_account_id, + queries, + shared_model::permissions::can_transfer))) // For both cases, dest_account must have can_receive - and checkAccountRolePermission( - command.destAccountId(), queries, model::can_receive); + and checkAccountRolePermission(command.destAccountId(), + queries, + shared_model::permissions::can_receive); } bool CommandValidator::isValid( @@ -745,12 +793,12 @@ namespace iroha { const shared_model::interface::CreateRole &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return std::all_of( - command.rolePermissions().begin(), - command.rolePermissions().end(), - [&queries, &creator_account_id](auto perm) { - return checkAccountRolePermission(creator_account_id, queries, perm); - }); + return std::all_of(command.rolePermissions().begin(), + command.rolePermissions().end(), + [&queries, &creator_account_id](auto perm) { + return checkAccountRolePermission( + creator_account_id, queries, perm); + }); } bool CommandValidator::isValid( @@ -772,8 +820,7 @@ namespace iroha { ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { auto account = queries.getAccount(command.accountId()); - auto signatories = - queries.getSignatories(command.accountId()); + auto signatories = queries.getSignatories(command.accountId()); if (not(account and signatories)) { // No account or signatories found @@ -803,8 +850,7 @@ namespace iroha { const shared_model::interface::SetQuorum &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - auto signatories = - queries.getSignatories(command.accountId()); + auto signatories = queries.getSignatories(command.accountId()); if (not(signatories)) { // No signatories of an account found @@ -831,7 +877,7 @@ namespace iroha { return false; } // Amount is formed wrong - if (command.amount().precision() != asset.value()->precision()) { + if (command.amount().precision() > asset.value()->precision()) { return false; } auto account_asset = diff --git a/irohad/model/impl/query_execution.cpp b/irohad/execution/impl/query_execution.cpp similarity index 94% rename from irohad/model/impl/query_execution.cpp rename to irohad/execution/impl/query_execution.cpp index 6feec625d0..40d5866f88 100644 --- a/irohad/model/impl/query_execution.cpp +++ b/irohad/execution/impl/query_execution.cpp @@ -15,14 +15,15 @@ * limitations under the License. */ -#include "model/query_execution.hpp" +#include "execution/query_execution.hpp" #include #include "execution/common_executor.hpp" #include "validators/permissions.hpp" -using namespace iroha::model; +using namespace shared_model::permissions; +using namespace iroha; using namespace iroha::ametsuchi; // TODO: 28/03/2018 x3medima17 remove poly wrapper, IR-1011 @@ -270,7 +271,7 @@ QueryProcessingFactory::executeGetAccountAssets( } QueryProcessingFactory::QueryResponseBuilderDone -iroha::model::QueryProcessingFactory::executeGetAccountDetail( +QueryProcessingFactory::executeGetAccountDetail( const shared_model::interface::GetAccountDetail &query) { auto acct_detail = _wsvQuery->getAccountDetail(query.accountId()); if (not acct_detail) { @@ -281,7 +282,7 @@ iroha::model::QueryProcessingFactory::executeGetAccountDetail( } QueryProcessingFactory::QueryResponseBuilderDone -iroha::model::QueryProcessingFactory::executeGetAccountAssetTransactions( +QueryProcessingFactory::executeGetAccountAssetTransactions( const shared_model::interface::GetAccountAssetTransactions &query) { auto acc_asset_tx = _blockQuery->getAccountAssetTransactions( query.accountId(), query.assetId()); @@ -312,18 +313,22 @@ QueryProcessingFactory::executeGetAccountTransactions( } QueryProcessingFactory::QueryResponseBuilderDone -iroha::model::QueryProcessingFactory::executeGetTransactions( - const shared_model::interface::GetTransactions &query) { - const std::vector &hashes = - query.transactionHashes(); +QueryProcessingFactory::executeGetTransactions( + const shared_model::interface::GetTransactions &q, + const shared_model::interface::types::AccountIdType &accountId) { + const std::vector &hashes = q.transactionHashes(); auto transactions = _blockQuery->getTransactions(hashes); std::vector txs; + bool can_get_all = + checkAccountRolePermission(accountId, *_wsvQuery, can_get_all_txs); transactions.subscribe([&](const auto &tx) { if (tx) { - txs.push_back( - *std::static_pointer_cast(*tx)); + auto proto_tx = + *std::static_pointer_cast(*tx); + if (can_get_all or proto_tx.creatorAccountId() == accountId) + txs.push_back(proto_tx); } }); @@ -343,7 +348,8 @@ QueryProcessingFactory::executeGetSignatories( } std::shared_ptr -QueryProcessingFactory::execute(const shared_model::interface::Query &query) { +QueryProcessingFactory::validateAndExecute( + const shared_model::interface::Query &query) { const auto &query_hash = query.hash(); QueryResponseBuilderDone builder; // TODO: 29/04/2018 x3medima18, Add visitor class, IR-1185 @@ -377,7 +383,7 @@ QueryProcessingFactory::execute(const shared_model::interface::Query &query) { if (not validate(query, *q)) { builder = statefulFailed(); } else { - builder = executeGetTransactions(*q); + builder = executeGetTransactions(*q, query.creatorAccountId()); } return clone(builder.queryHash(query_hash).build()); }, diff --git a/irohad/execution/query_execution.hpp b/irohad/execution/query_execution.hpp new file mode 100644 index 0000000000..e8bf548e23 --- /dev/null +++ b/irohad/execution/query_execution.hpp @@ -0,0 +1,135 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * 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 + * + * http://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. + */ +#ifndef IROHA_QUERY_EXECUTION_HPP +#define IROHA_QUERY_EXECUTION_HPP + +#include "ametsuchi/block_query.hpp" +#include "ametsuchi/wsv_query.hpp" +#include "builders/protobuf/builder_templates/query_response_template.hpp" + +namespace shared_model { + namespace interface { + class QueryResponse; + class Query; + } // namespace interface +} // namespace shared_model + +namespace iroha { + + /** + * Converting model objects to protobuf and vice versa + */ + class QueryProcessingFactory { + using QueryResponseBuilder = + shared_model::proto::TemplateQueryResponseBuilder<0>; + + using QueryResponseBuilderDone = + shared_model::proto::TemplateQueryResponseBuilder<1>; + + public: + /** + * Execute and validate query. + * + * @param query + * @return shared pointer to query response + */ + std::shared_ptr validateAndExecute( + const shared_model::interface::Query &query); + /** + * + * @param wsvQuery + * @param blockQuery + */ + QueryProcessingFactory(std::shared_ptr wsvQuery, + std::shared_ptr blockQuery); + + private: + bool validate(const shared_model::interface::Query &query, + const shared_model::interface::GetAssetInfo &get_asset_info); + + bool validate(const shared_model::interface::Query &query, + const shared_model::interface::GetRoles &get_roles); + + bool validate(const shared_model::interface::Query &query, + const shared_model::interface::GetRolePermissions + &get_role_permissions); + + bool validate( + const shared_model::interface::Query &query, + const shared_model::interface::GetAccountAssets &get_account_assets); + + bool validate(const shared_model::interface::Query &query, + const shared_model::interface::GetAccount &get_account); + + bool validate( + const shared_model::interface::Query &query, + const shared_model::interface::GetSignatories &get_signatories); + + bool validate(const shared_model::interface::Query &query, + const shared_model::interface::GetAccountTransactions + &get_account_transactions); + + bool validate(const shared_model::interface::Query &query, + const shared_model::interface::GetAccountAssetTransactions + &get_account_asset_transactions); + + bool validate( + const shared_model::interface::Query &query, + const shared_model::interface::GetAccountDetail &get_account_detail); + + bool validate( + const shared_model::interface::Query &query, + const shared_model::interface::GetTransactions &get_transactions); + + QueryResponseBuilderDone executeGetAssetInfo( + const shared_model::interface::GetAssetInfo &get_asset_info); + + QueryResponseBuilderDone executeGetRoles( + const shared_model::interface::GetRoles &query); + + QueryResponseBuilderDone executeGetRolePermissions( + const shared_model::interface::GetRolePermissions &query); + + QueryResponseBuilderDone executeGetAccountAssets( + const shared_model::interface::GetAccountAssets &query); + + QueryResponseBuilderDone executeGetAccountDetail( + const shared_model::interface::GetAccountDetail &query); + + QueryResponseBuilderDone executeGetAccount( + const shared_model::interface::GetAccount &query); + + QueryResponseBuilderDone executeGetSignatories( + const shared_model::interface::GetSignatories &query); + + QueryResponseBuilderDone executeGetAccountAssetTransactions( + const shared_model::interface::GetAccountAssetTransactions &query); + + QueryResponseBuilderDone executeGetAccountTransactions( + const shared_model::interface::GetAccountTransactions &query); + + QueryResponseBuilderDone executeGetTransactions( + const shared_model::interface::GetTransactions &query, + const shared_model::interface::types::AccountIdType &accountId); + + std::shared_ptr _wsvQuery; + std::shared_ptr _blockQuery; + }; + +} // namespace iroha + +#endif // IROHA_QUERY_EXECUTION_HPP diff --git a/irohad/main/CMakeLists.txt b/irohad/main/CMakeLists.txt index 39d1427740..cd351ee33e 100644 --- a/irohad/main/CMakeLists.txt +++ b/irohad/main/CMakeLists.txt @@ -24,9 +24,9 @@ target_link_libraries(server_runner boost # iroha::expected::Result ) -add_library(raw_block_insertion impl/raw_block_loader.cpp) -target_link_libraries(raw_block_insertion - json_model_converters +add_library(raw_block_loader impl/raw_block_loader.cpp) +target_link_libraries(raw_block_loader + shared_model_interfaces ) add_library(application @@ -39,7 +39,6 @@ target_link_libraries(application logger yac server_runner - model ametsuchi networking ordering_service @@ -57,7 +56,7 @@ target_link_libraries(application add_executable(irohad irohad.cpp) target_link_libraries(irohad application - raw_block_insertion + raw_block_loader gflags rapidjson keys_manager diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp index 3af241645c..b383b48233 100644 --- a/irohad/main/application.cpp +++ b/irohad/main/application.cpp @@ -42,7 +42,7 @@ Irohad::Irohad(const std::string &block_store_dir, std::chrono::milliseconds proposal_delay, std::chrono::milliseconds vote_delay, std::chrono::milliseconds load_delay, - const keypair_t &keypair) + const shared_model::crypto::Keypair &keypair) : block_store_dir_(block_store_dir), pg_conn_(pg_conn), torii_port_(torii_port), @@ -126,21 +126,16 @@ bool Irohad::restoreWsv() { /** * Initializing peer query interface */ -void Irohad::initPeerQuery() { - wsv = std::make_shared(storage->getWsvQuery()); - - log_->info("[Init] => peer query"); +std::unique_ptr Irohad::initPeerQuery() { + return std::make_unique(storage->getWsvQuery()); } /** * Initializing crypto provider */ void Irohad::initCryptoProvider() { - shared_model::crypto::Keypair keypair_( - shared_model::crypto::PublicKey(keypair.pubkey.to_string()), - shared_model::crypto::PrivateKey(keypair.privkey.to_string())); crypto_signer_ = - std::make_shared>(keypair_); + std::make_shared>(keypair); log_->info("[Init] => crypto provider"); } @@ -160,8 +155,11 @@ void Irohad::initValidators() { * Initializing ordering gate */ void Irohad::initOrderingGate() { - ordering_gate = ordering_init.initOrderingGate( - wsv, max_proposal_size_, proposal_delay_, ordering_service_storage_); + ordering_gate = ordering_init.initOrderingGate(initPeerQuery(), + max_proposal_size_, + proposal_delay_, + ordering_service_storage_, + storage->getBlockQuery()); log_->info("[Init] => init ordering gate - [{}]", logger::logBool(ordering_gate)); } @@ -183,8 +181,8 @@ void Irohad::initSimulator() { * Initializing block loader */ void Irohad::initBlockLoader() { - block_loader = loader_init.initBlockLoader( - wsv, storage->getBlockQuery()); + block_loader = + loader_init.initBlockLoader(initPeerQuery(), storage->getBlockQuery()); log_->info("[Init] => block loader"); } @@ -193,8 +191,12 @@ void Irohad::initBlockLoader() { * Initializing consensus gate */ void Irohad::initConsensusGate() { - consensus_gate = yac_init.initConsensusGate( - wsv, simulator, block_loader, keypair, vote_delay_, load_delay_); + consensus_gate = yac_init.initConsensusGate(initPeerQuery(), + simulator, + block_loader, + keypair, + vote_delay_, + load_delay_); log_->info("[Init] => consensus gate"); } diff --git a/irohad/main/application.hpp b/irohad/main/application.hpp index 0fa00f558f..77ca494c09 100644 --- a/irohad/main/application.hpp +++ b/irohad/main/application.hpp @@ -22,6 +22,7 @@ #include "ametsuchi/impl/storage_impl.hpp" #include "ametsuchi/ordering_service_persistent_state.hpp" #include "cryptography/crypto_provider/crypto_model_signer.hpp" +#include "cryptography/keypair.hpp" #include "logger/logger.hpp" #include "main/impl/block_loader_init.hpp" #include "main/impl/consensus_init.hpp" @@ -76,7 +77,7 @@ class Irohad { std::chrono::milliseconds proposal_delay, std::chrono::milliseconds vote_delay, std::chrono::milliseconds load_delay, - const iroha::keypair_t &keypair); + const shared_model::crypto::Keypair &keypair); /** * Initialization of whole objects in system @@ -111,7 +112,7 @@ class Irohad { virtual void initStorage(); - virtual void initPeerQuery(); + virtual std::unique_ptr initPeerQuery(); virtual void initCryptoProvider(); @@ -157,9 +158,6 @@ class Irohad { std::shared_ptr stateful_validator; std::shared_ptr chain_validator; - // peer query - std::shared_ptr wsv; - // WSV restorer std::shared_ptr wsv_restorer_; @@ -204,7 +202,7 @@ class Irohad { public: std::shared_ptr storage; - iroha::keypair_t keypair; + shared_model::crypto::Keypair keypair; grpc::ServerBuilder builder; }; diff --git a/irohad/main/impl/consensus_init.cpp b/irohad/main/impl/consensus_init.cpp index d219c66a09..40984c9f10 100644 --- a/irohad/main/impl/consensus_init.cpp +++ b/irohad/main/impl/consensus_init.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,14 +38,41 @@ namespace iroha { return consensus_network; } - auto YacInit::createCryptoProvider(const keypair_t &keypair) { + auto YacInit::createCryptoProvider( + const shared_model::crypto::Keypair &keypair) { auto crypto = std::make_shared(keypair); return crypto; } - auto YacInit::createTimer() { - return std::make_shared(); + auto YacInit::createTimer(std::chrono::milliseconds delay_milliseconds) { + return std::make_shared([delay_milliseconds] { + // static factory with a single thread + // + // observe_on_new_thread -- coordination which creates new thread with + // observe_on strategy -- all subsequent operations will be performed + // on this thread. + // + // scheduler owns a timeline that is exposed by the now() method. + // scheduler is also a factory for workers in that timeline. + // + // coordination is a factory for coordinators and has a scheduler. + // + // coordinator has a worker, and is a factory for coordinated + // observables, subscribers and schedulable functions. + // + // A new thread scheduler is created + // by calling .create_coordinator().get_scheduler() + // + // static allows to reuse the same thread in subsequent calls to this + // lambda + static rxcpp::observe_on_one_worker coordination( + rxcpp::observe_on_new_thread() + .create_coordinator() + .get_scheduler()); + return rxcpp::observable<>::timer( + std::chrono::milliseconds(delay_milliseconds), coordination); + }); } auto YacInit::createHashProvider() { @@ -54,21 +81,20 @@ namespace iroha { std::shared_ptr YacInit::createYac( ClusterOrdering initial_order, - const keypair_t &keypair, + const shared_model::crypto::Keypair &keypair, std::chrono::milliseconds delay_milliseconds) { return Yac::create(YacVoteStorage(), createNetwork(), createCryptoProvider(keypair), - createTimer(), - initial_order, - delay_milliseconds.count()); + createTimer(delay_milliseconds), + initial_order); } std::shared_ptr YacInit::initConsensusGate( std::shared_ptr wsv, std::shared_ptr block_creator, std::shared_ptr block_loader, - const keypair_t &keypair, + const shared_model::crypto::Keypair &keypair, std::chrono::milliseconds vote_delay_milliseconds, std::chrono::milliseconds load_delay_milliseconds) { auto peer_orderer = createPeerOrderer(wsv); diff --git a/irohad/main/impl/consensus_init.hpp b/irohad/main/impl/consensus_init.hpp index 97406e8009..dd2a82700e 100644 --- a/irohad/main/impl/consensus_init.hpp +++ b/irohad/main/impl/consensus_init.hpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ #include "consensus/yac/yac_gate.hpp" #include "consensus/yac/yac_hash_provider.hpp" #include "consensus/yac/yac_peer_orderer.hpp" +#include "cryptography/keypair.hpp" #include "network/block_loader.hpp" #include "simulator/block_creator.hpp" @@ -45,15 +46,15 @@ namespace iroha { auto createNetwork(); - auto createCryptoProvider(const keypair_t &keypair); + auto createCryptoProvider(const shared_model::crypto::Keypair &keypair); - auto createTimer(); + auto createTimer(std::chrono::milliseconds delay_milliseconds); auto createHashProvider(); std::shared_ptr createYac( ClusterOrdering initial_order, - const keypair_t &keypair, + const shared_model::crypto::Keypair &keypair, std::chrono::milliseconds delay_milliseconds); public: @@ -61,7 +62,7 @@ namespace iroha { std::shared_ptr wsv, std::shared_ptr block_creator, std::shared_ptr block_loader, - const keypair_t &keypair, + const shared_model::crypto::Keypair &keypair, std::chrono::milliseconds vote_delay_milliseconds, std::chrono::milliseconds load_delay_milliseconds); diff --git a/irohad/main/impl/ordering_init.cpp b/irohad/main/impl/ordering_init.cpp index a6b8137ffc..8026947707 100644 --- a/irohad/main/impl/ordering_init.cpp +++ b/irohad/main/impl/ordering_init.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "main/impl/ordering_init.hpp" @@ -22,8 +10,12 @@ namespace iroha { namespace network { auto OrderingInit::createGate( - std::shared_ptr transport) { - auto gate = std::make_shared(transport); + std::shared_ptr transport, + std::shared_ptr block_query) { + auto height = block_query->getTopBlocks(1).as_blocking().last()->height(); + auto gate = + std::make_shared(transport, height); + log_->info("Creating Ordering Gate with initial height {}", height); transport->subscribe(gate); return gate; } @@ -38,7 +30,8 @@ namespace iroha { return std::make_shared( wsv, max_size, - delay_milliseconds.count(), + rxcpp::observable<>::interval(delay_milliseconds, + rxcpp::observe_on_new_thread()), transport, persistent_state); } @@ -48,13 +41,15 @@ namespace iroha { size_t max_size, std::chrono::milliseconds delay_milliseconds, std::shared_ptr - persistent_state) { + persistent_state, + std::shared_ptr block_query) { auto ledger_peers = wsv->getLedgerPeers(); if (not ledger_peers or ledger_peers.value().empty()) { log_->error( "Ledger don't have peers. Do you set correct genesis block?"); } auto network_address = ledger_peers->front()->address(); + log_->info("Ordering gate is at {}", network_address); ordering_gate_transport = std::make_shared( network_address); @@ -67,7 +62,7 @@ namespace iroha { ordering_service_transport, persistent_state); ordering_service_transport->subscribe(ordering_service); - ordering_gate = createGate(ordering_gate_transport); + ordering_gate = createGate(ordering_gate_transport, block_query); return ordering_gate; } } // namespace network diff --git a/irohad/main/impl/ordering_init.hpp b/irohad/main/impl/ordering_init.hpp index bd8828be1d..8149866bbd 100644 --- a/irohad/main/impl/ordering_init.hpp +++ b/irohad/main/impl/ordering_init.hpp @@ -18,6 +18,7 @@ #ifndef IROHA_ORDERING_INIT_HPP #define IROHA_ORDERING_INIT_HPP +#include "ametsuchi/block_query.hpp" #include "ametsuchi/peer_query.hpp" #include "logger/logger.hpp" #include "ordering/impl/ordering_gate_impl.hpp" @@ -41,9 +42,12 @@ namespace iroha { /** * Init effective realisation of ordering gate (client of ordering * service) - * @param network_address - address of ordering service + * @param transport - object which will be notified + * about incoming proposals and send transactions + * @param block_query - block store to get last block height */ - auto createGate(std::shared_ptr); + auto createGate(std::shared_ptr transport, + std::shared_ptr block_query); /** * Init ordering service @@ -67,14 +71,16 @@ namespace iroha { * @param loop - handler of async events * @param max_size - limitation of proposal size * @param delay_milliseconds - delay before emitting proposal - * @return effective realisation of OrderingGate + * @param block_query - block store to get last block height + * @return efficient implementation of OrderingGate */ std::shared_ptr initOrderingGate( std::shared_ptr wsv, size_t max_size, std::chrono::milliseconds delay_milliseconds, std::shared_ptr - persistent_state); + persistent_state, + std::shared_ptr block_query); std::shared_ptr ordering_service; std::shared_ptr ordering_gate; diff --git a/irohad/main/impl/raw_block_loader.cpp b/irohad/main/impl/raw_block_loader.cpp index 38f1600e98..a4c601c243 100644 --- a/irohad/main/impl/raw_block_loader.cpp +++ b/irohad/main/impl/raw_block_loader.cpp @@ -16,26 +16,30 @@ */ #include "main/raw_block_loader.hpp" + #include -#include -#include "common/types.hpp" -#include "model/converters/json_common.hpp" + +#include "converters/protobuf/json_proto_converter.hpp" +#include "backend/protobuf/block.hpp" namespace iroha { namespace main { + using shared_model::converters::protobuf::jsonToProto; + using shared_model::interface::Block; + BlockLoader::BlockLoader() : log_(logger::log("BlockLoader")) {} - boost::optional BlockLoader::parseBlock(std::string data) { - auto document = model::converters::stringToJson(data); - if (not document) { - log_->error("Blob parsing failed"); - return boost::none; - } - return block_factory_.deserialize(document.value()); + boost::optional> BlockLoader::parseBlock( + const std::string &data) { + return jsonToProto(data) | [](auto &&block) { + return boost::optional>( + std::make_shared(std::move(block))); + }; } - boost::optional BlockLoader::loadFile(std::string path) { + boost::optional BlockLoader::loadFile( + const std::string &path) { std::ifstream file(path); if (not file) { log_->error("Cannot read '" + path + "'"); diff --git a/irohad/main/iroha_conf_loader.hpp b/irohad/main/iroha_conf_loader.hpp index 63982aa871..099137df3f 100644 --- a/irohad/main/iroha_conf_loader.hpp +++ b/irohad/main/iroha_conf_loader.hpp @@ -18,6 +18,7 @@ #ifndef IROHA_CONF_LOADER_HPP #define IROHA_CONF_LOADER_HPP +#include #include #include #include diff --git a/irohad/main/irohad.cpp b/irohad/main/irohad.cpp index 06f6936630..71fd35cb1f 100644 --- a/irohad/main/irohad.cpp +++ b/irohad/main/irohad.cpp @@ -20,7 +20,6 @@ #include #include #include -#include "backend/protobuf/from_old_model.hpp" #include "common/result.hpp" #include "crypto/keys_manager_impl.hpp" #include "main/application.hpp" @@ -100,11 +99,9 @@ int main(int argc, char *argv[]) { // Reading public and private key files iroha::KeysManagerImpl keysManager(FLAGS_keypair_name); - iroha::keypair_t keypair{}; + auto keypair = keysManager.loadKeys(); // Check if both keys are read properly - if (auto loadedKeypair = keysManager.loadKeys()) { - keypair = *loadedKeypair; - } else { + if (not keypair) { // Abort execution if not log->error("Failed to load keypair"); return EXIT_FAILURE; @@ -119,7 +116,7 @@ int main(int argc, char *argv[]) { std::chrono::milliseconds(config[mbr::ProposalDelay].GetUint()), std::chrono::milliseconds(config[mbr::VoteDelay].GetUint()), std::chrono::milliseconds(config[mbr::LoadDelay].GetUint()), - keypair); + *keypair); // Check if iroha daemon storage was successfully initialized if (not irohad.storage) { @@ -151,9 +148,20 @@ int main(int argc, char *argv[]) { log->info("Block is parsed"); // Applying transactions from genesis block to iroha storage - irohad.storage->insertBlock(shared_model::proto::from_old(block.value())); + irohad.storage->insertBlock(*block.value()); log->info("Genesis block inserted, number of transactions: {}", - block.value().transactions.size()); + block.value()->transactions().size()); + } + + // check if at least one block is available in the ledger + auto blocks_exist = false; + irohad.storage->getBlockQuery()->getTopBlocks(1).subscribe( + [&blocks_exist](auto block) { blocks_exist = true; }); + + if (not blocks_exist) { + log->error( + "There are no blocks in the ledger. Use --genesis_block parameter."); + return EXIT_FAILURE; } // init pipeline components diff --git a/irohad/main/raw_block_loader.hpp b/irohad/main/raw_block_loader.hpp index e6500c4b04..c6fc3b0c66 100644 --- a/irohad/main/raw_block_loader.hpp +++ b/irohad/main/raw_block_loader.hpp @@ -19,13 +19,17 @@ #define IROHA_RAW_BLOCK_INSERTION_HPP #include -#include #include -#include -#include "ametsuchi/storage.hpp" + +#include + #include "logger/logger.hpp" -#include "model/block.hpp" -#include "model/converters/json_block_factory.hpp" + +namespace shared_model { + namespace interface { + class Block; + } +} namespace iroha { namespace main { @@ -44,19 +48,17 @@ namespace iroha { * @param data - raw presenetation of block * @return object if operation done successfully, nullopt otherwise */ - boost::optional parseBlock(std::string data); + boost::optional> + parseBlock(const std::string &data); /** - * Additional method * Loading file from target path * @param path - target file - * @return string with content or nullopt + * @return string with file content or nullopt */ - boost::optional loadFile(std::string path); + boost::optional loadFile(const std::string &path); private: - model::converters::JsonBlockFactory block_factory_; - logger::Logger log_; }; diff --git a/irohad/main/server_runner.cpp b/irohad/main/server_runner.cpp index 0b40f3a8a7..c7b2a1db1e 100644 --- a/irohad/main/server_runner.cpp +++ b/irohad/main/server_runner.cpp @@ -44,6 +44,10 @@ iroha::expected::Result ServerRunner::run() { builder.RegisterService(service.get()); } + // in order to bypass built-it limitation of gRPC message size + builder.SetMaxReceiveMessageSize(INT_MAX); + builder.SetMaxSendMessageSize(INT_MAX); + serverInstance_ = builder.BuildAndStart(); serverInstanceCV_.notify_one(); @@ -61,3 +65,9 @@ void ServerRunner::waitForServersReady() { serverInstanceCV_.wait(lock); } } + +void ServerRunner::shutdown() { + if (serverInstance_) { + serverInstance_->Shutdown(); + } +} diff --git a/irohad/main/server_runner.hpp b/irohad/main/server_runner.hpp index 04de6ad1d5..d633d89f03 100644 --- a/irohad/main/server_runner.hpp +++ b/irohad/main/server_runner.hpp @@ -53,6 +53,11 @@ class ServerRunner { */ void waitForServersReady(); + /** + * Ask grpc server to terminate. + */ + void shutdown(); + private: std::unique_ptr serverInstance_; std::mutex waitForServer_; diff --git a/irohad/model/CMakeLists.txt b/irohad/model/CMakeLists.txt index a35be78ca2..77c02c7715 100644 --- a/irohad/model/CMakeLists.txt +++ b/irohad/model/CMakeLists.txt @@ -29,8 +29,8 @@ target_link_libraries(sha3_hash add_library(model model_crypto_provider_impl.cpp impl/model_operators.cpp - impl/query_execution.cpp ) + target_link_libraries(model hash sha3_hash diff --git a/irohad/model/converters/impl/json_transaction_factory.cpp b/irohad/model/converters/impl/json_transaction_factory.cpp index 75e1a285c1..7ab22929b9 100644 --- a/irohad/model/converters/impl/json_transaction_factory.cpp +++ b/irohad/model/converters/impl/json_transaction_factory.cpp @@ -43,7 +43,6 @@ namespace iroha { document.AddMember("created_ts", transaction.created_ts, allocator); document.AddMember( "creator_account_id", transaction.creator_account_id, allocator); - document.AddMember("tx_counter", transaction.tx_counter, allocator); Value commands; commands.SetArray(); @@ -82,7 +81,6 @@ namespace iroha { return boost::make_optional(Transaction()) | des.Uint64(&Transaction::created_ts, "created_ts") | des.String(&Transaction::creator_account_id, "creator_account_id") - | des.Uint64(&Transaction::tx_counter, "tx_counter") | des.Array(&Transaction::signatures, "signatures") | des.Array(&Transaction::commands, "commands", des_commands); } diff --git a/irohad/model/converters/impl/pb_command_factory.cpp b/irohad/model/converters/impl/pb_command_factory.cpp index 3a68bb484c..e8f7bcf363 100644 --- a/irohad/model/converters/impl/pb_command_factory.cpp +++ b/irohad/model/converters/impl/pb_command_factory.cpp @@ -21,6 +21,8 @@ #include "model/converters/pb_common.hpp" +using namespace shared_model::permissions; + namespace iroha { namespace model { namespace converters { @@ -85,19 +87,24 @@ namespace iroha { // Can get all account assets (protocol::RolePermission::can_get_all_acc_ast, can_get_all_acc_ast) // Can get domain account assets - (protocol::RolePermission::can_get_domain_acc_ast, can_get_domain_acc_ast) + (protocol::RolePermission::can_get_domain_acc_ast, + can_get_domain_acc_ast) // Can get my account detail - (protocol::RolePermission::can_get_my_acc_detail, can_get_my_acc_detail) + (protocol::RolePermission::can_get_my_acc_detail, + can_get_my_acc_detail) // Can get all account detail - (protocol::RolePermission::can_get_all_acc_detail, can_get_all_acc_detail) + (protocol::RolePermission::can_get_all_acc_detail, + can_get_all_acc_detail) // Can get domain account detail - (protocol::RolePermission::can_get_domain_acc_detail, can_get_domain_acc_detail) + (protocol::RolePermission::can_get_domain_acc_detail, + can_get_domain_acc_detail) // Can get my account transactions (protocol::RolePermission::can_get_my_acc_txs, can_get_my_acc_txs) // Can get all account transactions (protocol::RolePermission::can_get_all_acc_txs, can_get_all_acc_txs) // Can get domain account transactions - (protocol::RolePermission::can_get_domain_acc_txs, can_get_domain_acc_txs) + (protocol::RolePermission::can_get_domain_acc_txs, + can_get_domain_acc_txs) // Can get my account assets transactions (protocol::RolePermission::can_get_my_acc_ast_txs, can_get_my_acc_ast_txs) @@ -113,20 +120,20 @@ namespace iroha { (protocol::RolePermission::can_get_all_txs, can_get_all_txs) // Can grant set quorum - (protocol::RolePermission::can_grant_can_set_quorum, - can_grant + can_set_quorum) + (protocol::RolePermission::can_grant_can_set_my_quorum, + can_grant + can_set_my_quorum) // Can grant add signatory - (protocol::RolePermission::can_grant_can_add_signatory, - can_grant + can_add_signatory) + (protocol::RolePermission::can_grant_can_add_my_signatory, + can_grant + can_add_my_signatory) // Can grant remove signatory - (protocol::RolePermission::can_grant_can_remove_signatory, - can_grant + can_remove_signatory) + (protocol::RolePermission::can_grant_can_remove_my_signatory, + can_grant + can_remove_my_signatory) // Can grant can_transfer - (protocol::RolePermission::can_grant_can_transfer, - can_grant + can_transfer) + (protocol::RolePermission::can_grant_can_transfer_my_assets, + can_grant + can_transfer_my_assets) // Can write details to other accounts - (protocol::RolePermission::can_grant_can_set_detail, - can_grant + can_set_detail); + (protocol::RolePermission::can_grant_can_set_my_account_detail, + can_grant + can_set_my_account_detail); boost::assign::insert(pb_grant_map_) // Can add my signatory @@ -142,7 +149,7 @@ namespace iroha { can_set_detail) // Can transfer my assets (protocol::GrantablePermission::can_transfer_my_assets, - can_transfer); + can_transfer); } // asset quantity diff --git a/irohad/model/converters/impl/pb_query_response_factory.cpp b/irohad/model/converters/impl/pb_query_response_factory.cpp index 02ae58224c..80f288e432 100644 --- a/irohad/model/converters/impl/pb_query_response_factory.cpp +++ b/irohad/model/converters/impl/pb_query_response_factory.cpp @@ -84,7 +84,7 @@ namespace iroha { } if (response) { - response->set_query_hash(query_response->query_hash.to_string()); + response->set_query_hash(query_response->query_hash.to_hexstring()); } return response; } diff --git a/irohad/model/converters/impl/pb_transaction_factory.cpp b/irohad/model/converters/impl/pb_transaction_factory.cpp index c4fc9ec257..c7c34a3d15 100644 --- a/irohad/model/converters/impl/pb_transaction_factory.cpp +++ b/irohad/model/converters/impl/pb_transaction_factory.cpp @@ -34,7 +34,6 @@ namespace iroha { auto pl = pbtx.mutable_payload(); pl->set_created_time(tx.created_ts); pl->set_creator_account_id(tx.creator_account_id); - pl->set_tx_counter(tx.tx_counter); for (const auto &command : tx.commands) { auto cmd = pl->add_commands(); @@ -43,7 +42,7 @@ namespace iroha { } for (const auto &sig_obj : tx.signatures) { - auto proto_signature = pbtx.add_signature(); + auto proto_signature = pbtx.add_signatures(); proto_signature->set_pubkey(sig_obj.pubkey.to_string()); proto_signature->set_signature(sig_obj.signature.to_string()); } @@ -56,11 +55,10 @@ namespace iroha { model::Transaction tx; const auto &pl = pb_tx.payload(); - tx.tx_counter = pl.tx_counter(); tx.creator_account_id = pl.creator_account_id(); tx.created_ts = pl.created_time(); - for (const auto &pb_sig : pb_tx.signature()) { + for (const auto &pb_sig : pb_tx.signatures()) { model::Signature sig{}; sig.pubkey = pubkey_t::from_string(pb_sig.pubkey()); sig.signature = sig_t::from_string(pb_sig.signature()); diff --git a/irohad/model/generators/CMakeLists.txt b/irohad/model/generators/CMakeLists.txt index 90df2ed84f..7c05584618 100644 --- a/irohad/model/generators/CMakeLists.txt +++ b/irohad/model/generators/CMakeLists.txt @@ -16,7 +16,6 @@ # add_library(model_generators - impl/signature_generator.cpp impl/block_generator.cpp impl/transaction_generator.cpp impl/command_generator.cpp diff --git a/irohad/model/generators/impl/command_generator.cpp b/irohad/model/generators/impl/command_generator.cpp index 5b79890bef..6c1fcd4220 100644 --- a/irohad/model/generators/impl/command_generator.cpp +++ b/irohad/model/generators/impl/command_generator.cpp @@ -32,6 +32,7 @@ #include "validators/permissions.hpp" using namespace generator; +using namespace shared_model::permissions; namespace iroha { namespace model { diff --git a/irohad/model/generators/impl/transaction_generator.cpp b/irohad/model/generators/impl/transaction_generator.cpp index 0632dc2f98..fe44cde0e1 100644 --- a/irohad/model/generators/impl/transaction_generator.cpp +++ b/irohad/model/generators/impl/transaction_generator.cpp @@ -25,18 +25,28 @@ namespace iroha { namespace model { namespace generators { + + iroha::keypair_t *makeOldModel( + const shared_model::crypto::Keypair &keypair) { + return new iroha::keypair_t{ + shared_model::crypto::PublicKey::OldPublicKeyType::from_string( + toBinaryString(keypair.publicKey())), + shared_model::crypto::PrivateKey::OldPrivateKeyType::from_string( + toBinaryString(keypair.privateKey()))}; + } + Transaction TransactionGenerator::generateGenesisTransaction( ts64_t timestamp, std::vector peers_address) { Transaction tx; tx.created_ts = timestamp; tx.creator_account_id = ""; - tx.tx_counter = 0; CommandGenerator command_generator; // Add peers for (size_t i = 0; i < peers_address.size(); ++i) { KeysManagerImpl manager("node" + std::to_string(i)); manager.createKeys(); - auto keypair = *manager.loadKeys(); + auto keypair = *std::unique_ptr( + makeOldModel(*manager.loadKeys())); tx.commands.push_back(command_generator.generateAddPeer( Peer(peers_address[i], keypair.pubkey))); } @@ -57,12 +67,14 @@ namespace iroha { // Create accounts KeysManagerImpl manager("admin@test"); manager.createKeys(); - auto keypair = *manager.loadKeys(); + auto keypair = *std::unique_ptr( + makeOldModel(*manager.loadKeys())); tx.commands.push_back(command_generator.generateCreateAccount( "admin", "test", keypair.pubkey)); manager = KeysManagerImpl("test@test"); manager.createKeys(); - keypair = *manager.loadKeys(); + keypair = *std::unique_ptr( + makeOldModel(*manager.loadKeys())); tx.commands.push_back(command_generator.generateCreateAccount( "test", "test", keypair.pubkey)); @@ -76,22 +88,19 @@ namespace iroha { Transaction TransactionGenerator::generateTransaction( ts64_t timestamp, std::string creator_account_id, - uint64_t tx_counter, std::vector> commands) { Transaction tx; tx.created_ts = timestamp; tx.creator_account_id = creator_account_id; - tx.tx_counter = tx_counter; tx.commands = commands; return tx; } Transaction TransactionGenerator::generateTransaction( std::string creator_account_id, - uint64_t tx_counter, std::vector> commands) { return generateTransaction( - iroha::time::now(), creator_account_id, tx_counter, commands); + iroha::time::now(), creator_account_id, commands); } } // namespace generators diff --git a/irohad/model/generators/transaction_generator.hpp b/irohad/model/generators/transaction_generator.hpp index f72d335e76..fe72ed4361 100644 --- a/irohad/model/generators/transaction_generator.hpp +++ b/irohad/model/generators/transaction_generator.hpp @@ -41,27 +41,23 @@ namespace iroha { * Generate transaction from give meta data and commands list * @param timestamp * @param creator_account_id - * @param tx_counter * @param commands * @return */ Transaction generateTransaction( ts64_t timestamp, std::string creator_account_id, - uint64_t tx_counter, std::vector> commands); /** * Generate transaction from give meta data and commands list * @param timestamp * @param creator_account_id - * @param tx_counter * @param commands * @return */ Transaction generateTransaction( std::string creator_account_id, - uint64_t tx_counter, std::vector> commands); }; } // namespace generators diff --git a/irohad/model/impl/model_operators.cpp b/irohad/model/impl/model_operators.cpp index 1aaf25a310..89eb627738 100644 --- a/irohad/model/impl/model_operators.cpp +++ b/irohad/model/impl/model_operators.cpp @@ -201,7 +201,7 @@ namespace iroha { /* Signature */ bool Signature::operator==(const Signature &rhs) const { - return rhs.pubkey == pubkey && rhs.signature == signature; + return rhs.pubkey == pubkey; } /* Transaction */ @@ -211,7 +211,7 @@ namespace iroha { rhs.commands.begin(), rhs.commands.end(), [](const auto &i, const auto &j) { return *i == *j; }) - && rhs.tx_counter == tx_counter && rhs.signatures == signatures + && rhs.signatures == signatures && rhs.created_ts == created_ts; } diff --git a/irohad/model/query_execution.hpp b/irohad/model/query_execution.hpp index 7953d4be59..e69de29bb2 100644 --- a/irohad/model/query_execution.hpp +++ b/irohad/model/query_execution.hpp @@ -1,137 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. - */ -#ifndef IROHA_QUERY_EXECUTION_HPP -#define IROHA_QUERY_EXECUTION_HPP - -#include "ametsuchi/block_query.hpp" -#include "ametsuchi/wsv_query.hpp" -#include "builders/protobuf/builder_templates/query_response_template.hpp" - -namespace shared_model { - namespace interface { - class QueryResponse; - class Query; - } // namespace interface -} // namespace shared_model - -namespace iroha { - namespace model { - - /** - * Converting business objects to protobuf and vice versa - */ - class QueryProcessingFactory { - using QueryResponseBuilder = - shared_model::proto::TemplateQueryResponseBuilder<0>; - - using QueryResponseBuilderDone = - shared_model::proto::TemplateQueryResponseBuilder<1>; - - public: - /** - * Execute and validate query. - * - * @param query - * @return - */ - std::shared_ptr execute( - const shared_model::interface::Query &query); - /** - * - * @param wsvQuery - * @param blockQuery - */ - QueryProcessingFactory(std::shared_ptr wsvQuery, - std::shared_ptr blockQuery); - - private: - bool validate( - const shared_model::interface::Query &query, - const shared_model::interface::GetAssetInfo &get_asset_info); - - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetRoles &get_roles); - - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetRolePermissions - &get_role_permissions); - - bool validate( - const shared_model::interface::Query &query, - const shared_model::interface::GetAccountAssets &get_account_assets); - - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetAccount &get_account); - - bool validate( - const shared_model::interface::Query &query, - const shared_model::interface::GetSignatories &get_signatories); - - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetAccountTransactions - &get_account_transactions); - - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetAccountAssetTransactions - &get_account_asset_transactions); - - bool validate( - const shared_model::interface::Query &query, - const shared_model::interface::GetAccountDetail &get_account_detail); - - bool validate( - const shared_model::interface::Query &query, - const shared_model::interface::GetTransactions &get_transactions); - - QueryResponseBuilderDone executeGetAssetInfo( - const shared_model::interface::GetAssetInfo &get_asset_info); - - QueryResponseBuilderDone executeGetRoles( - const shared_model::interface::GetRoles &query); - - QueryResponseBuilderDone executeGetRolePermissions( - const shared_model::interface::GetRolePermissions &query); - - QueryResponseBuilderDone executeGetAccountAssets( - const shared_model::interface::GetAccountAssets &query); - - QueryResponseBuilderDone executeGetAccountDetail( - const shared_model::interface::GetAccountDetail &query); - - QueryResponseBuilderDone executeGetAccount( - const shared_model::interface::GetAccount &query); - - QueryResponseBuilderDone executeGetSignatories( - const shared_model::interface::GetSignatories &query); - - QueryResponseBuilderDone executeGetAccountAssetTransactions( - const shared_model::interface::GetAccountAssetTransactions &query); - - QueryResponseBuilderDone executeGetAccountTransactions( - const shared_model::interface::GetAccountTransactions &query); - - QueryResponseBuilderDone executeGetTransactions( - const shared_model::interface::GetTransactions &query); - - std::shared_ptr _wsvQuery; - std::shared_ptr _blockQuery; - }; - - } // namespace model -} // namespace iroha - -#endif // IROHA_QUERY_EXECUTION_HPP diff --git a/irohad/model/transaction.hpp b/irohad/model/transaction.hpp index 7b0df51ec3..16272c4765 100644 --- a/irohad/model/transaction.hpp +++ b/irohad/model/transaction.hpp @@ -54,17 +54,6 @@ namespace iroha { */ std::string creator_account_id{}; - /** - * Number for protecting against replay attack. - * Number that is stored inside of each account. - * Used to prevent replay attacks. - * During a stateful validation look at account and compare numbers - * if number inside a transaction is less than in account, - * this transaction is replayed. - * META field - */ - uint64_t tx_counter{}; - /** * Bunch of commands attached to transaction * shared_ptr is used since Proposal has to be copied diff --git a/irohad/network/CMakeLists.txt b/irohad/network/CMakeLists.txt index 9c57e2e9d5..0e0bfadd68 100644 --- a/irohad/network/CMakeLists.txt +++ b/irohad/network/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(networking target_link_libraries(networking rxcpp - model + shared_model_interfaces ordering_service synchronizer logger @@ -15,10 +15,11 @@ add_library(block_loader ) target_link_libraries(block_loader - pb_model_converters loader_grpc rxcpp - model + shared_model_interfaces + shared_model_proto_backend + logger ) add_library(block_loader_service diff --git a/irohad/network/impl/async_grpc_client.hpp b/irohad/network/impl/async_grpc_client.hpp index 9db412b9aa..b82a732a04 100644 --- a/irohad/network/impl/async_grpc_client.hpp +++ b/irohad/network/impl/async_grpc_client.hpp @@ -32,7 +32,9 @@ namespace iroha { template class AsyncGrpcClient { public: - AsyncGrpcClient() : thread_(&AsyncGrpcClient::asyncCompleteRpc, this) {} + explicit AsyncGrpcClient(logger::Logger &&log) + : thread_(&AsyncGrpcClient::asyncCompleteRpc, this), + log_(std::move(log)) {} /** * Listen to gRPC server responses @@ -42,7 +44,9 @@ namespace iroha { auto ok = false; while (cq_.Next(&got_tag, &ok)) { auto call = static_cast(got_tag); - + if (not call->status.ok()) { + log_->warn("RPC failed: {}", call->status.error_message()); + } delete call; } } @@ -56,6 +60,7 @@ namespace iroha { grpc::CompletionQueue cq_; std::thread thread_; + logger::Logger log_; /** * State and data information of gRPC call diff --git a/irohad/network/impl/block_loader_impl.cpp b/irohad/network/impl/block_loader_impl.cpp index 01670afb0c..90203580f4 100644 --- a/irohad/network/impl/block_loader_impl.cpp +++ b/irohad/network/impl/block_loader_impl.cpp @@ -17,13 +17,12 @@ #include "network/impl/block_loader_impl.hpp" -#include - #include #include "backend/protobuf/block.hpp" #include "builders/protobuf/transport_builder.hpp" #include "interfaces/common_objects/peer.hpp" +#include "network/impl/grpc_channel_builder.hpp" using namespace iroha::ametsuchi; using namespace iroha::network; @@ -43,7 +42,6 @@ BlockLoaderImpl::BlockLoaderImpl( const char *kPeerNotFound = "Cannot find peer"; const char *kTopBlockRetrieveFail = "Failed to retrieve top block"; -const char *kInvalidBlockSignatures = "Block signatures are invalid"; const char *kPeerRetrieveFail = "Failed to retrieve peers"; const char *kPeerFindFail = "Failed to find requested peer"; @@ -51,14 +49,11 @@ rxcpp::observable> BlockLoaderImpl::retrieveBlocks( const PublicKey &peer_pubkey) { return rxcpp::observable<>::create>( [this, peer_pubkey](auto subscriber) { - boost::optional top_block; + boost::optional> top_block; block_query_->getTopBlocks(1) .subscribe_on(rxcpp::observe_on_new_thread()) .as_blocking() - .subscribe([&top_block](auto block) { - top_block = - *std::unique_ptr(block->makeOldModel()); - }); + .subscribe([&top_block](auto block) { top_block = block; }); if (not top_block) { log_->error(kTopBlockRetrieveFail); subscriber.on_completed(); @@ -77,10 +72,10 @@ rxcpp::observable> BlockLoaderImpl::retrieveBlocks( protocol::Block block; // request next block to our top - request.set_height(top_block->height + 1); + request.set_height((*top_block)->height() + 1); auto reader = - this->getPeerStub(peer.value()).retrieveBlocks(&context, request); + this->getPeerStub(**peer).retrieveBlocks(&context, request); while (reader->Read(&block)) { shared_model::proto::TransportBuilder< shared_model::proto::Block, @@ -122,8 +117,7 @@ boost::optional> BlockLoaderImpl::retrieveBlock( // request block with specified hash request.set_hash(toBinaryString(block_hash)); - auto status = - getPeerStub(peer.value()).retrieveBlock(&context, request, &block); + auto status = getPeerStub(**peer).retrieveBlock(&context, request, &block); if (not status.ok()) { log_->warn(status.error_message()); return boost::none; @@ -140,8 +134,8 @@ boost::optional> BlockLoaderImpl::retrieveBlock( return boost::optional>(std::move(result)); } -boost::optional BlockLoaderImpl::findPeer( - const shared_model::crypto::PublicKey &pubkey) { +boost::optional> +BlockLoaderImpl::findPeer(const shared_model::crypto::PublicKey &pubkey) { auto peers = peer_query_->getLedgerPeers(); if (not peers) { log_->error(kPeerRetrieveFail); @@ -157,19 +151,17 @@ boost::optional BlockLoaderImpl::findPeer( log_->error(kPeerFindFail); return boost::none; } - - return *std::unique_ptr((*it)->makeOldModel()); + return *it; } proto::Loader::Stub &BlockLoaderImpl::getPeerStub( - const iroha::model::Peer &peer) { - auto it = peer_connections_.find(peer); + const shared_model::interface::Peer &peer) { + auto it = peer_connections_.find(peer.address()); if (it == peer_connections_.end()) { it = peer_connections_ .insert(std::make_pair( - peer, - proto::Loader::NewStub(grpc::CreateChannel( - peer.address, grpc::InsecureChannelCredentials())))) + peer.address(), + network::createClient(peer.address()))) .first; } return *it->second; diff --git a/irohad/network/impl/block_loader_impl.hpp b/irohad/network/impl/block_loader_impl.hpp index 2af25b7d3e..b78c75995f 100644 --- a/irohad/network/impl/block_loader_impl.hpp +++ b/irohad/network/impl/block_loader_impl.hpp @@ -36,7 +36,8 @@ namespace iroha { std::shared_ptr peer_query, std::shared_ptr block_query, std::shared_ptr = - std::make_shared()); + std::make_shared< + shared_model::validation::DefaultBlockValidator>()); rxcpp::observable> retrieveBlocks( @@ -54,16 +55,18 @@ namespace iroha { * @return peer, if it was found, otherwise nullopt * TODO 14/02/17 (@l4l) IR-960 rework method with returning result */ - boost::optional findPeer( + boost::optional> findPeer( const shared_model::crypto::PublicKey &pubkey); /** * Get or create a RPC stub for connecting to peer * @param peer for connecting * @return RPC stub */ - proto::Loader::Stub &getPeerStub(const model::Peer &peer); + proto::Loader::Stub &getPeerStub( + const shared_model::interface::Peer &peer); - std::unordered_map> + std::unordered_map> peer_connections_; std::shared_ptr peer_query_; std::shared_ptr block_query_; diff --git a/irohad/network/impl/block_loader_service.cpp b/irohad/network/impl/block_loader_service.cpp index bdd6d393bf..a0afac026d 100644 --- a/irohad/network/impl/block_loader_service.cpp +++ b/irohad/network/impl/block_loader_service.cpp @@ -16,13 +16,10 @@ */ #include "network/impl/block_loader_service.hpp" - -#include "common/byteutils.hpp" +#include "backend/protobuf/block.hpp" using namespace iroha; using namespace iroha::ametsuchi; -using namespace iroha::model; -using namespace iroha::model::converters; using namespace iroha::network; BlockLoaderService::BlockLoaderService(std::shared_ptr storage) @@ -35,9 +32,9 @@ grpc::Status BlockLoaderService::retrieveBlocks( const proto::BlocksRequest *request, ::grpc::ServerWriter<::iroha::protocol::Block> *writer) { storage_->getBlocksFrom(request->height()) - .map([this](auto block) { - return factory_.serialize( - *std::unique_ptr(block->makeOldModel())); + .map([](auto block) { + return std::dynamic_pointer_cast(block) + ->getTransport(); }) .as_blocking() .subscribe([writer](auto block) { writer->Write(block); }); @@ -48,23 +45,19 @@ grpc::Status BlockLoaderService::retrieveBlock( ::grpc::ServerContext *context, const proto::BlockRequest *request, protocol::Block *response) { - const auto hash = stringToBlob(request->hash()); - if (not hash) { - log_->error("Bad hash in request, {}", - bytestringToHexstring(request->hash())); + const auto hash = shared_model::crypto::Hash(request->hash()); + if (hash.size() == 0) { + log_->error("Bad hash in request"); return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Bad hash provided"); } boost::optional result; storage_->getBlocksFrom(1) - .filter([hash](auto block) { - return shared_model::crypto::toBinaryString(block->hash()) - == hash->to_string(); - }) - .map([this](auto block) { - return factory_.serialize( - *std::unique_ptr(block->makeOldModel())); + .filter([&hash](auto block) { return block->hash() == hash; }) + .map([](auto block) { + return std::dynamic_pointer_cast(block) + ->getTransport(); }) .as_blocking() .subscribe([&result](auto block) { result = block; }); diff --git a/irohad/network/impl/block_loader_service.hpp b/irohad/network/impl/block_loader_service.hpp index 723ecacc97..8347dcfa0d 100644 --- a/irohad/network/impl/block_loader_service.hpp +++ b/irohad/network/impl/block_loader_service.hpp @@ -21,7 +21,6 @@ #include "ametsuchi/block_query.hpp" #include "loader.grpc.pb.h" #include "logger/logger.hpp" -#include "model/converters/pb_block_factory.hpp" namespace iroha { namespace network { @@ -40,7 +39,6 @@ namespace iroha { protocol::Block *response) override; private: - model::converters::PbBlockFactory factory_; std::shared_ptr storage_; logger::Logger log_; }; diff --git a/irohad/network/impl/grpc_channel_builder.hpp b/irohad/network/impl/grpc_channel_builder.hpp new file mode 100644 index 0000000000..7b5f524776 --- /dev/null +++ b/irohad/network/impl/grpc_channel_builder.hpp @@ -0,0 +1,47 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * 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 + * + * http://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. + */ + +#ifndef IROHA_GRPC_CHANNEL_BUILDER_HPP +#define IROHA_GRPC_CHANNEL_BUILDER_HPP + +#include + +namespace iroha { + namespace network { + + /** + * Creates client which is capable of sending and receiving + * messages of INT_MAX bytes size + * @tparam T type for gRPC stub, e.g. proto::Yac + * @param address ip address for connection, ipv4:port + * @return gRPC stub of parametrized type + */ + template + auto createClient(const grpc::string& address) { + // in order to bypass built-in limitation of gRPC message size + grpc::ChannelArguments args; + args.SetMaxSendMessageSize(INT_MAX); + args.SetMaxReceiveMessageSize(INT_MAX); + + return + T::NewStub(grpc::CreateCustomChannel( + address, grpc::InsecureChannelCredentials(), args)); + } + } // namespace network +} // namespace iroha + +#endif // IROHA_GRPC_CHANNEL_BUILDER_HPP diff --git a/irohad/network/impl/peer_communication_service_impl.cpp b/irohad/network/impl/peer_communication_service_impl.cpp index 85bd639057..27776e156a 100644 --- a/irohad/network/impl/peer_communication_service_impl.cpp +++ b/irohad/network/impl/peer_communication_service_impl.cpp @@ -15,7 +15,6 @@ limitations under the License. */ #include "network/impl/peer_communication_service_impl.hpp" -#include "backend/protobuf/from_old_model.hpp" namespace iroha { namespace network { diff --git a/irohad/ordering/CMakeLists.txt b/irohad/ordering/CMakeLists.txt index 139e797f26..4f4e38c797 100644 --- a/irohad/ordering/CMakeLists.txt +++ b/irohad/ordering/CMakeLists.txt @@ -21,10 +21,9 @@ add_library(ordering_service target_link_libraries(ordering_service - pb_model_converters rxcpp tbb - model + shared_model_interfaces ordering_grpc logger ) diff --git a/irohad/ordering/impl/ordering_gate_impl.cpp b/irohad/ordering/impl/ordering_gate_impl.cpp index 0dfad79966..1daf800f9d 100644 --- a/irohad/ordering/impl/ordering_gate_impl.cpp +++ b/irohad/ordering/impl/ordering_gate_impl.cpp @@ -15,23 +15,37 @@ * limitations under the License. */ +#include #include -#include "interfaces/transaction.hpp" #include "ordering/impl/ordering_gate_impl.hpp" +#include "interfaces/iroha_internal/block.hpp" +#include "interfaces/iroha_internal/proposal.hpp" +#include "interfaces/transaction.hpp" + namespace iroha { namespace ordering { + bool ProposalComparator::operator()( + const std::shared_ptr &lhs, + const std::shared_ptr &rhs) const { + return lhs->height() > rhs->height(); + } + OrderingGateImpl::OrderingGateImpl( - std::shared_ptr transport) - : transport_(std::move(transport)), log_(logger::log("OrderingGate")) {} + std::shared_ptr transport, + shared_model::interface::types::HeightType initial_height, + bool run_async) + : transport_(std::move(transport)), + last_block_height_(initial_height), + log_(logger::log("OrderingGate")), + run_async_(run_async) {} void OrderingGateImpl::propagateTransaction( std::shared_ptr transaction) { - log_->info("propagate tx, tx_counter: {} account_id: {}", - std::to_string(transaction->transactionCounter()), + log_->info("propagate tx, account_id: {}", " account_id: " + transaction->creatorAccountId()); transport_->propagateTransaction(transaction); @@ -44,27 +58,61 @@ namespace iroha { void OrderingGateImpl::setPcs( const iroha::network::PeerCommunicationService &pcs) { - pcs_subscriber_ = pcs.on_commit().subscribe([this](auto) { - // TODO: 05/03/2018 @muratovv rework behavior of queue with respect to - // block height IR-1042 - unlock_next_.store(true); - this->tryNextRound(); + log_->info("setPcs"); + + auto top_block_height = + pcs.on_commit() + .transform([](const Commit &commit) { + // find height of last commited block + return commit.as_blocking().last()->height(); + }) + .start_with(last_block_height_); - }); + auto subscribe = [&](auto merge_strategy) { + pcs_subscriber_ = merge_strategy(net_proposals_.get_observable()) + .subscribe([this](const auto &t) { + this->tryNextRound(std::get<1>(t)); + }); + }; + + if (run_async_) { + subscribe([&top_block_height](auto observable) { + return observable.combine_latest(rxcpp::synchronize_new_thread(), + top_block_height); + }); + } else { + subscribe([&top_block_height](auto observable) { + return observable.combine_latest(top_block_height); + }); + } } void OrderingGateImpl::onProposal( std::shared_ptr proposal) { - log_->info("Received new proposal"); + log_->info("Received new proposal, height: {}", proposal->height()); proposal_queue_.push(std::move(proposal)); - tryNextRound(); + std::lock_guard lock(proposal_mutex_); + net_proposals_.get_subscriber().on_next(0); } - void OrderingGateImpl::tryNextRound() { - if (not proposal_queue_.empty() and unlock_next_.exchange(false)) { - std::shared_ptr next_proposal; - proposal_queue_.try_pop(next_proposal); - log_->info("Pass the proposal to pipeline"); + void OrderingGateImpl::tryNextRound( + shared_model::interface::types::HeightType last_block_height) { + log_->debug("TryNextRound"); + std::shared_ptr next_proposal; + while (proposal_queue_.try_pop(next_proposal)) { + // check for old proposal + if (next_proposal->height() < last_block_height + 1) { + log_->debug("Old proposal, discarding"); + continue; + } + // check for new proposal + if (next_proposal->height() > last_block_height + 1) { + log_->debug("Proposal newer than last block, keeping in queue"); + proposal_queue_.push(next_proposal); + break; + } + log_->info("Pass the proposal to pipeline height {}", + next_proposal->height()); proposals_.get_subscriber().on_next(next_proposal); } } diff --git a/irohad/ordering/impl/ordering_gate_impl.hpp b/irohad/ordering/impl/ordering_gate_impl.hpp index 3f4e8c52bb..b00b9a770d 100644 --- a/irohad/ordering/impl/ordering_gate_impl.hpp +++ b/irohad/ordering/impl/ordering_gate_impl.hpp @@ -20,9 +20,11 @@ #include "network/ordering_gate.hpp" -#include -#include +#include +#include + +#include "interfaces/common_objects/types.hpp" #include "logger/logger.hpp" #include "network/impl/async_grpc_client.hpp" #include "network/ordering_gate_transport.hpp" @@ -37,6 +39,15 @@ namespace shared_model { namespace iroha { namespace ordering { + /** + * Compare proposals by height + */ + struct ProposalComparator { + bool operator()( + const std::shared_ptr &lhs, + const std::shared_ptr &rhs) const; + }; + /** * OrderingGate implementation with gRPC asynchronous client * Interacts with given OrderingService @@ -46,8 +57,16 @@ namespace iroha { class OrderingGateImpl : public network::OrderingGate, public network::OrderingGateNotification { public: - explicit OrderingGateImpl( - std::shared_ptr transport); + /** + * @param transport - network communication layer + * @param initial_height - height of the last block stored on this peer + * @param run_async - whether proposals should be handled + * asynchronously (on separate thread). Default is true. + */ + OrderingGateImpl( + std::shared_ptr transport, + shared_model::interface::types::HeightType initial_height, + bool run_async = true); void propagateTransaction( std::shared_ptr @@ -66,25 +85,39 @@ namespace iroha { private: /** * Try to push proposal for next consensus round + * @param - last_block_height - what is the last block stored on this + * peer, or for which commit was received. If block is newer than + * currently stored proposals, proposals are discarded. If it is older, + * newer proposals are propagated in order */ - void tryNextRound(); + void tryNextRound( + shared_model::interface::types::HeightType last_block_height); rxcpp::subjects::subject< std::shared_ptr> proposals_; + + rxcpp::subjects::subject + net_proposals_; std::shared_ptr transport_; - /// invariant: true if proposal can be pushed to subscribers - std::atomic_bool unlock_next_{true}; + std::mutex proposal_mutex_; /// queue with all proposals received from ordering service - tbb::concurrent_queue> + tbb::concurrent_priority_queue< + std::shared_ptr, + ProposalComparator> proposal_queue_; + /// last commited block height + shared_model::interface::types::HeightType last_block_height_; + /// subscription of pcs::on_commit rxcpp::composite_subscription pcs_subscriber_; logger::Logger log_; + + bool run_async_; }; } // namespace ordering } // namespace iroha diff --git a/irohad/ordering/impl/ordering_gate_transport_grpc.cpp b/irohad/ordering/impl/ordering_gate_transport_grpc.cpp index 316300e784..15364dc778 100644 --- a/irohad/ordering/impl/ordering_gate_transport_grpc.cpp +++ b/irohad/ordering/impl/ordering_gate_transport_grpc.cpp @@ -19,6 +19,7 @@ #include "backend/protobuf/transaction.hpp" #include "builders/protobuf/proposal.hpp" #include "interfaces/common_objects/types.hpp" +#include "network/impl/grpc_channel_builder.hpp" using namespace iroha::ordering; @@ -52,9 +53,10 @@ grpc::Status OrderingGateTransportGrpc::onProposal( OrderingGateTransportGrpc::OrderingGateTransportGrpc( const std::string &server_address) - : client_(proto::OrderingServiceTransportGrpc::NewStub(grpc::CreateChannel( - server_address, grpc::InsecureChannelCredentials()))), - log_(logger::log("OrderingGate")) {} + : network::AsyncGrpcClient( + logger::log("OrderingGate")), + client_(network::createClient( + server_address)) {} void OrderingGateTransportGrpc::propagateTransaction( std::shared_ptr transaction) { @@ -64,6 +66,7 @@ void OrderingGateTransportGrpc::propagateTransaction( auto transaction_transport = static_cast(*transaction) .getTransport(); + log_->debug("Propagating: '{}'", transaction_transport.DebugString()); call->response_reader = client_->AsynconTransaction(&call->context, transaction_transport, &cq_); diff --git a/irohad/ordering/impl/ordering_gate_transport_grpc.hpp b/irohad/ordering/impl/ordering_gate_transport_grpc.hpp index 005bd16de4..39292b42f8 100644 --- a/irohad/ordering/impl/ordering_gate_transport_grpc.hpp +++ b/irohad/ordering/impl/ordering_gate_transport_grpc.hpp @@ -53,7 +53,6 @@ namespace iroha { private: std::weak_ptr subscriber_; std::unique_ptr client_; - logger::Logger log_; }; } // namespace ordering diff --git a/irohad/ordering/impl/ordering_service_impl.cpp b/irohad/ordering/impl/ordering_service_impl.cpp index 886bd8f96c..84bded1186 100644 --- a/irohad/ordering/impl/ordering_service_impl.cpp +++ b/irohad/ordering/impl/ordering_service_impl.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "ordering/impl/ordering_service_impl.hpp" @@ -30,21 +18,50 @@ namespace iroha { OrderingServiceImpl::OrderingServiceImpl( std::shared_ptr wsv, size_t max_size, - size_t delay_milliseconds, + rxcpp::observable proposal_timeout, std::shared_ptr transport, std::shared_ptr - persistent_state) + persistent_state, + bool is_async) : wsv_(wsv), max_size_(max_size), - delay_milliseconds_(delay_milliseconds), transport_(transport), - persistent_state_(persistent_state), - is_finished(false) { - updateTimer(); + persistent_state_(persistent_state) { log_ = logger::log("OrderingServiceImpl"); // restore state of ordering service from persistent storage - proposal_height = persistent_state_->loadProposalHeight().value(); + proposal_height_ = persistent_state_->loadProposalHeight().value(); + + rxcpp::observable timer = + proposal_timeout.map([](auto) { return ProposalEvent::kTimerEvent; }); + + auto subscribe = [&](auto merge_strategy) { + handle_ = merge_strategy(rxcpp::observable<>::from( + timer, transactions_.get_observable())) + .subscribe([this](auto &&v) { + auto check_queue = [&] { + switch (v) { + case ProposalEvent::kTimerEvent: + return not queue_.empty(); + case ProposalEvent::kTransactionEvent: + return queue_.unsafe_size() >= max_size_; + default: + BOOST_ASSERT_MSG(false, "Unknown value"); + } + }; + if (check_queue()) { + this->generateProposal(); + } + }); + }; + + if (is_async) { + subscribe([](auto observable) { + return observable.merge(rxcpp::synchronize_new_thread()); + }); + } else { + subscribe([](auto observable) { return observable.merge(); }); + } } void OrderingServiceImpl::onTransaction( @@ -52,25 +69,24 @@ namespace iroha { queue_.push(transaction); log_->info("Queue size is {}", queue_.unsafe_size()); - if (queue_.unsafe_size() >= max_size_) { - handle.unsubscribe(); - updateTimer(); - } + // on_next calls should not be concurrent + std::lock_guard lk(mutex_); + transactions_.get_subscriber().on_next(ProposalEvent::kTransactionEvent); } void OrderingServiceImpl::generateProposal() { // TODO 05/03/2018 andrei IR-1046 Server-side shared model object // factories with move semantics iroha::protocol::Proposal proto_proposal; - proto_proposal.set_height(proposal_height++); + proto_proposal.set_height(proposal_height_++); proto_proposal.set_created_time(iroha::time::now()); log_->info("Start proposal generation"); for (std::shared_ptr tx; static_cast(proto_proposal.transactions_size()) < max_size_ and queue_.try_pop(tx);) { - *proto_proposal.add_transactions() = std::move( - std::static_pointer_cast(tx) - ->getTransport()); + *proto_proposal.add_transactions() = + std::move(static_cast(tx.get()) + ->getTransport()); } auto proposal = std::make_unique( @@ -78,10 +94,10 @@ namespace iroha { // Save proposal height to the persistent storage. // In case of restart it reloads state. - if (persistent_state_->saveProposalHeight(proposal_height)) { + if (persistent_state_->saveProposalHeight(proposal_height_)) { publishProposal(std::move(proposal)); } else { - // TODO(@l4l) 23/03/18: publish proposal independant of psql status + // TODO(@l4l) 23/03/18: publish proposal independent of psql status // IR-1162 log_->warn( "Proposal height cannot be saved. Skipping proposal publish"); @@ -103,24 +119,8 @@ namespace iroha { } } - void OrderingServiceImpl::updateTimer() { - std::lock_guard lock(m); - if (is_finished) { - return; - } - if (not queue_.empty()) { - this->generateProposal(); - } - timer = rxcpp::observable<>::timer( - std::chrono::milliseconds(delay_milliseconds_)); - handle = timer.subscribe_on(rxcpp::observe_on_new_thread()) - .subscribe([this](auto) { this->updateTimer(); }); - } - OrderingServiceImpl::~OrderingServiceImpl() { - std::lock_guard lock(m); - is_finished = true; - handle.unsubscribe(); + handle_.unsubscribe(); } } // namespace ordering } // namespace iroha diff --git a/irohad/ordering/impl/ordering_service_impl.hpp b/irohad/ordering/impl/ordering_service_impl.hpp index e0c044a8cb..88b3948d7c 100644 --- a/irohad/ordering/impl/ordering_service_impl.hpp +++ b/irohad/ordering/impl/ordering_service_impl.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_ORDERING_SERVICE_IMPL_HPP @@ -31,7 +19,6 @@ namespace iroha { namespace ametsuchi { class OrderingServicePersistentState; - class OrderingServiceTransport; class PeerQuery; } // namespace ametsuchi @@ -42,20 +29,27 @@ namespace iroha { * Allows receiving transactions concurrently from multiple peers by using * concurrent queue * Sends proposal by given timer interval and proposal size - * @param delay_milliseconds timer delay - * @param max_size proposal size - * @param persistent_state - storage for persistent state of ordering - * service */ class OrderingServiceImpl : public network::OrderingService { public: + using TimeoutType = long; + /** + * Constructor + * @param wsv interface for fetching peers from world state view + * @param max_size maximum size of proposal + * @param proposal_timeout observable timeout for proposal creation + * @param transport receive transactions and publish proposals + * @param persistent_state storage for auxiliary information + * @param is_async whether proposals are generated in a separate thread + */ OrderingServiceImpl( std::shared_ptr wsv, size_t max_size, - size_t delay_milliseconds, + rxcpp::observable proposal_timeout, std::shared_ptr transport, std::shared_ptr - persistent_state); + persistent_state, + bool is_async = true); /** * Process transaction received from network @@ -77,22 +71,16 @@ namespace iroha { private: /** - * Collect transactions from queue - * Passes the generated proposal to publishProposal - */ - void generateProposal() override; - - /** - * Method update peers for sending proposal + * Events for queue check strategy */ + enum class ProposalEvent { kTransactionEvent, kTimerEvent }; /** - * Update the timer to be called after delay_milliseconds_ + * Collect transactions from queue + * Passes the generated proposal to publishProposal */ - void updateTimer(); + void generateProposal() override; - rxcpp::observable timer; - rxcpp::composite_subscription handle; std::shared_ptr wsv_; tbb::concurrent_queue< @@ -104,14 +92,10 @@ namespace iroha { */ const size_t max_size_; - /** - * wait for specified time if queue is empty - */ - const size_t delay_milliseconds_; std::shared_ptr transport_; /** - * Persistense storage for proposal counter. + * Persistent storage for proposal counter. * In case of relaunch, ordering server will enumerate proposals * consecutively. */ @@ -122,17 +106,18 @@ namespace iroha { * Proposal counter of expected proposal. Should be number of blocks in * the ledger + 1. */ - size_t proposal_height; + size_t proposal_height_; - /** - * Mutex for proper quit handling - */ - std::mutex m; + /// Observable for transaction events from the network + rxcpp::subjects::subject transactions_; + + /// Internal event observable handle + rxcpp::composite_subscription handle_; /** - * Set after destruction + * Mutex for incoming transactions */ - bool is_finished; + std::mutex mutex_; logger::Logger log_; }; diff --git a/irohad/ordering/impl/ordering_service_transport_grpc.cpp b/irohad/ordering/impl/ordering_service_transport_grpc.cpp index 98554df766..7092ff53dc 100644 --- a/irohad/ordering/impl/ordering_service_transport_grpc.cpp +++ b/irohad/ordering/impl/ordering_service_transport_grpc.cpp @@ -18,6 +18,7 @@ #include "backend/protobuf/transaction.hpp" #include "builders/protobuf/proposal.hpp" +#include "network/impl/grpc_channel_builder.hpp" using namespace iroha::ordering; @@ -30,6 +31,7 @@ grpc::Status OrderingServiceTransportGrpc::onTransaction( ::grpc::ServerContext *context, const iroha::protocol::Transaction *request, ::google::protobuf::Empty *response) { + log_->info("OrderingServiceTransportGrpc::onTransaction"); if (subscriber_.expired()) { log_->error("No subscriber"); } else { @@ -44,19 +46,20 @@ grpc::Status OrderingServiceTransportGrpc::onTransaction( void OrderingServiceTransportGrpc::publishProposal( std::unique_ptr proposal, const std::vector &peers) { + log_->info("OrderingServiceTransportGrpc::publishProposal"); std::unordered_map> peers_map; - for (const auto &peer : peers) { - peers_map[peer] = proto::OrderingGateTransportGrpc::NewStub( - grpc::CreateChannel(peer, grpc::InsecureChannelCredentials())); + peers_map[peer] = + network::createClient(peer); } for (const auto &peer : peers_map) { auto call = new AsyncClientCall; - auto proto = static_cast(proposal.get()); + log_->debug("Publishing proposal: '{}'", + proto->getTransport().DebugString()); call->response_reader = peer.second->AsynconProposal( &call->context, proto->getTransport(), &cq_); @@ -65,4 +68,5 @@ void OrderingServiceTransportGrpc::publishProposal( } OrderingServiceTransportGrpc::OrderingServiceTransportGrpc() - : log_(logger::testLog("OrderingServiceTransportGrpc")) {} + : network::AsyncGrpcClient( + logger::log("OrderingServiceTransportGrpc")) {} diff --git a/irohad/ordering/impl/ordering_service_transport_grpc.hpp b/irohad/ordering/impl/ordering_service_transport_grpc.hpp index 4e65196303..5946fb9a1a 100644 --- a/irohad/ordering/impl/ordering_service_transport_grpc.hpp +++ b/irohad/ordering/impl/ordering_service_transport_grpc.hpp @@ -50,7 +50,6 @@ namespace iroha { private: std::weak_ptr subscriber_; - logger::Logger log_; }; } // namespace ordering diff --git a/irohad/simulator/CMakeLists.txt b/irohad/simulator/CMakeLists.txt index 825855f90f..55aed3c0fb 100644 --- a/irohad/simulator/CMakeLists.txt +++ b/irohad/simulator/CMakeLists.txt @@ -3,7 +3,7 @@ add_library(simulator ) target_link_libraries(simulator - model + shared_model_proto_backend rxcpp logger ) diff --git a/irohad/simulator/impl/simulator.cpp b/irohad/simulator/impl/simulator.cpp index 436bd0ccf6..6404589ffc 100644 --- a/irohad/simulator/impl/simulator.cpp +++ b/irohad/simulator/impl/simulator.cpp @@ -31,7 +31,8 @@ namespace iroha { std::shared_ptr statefulValidator, std::shared_ptr factory, std::shared_ptr blockQuery, - std::shared_ptr> crypto_signer) + std::shared_ptr> + crypto_signer) : validator_(std::move(statefulValidator)), ametsuchi_factory_(std::move(factory)), block_queries_(std::move(blockQuery)), @@ -65,8 +66,10 @@ namespace iroha { const shared_model::interface::Proposal &proposal) { log_->info("process proposal"); // Get last block from local ledger - block_queries_->getTopBlocks(1).as_blocking().subscribe( - [this](auto block) { last_block = block; }); + block_queries_->getTopBlocks(1) + .subscribe_on(rxcpp::observe_on_new_thread()) + .as_blocking() + .subscribe([this](auto block) { last_block = block; }); if (not last_block) { log_->warn("Could not fetch last block"); return; @@ -97,8 +100,8 @@ namespace iroha { const shared_model::interface::Proposal &proposal) { log_->info("process verified proposal"); - // TODO: Alexey Chernyshov IR-1011 2018-03-08 rework BlockBuilder logic, so - // that this cast will not be needed + // TODO: Alexey Chernyshov IR-1011 2018-03-08 rework BlockBuilder logic, + // so that this cast will not be needed auto proto_txs = proposal.transactions() | boost::adaptors::transformed([](const auto &polymorphic_tx) { diff --git a/irohad/synchronizer/CMakeLists.txt b/irohad/synchronizer/CMakeLists.txt index e61d120f52..1f316488b1 100644 --- a/irohad/synchronizer/CMakeLists.txt +++ b/irohad/synchronizer/CMakeLists.txt @@ -3,7 +3,7 @@ add_library(synchronizer ) target_link_libraries(synchronizer - model + shared_model_interfaces rxcpp logger ) diff --git a/irohad/synchronizer/impl/synchronizer_impl.cpp b/irohad/synchronizer/impl/synchronizer_impl.cpp index 8a07f314b4..9b87528d1d 100644 --- a/irohad/synchronizer/impl/synchronizer_impl.cpp +++ b/irohad/synchronizer/impl/synchronizer_impl.cpp @@ -84,8 +84,13 @@ namespace iroha { return; } auto chain = blockLoader_->retrieveBlocks( - shared_model::crypto::PublicKey(signature->publicKey())); - if (validator_->validateChain(chain, *storage)) { + shared_model::crypto::PublicKey(signature.publicKey())); + // Check chain last commit + auto is_chain_end_expected = + chain.as_blocking().last()->hash() == commit_message->hash(); + + if (validator_->validateChain(chain, *storage) + and is_chain_end_expected) { // Peer send valid chain mutableFactory_->commit(std::move(storage)); notifier_.get_subscriber().on_next(chain); diff --git a/irohad/torii/CMakeLists.txt b/irohad/torii/CMakeLists.txt index 066e553ef1..6f5ad86cd6 100644 --- a/irohad/torii/CMakeLists.txt +++ b/irohad/torii/CMakeLists.txt @@ -20,22 +20,21 @@ add_library(command_client command_client.cpp) target_link_libraries(command_client torii_service endpoint - model + schema ) - target_link_libraries(query_client - torii_service - endpoint - ) + torii_service + endpoint + ) add_library(torii_service impl/query_service.cpp impl/command_service.cpp ) target_link_libraries(torii_service - pb_model_converters endpoint - model + shared_model_proto_backend logger shared_model_stateless_validation + processors ) diff --git a/irohad/torii/command_client.cpp b/irohad/torii/command_client.cpp index be4f1b7294..81816c0613 100644 --- a/irohad/torii/command_client.cpp +++ b/irohad/torii/command_client.cpp @@ -16,6 +16,8 @@ limitations under the License. #include #include "block.pb.h" +#include "network/impl/grpc_channel_builder.hpp" +#include "common/byteutils.hpp" #include "torii/command_client.hpp" namespace torii { @@ -26,9 +28,9 @@ namespace torii { CommandSyncClient::CommandSyncClient(const std::string &ip, size_t port) : ip_(ip), port_(port), - stub_(iroha::protocol::CommandService::NewStub( - grpc::CreateChannel(ip + ":" + std::to_string(port), - grpc::InsecureChannelCredentials()))) {} + stub_(iroha::network::createClient( + ip + ":" + std::to_string(port))), + log_(logger::log("CommandSyncClient")) {} CommandSyncClient::CommandSyncClient(const CommandSyncClient &rhs) : CommandSyncClient(rhs.ip_, rhs.port_) {} @@ -69,8 +71,12 @@ namespace torii { std::unique_ptr > reader( stub_->StatusStream(&context, tx)); while (reader->Read(&resp)) { + log_->debug("received new status: {}, hash {}", + resp.tx_status(), + iroha::bytestringToHexstring(resp.tx_hash())); response.push_back(resp); } + reader->Finish(); } void CommandSyncClient::swap(CommandSyncClient &lhs, CommandSyncClient &rhs) { diff --git a/irohad/torii/command_client.hpp b/irohad/torii/command_client.hpp index 1d4ee2d620..ec915d15c4 100644 --- a/irohad/torii/command_client.hpp +++ b/irohad/torii/command_client.hpp @@ -22,6 +22,8 @@ limitations under the License. #include #include +#include "logger/logger.hpp" + namespace torii { /** @@ -67,6 +69,7 @@ namespace torii { std::string ip_; size_t port_; std::unique_ptr stub_; + logger::Logger log_; }; } // namespace torii diff --git a/irohad/torii/command_service.hpp b/irohad/torii/command_service.hpp index 8f24618c64..5a37627ac6 100644 --- a/irohad/torii/command_service.hpp +++ b/irohad/torii/command_service.hpp @@ -125,7 +125,7 @@ namespace torii { override; private: - void checkCacheAndSend( + bool checkCacheAndSend( const boost::optional &resp, grpc::ServerWriter &response_writer) const; diff --git a/irohad/torii/impl/command_service.cpp b/irohad/torii/impl/command_service.cpp index d2369c7d40..1004ccdfbf 100644 --- a/irohad/torii/impl/command_service.cpp +++ b/irohad/torii/impl/command_service.cpp @@ -16,6 +16,7 @@ */ #include + #include "backend/protobuf/transaction_responses/proto_tx_response.hpp" #include "ametsuchi/block_query.hpp" @@ -24,6 +25,7 @@ #include "builders/protobuf/transport_builder.hpp" #include "common/byteutils.hpp" #include "common/types.hpp" +#include "cryptography/default_hash_provider.hpp" #include "endpoint.pb.h" #include "torii/command_service.hpp" #include "validators/default_validator.hpp" @@ -49,21 +51,7 @@ namespace torii { std::static_pointer_cast( iroha_response); auto tx_hash = proto_response->transactionHash(); - auto res = cache_->findItem(tx_hash); - if (not res) { - // TODO 05/03/2018 andrei IR-1046 Server-side shared model object - // factories with move semantics - auto response = shared_model::proto::TransactionStatusBuilder() - .txHash(tx_hash) - .notReceived() - .build(); - cache_->addItem(tx_hash, response.getTransport()); - return; - } - - auto proto_status = proto_response->getTransport().tx_status(); - res->set_tx_status(proto_status); - cache_->addItem(tx_hash, *res); + cache_->addItem(tx_hash, proto_response->getTransport()); }); } @@ -82,6 +70,8 @@ namespace torii { &iroha_tx) { tx_hash = iroha_tx.value.hash(); if (cache_->findItem(tx_hash)) { + log_->warn("Found transaction {} in cache, ignoring", + tx_hash.hex()); return; } @@ -90,7 +80,6 @@ namespace torii { response.set_tx_status( iroha::protocol::TxStatus::STATELESS_VALIDATION_SUCCESS); - cache_->addItem(tx_hash, response); // Send transaction to iroha tx_processor_->transactionHandle( std::make_shared( @@ -100,9 +89,8 @@ namespace torii { // getting hash from invalid transaction auto blobPayload = shared_model::proto::makeBlob(request.payload()); - tx_hash = - shared_model::proto::Transaction::HashProviderType::makeHash( - blobPayload); + tx_hash = shared_model::crypto::DefaultHashProvider::makeHash( + blobPayload); log_->warn("Stateless invalid tx: {}, hash: {}", error.error, tx_hash.hex()); @@ -112,8 +100,11 @@ namespace torii { shared_model::crypto::toBinaryString(tx_hash)); response.set_tx_status( iroha::protocol::TxStatus::STATELESS_VALIDATION_FAILED); + response.set_error_message(std::move(error.error)); }); - + log_->debug("Torii: adding item to cache: {}, status {} ", + tx_hash.hex(), + response.tx_status()); cache_->addItem(tx_hash, response); } @@ -133,7 +124,7 @@ namespace torii { response.CopyFrom(*resp); } else { response.set_tx_hash(request.tx_hash()); - if (block_query_->getTxByHashSync( + if (block_query_->hasTxWithHash( shared_model::crypto::Hash(request.tx_hash()))) { response.set_tx_status(iroha::protocol::TxStatus::COMMITTED); } else { @@ -141,6 +132,9 @@ namespace torii { iroha::bytestringToHexstring(request.tx_hash())); response.set_tx_status(iroha::protocol::TxStatus::NOT_RECEIVED); } + log_->debug("Status: adding item to cache: {}, status {}", + tx_hash.hex(), + response.tx_status()); cache_->addItem(tx_hash, response); } } @@ -157,20 +151,25 @@ namespace torii { iroha::protocol::TxStatusRequest const &request, grpc::ServerWriter &response_writer) { auto resp = cache_->findItem(shared_model::crypto::Hash(request.tx_hash())); - checkCacheAndSend(resp, response_writer); - - bool finished = false; + if (checkCacheAndSend(resp, response_writer)) { + return; + } + auto finished = std::make_shared>(false); auto subscription = rxcpp::composite_subscription(); - auto request_hash = shared_model::crypto::Hash(request.tx_hash()); + auto request_hash = + std::make_shared(request.tx_hash()); - /// condition variable to ensure that current method will not return before + /// Condition variable to ensure that current method will not return before /// transaction is processed or a timeout reached. It blocks current thread /// and waits for thread from subscribe() to unblock. - std::condition_variable cv; + auto cv = std::make_shared(); + + log_->debug("StatusStream before subscribe(), hash: {}", + request_hash->hex()); tx_processor_->transactionNotifier() .filter([&request_hash](auto response) { - return response->transactionHash() == request_hash; + return response->transactionHash() == *request_hash; }) .subscribe( subscription, @@ -179,14 +178,17 @@ namespace torii { auto proto_response = std::static_pointer_cast< shared_model::proto::TransactionResponse>(iroha_response); + log_->debug("subscribe new status: {}, hash {}", + proto_response->toString(), + proto_response->transactionHash().hex()); + iroha::protocol::ToriiResponse resp_sub = proto_response->getTransport(); if (isFinalStatus(resp_sub.tx_status())) { response_writer.WriteLast(resp_sub, grpc::WriteOptions()); - subscription.unsubscribe(); - finished = true; - cv.notify_one(); + *finished = true; + cv->notify_one(); } else { response_writer.Write(resp_sub); } @@ -194,31 +196,57 @@ namespace torii { std::mutex wait_subscription; std::unique_lock lock(wait_subscription); + + log_->debug("StatusStream waiting start, hash: {}", request_hash->hex()); + /// we expect that start_tx_processing_duration_ will be enough /// to at least start tx processing. /// Otherwise we think there is no such tx at all. - cv.wait_for(lock, start_tx_processing_duration_); - if (not finished) { + cv->wait_for(lock, start_tx_processing_duration_); + + log_->debug("StatusStream waiting finish, hash: {}", request_hash->hex()); + + if (not *finished) { if (not resp) { - subscription.unsubscribe(); + log_->warn("StatusStream request processing timeout, hash: {}", + request_hash->hex()); // TODO 05/03/2018 andrei IR-1046 Server-side shared model object // factories with move semantics auto resp_none = shared_model::proto::TransactionStatusBuilder() - .txHash(request_hash) + .txHash(*request_hash) .notReceived() .build(); response_writer.WriteLast(resp_none.getTransport(), grpc::WriteOptions()); } else { - log_->info( + log_->debug( "Tx processing was started but unfinished, awaiting more, hash: {}", - request_hash.hex()); + request_hash->hex()); /// We give it 2*proposal_delay time until timeout. - cv.wait_for(lock, 2 * proposal_delay_); + cv->wait_for(lock, 2 * proposal_delay_); + + /// status can be in the cache if it was finalized before we subscribed + if (not *finished) { + log_->debug("Transaction {} still not finished", request_hash->hex()); + + auto cache_second_check = + cache_->findItem(shared_model::crypto::Hash(request.tx_hash())); + log_->debug("Status in cache: {}", cache_second_check->tx_status()); + + /// final status means the case from a comment above + /// if it's not - let's ignore it for now + if (isFinalStatus(cache_second_check->tx_status())) { + log_->warn("Transaction was finalized before subscription"); + response_writer.WriteLast(*resp, grpc::WriteOptions()); + } + } } } else { - log_->warn("Command processing timeout, hash: {}", request_hash.hex()); + log_->debug("StatusStream request processed successfully, hash: {}", + request_hash->hex()); } + subscription.unsubscribe(); + log_->debug("StatusStream unsubscribed"); } grpc::Status CommandService::StatusStream( @@ -229,19 +257,22 @@ namespace torii { return grpc::Status::OK; } - void CommandService::checkCacheAndSend( + bool CommandService::checkCacheAndSend( const boost::optional &resp, grpc::ServerWriter &response_writer) const { if (resp) { if (isFinalStatus(resp->tx_status())) { + log_->debug("Transaction {} in service cache and final", + iroha::bytestringToHexstring(resp->tx_hash())); response_writer.WriteLast(*resp, grpc::WriteOptions()); - return; + return true; } + log_->debug("Transaction {} in service cache and not final", + iroha::bytestringToHexstring(resp->tx_hash())); response_writer.Write(*resp); - } else { - log_->debug("Transaction miss service cache"); } + return false; } bool CommandService::isFinalStatus( diff --git a/irohad/torii/impl/query_service.cpp b/irohad/torii/impl/query_service.cpp index e67a7e6a58..ec6db792fe 100644 --- a/irohad/torii/impl/query_service.cpp +++ b/irohad/torii/impl/query_service.cpp @@ -17,6 +17,7 @@ #include "torii/query_service.hpp" #include "backend/protobuf/query_responses/proto_query_response.hpp" +#include "cryptography/default_hash_provider.hpp" #include "validators/default_validator.hpp" namespace torii { @@ -41,7 +42,7 @@ namespace torii { iroha::protocol::QueryResponse &response) { shared_model::crypto::Hash hash; auto blobPayload = shared_model::proto::makeBlob(request.payload()); - hash = shared_model::proto::Query::HashProviderType::makeHash(blobPayload); + hash = shared_model::crypto::DefaultHashProvider::makeHash(blobPayload); if (cache_.findItem(hash)) { // Query was already processed @@ -70,12 +71,13 @@ namespace torii { cache_.addItem(hash, response); } }, - [&hash, &response]( - const iroha::expected::Error &error) { + [&hash, + &response](const iroha::expected::Error &error) { response.set_query_hash( - shared_model::crypto::toBinaryString(hash)); + hash.hex()); response.mutable_error_response()->set_reason( iroha::protocol::ErrorResponse::STATELESS_INVALID); + response.mutable_error_response()->set_message(std::move(error.error)); }); } diff --git a/irohad/torii/processor/CMakeLists.txt b/irohad/torii/processor/CMakeLists.txt index dfe629f093..ff25230d08 100644 --- a/irohad/torii/processor/CMakeLists.txt +++ b/irohad/torii/processor/CMakeLists.txt @@ -4,9 +4,9 @@ add_library(processors ) target_link_libraries(processors PUBLIC - model rxcpp logger endpoint shared_model_proto_builders + query_execution ) diff --git a/irohad/torii/processor/impl/query_processor_impl.cpp b/irohad/torii/processor/impl/query_processor_impl.cpp index 3fa59f47cb..01e4e93205 100644 --- a/irohad/torii/processor/impl/query_processor_impl.cpp +++ b/irohad/torii/processor/impl/query_processor_impl.cpp @@ -16,37 +16,9 @@ */ #include "torii/processor/query_processor_impl.hpp" -#include "backend/protobuf/from_old_model.hpp" -#include "backend/protobuf/query_responses/proto_query_response.hpp" namespace iroha { namespace torii { - - /** - * Checks if public keys are subset of given signaturies - * @param signatures user signatories, an iterable collection - * @param public_keys vector of public keys - * @return true if user has needed signatories, false instead - */ - bool signaturesSubset( - const shared_model::interface::SignatureSetType &signatures, - const std::vector &public_keys) { - // TODO 09/10/17 Lebedev: simplify the subset verification IR-510 - // #goodfirstissue - // TODO 30/04/2018 x3medima17: remove code duplication in query_processor - // IR-1192 and stateful_validator - std::unordered_set txPubkeys; - for (auto sign : signatures) { - txPubkeys.insert(sign->publicKey().toString()); - } - return std::all_of(public_keys.begin(), - public_keys.end(), - [&txPubkeys](const auto &public_key) { - return txPubkeys.find(public_key.toString()) - != txPubkeys.end(); - }); - } - /** * Builds QueryResponse that contains StatefulError * @param hash - original query hash @@ -71,14 +43,14 @@ namespace iroha { const auto &sig = *qry.signatures().begin(); const auto &wsv_query = storage_->getWsvQuery(); - auto qpf = - model::QueryProcessingFactory(wsv_query, storage_->getBlockQuery()); + auto qpf = QueryProcessingFactory(wsv_query, storage_->getBlockQuery()); auto signatories = wsv_query->getSignatories(qry.creatorAccountId()); if (not signatories) { return false; } - bool result = signaturesSubset({sig}, *signatories); - return result; + return std::find( + signatories->begin(), signatories->end(), sig.publicKey()) + != signatories->end(); } void QueryProcessorImpl::queryHandle( @@ -90,9 +62,8 @@ namespace iroha { } const auto &wsv_query = storage_->getWsvQuery(); - auto qpf = - model::QueryProcessingFactory(wsv_query, storage_->getBlockQuery()); - auto qpf_response = qpf.execute(*qry); + auto qpf = QueryProcessingFactory(wsv_query, storage_->getBlockQuery()); + auto qpf_response = qpf.validateAndExecute(*qry); auto qry_resp = std::static_pointer_cast( qpf_response); diff --git a/irohad/torii/processor/impl/transaction_processor_impl.cpp b/irohad/torii/processor/impl/transaction_processor_impl.cpp index 228cdf3c1a..cc2a2c2fea 100644 --- a/irohad/torii/processor/impl/transaction_processor_impl.cpp +++ b/irohad/torii/processor/impl/transaction_processor_impl.cpp @@ -36,8 +36,13 @@ namespace iroha { for (const auto &tx : model_proposal->transactions()) { auto hash = tx->hash(); proposal_set_.insert(hash); + log_->info("on proposal stateless success: {}", hash.hex()); + // different on_next() calls (this one and below) can happen in + // different threads and we don't expect emitting them concurrently + std::lock_guard lock(notifier_mutex_); notifier_.get_subscriber().on_next( - status_builder_.statelessValidationSuccess() + shared_model::builder::DefaultTransactionStatusBuilder() + .statelessValidationSuccess() .txHash(hash) .build()); } @@ -53,8 +58,11 @@ namespace iroha { if (this->proposal_set_.find(hash) != proposal_set_.end()) { proposal_set_.erase(hash); candidate_set_.insert(hash); + log_->info("on commit stateful success: {}", hash.hex()); + std::lock_guard lock(notifier_mutex_); notifier_.get_subscriber().on_next( - status_builder_.statefulValidationSuccess() + shared_model::builder::DefaultTransactionStatusBuilder() + .statefulValidationSuccess() .txHash(hash) .build()); } @@ -62,17 +70,25 @@ namespace iroha { }, // on complete [this]() { - for (auto& tx_hash : proposal_set_) { + for (auto &tx_hash : proposal_set_) { + log_->info("on commit stateful failed: {}", tx_hash.hex()); + std::lock_guard lock(notifier_mutex_); notifier_.get_subscriber().on_next( - status_builder_.statefulValidationFailed() + shared_model::builder::DefaultTransactionStatusBuilder() + .statefulValidationFailed() .txHash(tx_hash) .build()); } proposal_set_.clear(); - for (auto tx_hash : candidate_set_) { + for (auto &tx_hash : candidate_set_) { + log_->info("on commit committed: {}", tx_hash.hex()); + std::lock_guard lock(notifier_mutex_); notifier_.get_subscriber().on_next( - status_builder_.committed().txHash(tx_hash).build()); + shared_model::builder::DefaultTransactionStatusBuilder() + .committed() + .txHash(tx_hash) + .build()); } candidate_set_.clear(); }); diff --git a/irohad/torii/processor/query_processor_impl.hpp b/irohad/torii/processor/query_processor_impl.hpp index cad6de234a..d1c977be49 100644 --- a/irohad/torii/processor/query_processor_impl.hpp +++ b/irohad/torii/processor/query_processor_impl.hpp @@ -19,7 +19,7 @@ #define IROHA_QUERY_PROCESSOR_IMPL_HPP #include "ametsuchi/storage.hpp" -#include "model/query_execution.hpp" +#include "execution/query_execution.hpp" #include "torii/processor/query_processor.hpp" namespace iroha { diff --git a/irohad/torii/processor/transaction_processor_impl.hpp b/irohad/torii/processor/transaction_processor_impl.hpp index 3bb14dd1eb..261fcc90b6 100644 --- a/irohad/torii/processor/transaction_processor_impl.hpp +++ b/irohad/torii/processor/transaction_processor_impl.hpp @@ -18,6 +18,7 @@ #ifndef IROHA_TRANSACTION_PROCESSOR_STUB_HPP #define IROHA_TRANSACTION_PROCESSOR_STUB_HPP +#include #include "builders/default_builders.hpp" #include "interfaces/transaction_responses/tx_response.hpp" #include "logger/logger.hpp" @@ -60,9 +61,11 @@ namespace iroha { std::shared_ptr> notifier_; - shared_model::builder::DefaultTransactionStatusBuilder status_builder_; - logger::Logger log_; + + /// prevents from emitting new tx statuses from different threads + /// in parallel + std::mutex notifier_mutex_; }; } // namespace torii } // namespace iroha diff --git a/irohad/torii/query_client.cpp b/irohad/torii/query_client.cpp index 8e8da6a1e4..1f480c089a 100644 --- a/irohad/torii/query_client.cpp +++ b/irohad/torii/query_client.cpp @@ -13,6 +13,8 @@ limitations under the License. #include "torii/query_client.hpp" +#include "network/impl/grpc_channel_builder.hpp" + namespace torii_utils { using iroha::protocol::Query; @@ -21,9 +23,8 @@ namespace torii_utils { QuerySyncClient::QuerySyncClient(const std::string &ip, size_t port) : ip_(ip), port_(port), - stub_(iroha::protocol::QueryService::NewStub( - grpc::CreateChannel(ip + ":" + std::to_string(port), - grpc::InsecureChannelCredentials()))) {} + stub_(iroha::network::createClient( + ip + ":" + std::to_string(port))) {} QuerySyncClient::QuerySyncClient(const QuerySyncClient &rhs) : QuerySyncClient(rhs.ip_, rhs.port_) {} diff --git a/irohad/validation/CMakeLists.txt b/irohad/validation/CMakeLists.txt index 3c5b1ade2a..2d05d57730 100644 --- a/irohad/validation/CMakeLists.txt +++ b/irohad/validation/CMakeLists.txt @@ -20,8 +20,7 @@ add_library(stateful_validator ) target_link_libraries(stateful_validator rxcpp - model - model_interfaces + shared_model_interfaces logger ) @@ -29,7 +28,7 @@ add_library(chain_validator impl/chain_validator_impl.cpp) target_link_libraries(chain_validator rxcpp - model + shared_model_interfaces logger supermajority_check ) diff --git a/irohad/validation/impl/stateful_validator_impl.cpp b/irohad/validation/impl/stateful_validator_impl.cpp index 979552e70f..0d9b1854f9 100644 --- a/irohad/validation/impl/stateful_validator_impl.cpp +++ b/irohad/validation/impl/stateful_validator_impl.cpp @@ -39,7 +39,7 @@ namespace iroha { [&](const auto &account) { // Check if tx creator has account and has quorum to // execute transaction - return tx.signatures().size() >= account->quorum() + return boost::size(tx.signatures()) >= account->quorum() ? queries.getSignatories(tx.creatorAccountId()) : boost::none; } @@ -93,13 +93,13 @@ namespace iroha { } bool StatefulValidatorImpl::signaturesSubset( - const shared_model::interface::SignatureSetType &signatures, + const shared_model::interface::types::SignatureRangeType &signatures, const std::vector &public_keys) { // TODO 09/10/17 Lebedev: simplify the subset verification IR-510 // #goodfirstissue std::unordered_set txPubkeys; - for (auto sign : signatures) { - txPubkeys.insert(sign->publicKey().toString()); + for (auto &sign : signatures) { + txPubkeys.insert(sign.publicKey().toString()); } return std::all_of(public_keys.begin(), public_keys.end(), diff --git a/irohad/validation/impl/stateful_validator_impl.hpp b/irohad/validation/impl/stateful_validator_impl.hpp index 17cd7a8fb4..cce734f676 100644 --- a/irohad/validation/impl/stateful_validator_impl.hpp +++ b/irohad/validation/impl/stateful_validator_impl.hpp @@ -53,7 +53,7 @@ namespace iroha { * pubkeys */ bool signaturesSubset( - const shared_model::interface::SignatureSetType &signatures, + const shared_model::interface::types::SignatureRangeType &signatures, const std::vector &public_keys); logger::Logger log_; diff --git a/libs/amount/amount.hpp b/libs/amount/amount.hpp index cc27ef9414..3a472d1d9f 100644 --- a/libs/amount/amount.hpp +++ b/libs/amount/amount.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/libs/cache/abstract_cache.hpp b/libs/cache/abstract_cache.hpp index 1e575f9a70..fdab575acf 100644 --- a/libs/cache/abstract_cache.hpp +++ b/libs/cache/abstract_cache.hpp @@ -69,7 +69,7 @@ namespace iroha { * @param value - value to insert */ void addItem(const KeyType &key, const ValueType &value) { - std::lock_guard lock(add_item_mutex_); + std::lock_guard lock(access_mutex_); underlying().addItemImpl(key, value); } @@ -79,6 +79,7 @@ namespace iroha { * @return Optional of ValueType */ boost::optional findItem(const KeyType &key) const { + std::lock_guard lock(access_mutex_); return constUnderlying().findItemImpl(key); } @@ -90,7 +91,7 @@ namespace iroha { return static_cast(*this); } - std::mutex add_item_mutex_; + mutable std::mutex access_mutex_; }; } // namespace cache } // namespace iroha diff --git a/libs/cache/cache.hpp b/libs/cache/cache.hpp index 6dce9e7002..fb52bd9dd2 100644 --- a/libs/cache/cache.hpp +++ b/libs/cache/cache.hpp @@ -25,22 +25,30 @@ namespace iroha { namespace cache { - /** - * Cache for arbitrary types - * @tparam KeyType type of key objects - * @tparam ValueType type of value objects - * @tparam KeyHash hasher for keys - */ - template > - class Cache - : public AbstractCache> { + /** + * Cache for arbitrary types + * @tparam KeyType type of key objects + * @tparam ValueType type of value objects + * @tparam KeyHash hasher for keys + */ + template > + class Cache : public AbstractCache> { public: + Cache(uint32_t max_handler_map_size_high = 20000, + uint32_t max_handler_map_size_low = 10000) + : max_handler_map_size_high_(max_handler_map_size_high), + max_handler_map_size_low_(max_handler_map_size_low) {} + uint32_t getIndexSizeHighImpl() const { - return MAX_HANDLER_MAP_SIZE_HIGH; + return max_handler_map_size_high_; } uint32_t getIndexSizeLowImpl() const { - return MAX_HANDLER_MAP_SIZE_LOW; + return max_handler_map_size_low_; } uint32_t getCacheItemCountImpl() const { @@ -77,8 +85,8 @@ namespace iroha { * TODO 27/10/2017 luckychess Values are quite random and should be tuned * for better performance and may be even move to config IR-579 */ - const uint32_t MAX_HANDLER_MAP_SIZE_HIGH = 20000; - const uint32_t MAX_HANDLER_MAP_SIZE_LOW = 10000; + const uint32_t max_handler_map_size_high_; + const uint32_t max_handler_map_size_low_; }; } // namespace cache } // namespace iroha diff --git a/libs/common/CMakeLists.txt b/libs/common/CMakeLists.txt index fd3718fba2..8ceddbb477 100644 --- a/libs/common/CMakeLists.txt +++ b/libs/common/CMakeLists.txt @@ -4,9 +4,14 @@ add_library(libs_common target_link_libraries(libs_common logger + boost ) add_library(common INTERFACE) + +target_link_libraries(common INTERFACE + boost + ) target_include_directories(common INTERFACE ${PROJECT_SOURCE_DIR}/libs/common ) diff --git a/libs/common/byteutils.hpp b/libs/common/byteutils.hpp index 1920492cff..294988ad44 100644 --- a/libs/common/byteutils.hpp +++ b/libs/common/byteutils.hpp @@ -70,10 +70,11 @@ namespace iroha { for (size_t i = 0; i < result.length(); ++i) { std::string byte = str.substr(i * 2, 2); try { - result.at(i) = std::stoul(byte, nullptr, 16); - } catch (const std::invalid_argument &e) { + result.at(i) = + static_cast(std::stoul(byte, nullptr, 16)); + } catch (const std::invalid_argument &) { return boost::none; - } catch (const std::out_of_range &e) { + } catch (const std::out_of_range &) { return boost::none; } } diff --git a/libs/common/result.hpp b/libs/common/result.hpp index 2cfdf119b0..3e3ed3184a 100644 --- a/libs/common/result.hpp +++ b/libs/common/result.hpp @@ -18,6 +18,9 @@ #ifndef IROHA_RESULT_HPP #define IROHA_RESULT_HPP +#include + +#include #include #include "common/visitor.hpp" @@ -106,8 +109,68 @@ namespace iroha { std::forward(value_func), std::forward(error_func)); } + + /** + * Lazy error AND-chaining + * Works by the following table (aka boolean lazy AND): + * err1 * any -> err1 + * val1 * err2 -> err2 + * val1 * val2 -> val2 + * + * @param new_res second chain argument + * @return new_res if this Result contains a value + * otherwise return this + */ + template + constexpr Result and_res(const Result &new_res) const + noexcept { + return visit_in_place( + *this, + [res = new_res](ValueType) { return res; }, + [](ErrorType err) -> Result { return err; }); + } + + /** + * Lazy error OR-chaining + * Works by the following table (aka boolean lazy OR): + * val1 * any -> val1 + * err1 * val2 -> val2 + * err1 * err2 -> err2 + * + * @param new_res second chain argument + * @return new_res if this Result contains a error + * otherwise return this + */ + template + constexpr Result or_res(const Result &new_res) const + noexcept { + return visit_in_place( + *this, + [](ValueType val) -> Result { return val; }, + [res = new_res](ErrorType) { return res; }); + } }; + template + using ValueOf = typename ResultType::ValueType; + template + using ErrorOf = typename ResultType::ErrorType; + + /** + * Get a new result with the copied value or mapped error + * @param res base Result for getting new one + * @param map callback for error mapping + * @return result with changed error + */ + template + Result map_error(const Result &res, Fn &&map) noexcept { + return visit_in_place(res, + [](Value val) -> Result { return val; }, + [map](Error err) -> Result { + return Error{map(err.error)}; + }); + } + // Factory methods for avoiding type specification template Value makeValue(T &&value) { diff --git a/libs/common/types.hpp b/libs/common/types.hpp index 3967db07b7..e20d01ba98 100644 --- a/libs/common/types.hpp +++ b/libs/common/types.hpp @@ -19,6 +19,7 @@ #define IROHA_COMMON_TYPES_HPP #include +#include #include #include #include diff --git a/libs/crypto/CMakeLists.txt b/libs/crypto/CMakeLists.txt index d853a9dce1..3c0346253f 100644 --- a/libs/crypto/CMakeLists.txt +++ b/libs/crypto/CMakeLists.txt @@ -21,6 +21,6 @@ add_library(keys_manager ) target_link_libraries(keys_manager - ed25519_crypto + shared_model_cryptography logger ) diff --git a/libs/crypto/keys_manager.hpp b/libs/crypto/keys_manager.hpp index ad2337f5bd..523c891fd0 100644 --- a/libs/crypto/keys_manager.hpp +++ b/libs/crypto/keys_manager.hpp @@ -15,15 +15,23 @@ * limitations under the License. */ -#ifndef IROHA_CLI_KEYS_MANAGER_HPP -#define IROHA_CLI_KEYS_MANAGER_HPP +#ifndef IROHA_KEYS_MANAGER_HPP +#define IROHA_KEYS_MANAGER_HPP #include + #include -namespace iroha { - struct keypair_t; +namespace shared_model { + namespace crypto { + class Keypair; + } +} +namespace iroha { + /** + * Interface provides facilities to create and store keypair on disk. + */ class KeysManager { public: virtual ~KeysManager() = default; @@ -34,14 +42,6 @@ namespace iroha { */ virtual bool createKeys() = 0; - /** - * Load plain-text keys associated with the manager, then validate loaded - * keypair by signing and verifying signature of test message - * @return nullopt if no keypair found locally, or verification failure; - * related keypair otherwise - */ - virtual boost::optional loadKeys() = 0; - /** * Create keys a new keypair and store it encrypted on disk * @param pass_phrase is a password for the keys @@ -49,6 +49,14 @@ namespace iroha { */ virtual bool createKeys(const std::string &pass_phrase) = 0; + /** + * Load plain-text keys associated with the manager, then validate loaded + * keypair by signing and verifying signature of test message + * @return nullopt if no keypair found locally, or verification failure; + * related keypair otherwise + */ + virtual boost::optional loadKeys() = 0; + /** * Load encrypted keys associated with the manager, then validate loaded * keypair by signing and verifying signature of test message @@ -56,9 +64,9 @@ namespace iroha { * @return nullopt if no keypair found locally, or verification failure; * related keypair otherwise */ - virtual boost::optional loadKeys( + virtual boost::optional loadKeys( const std::string &pass_phrase) = 0; }; } // namespace iroha -#endif // IROHA_CLI_KEYS_MANAGER_HPP +#endif // IROHA_KEYS_MANAGER_HPP diff --git a/libs/crypto/keys_manager_impl.cpp b/libs/crypto/keys_manager_impl.cpp index f9a9973605..f9d7d968f8 100644 --- a/libs/crypto/keys_manager_impl.cpp +++ b/libs/crypto/keys_manager_impl.cpp @@ -17,37 +17,16 @@ #include "crypto/keys_manager_impl.hpp" -#include -#include #include -#include -#include #include "common/byteutils.hpp" -#include "common/types.hpp" // for keypair_t, pubkey_t, privkey_t -#include "cryptography/ed25519_sha3_impl/internal/ed25519_impl.hpp" -#include "cryptography/ed25519_sha3_impl/internal/sha3_hash.hpp" +#include "cryptography/crypto_provider/crypto_defaults.hpp" + +using namespace shared_model::crypto; using iroha::operator|; namespace iroha { - /** - * Return a function which will try deserialize the value to - * specified field in given keypair - * @tparam T - keypair field type - * @tparam V - value type to deserialize - * @param field - keypair field to be deserialized - * @param value - value to be deserialized - * @return function that will return keypair on success, otherwise nullopt - */ - template - static auto deserializeKeypairField(T keypair_t::*field, const V &value) { - return [=](auto keypair) mutable { - return hexstringToArray(value) - | assignObjectField(keypair, field); - }; - } - /** * Function for the key encryption via XOR * @tparam is a key type @@ -58,8 +37,10 @@ namespace iroha { template static std::string encrypt(const T &key, const std::string &pass_phrase) { std::string ciphertext(key.size(), '\0'); + // pass_size will always be > 0 const auto pass_size = std::max(1ul, pass_phrase.size()); - + // When pass_phrase is empty it, pass_phrase[0] is "\0", so no out_of_range + // exception is possible for (auto i = 0u; i < key.size(); i++) { ciphertext[i] = key[i] ^ pass_phrase[i % pass_size]; } @@ -69,39 +50,23 @@ namespace iroha { /** * Function for XOR decryption */ - static constexpr auto decrypt = encrypt; - - /** - * Return a function which will try to deserialize and then decrypt private - * key via XORing with pass phrase - * @param s is an encrypted data from file - * @param pass_phrase for decryption - * @return function that will set keypair::privkey on successful - * deserialization and decryption - */ - static auto deserializedEncrypted(const std::string &s, - const std::string &pass_phrase) { - constexpr auto size = privkey_t::size(); - - return [=](auto keypair) mutable { - return hexstringToBytestring(s) | - [&](auto binstr) { - return boost::make_optional(decrypt(binstr, pass_phrase)); - } - | stringToBlob | assignObjectField(keypair, &keypair_t::privkey); - }; - } + static constexpr auto decrypt = encrypt; KeysManagerImpl::KeysManagerImpl(const std::string &account_name) : account_name_(std::move(account_name)), log_(logger::log("KeysManagerImpl")) {} - bool KeysManagerImpl::validate(const keypair_t &keypair) const { - std::string test = "12345"; - auto sig = - sign(sha3_256(test).to_string(), keypair.pubkey, keypair.privkey); - if (not verify(sha3_256(test).to_string(), keypair.pubkey, sig)) { - log_->error("key validation failed"); + bool KeysManagerImpl::validate(const Keypair &keypair) const { + try { + auto test = Blob("12345"); + auto sig = DefaultCryptoAlgorithmType::sign(test, keypair); + if (not DefaultCryptoAlgorithmType::verify( + sig, test, keypair.publicKey())) { + log_->error("key validation failed"); + return false; + } + } catch (const BadFormatException &exception) { + log_->error("Cannot validate keyapir: {}", exception.what()); return false; } return true; @@ -118,59 +83,58 @@ namespace iroha { return true; } - boost::optional KeysManagerImpl::loadKeys() { + boost::optional KeysManagerImpl::loadKeys() { std::string pub_key; std::string priv_key; - if (not loadFile(account_name_ + kPubExt, pub_key) - or not loadFile(account_name_ + kPrivExt, priv_key)) + if (not loadFile(account_name_ + kPublicKeyExtension, pub_key) + or not loadFile(account_name_ + kPrivateKeyExtension, priv_key)) return boost::none; - return boost::make_optional(keypair_t()) - | deserializeKeypairField(&keypair_t::pubkey, pub_key) - | deserializeKeypairField(&keypair_t::privkey, priv_key) | - [this](auto keypair) { - return this->validate(keypair) ? boost::make_optional(keypair) - : boost::none; - }; + Keypair keypair = Keypair(PublicKey(Blob::fromHexString(pub_key)), + PrivateKey(Blob::fromHexString(priv_key))); + + return this->validate(keypair) ? boost::make_optional(keypair) + : boost::none; } - boost::optional KeysManagerImpl::loadKeys( + boost::optional KeysManagerImpl::loadKeys( const std::string &pass_phrase) { std::string pub_key; std::string priv_key; - if (not loadFile(account_name_ + kPubExt, pub_key) - or not loadFile(account_name_ + kPrivExt, priv_key)) + if (not loadFile(account_name_ + kPublicKeyExtension, pub_key) + or not loadFile(account_name_ + kPrivateKeyExtension, priv_key)) return boost::none; - return boost::make_optional(keypair_t()) - | deserializeKeypairField(&keypair_t::pubkey, pub_key) - | deserializedEncrypted(priv_key, pass_phrase) | [this](auto keypair) { - return this->validate(keypair) ? boost::make_optional(keypair) - : boost::none; - }; + Keypair keypair = Keypair( + PublicKey(Blob::fromHexString(pub_key)), + PrivateKey(decrypt(Blob::fromHexString(priv_key).blob(), pass_phrase))); + + return this->validate(keypair) ? boost::make_optional(keypair) + : boost::none; } bool KeysManagerImpl::createKeys() { - auto key_pairs = create_keypair(); + Keypair keypair = DefaultCryptoAlgorithmType::generateKeypair(); - auto pub = key_pairs.pubkey.to_hexstring(); - auto priv = key_pairs.privkey.to_hexstring(); + auto pub = keypair.publicKey().hex(); + auto priv = keypair.privateKey().hex(); return store(pub, priv); } bool KeysManagerImpl::createKeys(const std::string &pass_phrase) { - auto key_pairs = create_keypair(); + Keypair keypair = DefaultCryptoAlgorithmType::generateKeypair(); - auto pub = key_pairs.pubkey.to_hexstring(); - auto priv = bytestringToHexstring(encrypt(key_pairs.privkey, pass_phrase)); + auto pub = keypair.publicKey().hex(); + auto priv = bytestringToHexstring( + encrypt(keypair.privateKey().blob(), pass_phrase)); return store(pub, priv); } bool KeysManagerImpl::store(const std::string &pub, const std::string &priv) { - std::ofstream pub_file(account_name_ + kPubExt); - std::ofstream priv_file(account_name_ + kPrivExt); + std::ofstream pub_file(account_name_ + kPublicKeyExtension); + std::ofstream priv_file(account_name_ + kPrivateKeyExtension); if (not pub_file or not priv_file) { return false; } @@ -180,6 +144,6 @@ namespace iroha { return pub_file.good() && priv_file.good(); } - const std::string KeysManagerImpl::kPubExt = ".pub"; - const std::string KeysManagerImpl::kPrivExt = ".priv"; + const std::string KeysManagerImpl::kPublicKeyExtension = ".pub"; + const std::string KeysManagerImpl::kPrivateKeyExtension = ".priv"; } // namespace iroha diff --git a/libs/crypto/keys_manager_impl.hpp b/libs/crypto/keys_manager_impl.hpp index 0dfe8b6c64..05ac450bb1 100644 --- a/libs/crypto/keys_manager_impl.hpp +++ b/libs/crypto/keys_manager_impl.hpp @@ -15,14 +15,13 @@ * limitations under the License. */ -#ifndef IROHA_CLI_KEYS_MANAGER_IMPL_HPP -#define IROHA_CLI_KEYS_MANAGER_IMPL_HPP +#ifndef IROHA_KEYS_MANAGER_IMPL_HPP +#define IROHA_KEYS_MANAGER_IMPL_HPP #include "crypto/keys_manager.hpp" #include - -#include "common/types.hpp" // for keypair_t, pubkey_t, privkey_t +#include "cryptography/keypair.hpp" #include "logger/logger.hpp" namespace iroha { @@ -31,15 +30,15 @@ namespace iroha { public: explicit KeysManagerImpl(const std::string &account_name); - boost::optional loadKeys() override; - boost::optional loadKeys( - const std::string &pass_phrase) override; - bool createKeys() override; bool createKeys(const std::string &pass_phrase) override; - static const std::string kPubExt; - static const std::string kPrivExt; + boost::optional loadKeys() override; + boost::optional loadKeys( + const std::string &pass_phrase) override; + + static const std::string kPublicKeyExtension; + static const std::string kPrivateKeyExtension; private: /** @@ -47,7 +46,7 @@ namespace iroha { * @param keypair - keypair for validation * @return true, if verification of signature is successful */ - bool validate(const iroha::keypair_t &keypair) const; + bool validate(const shared_model::crypto::Keypair &keypair) const; /** * Tries to load file to the resulting string @@ -69,4 +68,4 @@ namespace iroha { logger::Logger log_; }; } // namespace iroha -#endif // IROHA_CLI_KEYS_MANAGER_IMPL_HPP +#endif // IROHA_KEYS_MANAGER_IMPL_HPP diff --git a/libs/generator/generator.cpp b/libs/generator/generator.cpp index a5af77d2f2..7e2ffbcdc1 100644 --- a/libs/generator/generator.cpp +++ b/libs/generator/generator.cpp @@ -19,11 +19,6 @@ namespace generator { - int64_t random_number(int64_t min, int64_t max) { - uint32_t SEED_ = 1337; - return min + (rand_r(&SEED_) % (max - min)); - } - std::string randomString(size_t len) { std::string str(len, 0); std::generate_n( diff --git a/libs/generator/generator.hpp b/libs/generator/generator.hpp index 2d78bd821a..43ac3816b5 100644 --- a/libs/generator/generator.hpp +++ b/libs/generator/generator.hpp @@ -24,11 +24,6 @@ namespace generator { - /** - * returns a number in a range [min, max) - */ - int64_t random_number(int64_t min, int64_t max); - template iroha::blob_t random_blob(size_t seed) { iroha::blob_t v; diff --git a/libs/parser/CMakeLists.txt b/libs/parser/CMakeLists.txt index 43748a1782..d7cc70534f 100644 --- a/libs/parser/CMakeLists.txt +++ b/libs/parser/CMakeLists.txt @@ -1 +1,5 @@ add_library(parser STATIC parser.cpp) + +target_link_libraries( parser + boost + ) diff --git a/patch/close.patch b/patch/close.patch new file mode 100644 index 0000000000..3138b895b6 --- /dev/null +++ b/patch/close.patch @@ -0,0 +1,29 @@ +diff --git a/lib/randombytes/random/random.c b/lib/randombytes/random/random.c +index e5eca79..e6e862e 100644 +--- a/lib/randombytes/random/random.c ++++ b/lib/randombytes/random/random.c +@@ -13,6 +13,7 @@ int randombytes(unsigned char *p, int len) { + while (completed < len) { + ssize_t result = read(source, p + completed, len - completed); + if (result < 0) { ++ close(source); + return ED25519_ERROR; + } + completed += result; +diff --git a/lib/randombytes/urandom/urandom.c b/lib/randombytes/urandom/urandom.c +index ecad2cf..5b4ec0d 100644 +--- a/lib/randombytes/urandom/urandom.c ++++ b/lib/randombytes/urandom/urandom.c +@@ -9,9 +9,12 @@ int randombytes(unsigned char *p, int len) { + } else { + ssize_t result = read(source, p, len); + if (result < 0) { ++ close(source); + return ED25519_ERROR; /* something went wrong */ + } + } + ++ close(source); ++ + return ED25519_SUCCESS; + } diff --git a/patch/fix-protobuf-package-include.patch b/patch/fix-protobuf-package-include.patch deleted file mode 100644 index e749a39d2a..0000000000 --- a/patch/fix-protobuf-package-include.patch +++ /dev/null @@ -1,36 +0,0 @@ -From a277fd504e6e8978bbb5e088cc45961e2a804694 Mon Sep 17 00:00:00 2001 -From: Andrei Lebedev -Date: Sun, 17 Sep 2017 10:34:22 +0800 -Subject: [PATCH] Fix protobuf_generate_grpc_cpp when package is not located in - usr/local - ---- - CMakeLists.txt | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index a5a7fad..4205a7e 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -203,16 +203,17 @@ elseif("${gRPC_PROTOBUF_PROVIDER}" STREQUAL "package") - if(TARGET protobuf::protoc) - set(_gRPC_PROTOBUF_PROTOC protobuf::protoc) - set(_gRPC_PROTOBUF_PROTOC_EXECUTABLE $) -+ get_target_property(PROTOBUF_WELLKNOWN_IMPORT_DIR protobuf::libprotoc INTERFACE_INCLUDE_DIRECTORIES) - else() - set(_gRPC_PROTOBUF_PROTOC ${PROTOBUF_PROTOC_EXECUTABLE}) - set(_gRPC_PROTOBUF_PROTOC_EXECUTABLE ${PROTOBUF_PROTOC_EXECUTABLE}) -+ set(PROTOBUF_WELLKNOWN_IMPORT_DIR ${PROTOBUF_INCLUDE_DIRS}) - endif() - set(_gRPC_FIND_PROTOBUF "if(NOT Protobuf_FOUND AND NOT PROTOBUF_FOUND)\n find_package(Protobuf ${gRPC_PROTOBUF_PACKAGE_TYPE})\nendif()") - endif() - if(PROTOBUF_FOUND) - include_directories(${PROTOBUF_INCLUDE_DIRS}) - endif() -- set(PROTOBUF_WELLKNOWN_IMPORT_DIR /usr/local/include) - endif() - - if("${gRPC_SSL_PROVIDER}" STREQUAL "module") --- -2.7.4 - diff --git a/schema/block.proto b/schema/block.proto index 3b37491963..7917d2706a 100644 --- a/schema/block.proto +++ b/schema/block.proto @@ -12,12 +12,11 @@ message Header { message Payload { repeated Command commands = 1; string creator_account_id = 2; - uint64 tx_counter = 3; - uint64 created_time = 4; + uint64 created_time = 3; } Payload payload = 1; - repeated Signature signature = 2; + repeated Signature signatures = 2; } message Block { diff --git a/schema/endpoint.proto b/schema/endpoint.proto index 42beb2e484..c4320302af 100644 --- a/schema/endpoint.proto +++ b/schema/endpoint.proto @@ -20,6 +20,7 @@ enum TxStatus { message ToriiResponse { TxStatus tx_status = 1; bytes tx_hash = 2; + string error_message = 3; } message TxStatusRequest{ diff --git a/schema/primitive.proto b/schema/primitive.proto index 4f226b9f5d..e930eb7516 100644 --- a/schema/primitive.proto +++ b/schema/primitive.proto @@ -63,13 +63,14 @@ enum RolePermission { can_get_domain_acc_ast_txs = 34; can_get_my_txs = 35; can_get_all_txs = 36; + can_get_blocks = 42; // Grant permissions - can_grant_can_set_quorum = 37; - can_grant_can_add_signatory = 38; - can_grant_can_remove_signatory = 39; - can_grant_can_transfer = 40; - can_grant_can_set_detail = 41; + can_grant_can_set_my_quorum = 37; + can_grant_can_add_my_signatory = 38; + can_grant_can_remove_my_signatory = 39; + can_grant_can_transfer_my_assets = 40; + can_grant_can_set_my_account_detail = 41; } enum GrantablePermission { diff --git a/schema/responses.proto b/schema/responses.proto index 88c64101ad..2818f8c19b 100644 --- a/schema/responses.proto +++ b/schema/responses.proto @@ -67,6 +67,7 @@ message ErrorResponse { NO_ROLES = 8; // when there are no roles defined in the system } Reason reason = 1; + string message = 2; } message SignatoriesResponse { @@ -89,5 +90,5 @@ message QueryResponse { RolesResponse roles_response = 8; RolePermissionsResponse role_permissions_response = 9; } - bytes query_hash = 10; + string query_hash = 10; } diff --git a/shared_model/CMakeLists.txt b/shared_model/CMakeLists.txt index 080f740356..bc1ade0296 100644 --- a/shared_model/CMakeLists.txt +++ b/shared_model/CMakeLists.txt @@ -25,9 +25,11 @@ include_directories( if (NOT IROHA_ROOT_PROJECT) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) - set(CMAKE_CXX_FLAGS "-std=c++14 -Wall") - set(CMAKE_CXX_FLAGS_RELEASE "-O3") - set(CMAKE_CXX_FLAGS_DEBUG "-g -Wextra -Wno-unused-parameter -Wno-deprecated-declarations -O0") + if (NOT MSVC) + set(CMAKE_CXX_FLAGS "-std=c++14 -Wall") + set(CMAKE_CXX_FLAGS_RELEASE "-O3") + set(CMAKE_CXX_FLAGS_DEBUG "-g -Wextra -Wno-unused-parameter -Wno-deprecated-declarations -O0") + endif () if(CMAKE_GENERATOR MATCHES "Make") set(MAKE "$(MAKE)") @@ -64,6 +66,7 @@ add_subdirectory(builders) add_subdirectory(converters) add_subdirectory(cryptography) add_subdirectory(interfaces) +add_subdirectory(utils) add_subdirectory(validators) if (NOT IROHA_ROOT_PROJECT) diff --git a/shared_model/backend/protobuf/CMakeLists.txt b/shared_model/backend/protobuf/CMakeLists.txt index 0fdc3ad7dc..2967ea3688 100644 --- a/shared_model/backend/protobuf/CMakeLists.txt +++ b/shared_model/backend/protobuf/CMakeLists.txt @@ -4,6 +4,6 @@ add_library(shared_model_proto_backend target_link_libraries(shared_model_proto_backend schema - model_interfaces + shared_model_interfaces iroha_amount ) diff --git a/shared_model/backend/protobuf/block.hpp b/shared_model/backend/protobuf/block.hpp index 13f7a1c865..826bd59a3e 100644 --- a/shared_model/backend/protobuf/block.hpp +++ b/shared_model/backend/protobuf/block.hpp @@ -64,7 +64,7 @@ namespace shared_model { return *blob_; } - const interface::SignatureSetType &signatures() const override { + interface::types::SignatureRangeType signatures() const override { return *signatures_; } @@ -75,11 +75,11 @@ namespace shared_model { const crypto::PublicKey &public_key) override { // if already has such signature if (std::find_if(signatures_->begin(), - signatures_->end(), - [&signed_blob, &public_key](auto signature) { - return signature->signedData() == signed_blob - and signature->publicKey() == public_key; - }) != signatures_->end()) { + signatures_->end(), + [&public_key](const auto &signature) { + return signature.publicKey() == public_key; + }) + != signatures_->end()) { return false; } @@ -91,11 +91,6 @@ namespace shared_model { return true; } - bool clearSignatures() override { - signatures_->clear(); - return (signatures_->size() == 0); - } - interface::types::TimestampType createdTime() const override { return payload_.created_time(); } @@ -131,11 +126,10 @@ namespace shared_model { return interface::types::HashType(proto_->payload().prev_block_hash()); }}; - const Lazy signatures_{[this] { - interface::SignatureSetType sigs; + const Lazy> signatures_{[this] { + SignatureSetType sigs; for (const auto &sig : proto_->signatures()) { - auto curr = detail::makePolymorphic(sig); - sigs.insert(curr); + sigs.emplace(sig); } return sigs; }}; diff --git a/shared_model/backend/protobuf/common_objects/amount.hpp b/shared_model/backend/protobuf/common_objects/amount.hpp index c4bb090f0d..0817a2000c 100644 --- a/shared_model/backend/protobuf/common_objects/amount.hpp +++ b/shared_model/backend/protobuf/common_objects/amount.hpp @@ -58,10 +58,20 @@ namespace shared_model { ValueType &value, const boost::multiprecision::uint256_t &amount) noexcept { constexpr auto offset = 64u; - value.set_first((amount >> offset * 3).template convert_to()); - value.set_second((amount >> offset * 2).template convert_to()); - value.set_third((amount >> offset).template convert_to()); - value.set_fourth(amount.template convert_to()); + constexpr boost::multiprecision::uint256_t mask_bits = + std::numeric_limits::max(); + auto convert = [&](auto i) { + // Select two middle bits from 011011 and offset = 2 + // 011011 >> (2 * 1) = 000110 + // Have to mask 2 high bits to prevent any overflows + // 000110 & 000011 = 000010 + return ((amount >> (offset * i)) & mask_bits) + .template convert_to(); + }; + value.set_first(convert(3)); + value.set_second(convert(2)); + value.set_third(convert(1)); + value.set_fourth(convert(0)); } class Amount final : public CopyableProto - -#include "backend/protobuf/block.hpp" -#include "backend/protobuf/proposal.hpp" -#include "backend/protobuf/queries/proto_query.hpp" -#include "backend/protobuf/query_responses/proto_query_response.hpp" -#include "backend/protobuf/transaction.hpp" -#include "builders/protobuf/proposal.hpp" -#include "model/converters/pb_block_factory.hpp" -#include "model/converters/pb_command_factory.hpp" -#include "model/converters/pb_query_factory.hpp" -#include "model/converters/pb_query_response_factory.hpp" -#include "model/converters/pb_transaction_factory.hpp" - -#include "backend/protobuf/common_objects/account.hpp" -#include "backend/protobuf/common_objects/account_asset.hpp" -#include "backend/protobuf/common_objects/asset.hpp" -#include "backend/protobuf/common_objects/domain.hpp" -#include "backend/protobuf/common_objects/peer.hpp" -#include "model/converters/pb_common.hpp" - -namespace shared_model { - namespace proto { - - inline static shared_model::proto::Block from_old( - const iroha::model::Block &block) { - return shared_model::proto::Block( - iroha::model::converters::PbBlockFactory().serialize(block)); - } - - inline static shared_model::proto::Transaction from_old( - const iroha::model::Transaction &tx) { - return shared_model::proto::Transaction( - iroha::model::converters::PbTransactionFactory().serialize(tx)); - } - - inline static shared_model::proto::Query from_old( - std::shared_ptr qry) { - return shared_model::proto::Query( - *iroha::model::converters::PbQueryFactory().serialize(qry)); - } - - inline static shared_model::proto::Proposal from_old( - const iroha::model::Proposal &proposal) { - return shared_model::proto::ProposalBuilder() - .height(proposal.height) - .createdTime(proposal.created_time) - .transactions(proposal.transactions - | boost::adaptors::transformed( - [](auto &tx) { return from_old(tx); })) - .build(); - } - - inline static shared_model::proto::QueryResponse from_old( - std::shared_ptr queryResponse) { - auto proto_resp = - *iroha::model::converters::PbQueryResponseFactory().serialize( - queryResponse); - auto res = shared_model::proto::QueryResponse(std::move(proto_resp)); - auto hash = res.queryHash(); - return shared_model::proto::QueryResponse( - *iroha::model::converters::PbQueryResponseFactory().serialize( - queryResponse)); - } - - inline static shared_model::proto::Account from_old( - const iroha::model::Account &account) { - return shared_model::proto::Account( - iroha::model::converters::serializeAccount(account)); - } - - inline static shared_model::proto::Peer from_old( - const iroha::model::Peer &peer) { - return shared_model::proto::Peer( - iroha::model::converters::serializePeer(peer)); - } - - inline static shared_model::proto::Asset from_old( - const iroha::model::Asset &asset) { - return shared_model::proto::Asset( - iroha::model::converters::serializeAsset(asset)); - } - - inline static shared_model::proto::AccountAsset from_old( - const iroha::model::AccountAsset &account_asset) { - return shared_model::proto::AccountAsset( - iroha::model::converters::serializeAccountAsset(account_asset)); - } - - inline static shared_model::proto::Domain from_old( - const iroha::model::Domain &domain) { - return shared_model::proto::Domain( - iroha::model::converters::serializeDomain(domain)); - } - - inline static shared_model::proto::Amount from_old( - const iroha::Amount &amount) { - return shared_model::proto::Amount( - iroha::model::converters::serializeAmount(amount)); - } - - inline static shared_model::proto::Command from_old( - const iroha::model::Command &command) { - return shared_model::proto::Command( - iroha::model::converters::PbCommandFactory().serializeAbstractCommand( - command)); - } - - } // namespace proto -} // namespace shared_model - -#endif // DISABLE_BACKWARD -#endif // SHARED_MODEL_FROM_OLD_HPP diff --git a/shared_model/backend/protobuf/queries/proto_query.hpp b/shared_model/backend/protobuf/queries/proto_query.hpp index ba4cb5686c..d23afeb14a 100644 --- a/shared_model/backend/protobuf/queries/proto_query.hpp +++ b/shared_model/backend/protobuf/queries/proto_query.hpp @@ -19,7 +19,6 @@ #define IROHA_SHARED_MODEL_PROTO_QUERY_HPP #include - #include "backend/protobuf/common_objects/signature.hpp" #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/queries/query.hpp" @@ -119,7 +118,7 @@ namespace shared_model { } // ------------------------| Signable override |------------------------- - const interface::SignatureSetType &signatures() const override { + interface::types::SignatureRangeType signatures() const override { return *signatures_; } @@ -135,11 +134,6 @@ namespace shared_model { return true; } - bool clearSignatures() override { - signatures_->clear(); - return (signatures_->size() == 0); - } - interface::types::TimestampType createdTime() const override { return proto_->payload().created_time(); } @@ -156,10 +150,10 @@ namespace shared_model { const Lazy payload_{ [this] { return makeBlob(proto_->payload()); }}; - const Lazy signatures_{[this] { - interface::SignatureSetType set; + const Lazy> signatures_{[this] { + SignatureSetType set; if (proto_->has_signature()) { - set.emplace(new Signature(proto_->signature())); + set.emplace(proto_->signature()); } return set; }}; diff --git a/shared_model/backend/protobuf/query_responses/proto_query_response.hpp b/shared_model/backend/protobuf/query_responses/proto_query_response.hpp index f41a55f3c1..a7b626e5f2 100644 --- a/shared_model/backend/protobuf/query_responses/proto_query_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_query_response.hpp @@ -39,10 +39,9 @@ template auto loadQueryResponse(Archive &&ar) { int which = ar.GetDescriptor()->FindFieldByNumber(ar.response_case())->index(); - return shared_model::detail::variant_impl:: - template load(std::forward(ar), - which); + return shared_model::detail::variant_impl::template load< + shared_model::interface::QueryResponse::QueryResponseVariantType>( + std::forward(ar), which); } namespace shared_model { @@ -97,8 +96,10 @@ namespace shared_model { return loadQueryResponse(*proto_); }}; - const Lazy hash_{ - [this] { return interface::types::HashType(proto_->query_hash()); }}; + const Lazy hash_{[this] { + return interface::types::HashType( + iroha::hexstringToBytestring(proto_->query_hash()).get()); + }}; }; } // namespace proto } // namespace shared_model diff --git a/shared_model/backend/protobuf/transaction.hpp b/shared_model/backend/protobuf/transaction.hpp index 76a5caca49..41a6517cdd 100644 --- a/shared_model/backend/protobuf/transaction.hpp +++ b/shared_model/backend/protobuf/transaction.hpp @@ -21,7 +21,6 @@ #include "interfaces/transaction.hpp" #include - #include "backend/protobuf/commands/proto_command.hpp" #include "backend/protobuf/common_objects/signature.hpp" #include "block.pb.h" @@ -46,10 +45,6 @@ namespace shared_model { return payload_.creator_account_id(); } - interface::types::CounterType transactionCounter() const override { - return payload_.tx_counter(); - } - const Transaction::CommandsType &commands() const override { return *commands_; } @@ -62,7 +57,7 @@ namespace shared_model { return *blobTypePayload_; } - const interface::SignatureSetType &signatures() const override { + interface::types::SignatureRangeType signatures() const override { return *signatures_; } @@ -71,15 +66,14 @@ namespace shared_model { // if already has such signature if (std::find_if(signatures_->begin(), signatures_->end(), - [&signed_blob, &public_key](auto signature) { - return signature->signedData() == signed_blob - and signature->publicKey() == public_key; + [&public_key](const auto &signature) { + return signature.publicKey() == public_key; }) != signatures_->end()) { return false; } - auto sig = proto_->add_signature(); + auto sig = proto_->add_signatures(); sig->set_signature(crypto::toBinaryString(signed_blob)); sig->set_pubkey(crypto::toBinaryString(public_key)); @@ -87,11 +81,6 @@ namespace shared_model { return true; } - bool clearSignatures() override { - signatures_->clear(); - return (signatures_->size() == 0); - } - interface::types::TimestampType createdTime() const override { return payload_.created_time(); } @@ -118,11 +107,11 @@ namespace shared_model { const Lazy blobTypePayload_{ [this] { return makeBlob(payload_); }}; - const Lazy signatures_{[this] { - return boost::accumulate(proto_->signature(), - interface::SignatureSetType{}, + const Lazy> signatures_{[this] { + return boost::accumulate(proto_->signatures(), + SignatureSetType{}, [](auto &&acc, const auto &sig) { - acc.emplace(new Signature(sig)); + acc.emplace(sig); return std::forward(acc); }); }}; diff --git a/shared_model/bindings/CMakeLists.txt b/shared_model/bindings/CMakeLists.txt index 4e21695fe4..fae93bdca0 100644 --- a/shared_model/bindings/CMakeLists.txt +++ b/shared_model/bindings/CMakeLists.txt @@ -37,56 +37,66 @@ if (SWIG_PYTHON OR SWIG_JAVA OR SWIG_CSHARP OR SWIG_NODE) if (SHARED_MODEL_DISABLE_COMPATIBILITY) set(CMAKE_SWIG_FLAGS "-DDISABLE_BACKWARD") endif() - include_directories(${CMAKE_CURRENT_SOURCE_DIR}) set_source_files_properties(bindings.i PROPERTIES CPLUSPLUS ON) set_property(GLOBAL PROPERTY SWIG_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) add_dependencies(bindings swig) + + macro (myswig_add_library target) + swig_add_library(${ARGV}) + # get internal dependencies + get_target_property(dependencies + ${SWIG_MODULE_${target}_REAL_NAME} + MANUALLY_ADDED_DEPENDENCIES) + if (dependencies) + # add external project dependency on internal targets + foreach (dependency IN LISTS dependencies) + add_dependencies(${dependency} swig) + endforeach() + endif() + endmacro() endif() if (SWIG_PYTHON) if(SUPPORT_PYTHON2) find_package(PythonLibs 2.7 REQUIRED) else() - find_package(PythonLibs 3.6 REQUIRED) + find_package(PythonLibs 3.5 REQUIRED) endif() - # path to where Python.h is found - include_directories(${PYTHON_INCLUDE_DIRS}) - if (${CMAKE_SYSTEM_NAME} STREQUAL Darwin) set(MAC_OPTS "-flat_namespace -undefined suppress") endif() - swig_add_library(iroha LANGUAGE python SOURCES bindings.i) + myswig_add_library(iroha LANGUAGE python SOURCES bindings.i) swig_link_libraries(iroha ${Python_LIBRARIES} bindings ${MAC_OPTS}) add_custom_target(irohapy DEPENDS ${SWIG_MODULE_iroha_REAL_NAME}) - + # path to where Python.h is found + target_include_directories(${SWIG_MODULE_iroha_REAL_NAME} PUBLIC + ${PYTHON_INCLUDE_DIRS} + ) endif() if (SWIG_JAVA) find_package(JNI REQUIRED) - # the include path to jni.h - include_directories(${JAVA_INCLUDE_PATH}) - # the include path to jni_md.h - include_directories(${JAVA_INCLUDE_PATH2}) - swig_add_library(irohajava LANGUAGE java SOURCES bindings.i) + myswig_add_library(irohajava LANGUAGE java SOURCES bindings.i) swig_link_libraries(irohajava ${Java_LIBRARIES} bindings) + # the include path to jni.h and jni_md.h + target_include_directories(${SWIG_MODULE_irohajava_REAL_NAME} PUBLIC + ${JAVA_INCLUDE_PATH} + ${JAVA_INCLUDE_PATH2} + ) endif() if (SWIG_CSHARP) - swig_add_library(libirohacs LANGUAGE csharp SOURCES bindings.i) + myswig_add_library(libirohacs LANGUAGE csharp SOURCES bindings.i) swig_link_libraries(libirohacs bindings) add_custom_target(irohacs DEPENDS ${SWIG_MODULE_libirohacs_REAL_NAME}) endif() if (SWIG_NODE) find_package (nodejs REQUIRED) - include_directories ( - ${NODEJS_INCLUDE_DIRS} - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) set (V8_VERSION_HEX 0x0${V8_VERSION_MAJOR}${V8_VERSION_MINOR}${V8_VERSION_PATCH}) string (LENGTH "${V8_VERSION_HEX}" V8_VERSION_HEX_length) @@ -103,11 +113,14 @@ if (SWIG_NODE) set_property(SOURCE bindings.i PROPERTY SWIG_FLAGS "-node" "-DV8_VERSION=${V8_VERSION_HEX}") # Build SWIG library always statically for the subsequent assembly by GYP - swig_add_library(irohanode + myswig_add_library(irohanode TYPE STATIC LANGUAGE javascript SOURCES bindings.i ) set_target_properties(irohanode PROPERTIES PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX}) target_link_libraries(irohanode bindings ${MAC_OPTS}) + target_include_directories(${SWIG_MODULE_irohanode_REAL_NAME} PUBLIC + ${NODEJS_INCLUDE_DIRS} + ) endif() diff --git a/shared_model/bindings/model_transaction_builder.cpp b/shared_model/bindings/model_transaction_builder.cpp index 45dad87608..2fbd77c656 100644 --- a/shared_model/bindings/model_transaction_builder.cpp +++ b/shared_model/bindings/model_transaction_builder.cpp @@ -20,7 +20,7 @@ namespace shared_model { namespace bindings { ModelTransactionBuilder::ModelTransactionBuilder() { - *this = creatorAccountId("").createdTime(0).txCounter(0); + *this = creatorAccountId("").createdTime(0); } ModelTransactionBuilder ModelTransactionBuilder::creatorAccountId( @@ -28,11 +28,6 @@ namespace shared_model { return ModelTransactionBuilder(builder_.creatorAccountId(account_id)); } - ModelTransactionBuilder ModelTransactionBuilder::txCounter( - interface::types::CounterType tx_counter) { - return ModelTransactionBuilder(builder_.txCounter(tx_counter)); - } - ModelTransactionBuilder ModelTransactionBuilder::createdTime( interface::types::TimestampType created_time) { return ModelTransactionBuilder(builder_.createdTime(created_time)); diff --git a/shared_model/bindings/model_transaction_builder.hpp b/shared_model/bindings/model_transaction_builder.hpp index 55dda0687c..ce9d96cf30 100644 --- a/shared_model/bindings/model_transaction_builder.hpp +++ b/shared_model/bindings/model_transaction_builder.hpp @@ -46,14 +46,6 @@ namespace shared_model { ModelTransactionBuilder creatorAccountId( const interface::types::AccountIdType &account_id); - /** - * Sets transaction counter field - * @param tx_counter - transaction counter - * @return builder with tx_counter field appended - */ - ModelTransactionBuilder txCounter( - interface::types::CounterType tx_counter); - /** * Sets time of creation * @param created_time - time of creation diff --git a/shared_model/builders/CMakeLists.txt b/shared_model/builders/CMakeLists.txt index 4d83299aed..f9e2bc0efc 100644 --- a/shared_model/builders/CMakeLists.txt +++ b/shared_model/builders/CMakeLists.txt @@ -18,4 +18,5 @@ add_library(shared_model_default_builders INTERFACE) target_link_libraries(shared_model_default_builders INTERFACE shared_model_proto_builders + shared_model_stateless_validation ) diff --git a/shared_model/builders/common_objects/common.hpp b/shared_model/builders/common_objects/common.hpp index 275c1bc407..ffd44669f6 100644 --- a/shared_model/builders/common_objects/common.hpp +++ b/shared_model/builders/common_objects/common.hpp @@ -65,6 +65,8 @@ namespace shared_model { } if (answer) { + // TODO 15.04.2018 x3medima17 IR-1240: rework with std::string instead + // of pointer to string return iroha::expected::makeError( std::make_shared(answer.reason())); } diff --git a/shared_model/builders/protobuf/CMakeLists.txt b/shared_model/builders/protobuf/CMakeLists.txt index 92d1e494e7..a7f4cdc027 100644 --- a/shared_model/builders/protobuf/CMakeLists.txt +++ b/shared_model/builders/protobuf/CMakeLists.txt @@ -19,6 +19,6 @@ add_library(shared_model_proto_builders target_link_libraries( shared_model_proto_builders - model_interfaces + shared_model_interfaces shared_model_proto_backend ) diff --git a/shared_model/builders/protobuf/builder_templates/query_response_template.hpp b/shared_model/builders/protobuf/builder_templates/query_response_template.hpp index 048268d768..6aa808fda4 100644 --- a/shared_model/builders/protobuf/builder_templates/query_response_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/query_response_template.hpp @@ -205,7 +205,7 @@ namespace shared_model { auto queryHash(const interface::types::HashType &query_hash) const { return transform([&](auto &proto_query_response) { proto_query_response.set_query_hash( - crypto::toBinaryString(query_hash)); + query_hash.hex()); }); } diff --git a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp index 1440e29366..2387a1a120 100644 --- a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp @@ -53,7 +53,6 @@ namespace shared_model { enum RequiredFields { Command, CreatorAccountId, - TxCounter, CreatedTime, TOTAL }; @@ -107,12 +106,6 @@ namespace shared_model { }); } - auto txCounter(interface::types::CounterType tx_counter) const { - return transform([&](auto &tx) { - tx.mutable_payload()->set_tx_counter(tx_counter); - }); - } - auto createdTime(interface::types::TimestampType created_time) const { return transform([&](auto &tx) { tx.mutable_payload()->set_created_time(created_time); diff --git a/shared_model/cmake/dependencies.cmake b/shared_model/cmake/dependencies.cmake index 43d574a35f..e0d0c2f10f 100644 --- a/shared_model/cmake/dependencies.cmake +++ b/shared_model/cmake/dependencies.cmake @@ -13,7 +13,21 @@ find_package(Threads REQUIRED) # protobuf # ################################ option(FIND_PROTOBUF "Try to find protobuf in system" ON) -find_package(protobuf) +if (MSVC) + set(CMAKE_MODULE_PATH "") + find_package(Protobuf REQUIRED) + add_library(protobuf INTERFACE) + target_link_libraries(protobuf INTERFACE + protobuf::libprotobuf + ) + add_executable(protoc IMPORTED) + set_target_properties(protoc PROPERTIES + IMPORTED_LOCATION ${Protobuf_PROTOC_EXECUTABLE} + ) + set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules) +else () + find_package(protobuf) +endif() ########################## # boost # @@ -34,5 +48,17 @@ find_package(ed25519) ########################## # testing is an option. Look at the main CMakeLists.txt for details. if (TESTING) - find_package(gtest) + if (MSVC) + set(CMAKE_MODULE_PATH "") + find_package(GTest REQUIRED) + add_library(gtest INTERFACE) + add_library(gmock INTERFACE) + + target_link_libraries(gtest INTERFACE + GTest::GTest GTest::Main + ) + set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules) + else () + find_package(gtest) + endif() endif () diff --git a/irohad/model/generators/impl/signature_generator.cpp b/shared_model/cryptography/default_hash_provider.hpp similarity index 51% rename from irohad/model/generators/impl/signature_generator.cpp rename to shared_model/cryptography/default_hash_provider.hpp index e5ea681e27..ce07b4b574 100644 --- a/irohad/model/generators/impl/signature_generator.cpp +++ b/shared_model/cryptography/default_hash_provider.hpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,21 +15,16 @@ * limitations under the License. */ -#include "model/generators/signature_generator.hpp" +#ifndef IROHA_DEFAULT_HASH_PROVIDER_HPP +#define IROHA_DEFAULT_HASH_PROVIDER_HPP -namespace iroha { - namespace model { - namespace generators { +#include "cryptography/hash_providers/sha3_256.hpp" - Signature generateSignature(size_t seed) { - Signature sign; - // sign.pubkey - sign.pubkey = generator::random_blob(seed); - sign.signature = generator::random_blob( - generator::random_number(0, seed)); - return sign; - } +namespace shared_model { + namespace crypto { + // Default class that provides hashing functionality + using DefaultHashProvider = shared_model::crypto::Sha3_256; + } // namespace crypto +} // namespace shared_model - } // namespace generators - } // namespace model -} // namespace iroha +#endif // IROHA_DEFAULT_HASH_PROVIDER_HPP diff --git a/shared_model/interfaces/CMakeLists.txt b/shared_model/interfaces/CMakeLists.txt index 1a05fc4b8e..8a610cbb34 100644 --- a/shared_model/interfaces/CMakeLists.txt +++ b/shared_model/interfaces/CMakeLists.txt @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -add_library(model_interfaces +add_library(shared_model_interfaces impl.cpp ) -target_link_libraries(model_interfaces +target_link_libraries(shared_model_interfaces old_model shared_model_cryptography ${Boost_LIBRARIES} diff --git a/shared_model/interfaces/base/model_primitive.hpp b/shared_model/interfaces/base/model_primitive.hpp index f0128bc533..625d2a12aa 100644 --- a/shared_model/interfaces/base/model_primitive.hpp +++ b/shared_model/interfaces/base/model_primitive.hpp @@ -18,6 +18,8 @@ #ifndef IROHA_MODEL_PRIMITIVE_HPP #define IROHA_MODEL_PRIMITIVE_HPP +#include + #include "utils/string_builder.hpp" #include "common/cloneable.hpp" diff --git a/shared_model/interfaces/base/signable.hpp b/shared_model/interfaces/base/signable.hpp index f65164f11a..51f7f10d90 100644 --- a/shared_model/interfaces/base/signable.hpp +++ b/shared_model/interfaces/base/signable.hpp @@ -18,10 +18,11 @@ #ifndef IROHA_SIGNABLE_HPP #define IROHA_SIGNABLE_HPP +#include #include +#include -#include "cryptography/hash_providers/sha3_256.hpp" -#include "interfaces/common_objects/signable_hash.hpp" +#include "cryptography/default_hash_provider.hpp" #include "interfaces/common_objects/signature.hpp" #include "interfaces/common_objects/types.hpp" #include "utils/string_builder.hpp" @@ -48,20 +49,19 @@ namespace shared_model { #ifndef DISABLE_BACKWARD template + typename HashProvider = crypto::DefaultHashProvider> class Signable : public Primitive { #else template class Signable : public ModelPrimitive { #endif - public: - using HashProviderType = HashProvider; + public: /** * @return attached signatures */ - virtual const SignatureSetType &signatures() const = 0; + virtual types::SignatureRangeType signatures() const = 0; /** * Attach signature to object @@ -71,12 +71,6 @@ namespace shared_model { virtual bool addSignature(const crypto::Signed &signed_blob, const crypto::PublicKey &public_key) = 0; - /** - * Clear object's signatures - * @return true, if signatures were cleared - */ - virtual bool clearSignatures() = 0; - /** * @return time of creation */ @@ -99,13 +93,13 @@ namespace shared_model { */ bool operator==(const Model &rhs) const override { return this->hash() == rhs.hash() - and this->signatures() == rhs.signatures() + and boost::equal(this->signatures(), rhs.signatures()) and this->createdTime() == rhs.createdTime(); } const types::HashType &hash() const { if (hash_ == boost::none) { - hash_.emplace(HashProviderType::makeHash(payload())); + hash_.emplace(HashProvider::makeHash(payload())); } return *hash_; } @@ -117,10 +111,47 @@ namespace shared_model { .init("Signable") .append("created_time", std::to_string(createdTime())) .appendAll(signatures(), - [](auto &signature) { return signature->toString(); }) + [](auto &signature) { return signature.toString(); }) .finalize(); } + protected: + /** + * Type of set of signatures + * + * Note: we can't use const SignatureType due to unordered_set + * limitations: it requires to have write access for elements for some + * internal operations. + */ + + protected: + class SignatureSetTypeOps { + public: + /** + * @param sig is item to find hash from + * @return calculated hash of public key + */ + template + size_t operator()(const T &sig) const { + return std::hash{}(sig.publicKey().hex()); + } + + /** + * Function for set elements uniqueness by public key + * @param lhs + * @param rhs + * @return true, if public keys are the same + */ + template + bool operator()(const T &lhs, const T &rhs) const { + return lhs.publicKey() == rhs.publicKey(); + } + }; + + template + using SignatureSetType = + std::unordered_set; + private: mutable boost::optional hash_; }; diff --git a/shared_model/interfaces/common_objects/account.hpp b/shared_model/interfaces/common_objects/account.hpp index 545d5aa335..3e22a276b7 100644 --- a/shared_model/interfaces/common_objects/account.hpp +++ b/shared_model/interfaces/common_objects/account.hpp @@ -65,6 +65,7 @@ namespace shared_model { .append("accountId", accountId()) .append("domainId", domainId()) .append("quorum", std::to_string(quorum())) + .append("json", jsonData()) .finalize(); } diff --git a/shared_model/interfaces/common_objects/signable_hash.hpp b/shared_model/interfaces/common_objects/signable_hash.hpp deleted file mode 100644 index 40c091533b..0000000000 --- a/shared_model/interfaces/common_objects/signable_hash.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. - */ - -#ifndef IROHA_SHARED_MODEL_SIGNABLE_HASH_HPP -#define IROHA_SHARED_MODEL_SIGNABLE_HASH_HPP - -#include -#include - -#include "interfaces/common_objects/signature.hpp" -#include "interfaces/common_objects/types.hpp" - -namespace shared_model { - - namespace interface { - /** - * Hash class for SigWrapper type. It's required since std::unordered_set - * uses hash inside and it should be declared explicitly for user-defined - * types. - */ - class SignableHash { - public: - /** - * Operator which actually calculates hash. Uses boost::hash_combine to - * calculate hash from several fields. - * @param sig - item to find hash from - * @return calculated hash - */ - size_t operator()(const types::SignatureType &sig) const { - std::size_t seed = 0; - boost::hash_combine(seed, sig->publicKey().blob()); - boost::hash_combine(seed, sig->signedData().blob()); - return seed; - } - }; - /** - * Type of set of signatures - * - * Note: we can't use const SignatureType due to unordered_set - * limitations: it requires to have write access for elements for some - * internal operations. - */ - using SignatureSetType = - std::unordered_set; - } // namespace interface -} // namespace shared_model - -#endif // IROHA_SHARED_MODEL_SIGNABLE_HASH_HPP diff --git a/shared_model/interfaces/common_objects/signature.hpp b/shared_model/interfaces/common_objects/signature.hpp index f0b43a1a57..f3d5ff0e3f 100644 --- a/shared_model/interfaces/common_objects/signature.hpp +++ b/shared_model/interfaces/common_objects/signature.hpp @@ -58,8 +58,7 @@ namespace shared_model { virtual const SignedType &signedData() const = 0; bool operator==(const Signature &rhs) const override { - return publicKey() == rhs.publicKey() - and signedData() == rhs.signedData(); + return publicKey() == rhs.publicKey(); } #ifndef DISABLE_BACKWARD diff --git a/shared_model/interfaces/common_objects/types.hpp b/shared_model/interfaces/common_objects/types.hpp index f175c9d8e0..b2d4b47227 100644 --- a/shared_model/interfaces/common_objects/types.hpp +++ b/shared_model/interfaces/common_objects/types.hpp @@ -18,6 +18,7 @@ #ifndef IROHA_SHARED_MODEL_TYPES_HPP #define IROHA_SHARED_MODEL_TYPES_HPP +#include #include #include #include @@ -64,10 +65,9 @@ namespace shared_model { using PermissionSetType = std::set; /// Type of Quorum used in transaction and set quorum using QuorumType = uint32_t; - /// Type of transaction signature - // TODO Alexey Chernyshov 2018-03-28 - remove PolymorphicWrapper here - // https://soramitsu.atlassian.net/browse/IR-1175 - using SignatureType = detail::PolymorphicWrapper; + /// Type of signature range, which returns when signatures are invoked + using SignatureRangeType = boost::any_range; /// Type of timestamp using TimestampType = uint64_t; /// Type of peer address diff --git a/shared_model/interfaces/iroha_internal/block.hpp b/shared_model/interfaces/iroha_internal/block.hpp index ff39d7f526..d2e9a46a87 100644 --- a/shared_model/interfaces/iroha_internal/block.hpp +++ b/shared_model/interfaces/iroha_internal/block.hpp @@ -76,7 +76,7 @@ namespace shared_model { std::for_each( signatures().begin(), signatures().end(), [&old_block](auto &sig) { std::unique_ptr old_sig( - sig->makeOldModel()); + sig.makeOldModel()); old_block->sigs.emplace_back(*old_sig); }); return old_block; @@ -94,7 +94,7 @@ namespace shared_model { .append("transactions") .appendAll(transactions(), [](auto &tx) { return tx->toString(); }) .append("signatures") - .appendAll(signatures(), [](auto &sig) { return sig->toString(); }) + .appendAll(signatures(), [](auto &sig) { return sig.toString(); }) .finalize(); } }; diff --git a/shared_model/interfaces/queries/query.hpp b/shared_model/interfaces/queries/query.hpp index 897b7bfda2..cb36c1ffa3 100644 --- a/shared_model/interfaces/queries/query.hpp +++ b/shared_model/interfaces/queries/query.hpp @@ -114,7 +114,7 @@ namespace shared_model { // for_each cycle will assign last signature for old // model. Also, if in new model absence at least one // signature, this part will be worked correctly. - auto old_sig = signature_wrapper->makeOldModel(); + auto old_sig = signature_wrapper.makeOldModel(); old_model->signature = *old_sig; delete old_sig; }); diff --git a/shared_model/interfaces/query_responses/error_query_response.hpp b/shared_model/interfaces/query_responses/error_query_response.hpp index c8aa5a4a8c..480c3f9a45 100644 --- a/shared_model/interfaces/query_responses/error_query_response.hpp +++ b/shared_model/interfaces/query_responses/error_query_response.hpp @@ -19,6 +19,7 @@ #define IROHA_SHARED_MODEL_QUERY_ERROR_RESPONSE_HPP #include + #include "interfaces/base/primitive.hpp" #include "interfaces/query_responses/error_responses/no_account_assets_error_response.hpp" #include "interfaces/query_responses/error_responses/no_account_detail_error_response.hpp" @@ -29,6 +30,7 @@ #include "interfaces/query_responses/error_responses/not_supported_error_response.hpp" #include "interfaces/query_responses/error_responses/stateful_failed_error_response.hpp" #include "interfaces/query_responses/error_responses/stateless_failed_error_response.hpp" +#include "utils/visitor_apply_for_all.hpp" namespace shared_model { namespace interface { diff --git a/shared_model/interfaces/transaction.hpp b/shared_model/interfaces/transaction.hpp index 970a17b885..69c408a1d8 100644 --- a/shared_model/interfaces/transaction.hpp +++ b/shared_model/interfaces/transaction.hpp @@ -44,11 +44,6 @@ namespace shared_model { */ virtual const types::AccountIdType &creatorAccountId() const = 0; - /** - * @return actual number of transaction of this user - */ - virtual types::CounterType transactionCounter() const = 0; - /// Type of command using CommandType = detail::PolymorphicWrapper; @@ -66,7 +61,6 @@ namespace shared_model { new iroha::model::Transaction(); oldStyleTransaction->created_ts = createdTime(); oldStyleTransaction->creator_account_id = creatorAccountId(); - oldStyleTransaction->tx_counter = transactionCounter(); std::for_each(commands().begin(), commands().end(), @@ -79,8 +73,9 @@ namespace shared_model { std::for_each(signatures().begin(), signatures().end(), [oldStyleTransaction](auto &sig) { - oldStyleTransaction->signatures.emplace_back( - *sig->makeOldModel()); + std::unique_ptr up_sig( + sig.makeOldModel()); + oldStyleTransaction->signatures.emplace_back(*up_sig); }); return oldStyleTransaction; @@ -91,14 +86,13 @@ namespace shared_model { return detail::PrettyStringBuilder() .init("Transaction") .append("hash", hash().hex()) - .append("txCounter", std::to_string(transactionCounter())) .append("creatorAccountId", creatorAccountId()) .append("createdTime", std::to_string(createdTime())) .append("commands") .appendAll(commands(), [](auto &command) { return command->toString(); }) .append("signatures") - .appendAll(signatures(), [](auto &sig) { return sig->toString(); }) + .appendAll(signatures(), [](auto &sig) { return sig.toString(); }) .finalize(); } }; diff --git a/shared_model/packages/android/android-build.sh b/shared_model/packages/android/android-build.sh index 7d03d4cb57..c84eeca0e6 100755 --- a/shared_model/packages/android/android-build.sh +++ b/shared_model/packages/android/android-build.sh @@ -7,7 +7,7 @@ fi if [[ ( "$#" -ne 4 ) && ( "$#" -ne 5 ) ]]; then echo "Illegal number of parameters" echo "Usage: $0 [BUILD_TYPE=Release]" - echo "Example: $0 arm64-v8a 26 /Users/me/Downloads/android-ndk-r16b jp.co.soramitsu.iroha.android Debug" + echo "Example: $0 arm64-v8a 26 $HOME/Downloads/android-ndk-r16b jp.co.soramitsu.iroha.android Debug" exit 1 fi @@ -80,9 +80,9 @@ fi tar xf ./boost_1_66_0.tar.gz cp -R ./boost_1_66_0/boost "$DEPS_DIR"/include -# protobuf +# protobuf v3.5.1 git clone https://github.com/google/protobuf -(cd ./protobuf ; git checkout b5fbb742af122b565925987e65c08957739976a7) +(cd ./protobuf ; git checkout 106ffc04be1abf3ff3399f54ccf149815b287dd9) cmake -Dprotobuf_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE="$BUILD_TYPE" -H./protobuf/cmake -B./protobuf/host_build # build for host to get js_embed VERBOSE=1 cmake --build ./protobuf/host_build -- -j"$CORES" # to be able to run js_embed we need its host version @@ -92,17 +92,17 @@ LDFLAGS="-llog -landroid" cmake "${ANDROID_TOOLCHAIN_ARGS[@]}" "${INSTALL_ARGS[@ VERBOSE=1 cmake --build ./protobuf/.build --target install -- -j"$CORES" # ed25519 -git clone git://github.com/hyperledger/iroha-ed25519 +git clone https://github.com/hyperledger/iroha-ed25519.git (cd ./iroha-ed25519 ; git checkout e7188b8393dbe5ac54378610d53630bd4a180038) cmake "${ANDROID_TOOLCHAIN_ARGS[@]}" "${INSTALL_ARGS[@]}" -DTESTING=OFF -DCMAKE_BUILD_TYPE="$BUILD_TYPE" -DBUILD=STATIC -H./iroha-ed25519 -B./iroha-ed25519/build VERBOSE=1 cmake --build ./iroha-ed25519/build --target install -- -j"$CORES" mv "$DEPS_DIR"/lib/static/libed25519.a "$DEPS_DIR"/lib; rmdir "$DEPS_DIR"/lib/static/ # SWIG fixes -sed -i.bak "s~find_package(JNI REQUIRED)~#find_package(JNI REQUIRED)~" ./iroha/shared_model/bindings/CMakeLists.txt -sed -i.bak "s~include_directories(${JAVA_INCLUDE_PATH})~#include_directories(${JAVA_INCLUDE_PATH})~" ./iroha/shared_model/bindings/CMakeLists.txt -sed -i.bak "s~include_directories(${JAVA_INCLUDE_PATH2})~#include_directories(${JAVA_INCLUDE_PATH2})~" ./iroha/shared_model/bindings/CMakeLists.txt -sed -i.bak "s~# the include path to jni.h~SET(CMAKE_SWIG_FLAGS \${CMAKE_SWIG_FLAGS} -package ${PACKAGE})~" ./iroha/shared_model/bindings/CMakeLists.txt +sed -i.bak "s~find_package(JNI REQUIRED)~SET(CMAKE_SWIG_FLAGS \${CMAKE_SWIG_FLAGS} -package ${PACKAGE})~" ./iroha/shared_model/bindings/CMakeLists.txt +sed -i.bak "s~\${JAVA_INCLUDE_PATH}~#\${JAVA_INCLUDE_PATH}~" ./iroha/shared_model/bindings/CMakeLists.txt +sed -i.bak "s~\${JAVA_INCLUDE_PATH2}~#\${JAVA_INCLUDE_PATH2}~" ./iroha/shared_model/bindings/CMakeLists.txt +sed -i.bak "s~target_include_directories(\${SWIG_MODULE_irohajava_REAL_NAME} PUBLIC~SET(CMAKE_SWIG_FLAGS \${CMAKE_SWIG_FLAGS}~" ./iroha/shared_model/bindings/CMakeLists.txt sed -i.bak "s~swig_link_libraries(irohajava~swig_link_libraries(irohajava \"${PWD}/protobuf/.build/lib${PROTOBUF_LIB_NAME}.a\" \"${NDK_PATH}/platforms/android-${VERSION}/${ARCH}/usr/${LIBP}/liblog.so\"~" ./iroha/shared_model/bindings/CMakeLists.txt # build iroha diff --git a/shared_model/packages/ios/ios-build.sh b/shared_model/packages/ios/ios-build.sh index 5678f7afa0..a798c2fcf6 100755 --- a/shared_model/packages/ios/ios-build.sh +++ b/shared_model/packages/ios/ios-build.sh @@ -78,10 +78,10 @@ curl -OL https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar. tar xf ./boost_1_66_0.tar.gz cp -R ./boost_1_66_0/boost "$DEPS_DIR"/include -# protobuf +# protobuf v3.5.1 git clone https://github.com/google/protobuf (cd ./protobuf; -git checkout 80a37e0782d2d702d52234b62dd4b9ec74fd2c95) +git checkout 106ffc04be1abf3ff3399f54ccf149815b287dd9) cmake -DCMAKE_BUILD_TYPE="$BUILD_TYPE" -Dprotobuf_BUILD_TESTS=OFF -H./protobuf/cmake -B./protobuf/host_build # build for host to get js_embed VERBOSE=1 cmake --build ./protobuf/host_build -- -j"$CORES" # to be able to run js_embed we need its host version @@ -90,7 +90,7 @@ cmake -DCMAKE_BUILD_TYPE="$BUILD_TYPE" "${IOS_TOOLCHAIN_ARGS[@]}" "${INSTALL_ARG VERBOSE=1 cmake --build ./protobuf/.build --target install -- -j"$CORES" # ed25519 -git clone git://github.com/hyperledger/iroha-ed25519 +git clone https://github.com/hyperledger/iroha-ed25519.git (cd ./iroha-ed25519; git checkout e7188b8393dbe5ac54378610d53630bd4a180038) cmake -DCMAKE_BUILD_TYPE="$BUILD_TYPE" "${IOS_TOOLCHAIN_ARGS[@]}" "${INSTALL_ARGS[@]}" -DTESTING=OFF -DBUILD=STATIC -H./iroha-ed25519 -B./iroha-ed25519/build diff --git a/shared_model/packages/ios/iroha-preparation.sh b/shared_model/packages/ios/iroha-preparation.sh new file mode 100755 index 0000000000..03a74e3e27 --- /dev/null +++ b/shared_model/packages/ios/iroha-preparation.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# download ios client and update dependencies +git clone https://github.com/hyperledger/iroha-ios.git +cd iroha-ios/ +carthage update --platform iOS +cd SwiftyIrohaExample +carthage update --platform iOS + +# build grpc client for sample application +cd grpc-swift/ +rm -rf ./* +rm -rf ./.* +git clone --branch 0.3.3 https://github.com/grpc/grpc-swift.git . +make + +# back to the root where script was executed +cd ../../.. + +# download and build Iroha library for iOS +curl https://raw.githubusercontent.com/hyperledger/iroha/master/shared_model/packages/ios/ios-build.sh > ios-build.sh + +# optional step - sometimes connection timeout appears when using git: scheme instead of https url +sed -i '' 's|git://github.com/hyperledger/iroha-ed25519|https://github.com/hyperledger/iroha-ed25519.git|g' ios-build.sh + +# build library +chmod +x ios-build.sh +./ios-build.sh SIMULATOR64 Debug + +# place artifacts to proper sample's locations + +# this command shows location for simulator artifacts +# use this command for device instead: +# mkdir -p iroha-ios/libs/iOS/ +mkdir -p iroha-ios/libs/Simulator/ + +# this command shows location for simulator artifacts +# use this command for device instead: +# cp lib/* iroha-ios/libs/iOS/ +cp lib/* iroha-ios/libs/Simulator/ +cp -a include/. iroha-ios/headers/ diff --git a/shared_model/packages/javascript/binding.gyp b/shared_model/packages/javascript/binding.gyp index 644d6fd768..d687b77dd7 100644 --- a/shared_model/packages/javascript/binding.gyp +++ b/shared_model/packages/javascript/binding.gyp @@ -1,6 +1,6 @@ { 'variables': { - 'iroha_home_dir': '../../../' + 'iroha_lib_dir': '../../' }, 'targets': [ { @@ -11,18 +11,17 @@ 'action_name': 'configure', 'message': 'Generate CMake build configuration for shared_model...', 'inputs': [ - '<(iroha_home_dir)/shared_model/bindings/CMakeLists.txt' + '<(iroha_lib_dir)/bindings/CMakeLists.txt' ], 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/Makefile', + '<(SHARED_INTERMEDIATE_DIR)/bindings/Makefile', ], 'action': [ 'cmake', - '-H<(iroha_home_dir)', + '-H<(iroha_lib_dir)', '-B<(SHARED_INTERMEDIATE_DIR)', '-DSWIG_NODE=ON', '-DENABLE_LIBS_PACKAGING=OFF', - '-DSHARED_MODEL_DISABLE_COMPATIBILITY=ON', '-DCMAKE_POSITION_INDEPENDENT_CODE=ON', '-DCMAKE_BUILD_TYPE=Release' ], @@ -31,12 +30,12 @@ 'action_name': 'build', 'message': 'Build shared_model libraries by CMake...', 'inputs': [ - '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/Makefile', + '<(SHARED_INTERMEDIATE_DIR)/bindings/Makefile', ], 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/bindingsJAVASCRIPT_wrap.cxx', - '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/libirohanode.a', - '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/libbindings.a' + '<(SHARED_INTERMEDIATE_DIR)/bindings/bindingsJAVASCRIPT_wrap.cxx', + '<(SHARED_INTERMEDIATE_DIR)/bindings/libirohanode.a', + '<(SHARED_INTERMEDIATE_DIR)/bindings/libbindings.a' ], 'action': [ 'cmake', @@ -53,20 +52,19 @@ 'copies': [ { 'files': [ - '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/libirohanode.a', - '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/libbindings.a', + '<(SHARED_INTERMEDIATE_DIR)/bindings/libirohanode.a', + '<(SHARED_INTERMEDIATE_DIR)/bindings/libbindings.a', + '<(SHARED_INTERMEDIATE_DIR)/generator/libgenerator.a', + '<(SHARED_INTERMEDIATE_DIR)/amount/libiroha_amount.a', '<(SHARED_INTERMEDIATE_DIR)/schema/libschema.a', - '<(SHARED_INTERMEDIATE_DIR)/libs/generator/libgenerator.a', - '<(SHARED_INTERMEDIATE_DIR)/libs/amount/libiroha_amount.a', - '<(SHARED_INTERMEDIATE_DIR)/shared_model/validators/libshared_model_stateless_validation.a', - # Cryptography libs - '<(SHARED_INTERMEDIATE_DIR)/shared_model/cryptography/ed25519_sha3_impl/libshared_model_cryptography.a', - '<(SHARED_INTERMEDIATE_DIR)/shared_model/cryptography/ed25519_sha3_impl/internal/libhash.a', - '<(SHARED_INTERMEDIATE_DIR)/shared_model/cryptography/ed25519_sha3_impl/internal/libed25519_crypto.a', - '<(SHARED_INTERMEDIATE_DIR)/shared_model/cryptography/model_impl/libshared_model_cryptography_model.a', + '<(SHARED_INTERMEDIATE_DIR)/validators/libshared_model_stateless_validation.a', + '<(SHARED_INTERMEDIATE_DIR)/cryptography/ed25519_sha3_impl/libshared_model_cryptography.a', + '<(SHARED_INTERMEDIATE_DIR)/cryptography/ed25519_sha3_impl/internal/libhash.a', + '<(SHARED_INTERMEDIATE_DIR)/cryptography/ed25519_sha3_impl/internal/libed25519_crypto.a', + '<(SHARED_INTERMEDIATE_DIR)/cryptography/model_impl/libshared_model_cryptography_model.a', # Third-party libraries - '<(iroha_home_dir)/external/src/hyperledger_ed25519-build/libed25519.a' + '<(iroha_lib_dir)/external/src/hyperledger_ed25519-build/libed25519.a' ], 'destination': '<(PRODUCT_DIR)' } @@ -76,12 +74,14 @@ 'target_name': '<(module_name)', 'dependencies': [ 'shared_model' ], 'include_dirs': [ - '<(iroha_home_dir)/shared_model', - '<(iroha_home_dir)/libs', - '<(iroha_home_dir)/schema' + '<(iroha_lib_dir)', + # TODO: Remove these include directories when Shared Model + # will be completely separated from Iroha + '<(iroha_lib_dir)/../schema', + '<(iroha_lib_dir)/../libs' ], 'sources': [ - '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/bindingsJAVASCRIPT_wrap.cxx' + '<(SHARED_INTERMEDIATE_DIR)/bindings/bindingsJAVASCRIPT_wrap.cxx' ], 'cflags_cc': ['-std=c++14', '-fexceptions', '-DDISABLE_BACKWARD'], 'cflags_cc!': ['-fno-rtti'], diff --git a/shared_model/packages/javascript/example/index.js b/shared_model/packages/javascript/example/index.js index a5e6345997..563b19eb03 100644 --- a/shared_model/packages/javascript/example/index.js +++ b/shared_model/packages/javascript/example/index.js @@ -28,14 +28,12 @@ var adminPub = fs.readFileSync('admin@test.pub').toString() var keys = crypto.convertFromExisting(adminPub, adminPriv) var currentTime = Date.now() -var startTxCounter = 1 var startQueryCounter = 1 var creator = 'admin@test' // build transaction var tx = txBuilder .creatorAccountId(creator) - .txCounter(startTxCounter) .createdTime(currentTime) .createDomain('ru', 'user') .createAsset('dollar', 'ru', 2) diff --git a/shared_model/packages/javascript/package.json b/shared_model/packages/javascript/package.json index a057a74fcf..8776868774 100644 --- a/shared_model/packages/javascript/package.json +++ b/shared_model/packages/javascript/package.json @@ -7,9 +7,10 @@ "prepare": "sh scripts/generate-protobuf.sh", "prepublishOnly": "npm ls", "install": "node-pre-gyp install --fallback-to-build", - "test": "tape tests/**/*.js | tap-spec", + "test": "tape tests/**/*.js | tap-dot", "build": "node-pre-gyp build", - "rebuild": "node-pre-gyp rebuild" + "rebuild": "node-pre-gyp rebuild", + "build-and-test": "npm install --build-from-source=iroha-lib && npm run test" }, "repository": { "type": "git", @@ -44,13 +45,16 @@ "dependencies": { "google-protobuf": "^3.5.0", "grpc": "^1.9.1", - "node-pre-gyp": "^0.6.39" + "node-pre-gyp": "^0.6.39", + "npm": "^5.8.0", + "run": "^1.4.0", + "test": "^0.6.0" }, "devDependencies": { "grpc-tools": "^1.6.6", "node-gyp": "^3.6.2", "node-pre-gyp-github": "^1.3.1", - "tap-spec": "^4.1.1", + "tap-dot": "^1.0.5", "tape": "^4.9.0" }, "bundledDependencies": [ diff --git a/shared_model/packages/javascript/scripts/install-dependencies.sh b/shared_model/packages/javascript/scripts/install-dependencies.sh index 27cc229bcf..41f17879aa 100755 --- a/shared_model/packages/javascript/scripts/install-dependencies.sh +++ b/shared_model/packages/javascript/scripts/install-dependencies.sh @@ -20,8 +20,9 @@ git clone https://github.com/boostorg/boost /tmp/boost; \ (cd /tmp/boost ; sudo /tmp/boost/b2 link=static cxxflags="-std=c++14" -j "$(getconf _NPROCESSORS_ONLN)" install); \ rm -rf /tmp/boost -# Protobuf (static) -git clone --depth 1 --branch v3.5.1 https://github.com/google/protobuf +# Protobuf (static) v3.5.1 +git clone https://github.com/google/protobuf cd protobuf +git checkout 106ffc04be1abf3ff3399f54ccf149815b287dd9 cmake -Hcmake/ -Bbuild -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_BUILD_SHARED_LIBS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release sudo cmake --build build/ --target install -- -j"$(getconf _NPROCESSORS_ONLN)" diff --git a/shared_model/packages/javascript/tests/queryBuilder.js b/shared_model/packages/javascript/tests/queryBuilder.js index 6a5ea1a2dd..17da9bdcc5 100644 --- a/shared_model/packages/javascript/tests/queryBuilder.js +++ b/shared_model/packages/javascript/tests/queryBuilder.js @@ -5,7 +5,7 @@ const accountId = 'admin@test' const assetId = 'coin#test' test('ModelQueryBuilder tests', function (t) { - t.plan(49) + t.plan(50) let queryBuilder = new iroha.ModelQueryBuilder() const time = (new Date()).getTime() @@ -86,9 +86,13 @@ test('ModelQueryBuilder tests', function (t) { t.comment('Testing getTransactions()') t.throws(() => correctQuery.getTransactions(), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctQuery.getTransactions(''), /argument 2 of type 'std::vector< shared_model::crypto::Hash >/, 'Should throw ...argument 2 of type...') + let hv = new iroha.HashVector() hv.add(new iroha.Hash('11111111111111111111111111111111')) hv.add(new iroha.Hash('22222222222222222222222222222222')) + let emptyHv = new iroha.HashVector() + + t.throws(() => correctQuery.getTransactions(emptyHv), /Hash set should contain at least one hash/, 'Should throw Hash set should contain at least one hash') t.doesNotThrow(() => correctQuery.getTransactions(hv), null, 'Should not throw any exceptions') // getAccountDetail() tests diff --git a/shared_model/packages/javascript/tests/txbuilder.js b/shared_model/packages/javascript/tests/txbuilder.js index 79b59516ea..cc29430020 100644 --- a/shared_model/packages/javascript/tests/txbuilder.js +++ b/shared_model/packages/javascript/tests/txbuilder.js @@ -9,34 +9,36 @@ const assetId = 'coin#test' const testAccountId = 'test@test' test('ModelTransactionBuilder tests', function (t) { - t.plan(130) + t.plan(135) let crypto = new iroha.ModelCrypto() let keypair = crypto.convertFromExisting(publicKey, privateKey) let txBuilder = new iroha.ModelTransactionBuilder() const time = (new Date()).getTime() + const futureTime = 2400000000000 const address = '0.0.0.0:50051' t.comment('Basic TransactionBuilder tests') - t.throws(() => txBuilder.build(), /Transaction should contain at least one command/, 'Should throw exception 0 commands in transaction, wrong creator_account_id, timestamp and counter') - t.throws(() => txBuilder.creatorAccountId(adminAccountId).build(), /Transaction should contain at least one command/, 'Should throw exception about zero commands in transaction, wrong timestamp and counter') - t.throws(() => txBuilder.creatorAccountId(adminAccountId).createdTime(0).txCounter(1).build(), /Transaction should contain at least one command bad timestamp: too old/, 'Should throw 0 commands + bad timestamp: too old') - t.throws(() => txBuilder.creatorAccountId(adminAccountId).createdTime(time).txCounter(0).build(), /Transaction should contain at least one command Counter should be > 0/, 'Should throw 0 commands + Counter should be > 0') - t.throws(() => txBuilder.creatorAccountId('').createdTime(time).txCounter(1).build(), /Transaction should contain at least one command Wrongly formed creator_account_id, passed value: ''/, 'Should throw 0 commands + Wrongly formed creator_account_id') - t.throws(() => txBuilder.creatorAccountId('@@@').createdTime(time).txCounter(1).build(), /Transaction should contain at least one command Wrongly formed creator_account_id, passed value: '@@@'/, 'Should throw 0 commands + Wrongly formed creator_account_id') - t.throws(() => txBuilder.creatorAccountId(adminAccountId).createdTime(time).txCounter(1).build(), /Transaction should contain at least one command/, 'Should throw exception about zero commands in transaction') + t.throws(() => txBuilder.build(), /Transaction should contain at least one command(.*)Wrongly formed creator_account_id, passed value: ''(.*)bad timestamp: too old/, 'Should throw exception 0 commands in transaction, wrong creator_account_id, timestamp') + t.throws(() => txBuilder.creatorAccountId(adminAccountId).build(), /Transaction should contain at least one command(.*)bad timestamp: too old/, 'Should throw exception about zero commands in transaction, wrong timestamp') + t.throws(() => txBuilder.creatorAccountId(adminAccountId).createdTime(0).build(), /Transaction should contain at least one command(.*)bad timestamp: too old/, 'Should throw 0 commands + bad timestamp: too old') + t.throws(() => txBuilder.creatorAccountId(adminAccountId).createdTime(time).build(), /Transaction should contain at least one command/, 'Should throw 0 commands') + t.throws(() => txBuilder.creatorAccountId('').createdTime(time).build(), /Transaction should contain at least one command(.*)Wrongly formed creator_account_id, passed value: ''/, 'Should throw 0 commands + Wrongly formed creator_account_id') + t.throws(() => txBuilder.creatorAccountId('@@@').createdTime(time).build(), /Transaction should contain at least one command(.*)Wrongly formed creator_account_id, passed value: '@@@'/, 'Should throw 0 commands + Wrongly formed creator_account_id') + t.throws(() => txBuilder.creatorAccountId(adminAccountId).createdTime(futureTime).build(), /Transaction should contain at least one command(.*)bad timestamp: sent from future/, 'Should throw exception about zero commands in transaction, Sent from future') + t.throws(() => txBuilder.creatorAccountId(adminAccountId).createdTime(time).build(), /Transaction should contain at least one command/, 'Should throw exception about zero commands in transaction') - // Transaction with valid txCounter, creatorAccountId and createdTime - let correctTx = txBuilder.creatorAccountId(adminAccountId).createdTime(time).txCounter(1) + // Transaction with valid creatorAccountId and createdTime + let correctTx = txBuilder.creatorAccountId(adminAccountId).createdTime(time) // addAssetQuantity() tests t.comment('Testing addAssetQuantity()') t.throws(() => correctTx.addAssetQuantity(), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.addAssetQuantity(''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.addAssetQuantity('', ''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') - t.throws(() => correctTx.addAssetQuantity('', '', '').build(), /AddAssetQuantity: \[\[Wrongly formed account_id, passed value: '' Wrongly formed asset_id, passed value: '' Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw wrongly formed account_id, asset_id, Amount must be greater than 0') + t.throws(() => correctTx.addAssetQuantity('', '', '').build(), /AddAssetQuantity: \[\[Wrongly formed account_id, passed value: ''(.*)Wrongly formed asset_id, passed value: ''(.*)Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw wrongly formed account_id, asset_id, Amount must be greater than 0') t.throws(() => correctTx.addAssetQuantity(adminAccountId, assetId, '0').build(), /AddAssetQuantity: \[\[Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw Amount must be greater than 0') t.throws(() => correctTx.addAssetQuantity('', assetId, '1000').build(), /Wrongly formed account_id, passed value: ''/, 'Should throw Wrongly formed account_id') t.throws(() => correctTx.addAssetQuantity('@@@', assetId, '1000').build(), /Wrongly formed account_id, passed value: '@@@'/, 'Should throw Wrongly formed account_id') @@ -74,12 +76,11 @@ test('ModelTransactionBuilder tests', function (t) { t.comment('Testing appendRole()') t.throws(() => correctTx.appendRole(), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.appendRole(''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') - t.throws(() => correctTx.appendRole('', 'ruser').build(), /Wrongly formed account_id, passed value: ''/, 'Should throw Wrongly formed account_id') - t.throws(() => correctTx.appendRole('@@@', 'ruser').build(), /Wrongly formed account_id, passed value: '@@@'/, 'Should throw Wrongly formed account_id') + t.throws(() => correctTx.appendRole('', 'new_user_role').build(), /Wrongly formed account_id, passed value: ''/, 'Should throw Wrongly formed account_id') + t.throws(() => correctTx.appendRole('@@@', 'new_user_role').build(), /Wrongly formed account_id, passed value: '@@@'/, 'Should throw Wrongly formed account_id') t.throws(() => correctTx.appendRole(adminAccountId, '').build(), /Wrongly formed role_id, passed value: ''/, 'Should throw Wrongly formed role_id') t.throws(() => correctTx.appendRole(adminAccountId, '@@@').build(), /Wrongly formed role_id, passed value: '@@@'/, 'Should throw Wrongly formed role_id') - // TODO: 8 symbols - t.doesNotThrow(() => correctTx.appendRole(adminAccountId, 'ruser').build(), null, 'Should not throw any exceptions') + t.doesNotThrow(() => correctTx.appendRole(adminAccountId, 'new_user_role').build(), null, 'Should not throw any exceptions') // createAsset() tests t.comment('Testing createAsset()') @@ -110,34 +111,40 @@ test('ModelTransactionBuilder tests', function (t) { t.comment('Testing createDomain()') t.throws(() => correctTx.createDomain(), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.createDomain(''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') - t.throws(() => correctTx.createDomain('', 'ruser').build(), /Wrongly formed domain_id, passed value: ''/, 'Should throw Wrongly formed domain_id') - t.throws(() => correctTx.createDomain('$$$', 'ruser').build(), /Wrongly formed domain_id, passed value: '\$\$\$'/, 'Should throw Wrongly formed domain_id') + t.throws(() => correctTx.createDomain('', 'new_user_role').build(), /Wrongly formed domain_id, passed value: ''/, 'Should throw Wrongly formed domain_id') + t.throws(() => correctTx.createDomain('$$$', 'new_user_role').build(), /Wrongly formed domain_id, passed value: '\$\$\$'/, 'Should throw Wrongly formed domain_id') t.throws(() => correctTx.createDomain('domain', '').build(), /Wrongly formed role_id, passed value: ''/, 'Should throw Wrongly formed role_id') t.throws(() => correctTx.createDomain('domain', '@@@').build(), /Wrongly formed role_id, passed value: '@@@'/, 'Should throw Wrongly formed role_id') - t.doesNotThrow(() => correctTx.createDomain('domain', 'ruser').build(), null, 'Should not throw any exceptions') + t.doesNotThrow(() => correctTx.createDomain('domain', 'new_user_role').build(), null, 'Should not throw any exceptions') // createRole() tests t.comment('Testing createRole()') t.throws(() => correctTx.createRole(), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.createRole(''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') + let sv = new iroha.StringVector() - sv.add('permission1') - sv.add('permission2') + sv.add('can_add_peer') + sv.add('can_read_assets') + let emptySv = new iroha.StringVector() + let invalidPermissionSv = new iroha.StringVector() + invalidPermissionSv.add('wrong_permission') + + t.throws(() => correctTx.createRole('new_user_role', emptySv).build(), /Permission set should contain at least one permission/, 'Should throw Permission set should contain at least one permission') + t.throws(() => correctTx.createRole('new_user_role', invalidPermissionSv).build(), /Provided permission does not exist/, 'Should throw Provided permission does not exist') t.throws(() => correctTx.createRole('', sv).build(), /Wrongly formed role_id, passed value: ''/, 'Should throw Wrongly formed role_id') t.throws(() => correctTx.createRole('@@@', sv).build(), /Wrongly formed role_id, passed value: '@@@'/, 'Should throw Wrongly formed role_id') - t.throws(() => correctTx.createRole('ruser', '').build(), /argument 3 of type 'std::vector< shared_model::interface::types::PermissionNameType >/, 'Should throw ...argument 3 of type...') - t.doesNotThrow(() => correctTx.createRole('ruser', sv).build(), null, 'Should not throw any exceptions') + t.throws(() => correctTx.createRole('new_user_role', '').build(), /argument 3 of type 'std::vector< shared_model::interface::types::PermissionNameType >/, 'Should throw ...argument 3 of type...') + t.doesNotThrow(() => correctTx.createRole('new_user_role', sv).build(), null, 'Should not throw any exceptions') // detachRole() tests t.comment('Testing detachRole()') t.throws(() => correctTx.detachRole(), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.detachRole(''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') - t.throws(() => correctTx.detachRole('', 'ruser').build(), /Wrongly formed account_id, passed value: ''/, 'Should throw Wrongly formed account_id') - t.throws(() => correctTx.detachRole('@@@', 'ruser').build(), /Wrongly formed account_id, passed value: '@@@'/, 'Should throw Wrongly formed account_id') + t.throws(() => correctTx.detachRole('', 'new_user_role').build(), /Wrongly formed account_id, passed value: ''/, 'Should throw Wrongly formed account_id') + t.throws(() => correctTx.detachRole('@@@', 'new_user_role').build(), /Wrongly formed account_id, passed value: '@@@'/, 'Should throw Wrongly formed account_id') t.throws(() => correctTx.detachRole(adminAccountId, '').build(), /Wrongly formed role_id, passed value: ''/, 'Should throw Wrongly formed role_id') t.throws(() => correctTx.detachRole(adminAccountId, '@@@').build(), /Wrongly formed role_id, passed value: '@@@'/, 'Should throw Wrongly formed role_id') - // TODO: 8 symbols - t.doesNotThrow(() => correctTx.detachRole(adminAccountId, 'ruser').build(), null, 'Should not throw any exceptions') + t.doesNotThrow(() => correctTx.detachRole(adminAccountId, 'new_user_role').build(), null, 'Should not throw any exceptions') // grantPermission() tests t.comment('Testing grantPermission()') @@ -145,8 +152,8 @@ test('ModelTransactionBuilder tests', function (t) { t.throws(() => correctTx.grantPermission(''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.grantPermission('', 'can_read_assets').build(), /Wrongly formed account_id, passed value: ''/, 'Should throw Wrongly formed account_id') t.throws(() => correctTx.grantPermission('@@@', 'can_read_assets').build(), /Wrongly formed account_id, passed value: '@@@'/, 'Should throw Wrongly formed account_id') - t.throws(() => correctTx.grantPermission(adminAccountId, '').build(), /Wrongly formed permission, passed value: ''/, 'Should throw Wrongly formed permission') - t.throws(() => correctTx.grantPermission(adminAccountId, '@@@').build(), /Wrongly formed permission, passed value: '@@@'/, 'Should throw Wrongly formed permission') + t.throws(() => correctTx.grantPermission(adminAccountId, '').build(), /Provided permission does not exist/, 'Should throw Provided permission does not exist') + t.throws(() => correctTx.grantPermission(adminAccountId, '@@@').build(), /Provided permission does not exist/, 'Should throw Provided permission does not exist') t.doesNotThrow(() => correctTx.grantPermission(adminAccountId, 'can_read_assets').build(), null, 'Should not throw any exceptions') // revokePermission() tests @@ -155,8 +162,8 @@ test('ModelTransactionBuilder tests', function (t) { t.throws(() => correctTx.revokePermission(''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.revokePermission('', 'can_read_assets').build(), /Wrongly formed account_id, passed value: ''/, 'Should throw Wrongly formed account_id') t.throws(() => correctTx.revokePermission('@@@', 'can_read_assets').build(), /Wrongly formed account_id, passed value: '@@@'/, 'Should throw Wrongly formed account_id') - t.throws(() => correctTx.revokePermission(adminAccountId, '').build(), /Wrongly formed permission, passed value: ''/, 'Should throw Wrongly formed permission') - t.throws(() => correctTx.revokePermission(adminAccountId, '@@@').build(), /Wrongly formed permission, passed value: '@@@'/, 'Should throw Wrongly formed permission') + t.throws(() => correctTx.revokePermission(adminAccountId, '').build(), /Provided permission does not exist/, 'Should throw Provided permission does not exist') + t.throws(() => correctTx.revokePermission(adminAccountId, '@@@').build(), /Provided permission does not exist/, 'Should throw Provided permission does not exist') t.doesNotThrow(() => correctTx.revokePermission(adminAccountId, 'can_read_assets').build(), null, 'Should not throw any exceptions') // setAccountDetail() tests @@ -177,6 +184,8 @@ test('ModelTransactionBuilder tests', function (t) { t.throws(() => correctTx.setAccountQuorum('', 10).build(), /Wrongly formed account_id, passed value: ''/, 'Should throw Wrongly formed account_id') t.throws(() => correctTx.setAccountQuorum('@@@', 10).build(), /Wrongly formed account_id, passed value: '@@@'/, 'Should throw Wrongly formed account_id') t.throws(() => correctTx.setAccountQuorum(adminAccountId, 'kek').build(), /argument 3 of type 'shared_model::interface::types::QuorumType'/, 'Should throw ...argument 3 of type...') + t.throws(() => correctTx.setAccountQuorum(adminAccountId, 0).build(), /Quorum should be within range \(0, 128\]/, 'Should throw Quorum should be within range (0, 128]') + t.throws(() => correctTx.setAccountQuorum(adminAccountId, 200).build(), /Quorum should be within range \(0, 128\]/, 'Should throw Quorum should be within range (0, 128]') t.doesNotThrow(() => correctTx.setAccountQuorum(adminAccountId, 10).build(), null, 'Should not throw any exceptions') // subtractAssetQuantity() tests @@ -184,7 +193,7 @@ test('ModelTransactionBuilder tests', function (t) { t.throws(() => correctTx.subtractAssetQuantity(), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.subtractAssetQuantity(''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.subtractAssetQuantity('', ''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') - t.throws(() => correctTx.subtractAssetQuantity('', '', '').build(), /SubtractAssetQuantity: \[\[Wrongly formed account_id, passed value: '' Wrongly formed asset_id, passed value: '' Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw wrongly formed account_id, asset_id, Amount must be greater than 0') + t.throws(() => correctTx.subtractAssetQuantity('', '', '').build(), /SubtractAssetQuantity: \[\[Wrongly formed account_id, passed value: ''(.*)Wrongly formed asset_id, passed value: ''(.*)Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw wrongly formed account_id, asset_id, Amount must be greater than 0') t.throws(() => correctTx.subtractAssetQuantity(adminAccountId, assetId, '0').build(), /SubtractAssetQuantity: \[\[Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw Amount must be greater than 0') // TODO: MAYBE Throw an exception on real amount // t.throws(() => correctTx.subtractAssetQuantity(adminAccountId, assetId, '0.123').build(), /SubtractAssetQuantity: \[\[Amount must be integer, passed value: 0.123 \]\]/, 'Should throw Amount must be integer') diff --git a/shared_model/packages/python/README.md b/shared_model/packages/python/README.md index d769c7ea82..e0c53985b1 100644 --- a/shared_model/packages/python/README.md +++ b/shared_model/packages/python/README.md @@ -1,4 +1,5 @@ # Python client library example +***Default branch that is going to be built is develop, if you want to build other branch, please set it in setup.py*** ## How to install iroha python library To install latest release: @@ -10,7 +11,12 @@ To install developer version: pip install -i https://testpypi.python.org/pypi iroha ``` -## How to build iroha +## How to build and install iroha locally +```bash +python setup.py install +``` + +## How to build and publish iroha Creating MacOS wheel and publishing it: ```bash python setup.py bdist_wheel diff --git a/shared_model/utils/CMakeLists.txt b/shared_model/utils/CMakeLists.txt new file mode 100644 index 0000000000..1b7146d58a --- /dev/null +++ b/shared_model/utils/CMakeLists.txt @@ -0,0 +1,8 @@ + +add_library(shared_model_amount_utils + amount_utils.cpp + ) + +target_link_libraries(shared_model_amount_utils + shared_model_proto_backend + ) diff --git a/shared_model/utils/amount_utils.cpp b/shared_model/utils/amount_utils.cpp new file mode 100644 index 0000000000..45a88d5a10 --- /dev/null +++ b/shared_model/utils/amount_utils.cpp @@ -0,0 +1,139 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * 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 + * + * http://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. + */ + +#include "utils/amount_utils.hpp" + +#include + +#include "builders/default_builders.hpp" +#include "builders/protobuf/common_objects/proto_amount_builder.hpp" + +namespace shared_model { + namespace detail { + const boost::multiprecision::uint256_t ten = 10; + + boost::multiprecision::uint256_t increaseValuePrecision( + boost::multiprecision::uint256_t value, int degree) { + return value * pow(ten, degree); + } + + /** + * Sums up two amounts. + * Result is returned + * @param a left term + * @param b right term + */ + iroha::expected::PolymorphicResult + operator+(const shared_model::interface::Amount &a, + const shared_model::interface::Amount &b) { + auto max_precision = std::max(a.precision(), b.precision()); + auto val_a = + increaseValuePrecision(a.intValue(), max_precision - a.precision()); + auto val_b = + increaseValuePrecision(b.intValue(), max_precision - b.precision()); + if (val_a < a.intValue() || val_b < b.intValue() || val_a + val_b < val_a + || val_a + val_b < val_b) { + return iroha::expected::makeError(std::make_shared( + (boost::format("addition overflows (%s + %s)") % a.intValue().str() + % b.intValue().str()) + .str())); + } + return shared_model::builder::AmountBuilderWithoutValidator() + .precision(max_precision) + .intValue(val_a + val_b) + .build(); + } + + /** + * Subtracts two amounts. + * Result is returned + * @param a left term + * @param b right term + */ + iroha::expected::PolymorphicResult + operator-(const shared_model::interface::Amount &a, + const shared_model::interface::Amount &b) { + // check if a greater than b + if (a.intValue() < b.intValue()) { + return iroha::expected::makeError(std::make_shared( + (boost::format("minuend is smaller than subtrahend (%s - %s)") + % a.intValue().str() % b.intValue().str()) + .str())); + } + auto max_precision = std::max(a.precision(), b.precision()); + auto val_a = + increaseValuePrecision(a.intValue(), max_precision - a.precision()); + auto val_b = + increaseValuePrecision(b.intValue(), max_precision - b.precision()); + if (val_a < a.intValue() || val_b < b.intValue()) { + return iroha::expected::makeError( + std::make_shared("new precision overflows number")); + } + return shared_model::builder::AmountBuilderWithoutValidator() + .precision(max_precision) + .intValue(val_a - val_b) + .build(); + } + + /** + * Make amount with bigger precision + * Result is returned + * @param a amount + * @param b right term + */ + iroha::expected::PolymorphicResult + makeAmountWithPrecision(const shared_model::interface::Amount &amount, + const int new_precision) { + if (amount.precision() > new_precision) { + return iroha::expected::makeError(std::make_shared( + (boost::format("new precision is smaller than current (%d < %d)") + % new_precision % amount.precision()) + .str())); + } + auto val_amount = increaseValuePrecision( + amount.intValue(), new_precision - amount.precision()); + if (val_amount < amount.intValue()) { + return iroha::expected::makeError( + std::make_shared("operation overflows number")); + } + return shared_model::builder::AmountBuilderWithoutValidator() + .precision(new_precision) + .intValue(val_amount) + .build(); + } + + int compareAmount(const shared_model::interface::Amount &a, + const shared_model::interface::Amount &b) { + if (a.precision() == b.precision()) { + return (a.intValue() < b.intValue()) + ? -1 + : (a.intValue() > b.intValue()) ? 1 : 0; + } + // when different precisions transform to have the same scale + auto max_precision = std::max(a.precision(), b.precision()); + + auto val1 = + increaseValuePrecision(a.intValue(), max_precision - a.precision()); + auto val2 = + increaseValuePrecision(b.intValue(), max_precision - b.precision()); + return (val1 < val2) ? -1 : (val1 > val2) ? 1 : 0; + } + } // namespace detail +} // namespace shared_model diff --git a/shared_model/utils/amount_utils.hpp b/shared_model/utils/amount_utils.hpp index d967d0cc9c..6d92c0c2d7 100644 --- a/shared_model/utils/amount_utils.hpp +++ b/shared_model/utils/amount_utils.hpp @@ -18,74 +18,54 @@ #ifndef IROHA_AMOUNT_UTILS_HPP #define IROHA_AMOUNT_UTILS_HPP -#include "builders/protobuf/common_objects/proto_amount_builder.hpp" +#include -/** - * Sums up two amounts. - * Requires to have the same scale. - * Otherwise nullopt is returned - * @param a left term - * @param b right term - */ -iroha::expected::Result, - std::shared_ptr> -operator+(const shared_model::interface::Amount &a, - const shared_model::interface::Amount &b) { - // check precisions - if (a.precision() != b.precision()) { - return iroha::expected::makeError( - std::make_shared("precision mismatch")); - } - if (a.intValue() + b.intValue() < a.intValue() - || a.intValue() + b.intValue() < b.intValue()) { - return iroha::expected::makeError( - std::make_shared("addition overflows")); - } - return shared_model::builder::AmountBuilderWithoutValidator() - .precision(a.precision()) - .intValue(a.intValue() + b.intValue()) - .build(); -} +#include "common/result.hpp" -/** - * Subtracts two amounts. - * Requires to have the same scale. - * Otherwise nullopt is returned - * @param a left term - * @param b right term - */ -iroha::expected::Result, - std::shared_ptr> -operator-(const shared_model::interface::Amount &a, - const shared_model::interface::Amount &b) { - // check precisions - if (a.precision() != b.precision()) { - return iroha::expected::makeError( - std::make_shared("precision mismatch")); - } - // check if a greater than b - if (a.intValue() < b.intValue()) { - return iroha::expected::makeError( - std::make_shared("minuend is smaller than subtrahend")); - } - return shared_model::builder::AmountBuilderWithoutValidator() - .precision(a.precision()) - .intValue(a.intValue() - b.intValue()) - .build(); -} +namespace shared_model { + namespace interface { + class Amount; + } // namespace interface + + namespace detail { + boost::multiprecision::uint256_t increaseValuePrecision( + boost::multiprecision::uint256_t value, int degree); + + /** + * Sums up two amounts. + * Result is returned + * @param a left term + * @param b right term + */ + iroha::expected::PolymorphicResult + operator+(const shared_model::interface::Amount &a, + const shared_model::interface::Amount &b); + + /** + * Subtracts two amounts. + * Result is returned + * @param a left term + * @param b right term + */ + iroha::expected::PolymorphicResult + operator-(const shared_model::interface::Amount &a, + const shared_model::interface::Amount &b); -int compareAmount(const shared_model::interface::Amount &a, - const shared_model::interface::Amount &b) { - if (a.precision() == b.precision()) { - return (a.intValue() < b.intValue()) - ? -1 - : (a.intValue() > b.intValue()) ? 1 : 0; - } - // when different precisions transform to have the same scale - auto max_precision = std::max(a.precision(), b.precision()); - auto val1 = a.intValue() * (int)std::pow(10, max_precision - a.precision()); - auto val2 = b.intValue() * (int)std::pow(10, max_precision - b.precision()); - return (val1 < val2) ? -1 : (val1 > val2) ? 1 : 0; -} + /** + * Make amount with bigger precision + * Result is returned + * @param a amount + * @param b right term + */ + iroha::expected::PolymorphicResult + makeAmountWithPrecision(const shared_model::interface::Amount &amount, + const int new_precision); + int compareAmount(const shared_model::interface::Amount &a, + const shared_model::interface::Amount &b); + } // namespace detail +} // namespace shared_model #endif // IROHA_AMOUNT_UTILS_HPP diff --git a/shared_model/utils/polymorphic_wrapper.hpp b/shared_model/utils/polymorphic_wrapper.hpp index 1ac9ec2fe3..e6a90db995 100644 --- a/shared_model/utils/polymorphic_wrapper.hpp +++ b/shared_model/utils/polymorphic_wrapper.hpp @@ -105,6 +105,10 @@ namespace shared_model { return *ptr_ == *rhs.ptr_; } + bool operator!=(const PolymorphicWrapper &rhs) const { + return not (*ptr_ == *rhs.ptr_); + } + /** * Mutable wrapped object pointer * @return pointer for wrapped object diff --git a/shared_model/validators/CMakeLists.txt b/shared_model/validators/CMakeLists.txt index a540fbe418..44cc2c7ab4 100644 --- a/shared_model/validators/CMakeLists.txt +++ b/shared_model/validators/CMakeLists.txt @@ -19,5 +19,5 @@ add_library(shared_model_stateless_validation target_link_libraries(shared_model_stateless_validation schema - model_interfaces + shared_model_interfaces ) diff --git a/shared_model/validators/container_validator.hpp b/shared_model/validators/container_validator.hpp index 52634050c2..450f0f265e 100644 --- a/shared_model/validators/container_validator.hpp +++ b/shared_model/validators/container_validator.hpp @@ -32,7 +32,9 @@ namespace shared_model { /** * Class that validates blocks and proposal common fieds */ - template + template class ContainerValidator { protected: void validateHeight(ReasonsGroupType &reason, @@ -49,9 +51,7 @@ namespace shared_model { const interface::Transaction &transaction) const { auto answer = transaction_validator_.validate(transaction); if (answer.hasErrors()) { - auto message = (boost::format("Tx #%d: %s") - % transaction.transactionCounter() % answer.reason()) - .str(); + auto message = (boost::format("Tx: %s") % answer.reason()).str(); reason.second.push_back(message); } } @@ -83,8 +83,10 @@ namespace shared_model { } return answer; } + private: TransactionValidator transaction_validator_; + protected: FieldValidator field_validator_; }; diff --git a/shared_model/validators/field_validator.cpp b/shared_model/validators/field_validator.cpp index 64cf17a4c3..80a1392a03 100644 --- a/shared_model/validators/field_validator.cpp +++ b/shared_model/validators/field_validator.cpp @@ -18,8 +18,8 @@ #include "validators/field_validator.hpp" #include #include -#include "permissions.hpp" #include "cryptography/crypto_provider/crypto_verifier.hpp" +#include "permissions.hpp" // TODO: 15.02.18 nickaleks Change structure to compositional IR-978 @@ -47,7 +47,8 @@ namespace shared_model { const std::string FieldValidator::role_id_pattern_ = R"#([a-z_0-9]{1,32})#"; const size_t FieldValidator::public_key_size = 32; - const size_t FieldValidator::value_size = 4096; + /// limit for the set account detail size in bytes + const size_t FieldValidator::value_size = 4 * 1024 * 1024; const size_t FieldValidator::description_size = 64; FieldValidator::FieldValidator(time_t future_gap) @@ -213,8 +214,8 @@ namespace shared_model { void FieldValidator::validatePermission( ReasonsGroupType &reason, const interface::types::PermissionNameType &permission_name) const { - if (iroha::model::all_perm_group.find(permission_name) - == iroha::model::all_perm_group.end()) { + if (shared_model::permissions::all_perm_group.find(permission_name) + == shared_model::permissions::all_perm_group.end()) { reason.second.push_back("Provided permission does not exist"); } } @@ -226,8 +227,8 @@ namespace shared_model { reason.second.push_back( "Permission set should contain at least one permission"); } - if (not std::includes(iroha::model::role_perm_group.begin(), - iroha::model::role_perm_group.end(), + if (not std::includes(shared_model::permissions::role_perm_group.begin(), + shared_model::permissions::role_perm_group.end(), permissions.begin(), permissions.end())) { reason.second.push_back( @@ -291,11 +292,11 @@ namespace shared_model { void FieldValidator::validateSignatures( ReasonsGroupType &reason, - const interface::SignatureSetType &signatures, + const interface::types::SignatureRangeType &signatures, const crypto::Blob &source) const { for (const auto &signature : signatures) { - const auto &sign = signature->signedData(); - const auto &pkey = signature->publicKey(); + const auto &sign = signature.signedData(); + const auto &pkey = signature.publicKey(); bool is_valid = true; if (sign.blob().size() != 64) { diff --git a/shared_model/validators/field_validator.hpp b/shared_model/validators/field_validator.hpp index 4376b9d44b..3ee0c4e50c 100644 --- a/shared_model/validators/field_validator.hpp +++ b/shared_model/validators/field_validator.hpp @@ -23,7 +23,6 @@ #include "datetime/time.hpp" #include "interfaces/base/signable.hpp" #include "interfaces/commands/command.hpp" -#include "interfaces/common_objects/signable_hash.hpp" #include "interfaces/transaction.hpp" #include "validators/answer.hpp" @@ -129,9 +128,10 @@ namespace shared_model { void validateCounter(ReasonsGroupType &reason, const interface::types::CounterType &counter) const; - void validateSignatures(ReasonsGroupType &reason, - const interface::SignatureSetType &signatures, - const crypto::Blob &source) const; + void validateSignatures( + ReasonsGroupType &reason, + const interface::types::SignatureRangeType &signatures, + const crypto::Blob &source) const; void validateDescription( ReasonsGroupType &reason, diff --git a/shared_model/validators/permissions.hpp b/shared_model/validators/permissions.hpp index 0f692b0dfa..605885e6a1 100644 --- a/shared_model/validators/permissions.hpp +++ b/shared_model/validators/permissions.hpp @@ -15,14 +15,14 @@ * limitations under the License. */ -#ifndef IROHA_PERMISSIONS_HPP -#define IROHA_PERMISSIONS_HPP +#ifndef SHARED_MODEL_PERMISSIONS_HPP +#define SHARED_MODEL_PERMISSIONS_HPP #include #include -namespace iroha { - namespace model { +namespace shared_model { + namespace permissions { /* ~~~~~~~~ Command-related permissions ~~~~~~~~ */ @@ -115,6 +115,9 @@ namespace iroha { const std::string can_get_my_txs = "can_get_my_txs"; const std::string can_get_all_txs = "can_get_all_txs"; + /* Blocks */ + const std::string can_get_blocks = "can_get_blocks"; + /* ~~~~~~~~ Groups ~~~~~~~~ */ const std::set read_self_group = {can_get_my_account, can_get_my_signatories, @@ -132,7 +135,8 @@ namespace iroha { can_get_all_acc_ast_txs, can_get_all_txs, can_get_roles, - can_read_assets}; + can_read_assets, + can_get_blocks}; const std::set read_domain_group = { can_get_domain_accounts, @@ -145,11 +149,11 @@ namespace iroha { /* Grantable permissions */ const std::string can_grant = "can_grant_"; - const std::set grant_group = {can_grant + can_set_quorum, - can_grant + can_add_signatory, - can_grant + can_remove_signatory, - can_grant + can_transfer, - can_grant + can_set_detail}; + const std::set grant_group = {can_grant + can_set_my_quorum, + can_grant + can_add_my_signatory, + can_grant + can_remove_my_signatory, + can_grant + can_transfer_my_assets, + can_grant + can_set_my_account_detail}; const std::set edit_self_group = { can_set_quorum, can_add_signatory, can_remove_signatory}; @@ -195,11 +199,11 @@ namespace iroha { can_get_domain_acc_ast_txs, can_get_my_txs, can_get_all_txs, - can_grant + can_set_quorum, - can_grant + can_add_signatory, - can_grant + can_remove_signatory, - can_grant + can_transfer, - can_grant + can_set_detail}; + can_grant + can_set_my_quorum, + can_grant + can_add_my_signatory, + can_grant + can_remove_my_signatory, + can_grant + can_transfer_my_assets, + can_grant + can_set_my_account_detail}; /* All permissions */ const std::set all_perm_group = { @@ -240,19 +244,20 @@ namespace iroha { can_get_domain_acc_ast_txs, can_get_my_txs, can_get_all_txs, - can_grant + can_set_quorum, - can_grant + can_add_signatory, - can_grant + can_remove_signatory, - can_grant + can_transfer, - can_grant + can_set_detail, + can_grant + can_set_my_quorum, + can_grant + can_add_my_signatory, + can_grant + can_remove_my_signatory, + can_grant + can_transfer_my_assets, + can_grant + can_set_my_account_detail, // TODO: IR 1190 kamilsa 30.03.2018 move permissions below to separated group can_add_my_signatory, can_remove_my_signatory, can_set_my_quorum, can_set_my_account_detail, - can_transfer_my_assets}; + can_transfer_my_assets, + can_get_blocks}; - } // namespace model -} // namespace iroha + } // namespace shared_model +} // namespace permissions -#endif // IROHA_PERMISSIONS_HPP +#endif // SHARED_MODEL_PERMISSIONS_HPP diff --git a/shared_model/validators/transaction_validator.hpp b/shared_model/validators/transaction_validator.hpp index 43f6a42f8b..655b1842e6 100644 --- a/shared_model/validators/transaction_validator.hpp +++ b/shared_model/validators/transaction_validator.hpp @@ -279,7 +279,6 @@ namespace shared_model { field_validator_.validateCreatorAccountId(tx_reason, tx.creatorAccountId()); field_validator_.validateCreatedTime(tx_reason, tx.createdTime()); - field_validator_.validateCounter(tx_reason, tx.transactionCounter()); if (not tx_reason.second.empty()) { answer.addReason(std::move(tx_reason)); diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 554f11256b..fc482af82d 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -60,7 +60,7 @@ parts: ./b2 cxxflags="-std=c++14" install protobuf: source: https://github.com/google/protobuf.git - source-commit: 80a37e0782d2d702d52234b62dd4b9ec74fd2c95 + source-commit: 106ffc04be1abf3ff3399f54ccf149815b287dd9 # Protocol Buffers v3.5.1 source-subdir: cmake plugin: cmake configflags: [-Dprotobuf_BUILD_TESTS=OFF, -Dprotobuf_BUILD_SHARED_LIBS=ON] @@ -79,7 +79,7 @@ parts: after: [cmake] grpc: source: https://github.com/grpc/grpc.git - source-commit: bfcbad3b86c7912968dc8e64f2121c920dad4dfb + source-commit: bd44e485f69d70ca4095cea92decd98de3892aa6 # Release 1.11.0 plugin: cmake configflags: - -DgRPC_ZLIB_PROVIDER=package diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 745955ffba..cd9ea35c24 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,8 +1,26 @@ -SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/test_bin) +# +# Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. +# http://soramitsu.co.jp +# +# 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 +# +# http://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. +# + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/test_bin) add_subdirectory(module) add_subdirectory(framework) add_subdirectory(integration) +add_subdirectory(regression) add_subdirectory(system) if(BENCHMARKING) diff --git a/test/framework/CMakeLists.txt b/test/framework/CMakeLists.txt index 9363c06aaa..4212071a0c 100644 --- a/test/framework/CMakeLists.txt +++ b/test/framework/CMakeLists.txt @@ -20,13 +20,6 @@ target_link_libraries(test_subscriber_testing rxcpp ) -add_library(test_block_generator test_block_generator.cpp) -target_link_libraries(test_block_generator - model - ) - -target_include_directories(test_block_generator PUBLIC ${PROJECT_SOURCE_DIR}/test) - add_library(integration_framework integration_framework/integration_test_framework.cpp @@ -34,14 +27,12 @@ add_library(integration_framework ) target_link_libraries(integration_framework application - raw_block_insertion - keys_manager - model_generators - pb_model_converters - tbb ) target_include_directories(integration_framework PUBLIC ${PROJECT_SOURCE_DIR}/test) add_library(integration_framework_config_helper config_helper.cpp) +target_link_libraries(integration_framework_config_helper + logger + ) target_include_directories(integration_framework_config_helper PUBLIC ${PROJECT_SOURCE_DIR}/test) diff --git a/test/framework/base_tx.hpp b/test/framework/base_tx.hpp deleted file mode 100644 index 67314f7be0..0000000000 --- a/test/framework/base_tx.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. - */ - -#include "backend/protobuf/transaction.hpp" -#include "builders/protobuf/transaction.hpp" -#include "cryptography/public_key.hpp" -#include "datetime/time.hpp" -#include "framework/integration_framework/integration_test_framework.hpp" - -namespace framework { - /** - * Creates a set of transactions for user creation - * @param user is username of new user - * @param key is a public key of new user - * @return pre-built transaction - */ - static inline auto createUser(const std::string &user, - const shared_model::crypto::PublicKey &key) { - return shared_model::proto::TransactionBuilder() - .createAccount( - user, - integration_framework::IntegrationTestFramework::kDefaultDomain, - key) - .txCounter(1) - .creatorAccountId( - integration_framework::IntegrationTestFramework::kAdminId) - .createdTime(iroha::time::now()); - } - - /** - * Creates a set of transactions for user creation with specified permissions - * @param user is username of new user - * @param key is a public key of new user - * @param role_id is new role of the user - * @param perms is a collections of permissions of the user - * @return pre-build transaction - */ - static inline auto createUserWithPerms( - const std::string &user, - const shared_model::crypto::PublicKey &key, - const std::string role_id, - std::vector perms) { - const auto user_id = user + "@" - + integration_framework::IntegrationTestFramework::kDefaultDomain; - return createUser(user, key) - .detachRole( - user_id, - integration_framework::IntegrationTestFramework::kDefaultRole) - .createRole(role_id, perms) - .appendRole(user_id, role_id); - } -} // namespace framework diff --git a/test/framework/config_helper.cpp b/test/framework/config_helper.cpp index 6d4be64578..0f8596f696 100644 --- a/test/framework/config_helper.cpp +++ b/test/framework/config_helper.cpp @@ -17,8 +17,8 @@ #include "framework/config_helper.hpp" -#include #include +#include "logger/logger.hpp" namespace integration_framework { std::string getPostgresCredsOrDefault(const std::string &default_conn) { @@ -32,6 +32,7 @@ namespace integration_framework { std::stringstream ss; ss << "host=" << pg_host << " port=" << pg_port << " user=" << pg_user << " password=" << pg_pass; + logger::log("ITF")->info("Postgres credentials: {}", ss.str()); return ss.str(); } } diff --git a/test/framework/integration_framework/integration_test_framework.cpp b/test/framework/integration_framework/integration_test_framework.cpp index de9bccc4f9..04979b6323 100644 --- a/test/framework/integration_framework/integration_test_framework.cpp +++ b/test/framework/integration_framework/integration_test_framework.cpp @@ -18,13 +18,22 @@ #include "framework/integration_framework/integration_test_framework.hpp" #include + +#include "backend/protobuf/block.hpp" +#include "backend/protobuf/queries/proto_query.hpp" +#include "backend/protobuf/query_responses/proto_query_response.hpp" +#include "backend/protobuf/transaction.hpp" +#include "backend/protobuf/transaction_responses/proto_tx_response.hpp" #include "builders/protobuf/block.hpp" #include "builders/protobuf/proposal.hpp" #include "builders/protobuf/transaction.hpp" #include "common/files.hpp" -#include "cryptography/hash_providers/sha3_256.hpp" +#include "cryptography/crypto_provider/crypto_defaults.hpp" +#include "cryptography/default_hash_provider.hpp" #include "datetime/time.hpp" -// TODO (@l4l) IR-874 create more comfort way for permssion-dependent proto +#include "framework/integration_framework/iroha_instance.hpp" +#include "framework/integration_framework/test_irohad.hpp" +// TODO (@l4l) IR-874 create more comfort way for permission-dependent proto // building #include "validators/permissions.hpp" @@ -39,48 +48,35 @@ namespace integration_framework { const std::string IntegrationTestFramework::kAdminId = "admin@test"; const std::string IntegrationTestFramework::kAssetName = "coin"; + IntegrationTestFramework::IntegrationTestFramework( + size_t maximum_proposal_size, + std::function + deleter) + : maximum_proposal_size_(maximum_proposal_size), deleter_(deleter) {} + IntegrationTestFramework::~IntegrationTestFramework() { - pqxx::lazyconnection connection(iroha_instance_->pg_conn_); - const auto drop = R"( -DROP TABLE IF EXISTS account_has_signatory; -DROP TABLE IF EXISTS account_has_asset; -DROP TABLE IF EXISTS role_has_permissions; -DROP TABLE IF EXISTS account_has_roles; -DROP TABLE IF EXISTS account_has_grantable_permissions; -DROP TABLE IF EXISTS account; -DROP TABLE IF EXISTS asset; -DROP TABLE IF EXISTS domain; -DROP TABLE IF EXISTS signatory; -DROP TABLE IF EXISTS peer; -DROP TABLE IF EXISTS role; -DROP TABLE IF EXISTS height_by_hash; -DROP TABLE IF EXISTS height_by_account_set; -DROP TABLE IF EXISTS index_by_creator_height; -DROP TABLE IF EXISTS index_by_id_height_asset; -)"; - - pqxx::work txn(connection); - txn.exec(drop); - txn.commit(); - connection.disconnect(); - - iroha::remove_dir_contents(iroha_instance_->block_store_dir_); + if (deleter_) { + deleter_(*this); + } + // the code below should be executed anyway in order to prevent app hang + if (iroha_instance_ and iroha_instance_->instance_) { + iroha_instance_->instance_->terminate(); + } } - IntegrationTestFramework &IntegrationTestFramework::setInitialState( + shared_model::proto::Block IntegrationTestFramework::defaultBlock( const shared_model::crypto::Keypair &key) { auto genesis_tx = shared_model::proto::TransactionBuilder() .creatorAccountId(kAdminId) - .txCounter(1) .createdTime(iroha::time::now()) .addPeer("0.0.0.0:50541", key.publicKey()) - .createRole( - kDefaultRole, - // TODO (@l4l) IR-874 create more confort way for - // permssion-dependent proto building - std::vector{iroha::model::role_perm_group.begin(), - iroha::model::role_perm_group.end()}) + .createRole(kDefaultRole, + // TODO (@l4l) IR-874 create more comfort way for + // permission-dependent proto building + std::vector{ + shared_model::permissions::role_perm_group.begin(), + shared_model::permissions::role_perm_group.end()}) .createDomain(kDefaultDomain, kDefaultRole) .createAccount(kAdminName, kDefaultDomain, key.publicKey()) .createAsset(kAssetName, kDefaultDomain, 1) @@ -91,18 +87,24 @@ DROP TABLE IF EXISTS index_by_id_height_asset; .transactions( std::vector{genesis_tx}) .height(1) - .prevHash(Sha3_256::makeHash(Blob(""))) + .prevHash(DefaultHashProvider::makeHash(Blob(""))) .createdTime(iroha::time::now()) .build() .signAndAddSignature(key); - return setInitialState(key, genesis_block); + return genesis_block; + } + + IntegrationTestFramework &IntegrationTestFramework::setInitialState( + const Keypair &keypair) { + return setInitialState(keypair, + IntegrationTestFramework::defaultBlock(keypair)); } IntegrationTestFramework &IntegrationTestFramework::setInitialState( const Keypair &keypair, const shared_model::interface::Block &block) { log_->info("init state"); // peer initialization - iroha_instance_->initPipeline(keypair, maximum_block_size_); + iroha_instance_->initPipeline(keypair, maximum_proposal_size_); log_->info("created pipeline"); // iroha_instance_->clearLedger(); // log_->info("cleared ledger"); @@ -151,23 +153,75 @@ DROP TABLE IF EXISTS index_by_id_height_asset; return shared_model::proto::TransactionResponse(std::move(response)); } + IntegrationTestFramework &IntegrationTestFramework::sendTx( + const shared_model::proto::Transaction &tx, + std::function + validation) { + log_->info("send transaction"); + iroha_instance_->getIrohaInstance()->getCommandService()->Torii( + tx.getTransport()); + // fetch status of transaction + shared_model::proto::TransactionResponse status = getTxStatus(tx.hash()); + + // check validation function + validation(status); + return *this; + } + IntegrationTestFramework &IntegrationTestFramework::sendTx( const shared_model::proto::Transaction &tx) { sendTx(tx, [](const auto &) {}); return *this; } + IntegrationTestFramework &IntegrationTestFramework::sendQuery( + const shared_model::proto::Query &qry, + std::function + validation) { + log_->info("send query"); + + iroha::protocol::QueryResponse response; + iroha_instance_->getIrohaInstance()->getQueryService()->Find( + qry.getTransport(), response); + auto query_response = + shared_model::proto::QueryResponse(std::move(response)); + + validation(query_response); + return *this; + } + IntegrationTestFramework &IntegrationTestFramework::sendQuery( const shared_model::proto::Query &qry) { sendQuery(qry, [](const auto &) {}); return *this; } + IntegrationTestFramework &IntegrationTestFramework::checkProposal( + std::function validation) { + log_->info("check proposal"); + // fetch first proposal from proposal queue + ProposalType proposal; + fetchFromQueue( + proposal_queue_, proposal, proposal_waiting, "missed proposal"); + validation(proposal); + return *this; + } + IntegrationTestFramework &IntegrationTestFramework::skipProposal() { checkProposal([](const auto &) {}); return *this; } + IntegrationTestFramework &IntegrationTestFramework::checkBlock( + std::function validation) { + // fetch first from block queue + log_->info("check block"); + BlockType block; + fetchFromQueue(block_queue_, block, block_waiting, "missed block"); + validation(block); + return *this; + } + IntegrationTestFramework &IntegrationTestFramework::skipBlock() { checkBlock([](const auto &) {}); return *this; @@ -175,6 +229,8 @@ DROP TABLE IF EXISTS index_by_id_height_asset; void IntegrationTestFramework::done() { log_->info("done"); - iroha_instance_->instance_->storage->dropStorage(); + if (iroha_instance_->instance_ and iroha_instance_->instance_->storage) { + iroha_instance_->instance_->storage->dropStorage(); + } } } // namespace integration_framework diff --git a/test/framework/integration_framework/integration_test_framework.hpp b/test/framework/integration_framework/integration_test_framework.hpp index 62d1e063d5..3d6b96dba5 100644 --- a/test/framework/integration_framework/integration_test_framework.hpp +++ b/test/framework/integration_framework/integration_test_framework.hpp @@ -18,28 +18,32 @@ #ifndef IROHA_INTEGRATION_FRAMEWORK_HPP #define IROHA_INTEGRATION_FRAMEWORK_HPP -#include #include #include #include +#include #include #include #include #include -#include "crypto/keys_manager_impl.hpp" -#include "cryptography/blob.hpp" -#include "cryptography/ed25519_sha3_impl/internal/sha3_hash.hpp" -#include "cryptography/keypair.hpp" + +#include #include "framework/integration_framework/iroha_instance.hpp" #include "framework/integration_framework/test_irohad.hpp" #include "logger/logger.hpp" -#include "backend/protobuf/block.hpp" -#include "backend/protobuf/proposal.hpp" -#include "backend/protobuf/queries/proto_query.hpp" -#include "backend/protobuf/query_responses/proto_query_response.hpp" -#include "backend/protobuf/transaction.hpp" -#include "backend/protobuf/transaction_responses/proto_tx_response.hpp" +namespace shared_model { + namespace crypto { + class Keypair; + } + namespace interface { + class Block; + class Proposal; + } + namespace proto { + class Block; + } +} namespace integration_framework { @@ -51,41 +55,143 @@ namespace integration_framework { using BlockType = std::shared_ptr; public: - IntegrationTestFramework(size_t maximum_block_size = 10) - : maximum_block_size_(maximum_block_size) {} + /** + * Construct test framework instance + * @param maximum_proposal_size - Maximum number of transactions per + * proposal + * @param destructor_lambda - (default nullptr) Pointer to function which + * receives pointer to constructed instance of Integration Test Framework. + * If specified, then will be called instead of default destructor's code + */ + explicit IntegrationTestFramework( + size_t maximum_proposal_size, + std::function deleter = + [](IntegrationTestFramework &itf) { itf.done(); }); + ~IntegrationTestFramework(); + + /** + * Construct default genesis block. + * + * Genesis block contains single transaction that + * creates a single role (kDefaultRole), domain (kDefaultDomain), + * account (kAdminName) and asset (kAssetName). + * @param key - signing key + * @return signed genesis block + */ + static shared_model::proto::Block defaultBlock( + const shared_model::crypto::Keypair &key); + + /** + * Initialize Iroha instance with default genesis block and provided signing + * key + * @param keypair - signing key + * @return this + */ IntegrationTestFramework &setInitialState( const shared_model::crypto::Keypair &keypair); + + /** + * Initialize Iroha instance with provided genesis block and signing key + * @param keypair - signing key + * @param block - genesis block used for iroha initialization + * @return this + */ IntegrationTestFramework &setInitialState( const shared_model::crypto::Keypair &keypair, const shared_model::interface::Block &block); - template - IntegrationTestFramework &sendTx(const shared_model::proto::Transaction &tx, - Lambda validation); + /** + * Send transaction to Iroha and validate its status + * @param tx - transaction for sending + * @param validation - callback for transaction status validation that + * receives object of type \relates shared_model::proto::TransactionResponse + * by reference + * @return this + */ + IntegrationTestFramework &sendTx( + const shared_model::proto::Transaction &tx, + std::function + validation); + + /** + * Send transaction to Iroha without status validation + * @param tx - transaction for sending + * @return this + */ IntegrationTestFramework &sendTx( const shared_model::proto::Transaction &tx); + + /** + * Check current status of transaction + * @param hash - hash of transaction to check + * @return TransactonResponse object + */ shared_model::proto::TransactionResponse getTxStatus( const shared_model::crypto::Hash &hash); - template - IntegrationTestFramework &sendQuery(const shared_model::proto::Query &qry, - Lambda validation); + /** + * Send query to Iroha and validate the response + * @param qry - query to be requested + * @param validation - callback for query result check that receives object + * of type \relates shared_model::proto::QueryResponse by reference + * @return this + */ + IntegrationTestFramework &sendQuery( + const shared_model::proto::Query &qry, + std::function + validation); + + /** + * Send query to Iroha without response validation + * @param qry - query to be requested + * @return this + */ IntegrationTestFramework &sendQuery(const shared_model::proto::Query &qry); - template - IntegrationTestFramework &checkProposal(Lambda validation); + /** + * Request next proposal from queue and serve it with custom handler + * @param validation - callback that receives object of type \relates + * std::shared_ptr by reference + * @return this + */ + IntegrationTestFramework &checkProposal( + std::function validation); + + /** + * Request next proposal from queue and skip it + * @return this + */ IntegrationTestFramework &skipProposal(); - template - IntegrationTestFramework &checkBlock(Lambda validation); + /** + * Request next block from queue and serve it with custom handler + * @param validation - callback that receives object of type \relates + * std::shared_ptr by reference + * @return this + */ + IntegrationTestFramework &checkBlock( + std::function validation); + + /** + * Request next block from queue and skip it + * @return this + */ IntegrationTestFramework &skipBlock(); /** - * Shutdown iroha + * Shutdown ITF instance */ void done(); + static const std::string kDefaultDomain; + static const std::string kDefaultRole; + + static const std::string kAdminName; + static const std::string kAdminId; + static const std::string kAssetName; + + protected: /** * general way to fetch object from concurrent queue * @tparam Queue - Type of queue @@ -94,7 +200,7 @@ namespace integration_framework { * @param queue - queue instance for fetching * @param ref_for_insertion - reference to insert object * @param wait - time of waiting - * @param error_reason - reason if thehre is no appeared object at all + * @param error_reason - reason if there is no appeared object at all */ template void fetchFromQueue(Queue &queue, @@ -102,14 +208,6 @@ namespace integration_framework { const WaitTime &wait, const std::string &error_reason); - static const std::string kDefaultDomain; - static const std::string kDefaultRole; - - static const std::string kAdminName; - static const std::string kAdminId; - static const std::string kAssetName; - - protected: tbb::concurrent_queue proposal_queue_; tbb::concurrent_queue block_queue_; std::shared_ptr iroha_instance_ = @@ -124,66 +222,15 @@ namespace integration_framework { /// maximum time of waiting before appearing next committed block const milliseconds block_waiting = milliseconds(20000); - size_t maximum_block_size_; + size_t maximum_proposal_size_; private: logger::Logger log_ = logger::log("IntegrationTestFramework"); std::mutex queue_mu; std::condition_variable queue_cond; + std::function deleter_; }; - template - IntegrationTestFramework &IntegrationTestFramework::sendTx( - const shared_model::proto::Transaction &tx, Lambda validation) { - log_->info("send transaction"); - iroha_instance_->getIrohaInstance()->getCommandService()->Torii( - tx.getTransport()); - // fetch status of transaction - shared_model::proto::TransactionResponse status = getTxStatus(tx.hash()); - - // check validation function - validation(status); - return *this; - } - - template - IntegrationTestFramework &IntegrationTestFramework::sendQuery( - const shared_model::proto::Query &qry, Lambda validation) { - log_->info("send query"); - - iroha::protocol::QueryResponse response; - iroha_instance_->getIrohaInstance()->getQueryService()->Find( - qry.getTransport(), response); - auto query_response = - shared_model::proto::QueryResponse(std::move(response)); - - validation(query_response); - return *this; - } - - template - IntegrationTestFramework &IntegrationTestFramework::checkBlock( - Lambda validation) { - // fetch first from block queue - log_->info("check block"); - BlockType block; - fetchFromQueue(block_queue_, block, block_waiting, "missed block"); - validation(block); - return *this; - } - - template - IntegrationTestFramework &IntegrationTestFramework::checkProposal( - Lambda validation) { - log_->info("check proposal"); - // fetch first proposal from proposal queue - ProposalType proposal; - fetchFromQueue( - proposal_queue_, proposal, proposal_waiting, "missed proposal"); - validation(proposal); - return *this; - } - template void IntegrationTestFramework::fetchFromQueue( Queue &queue, diff --git a/test/framework/integration_framework/iroha_instance.cpp b/test/framework/integration_framework/iroha_instance.cpp index d178ddb2a3..e8ab64f815 100644 --- a/test/framework/integration_framework/iroha_instance.cpp +++ b/test/framework/integration_framework/iroha_instance.cpp @@ -33,9 +33,13 @@ namespace integration_framework { pg_conn_(getPostgreCredsOrDefault()), torii_port_(11501), internal_port_(50541), - proposal_delay_(5000ms), - vote_delay_(5000ms), - load_delay_(5000ms) {} + // proposal_timeout results in non-deterministic behavior due + // to thread scheduling and network + proposal_delay_(1h), + // not required due to solo consensus + vote_delay_(0ms), + // same as above + load_delay_(0ms) {} void IrohaInstance::makeGenesis(const shared_model::interface::Block &block) { instance_->storage->dropStorage(); diff --git a/test/framework/integration_framework/test_irohad.hpp b/test/framework/integration_framework/test_irohad.hpp index 6fe5eb500e..e5f66157f4 100644 --- a/test/framework/integration_framework/test_irohad.hpp +++ b/test/framework/integration_framework/test_irohad.hpp @@ -44,7 +44,7 @@ namespace integration_framework { proposal_delay, vote_delay, load_delay, - *std::unique_ptr(keypair.makeOldModel())) {} + keypair) {} auto &getCommandService() { return command_service; @@ -76,6 +76,12 @@ namespace integration_framework { }); log_->info("===> iroha initialized"); } + + void terminate() { + if (internal_server) { + internal_server->shutdown(); + } + } }; } // namespace integration_framework diff --git a/test/framework/result_fixture.hpp b/test/framework/result_fixture.hpp index 1c773afce6..c2e69f068e 100644 --- a/test/framework/result_fixture.hpp +++ b/test/framework/result_fixture.hpp @@ -22,22 +22,34 @@ namespace framework { namespace expected { + template + using ValueOf = iroha::expected::ValueOf; + template + using ErrorOf = iroha::expected::ErrorOf; /** - * @throws bad_get exception if result contains error - * @return value from result + * @return optional with value if present + * otherwise none */ template - typename ResultType::ValueType checkValueCase(const ResultType &result) { - return boost::get(result); + boost::optional> val(const ResultType &res) noexcept { + using RetType = boost::optional>; + return iroha::visit_in_place( + res, + [](ValueOf v) { return RetType(v); }, + [](ErrorOf e) -> RetType { return {}; }); } /** - * @throws bad_get exception if result contains value - * @return error from result + * @return optional with error if present + * otherwise none */ template - typename ResultType::ErrorType checkErrorCase(const ResultType &result) { - return boost::get(result); + boost::optional> err(const ResultType &res) noexcept { + using RetType = boost::optional>; + return iroha::visit_in_place( + res, + [](ValueOf v) -> RetType { return {}; }, + [](ErrorOf e) { return RetType(e); }); } } // namespace expected } // namespace framework diff --git a/test/framework/test_block_generator.cpp b/test/framework/test_block_generator.cpp deleted file mode 100644 index 3dd0a229cd..0000000000 --- a/test/framework/test_block_generator.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. - */ - -#include "framework/test_block_generator.hpp" -#include -#include -#include "model/commands/add_peer.hpp" -#include "model/commands/create_account.hpp" -#include "model/commands/create_asset.hpp" -#include "model/commands/create_domain.hpp" -#include "validators/permissions.hpp" -#include "model/sha3_hash.hpp" - -using namespace iroha; -using namespace iroha::model; - -namespace framework { - namespace generator { - Transaction getAddPeerTransaction(uint64_t create_time, - std::string address) { - Transaction transaction; - transaction.created_ts = create_time; - Signature sign{}; - transaction.signatures = {sign}; - - auto add_peer = std::make_shared(); - add_peer->peer.address = address; - add_peer->peer.pubkey = {}; - - transaction.commands = {add_peer}; - return transaction; - } - - Transaction getTestCreateTransaction(uint64_t create_time) { - Transaction transaction; - transaction.created_ts = create_time; - Signature sign{}; - transaction.signatures = {sign}; - - auto create_role = std::make_shared("user", role_perm_group); - - auto create_domain = std::make_shared(); - create_domain->domain_id = "test"; - create_domain->user_default_role = "user"; - - auto create_asset = std::make_shared(); - create_asset->domain_id = "test"; - create_asset->asset_name = "coin"; - create_asset->precision = 2; - - auto create_admin = std::make_shared(); - create_admin->domain_id = "test"; - create_admin->account_name = "admin"; - - auto create_acc = std::make_shared(); - create_acc->domain_id = "test"; - create_acc->account_name = "test"; - - transaction.commands = { - create_domain, create_asset, create_admin, create_acc}; - return transaction; - } - - Block generateBlock() { - Block block; - block.created_ts = 100500; - block.height = 1; - std::fill(block.prev_hash.begin(), block.prev_hash.end(), 0); - block.txs_number = 4; - - auto start_port = 10001u; - for (size_t i = start_port; i < start_port + block.txs_number; ++i) { - block.transactions.push_back(getAddPeerTransaction( - block.created_ts, "0.0.0.0:" + std::to_string(i))); - } - - block.transactions.push_back(getTestCreateTransaction(block.created_ts)); - block.txs_number++; - - Signature sign{}; - block.sigs = {sign}; - block.hash = hash(block); - return block; - } - } // namespace generator -} // namespace framework diff --git a/test/framework/test_block_generator.hpp b/test/framework/test_block_generator.hpp deleted file mode 100644 index c3ecc46266..0000000000 --- a/test/framework/test_block_generator.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. - */ - -#ifndef IROHA_TEST_BLOCK_GENERATOR_HPP -#define IROHA_TEST_BLOCK_GENERATOR_HPP - -#include "model/block.hpp" -#include "model/transaction.hpp" - -namespace framework { - namespace generator { - iroha::model::Transaction getAddPeerTransaction(uint64_t create_time, - std::string address); - - iroha::model::Transaction getTestCreateTransaction(uint64_t create_time); - - iroha::model::Block generateBlock(); - } // namespace generator -} // namespace framework -#endif // IROHA_TEST_BLOCK_GENERATOR_HPP diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index bb9997b377..a63e3c16a3 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -1,17 +1,21 @@ -set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/test_bin) +# +# Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. +# http://soramitsu.co.jp +# +# 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 +# +# http://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. +# add_subdirectory(acceptance) add_subdirectory(consensus) add_subdirectory(pipeline) - -addtest(client_test client_test.cpp) -target_link_libraries(client_test - pb_model_converters - json_model_converters - client - processors - server_runner - ) -target_include_directories(client_test PUBLIC - ${PROJECT_SOURCE_DIR}/iroha-cli - ) +add_subdirectory(transport) diff --git a/test/integration/acceptance/CMakeLists.txt b/test/integration/acceptance/CMakeLists.txt index 990bed52eb..7a1b183a2e 100644 --- a/test/integration/acceptance/CMakeLists.txt +++ b/test/integration/acceptance/CMakeLists.txt @@ -14,75 +14,59 @@ # See the License for the specific language governing permissions and # limitations under the License. # - -addtest(tx_acceptance_test tx_acceptance_test.cpp) -target_link_libraries(tx_acceptance_test - application - raw_block_insertion - model_generators - integration_framework - shared_model_stateless_validation - ) -addtest(get_transactions_test get_transactions_test.cpp) -target_link_libraries(get_transactions_test - application - integration_framework - shared_model_proto_builders - shared_model_stateless_validation - ) +add_library(acceptance_fixture acceptance_fixture.cpp) +target_link_libraries(acceptance_fixture + gtest + integration_framework + shared_model_proto_builders + ) addtest(add_asset_qty_test add_asset_qty_test.cpp) target_link_libraries(add_asset_qty_test - application - integration_framework - shared_model_proto_builders - shared_model_stateless_validation - ) + acceptance_fixture + ) + +addtest(create_account_test create_account_test.cpp) +target_link_libraries(create_account_test + acceptance_fixture + ) + +addtest(create_domain_test create_domain_test.cpp) +target_link_libraries(create_domain_test + acceptance_fixture + ) addtest(create_role_test create_role_test.cpp) target_link_libraries(create_role_test - application - integration_framework - shared_model_proto_builders - shared_model_stateless_validation - ) + acceptance_fixture + ) + +addtest(get_transactions_test get_transactions_test.cpp) +target_link_libraries(get_transactions_test + acceptance_fixture + ) addtest(invalid_fields_test invalid_fields_test.cpp) target_link_libraries(invalid_fields_test - application - integration_framework - shared_model_proto_builders - shared_model_stateless_validation - ) + acceptance_fixture + ) + +addtest(subtract_asset_qty_test subtract_asset_qty_test.cpp) +target_link_libraries(subtract_asset_qty_test + acceptance_fixture + ) addtest(transfer_asset_test transfer_asset_test.cpp) target_link_libraries(transfer_asset_test - application - integration_framework - shared_model_proto_builders - shared_model_stateless_validation - ) - -addtest(create_domain_test create_domain_test.cpp) -target_link_libraries(create_domain_test - application - integration_framework - shared_model_proto_builders - shared_model_stateless_validation - ) + acceptance_fixture + ) -addtest(subtract_asset_qty_test subtract_asset_qty_test.cpp) -target_link_libraries(subtract_asset_qty_test - application - integration_framework - shared_model_proto_builders - shared_model_stateless_validation - ) +addtest(tx_acceptance_test tx_acceptance_test.cpp) +target_link_libraries(tx_acceptance_test + acceptance_fixture + ) -addtest(create_account_test create_account_test.cpp) -target_link_libraries(create_account_test - application - integration_framework - shared_model_proto_builders - shared_model_stateless_validation - ) +addtest(tx_heavy_data tx_heavy_data.cpp) +target_link_libraries(tx_heavy_data + acceptance_fixture + ) diff --git a/test/integration/acceptance/acceptance_fixture.cpp b/test/integration/acceptance/acceptance_fixture.cpp new file mode 100644 index 0000000000..b414fc978c --- /dev/null +++ b/test/integration/acceptance/acceptance_fixture.cpp @@ -0,0 +1,109 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "integration/acceptance/acceptance_fixture.hpp" + +#include "datetime/time.hpp" +#include "framework/integration_framework/integration_test_framework.hpp" +#include "interfaces/utils/specified_visitor.hpp" + +AcceptanceFixture::AcceptanceFixture() + : kUser("user"), + kRole("role"), + kDomain(integration_framework::IntegrationTestFramework::kDefaultDomain), + kAsset(integration_framework::IntegrationTestFramework::kAssetName + "#" + + integration_framework::IntegrationTestFramework::kDefaultDomain), + kUserId( + kUser + "@" + + integration_framework::IntegrationTestFramework::kDefaultDomain), + kAdminKeypair( + shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair()), + kUserKeypair( + shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair()), + checkStatelessInvalid([](auto &status) { + ASSERT_TRUE(boost::apply_visitor( + shared_model::interface::SpecifiedVisitor< + shared_model::interface::StatelessFailedTxResponse>(), + status.get())); + }) {} + +TestUnsignedTransactionBuilder AcceptanceFixture::createUser( + const std::string &user, const shared_model::crypto::PublicKey &key) { + return TestUnsignedTransactionBuilder() + .createAccount( + user, + integration_framework::IntegrationTestFramework::kDefaultDomain, + key) + .creatorAccountId( + integration_framework::IntegrationTestFramework::kAdminId) + .createdTime(iroha::time::now()); +} + +TestUnsignedTransactionBuilder AcceptanceFixture::createUserWithPerms( + const std::string &user, + const shared_model::crypto::PublicKey &key, + const std::string &role_id, + std::vector perms) { + const auto user_id = user + "@" + + integration_framework::IntegrationTestFramework::kDefaultDomain; + return createUser(user, key) + .detachRole(user_id, + integration_framework::IntegrationTestFramework::kDefaultRole) + .createRole(role_id, perms) + .appendRole(user_id, role_id); +} + +shared_model::proto::Transaction AcceptanceFixture::makeUserWithPerms( + const std::string &role_name, const std::vector &perms) { + return createUserWithPerms(kUser, kUserKeypair.publicKey(), role_name, perms) + .build() + .signAndAddSignature(kAdminKeypair); +} + +shared_model::proto::Transaction AcceptanceFixture::makeUserWithPerms( + const std::vector &perms) { + return makeUserWithPerms(kRole, perms); +} + +template +auto AcceptanceFixture::base(Builder builder) -> decltype( + builder.creatorAccountId(std::string()).createdTime(uint64_t())) { + return builder.creatorAccountId(kUserId).createdTime(iroha::time::now()); +} + +template auto AcceptanceFixture::base( + TestUnsignedTransactionBuilder builder) + -> decltype( + builder.creatorAccountId(std::string()).createdTime(uint64_t())); +template auto AcceptanceFixture::base( + TestUnsignedQueryBuilder builder) + -> decltype( + builder.creatorAccountId(std::string()).createdTime(uint64_t())); + +auto AcceptanceFixture::baseTx() + -> decltype(base(TestUnsignedTransactionBuilder())) { + return base(TestUnsignedTransactionBuilder()); +} + +auto AcceptanceFixture::baseQry() + -> decltype(base(TestUnsignedQueryBuilder())) { + return base(TestUnsignedQueryBuilder()); +} + +template +auto AcceptanceFixture::complete(Builder builder) + -> decltype(builder.build().signAndAddSignature( + std::declval())) { + return builder.build().signAndAddSignature(kUserKeypair); +} + +template auto AcceptanceFixture::complete( + TestUnsignedTransactionBuilder builder) + -> decltype(builder.build().signAndAddSignature( + std::declval())); +template auto AcceptanceFixture::complete( + TestUnsignedQueryBuilder builder) + -> decltype(builder.build().signAndAddSignature( + std::declval())); diff --git a/test/integration/acceptance/acceptance_fixture.hpp b/test/integration/acceptance/acceptance_fixture.hpp new file mode 100644 index 0000000000..0f5ad76d68 --- /dev/null +++ b/test/integration/acceptance/acceptance_fixture.hpp @@ -0,0 +1,115 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_ACCEPTANCE_FIXTURE_HPP +#define IROHA_ACCEPTANCE_FIXTURE_HPP + +#include +#include +#include +#include +#include "cryptography/keypair.hpp" +#include "module/shared_model/builders/protobuf/test_query_builder.hpp" +#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" + +namespace shared_model { + namespace proto { + class TransactionResponse; + } // namespace proto +} // namespace shared_model + +/** + * Common values (user, domain, asset) + * and methods (create user, base transaction) for acceptance tests + */ +class AcceptanceFixture : public ::testing::Test { + public: + AcceptanceFixture(); + + /** + * Creates a set of transactions for user creation + * @param user is username of new user + * @param key is a public key of new user + * @return pre-built transaction + */ + TestUnsignedTransactionBuilder createUser( + const std::string &user, const shared_model::crypto::PublicKey &key); + + /** + * Creates a set of transactions for user creation with specified permissions + * @param user is username of new user + * @param key is a public key of new user + * @param role_id is new role of the user + * @param perms is a collections of permissions of the user + * @return pre-build transaction + */ + TestUnsignedTransactionBuilder createUserWithPerms( + const std::string &user, + const shared_model::crypto::PublicKey &key, + const std::string &role_id, + std::vector perms); + + /** + * Creates the transaction with the user creation commands + * @param role_name is a name of the role + * @param perms are the permissions of the user + * @return built tx and a hash of its payload + */ + shared_model::proto::Transaction makeUserWithPerms( + const std::string &role_name, const std::vector &perms); + + /** + * Creates the transaction with the user creation commands + * @param perms are the permissions of the user + * @return built tx and a hash of its payload + */ + shared_model::proto::Transaction makeUserWithPerms( + const std::vector &perms); + + /** + * Add default user creator account id and current created time to builder + * @tparam Builder type (transaction, query) + * @param builder object to modify + * @return builder containing creator account id and created time + */ + template + auto base(Builder builder) -> decltype( + builder.creatorAccountId(std::string()).createdTime(uint64_t())); + + /** + * Create valid base pre-built transaction + * @return pre-built tx + */ + auto baseTx() -> decltype(base(TestUnsignedTransactionBuilder())); + + /** + * Create valid base pre-built query + * @return pre-built query + */ + auto baseQry() -> decltype(base(TestUnsignedQueryBuilder())); + + /** + * Completes pre-built object + * @param builder is a pre-built object + * @return built object + */ + template + auto complete(Builder builder) + -> decltype(builder.build().signAndAddSignature( + std::declval())); + + const std::string kUser; + const std::string kRole; + const std::string kDomain; + const std::string kAsset; + const std::string kUserId; + const shared_model::crypto::Keypair kAdminKeypair; + const shared_model::crypto::Keypair kUserKeypair; + + const std::function + checkStatelessInvalid; +}; + +#endif // IROHA_ACCEPTANCE_FIXTURE_HPP diff --git a/test/integration/acceptance/add_asset_qty_test.cpp b/test/integration/acceptance/add_asset_qty_test.cpp index ed49ed805e..53559ac3ac 100644 --- a/test/integration/acceptance/add_asset_qty_test.cpp +++ b/test/integration/acceptance/add_asset_qty_test.cpp @@ -1,77 +1,24 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include -#include "backend/protobuf/transaction.hpp" -#include "cryptography/crypto_provider/crypto_defaults.hpp" -#include "datetime/time.hpp" -#include "framework/base_tx.hpp" #include "framework/integration_framework/integration_test_framework.hpp" +#include "integration/acceptance/acceptance_fixture.hpp" #include "validators/permissions.hpp" -#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" -using namespace std::string_literals; using namespace integration_framework; using namespace shared_model; -class AddAssetQuantity : public ::testing::Test { +class AddAssetQuantity : public AcceptanceFixture { public: - /** - * Creates the transaction with the user creation commands - * @param perms are the permissions of the user - * @return built tx and a hash of its payload - */ auto makeUserWithPerms(const std::vector &perms = { - iroha::model::can_add_asset_qty}) { - return framework::createUserWithPerms( - kUser, kUserKeypair.publicKey(), "role"s, perms) - .build() - .signAndAddSignature(kAdminKeypair); + shared_model::permissions::can_add_asset_qty}) { + return AcceptanceFixture::makeUserWithPerms(perms); } - /** - * Create valid base pre-built transaction - * @return pre-built tx - */ - auto baseTx() { - return TestUnsignedTransactionBuilder() - .txCounter(1) - .creatorAccountId(kUserId) - .createdTime(iroha::time::now()); - } - - /** - * Completes pre-built transaction - * @param builder is a pre-built tx - * @return built tx - */ - template - auto completeTx(TestTransactionBuilder builder) { - return builder.build().signAndAddSignature(kUserKeypair); - } - - const std::string kUser = "user"s; - const std::string kAsset = IntegrationTestFramework::kAssetName + "#test"; - const std::string kUserId = kUser + "@test"; const std::string kAmount = "1.0"; - const crypto::Keypair kAdminKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); - const crypto::Keypair kUserKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); }; /** @@ -80,12 +27,12 @@ class AddAssetQuantity : public ::testing::Test { * @then there is the tx in proposal */ TEST_F(AddAssetQuantity, Basic) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().addAssetQuantity(kUserId, kAsset, kAmount))) + .sendTx(complete(baseTx().addAssetQuantity(kUserId, kAsset, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) @@ -98,12 +45,12 @@ TEST_F(AddAssetQuantity, Basic) { * @then there is no tx in proposal */ TEST_F(AddAssetQuantity, NoPermissions) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms({iroha::model::can_get_my_txs})) + .sendTx(makeUserWithPerms({shared_model::permissions::can_get_my_txs})) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().addAssetQuantity(kUserId, kAsset, kAmount))) + .sendTx(complete(baseTx().addAssetQuantity(kUserId, kAsset, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -117,13 +64,13 @@ TEST_F(AddAssetQuantity, NoPermissions) { * (aka skipProposal throws) */ TEST_F(AddAssetQuantity, NegativeAmount) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().addAssetQuantity(kUserId, kAsset, "-1.0"))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx().addAssetQuantity(kUserId, kAsset, "-1.0")), + checkStatelessInvalid); } /** @@ -133,13 +80,13 @@ TEST_F(AddAssetQuantity, NegativeAmount) { * (aka skipProposal throws) */ TEST_F(AddAssetQuantity, ZeroAmount) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().addAssetQuantity(kUserId, kAsset, "0.0"))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx().addAssetQuantity(kUserId, kAsset, "0.0")), + checkStatelessInvalid); } /** @@ -150,23 +97,23 @@ TEST_F(AddAssetQuantity, ZeroAmount) { * second */ TEST_F(AddAssetQuantity, Uint256DestOverflow) { - const std::string &uint256_halfmax = + std::string uint256_halfmax = "723700557733226221397318656304299424082937404160253525246609900049457060" "2495.0"; // 2**252 - 1 - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() // Add first half of the maximum - .sendTx(completeTx( - baseTx().addAssetQuantity(kUserId, kAsset, uint256_halfmax))) + .sendTx( + complete(baseTx().addAssetQuantity(kUserId, kAsset, uint256_halfmax))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) // Add second half of the maximum - .sendTx(completeTx( - baseTx().addAssetQuantity(kUserId, kAsset, uint256_halfmax))) + .sendTx( + complete(baseTx().addAssetQuantity(kUserId, kAsset, uint256_halfmax))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -175,18 +122,17 @@ TEST_F(AddAssetQuantity, Uint256DestOverflow) { /** * @given some user with all required permissions - * @when execute tx with AddAssetQuantity command with inexistent account + * @when execute tx with AddAssetQuantity command with nonexistent account * @then there is an empty proposal */ -TEST_F(AddAssetQuantity, InexistentAccount) { - const std::string &inexistent = "inexist@test"s; - IntegrationTestFramework() +TEST_F(AddAssetQuantity, NonexistentAccount) { + std::string nonexistent = "inexist@test"; + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx( - completeTx(baseTx().addAssetQuantity(inexistent, kAsset, kAmount))) + .sendTx(complete(baseTx().addAssetQuantity(nonexistent, kAsset, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -195,18 +141,18 @@ TEST_F(AddAssetQuantity, InexistentAccount) { /** * @given some user with all required permissions - * @when execute tx with AddAssetQuantity command with inexistent asset + * @when execute tx with AddAssetQuantity command with nonexistent asset * @then there is an empty proposal */ -TEST_F(AddAssetQuantity, InexistentAsset) { - const std::string &inexistent = "inexist#test"s; - IntegrationTestFramework() +TEST_F(AddAssetQuantity, NonexistentAsset) { + std::string nonexistent = "inexist#test"; + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() .sendTx( - completeTx(baseTx().addAssetQuantity(kUserId, inexistent, kAmount))) + complete(baseTx().addAssetQuantity(kUserId, nonexistent, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -219,12 +165,12 @@ TEST_F(AddAssetQuantity, InexistentAsset) { * @then there is no tx in proposal */ TEST_F(AddAssetQuantity, OtherUser) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().addAssetQuantity( + .sendTx(complete(baseTx().addAssetQuantity( IntegrationTestFramework::kAdminId, kAsset, kAmount))) .skipProposal() .checkBlock( @@ -242,19 +188,18 @@ TEST_F(AddAssetQuantity, OtherDomain) { const auto kNewRole = "newrl"; const auto kNewDomain = "newdom"; const auto kNewUser = "newusr"; - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(2) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) // Generate new domain, new user and an asset .sendTx( - shared_model::proto::TransactionBuilder() - .txCounter(1) + TestUnsignedTransactionBuilder() .creatorAccountId( integration_framework::IntegrationTestFramework::kAdminId) .createdTime(iroha::time::now()) - .createRole( - kNewRole, - std::vector{iroha::model::can_get_my_txs}) + .createRole(kNewRole, + std::vector{ + shared_model::permissions::can_get_my_txs}) .createDomain(kNewDomain, kNewRole) .createAccount( kNewUser, @@ -268,6 +213,6 @@ TEST_F(AddAssetQuantity, OtherDomain) { // Make sure everything is committed .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 2); }) - .sendTx(completeTx(baseTx().addAssetQuantity(kNewUser, kAsset, kAmount))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx().addAssetQuantity(kNewUser, kAsset, kAmount)), + checkStatelessInvalid); } diff --git a/test/integration/acceptance/create_account_test.cpp b/test/integration/acceptance/create_account_test.cpp index e0f3512725..cd3ddef3a8 100644 --- a/test/integration/acceptance/create_account_test.cpp +++ b/test/integration/acceptance/create_account_test.cpp @@ -1,77 +1,24 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include -#include "backend/protobuf/transaction.hpp" -#include "cryptography/crypto_provider/crypto_defaults.hpp" -#include "datetime/time.hpp" -#include "framework/base_tx.hpp" #include "framework/integration_framework/integration_test_framework.hpp" +#include "integration/acceptance/acceptance_fixture.hpp" #include "validators/permissions.hpp" -#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" -using namespace std::string_literals; using namespace integration_framework; using namespace shared_model; -class CreateAccount : public ::testing::Test { +class CreateAccount : public AcceptanceFixture { public: - /** - * Creates the transaction with the user creation commands - * @param perms are the permissions of the user - * @return built tx and a hash of its payload - */ auto makeUserWithPerms(const std::vector &perms = { - iroha::model::can_create_account}) { - return framework::createUserWithPerms( - kUser, kUserKeypair.publicKey(), "role"s, perms) - .build() - .signAndAddSignature(kAdminKeypair); + shared_model::permissions::can_create_account}) { + return AcceptanceFixture::makeUserWithPerms(perms); } - /** - * Create valid base pre-built transaction - * @return pre-built tx - */ - auto baseTx() { - return TestUnsignedTransactionBuilder() - .txCounter(1) - .creatorAccountId(kUserId) - .createdTime(iroha::time::now()); - } - - /** - * Completes pre-built transaction - * @param builder is a pre-built tx - * @return built tx - */ - template - auto completeTx(TestTransactionBuilder builder) { - return builder.build().signAndAddSignature(kUserKeypair); - } - - const std::string kUser = "user"s; - const std::string kNewUser = "userone"s; - const std::string kDomain = IntegrationTestFramework::kDefaultDomain; - const std::string kUserId = kUser + "@" + kDomain; - const crypto::Keypair kAdminKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); - const crypto::Keypair kUserKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); + const std::string kNewUser = "userone"; const crypto::Keypair kNewUserKeypair = crypto::DefaultCryptoAlgorithmType::generateKeypair(); }; @@ -82,12 +29,12 @@ class CreateAccount : public ::testing::Test { * @then there is the tx in proposal */ TEST_F(CreateAccount, Basic) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createAccount( + .sendTx(complete(baseTx().createAccount( kNewUser, kDomain, kNewUserKeypair.publicKey()))) .skipProposal() .checkBlock( @@ -101,12 +48,12 @@ TEST_F(CreateAccount, Basic) { * @then there is no tx in proposal */ TEST_F(CreateAccount, NoPermissions) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms({iroha::model::can_get_my_txs})) + .sendTx(makeUserWithPerms({shared_model::permissions::can_get_my_txs})) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createAccount( + .sendTx(complete(baseTx().createAccount( kNewUser, kDomain, kNewUserKeypair.publicKey()))) .skipProposal() .checkBlock( @@ -116,18 +63,18 @@ TEST_F(CreateAccount, NoPermissions) { /** * @given some user with can_create_account permission - * @when execute tx with CreateAccount command with inexistent domain + * @when execute tx with CreateAccount command with nonexistent domain * @then there is no tx in proposal */ TEST_F(CreateAccount, NoDomain) { - const std::string inexistent_domain = "asdf"s; - IntegrationTestFramework() + const std::string nonexistent_domain = "asdf"; + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createAccount( - kNewUser, inexistent_domain, kNewUserKeypair.publicKey()))) + .sendTx(complete(baseTx().createAccount( + kNewUser, nonexistent_domain, kNewUserKeypair.publicKey()))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -139,15 +86,15 @@ TEST_F(CreateAccount, NoDomain) { * @when execute tx with CreateAccount command with already existing username * @then there is no tx in proposal */ -TEST_F(CreateAccount, ExistentName) { - const std::string &existent_name = kUser; - IntegrationTestFramework() +TEST_F(CreateAccount, ExistingName) { + std::string existing_name = kUser; + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createAccount( - existent_name, kDomain, kNewUserKeypair.publicKey()))) + .sendTx(complete(baseTx().createAccount( + existing_name, kDomain, kNewUserKeypair.publicKey()))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -160,12 +107,12 @@ TEST_F(CreateAccount, ExistentName) { * @then there is the tx in proposal */ TEST_F(CreateAccount, MaxLenName) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createAccount( + .sendTx(complete(baseTx().createAccount( std::string(32, 'a'), kDomain, kNewUserKeypair.publicKey()))) .skipProposal() .checkBlock( @@ -180,14 +127,14 @@ TEST_F(CreateAccount, MaxLenName) { * (aka skipProposal throws) */ TEST_F(CreateAccount, TooLongName) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createAccount( - std::string(33, 'a'), kDomain, kNewUserKeypair.publicKey()))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx().createAccount( + std::string(33, 'a'), kDomain, kNewUserKeypair.publicKey())), + checkStatelessInvalid); } /** @@ -197,13 +144,13 @@ TEST_F(CreateAccount, TooLongName) { * (aka skipProposal throws) */ TEST_F(CreateAccount, EmptyName) { - const std::string &empty_name = ""; - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + std::string empty_name = ""; + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createAccount( - empty_name, kDomain, kNewUserKeypair.publicKey()))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx().createAccount( + empty_name, kDomain, kNewUserKeypair.publicKey())), + checkStatelessInvalid); } diff --git a/test/integration/acceptance/create_domain_test.cpp b/test/integration/acceptance/create_domain_test.cpp index d8e3e73131..e67a8f5fc8 100644 --- a/test/integration/acceptance/create_domain_test.cpp +++ b/test/integration/acceptance/create_domain_test.cpp @@ -1,77 +1,24 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include -#include "backend/protobuf/transaction.hpp" -#include "cryptography/crypto_provider/crypto_defaults.hpp" -#include "datetime/time.hpp" -#include "framework/base_tx.hpp" #include "framework/integration_framework/integration_test_framework.hpp" +#include "integration/acceptance/acceptance_fixture.hpp" #include "validators/permissions.hpp" -#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" -using namespace std::string_literals; using namespace integration_framework; using namespace shared_model; -class CreateDomain : public ::testing::Test { +class CreateDomain : public AcceptanceFixture { public: - /** - * Creates the transaction with the user creation commands - * @param perms are the permissions of the user - * @return built tx - */ auto makeUserWithPerms(const std::vector &perms = { - iroha::model::can_create_domain}) { - return framework::createUserWithPerms( - kUser, kUserKeypair.publicKey(), kRole, perms) - .build() - .signAndAddSignature(kAdminKeypair); + shared_model::permissions::can_create_domain}) { + return AcceptanceFixture::makeUserWithPerms(perms); } - /** - * Create valid base pre-built transaction - * @return pre-built tx - */ - auto baseTx() { - return TestUnsignedTransactionBuilder() - .txCounter(1) - .creatorAccountId(kUserId) - .createdTime(iroha::time::now()); - } - - /** - * Completes pre-built transaction - * @param builder is a pre-built tx - * @return built tx - */ - template - auto completeTx(TestTransactionBuilder builder) { - return builder.build().signAndAddSignature(kUserKeypair); - } - - const std::string kRole = "role"s; - const std::string kUser = "user"s; const std::string kNewDomain = "newdomain"; - const std::string kUserId = kUser + "@test"; - const crypto::Keypair kAdminKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); - const crypto::Keypair kUserKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); }; /** @@ -80,12 +27,12 @@ class CreateDomain : public ::testing::Test { * @then there is the tx in proposal */ TEST_F(CreateDomain, Basic) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createDomain(kNewDomain, kRole))) + .sendTx(complete(baseTx().createDomain(kNewDomain, kRole))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) @@ -98,12 +45,12 @@ TEST_F(CreateDomain, Basic) { * @then there is no tx in proposal */ TEST_F(CreateDomain, NoPermissions) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms({iroha::model::can_get_my_txs})) + .sendTx(makeUserWithPerms({shared_model::permissions::can_get_my_txs})) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createDomain(kNewDomain, kRole))) + .sendTx(complete(baseTx().createDomain(kNewDomain, kRole))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -112,17 +59,17 @@ TEST_F(CreateDomain, NoPermissions) { /** * @given some user with can_create_domain permission - * @when execute tx with CreateDomain command with inexistent role + * @when execute tx with CreateDomain command with nonexistent role * @then there is no tx in proposal */ TEST_F(CreateDomain, NoRole) { - const std::string inexistent_role = "asdf"s; - IntegrationTestFramework() + const std::string nonexistent_role = "asdf"; + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createDomain(kNewDomain, inexistent_role))) + .sendTx(complete(baseTx().createDomain(kNewDomain, nonexistent_role))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -134,14 +81,14 @@ TEST_F(CreateDomain, NoRole) { * @when execute tx with CreateDomain command with already existing domain * @then there is no tx in proposal */ -TEST_F(CreateDomain, ExistentName) { - const std::string &existing_domain = IntegrationTestFramework::kDefaultDomain; - IntegrationTestFramework() +TEST_F(CreateDomain, ExistingName) { + std::string existing_domain = IntegrationTestFramework::kDefaultDomain; + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createDomain(existing_domain, kRole))) + .sendTx(complete(baseTx().createDomain(existing_domain, kRole))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -160,12 +107,12 @@ TEST_F(CreateDomain, MaxLenName) { "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad"; - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createDomain(maxLongDomain, kRole))) + .sendTx(complete(baseTx().createDomain(maxLongDomain, kRole))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) @@ -179,19 +126,13 @@ TEST_F(CreateDomain, MaxLenName) { * (aka skipProposal throws) */ TEST_F(CreateDomain, TooLongName) { - std::string tooLongDomain = - // 256 characters string - "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." - "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." - "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." - "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPads"; - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createDomain(std::string(257, 'a'), kRole))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx().createDomain(std::string(257, 'a'), kRole)), + checkStatelessInvalid); } /** @@ -201,14 +142,14 @@ TEST_F(CreateDomain, TooLongName) { * (aka skipProposal throws) */ TEST_F(CreateDomain, EmptyName) { - const std::string &empty_name = ""; - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + std::string empty_name = ""; + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createDomain(empty_name, kRole))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx().createDomain(empty_name, kRole)), + checkStatelessInvalid); } /** @@ -218,12 +159,12 @@ TEST_F(CreateDomain, EmptyName) { * (aka skipProposal throws) */ TEST_F(CreateDomain, DISABLED_EmptyRoleName) { - const std::string &empty_name = ""; - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + std::string empty_name = ""; + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().createDomain(kNewDomain, empty_name))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx().createDomain(kNewDomain, empty_name)), + checkStatelessInvalid); } diff --git a/test/integration/acceptance/create_role_test.cpp b/test/integration/acceptance/create_role_test.cpp index 8f75a234f2..25b7280053 100644 --- a/test/integration/acceptance/create_role_test.cpp +++ b/test/integration/acceptance/create_role_test.cpp @@ -1,87 +1,39 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include #include "backend/protobuf/transaction.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" #include "datetime/time.hpp" -#include "framework/base_tx.hpp" #include "framework/integration_framework/integration_test_framework.hpp" -#include "validators/permissions.hpp" +#include "integration/acceptance/acceptance_fixture.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "validators/permissions.hpp" -using namespace std::string_literals; using namespace integration_framework; using namespace shared_model; -class CreateRole : public ::testing::Test { +class CreateRole : public AcceptanceFixture { public: - /** - * Creates the transaction with the user creation commands - * @param perms are the permissions of the user - * @return built tx and a hash of its payload - */ auto makeUserWithPerms(const std::vector &perms = { - iroha::model::can_get_my_txs, - iroha::model::can_create_role}) { - return framework::createUserWithPerms( - kUser, kUserKeypair.publicKey(), kNewRole, perms) - .build() - .signAndAddSignature(kAdminKeypair); + shared_model::permissions::can_get_my_txs, + shared_model::permissions::can_create_role}) { + return AcceptanceFixture::makeUserWithPerms(kNewRole, perms); } - /** - * Create valid base pre-built transaction with CreateRole command - * @param perms is a permission list - * @param role_name is a name of the role - * @return pre-built tx - */ auto baseTx(const std::vector &perms, const std::string &role_name) { - return TestUnsignedTransactionBuilder() - .createRole(role_name, perms) - .txCounter(1) - .creatorAccountId(kUserId) - .createdTime(iroha::time::now()); + return AcceptanceFixture::baseTx().createRole(role_name, perms); } auto baseTx(const std::vector &perms = { - iroha::model::can_get_my_txs}) { + shared_model::permissions::can_get_my_txs}) { return baseTx(perms, kRole); } - /** - * Completes pre-built transaction - * @param builder is a pre-built tx - * @return built tx - */ - template - auto completeTx(TestTransactionBuilder builder) { - return builder.build().signAndAddSignature(kUserKeypair); - } - - const std::string kRole = "role"s; - const std::string kUser = "user"s; - const std::string kNewRole = "rl"s; - const std::string kUserId = kUser + "@test"; - const crypto::Keypair kAdminKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); - const crypto::Keypair kUserKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); + const std::string kNewRole = "rl"; }; /** @@ -90,12 +42,12 @@ class CreateRole : public ::testing::Test { * @then there is the tx in proposal */ TEST_F(CreateRole, Basic) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx())) + .sendTx(complete(baseTx())) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) @@ -108,12 +60,12 @@ TEST_F(CreateRole, Basic) { * @then there is an empty verified proposal */ TEST_F(CreateRole, HaveNoPerms) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms({iroha::model::can_get_my_txs})) + .sendTx(makeUserWithPerms({shared_model::permissions::can_get_my_txs})) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx())) + .sendTx(complete(baseTx())) .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) .done(); @@ -126,13 +78,13 @@ TEST_F(CreateRole, HaveNoPerms) { * (aka skipProposal throws) */ TEST_F(CreateRole, EmptyRole) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx({iroha::model::can_get_my_txs}, ""))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx({shared_model::permissions::can_get_my_txs}, "")), + checkStatelessInvalid); } /** @@ -142,13 +94,12 @@ TEST_F(CreateRole, EmptyRole) { * (aka skipProposal throws) */ TEST_F(CreateRole, EmptyPerms) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx({}))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx({})), checkStatelessInvalid); } /** @@ -158,14 +109,14 @@ TEST_F(CreateRole, EmptyPerms) { * (aka skipProposal throws) */ TEST_F(CreateRole, LongRoleName) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx( - baseTx({iroha::model::can_get_my_txs}, std::string(33, 'a')))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx({shared_model::permissions::can_get_my_txs}, + std::string(33, 'a'))), + checkStatelessInvalid); } /** @@ -174,13 +125,13 @@ TEST_F(CreateRole, LongRoleName) { * @then the tx is comitted */ TEST_F(CreateRole, MaxLenRoleName) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx( - baseTx({iroha::model::can_get_my_txs}, std::string(32, 'a')))) + .sendTx(complete(baseTx({shared_model::permissions::can_get_my_txs}, + std::string(32, 'a')))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) @@ -188,32 +139,37 @@ TEST_F(CreateRole, MaxLenRoleName) { } /** + * TODO 15/05/2018 andrei: IR-1267 fix builders setting default value for + * nonexisting permissions * @given some user with can_create_role permission - * @when execute tx with CreateRole command with inexistent permission name + * @when execute tx with CreateRole command with nonexistent permission name * @then the tx hasn't passed stateless validation * (aka skipProposal throws) */ -TEST_F(CreateRole, DISABLED_InexistentPerm) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) +TEST_F(CreateRole, DISABLED_NonexistentPerm) { + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx({"this_permission_doesnt_exist"}))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx({"this_permission_doesnt_exist"})), + checkStatelessInvalid); } /** * @given some user with can_create_role permission - * @when execute tx with CreateRole command with existent role name + * @when execute tx with CreateRole command with existing role name * @then there is an empty verified proposal */ -TEST_F(CreateRole, DISABLED_ExistingRole) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) +TEST_F(CreateRole, ExistingRole) { + IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx())); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete( + baseTx({shared_model::permissions::can_get_my_txs}, kNewRole))) + .skipProposal() + .checkBlock( + [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }); } diff --git a/test/integration/acceptance/get_transactions_test.cpp b/test/integration/acceptance/get_transactions_test.cpp index a290197902..1319e8f644 100644 --- a/test/integration/acceptance/get_transactions_test.cpp +++ b/test/integration/acceptance/get_transactions_test.cpp @@ -1,49 +1,33 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include #include "backend/protobuf/transaction.hpp" #include "builders/protobuf/queries.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" -#include "datetime/time.hpp" -#include "framework/base_tx.hpp" #include "framework/integration_framework/integration_test_framework.hpp" +#include "integration/acceptance/acceptance_fixture.hpp" #include "interfaces/utils/specified_visitor.hpp" -#include "validators/permissions.hpp" #include "utils/query_error_response_visitor.hpp" +#include "validators/permissions.hpp" -using namespace std::string_literals; using namespace integration_framework; using namespace shared_model; -class GetTransactions : public ::testing::Test { +class GetTransactions : public AcceptanceFixture { public: /** * Creates the transaction with the user creation commands * @param perms are the permissions of the user * @return built tx and a hash of its payload */ - auto makeUserWithPerms(const std::vector &perms) { + auto makeUserWithPerms(const std::vector &perms = { + shared_model::permissions::can_get_my_txs}) { auto new_perms = perms; - new_perms.push_back(iroha::model::can_set_quorum); - return framework::createUserWithPerms( - kUser, kUserKeypair.publicKey(), kNewRole, new_perms) - .build() - .signAndAddSignature(kAdminKeypair); + new_perms.push_back(shared_model::permissions::can_set_quorum); + return AcceptanceFixture::makeUserWithPerms(kNewRole, new_perms); } /** @@ -52,13 +36,7 @@ class GetTransactions : public ::testing::Test { * Note: It should affect the ledger minimally */ auto dummyTx() { - return shared_model::proto::TransactionBuilder() - .setAccountQuorum(kUserId, 1) - .txCounter(1) - .creatorAccountId(kUserId) - .createdTime(iroha::time::now()) - .build() - .signAndAddSignature(kUserKeypair); + return complete(AcceptanceFixture::baseTx().setAccountQuorum(kUserId, 1)); } /** @@ -67,22 +45,11 @@ class GetTransactions : public ::testing::Test { * @return built query */ auto makeQuery(const crypto::Hash &hash) { - return proto::QueryBuilder() - .createdTime(iroha::time::now()) - .creatorAccountId(kUserId) - .queryCounter(1) - .getTransactions(std::vector{hash}) - .build() - .signAndAddSignature(kUserKeypair); + return complete(baseQry().queryCounter(1).getTransactions( + std::vector{hash})); } - const std::string kUser = "user"s; - const std::string kNewRole = "rl"s; - const std::string kUserId = kUser + "@test"; - const crypto::Keypair kAdminKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); - const crypto::Keypair kUserKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); + const std::string kNewRole = "rl"; }; /** @@ -99,9 +66,9 @@ TEST_F(GetTransactions, HaveNoGetPerms) { }; auto dummy_tx = dummyTx(); - IntegrationTestFramework() + IntegrationTestFramework(2) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms({iroha::model::can_read_assets})) + .sendTx(makeUserWithPerms({shared_model::permissions::can_read_assets})) .sendTx(dummy_tx) .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 2); }) @@ -125,9 +92,9 @@ TEST_F(GetTransactions, HaveGetAllTx) { ASSERT_EQ(*resp.value()->transactions()[0].operator->(), dummy_tx); }; - IntegrationTestFramework() + IntegrationTestFramework(2) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms({iroha::model::can_get_all_txs})) + .sendTx(makeUserWithPerms({shared_model::permissions::can_get_all_txs})) .sendTx(dummy_tx) .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 2); }) @@ -151,9 +118,9 @@ TEST_F(GetTransactions, HaveGetMyTx) { ASSERT_EQ(*resp.value()->transactions()[0].operator->(), dummy_tx); }; - IntegrationTestFramework() + IntegrationTestFramework(2) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms({iroha::model::can_get_my_txs})) + .sendTx(makeUserWithPerms()) .sendTx(dummy_tx) .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 2); }) @@ -176,28 +143,27 @@ TEST_F(GetTransactions, InvalidSignatures) { interface::StatefulFailedErrorResponse>>(resp->get())); }; - auto query = proto::QueryBuilder() - .createdTime(iroha::time::now()) - .creatorAccountId(kUserId) + auto query = baseQry() .queryCounter(1) .getTransactions(std::vector{dummy_tx.hash()}) .build() .signAndAddSignature( crypto::DefaultCryptoAlgorithmType::generateKeypair()); - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms({iroha::model::can_get_my_txs})) + .sendTx(makeUserWithPerms()) + .skipBlock() .sendQuery(query, check) .done(); } /** * @given some user with only can_get_my_txs permission - * @when query GetTransactions with inexistent hash + * @when query GetTransactions with nonexistent hash * @then TransactionsResponse with no transactions */ -TEST_F(GetTransactions, InexistentHash) { +TEST_F(GetTransactions, NonexistentHash) { auto check = [](auto &status) { auto resp = boost::apply_visitor( interface::SpecifiedVisitor(), @@ -206,9 +172,9 @@ TEST_F(GetTransactions, InexistentHash) { ASSERT_EQ(resp.value()->transactions().size(), 0); }; - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms({iroha::model::can_get_my_txs})) + .sendTx(makeUserWithPerms()) .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) .sendQuery(makeQuery(crypto::Hash(std::string(32, '0'))), check) @@ -219,9 +185,8 @@ TEST_F(GetTransactions, InexistentHash) { * @given some user with can_get_my_txs * @when query GetTransactions of existing transaction of the other user * @then TransactionsResponse with no transactions - * TODO(@l4l) 02/01/18 Should be enabled after resolving IR-1039 */ -TEST_F(GetTransactions, DISABLED_OtherUserTx) { +TEST_F(GetTransactions, OtherUserTx) { auto check = [](auto &status) { auto resp = boost::apply_visitor( interface::SpecifiedVisitor(), @@ -230,8 +195,8 @@ TEST_F(GetTransactions, DISABLED_OtherUserTx) { ASSERT_EQ(resp.value()->transactions().size(), 0); }; - auto tx = makeUserWithPerms({iroha::model::can_get_my_txs}); - IntegrationTestFramework() + auto tx = makeUserWithPerms(); + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(tx) .checkBlock( diff --git a/test/integration/acceptance/invalid_fields_test.cpp b/test/integration/acceptance/invalid_fields_test.cpp index e5bdbbfdec..69f5ceb5b2 100644 --- a/test/integration/acceptance/invalid_fields_test.cpp +++ b/test/integration/acceptance/invalid_fields_test.cpp @@ -1,42 +1,17 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include -#include "backend/protobuf/transaction.hpp" #include "block.pb.h" -#include "builders/protobuf/transaction.hpp" -#include "cryptography/crypto_provider/crypto_defaults.hpp" -#include "datetime/time.hpp" #include "framework/integration_framework/integration_test_framework.hpp" -#include "interfaces/utils/specified_visitor.hpp" -#include "module/shared_model/validators/validators.hpp" +#include "integration/acceptance/acceptance_fixture.hpp" -using namespace std::string_literals; using namespace integration_framework; using namespace shared_model; -class InvalidField : public ::testing::Test { - public: - const std::string kUser = "user"s; - const crypto::Keypair kAdminKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); - const crypto::Keypair kUserKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); -}; +class InvalidField : public AcceptanceFixture {}; /** * @given tx with CreateAccount command and invalid signature size @@ -44,26 +19,14 @@ class InvalidField : public ::testing::Test { * @then Torii returns stateless fail */ TEST_F(InvalidField, Signature) { - auto tx = proto::TransactionBuilder() - .createAccount(kUser, "test", kUserKeypair.publicKey()) - .txCounter(1) - .creatorAccountId("admin@test") - .createdTime(iroha::time::now()) - .build() - .signAndAddSignature(kAdminKeypair) - .getTransport(); + auto tx = complete(baseTx()).getTransport(); // extend signature to invalid size - auto sig = tx.mutable_signature(0)->mutable_signature(); + auto sig = tx.mutable_signatures(0)->mutable_signature(); sig->resize(sig->size() + 1, 'a'); - auto check = [](auto &resp) { - ASSERT_TRUE(boost::apply_visitor( - interface::SpecifiedVisitor(), - resp.get())); - }; - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(proto::Transaction(tx), check) + .sendTx(proto::Transaction(tx), checkStatelessInvalid) .done(); } @@ -73,25 +36,13 @@ TEST_F(InvalidField, Signature) { * @then Torii returns stateless fail */ TEST_F(InvalidField, Pubkey) { - auto tx = proto::TransactionBuilder() - .createAccount(kUser, "test", kUserKeypair.publicKey()) - .txCounter(1) - .creatorAccountId("admin@test") - .createdTime(iroha::time::now()) - .build() - .signAndAddSignature(kAdminKeypair) - .getTransport(); + auto tx = complete(baseTx()).getTransport(); // extend public key to invalid size - auto pkey = tx.mutable_signature(0)->mutable_pubkey(); + auto pkey = tx.mutable_signatures(0)->mutable_pubkey(); pkey->resize(pkey->size() + 1, 'a'); - auto check = [](auto &resp) { - ASSERT_TRUE(boost::apply_visitor( - interface::SpecifiedVisitor(), - resp.get())); - }; - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(proto::Transaction(tx), check) + .sendTx(proto::Transaction(tx), checkStatelessInvalid) .done(); } diff --git a/test/integration/acceptance/subtract_asset_qty_test.cpp b/test/integration/acceptance/subtract_asset_qty_test.cpp index 5cb78ecf57..dd48600264 100644 --- a/test/integration/acceptance/subtract_asset_qty_test.cpp +++ b/test/integration/acceptance/subtract_asset_qty_test.cpp @@ -1,34 +1,20 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include #include "backend/protobuf/transaction.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" -#include "datetime/time.hpp" -#include "framework/base_tx.hpp" #include "framework/integration_framework/integration_test_framework.hpp" -#include "validators/permissions.hpp" +#include "integration/acceptance/acceptance_fixture.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "validators/permissions.hpp" -using namespace std::string_literals; using namespace integration_framework; using namespace shared_model; -class SubtractAssetQuantity : public ::testing::Test { +class SubtractAssetQuantity : public AcceptanceFixture { public: /** * Creates the transaction with the user creation commands @@ -36,50 +22,19 @@ class SubtractAssetQuantity : public ::testing::Test { * @return built tx and a hash of its payload */ auto makeUserWithPerms(const std::vector &perms = { - iroha::model::can_subtract_asset_qty, - iroha::model::can_add_asset_qty}) { - return framework::createUserWithPerms( - kUser, kUserKeypair.publicKey(), "role"s, perms) - .build() - .signAndAddSignature(kAdminKeypair); - } - - /** - * Create valid base pre-built transaction - * @return pre-built tx - */ - auto baseTx() { - return TestUnsignedTransactionBuilder() - .txCounter(1) - .creatorAccountId(kUserId) - .createdTime(iroha::time::now()); - } - - /** - * Completes pre-built transaction - * @param builder is a pre-built tx - * @return built tx - */ - template - auto completeTx(TestTransactionBuilder builder) { - return builder.build().signAndAddSignature(kUserKeypair); + shared_model::permissions::can_subtract_asset_qty, + shared_model::permissions::can_add_asset_qty}) { + return AcceptanceFixture::makeUserWithPerms(perms); } /** * @return built tx that adds kAmount assets to the users */ auto replenish() { - return completeTx(baseTx().addAssetQuantity(kUserId, kAsset, kAmount)); + return complete(baseTx().addAssetQuantity(kUserId, kAsset, kAmount)); } - const std::string kUser = "user"s; - const std::string kAsset = IntegrationTestFramework::kAssetName + "#test"; - const std::string kUserId = kUser + "@test"; const std::string kAmount = "1.0"; - const crypto::Keypair kAdminKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); - const crypto::Keypair kUserKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); }; /** @@ -88,14 +43,16 @@ class SubtractAssetQuantity : public ::testing::Test { * @then there is the tx in proposal */ TEST_F(SubtractAssetQuantity, Everything) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) + .skipProposal() + .skipBlock() .sendTx(replenish()) .skipProposal() .skipBlock() .sendTx( - completeTx(baseTx().subtractAssetQuantity(kUserId, kAsset, kAmount))) + complete(baseTx().subtractAssetQuantity(kUserId, kAsset, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) @@ -109,14 +66,15 @@ TEST_F(SubtractAssetQuantity, Everything) { * @then there is no tx in proposal */ TEST_F(SubtractAssetQuantity, Overdraft) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) + .skipProposal() + .skipBlock() .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx( - completeTx(baseTx().subtractAssetQuantity(kUserId, kAsset, "2.0"))) + .sendTx(complete(baseTx().subtractAssetQuantity(kUserId, kAsset, "2.0"))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -129,14 +87,16 @@ TEST_F(SubtractAssetQuantity, Overdraft) { * @then there is no tx in proposal */ TEST_F(SubtractAssetQuantity, NoPermissions) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms({iroha::model::can_add_asset_qty})) + .sendTx(makeUserWithPerms({shared_model::permissions::can_add_asset_qty})) + .skipProposal() + .skipBlock() .sendTx(replenish()) .skipProposal() .skipBlock() .sendTx( - completeTx(baseTx().subtractAssetQuantity(kUserId, kAsset, kAmount))) + complete(baseTx().subtractAssetQuantity(kUserId, kAsset, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -150,15 +110,14 @@ TEST_F(SubtractAssetQuantity, NoPermissions) { * (aka skipProposal throws) */ TEST_F(SubtractAssetQuantity, NegativeAmount) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(2) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx( - completeTx(baseTx().subtractAssetQuantity(kUserId, kAsset, "-1.0"))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx().subtractAssetQuantity(kUserId, kAsset, "-1.0")), + checkStatelessInvalid); } /** @@ -168,32 +127,33 @@ TEST_F(SubtractAssetQuantity, NegativeAmount) { * (aka skipProposal throws) */ TEST_F(SubtractAssetQuantity, ZeroAmount) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(2) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx( - completeTx(baseTx().subtractAssetQuantity(kUserId, kAsset, "0.0"))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(complete(baseTx().subtractAssetQuantity(kUserId, kAsset, "0.0")), + checkStatelessInvalid); } /** * @given some user with all required permissions - * @when execute tx with SubtractAssetQuantity command with inexistent account + * @when execute tx with SubtractAssetQuantity command with nonexitent account * @then there is an empty proposal */ -TEST_F(SubtractAssetQuantity, InexistentAccount) { - const std::string &inexistent = "inexist@test"s; - IntegrationTestFramework() +TEST_F(SubtractAssetQuantity, NonexistentAccount) { + std::string nonexistent = "inexist@test"; + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) + .skipProposal() + .skipBlock() .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx(completeTx( - baseTx().subtractAssetQuantity(inexistent, kAsset, kAmount))) + .sendTx(complete( + baseTx().subtractAssetQuantity(nonexistent, kAsset, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -202,19 +162,21 @@ TEST_F(SubtractAssetQuantity, InexistentAccount) { /** * @given some user with all required permissions - * @when execute tx with SubtractAssetQuantity command with inexistent asset + * @when execute tx with SubtractAssetQuantity command with nonexistent asset * @then there is an empty proposal */ -TEST_F(SubtractAssetQuantity, InexistentAsset) { - const std::string &inexistent = "inexist#test"s; - IntegrationTestFramework() +TEST_F(SubtractAssetQuantity, NonexistentAsset) { + std::string nonexistent = "inexist#test"; + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) + .skipProposal() + .skipBlock() .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx(completeTx( - baseTx().subtractAssetQuantity(kUserId, inexistent, kAmount))) + .sendTx(complete( + baseTx().subtractAssetQuantity(kUserId, nonexistent, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -227,13 +189,15 @@ TEST_F(SubtractAssetQuantity, InexistentAsset) { * @then there is no tx in proposal */ TEST_F(SubtractAssetQuantity, OtherUser) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) + .skipProposal() + .skipBlock() .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx(completeTx(baseTx().subtractAssetQuantity( + .sendTx(complete(baseTx().subtractAssetQuantity( IntegrationTestFramework::kAdminId, kAsset, kAmount))) .skipProposal() .checkBlock( diff --git a/test/integration/acceptance/transfer_asset_test.cpp b/test/integration/acceptance/transfer_asset_test.cpp index cb06c4657e..34f87e42e5 100644 --- a/test/integration/acceptance/transfer_asset_test.cpp +++ b/test/integration/acceptance/transfer_asset_test.cpp @@ -1,39 +1,22 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include -#include +#include "acceptance_fixture.hpp" #include "backend/protobuf/transaction.hpp" #include "builders/protobuf/queries.hpp" #include "builders/protobuf/transaction.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" -#include "datetime/time.hpp" -#include "framework/base_tx.hpp" #include "framework/integration_framework/integration_test_framework.hpp" -#include "interfaces/utils/specified_visitor.hpp" -#include "validators/permissions.hpp" -#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" #include "utils/query_error_response_visitor.hpp" +#include "validators/permissions.hpp" -using namespace std::string_literals; using namespace integration_framework; using namespace shared_model; -class TransferAsset : public ::testing::Test { +class TransferAsset : public AcceptanceFixture { public: /** * Creates the transaction with the user creation commands @@ -44,7 +27,7 @@ class TransferAsset : public ::testing::Test { const crypto::Keypair &key, const std::vector &perms, const std::string &role) { - return framework::createUserWithPerms(user, key.publicKey(), role, perms) + return createUserWithPerms(user, key.publicKey(), role, perms) .build() .signAndAddSignature(kAdminKeypair); } @@ -59,7 +42,6 @@ class TransferAsset : public ::testing::Test { const std::string &amount) { const std::string kUserId = user + "@test"; return proto::TransactionBuilder() - .txCounter(1) .creatorAccountId(kUserId) .createdTime(iroha::time::now()) .addAssetQuantity(kUserId, kAsset, amount) @@ -73,7 +55,6 @@ class TransferAsset : public ::testing::Test { */ auto baseTx() { return TestUnsignedTransactionBuilder() - .txCounter(1) .creatorAccountId(kUser1 + "@test") .createdTime(iroha::time::now()); } @@ -88,24 +69,22 @@ class TransferAsset : public ::testing::Test { return builder.build().signAndAddSignature(kUser1Keypair); } - const std::string kAsset = IntegrationTestFramework::kAssetName + "#test"; - const std::string kAmount = "1.0"s; - const std::string kDesc = "description"s; - const std::string kUser1 = "userone"s; - const std::string kUser2 = "usertwo"s; - const std::string kRole1 = "roleone"s; - const std::string kRole2 = "roletwo"s; + const std::string kAmount = "1.0"; + const std::string kDesc = "description"; + const std::string kUser1 = "userone"; + const std::string kUser2 = "usertwo"; + const std::string kRole1 = "roleone"; + const std::string kRole2 = "roletwo"; const std::string kUser1Id = kUser1 + "@test"; const std::string kUser2Id = kUser2 + "@test"; - const crypto::Keypair kAdminKeypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); const crypto::Keypair kUser1Keypair = crypto::DefaultCryptoAlgorithmType::generateKeypair(); const crypto::Keypair kUser2Keypair = crypto::DefaultCryptoAlgorithmType::generateKeypair(); - const std::vector kPerms{iroha::model::can_add_asset_qty, - iroha::model::can_transfer, - iroha::model::can_receive}; + const std::vector kPerms{ + shared_model::permissions::can_add_asset_qty, + shared_model::permissions::can_transfer, + shared_model::permissions::can_receive}; }; /** @@ -114,18 +93,15 @@ class TransferAsset : public ::testing::Test { * @then there is the tx in proposal */ TEST_F(TransferAsset, Basic) { - IntegrationTestFramework() + IntegrationTestFramework(4) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() .sendTx(completeTx( baseTx().transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, kAmount))) - .skipProposal() .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) + [](auto &block) { ASSERT_EQ(block->transactions().size(), 4); }) .done(); } @@ -135,11 +111,17 @@ TEST_F(TransferAsset, Basic) { * @then there is an empty proposal */ TEST_F(TransferAsset, WithOnlyCanTransferPerm) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms( - kUser1, kUser1Keypair, {iroha::model::can_transfer}, kRole1)) + .sendTx(makeUserWithPerms(kUser1, + kUser1Keypair, + {shared_model::permissions::can_transfer}, + kRole1)) + .skipProposal() + .skipBlock() .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) + .skipProposal() + .skipBlock() .sendTx(addAssets(kUser1, kUser1Keypair)) .skipProposal() .skipBlock() @@ -158,11 +140,17 @@ TEST_F(TransferAsset, WithOnlyCanTransferPerm) { * @then there is an empty proposal */ TEST_F(TransferAsset, WithOnlyCanReceivePerm) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms( - kUser1, kUser1Keypair, {iroha::model::can_receive}, kRole1)) + .sendTx(makeUserWithPerms(kUser1, + kUser1Keypair, + {shared_model::permissions::can_receive}, + kRole1)) + .skipProposal() + .skipBlock() .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) + .skipProposal() + .skipBlock() .sendTx(addAssets(kUser1, kUser1Keypair)) .skipProposal() .skipBlock() @@ -177,19 +165,21 @@ TEST_F(TransferAsset, WithOnlyCanReceivePerm) { /** * @given some user with all required permissions - * @when execute tx with TransferAsset command to inexistent destination + * @when execute tx with TransferAsset command to nonexistent destination * @then there is an empty proposal */ -TEST_F(TransferAsset, InexistentDest) { - const std::string &inexistent = "inexist@test"s; - IntegrationTestFramework() +TEST_F(TransferAsset, NonexistentDest) { + std::string nonexistent = "inexist@test"; + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) + .skipProposal() + .skipBlock() .sendTx(addAssets(kUser1, kUser1Keypair)) .skipProposal() .skipBlock() .sendTx(baseTx() - .transferAsset(kUser1Id, inexistent, kAsset, kDesc, kAmount) + .transferAsset(kUser1Id, nonexistent, kAsset, kDesc, kAmount) .build() .signAndAddSignature(kUser1Keypair)) .checkBlock( @@ -199,22 +189,27 @@ TEST_F(TransferAsset, InexistentDest) { /** * @given pair of users with all required permissions - * @when execute tx with TransferAsset command with inexistent asset + * @when execute tx with TransferAsset command with nonexistent asset * @then there is an empty proposal */ -TEST_F(TransferAsset, InexistentAsset) { - const std::string &inexistent = "inexist#test"s; - IntegrationTestFramework() +TEST_F(TransferAsset, NonexistentAsset) { + std::string nonexistent = "inexist#test"; + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) + .skipProposal() + .skipBlock() .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) + .skipProposal() + .skipBlock() .sendTx(addAssets(kUser1, kUser1Keypair)) .skipProposal() .skipBlock() - .sendTx(baseTx() - .transferAsset(kUser1Id, kUser2Id, inexistent, kDesc, kAmount) - .build() - .signAndAddSignature(kUser1Keypair)) + .sendTx( + baseTx() + .transferAsset(kUser1Id, kUser2Id, nonexistent, kDesc, kAmount) + .build() + .signAndAddSignature(kUser1Keypair)) .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) .done(); @@ -227,8 +222,8 @@ TEST_F(TransferAsset, InexistentAsset) { * (aka skipProposal throws) */ TEST_F(TransferAsset, NegativeAmount) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(3) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) .sendTx(addAssets(kUser1, kUser1Keypair)) @@ -237,8 +232,8 @@ TEST_F(TransferAsset, NegativeAmount) { .sendTx(baseTx() .transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, "-1.0") .build() - .signAndAddSignature(kUser1Keypair)); - ASSERT_ANY_THROW(itf.skipProposal()); + .signAndAddSignature(kUser1Keypair), + checkStatelessInvalid); } /** @@ -248,8 +243,8 @@ TEST_F(TransferAsset, NegativeAmount) { * (aka skipProposal throws) */ TEST_F(TransferAsset, ZeroAmount) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(3) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) .sendTx(addAssets(kUser1, kUser1Keypair)) @@ -258,8 +253,8 @@ TEST_F(TransferAsset, ZeroAmount) { .sendTx(baseTx() .transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, "0.0") .build() - .signAndAddSignature(kUser1Keypair)); - ASSERT_ANY_THROW(itf.skipProposal()); + .signAndAddSignature(kUser1Keypair), + checkStatelessInvalid); } /** @@ -268,18 +263,15 @@ TEST_F(TransferAsset, ZeroAmount) { * @then it passed to the proposal */ TEST_F(TransferAsset, EmptyDesc) { - IntegrationTestFramework() + IntegrationTestFramework(4) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() .sendTx(completeTx( baseTx().transferAsset(kUser1Id, kUser2Id, kAsset, "", kAmount))) - .skipProposal() .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) + [](auto &block) { ASSERT_EQ(block->transactions().size(), 4); }) .done(); } @@ -293,20 +285,14 @@ TEST_F(TransferAsset, LongDesc) { std::string long_desc(100000, 'a'); auto invalid_tx = completeTx( baseTx().transferAsset(kUser1Id, kUser2Id, kAsset, long_desc, kAmount)); - using ExpectedStatusType = shared_model::detail::PolymorphicWrapper< - shared_model::interface::StatelessFailedTxResponse>; - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(3) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) .sendTx(addAssets(kUser1, kUser1Keypair)) .skipProposal() .skipBlock() - .sendTx(invalid_tx, - [](shared_model::proto::TransactionResponse &status) { - // check if returned status is as expected - ASSERT_NO_THROW(boost::get(status.get())); - }) + .sendTx(invalid_tx, checkStatelessInvalid) .done(); } @@ -316,10 +302,14 @@ TEST_F(TransferAsset, LongDesc) { * @then there is an empty proposal */ TEST_F(TransferAsset, MoreThanHas) { - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) + .skipProposal() + .skipBlock() .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) + .skipProposal() + .skipBlock() .sendTx(addAssets(kUser1, kUser1Keypair, "50.0")) .skipProposal() .skipBlock() @@ -340,24 +330,31 @@ TEST_F(TransferAsset, MoreThanHas) { * second */ TEST_F(TransferAsset, Uint256DestOverflow) { - const std::string &uint256_halfmax = + std::string uint256_halfmax = "723700557733226221397318656304299424082937404160253525246609900049457060" "2495.0"; // 2**252 - 1 - IntegrationTestFramework() + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) + .skipProposal() + .skipBlock() .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) + .skipProposal() + .skipBlock() .sendTx(addAssets(kUser1, kUser1Keypair, uint256_halfmax)) .skipProposal() .skipBlock() // Send first half of the maximum .sendTx(completeTx(baseTx().transferAsset( kUser1Id, kUser2Id, kAsset, kDesc, uint256_halfmax))) + .skipProposal() + .checkBlock( + [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) // Restore self balance .sendTx(addAssets(kUser1, kUser1Keypair, uint256_halfmax)) .skipProposal() .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 2); }) + [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) // Send second half of the maximum .sendTx(completeTx(baseTx().transferAsset( kUser1Id, kUser2Id, kAsset, kDesc, uint256_halfmax))) @@ -375,15 +372,15 @@ TEST_F(TransferAsset, Uint256DestOverflow) { * (aka skipProposal throws) */ TEST_F(TransferAsset, SourceIsDest) { - IntegrationTestFramework itf; - itf.setInitialState(kAdminKeypair) + IntegrationTestFramework(2) + .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) .sendTx(addAssets(kUser1, kUser1Keypair)) .skipProposal() .skipBlock() - .sendTx(completeTx( - baseTx().transferAsset(kUser1Id, kUser1Id, kAsset, kDesc, kAmount))); - ASSERT_ANY_THROW(itf.skipProposal()); + .sendTx(completeTx(baseTx().transferAsset( + kUser1Id, kUser1Id, kAsset, kDesc, kAmount)), + checkStatelessInvalid); } /** @@ -396,17 +393,17 @@ TEST_F(TransferAsset, InterDomain) { const auto kNewRole = "newrl"; const auto kNewDomain = "newdom"; const auto kUser2Id = kUser2 + "@" + kNewDomain; - IntegrationTestFramework() + IntegrationTestFramework(4) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) .sendTx( shared_model::proto::TransactionBuilder() - .txCounter(1) .creatorAccountId( integration_framework::IntegrationTestFramework::kAdminId) .createdTime(iroha::time::now()) .createRole(kNewRole, - std::vector{iroha::model::can_receive}) + std::vector{ + shared_model::permissions::can_receive}) .createDomain(kNewDomain, kNewRole) .createAccount( kUser2, @@ -417,13 +414,9 @@ TEST_F(TransferAsset, InterDomain) { .build() .signAndAddSignature(kAdminKeypair)) .sendTx(addAssets(kUser1, kUser1Keypair, kAmount)) - .skipProposal() - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 3); }) .sendTx(completeTx( baseTx().transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, kAmount))) - .skipProposal() .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) + [](auto &block) { ASSERT_EQ(block->transactions().size(), 4); }) .done(); } diff --git a/test/integration/acceptance/tx_acceptance_test.cpp b/test/integration/acceptance/tx_acceptance_test.cpp index fb497f6248..2a01e3452d 100644 --- a/test/integration/acceptance/tx_acceptance_test.cpp +++ b/test/integration/acceptance/tx_acceptance_test.cpp @@ -1,53 +1,36 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ -#include "backend/protobuf/transaction.hpp" -#include "builders/protobuf/queries.hpp" -#include "builders/protobuf/transaction.hpp" -#include "cryptography/crypto_provider/crypto_defaults.hpp" -#include "datetime/time.hpp" #include "framework/integration_framework/integration_test_framework.hpp" -#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" -#include "responses.pb.h" +#include "integration/acceptance/acceptance_fixture.hpp" +#include "interfaces/utils/specified_visitor.hpp" -constexpr auto kAdmin = "admin@test"; -constexpr auto kNonUser = "nonuser@test"; -constexpr auto kAsset = "coin#test"; -const shared_model::crypto::Keypair kAdminKeypair = - shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair(); -auto checkStatelessValid = [](auto &status) { - ASSERT_NO_THROW( - boost::get>(status.get())); -}; -auto checkStatelessInvalid = [](auto &status) { - ASSERT_NO_THROW( - boost::get>(status.get())); -}; -auto checkProposal = [](auto &proposal) { - ASSERT_EQ(proposal->transactions().size(), 1); +class AcceptanceTest : public AcceptanceFixture { + public: + const std::string kAdmin = "admin@test"; + const std::string kNonUser = "nonuser@test"; -}; -auto checkStatefulInvalid = [](auto &block) { - ASSERT_EQ(block->transactions().size(), 0); -}; -auto checkStatefulValid = [](auto &block) { - ASSERT_EQ(block->transactions().size(), 1); + const std::function + checkStatelessValid = [](auto &status) { + ASSERT_TRUE(boost::apply_visitor( + shared_model::interface::SpecifiedVisitor< + shared_model::interface::StatelessValidTxResponse>(), + status.get())); + }; + const std::function &)> + checkProposal = + [](auto &proposal) { ASSERT_EQ(proposal->transactions().size(), 1); }; + const std::function &)> + checkStatefulInvalid = + [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }; + const std::function &)> + checkStatefulValid = + [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }; }; /** @@ -56,9 +39,8 @@ auto checkStatefulValid = [](auto &block) { * @then receive STATELESS_VALIDATION_SUCCESS status * AND STATEFUL_VALIDATION_FAILED on that tx */ -TEST(AcceptanceTest, NonExistentCreatorAccountId) { - auto tx = shared_model::proto::TransactionBuilder() - .txCounter(2) +TEST_F(AcceptanceTest, NonExistentCreatorAccountId) { + auto tx = TestUnsignedTransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId(kNonUser) .addAssetQuantity(kAdmin, kAsset, "1.0") @@ -73,74 +55,14 @@ TEST(AcceptanceTest, NonExistentCreatorAccountId) { .done(); } -/** - * @given some user - * @when sending 2 transactions with the same txCounter to the ledger - * @then receive STATELESS_VALIDATION_SUCCESS status on both txs - * AND STATELESS_VALIDATION_SUCCESS on first - * AND STATEFUL_VALIDATION_FAILED on second - */ - -// TODO: Solonets / IR-1166 - Double tx counter / Satisfy disabled test -TEST(AcceptanceTest, DISABLED_DublicatedTxCounter) { - auto tx1 = shared_model::proto::TransactionBuilder() - .txCounter(2) - .createdTime(iroha::time::now()) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .build() - .signAndAddSignature(kAdminKeypair); - auto tx2 = shared_model::proto::TransactionBuilder() - .txCounter(2) - .createdTime(iroha::time::now()) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .build() - .signAndAddSignature(kAdminKeypair); - - integration_framework::IntegrationTestFramework(1) - .setInitialState(kAdminKeypair) - .sendTx(tx1, checkStatelessValid) - .skipProposal() - .checkBlock(checkStatefulValid) - .sendTx(tx2, checkStatelessValid) - .skipProposal() - .checkBlock(checkStatefulInvalid) - .done(); -} - -/** - * @given some user - * @when sending transactions with the Maximum txCounter possible to the ledger - * @then receive STATELESS_VALIDATION_SUCCESS status - * AND STATEFUL_VALIDATION_SUCCESS on that tx - */ -TEST(AcceptanceTest, MaxTxCounter) { - auto tx1 = shared_model::proto::TransactionBuilder() - .txCounter(2) - .createdTime(iroha::time::now()) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .build() - .signAndAddSignature(kAdminKeypair); - - integration_framework::IntegrationTestFramework(1) - .setInitialState(kAdminKeypair) - .sendTx(tx1, checkStatelessValid) - .skipProposal() - .checkBlock(checkStatefulValid) - .done(); -} - /** * @given some user * @when sending transactions with an 1 hour old UNIX time * @then receive STATELESS_VALIDATION_SUCCESS status * AND STATEFUL_VALIDATION_SUCCESS on that tx */ -TEST(AcceptanceTest, Transaction1HourOld) { - auto tx = shared_model::proto::TransactionBuilder() - .txCounter(2) +TEST_F(AcceptanceTest, Transaction1HourOld) { + auto tx = TestUnsignedTransactionBuilder() .createdTime(iroha::time::now(std::chrono::hours(-1))) .creatorAccountId(kAdmin) .addAssetQuantity(kAdmin, kAsset, "1.0") @@ -160,9 +82,8 @@ TEST(AcceptanceTest, Transaction1HourOld) { * @then receive STATELESS_VALIDATION_SUCCESS status * AND STATEFUL_VALIDATION_SUCCESS on that tx */ -TEST(AcceptanceTest, DISABLED_TransactionLess24HourOld) { - auto tx = shared_model::proto::TransactionBuilder() - .txCounter(2) +TEST_F(AcceptanceTest, DISABLED_TransactionLess24HourOld) { + auto tx = TestUnsignedTransactionBuilder() .createdTime(iroha::time::now(std::chrono::hours(24) - std::chrono::minutes(1))) .creatorAccountId(kAdmin) @@ -182,18 +103,8 @@ TEST(AcceptanceTest, DISABLED_TransactionLess24HourOld) { * @when sending transactions with an more than 24 hour old UNIX time * @then receive STATELESS_VALIDATION_FAILED status */ -TEST(AcceptanceTest, TransactionMore24HourOld) { - ASSERT_ANY_THROW( - auto tx = shared_model::proto::TransactionBuilder() - .txCounter(2) - .createdTime(iroha::time::now(std::chrono::hours(24) - + std::chrono::minutes(1))) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .build() - .signAndAddSignature(kAdminKeypair);); +TEST_F(AcceptanceTest, TransactionMore24HourOld) { auto tx = TestUnsignedTransactionBuilder() - .txCounter(2) .createdTime(iroha::time::now(std::chrono::hours(24) + std::chrono::minutes(1))) .creatorAccountId(kAdmin) @@ -212,9 +123,8 @@ TEST(AcceptanceTest, TransactionMore24HourOld) { * @then receive STATELESS_VALIDATION_SUCCESS status * AND STATEFUL_VALIDATION_SUCCESS on that tx */ -TEST(AcceptanceTest, Transaction5MinutesFromFuture) { - auto tx = shared_model::proto::TransactionBuilder() - .txCounter(2) +TEST_F(AcceptanceTest, Transaction5MinutesFromFuture) { + auto tx = TestUnsignedTransactionBuilder() .createdTime(iroha::time::now(std::chrono::minutes(5) - std::chrono::seconds(10))) .creatorAccountId(kAdmin) @@ -235,17 +145,8 @@ TEST(AcceptanceTest, Transaction5MinutesFromFuture) { * @when sending transactions with an 10 minutes from future UNIX time * @then receive STATELESS_VALIDATION_FAILED status */ -TEST(AcceptanceTest, Transaction10MinutesFromFuture) { - ASSERT_ANY_THROW( - auto tx = shared_model::proto::TransactionBuilder() - .txCounter(2) - .createdTime(iroha::time::now(std::chrono::minutes(10))) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .build() - .signAndAddSignature(kAdminKeypair);); +TEST_F(AcceptanceTest, Transaction10MinutesFromFuture) { auto tx = TestUnsignedTransactionBuilder() - .txCounter(2) .createdTime(iroha::time::now(std::chrono::minutes(10))) .creatorAccountId(kAdmin) .addAssetQuantity(kAdmin, kAsset, "1.0") @@ -262,10 +163,9 @@ TEST(AcceptanceTest, Transaction10MinutesFromFuture) { * @when sending transactions with an empty public Key * @then receive STATELESS_VALIDATION_FAILED status */ -TEST(AcceptanceTest, TransactionEmptyPubKey) { +TEST_F(AcceptanceTest, TransactionEmptyPubKey) { shared_model::proto::Transaction tx = TestTransactionBuilder() - .txCounter(2) .createdTime(iroha::time::now()) .creatorAccountId(kAdmin) .addAssetQuantity(kAdmin, kAsset, "1.0") @@ -285,10 +185,9 @@ TEST(AcceptanceTest, TransactionEmptyPubKey) { * @when sending transactions with an empty signedBlob * @then receive STATELESS_VALIDATION_FAILED status */ -TEST(AcceptanceTest, TransactionEmptySignedblob) { +TEST_F(AcceptanceTest, TransactionEmptySignedblob) { shared_model::proto::Transaction tx = TestTransactionBuilder() - .txCounter(2) .createdTime(iroha::time::now()) .creatorAccountId(kAdmin) .addAssetQuantity(kAdmin, kAsset, "1.0") @@ -305,10 +204,9 @@ TEST(AcceptanceTest, TransactionEmptySignedblob) { * @when sending transactions with Invalid PublicKey * @then receive STATELESS_VALIDATION_FAILED status */ -TEST(AcceptanceTest, TransactionInvalidPublicKey) { +TEST_F(AcceptanceTest, TransactionInvalidPublicKey) { shared_model::proto::Transaction tx = TestTransactionBuilder() - .txCounter(2) .createdTime(iroha::time::now()) .creatorAccountId(kAdmin) .addAssetQuantity(kAdmin, kAsset, "1.0") @@ -328,10 +226,9 @@ TEST(AcceptanceTest, TransactionInvalidPublicKey) { * @when sending transactions with Invalid SignedBlock * @then receive STATELESS_VALIDATION_FAILED status */ -TEST(AcceptanceTest, TransactionInvalidSignedBlob) { +TEST_F(AcceptanceTest, TransactionInvalidSignedBlob) { shared_model::proto::Transaction tx = TestTransactionBuilder() - .txCounter(2) .createdTime(iroha::time::now()) .creatorAccountId(kAdmin) .addAssetQuantity(kAdmin, kAsset, "1.0") @@ -357,10 +254,9 @@ TEST(AcceptanceTest, TransactionInvalidSignedBlob) { * @then receive STATELESS_VALIDATION_SUCCESS status * AND STATEFUL_VALIDATION_SUCCESS on that tx */ -TEST(AcceptanceTest, TransactionValidSignedBlob) { +TEST_F(AcceptanceTest, TransactionValidSignedBlob) { shared_model::proto::Transaction tx = - shared_model::proto::TransactionBuilder() - .txCounter(2) + TestUnsignedTransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId(kAdmin) .addAssetQuantity(kAdmin, kAsset, "1.0") diff --git a/test/integration/acceptance/tx_heavy_data.cpp b/test/integration/acceptance/tx_heavy_data.cpp new file mode 100644 index 0000000000..eb22794a15 --- /dev/null +++ b/test/integration/acceptance/tx_heavy_data.cpp @@ -0,0 +1,154 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "framework/integration_framework/integration_test_framework.hpp" +#include "integration/acceptance/acceptance_fixture.hpp" +#include "interfaces/utils/specified_visitor.hpp" +#include "validators/permissions.hpp" + +using namespace integration_framework; +using namespace shared_model; + +class HeavyTransactionTest : public AcceptanceFixture { + public: + /** + * Creates the transaction with the user creation commands + * @param perms are the permissions of the user + * @return built tx and a hash of its payload + */ + auto makeUserWithPerms( + const std::vector &perms = { + shared_model::permissions::role_perm_group.begin(), + shared_model::permissions::role_perm_group.end()}) { + return AcceptanceFixture::makeUserWithPerms(perms); + } + + /** + * Generate stub of transaction for setting data to default user account + * @param key - key for the insertion + * @param value - data that will be attached + * @return generated builder, without signature + */ + auto setAcountDetailTx(const std::string &key, const std::string &value) { + return baseTx().setAccountDetail(kUserId, key, value); + } + + /** + * Util method for stub data generation + * @param quantity - number of bytes + * @return new string with passed quantity length + */ + static auto generateData(size_t quantity) { + return std::string(quantity, 'F'); + } + + /** + * Create valid basis of pre-built query + * @return query stub with counter, creator and time + */ + auto baseQuery() { + return baseQry().queryCounter(1); + } +}; + +/** + * TODO: refactor the test when all stability issues are fixed + * IR-1264 20/04/2018 neewy + * @given some user with all required permissions + * @when send many txes with addAccountDetail with large data inside + * @then transaction have been passed + */ +TEST_F(HeavyTransactionTest, DISABLED_ManyLargeTxes) { + auto number_of_txes = 4u; + IntegrationTestFramework itf(number_of_txes + 1); + + itf.setInitialState(kAdminKeypair).sendTx(makeUserWithPerms()); + + for (auto i = 0u; i < number_of_txes; ++i) { + itf.sendTx(complete(setAcountDetailTx("foo_" + std::to_string(i), + generateData(2 * 1024 * 1024)))); + } + itf.skipProposal() + .checkBlock([&](auto &b) { + ASSERT_EQ(b->transactions().size(), number_of_txes + 1); + }) + .done(); +} + +/** + * TODO: enable the test when performance issues are solved + * IR-1264 14/05/2018 andrei + * @given some user with all required permissions + * @when send tx with many addAccountDetails with large data inside + * @then transaction is passed + */ +TEST_F(HeavyTransactionTest, DISABLED_VeryLargeTxWithManyCommands) { + auto big_data = generateData(3 * 1024 * 1024); + auto large_tx_builder = setAcountDetailTx("foo_1", big_data) + .setAccountDetail(kUserId, "foo_2", big_data) + .setAccountDetail(kUserId, "foo_3", big_data); + + IntegrationTestFramework(2) + .setInitialState(kAdminKeypair) + .sendTx(makeUserWithPerms()) + .sendTx(complete(large_tx_builder)) + .skipProposal() + .checkBlock( + [](auto &block) { ASSERT_EQ(block->transactions().size(), 2); }) + .done(); +} + +/** + * TODO: disabled until proposal process time in simulator is not optimized + * and the test freezes (on a proposal verification stage) + * IR-1264 20/04/2018 neewy + * @given some user with all required permissions + * AND max proposal size is 1. + * @when send txes with addAccountDetail with large data inside. + * AND transactions are passed stateful validation + * @then query executed successfully + */ +TEST_F(HeavyTransactionTest, DISABLED_QueryLargeData) { + auto number_of_times = 15u; + auto size_of_data = 3 * 1024 * 1024u; + auto data = generateData(size_of_data); + + auto name_generator = [](auto val) { return "foo_" + std::to_string(val); }; + + auto query_checker = [&](auto &status) { + auto response = *boost::apply_visitor( + interface::SpecifiedVisitor(), + status.get()); + + boost::property_tree::ptree root; + boost::property_tree::read_json(response->account().jsonData(), root); + auto user = root.get_child(kUserId); + + ASSERT_EQ(number_of_times, user.size()); + + for (auto i = 0u; i < number_of_times; ++i) { + ASSERT_EQ(data, user.get(name_generator(i))); + } + }; + + IntegrationTestFramework itf(1); + itf.setInitialState(kAdminKeypair).sendTx(makeUserWithPerms()); + + for (auto i = 0u; i < number_of_times; ++i) { + itf.sendTx(complete(setAcountDetailTx(name_generator(i), data))) + .skipProposal() + .checkBlock( + [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }); + } + + // The query works fine only with ITF. It doesn't work in production version + // of Iroha + itf.sendQuery(complete(baseQuery().getAccount(kUserId)), query_checker) + .done(); +} diff --git a/test/integration/consensus/CMakeLists.txt b/test/integration/consensus/CMakeLists.txt index ad8508c60d..cf5cc95b80 100644 --- a/test/integration/consensus/CMakeLists.txt +++ b/test/integration/consensus/CMakeLists.txt @@ -1,6 +1,22 @@ +# +# Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. +# http://soramitsu.co.jp +# +# 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 +# +# http://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. +# + addtest(consensus_sunny_day consensus_sunny_day.cpp) target_link_libraries(consensus_sunny_day yac - model shared_model_stateless_validation ) diff --git a/test/integration/consensus/consensus_sunny_day.cpp b/test/integration/consensus/consensus_sunny_day.cpp index e3a7a85ec0..1e40bdf539 100644 --- a/test/integration/consensus/consensus_sunny_day.cpp +++ b/test/integration/consensus/consensus_sunny_day.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,13 +16,17 @@ */ #include + #include "consensus/yac/impl/timer_impl.hpp" #include "consensus/yac/storage/yac_proposal_storage.hpp" #include "consensus/yac/transport/impl/network_impl.hpp" +#include "cryptography/crypto_provider/crypto_defaults.hpp" #include "framework/test_subscriber.hpp" #include "module/irohad/consensus/yac/yac_mocks.hpp" +#include "module/shared_model/builders/protobuf/test_signature_builder.hpp" using ::testing::An; +using ::testing::InvokeWithoutArgs; using ::testing::Return; using namespace iroha::consensus::yac; @@ -36,22 +40,28 @@ auto mk_local_peer(uint64_t num) { class FixedCryptoProvider : public MockYacCryptoProvider { public: explicit FixedCryptoProvider(const std::string &public_key) { - pubkey.fill(0); - std::copy(public_key.begin(), public_key.end(), pubkey.begin()); + // TODO 15.04.2018 x3medima17 IR-1189: move to separate class + auto size = + shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair() + .publicKey() + .size(); + // TODO 16.04.2018 x3medima17 IR-977: add sizes + std::string key(size, 0); + std::copy(public_key.begin(), public_key.end(), key.begin()); + pubkey = clone(shared_model::crypto::PublicKey(key)); } VoteMessage getVote(YacHash hash) override { auto vote = MockYacCryptoProvider::getVote(hash); - vote.signature.pubkey = pubkey; + vote.signature = clone(TestSignatureBuilder().publicKey(*pubkey).build()); return vote; } - decltype(VoteMessage().signature.pubkey) pubkey; + std::unique_ptr pubkey; }; class ConsensusSunnyDayTest : public ::testing::Test { public: - std::thread thread; std::unique_ptr server; std::shared_ptr network; std::shared_ptr crypto; @@ -64,40 +74,32 @@ class ConsensusSunnyDayTest : public ::testing::Test { void SetUp() override { network = std::make_shared(); crypto = std::make_shared(std::to_string(my_num)); - timer = std::make_shared(); + timer = std::make_shared([this] { + // static factory with a single thread + // see YacInit::createTimer in consensus_init.cpp + static rxcpp::observe_on_one_worker coordination( + rxcpp::observe_on_new_thread().create_coordinator().get_scheduler()); + return rxcpp::observable<>::timer(std::chrono::milliseconds(delay), + coordination); + }); auto order = ClusterOrdering::create(default_peers); ASSERT_TRUE(order); - yac = Yac::create( - YacVoteStorage(), network, crypto, timer, order.value(), delay); + yac = Yac::create(YacVoteStorage(), network, crypto, timer, order.value()); network->subscribe(yac); - std::mutex mtx; - std::condition_variable cv; - - thread = std::thread([&cv, this] { - grpc::ServerBuilder builder; - int port = 0; - builder.AddListeningPort( - my_peer->address(), grpc::InsecureServerCredentials(), &port); - builder.RegisterService(network.get()); - server = builder.BuildAndStart(); - ASSERT_TRUE(server); - ASSERT_NE(port, 0); - cv.notify_one(); - server->Wait(); - }); - - // wait until server woke up - std::unique_lock lock(mtx); - cv.wait(lock); + grpc::ServerBuilder builder; + int port = 0; + builder.AddListeningPort( + my_peer->address(), grpc::InsecureServerCredentials(), &port); + builder.RegisterService(network.get()); + server = builder.BuildAndStart(); + ASSERT_TRUE(server); + ASSERT_NE(port, 0); } void TearDown() override { server->Shutdown(); - if (thread.joinable()) { - thread.join(); - } } static uint64_t my_num, delay_before, delay_after; @@ -113,7 +115,7 @@ class ConsensusSunnyDayTest : public ::testing::Test { } if (num_peers == 1) { delay_before = 0; - delay_after = 50; + delay_after = 5 * 1000; } else { delay_before = 10 * 1000; delay_after = 3 * default_peers.size() + 10 * 1000; @@ -129,25 +131,33 @@ std::vector> ConsensusSunnyDayTest::default_peers; TEST_F(ConsensusSunnyDayTest, SunnyDayTest) { + std::condition_variable cv; auto wrapper = make_test_subscriber(yac->on_commit(), 1); wrapper.subscribe( [](auto hash) { std::cout << "^_^ COMMITTED!!!" << std::endl; }); EXPECT_CALL(*crypto, verify(An())) .Times(1) - .WillRepeatedly(Return(true)); + .WillRepeatedly(DoAll(InvokeWithoutArgs([&cv] { + // wake up after commit is received from the + // network so that it is safe to shutdown + cv.notify_one(); + }), + Return(true))); EXPECT_CALL(*crypto, verify(An())).WillRepeatedly(Return(true)); // Wait for other peers to start std::this_thread::sleep_for(std::chrono::milliseconds(delay_before)); YacHash my_hash("proposal_hash", "block_hash"); - + my_hash.block_signature = createSig(""); auto order = ClusterOrdering::create(default_peers); ASSERT_TRUE(order); yac->vote(my_hash, *order); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_after)); + std::mutex m; + std::unique_lock lk(m); + cv.wait_for(lk, std::chrono::milliseconds(delay_after)); ASSERT_TRUE(wrapper.validate()); } diff --git a/test/integration/pipeline/CMakeLists.txt b/test/integration/pipeline/CMakeLists.txt index 19d646aa8a..6cad70072b 100644 --- a/test/integration/pipeline/CMakeLists.txt +++ b/test/integration/pipeline/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. +# Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. # http://soramitsu.co.jp # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test/integration/pipeline/pipeline_test.cpp b/test/integration/pipeline/pipeline_test.cpp index 5834c008ac..2a9f49c574 100644 --- a/test/integration/pipeline/pipeline_test.cpp +++ b/test/integration/pipeline/pipeline_test.cpp @@ -54,7 +54,7 @@ TEST(PipelineIntegrationTest, SendQuery) { shared_model::interface::StatefulFailedErrorResponse>(), status.get())); }; - integration_framework::IntegrationTestFramework() + integration_framework::IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendQuery(query, check) .done(); @@ -70,7 +70,6 @@ TEST(PipelineIntegrationTest, SendTx) { auto tx = shared_model::proto::TransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId(kUser) - .txCounter(1) .addAssetQuantity(kUser, kAsset, "1.0") .build() .signAndAddSignature( @@ -89,7 +88,7 @@ TEST(PipelineIntegrationTest, SendTx) { auto checkBlock = [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }; - integration_framework::IntegrationTestFramework() + integration_framework::IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(tx, checkStatelessValid) .checkProposal(checkProposal) diff --git a/deploy/ansible/playbooks/iroha-docker-cluster/main.yml b/test/integration/pipeline/test_irohad.hpp similarity index 100% rename from deploy/ansible/playbooks/iroha-docker-cluster/main.yml rename to test/integration/pipeline/test_irohad.hpp diff --git a/test/integration/pipeline/tx_pipeline_integration_test.cpp b/test/integration/pipeline/tx_pipeline_integration_test.cpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/integration/transport/CMakeLists.txt b/test/integration/transport/CMakeLists.txt new file mode 100644 index 0000000000..ba36f9b9e6 --- /dev/null +++ b/test/integration/transport/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +addtest(ordering_gate_service_test ordering_gate_service_test.cpp) +target_link_libraries(ordering_gate_service_test + ordering_service + shared_model_stateless_validation + shared_model_cryptography_model + iroha_amount + ) diff --git a/test/module/irohad/ordering/ordering_gate_service_test.cpp b/test/integration/transport/ordering_gate_service_test.cpp similarity index 76% rename from test/module/irohad/ordering/ordering_gate_service_test.cpp rename to test/integration/transport/ordering_gate_service_test.cpp index 5f4c1c887a..108cd57dd1 100644 --- a/test/module/irohad/ordering/ordering_gate_service_test.cpp +++ b/test/integration/transport/ordering_gate_service_test.cpp @@ -20,9 +20,9 @@ #include "builders/protobuf/common_objects/proto_peer_builder.hpp" #include "builders/protobuf/transaction.hpp" #include "framework/test_subscriber.hpp" -#include "mock_ordering_service_persistent_state.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" +#include "module/irohad/ordering/mock_ordering_service_persistent_state.hpp" #include "module/shared_model/builders/protobuf/test_block_builder.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" #include "ordering/impl/ordering_gate_impl.hpp" @@ -54,12 +54,11 @@ class OrderingGateServiceTest : public ::testing::Test { EXPECT_CALL(*pcs_, on_commit()) .WillRepeatedly(Return(commit_subject_.get_observable())); gate_transport = std::make_shared(address); - gate = std::make_shared(gate_transport); + gate = std::make_shared(gate_transport, 1, false); gate->setPcs(*pcs_); gate_transport->subscribe(gate); service_transport = std::make_shared(); - counter = 2; wsv = std::make_shared(); } @@ -69,6 +68,16 @@ class OrderingGateServiceTest : public ::testing::Test { std::make_shared(); } + void initOs(size_t max_proposal) { + service = + std::make_shared(wsv, + max_proposal, + proposal_timeout.get_observable(), + service_transport, + fake_persistent_state); + service_transport->subscribe(service); + } + void start() { std::mutex mtx; std::condition_variable cv; @@ -104,20 +113,17 @@ class OrderingGateServiceTest : public ::testing::Test { TestSubscriber> init( size_t times) { auto wrapper = make_test_subscriber(gate->on_proposal(), times); - gate->on_proposal().subscribe([this](auto) { - counter--; - cv.notify_one(); - }); gate->on_proposal().subscribe([this](auto proposal) { proposals.push_back(proposal); - // emulate commit event after receiving the proposal to perform next // round inside the peer. std::shared_ptr block = std::make_shared( - TestBlockBuilder().build()); + TestBlockBuilder().height(proposal->height()).build()); commit_subject_.get_subscriber().on_next( rxcpp::observable<>::just(block)); + counter--; + cv.notify_one(); }); wrapper.subscribe(); return wrapper; @@ -130,20 +136,27 @@ class OrderingGateServiceTest : public ::testing::Test { void send_transaction(size_t i) { auto tx = std::make_shared( shared_model::proto::TransactionBuilder() - .txCounter(i) .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") .addAssetQuantity("admin@tu", "coin#coin", "1.0") .build() .signAndAddSignature( shared_model::crypto::DefaultCryptoAlgorithmType:: - generateKeypair()) - ); + generateKeypair())); gate->propagateTransaction(tx); // otherwise tx may come unordered std::this_thread::sleep_for(20ms); } + void makeProposalTimeout() { + proposal_timeout.get_subscriber().on_next(0); + } + + void waitForGate() { + std::unique_lock lk(m); + cv.wait_for(lk, 10s, [this] { return counter == 0; }); + } + std::string address{"0.0.0.0:50051"}; std::shared_ptr gate; std::shared_ptr service; @@ -152,6 +165,7 @@ class OrderingGateServiceTest : public ::testing::Test { /// commits for Ordering Service std::shared_ptr pcs_; rxcpp::subjects::subject commit_subject_; + rxcpp::subjects::subject proposal_timeout; std::vector> proposals; std::atomic counter; @@ -168,24 +182,20 @@ class OrderingGateServiceTest : public ::testing::Test { }; /** - * @given Ordering service + * @given Ordering Service * @when Send 8 transactions * AND 2 transactions to OS * @then Received proposal with 8 transactions * AND proposal with 2 transactions */ -TEST_F(OrderingGateServiceTest, SplittingBunchTransactions) { - // 8 transaction -> proposal -> 2 transaction -> proposal - EXPECT_CALL(*wsv, getLedgerPeers()) - .WillRepeatedly(Return(std::vector{peer})); - +TEST_F(OrderingGateServiceTest, DISABLED_SplittingBunchTransactions) { const size_t max_proposal = 100; - const size_t commit_delay = 400; + EXPECT_CALL(*wsv, getLedgerPeers()) + .WillRepeatedly(Return(std::vector{peer})); EXPECT_CALL(*fake_persistent_state, loadProposalHeight()) .Times(1) .WillOnce(Return(boost::optional(2))); - EXPECT_CALL(*fake_persistent_state, saveProposalHeight(3)) .Times(1) .WillOnce(Return(true)); @@ -193,59 +203,40 @@ TEST_F(OrderingGateServiceTest, SplittingBunchTransactions) { .Times(1) .WillOnce(Return(true)); - service = std::make_shared(wsv, - max_proposal, - commit_delay, - service_transport, - fake_persistent_state); - service_transport->subscribe(service); - + initOs(max_proposal); start(); - std::unique_lock lk(m); auto wrapper = init(2); + counter = 2; for (size_t i = 0; i < 8; ++i) { send_transaction(i + 1); } - cv.wait_for(lk, 10s); + makeProposalTimeout(); send_transaction(9); send_transaction(10); - cv.wait_for(lk, 10s); + makeProposalTimeout(); - std::this_thread::sleep_for(1s); + waitForGate(); ASSERT_EQ(proposals.size(), 2); ASSERT_EQ(proposals.at(0)->transactions().size(), 8); ASSERT_EQ(proposals.at(1)->transactions().size(), 2); - ASSERT_EQ(counter, 0); ASSERT_TRUE(wrapper.validate()); - - size_t i = 1; - for (auto &&proposal : proposals) { - for (auto &&tx : proposal->transactions()) { - ASSERT_EQ(tx->transactionCounter(), i++); - } - } } /** - * @given ordering service - * @when a bunch of transaction has arrived - * @then split transactions on to two proposal + * @given Ordering Service with max proposal 5 + * @when Two bunches of 5 tx has been sent + * @then Transactions are splitted in two proposals by 5 tx each */ -TEST_F(OrderingGateServiceTest, ProposalsReceivedWhenProposalSize) { - // commits on the fulfilling proposal queue - // 10 transaction -> proposal with 5 -> proposal with 5 - EXPECT_CALL(*wsv, getLedgerPeers()) - .WillRepeatedly(Return(std::vector{peer})); - +TEST_F(OrderingGateServiceTest, DISABLED_ProposalsReceivedWhenProposalSize) { const size_t max_proposal = 5; - const size_t commit_delay = 1000; + EXPECT_CALL(*wsv, getLedgerPeers()) + .WillRepeatedly(Return(std::vector{peer})); EXPECT_CALL(*fake_persistent_state, loadProposalHeight()) .Times(1) .WillOnce(Return(boost::optional(2))); - EXPECT_CALL(*fake_persistent_state, saveProposalHeight(3)) .Times(1) .WillOnce(Return(true)); @@ -253,33 +244,18 @@ TEST_F(OrderingGateServiceTest, ProposalsReceivedWhenProposalSize) { .Times(1) .WillOnce(Return(true)); - service = std::make_shared(wsv, - max_proposal, - commit_delay, - service_transport, - fake_persistent_state); - service_transport->subscribe(service); - + initOs(max_proposal); start(); - std::unique_lock lk(m); auto wrapper = init(2); + counter = 2; for (size_t i = 0; i < 10; ++i) { send_transaction(i + 1); } - // long == something wrong - cv.wait_for(lk, 10s, [this]() { return counter == 0; }); - - ASSERT_TRUE(wrapper.validate()); + waitForGate(); ASSERT_EQ(proposals.size(), 2); - ASSERT_EQ(counter, 0); - - size_t i = 1; - for (auto &&proposal : proposals) { - ASSERT_EQ(proposal->transactions().size(), 5); - for (auto &&tx : proposal->transactions()) { - ASSERT_EQ(tx->transactionCounter(), i++); - } - } + ASSERT_EQ(proposals.at(0)->transactions().size(), 5); + ASSERT_EQ(proposals.at(1)->transactions().size(), 5); + ASSERT_TRUE(wrapper.validate()); } diff --git a/test/module/CMakeLists.txt b/test/module/CMakeLists.txt index 02b87b1f6a..34b0656c93 100644 --- a/test/module/CMakeLists.txt +++ b/test/module/CMakeLists.txt @@ -1,6 +1,22 @@ -set(CMAKE_BUILD_TYPE Debug) +# +# Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. +# http://soramitsu.co.jp +# +# 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 +# +# http://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. +# # Reusable tests +add_subdirectory(iroha-cli) add_subdirectory(irohad) add_subdirectory(libs) add_subdirectory(vendor) diff --git a/test/module/iroha-cli/CMakeLists.txt b/test/module/iroha-cli/CMakeLists.txt new file mode 100644 index 0000000000..fd78e36041 --- /dev/null +++ b/test/module/iroha-cli/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. +# http://soramitsu.co.jp +# +# 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 +# +# http://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. +# + +addtest(client_test client_test.cpp) +target_link_libraries(client_test + client + processors + server_runner + query_execution + ) +target_include_directories(client_test PUBLIC + ${PROJECT_SOURCE_DIR}/iroha-cli + ) diff --git a/test/integration/client_test.cpp b/test/module/iroha-cli/client_test.cpp similarity index 86% rename from test/integration/client_test.cpp rename to test/module/iroha-cli/client_test.cpp index 5871442384..6e98634680 100644 --- a/test/integration/client_test.cpp +++ b/test/module/iroha-cli/client_test.cpp @@ -15,11 +15,6 @@ * limitations under the License. */ -#include - -#include - -#include "builders/protobuf/common_objects/proto_account_builder.hpp" #include "model/sha3_hash.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" @@ -47,17 +42,16 @@ constexpr const char *Ip = "0.0.0.0"; constexpr int Port = 50051; +using ::testing::_; using ::testing::A; using ::testing::AtLeast; using ::testing::Return; -using ::testing::_; using namespace iroha::ametsuchi; using namespace iroha::network; using namespace iroha::validation; -using namespace iroha::model::converters; -using namespace iroha::model; using namespace shared_model::proto; +using namespace shared_model::permissions; using namespace std::chrono_literals; constexpr std::chrono::milliseconds proposal_delay = 10s; @@ -128,7 +122,6 @@ TEST_F(ClientServerTest, SendTxWhenValid) { auto shm_tx = shared_model::proto::TransactionBuilder() .creatorAccountId("some@account") - .txCounter(1) .createdTime(iroha::time::now()) .setAccountQuorum("some@account", 2) .build() @@ -136,8 +129,7 @@ TEST_F(ClientServerTest, SendTxWhenValid) { shared_model::crypto::DefaultCryptoAlgorithmType:: generateKeypair()); - std::unique_ptr old_model(shm_tx.makeOldModel()); - auto status = client.sendTx(*old_model); + auto status = client.sendTx(shm_tx); ASSERT_EQ(status.answer, iroha_cli::CliClient::OK); } @@ -155,8 +147,8 @@ TEST_F(ClientServerTest, SendTxWhenInvalidJson) { } }] })"; - JsonTransactionFactory tx_factory; - auto json_doc = stringToJson(json_string); + iroha::model::converters::JsonTransactionFactory tx_factory; + auto json_doc = iroha::model::converters::stringToJson(json_string); ASSERT_TRUE(json_doc); auto model_tx = tx_factory.deserialize(json_doc.value()); ASSERT_FALSE(model_tx); @@ -166,19 +158,18 @@ TEST_F(ClientServerTest, SendTxWhenStatelessInvalid) { // creating stateless invalid tx auto shm_tx = TestTransactionBuilder() .creatorAccountId("some@account") - .txCounter(1) .createdTime(iroha::time::now()) .setAccountQuorum("some@@account", 2) .build(); - std::unique_ptr old_tx(shm_tx.makeOldModel()); - ASSERT_EQ(iroha_cli::CliClient(Ip, Port).sendTx(*old_tx).answer, + ASSERT_EQ(iroha_cli::CliClient(Ip, Port).sendTx(shm_tx).answer, iroha_cli::CliClient::OK); auto tx_hash = shm_tx.hash(); - ASSERT_EQ(iroha_cli::CliClient(Ip, Port) - .getTxStatus(shared_model::crypto::toBinaryString(tx_hash)) - .answer.tx_status(), + auto res = iroha_cli::CliClient(Ip, Port).getTxStatus( + shared_model::crypto::toBinaryString(tx_hash)); + ASSERT_EQ(res.answer.tx_status(), iroha::protocol::TxStatus::STATELESS_VALIDATION_FAILED); + ASSERT_NE(res.answer.error_message().size(), 0); } TEST_F(ClientServerTest, SendQueryWhenInvalidJson) { @@ -197,7 +188,7 @@ TEST_F(ClientServerTest, SendQueryWhenInvalidJson) { }] })"; - JsonQueryFactory queryFactory; + iroha::model::converters::JsonQueryFactory queryFactory; auto model_query = queryFactory.deserialize(json_query); ASSERT_FALSE(model_query); } @@ -212,19 +203,17 @@ TEST_F(ClientServerTest, SendQueryWhenStatelessInvalid) { .build(); auto proto_query = query.getTransport(); - auto res = client.sendQuery( - std::shared_ptr(query.makeOldModel())); + auto res = client.sendQuery(query); ASSERT_TRUE(res.status.ok()); ASSERT_TRUE(res.answer.has_error_response()); ASSERT_EQ(res.answer.error_response().reason(), - iroha::model::ErrorResponse::STATELESS_INVALID); + iroha::protocol::ErrorResponse::STATELESS_INVALID); + ASSERT_NE(res.answer.error_response().message().size(), 0); } TEST_F(ClientServerTest, SendQueryWhenValid) { // TODO: 30/04/2018 x3medima17, fix Uninteresting mock function call, IR-1187 iroha_cli::CliClient client(Ip, Port); - auto account_admin = iroha::model::Account(); - account_admin.account_id = "admin@test"; std::shared_ptr account_test = clone( shared_model::proto::AccountBuilder().accountId("test@test").build()); @@ -240,6 +229,9 @@ TEST_F(ClientServerTest, SendQueryWhenValid) { EXPECT_CALL(*wsv_query, getAccountDetail("test@test")) .WillOnce(Return(boost::make_optional(std::string("value")))); + EXPECT_CALL(*wsv_query, getAccountRoles("admin@test")) + .WillOnce(Return(boost::none)); + auto query = QueryBuilder() .createdTime(iroha::time::now()) .creatorAccountId("admin@test") @@ -248,18 +240,12 @@ TEST_F(ClientServerTest, SendQueryWhenValid) { .build() .signAndAddSignature(pair); - auto res = client.sendQuery( - std::shared_ptr(query.makeOldModel())); + auto res = client.sendQuery(query); ASSERT_EQ(res.answer.account_detail_response().detail(), "value"); } TEST_F(ClientServerTest, SendQueryWhenStatefulInvalid) { iroha_cli::CliClient client(Ip, Port); - auto account_admin = iroha::model::Account(); - account_admin.account_id = "admin@test"; - - auto account_test = iroha::model::Account(); - account_test.account_id = "test@test"; EXPECT_CALL(*wsv_query, getSignatories("admin@test")) .WillRepeatedly(Return(signatories)); @@ -269,6 +255,9 @@ TEST_F(ClientServerTest, SendQueryWhenStatefulInvalid) { "admin@test", "test@test", can_get_my_acc_detail)) .WillOnce(Return(false)); + EXPECT_CALL(*wsv_query, getAccountRoles("admin@test")) + .WillOnce(Return(boost::none)); + auto query = QueryBuilder() .createdTime(iroha::time::now()) .creatorAccountId("admin@test") @@ -277,8 +266,7 @@ TEST_F(ClientServerTest, SendQueryWhenStatefulInvalid) { .build() .signAndAddSignature(pair); - auto res = client.sendQuery( - std::shared_ptr(query.makeOldModel())); + auto res = client.sendQuery(query); ASSERT_EQ(res.answer.error_response().reason(), iroha::protocol::ErrorResponse::STATEFUL_INVALID); } diff --git a/test/module/irohad/CMakeLists.txt b/test/module/irohad/CMakeLists.txt index cda20ef600..c3654e516b 100644 --- a/test/module/irohad/CMakeLists.txt +++ b/test/module/irohad/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2017 Soramitsu Co., Ltd. +# Copyright 2018 Soramitsu Co., Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,20 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -set(CMAKE_BUILD_TYPE Debug) - -SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/test_bin) - # Reusable tests -add_subdirectory(common) add_subdirectory(ametsuchi) +add_subdirectory(common) add_subdirectory(consensus) +add_subdirectory(execution) add_subdirectory(logger) -add_subdirectory(validation) -add_subdirectory(torii) add_subdirectory(main) add_subdirectory(model) -add_subdirectory(synchronizer) -add_subdirectory(simulator) -add_subdirectory(ordering) add_subdirectory(network) +add_subdirectory(ordering) +add_subdirectory(simulator) +add_subdirectory(synchronizer) +add_subdirectory(torii) +add_subdirectory(validation) diff --git a/test/module/irohad/ametsuchi/CMakeLists.txt b/test/module/irohad/ametsuchi/CMakeLists.txt index 8d72781c58..485bb11d09 100644 --- a/test/module/irohad/ametsuchi/CMakeLists.txt +++ b/test/module/irohad/ametsuchi/CMakeLists.txt @@ -42,5 +42,4 @@ target_link_libraries(kv_storage_test add_library(ametsuchi_fixture INTERFACE) target_link_libraries(ametsuchi_fixture INTERFACE pqxx - model_generators ) diff --git a/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp b/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp index 89fc25c817..0874ce5bbd 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp @@ -140,7 +140,6 @@ CREATE TABLE IF NOT EXISTS account ( account_id character varying(288), domain_id character varying(255) NOT NULL REFERENCES domain, quorum int NOT NULL, - transaction_count int NOT NULL DEFAULT 0, data JSONB, PRIMARY KEY (account_id) ); diff --git a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp index 039b408061..effb87a23c 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp @@ -163,6 +163,7 @@ namespace iroha { rxcpp::observable( shared_model::interface::types::HeightType)); MOCK_METHOD1(getTopBlocks, rxcpp::observable(uint32_t)); + MOCK_METHOD1(hasTxWithHash, bool(const shared_model::crypto::Hash &hash)); }; class MockTemporaryFactory : public TemporaryFactory { @@ -231,6 +232,9 @@ namespace iroha { bool(const std::vector< std::shared_ptr> &)); MOCK_METHOD0(dropStorage, void(void)); + MOCK_METHOD0( + on_commit, + rxcpp::observable>()); void commit(std::unique_ptr storage) override { doCommit(storage.get()); diff --git a/test/module/irohad/ametsuchi/ametsuchi_test.cpp b/test/module/irohad/ametsuchi/ametsuchi_test.cpp index d859d45e5e..bc99c6f5b3 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_test.cpp +++ b/test/module/irohad/ametsuchi/ametsuchi_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,12 +22,14 @@ #include "ametsuchi/impl/postgres_wsv_query.hpp" #include "ametsuchi/impl/wsv_restorer_impl.hpp" #include "ametsuchi/mutable_storage.hpp" +#include "builders/default_builders.hpp" #include "builders/protobuf/transaction.hpp" +#include "framework/result_fixture.hpp" #include "framework/test_subscriber.hpp" -#include "validators/permissions.hpp" #include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" #include "module/shared_model/builders/protobuf/test_block_builder.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "validators/permissions.hpp" using namespace iroha::ametsuchi; using namespace framework::test_subscriber; @@ -35,6 +37,17 @@ using namespace framework::test_subscriber; auto zero_string = std::string(32, '0'); auto fake_hash = shared_model::crypto::Hash(zero_string); auto fake_pubkey = shared_model::crypto::PublicKey(zero_string); +using AmountBuilder = shared_model::builder::AmountBuilderWithoutValidator; + +/** + * Return shared pointer to amount from result, or throw exception + * @return amount from result + */ +std::shared_ptr getAmount( + const shared_model::builder::BuilderResult + &result) { + return framework::expected::val(result)->value; +} /** * Shortcut to create CallExact observable wrapper, subscribe with given lambda, @@ -110,14 +123,12 @@ template void validateAccountAsset(W &&wsv, const std::string &account, const std::string &asset, - const iroha::Amount &amount) { + const shared_model::interface::Amount &amount) { auto account_asset = wsv->getAccountAsset(account, asset); ASSERT_TRUE(account_asset); ASSERT_EQ((*account_asset)->accountId(), account); ASSERT_EQ((*account_asset)->assetId(), asset); - ASSERT_EQ(*std::unique_ptr( - (*account_asset)->balance().makeOldModel()), - amount); + ASSERT_EQ((*account_asset)->balance(), amount); } /** @@ -183,7 +194,6 @@ TEST_F(AmetsuchiTest, SampleTest) { assetid = "rub#ru"; std::string account, src_account, dest_account, asset; - iroha::Amount amount; // Block 1 // TODO: 26/04/2018 x3medima17 replace string permissions IR-999 @@ -195,9 +205,9 @@ TEST_F(AmetsuchiTest, SampleTest) { .createRole( "user", shared_model::interface::types::PermissionSetType{ - iroha::model::can_add_peer, - iroha::model::can_create_asset, - iroha::model::can_get_my_account}) + shared_model::permissions::can_add_peer, + shared_model::permissions::can_create_asset, + shared_model::permissions::can_get_my_account}) .createDomain(domain, "user") .createAccount(user1name, domain, fake_pubkey) .build()})) @@ -227,14 +237,14 @@ TEST_F(AmetsuchiTest, SampleTest) { apply(storage, block2); validateAccountAsset( - wsv, user1id, assetid, *iroha::Amount::createFromString("50.0")); + wsv, user1id, assetid, *getAmount(AmountBuilder::fromString("50.0"))); validateAccountAsset( - wsv, user2id, assetid, *iroha::Amount::createFromString("100.0")); + wsv, user2id, assetid, *getAmount(AmountBuilder::fromString("100.0"))); // Block store tests auto hashes = {block1.hash(), block2.hash()}; validateCalls(blocks->getBlocks(1, 2), - [i = 0, &hashes](auto eachBlock) mutable { + [ i = 0, &hashes ](auto eachBlock) mutable { EXPECT_EQ(*(hashes.begin() + i), eachBlock->hash()); ++i; }, @@ -286,16 +296,16 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { asset2id = "assettwo#domain"; std::string account, src_account, dest_account, asset; - iroha::Amount amount; // 1st tx auto txn1 = TestTransactionBuilder() .creatorAccountId(admin) .createRole("user", - std::set{iroha::model::can_add_peer, - iroha::model::can_create_asset, - iroha::model::can_get_my_account}) + std::set{ + shared_model::permissions::can_add_peer, + shared_model::permissions::can_create_asset, + shared_model::permissions::can_get_my_account}) .createDomain(domain, "user") .createAccount(user1name, domain, fake_pubkey) .createAccount(user2name, domain, fake_pubkey) @@ -322,9 +332,9 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { // Check querying assets for users validateAccountAsset( - wsv, user1id, asset1id, *iroha::Amount::createFromString("300.0")); + wsv, user1id, asset1id, *getAmount(AmountBuilder::fromString("300.0"))); validateAccountAsset( - wsv, user2id, asset2id, *iroha::Amount::createFromString("250.0")); + wsv, user2id, asset2id, *getAmount(AmountBuilder::fromString("250.0"))); // 2th tx (user1 -> user2 # asset1) auto txn2 = @@ -343,9 +353,9 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { // Check account asset after transfer assets validateAccountAsset( - wsv, user1id, asset1id, *iroha::Amount::createFromString("180.0")); + wsv, user1id, asset1id, *getAmount(AmountBuilder::fromString("180.0"))); validateAccountAsset( - wsv, user2id, asset1id, *iroha::Amount::createFromString("120.0")); + wsv, user2id, asset1id, *getAmount(AmountBuilder::fromString("120.0"))); // 3rd tx // (user2 -> user3 # asset2) @@ -367,16 +377,16 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { apply(storage, block3); validateAccountAsset( - wsv, user2id, asset2id, *iroha::Amount::createFromString("90.0")); + wsv, user2id, asset2id, *getAmount(AmountBuilder::fromString("90.0"))); validateAccountAsset( - wsv, user3id, asset2id, *iroha::Amount::createFromString("150.0")); + wsv, user3id, asset2id, *getAmount(AmountBuilder::fromString("150.0"))); validateAccountAsset( - wsv, user1id, asset2id, *iroha::Amount::createFromString("10.0")); + wsv, user1id, asset2id, *getAmount(AmountBuilder::fromString("10.0"))); // Block store test auto hashes = {block1.hash(), block2.hash(), block3.hash()}; validateCalls(blocks->getBlocks(1, 3), - [i = 0, &hashes](auto eachBlock) mutable { + [ i = 0, &hashes ](auto eachBlock) mutable { EXPECT_EQ(*(hashes.begin() + i), eachBlock->hash()); ++i; }, @@ -402,8 +412,8 @@ TEST_F(AmetsuchiTest, AddSignatoryTest) { ASSERT_TRUE(storage); auto wsv = storage->getWsvQuery(); - shared_model::crypto::PublicKey pubkey1(std::string("1", 32)); - shared_model::crypto::PublicKey pubkey2(std::string("2", 32)); + shared_model::crypto::PublicKey pubkey1(std::string(32, '1')); + shared_model::crypto::PublicKey pubkey2(std::string(32, '2')); auto user1id = "userone@domain"; auto user2id = "usertwo@domain"; @@ -413,9 +423,10 @@ TEST_F(AmetsuchiTest, AddSignatoryTest) { TestTransactionBuilder() .creatorAccountId("adminone") .createRole("user", - std::set{iroha::model::can_add_peer, - iroha::model::can_create_asset, - iroha::model::can_get_my_account}) + std::set{ + shared_model::permissions::can_add_peer, + shared_model::permissions::can_create_asset, + shared_model::permissions::can_get_my_account}) .createDomain("domain", "user") .createAccount("userone", "domain", pubkey1) .build(); @@ -610,6 +621,8 @@ TEST_F(AmetsuchiTest, TestingStorageWhenInsertBlock) { "=> insert block " "=> assert that inserted"); ASSERT_TRUE(storage); + auto wrapper = make_test_subscriber(storage->on_commit(), 1); + wrapper.subscribe(); auto wsv = storage->getWsvQuery(); ASSERT_EQ(0, wsv->getPeers().value().size()); @@ -625,6 +638,8 @@ TEST_F(AmetsuchiTest, TestingStorageWhenInsertBlock) { log->info("Drop ledger"); storage->dropStorage(); + + ASSERT_TRUE(wrapper.validate()); } TEST_F(AmetsuchiTest, TestingStorageWhenDropAll) { @@ -691,16 +706,17 @@ TEST_F(AmetsuchiTest, FindTxByHashTest) { ASSERT_TRUE(storage); auto blocks = storage->getBlockQuery(); - shared_model::crypto::PublicKey pubkey1(std::string("1", 32)); - shared_model::crypto::PublicKey pubkey2(std::string("2", 32)); + shared_model::crypto::PublicKey pubkey1(std::string(32, '1')); + shared_model::crypto::PublicKey pubkey2(std::string(32, '2')); auto txn1 = TestTransactionBuilder() .creatorAccountId("admin1") .createRole("user", - std::set{iroha::model::can_add_peer, - iroha::model::can_create_asset, - iroha::model::can_get_my_account}) + std::set{ + shared_model::permissions::can_add_peer, + shared_model::permissions::can_create_asset, + shared_model::permissions::can_get_my_account}) .createDomain("domain", "user") .createAccount("userone", "domain", pubkey1) .build(); @@ -709,9 +725,10 @@ TEST_F(AmetsuchiTest, FindTxByHashTest) { TestTransactionBuilder() .creatorAccountId("admin1") .createRole("usertwo", - std::set{iroha::model::can_add_peer, - iroha::model::can_create_asset, - iroha::model::can_get_my_account}) + std::set{ + shared_model::permissions::can_add_peer, + shared_model::permissions::can_create_asset, + shared_model::permissions::can_get_my_account}) .createDomain("domaintwo", "user") .build(); @@ -730,13 +747,14 @@ TEST_F(AmetsuchiTest, FindTxByHashTest) { auto tx2hash = txn2.hash(); auto tx3hash = shared_model::crypto::Hash("some garbage"); - auto tx1check = *blocks->getTxByHashSync(tx1hash); + auto tx1 = blocks->getTxByHashSync(tx1hash); + ASSERT_TRUE(tx1); - auto tx1 = *blocks->getTxByHashSync(tx1hash); - auto tx2 = *blocks->getTxByHashSync(tx2hash); + auto tx2 = blocks->getTxByHashSync(tx2hash); + ASSERT_TRUE(tx2); - ASSERT_EQ(*tx1.operator->(), txn1); - ASSERT_EQ(*tx2.operator->(), txn2); + ASSERT_EQ(**tx1, txn1); + ASSERT_EQ(**tx2, txn2); ASSERT_EQ(blocks->getTxByHashSync(tx3hash), boost::none); } @@ -863,15 +881,15 @@ TEST_F(AmetsuchiTest, TestRestoreWSV) { auto genesis_tx = shared_model::proto::TransactionBuilder() .creatorAccountId("admin@test") - .txCounter(1) .createdTime(iroha::time::now()) .createRole(default_role, - std::vector{iroha::model::can_create_domain, - iroha::model::can_create_account, - iroha::model::can_add_asset_qty, - iroha::model::can_add_peer, - iroha::model::can_receive, - iroha::model::can_transfer}) + std::vector{ + shared_model::permissions::can_create_domain, + shared_model::permissions::can_create_account, + shared_model::permissions::can_add_asset_qty, + shared_model::permissions::can_add_peer, + shared_model::permissions::can_receive, + shared_model::permissions::can_transfer}) .createDomain(default_domain, default_role) .build() .signAndAddSignature( diff --git a/test/module/irohad/ametsuchi/block_query_test.cpp b/test/module/irohad/ametsuchi/block_query_test.cpp index 8e70a0df32..7c82baa7a6 100644 --- a/test/module/irohad/ametsuchi/block_query_test.cpp +++ b/test/module/irohad/ametsuchi/block_query_test.cpp @@ -19,7 +19,7 @@ #include #include "ametsuchi/impl/postgres_block_index.hpp" #include "ametsuchi/impl/postgres_block_query.hpp" -#include "backend/protobuf/from_old_model.hpp" +#include "converters/protobuf/json_proto_converter.hpp" #include "framework/test_subscriber.hpp" #include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" #include "module/shared_model/builders/protobuf/test_block_builder.hpp" @@ -83,13 +83,9 @@ class BlockQueryTest : public AmetsuchiTest { .build(); for (const auto &b : {block1, block2}) { - // TODO IR-975 victordrobny 12.02.2018 convert from - // shared_model::proto::Block after FlatFile will be reworked to new - // model - auto old_block = *std::unique_ptr(b.makeOldModel()); file->add(b.height(), - iroha::stringToBytes(iroha::model::converters::jsonToString( - iroha::model::converters::JsonBlockFactory().serialize(old_block)))); + iroha::stringToBytes( + shared_model::converters::protobuf::modelToJson(b))); index->index(b); blocks_total++; } @@ -104,7 +100,7 @@ class BlockQueryTest : public AmetsuchiTest { std::string creator1 = "user1@test"; std::string creator2 = "user2@test"; std::size_t blocks_total{0}; - std::string zero_string = std::string("0", 32); + std::string zero_string = std::string(32, '0'); }; /** @@ -167,10 +163,10 @@ TEST_F(BlockQueryTest, GetTransactionsExistingTxHashes) { static auto subs_cnt = 0; subs_cnt++; if (subs_cnt == 1) { - EXPECT_TRUE(tx); + ASSERT_TRUE(tx); EXPECT_EQ(tx_hashes[1], (*tx)->hash()); } else { - EXPECT_TRUE(tx); + ASSERT_TRUE(tx); EXPECT_EQ(tx_hashes[3], (*tx)->hash()); } }); @@ -186,7 +182,7 @@ TEST_F(BlockQueryTest, GetTransactionsExistingTxHashes) { */ TEST_F(BlockQueryTest, GetTransactionsIncludesNonExistingTxHashes) { shared_model::crypto::Hash invalid_tx_hash_1(zero_string), - invalid_tx_hash_2(std::string("9", 32)); + invalid_tx_hash_2(std::string(32, '9')); auto wrapper = make_test_subscriber( blocks->getTransactions({invalid_tx_hash_1, invalid_tx_hash_2}), 2); wrapper.subscribe( @@ -370,3 +366,25 @@ TEST_F(BlockQueryTest, GetTop2Blocks) { ASSERT_TRUE(wrapper.validate()); } + +/** + * @given block store with preinserted blocks + * @when hasTxWithHash is invoked on existing transaction hash + * @then True is returned + */ +TEST_F(BlockQueryTest, HasTxWithExistingHash) { + for (const auto &hash : tx_hashes) { + EXPECT_TRUE(blocks->hasTxWithHash(hash)); + } +} + +/** + * @given block store with preinserted blocks + * user1@test AND 1 tx created by user2@test + * @when hasTxWithHash is invoked on non-existing hash + * @then False is returned + */ +TEST_F(BlockQueryTest, HasTxWithInvalidHash) { + shared_model::crypto::Hash invalid_tx_hash(zero_string); + EXPECT_FALSE(blocks->hasTxWithHash(invalid_tx_hash)); +} diff --git a/test/module/irohad/ametsuchi/block_query_transfer_test.cpp b/test/module/irohad/ametsuchi/block_query_transfer_test.cpp index 4ca512aa04..2820721a70 100644 --- a/test/module/irohad/ametsuchi/block_query_transfer_test.cpp +++ b/test/module/irohad/ametsuchi/block_query_transfer_test.cpp @@ -18,7 +18,7 @@ #include #include "ametsuchi/impl/postgres_block_index.hpp" #include "ametsuchi/impl/postgres_block_query.hpp" -#include "backend/protobuf/from_old_model.hpp" +#include "converters/protobuf/json_proto_converter.hpp" #include "framework/test_subscriber.hpp" #include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" #include "module/shared_model/builders/protobuf/test_block_builder.hpp" @@ -52,16 +52,10 @@ namespace iroha { transaction->exec(init_); } - void insert(const shared_model::interface::Block &block) { - // TODO IR-975 victordrobny 12.02.2018 convert from - // shared_model::proto::Block after FlatFile will be reworked to new - // model - auto old_block = - *std::unique_ptr(block.makeOldModel()); - file->add( - block.height(), - iroha::stringToBytes(model::converters::jsonToString( - model::converters::JsonBlockFactory().serialize(old_block)))); + void insert(const shared_model::proto::Block &block) { + file->add(block.height(), + iroha::stringToBytes( + shared_model::converters::protobuf::modelToJson(block))); index->index(block); } @@ -77,7 +71,7 @@ namespace iroha { std::string asset = "coin#test"; }; - auto zero_string = std::string("0", 32); + auto zero_string = std::string(32, '0'); auto fake_hash = shared_model::crypto::Hash(zero_string); /** diff --git a/test/module/irohad/ametsuchi/kv_storage_test.cpp b/test/module/irohad/ametsuchi/kv_storage_test.cpp index 4d09b1179a..46fa55b442 100644 --- a/test/module/irohad/ametsuchi/kv_storage_test.cpp +++ b/test/module/irohad/ametsuchi/kv_storage_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,20 +17,15 @@ #include #include -#include #include "ametsuchi/block_query.hpp" #include "ametsuchi/impl/postgres_wsv_query.hpp" #include "ametsuchi/impl/storage_impl.hpp" #include "ametsuchi/mutable_storage.hpp" -#include "model/block.hpp" -#include "validators/permissions.hpp" -#include "model/sha3_hash.hpp" #include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" - -// TODO: 14-02-2018 Alexey Chernyshov remove this after relocation to -// shared_model https://soramitsu.atlassian.net/browse/IR-887 -#include "backend/protobuf/from_old_model.hpp" +#include "module/shared_model/builders/protobuf/test_block_builder.hpp" +#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "validators/permissions.hpp" using namespace iroha::ametsuchi; @@ -55,54 +50,34 @@ class KVTest : public AmetsuchiTest { blocks = storage->getBlockQuery(); wsv_query = storage->getWsvQuery(); - // First transaction in block1 - iroha::model::Transaction txn1_1; - txn1_1.creator_account_id = "userone@ru"; - - iroha::model::CreateRole createRole; - createRole.role_name = "user"; - createRole.permissions = {iroha::model::can_add_peer, - iroha::model::can_create_asset, - iroha::model::can_get_my_account}; - - // Create domain ru - txn1_1.commands.push_back( - std::make_shared(createRole)); - iroha::model::CreateDomain createDomain; - createDomain.domain_id = "ru"; - createDomain.user_default_role = "user"; - txn1_1.commands.push_back( - std::make_shared(createDomain)); - - // Create account user1 - iroha::model::CreateAccount createAccount1; - createAccount1.account_name = account_name1; - createAccount1.domain_id = domain_id; - txn1_1.commands.push_back( - std::make_shared(createAccount1)); - - // Create account user2 - iroha::model::CreateAccount createAccount2; - createAccount2.account_name = account_name2; - createAccount2.domain_id = domain_id; - txn1_1.commands.push_back( - std::make_shared(createAccount2)); - - // Set age for user2 - iroha::model::SetAccountDetail setAccount2Age; - setAccount2Age.account_id = account_name2 + "@" + domain_id; - setAccount2Age.key = "age"; - setAccount2Age.value = "24"; - txn1_1.commands.push_back( - std::make_shared(setAccount2Age)); - - iroha::model::Block old_block1; - old_block1.height = 1; - old_block1.transactions.push_back(txn1_1); - old_block1.prev_hash.fill(0); - auto block1hash = iroha::hash(old_block1); - old_block1.hash = block1hash; - old_block1.txs_number = old_block1.transactions.size(); + std::string empty_key(32, '0'); + // transaction for block 1 + auto txn = + TestTransactionBuilder() + .creatorAccountId("userone@ru") + .createRole( + "user", + std::vector{ + shared_model::permissions::can_add_peer, + shared_model::permissions::can_create_asset, + shared_model::permissions::can_get_my_account}) + .createDomain("ru", "user") + .createAccount( + account_name1, + domain_id, + shared_model::crypto::PublicKey(empty_key)) + .createAccount( + account_name2, + domain_id, + shared_model::crypto::PublicKey(empty_key)) + .setAccountDetail(account_name2 + "@" + domain_id, "age", "24") + .build(); + auto block1 = + TestBlockBuilder() + .height(1) + .prevHash(shared_model::crypto::Hash(empty_key)) + .transactions(std::vector{txn}) + .build(); { std::unique_ptr ms; @@ -113,9 +88,7 @@ class KVTest : public AmetsuchiTest { [](iroha::expected::Error &error) { FAIL() << "MutableStorage: " << error.error; }); - // TODO: 14-02-2018 Alexey Chernyshov remove this after relocation to - // shared_model https://soramitsu.atlassian.net/browse/IR-887 - auto block1 = shared_model::proto::from_old(old_block1); + ms->apply(block1, [](const auto &blk, auto &query, const auto &top_hash) { return true; }); diff --git a/test/module/irohad/ametsuchi/wsv_query_command_test.cpp b/test/module/irohad/ametsuchi/wsv_query_command_test.cpp index ed66cb7e75..f277d51d2a 100644 --- a/test/module/irohad/ametsuchi/wsv_query_command_test.cpp +++ b/test/module/irohad/ametsuchi/wsv_query_command_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,12 +18,10 @@ #include "ametsuchi/impl/postgres_wsv_command.hpp" #include "ametsuchi/impl/postgres_wsv_query.hpp" #include "framework/result_fixture.hpp" -#include "backend/protobuf/from_old_model.hpp" -#include "model/account.hpp" -#include "model/asset.hpp" -#include "model/domain.hpp" -#include "model/peer.hpp" #include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" +#include "module/shared_model/builders/protobuf/test_account_builder.hpp" +#include "module/shared_model/builders/protobuf/test_domain_builder.hpp" +#include "module/shared_model/builders/protobuf/test_peer_builder.hpp" namespace iroha { namespace ametsuchi { @@ -33,12 +31,15 @@ namespace iroha { class WsvQueryCommandTest : public AmetsuchiTest { public: WsvQueryCommandTest() { - domain.domain_id = "domain"; - domain.default_role = role; - account.domain_id = domain.domain_id; - account.account_id = "id@" + account.domain_id; - account.quorum = 1; - account.json_data = R"({"id@domain": {"key": "value"}})"; + domain = clone( + TestDomainBuilder().domainId("domain").defaultRole(role).build()); + + account = clone(TestAccountBuilder() + .domainId(domain->domainId()) + .accountId("id@" + domain->domainId()) + .quorum(1) + .jsonData(R"({"id@domain": {"key": "value"}})") + .build()); } void SetUp() override { @@ -59,8 +60,8 @@ namespace iroha { } std::string role = "role", permission = "permission"; - model::Account account; - model::Domain domain; + std::unique_ptr account; + std::unique_ptr domain; std::unique_ptr postgres_connection; std::unique_ptr wsv_transaction; @@ -71,17 +72,26 @@ namespace iroha { class RoleTest : public WsvQueryCommandTest {}; + /** + * @given WSV command and valid role name + * @when trying to insert new role + * @then role is successfully inserted + */ TEST_F(RoleTest, InsertRoleWhenValidName) { - ASSERT_NO_THROW(checkValueCase(command->insertRole(role))); + ASSERT_TRUE(val(command->insertRole(role))); auto roles = query->getRoles(); ASSERT_TRUE(roles); ASSERT_EQ(1, roles->size()); ASSERT_EQ(role, roles->front()); } + /** + * @given WSV command and invalid role name + * @when trying to insert new role + * @then role is failed + */ TEST_F(RoleTest, InsertRoleWhenInvalidName) { - ASSERT_NO_THROW( - checkErrorCase(command->insertRole(std::string(46, 'a')))); + ASSERT_TRUE(err(command->insertRole(std::string(46, 'a')))); auto roles = query->getRoles(); ASSERT_TRUE(roles); @@ -91,13 +101,17 @@ namespace iroha { class RolePermissionsTest : public WsvQueryCommandTest { void SetUp() override { WsvQueryCommandTest::SetUp(); - ASSERT_NO_THROW(checkValueCase(command->insertRole(role))); + ASSERT_TRUE(val(command->insertRole(role))); } }; + /** + * @given WSV command and role exists and valid permissions + * @when trying to insert role permissions + * @then RolePermissions are inserted + */ TEST_F(RolePermissionsTest, InsertRolePermissionsWhenRoleExists) { - ASSERT_NO_THROW( - checkValueCase(command->insertRolePermissions(role, {permission}))); + ASSERT_TRUE(val(command->insertRolePermissions(role, {permission}))); auto permissions = query->getRolePermissions(role); ASSERT_TRUE(permissions); @@ -105,10 +119,14 @@ namespace iroha { ASSERT_EQ(permission, permissions->front()); } + /** + * @given WSV command and role doesn't exist and valid permissions + * @when trying to insert role permissions + * @then RolePermissions are not inserted + */ TEST_F(RolePermissionsTest, InsertRolePermissionsWhenNoRole) { auto new_role = role + " "; - ASSERT_NO_THROW(checkErrorCase( - command->insertRolePermissions(new_role, {permission}))); + ASSERT_TRUE(err(command->insertRolePermissions(new_role, {permission}))); auto permissions = query->getRolePermissions(new_role); ASSERT_TRUE(permissions); @@ -118,9 +136,8 @@ namespace iroha { class AccountTest : public WsvQueryCommandTest { void SetUp() override { WsvQueryCommandTest::SetUp(); - ASSERT_NO_THROW(checkValueCase(command->insertRole(role))); - ASSERT_NO_THROW(checkValueCase( - command->insertDomain(shared_model::proto::from_old(domain)))); + ASSERT_TRUE(val(command->insertRole(role))); + ASSERT_TRUE(val(command->insertDomain(*domain))); } }; @@ -130,11 +147,10 @@ namespace iroha { * @then get account and check json data is the same */ TEST_F(AccountTest, InsertAccountWithJSONData) { - ASSERT_NO_THROW(checkValueCase( - command->insertAccount(shared_model::proto::from_old(account)))); - auto acc = query->getAccount(account.account_id); + ASSERT_TRUE(val(command->insertAccount(*account))); + auto acc = query->getAccount(account->accountId()); ASSERT_TRUE(acc); - ASSERT_EQ(account.json_data, acc.value()->jsonData()); + ASSERT_EQ(account->jsonData(), acc.value()->jsonData()); } /** @@ -143,11 +159,10 @@ namespace iroha { * @then get account and check json data is the same */ TEST_F(AccountTest, InsertNewJSONDataAccount) { - ASSERT_NO_THROW(checkValueCase( - command->insertAccount(shared_model::proto::from_old(account)))); - ASSERT_NO_THROW(checkValueCase(command->setAccountKV( - account.account_id, account.account_id, "id", "val"))); - auto acc = query->getAccount(account.account_id); + ASSERT_TRUE(val(command->insertAccount(*account))); + ASSERT_TRUE(val(command->setAccountKV( + account->accountId(), account->accountId(), "id", "val"))); + auto acc = query->getAccount(account->accountId()); ASSERT_TRUE(acc); ASSERT_EQ(R"({"id@domain": {"id": "val", "key": "value"}})", acc.value()->jsonData()); @@ -159,11 +174,10 @@ namespace iroha { * @then get account and check json data is the same */ TEST_F(AccountTest, InsertNewJSONDataToOtherAccount) { - ASSERT_NO_THROW(checkValueCase( - command->insertAccount(shared_model::proto::from_old(account)))); - ASSERT_NO_THROW(checkValueCase( - command->setAccountKV(account.account_id, "admin", "id", "val"))); - auto acc = query->getAccount(account.account_id); + ASSERT_TRUE(val(command->insertAccount(*account))); + ASSERT_TRUE(val( + command->setAccountKV(account->accountId(), "admin", "id", "val"))); + auto acc = query->getAccount(account->accountId()); ASSERT_TRUE(acc); ASSERT_EQ(R"({"admin": {"id": "val"}, "id@domain": {"key": "value"}})", acc.value()->jsonData()); @@ -175,11 +189,10 @@ namespace iroha { * @then get account and check json data is the same */ TEST_F(AccountTest, InsertNewComplexJSONDataAccount) { - ASSERT_NO_THROW(checkValueCase( - command->insertAccount(shared_model::proto::from_old(account)))); - ASSERT_NO_THROW(checkValueCase(command->setAccountKV( - account.account_id, account.account_id, "id", "[val1, val2]"))); - auto acc = query->getAccount(account.account_id); + ASSERT_TRUE(val(command->insertAccount(*account))); + ASSERT_TRUE(val(command->setAccountKV( + account->accountId(), account->accountId(), "id", "[val1, val2]"))); + auto acc = query->getAccount(account->accountId()); ASSERT_TRUE(acc); ASSERT_EQ(R"({"id@domain": {"id": "[val1, val2]", "key": "value"}})", acc.value()->jsonData()); @@ -191,11 +204,10 @@ namespace iroha { * @then get account and check json data is the same */ TEST_F(AccountTest, UpdateAccountJSONData) { - ASSERT_NO_THROW(checkValueCase( - command->insertAccount(shared_model::proto::from_old(account)))); - ASSERT_NO_THROW(checkValueCase(command->setAccountKV( - account.account_id, account.account_id, "key", "val2"))); - auto acc = query->getAccount(account.account_id); + ASSERT_TRUE(val(command->insertAccount(*account))); + ASSERT_TRUE(val(command->setAccountKV( + account->accountId(), account->accountId(), "key", "val2"))); + auto acc = query->getAccount(account->accountId()); ASSERT_TRUE(acc); ASSERT_EQ(R"({"id@domain": {"key": "val2"}})", acc.value()->jsonData()); } @@ -221,40 +233,51 @@ namespace iroha { class AccountRoleTest : public WsvQueryCommandTest { void SetUp() override { WsvQueryCommandTest::SetUp(); - ASSERT_NO_THROW(checkValueCase(command->insertRole(role))); - ASSERT_NO_THROW(checkValueCase( - command->insertDomain(shared_model::proto::from_old(domain)))); - ASSERT_NO_THROW(checkValueCase( - command->insertAccount(shared_model::proto::from_old(account)))); + ASSERT_TRUE(val(command->insertRole(role))); + ASSERT_TRUE(val(command->insertDomain(*domain))); + ASSERT_TRUE(val(command->insertAccount(*account))); } }; + /** + * @given WSV command and account exists and valid account role + * @when trying to insert account + * @then account role is inserted + */ TEST_F(AccountRoleTest, InsertAccountRoleWhenAccountRoleExist) { - ASSERT_NO_THROW( - checkValueCase(command->insertAccountRole(account.account_id, role))); + ASSERT_TRUE(val(command->insertAccountRole(account->accountId(), role))); - auto roles = query->getAccountRoles(account.account_id); + auto roles = query->getAccountRoles(account->accountId()); ASSERT_TRUE(roles); ASSERT_EQ(1, roles->size()); ASSERT_EQ(role, roles->front()); } + /** + * @given WSV command and account does not exist and valid account role + * @when trying to insert account + * @then account role is not inserted + */ TEST_F(AccountRoleTest, InsertAccountRoleWhenNoAccount) { - auto account_id = account.account_id + " "; - ASSERT_NO_THROW( - checkErrorCase(command->insertAccountRole(account_id, role))); + auto account_id = account->accountId() + " "; + ASSERT_TRUE(err(command->insertAccountRole(account_id, role))); auto roles = query->getAccountRoles(account_id); ASSERT_TRUE(roles); ASSERT_EQ(0, roles->size()); } + /** + * @given WSV command and account exists and invalid account role + * @when trying to insert account + * @then account role is not inserted + */ TEST_F(AccountRoleTest, InsertAccountRoleWhenNoRole) { auto new_role = role + " "; - ASSERT_NO_THROW(checkErrorCase( - command->insertAccountRole(account.account_id, new_role))); + ASSERT_TRUE( + err(command->insertAccountRole(account->accountId(), new_role))); - auto roles = query->getAccountRoles(account.account_id); + auto roles = query->getAccountRoles(account->accountId()); ASSERT_TRUE(roles); ASSERT_EQ(0, roles->size()); } @@ -265,11 +288,9 @@ namespace iroha { * @then role is detached */ TEST_F(AccountRoleTest, DeleteAccountRoleWhenExist) { - ASSERT_NO_THROW( - checkValueCase(command->insertAccountRole(account.account_id, role))); - ASSERT_NO_THROW( - checkValueCase(command->deleteAccountRole(account.account_id, role))); - auto roles = query->getAccountRoles(account.account_id); + ASSERT_TRUE(val(command->insertAccountRole(account->accountId(), role))); + ASSERT_TRUE(val(command->deleteAccountRole(account->accountId(), role))); + auto roles = query->getAccountRoles(account->accountId()); ASSERT_TRUE(roles); ASSERT_EQ(0, roles->size()); } @@ -280,10 +301,9 @@ namespace iroha { * @then nothing is deleted */ TEST_F(AccountRoleTest, DeleteAccountRoleWhenNoAccount) { - ASSERT_NO_THROW( - checkValueCase(command->insertAccountRole(account.account_id, role))); - ASSERT_NO_THROW(checkValueCase(command->deleteAccountRole("no", role))); - auto roles = query->getAccountRoles(account.account_id); + ASSERT_TRUE(val(command->insertAccountRole(account->accountId(), role))); + ASSERT_TRUE(val(command->deleteAccountRole("no", role))); + auto roles = query->getAccountRoles(account->accountId()); ASSERT_TRUE(roles); ASSERT_EQ(1, roles->size()); } @@ -294,85 +314,96 @@ namespace iroha { * @then nothing is deleted */ TEST_F(AccountRoleTest, DeleteAccountRoleWhenNoRole) { - ASSERT_NO_THROW( - checkValueCase(command->insertAccountRole(account.account_id, role))); - ASSERT_NO_THROW( - checkValueCase(command->deleteAccountRole(account.account_id, "no"))); - auto roles = query->getAccountRoles(account.account_id); + ASSERT_TRUE(val(command->insertAccountRole(account->accountId(), role))); + ASSERT_TRUE(val(command->deleteAccountRole(account->accountId(), "no"))); + auto roles = query->getAccountRoles(account->accountId()); ASSERT_TRUE(roles); ASSERT_EQ(1, roles->size()); } class AccountGrantablePermissionTest : public WsvQueryCommandTest { public: - AccountGrantablePermissionTest() { - permittee_account = account; - permittee_account.account_id = "id2@" + permittee_account.domain_id; - permittee_account.quorum = 1; - } - void SetUp() override { WsvQueryCommandTest::SetUp(); - ASSERT_NO_THROW(checkValueCase(command->insertRole(role))); - ASSERT_NO_THROW(checkValueCase( - command->insertDomain(shared_model::proto::from_old(domain)))); - ASSERT_NO_THROW(checkValueCase( - command->insertAccount(shared_model::proto::from_old(account)))); - ASSERT_NO_THROW(checkValueCase(command->insertAccount( - shared_model::proto::from_old(permittee_account)))); + + permittee_account = + clone(TestAccountBuilder() + .domainId(domain->domainId()) + .accountId("id2@" + domain->domainId()) + .quorum(1) + .jsonData(R"({"id@domain": {"key": "value"}})") + .build()); + + ASSERT_TRUE(val(command->insertRole(role))); + ASSERT_TRUE(val(command->insertDomain(*domain))); + ASSERT_TRUE(val(command->insertAccount(*account))); + ASSERT_TRUE(val(command->insertAccount(*permittee_account))); } - model::Account permittee_account; + std::shared_ptr permittee_account; }; + /** + * @given WSV command and account exists and valid grantable permissions + * @when trying to insert grantable permissions + * @then grantable permissions are inserted + */ TEST_F(AccountGrantablePermissionTest, InsertAccountGrantablePermissionWhenAccountsExist) { - ASSERT_NO_THROW(checkValueCase(command->insertAccountGrantablePermission( - permittee_account.account_id, account.account_id, permission))); + ASSERT_TRUE(val(command->insertAccountGrantablePermission( + permittee_account->accountId(), account->accountId(), permission))); ASSERT_TRUE(query->hasAccountGrantablePermission( - permittee_account.account_id, account.account_id, permission)); + permittee_account->accountId(), account->accountId(), permission)); } + /** + * @given WSV command and invalid permittee and valid grantable permissions + * @when trying to insert grantable permissions + * @then grantable permissions are not inserted + */ TEST_F(AccountGrantablePermissionTest, InsertAccountGrantablePermissionWhenNoPermitteeAccount) { - auto permittee_account_id = permittee_account.account_id + " "; - ASSERT_NO_THROW(checkErrorCase(command->insertAccountGrantablePermission( - permittee_account_id, account.account_id, permission))); + auto permittee_account_id = permittee_account->accountId() + " "; + ASSERT_TRUE(err(command->insertAccountGrantablePermission( + permittee_account_id, account->accountId(), permission))); ASSERT_FALSE(query->hasAccountGrantablePermission( - permittee_account_id, account.account_id, permission)); + permittee_account_id, account->accountId(), permission)); } TEST_F(AccountGrantablePermissionTest, InsertAccountGrantablePermissionWhenNoAccount) { - auto account_id = account.account_id + " "; - ASSERT_NO_THROW(checkErrorCase(command->insertAccountGrantablePermission( - permittee_account.account_id, account_id, permission))); + auto account_id = account->accountId() + " "; + ASSERT_TRUE(err(command->insertAccountGrantablePermission( + permittee_account->accountId(), account_id, permission))); ASSERT_FALSE(query->hasAccountGrantablePermission( - permittee_account.account_id, account_id, permission)); + permittee_account->accountId(), account_id, permission)); } + /** + * @given WSV command to delete grantable permission with valid parameters + * @when trying to delete grantable permissions + * @then grantable permissions are deleted + */ TEST_F(AccountGrantablePermissionTest, DeleteAccountGrantablePermissionWhenAccountsPermissionExist) { - ASSERT_NO_THROW(checkValueCase(command->deleteAccountGrantablePermission( - permittee_account.account_id, account.account_id, permission))); + ASSERT_TRUE(val(command->deleteAccountGrantablePermission( + permittee_account->accountId(), account->accountId(), permission))); ASSERT_FALSE(query->hasAccountGrantablePermission( - permittee_account.account_id, account.account_id, permission)); + permittee_account->accountId(), account->accountId(), permission)); } class DeletePeerTest : public WsvQueryCommandTest { public: - DeletePeerTest() { - peer = model::Peer(); - } - void SetUp() override { WsvQueryCommandTest::SetUp(); + + peer = clone(TestPeerBuilder().build()); } - model::Peer peer; + std::unique_ptr peer; }; /** @@ -381,11 +412,9 @@ namespace iroha { * @then peer is successfully deleted */ TEST_F(DeletePeerTest, DeletePeerValidWhenPeerExists) { - ASSERT_NO_THROW(checkValueCase( - command->insertPeer(shared_model::proto::from_old(peer)))); + ASSERT_TRUE(val(command->insertPeer(*peer))); - ASSERT_NO_THROW(checkValueCase( - command->deletePeer(shared_model::proto::from_old(peer)))); + ASSERT_TRUE(val(command->deletePeer(*peer))); } class GetAssetTest : public WsvQueryCommandTest {}; diff --git a/test/module/irohad/common/CMakeLists.txt b/test/module/irohad/common/CMakeLists.txt index 685320ae24..879ed80660 100644 --- a/test/module/irohad/common/CMakeLists.txt +++ b/test/module/irohad/common/CMakeLists.txt @@ -3,10 +3,7 @@ target_link_libraries(blob_converter_test common ) -AddTest(block_insertion_test raw_block_loader_test.cpp) -target_link_libraries(block_insertion_test - json_model_converters - test_block_generator - raw_block_insertion - logger +AddTest(raw_block_loader_test raw_block_loader_test.cpp) +target_link_libraries(raw_block_loader_test + raw_block_loader ) diff --git a/test/module/irohad/common/raw_block_loader_test.cpp b/test/module/irohad/common/raw_block_loader_test.cpp index 20906b3eef..62b23a7a5e 100644 --- a/test/module/irohad/common/raw_block_loader_test.cpp +++ b/test/module/irohad/common/raw_block_loader_test.cpp @@ -15,32 +15,39 @@ * limitations under the License. */ -#include "main/raw_block_loader.hpp" #include -#include "framework/test_block_generator.hpp" -#include "model/converters/json_block_factory.hpp" -#include "model/converters/json_common.hpp" -#include "model/converters/pb_command_factory.hpp" +#include "interfaces/iroha_internal/block.hpp" +#include "main/raw_block_loader.hpp" -using namespace iroha::main; -using namespace iroha::model::converters; -using namespace iroha; +using iroha::main::BlockLoader; /** - * @given generated block - * - * @when convert block to JSON - * AND parseBlock() with BlockLoader API - * - * @then check that blocks are equal + * @given block in json format + * @when converting json to block using raw block loader + * @then check that the block is correct */ -TEST(BlockLoaderTest, BlockLoaderWhenParseBlock) { +TEST(BlockLoaderTest, BlockLoaderJsonParsing) { BlockLoader loader; - auto block = framework::generator::generateBlock(); - auto doc = JsonBlockFactory().serialize(block); - auto str = jsonToString(doc); - auto new_block = loader.parseBlock(str); - ASSERT_TRUE(new_block); - ASSERT_EQ(block, *new_block); + auto str = + R"({ +"payload": { + "transactions": [], + "height": 1, + "prev_block_hash": "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=", + "created_time": 0 + }, +"signatures": [] +})"; + + auto block = loader.parseBlock(str); + + ASSERT_TRUE(block); + auto b = block.value(); + + ASSERT_EQ(b->transactions().size(), 0); + ASSERT_EQ(b->height(), 1); + ASSERT_EQ(b->createdTime(), 0); + ASSERT_TRUE(b->signatures().empty()); + ASSERT_EQ(b->prevHash().hex(), "0101010101010101010101010101010101010101010101010101010101010101"); } diff --git a/test/module/irohad/consensus/yac/CMakeLists.txt b/test/module/irohad/consensus/yac/CMakeLists.txt index 026f86f422..3cf40ea7c0 100644 --- a/test/module/irohad/consensus/yac/CMakeLists.txt +++ b/test/module/irohad/consensus/yac/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. +# Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. # http://soramitsu.co.jp # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,49 +19,39 @@ addtest(cluster_order_test cluster_order_test.cpp) target_link_libraries(cluster_order_test yac - shared_model_cryptography_model + shared_model_cryptography ) addtest(yac_cold_case_test yac_simple_cold_case_test.cpp) target_link_libraries(yac_cold_case_test yac - model - shared_model_cryptography_model ) addtest(yac_sunny_day_test yac_sunny_day_test.cpp) target_link_libraries(yac_sunny_day_test yac - model shared_model_cryptography_model ) addtest(yac_rainy_day_test yac_rainy_day_test.cpp) target_link_libraries(yac_rainy_day_test yac - model shared_model_cryptography_model ) addtest(yac_unknown_peer_test yac_unknown_peer_test.cpp) target_link_libraries(yac_unknown_peer_test yac - model - shared_model_stateless_validation ) addtest(yac_block_storage_test yac_block_storage_test.cpp) target_link_libraries(yac_block_storage_test yac - model - shared_model_cryptography_model ) addtest(yac_proposal_storage_test yac_proposal_storage_test.cpp) target_link_libraries(yac_proposal_storage_test yac - model - shared_model_stateless_validation ) addtest(yac_timer_test timer_test.cpp) @@ -72,47 +62,46 @@ target_link_libraries(yac_timer_test addtest(yac_network_test network_test.cpp) target_link_libraries(yac_network_test yac - model - shared_model_stateless_validation + shared_model_interfaces + shared_model_default_builders ) addtest(yac_peer_orderer_test peer_orderer_test.cpp) target_link_libraries(yac_peer_orderer_test yac - shared_model_stateless_validation - model + shared_model_interfaces + shared_model_default_builders ) addtest(yac_gate_test yac_gate_test.cpp) target_link_libraries(yac_gate_test yac - model shared_model_cryptography_model - shared_model_stateless_validation + shared_model_default_builders ) addtest(yac_hash_provider_test yac_hash_provider_test.cpp) target_link_libraries(yac_hash_provider_test yac - shared_model_cryptography_model + shared_model_cryptography ) addtest(yac_common_test yac_common_test.cpp) target_link_libraries(yac_common_test yac - shared_model_cryptography_model + shared_model_cryptography ) addtest(yac_crypto_provider_test yac_crypto_provider_test.cpp) target_link_libraries(yac_crypto_provider_test yac - model shared_model_cryptography_model - shared_model_stateless_validation + shared_model_default_builders ) addtest(supermajority_checker_test supermajority_checker_test.cpp) target_link_libraries(supermajority_checker_test yac - model + shared_model_cryptography + shared_model_stateless_validation ) diff --git a/test/module/irohad/consensus/yac/network_test.cpp b/test/module/irohad/consensus/yac/network_test.cpp index d338d06038..4e6a182213 100644 --- a/test/module/irohad/consensus/yac/network_test.cpp +++ b/test/module/irohad/consensus/yac/network_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,9 +19,7 @@ #include -#include "consensus/yac/storage/yac_proposal_storage.hpp" #include "consensus/yac/transport/impl/network_impl.hpp" -#include "consensus/yac/transport/yac_pb_converters.hpp" using ::testing::_; using ::testing::InvokeWithoutArgs; @@ -47,6 +45,7 @@ namespace iroha { .build(); message.hash.block_signature = clone(sig); + message.signature = createSig(""); network->subscribe(notifications); grpc::ServerBuilder builder; diff --git a/test/module/irohad/consensus/yac/peer_orderer_test.cpp b/test/module/irohad/consensus/yac/peer_orderer_test.cpp index 9cbd59ed41..2314a2bd46 100644 --- a/test/module/irohad/consensus/yac/peer_orderer_test.cpp +++ b/test/module/irohad/consensus/yac/peer_orderer_test.cpp @@ -29,7 +29,6 @@ #include "consensus/yac/storage/yac_proposal_storage.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" #include "module/irohad/consensus/yac/yac_mocks.hpp" -#include "validators/field_validator.hpp" using namespace boost::adaptors; using namespace iroha::ametsuchi; diff --git a/test/module/irohad/consensus/yac/supermajority_checker_test.cpp b/test/module/irohad/consensus/yac/supermajority_checker_test.cpp index 4ff61d4624..5429d18fee 100644 --- a/test/module/irohad/consensus/yac/supermajority_checker_test.cpp +++ b/test/module/irohad/consensus/yac/supermajority_checker_test.cpp @@ -17,8 +17,12 @@ #include +#include "backend/protobuf/common_objects/signature.hpp" +#include "builders/protobuf/common_objects/proto_peer_builder.hpp" +#include "builders/protobuf/common_objects/proto_signature_builder.hpp" #include "consensus/yac/impl/supermajority_checker_impl.hpp" #include "logger/logger.hpp" +#include "module/shared_model/builders/protobuf/test_block_builder.hpp" using namespace iroha::consensus::yac; @@ -87,3 +91,30 @@ TEST_F(SupermajorityCheckerTest, RejectProofNegativeCase) { ASSERT_FALSE(hasReject(5, 6, 7)); ASSERT_FALSE(hasReject(6, 6, 7)); } + +/** + * @given a pair of peers and a pair different signatures by the first peer + * @when hasSupermajority is called + * @then it return false + */ +TEST_F(SupermajorityCheckerTest, PublicKeyUniqueness) { + using namespace shared_model::crypto; + std::vector> peers; + auto make_peer_key = [&peers](const std::string &key) { + PublicKey pub_key(key); + peers.emplace_back(clone(shared_model::proto::PeerBuilder() + .address("localhost") + .pubkey(pub_key) + .build())); + return pub_key; + }; + + auto peer_key = make_peer_key(std::string(32, '0')); + make_peer_key(std::string(32, '1')); + + auto block = TestBlockBuilder().build(); + block.addSignature(Signed("1"), peer_key); + block.addSignature(Signed("2"), peer_key); + + ASSERT_FALSE(hasSupermajority(block.signatures(), peers)); +} diff --git a/test/module/irohad/consensus/yac/timer_test.cpp b/test/module/irohad/consensus/yac/timer_test.cpp index 3eed8b8e23..c97c17aebc 100644 --- a/test/module/irohad/consensus/yac/timer_test.cpp +++ b/test/module/irohad/consensus/yac/timer_test.cpp @@ -24,41 +24,45 @@ using namespace iroha::consensus::yac; class TimerTest : public ::testing::Test { protected: void SetUp() override { - timer = std::make_shared(); + timer = std::make_shared( + [this] { return invoke_delay.get_observable(); }); } void TearDown() override { timer.reset(); } + void invokeTimer() { + invoke_delay.get_subscriber().on_next(0); + } + public: + rxcpp::subjects::subject invoke_delay; std::shared_ptr timer; }; TEST_F(TimerTest, NothingInvokedWhenDenied) { int status = 0; - timer->invokeAfterDelay(50, [&status]() { status = 1; }); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + timer->invokeAfterDelay([&status] { status = 1; }); timer->deny(); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); + invokeTimer(); ASSERT_EQ(status, 0); } TEST_F(TimerTest, FirstInvokedWhenOneSubmitted) { int status = 0; - timer->invokeAfterDelay(10, [&status]() { status = 1; }); - std::this_thread::sleep_for(std::chrono::milliseconds(20)); + timer->invokeAfterDelay([&status] { status = 1; }); + invokeTimer(); ASSERT_EQ(status, 1); } TEST_F(TimerTest, SecondInvokedWhenTwoSubmitted) { int status = 0; - timer->invokeAfterDelay(10, [&status]() { status = 1; }); - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - timer->invokeAfterDelay(10, [&status]() { status = 2; }); - std::this_thread::sleep_for(std::chrono::milliseconds(20)); + timer->invokeAfterDelay([&status] { status = 1; }); + timer->invokeAfterDelay([&status] { status = 2; }); + invokeTimer(); ASSERT_EQ(status, 2); } diff --git a/test/module/irohad/consensus/yac/yac_common_test.cpp b/test/module/irohad/consensus/yac/yac_common_test.cpp index 037ff904cc..51e8b0490a 100644 --- a/test/module/irohad/consensus/yac/yac_common_test.cpp +++ b/test/module/irohad/consensus/yac/yac_common_test.cpp @@ -15,8 +15,9 @@ * limitations under the License. */ -#include "consensus/yac/storage/yac_common.hpp" #include + +#include "consensus/yac/storage/yac_common.hpp" #include "consensus/yac/storage/yac_proposal_storage.hpp" #include "logger/logger.hpp" #include "module/irohad/consensus/yac/yac_mocks.hpp" diff --git a/test/module/irohad/consensus/yac/yac_crypto_provider_test.cpp b/test/module/irohad/consensus/yac/yac_crypto_provider_test.cpp index 2d448f39be..179bc7fc12 100644 --- a/test/module/irohad/consensus/yac/yac_crypto_provider_test.cpp +++ b/test/module/irohad/consensus/yac/yac_crypto_provider_test.cpp @@ -18,24 +18,25 @@ #include #include "builders/protobuf/common_objects/proto_signature_builder.hpp" #include "consensus/yac/impl/yac_crypto_provider_impl.hpp" -#include "consensus/yac/impl/yac_hash_provider_impl.hpp" #include "consensus/yac/messages.hpp" -#include "cryptography/ed25519_sha3_impl/internal/ed25519_impl.hpp" +#include "cryptography/crypto_provider/crypto_defaults.hpp" -const auto pubkey = std::string('0', 32); -const auto signed_data = std::string('1', 32); +const auto pubkey = std::string(32, '0'); +const auto signed_data = std::string(32, '1'); namespace iroha { namespace consensus { namespace yac { class YacCryptoProviderTest : public ::testing::Test { public: - YacCryptoProviderTest() : keypair(create_keypair()) {} + YacCryptoProviderTest() + : keypair(shared_model::crypto::DefaultCryptoAlgorithmType:: + generateKeypair()) {} void SetUp() override { crypto_provider = std::make_shared(keypair); } - const keypair_t keypair; + const shared_model::crypto::Keypair keypair; std::shared_ptr crypto_provider; }; diff --git a/test/module/irohad/consensus/yac/yac_gate_test.cpp b/test/module/irohad/consensus/yac/yac_gate_test.cpp index 84de78655f..98ef083568 100644 --- a/test/module/irohad/consensus/yac/yac_gate_test.cpp +++ b/test/module/irohad/consensus/yac/yac_gate_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,19 +18,15 @@ #include #include -#include "backend/protobuf/from_old_model.hpp" #include "builders/protobuf/block.hpp" #include "builders/protobuf/common_objects/proto_signature_builder.hpp" #include "consensus/yac/impl/yac_gate_impl.hpp" #include "consensus/yac/storage/yac_proposal_storage.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" -#include "cryptography/hash.hpp" -#include "cryptography/hash_providers/sha3_256.hpp" #include "framework/test_subscriber.hpp" #include "module/irohad/consensus/yac/yac_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" #include "module/irohad/simulator/simulator_mocks.hpp" -#include "cryptography/hash.hpp" using namespace iroha::consensus::yac; using namespace iroha::network; @@ -61,15 +57,11 @@ class YacGateTest : public ::testing::Test { .signAndAddSignature(keypair); expected_block = clone(tmp); - const auto &wrapped_sig = *(expected_block->signatures().begin()); - const auto &signature = *wrapped_sig; - // TODO: 24/04/2018 x3medima17 remove makeOldModel in next PR - const auto old_signature = - *std::unique_ptr(signature.makeOldModel()); + const auto &signature = *(expected_block->signatures().begin()); expected_hash.block_signature = clone(signature); message.hash = expected_hash; - message.signature = old_signature; + message.signature = clone(signature); commit_message = CommitMessage({message}); expected_commit = rxcpp::observable<>::just(commit_message); @@ -133,9 +125,8 @@ TEST_F(YacGateTest, YacGateSubscriptionTest) { // verify that yac gate emit expected block auto gate_wrapper = make_test_subscriber(gate->on_commit(), 1); - gate_wrapper.subscribe([this](auto block) { - ASSERT_EQ(*block, *expected_block); - }); + gate_wrapper.subscribe( + [this](auto block) { ASSERT_EQ(*block, *expected_block); }); ASSERT_TRUE(gate_wrapper.validate()); } @@ -195,8 +186,62 @@ TEST_F(YacGateTest, LoadBlockWhenDifferentCommit) { // load block auto sig = expected_block->signatures().begin(); - auto &pubkey = (*sig)->publicKey(); + auto &pubkey = sig->publicKey(); + EXPECT_CALL(*block_loader, retrieveBlock(pubkey, expected_block->hash())) + .WillOnce(Return(expected_block)); + + init(); + + // verify that yac gate emit expected block + auto gate_wrapper = make_test_subscriber(gate->on_commit(), 1); + gate_wrapper.subscribe( + [this](auto block) { ASSERT_EQ(*block, *expected_block); }); + + ASSERT_TRUE(gate_wrapper.validate()); +} + +/** + * @given yac gate + * @when recives new commit different to the one it voted for + * @then polls nodes for the block with corresponding hash until it succeed, + * (reciving none on the first poll) + */ +TEST_F(YacGateTest, LoadBlockWhenDifferentCommitFailFirst) { + // Vote for block => receive different block => load committed block + + // make blocks + EXPECT_CALL(*block_creator, on_block()) + .WillOnce(Return(rxcpp::observable<>::just(expected_block))); + + // make hash from block + EXPECT_CALL(*hash_provider, makeHash(_)).WillOnce(Return(expected_hash)); + + // generate order of peers + EXPECT_CALL(*peer_orderer, getOrdering(_)) + .WillOnce(Return(ClusterOrdering::create({mk_peer("fake_node")}))); + + EXPECT_CALL(*hash_gate, vote(expected_hash, _)).Times(1); + + // expected values + expected_hash = YacHash("actual_proposal", "actual_block"); + + message.hash = expected_hash; + + commit_message = CommitMessage({message}); + expected_commit = rxcpp::observable<>::just(commit_message); + + // yac consensus + EXPECT_CALL(*hash_gate, on_commit()).WillOnce(Return(expected_commit)); + + // convert yac hash to model hash + EXPECT_CALL(*hash_provider, toModelHash(expected_hash)) + .WillOnce(Return(expected_block->hash())); + + // load block + auto sig = expected_block->signatures().begin(); + auto &pubkey = sig->publicKey(); EXPECT_CALL(*block_loader, retrieveBlock(pubkey, expected_block->hash())) + .WillOnce(Return(boost::none)) .WillOnce(Return(expected_block)); init(); diff --git a/test/module/irohad/consensus/yac/yac_mocks.hpp b/test/module/irohad/consensus/yac/yac_mocks.hpp index 34262eab54..36992fd860 100644 --- a/test/module/irohad/consensus/yac/yac_mocks.hpp +++ b/test/module/irohad/consensus/yac/yac_mocks.hpp @@ -32,7 +32,9 @@ #include "consensus/yac/yac_gate.hpp" #include "consensus/yac/yac_hash_provider.hpp" #include "consensus/yac/yac_peer_orderer.hpp" +#include "cryptography/crypto_provider/crypto_defaults.hpp" #include "interfaces/iroha_internal/block.hpp" +#include "module/shared_model/builders/protobuf/test_signature_builder.hpp" namespace iroha { namespace consensus { @@ -49,12 +51,28 @@ namespace iroha { return clone(ptr); } + /** + * Creates test signature with empty signed data, and provided pubkey + * @param pub_key - public key to put in the signature + * @return new signature + */ + std::shared_ptr createSig( + const std::string &pub_key) { + auto tmp = + shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair() + .publicKey(); + std::string key(tmp.blob().size(), 0); + std::copy(pub_key.begin(), pub_key.end(), key.begin()); + + return clone(TestSignatureBuilder() + .publicKey(shared_model::crypto::PublicKey(key)) + .build()); + } + VoteMessage create_vote(YacHash hash, std::string pub_key) { VoteMessage vote; vote.hash = hash; - // TODO: 19.01.2019 kamil substitute with function, IR-813 - std::copy( - pub_key.begin(), pub_key.end(), vote.signature.pubkey.begin()); + vote.signature = createSig(pub_key); return vote; } @@ -67,6 +85,7 @@ namespace iroha { VoteMessage getVote(YacHash hash) override { VoteMessage vote; vote.hash = hash; + vote.signature = createSig(""); return vote; } @@ -81,8 +100,7 @@ namespace iroha { class MockTimer : public Timer { public: - void invokeAfterDelay(uint64_t millis, - std::function handler) override { + void invokeAfterDelay(std::function handler) override { handler(); } @@ -205,13 +223,15 @@ namespace iroha { public: MOCK_CONST_METHOD2( hasSupermajority, - bool(const shared_model::interface::SignatureSetType &signatures, + bool(const shared_model::interface::types::SignatureRangeType + &signatures, const std::vector< std::shared_ptr> &peers)); MOCK_CONST_METHOD2(checkSize, bool(uint64_t current, uint64_t all)); MOCK_CONST_METHOD2( peersSubset, - bool(const shared_model::interface::SignatureSetType &signatures, + bool(const shared_model::interface::types::SignatureRangeType + &signatures, const std::vector< std::shared_ptr> &peers)); MOCK_CONST_METHOD3( @@ -224,7 +244,6 @@ namespace iroha { std::shared_ptr network; std::shared_ptr crypto; std::shared_ptr timer; - uint64_t delay = 100500; std::shared_ptr yac; // ------|Round|------ @@ -244,18 +263,17 @@ namespace iroha { timer = std::make_shared(); auto ordering = ClusterOrdering::create(default_peers); ASSERT_TRUE(ordering); - yac = Yac::create(YacVoteStorage(), - network, - crypto, - timer, - ordering.value(), - delay); - network->subscribe(yac); - }; + initYac(ordering.value()); + } void TearDown() override { network->release(); - }; + } + + void initYac(ClusterOrdering ordering) { + yac = Yac::create(YacVoteStorage(), network, crypto, timer, ordering); + network->subscribe(yac); + } }; } // namespace yac } // namespace consensus diff --git a/test/module/irohad/consensus/yac/yac_rainy_day_test.cpp b/test/module/irohad/consensus/yac/yac_rainy_day_test.cpp index 98889ff8d8..28d1a87929 100644 --- a/test/module/irohad/consensus/yac/yac_rainy_day_test.cpp +++ b/test/module/irohad/consensus/yac/yac_rainy_day_test.cpp @@ -41,12 +41,7 @@ TEST_F(YacTest, InvalidCaseWhenNotReceiveSupermajority) { auto my_order = ClusterOrdering::create(my_peers); ASSERT_TRUE(my_order); - // delay preference - uint64_t wait_seconds = 10; - delay = wait_seconds * 1000; - - yac = Yac::create( - YacVoteStorage(), network, crypto, timer, my_order.value(), delay); + initYac(my_order.value()); EXPECT_CALL(*network, send_commit(_, _)).Times(0); EXPECT_CALL(*network, send_reject(_, _)).Times(my_peers.size()); @@ -84,12 +79,7 @@ TEST_F(YacTest, InvalidCaseWhenDoesNotVerify) { auto my_order = ClusterOrdering::create(my_peers); ASSERT_TRUE(my_order); - // delay preference - uint64_t wait_seconds = 10; - delay = wait_seconds * 1000; - - yac = Yac::create( - YacVoteStorage(), network, crypto, timer, my_order.value(), delay); + initYac(my_order.value()); EXPECT_CALL(*network, send_reject(_, _)).Times(0); @@ -128,12 +118,7 @@ TEST_F(YacTest, ValidCaseWhenReceiveOnVoteAfterReject) { auto my_order = ClusterOrdering::create(my_peers); ASSERT_TRUE(my_order); - // delay preference - uint64_t wait_seconds = 10; - delay = wait_seconds * 1000; - - yac = Yac::create( - YacVoteStorage(), network, crypto, timer, my_order.value(), delay); + initYac(my_order.value()); EXPECT_CALL(*network, send_commit(_, _)).Times(0); EXPECT_CALL(*network, send_reject(_, _)) diff --git a/test/module/irohad/consensus/yac/yac_simple_cold_case_test.cpp b/test/module/irohad/consensus/yac/yac_simple_cold_case_test.cpp index 4978884e57..c889266f26 100644 --- a/test/module/irohad/consensus/yac/yac_simple_cold_case_test.cpp +++ b/test/module/irohad/consensus/yac/yac_simple_cold_case_test.cpp @@ -15,9 +15,6 @@ * limitations under the License. */ -#ifndef IROHA_YAC_SIMPLE_CASE_TEST_HPP -#define IROHA_YAC_SIMPLE_CASE_TEST_HPP - #include #include #include @@ -33,39 +30,13 @@ using ::testing::_; using ::testing::An; using ::testing::AtLeast; +using ::testing::Invoke; using ::testing::Return; using namespace iroha::consensus::yac; using namespace framework::test_subscriber; using namespace std; -/** - * Test provide use case for init yac object - */ -TEST_F(YacTest, YacWhenInit) { - cout << "----------|Just init object|----------" << endl; - - MockYacNetwork network_; - - MockYacCryptoProvider crypto_; - - MockTimer timer_; - - auto fake_delay_ = 100500; - - auto order = ClusterOrdering::create(default_peers); - ASSERT_TRUE(order); - - auto yac_ = Yac::create(YacVoteStorage(), - std::make_shared(network_), - std::make_shared(crypto_), - std::make_shared(timer_), - *order, - fake_delay_); - - network_.subscribe(yac_); -} - /** * Test provide scenario when yac vote for hash */ @@ -176,4 +147,63 @@ TEST_F(YacTest, YacWhenColdStartAndAchieveCommitMessage) { ASSERT_TRUE(wrapper.validate()); } -#endif // IROHA_YAC_SIMPLE_CASE_TEST_HPP + +/** + * @given initialized YAC + * @when receive supermajority of votes for a hash + * @then commit is sent to the network before notifying subscribers + */ +TEST_F(YacTest, PropagateCommitBeforeNotifyingSubscribersApplyVote) { + EXPECT_CALL(*crypto, verify(An())) + .Times(default_peers.size()) + .WillRepeatedly(Return(true)); + std::vector messages; + EXPECT_CALL(*network, send_commit(_, _)) + .Times(default_peers.size()) + .WillRepeatedly(Invoke( + [&](const auto &, const auto &msg) { messages.push_back(msg); })); + + yac->on_commit().subscribe([&](auto msg) { + // verify that commits are already sent to the network + ASSERT_EQ(default_peers.size(), messages.size()); + messages.push_back(msg); + }); + + for (size_t i = 0; i < default_peers.size(); ++i) { + yac->on_vote(create_vote(YacHash{}, std::to_string(i))); + } + + // verify that on_commit subscribers are notified + ASSERT_EQ(default_peers.size() + 1, messages.size()); +} + +/** + * @given initialized YAC + * @when receive reject message which triggers commit + * @then commit is sent to the network before notifying subscribers + */ +TEST_F(YacTest, PropagateCommitBeforeNotifyingSubscribersApplyReject) { + EXPECT_CALL(*crypto, verify(An())).WillOnce(Return(true)); + EXPECT_CALL(*timer, deny()).Times(AtLeast(1)); + std::vector messages; + EXPECT_CALL(*network, send_commit(_, _)) + .Times(default_peers.size()) + .WillRepeatedly(Invoke( + [&](const auto &, const auto &msg) { messages.push_back(msg); })); + + yac->on_commit().subscribe([&](auto msg) { + // verify that commits are already sent to the network + ASSERT_EQ(default_peers.size(), messages.size()); + messages.push_back(msg); + }); + + RejectMessage reject({}); + for (size_t i = 0; i < default_peers.size(); ++i) { + reject.votes.push_back(create_vote(YacHash{}, std::to_string(i))); + } + + yac->on_reject(reject); + + // verify that on_commit subscribers are notified + ASSERT_EQ(default_peers.size() + 1, messages.size()); +} diff --git a/test/module/irohad/consensus/yac/yac_sunny_day_test.cpp b/test/module/irohad/consensus/yac/yac_sunny_day_test.cpp index c78c85bf14..c79e93e1fb 100644 --- a/test/module/irohad/consensus/yac/yac_sunny_day_test.cpp +++ b/test/module/irohad/consensus/yac/yac_sunny_day_test.cpp @@ -41,12 +41,7 @@ TEST_F(YacTest, ValidCaseWhenReceiveSupermajority) { auto my_order = ClusterOrdering::create(my_peers); ASSERT_TRUE(my_order); - // delay preference - uint64_t wait_seconds = 10; - delay = wait_seconds * 1000; - - yac = Yac::create( - YacVoteStorage(), network, crypto, timer, my_order.value(), delay); + initYac(my_order.value()); EXPECT_CALL(*network, send_commit(_, _)).Times(my_peers.size()); EXPECT_CALL(*network, send_reject(_, _)).Times(0); @@ -76,12 +71,7 @@ TEST_F(YacTest, ValidCaseWhenReceiveCommit) { auto my_order = ClusterOrdering::create(my_peers); ASSERT_TRUE(my_order); - // delay preference - uint64_t wait_seconds = 10; - delay = wait_seconds * 1000; - - yac = Yac::create( - YacVoteStorage(), network, crypto, timer, my_order.value(), delay); + initYac(my_order.value()); YacHash my_hash("proposal_hash", "block_hash"); auto wrapper = make_test_subscriber(yac->on_commit(), 1); @@ -118,13 +108,9 @@ TEST_F(YacTest, ValidCaseWhenReceiveCommitTwice) { auto my_order = ClusterOrdering::create(my_peers); ASSERT_TRUE(my_order); - // delay preference - uint64_t wait_seconds = 10; - delay = wait_seconds * 1000; EXPECT_CALL(*timer, deny()).Times(2); - yac = Yac::create( - YacVoteStorage(), network, crypto, timer, my_order.value(), delay); + initYac(my_order.value()); YacHash my_hash("proposal_hash", "block_hash"); auto wrapper = make_test_subscriber(yac->on_commit(), 1); @@ -166,12 +152,7 @@ TEST_F(YacTest, ValidCaseWhenSoloConsensus) { auto my_order = ClusterOrdering::create(my_peers); ASSERT_TRUE(my_order); - // delay preference - uint64_t wait_seconds = 10; - delay = wait_seconds * 1000; - - yac = Yac::create( - YacVoteStorage(), network, crypto, timer, my_order.value(), delay); + initYac(my_order.value()); EXPECT_CALL(*network, send_commit(_, _)).Times(my_peers.size()); EXPECT_CALL(*network, send_reject(_, _)).Times(0); @@ -214,12 +195,7 @@ TEST_F(YacTest, ValidCaseWhenVoteAfterCommit) { auto my_order = ClusterOrdering::create(my_peers); ASSERT_TRUE(my_order); - // delay preference - uint64_t wait_seconds = 10; - delay = wait_seconds * 1000; - - yac = Yac::create( - YacVoteStorage(), network, crypto, timer, my_order.value(), delay); + initYac(my_order.value()); EXPECT_CALL(*network, send_commit(_, _)).Times(0); EXPECT_CALL(*network, send_reject(_, _)).Times(0); diff --git a/test/module/irohad/consensus/yac/yac_unknown_peer_test.cpp b/test/module/irohad/consensus/yac/yac_unknown_peer_test.cpp index 8febd0de8c..fdf8a80da5 100644 --- a/test/module/irohad/consensus/yac/yac_unknown_peer_test.cpp +++ b/test/module/irohad/consensus/yac/yac_unknown_peer_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -51,7 +51,8 @@ TEST_F(YacTest, UnknownVoteBeforeCommit) { VoteMessage vote; vote.hash = YacHash("my_proposal", "my_block"); std::string unknown = "unknown"; - std::copy(unknown.begin(), unknown.end(), vote.signature.pubkey.begin()); + vote.signature = createSig(unknown); + // assume that our peer receive message network->notification->on_vote(vote); @@ -72,12 +73,7 @@ TEST_F(YacTest, UnknownVoteAfterCommit) { auto my_order = ClusterOrdering::create(my_peers); ASSERT_TRUE(my_order); - // delay preference - uint64_t wait_seconds = 10; - delay = wait_seconds * 1000; - - yac = Yac::create( - YacVoteStorage(), network, crypto, timer, my_order.value(), delay); + initYac(my_order.value()); EXPECT_CALL(*network, send_commit(_, _)).Times(0); EXPECT_CALL(*network, send_reject(_, _)).Times(0); @@ -101,7 +97,6 @@ TEST_F(YacTest, UnknownVoteAfterCommit) { VoteMessage vote; vote.hash = my_hash; std::string unknown = "unknown"; - std::copy(unknown.begin(), unknown.end(), vote.signature.pubkey.begin()); - + vote.signature = createSig(unknown); yac->on_vote(vote); } diff --git a/test/module/irohad/execution/CMakeLists.txt b/test/module/irohad/execution/CMakeLists.txt new file mode 100644 index 0000000000..3068bc81a7 --- /dev/null +++ b/test/module/irohad/execution/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright 2018 Soramitsu Co., Ltd. +# +# 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 +# +# http://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. + +addtest(command_validate_execute_test command_validate_execute_test.cpp) +target_link_libraries(command_validate_execute_test + command_execution + shared_model_stateless_validation + ) diff --git a/test/module/irohad/execution/command_validate_execute_test.cpp b/test/module/irohad/execution/command_validate_execute_test.cpp new file mode 100644 index 0000000000..1058cb9749 --- /dev/null +++ b/test/module/irohad/execution/command_validate_execute_test.cpp @@ -0,0 +1,2220 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * 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 + * + * http://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. + */ + +#include + +#include "builders/default_builders.hpp" +#include "execution/command_executor.hpp" +#include "framework/result_fixture.hpp" +#include "interfaces/commands/command.hpp" +#include "interfaces/utils/specified_visitor.hpp" +#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "validators/permissions.hpp" + +using ::testing::_; +using ::testing::Return; +using ::testing::StrictMock; + +using namespace iroha; +using namespace iroha::ametsuchi; +using namespace framework::expected; +using namespace shared_model::permissions; + +// TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework function with +// CommandBuilder +/** + * Hepler function to build command and wrap it into + * std::unique_ptr<> + * @param builder command builder + * @return command + */ +std::unique_ptr buildCommand( + const TestTransactionBuilder &builder) { + return clone(*(builder.build().commands().front())); +} + +/** + * Helper function to get concrete command from Command container. + * @tparam T - type of concrete command + * @param command - Command container + * @return concrete command extracted from container + */ +template +std::shared_ptr getConcreteCommand( + const std::unique_ptr &command) { + return clone( + *(boost::apply_visitor(shared_model::interface::SpecifiedVisitor(), + command->get()) + .value())); +} + +class CommandValidateExecuteTest : public ::testing::Test { + public: + void SetUp() override { + wsv_query = std::make_shared>(); + wsv_command = std::make_shared>(); + + executor = std::make_unique(wsv_query, wsv_command); + validator = std::make_unique(wsv_query); + + shared_model::builder::AccountBuilder< + shared_model::proto::AccountBuilder, + shared_model::validation::FieldValidator>() + .accountId(kAdminId) + .domainId(kDomainId) + .quorum(1) + .build() + .match( + [&](expected::Value< + std::shared_ptr> &v) { + creator = v.value; + }, + [](expected::Error> &e) { + FAIL() << *e.error; + }); + + shared_model::builder::AccountBuilder< + shared_model::proto::AccountBuilder, + shared_model::validation::FieldValidator>() + .accountId(kAccountId) + .domainId(kDomainId) + .quorum(1) + .build() + .match( + [&](expected::Value< + std::shared_ptr> &v) { + account = v.value; + }, + [](expected::Error> &e) { + FAIL() << *e.error; + }); + + shared_model::builder::AssetBuilder< + shared_model::proto::AssetBuilder, + shared_model::validation::FieldValidator>() + .assetId(kAssetId) + .domainId(kDomainId) + .precision(2) + .build() + .match( + [&](expected::Value> + &v) { asset = v.value; }, + [](expected::Error> &e) { + FAIL() << *e.error; + }); + + shared_model::builder::AmountBuilder< + shared_model::proto::AmountBuilder, + shared_model::validation::FieldValidator>() + .intValue(150) + .precision(2) + .build() + .match( + [&](expected::Value< + std::shared_ptr> &v) { + balance = v.value; + }, + [](expected::Error> &e) { + FAIL() << *e.error; + }); + + shared_model::builder::AccountAssetBuilder< + shared_model::proto::AccountAssetBuilder, + shared_model::validation::FieldValidator>() + .assetId(kAssetId) + .accountId(kAccountId) + .balance(*balance) + .build() + .match( + [&](expected::Value< + std::shared_ptr> &v) { + wallet = v.value; + }, + [](expected::Error> &e) { + FAIL() << *e.error; + }); + } + + iroha::ExecutionResult validateAndExecute( + const std::unique_ptr &command) { + validator->setCreatorAccountId(creator->accountId()); + + if (boost::apply_visitor(*validator, command->get())) { + return execute(command); + } + return expected::makeError( + iroha::ExecutionError{"Validate", "validation of a command failed"}); + } + + iroha::ExecutionResult execute( + const std::unique_ptr &command) { + executor->setCreatorAccountId(creator->accountId()); + return boost::apply_visitor(*executor, command->get()); + } + + /// return result with empty error message + WsvCommandResult makeEmptyError() { + return WsvCommandResult(iroha::expected::makeError("")); + } + + /// Returns error from result or throws error in case result contains value + iroha::ExecutionResult::ErrorType checkErrorCase( + const iroha::ExecutionResult &result) { + return boost::get(result); + } + + const std::string kMaxAmountStr = + std::numeric_limits::max().str() + + ".00"; + const std::string kAmountWrongPrecision = "1.0000"; + const std::string kAmount = "1.00"; + const std::string kAmountOverflow = "12.04"; + const std::string kAdminId = "admin@test"; + const std::string kAccountId = "test@test"; + const std::string kNoAcountId = "noacc"; + const std::string kAssetId = "coin#test"; + const std::string kNoAssetId = "no_asset#test"; + const std::string kDomainId = "test"; + const std::string kDescription = "test transfer"; + const std::string kAdminRole = "admin"; + const std::string kMasterRole = "master"; + const std::vector admin_roles = {kAdminRole}; + const shared_model::interface::types::PubkeyType kPubKey1 = + shared_model::interface::types::PubkeyType(std::string(32, '1')); + const shared_model::interface::types::PubkeyType kPubKey2 = + shared_model::interface::types::PubkeyType(std::string(32, '2')); + + std::vector role_permissions; + std::shared_ptr creator, account; + std::shared_ptr balance; + std::shared_ptr asset; + std::shared_ptr wallet; + + std::unique_ptr command; + + std::shared_ptr wsv_query; + std::shared_ptr wsv_command; + + std::unique_ptr executor; + std::unique_ptr validator; +}; + +class AddAssetQuantityTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + role_permissions = {can_add_asset_qty}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().addAssetQuantity( + creator->accountId(), kAssetId, kAmount)); + add_asset_quantity = + getConcreteCommand(command); + } + + std::shared_ptr add_asset_quantity; +}; + +/** + * @given AddAssetQuantity where accountAsset doesn't exist at first call + * @when command is executed, new accountAsset will be created + * @then executor will be passed + */ +TEST_F(AddAssetQuantityTest, ValidWhenNewWallet) { + EXPECT_CALL(*wsv_query, getAccountAsset(add_asset_quantity->accountId(), _)) + .WillOnce(Return(boost::none)); + EXPECT_CALL(*wsv_query, getAsset(add_asset_quantity->assetId())) + .WillOnce(Return(asset)); + EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->accountId())) + .WillOnce(Return(account)); + EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) + .WillOnce(Return(WsvCommandResult())); + EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given AddAssetQuantity where accountAsset exists + * @when command is executed + * @then executor will be passed + */ +TEST_F(AddAssetQuantityTest, ValidWhenExistingWallet) { + EXPECT_CALL(*wsv_query, + getAccountAsset(add_asset_quantity->accountId(), + add_asset_quantity->assetId())) + .WillOnce(Return(wallet)); + EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); + EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->accountId())) + .WillOnce(Return(account)); + EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) + .WillOnce(Return(WsvCommandResult())); + EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->accountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given AddAssetQuantity where command creator role is wrong + * @when command is executed + * @then executor will be failed + */ +TEST_F(AddAssetQuantityTest, InvalidWhenNoRoles) { + EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->accountId())) + .WillOnce(Return(boost::none)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AddAssetQuantity with amount with wrong precision (must be 2) + * @when command is executed + * @then executor will be failed + */ +TEST_F(AddAssetQuantityTest, InvalidWhenWrongPrecision) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - IR-1276 - rework with + // CommandBuilder + command = buildCommand(TestTransactionBuilder().addAssetQuantity( + creator->accountId(), kAssetId, kAmountWrongPrecision)); + add_asset_quantity = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AddAssetQuantity + * @when command references non-existing account + * @then execute fails + */ +TEST_F(AddAssetQuantityTest, InvalidWhenNoAccount) { + // Account to add does not exist + EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->accountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); + EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->accountId())) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AddAssetQuantity with wrong asset + * @when command is executed + * @then execute fails + */ +TEST_F(AddAssetQuantityTest, InvalidWhenNoAsset) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - IR-1276 - rework with + // CommandBuilder + command = buildCommand(TestTransactionBuilder().addAssetQuantity( + creator->accountId(), kNoAssetId, kAmount)); + add_asset_quantity = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + + EXPECT_CALL(*wsv_query, getAsset(add_asset_quantity->assetId())) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AddAssetQuantity + * @when command adds value which overflows account balance + * @then execute fails + */ +TEST_F(AddAssetQuantityTest, InvalidWhenAssetAdditionFails) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().addAssetQuantity( + creator->accountId(), kAssetId, kMaxAmountStr)); + add_asset_quantity = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, + getAccountAsset(add_asset_quantity->accountId(), + add_asset_quantity->assetId())) + .WillOnce(Return(wallet)); + EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); + EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->accountId())) + .WillOnce(Return(account)); + EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->accountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +class SubtractAssetQuantityTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + role_permissions = {can_subtract_asset_qty}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( + creator->accountId(), kAssetId, kAmount)); + subtract_asset_quantity = + getConcreteCommand( + command); + } + + std::shared_ptr + subtract_asset_quantity; +}; + +/** + * @given SubtractAssetQuantity + * @when account doesn't have wallet of target asset + * @then executor will be failed + */ +TEST_F(SubtractAssetQuantityTest, InvalidWhenNoWallet) { + EXPECT_CALL(*wsv_query, + getAccountAsset(subtract_asset_quantity->accountId(), + subtract_asset_quantity->assetId())) + .WillOnce(Return(boost::none)); + EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->accountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given SubtractAssetQuantity + * @when arguments are valid + * @then executor will be passed + */ +TEST_F(SubtractAssetQuantityTest, ValidWhenExistingWallet) { + EXPECT_CALL(*wsv_query, + getAccountAsset(subtract_asset_quantity->accountId(), + subtract_asset_quantity->assetId())) + .WillOnce(Return(wallet)); + EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); + EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) + .WillOnce(Return(WsvCommandResult())); + EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->accountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given SubtractAssetQuantity + * @when arguments amount is greater than wallet's amount + * @then executor will be failed + */ +TEST_F(SubtractAssetQuantityTest, InvalidWhenOverAmount) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( + creator->accountId(), kAssetId, kAmountOverflow)); + subtract_asset_quantity = + getConcreteCommand( + command); + + EXPECT_CALL(*wsv_query, + getAccountAsset(subtract_asset_quantity->accountId(), + subtract_asset_quantity->assetId())) + .WillOnce(Return(wallet)); + + EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->accountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given SubtractAssetQuantity + * @when account doesn't have role + * @then executor will be failed + */ +TEST_F(SubtractAssetQuantityTest, InvalidWhenNoRoles) { + EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->accountId())) + .WillOnce(Return(boost::none)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given SubtractAssetQuantity + * @when arguments amount precision is invalid (greater than 2) + * @then executor will be failed + */ +TEST_F(SubtractAssetQuantityTest, InvalidWhenWrongPrecision) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( + creator->accountId(), kAssetId, kAmountWrongPrecision)); + subtract_asset_quantity = + getConcreteCommand( + command); + + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given SubtractAssetQuantity + * @when account doesn't exist + * @then executor will be failed + */ +TEST_F(SubtractAssetQuantityTest, InvalidWhenNoAccount) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( + kNoAcountId, kAssetId, kAmount)); + subtract_asset_quantity = + getConcreteCommand( + command); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given SubtractAssetQuantity + * @when asset doesn't exist + * @then executor will be failed + */ +TEST_F(SubtractAssetQuantityTest, InvalidWhenNoAsset) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( + creator->accountId(), kNoAssetId, kAmount)); + subtract_asset_quantity = + getConcreteCommand( + command); + + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getAsset(subtract_asset_quantity->assetId())) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +class AddSignatoryTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + role_permissions = {can_add_signatory}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().addSignatory(kAccountId, kPubKey1)); + add_signatory = + getConcreteCommand(command); + } + + std::shared_ptr add_signatory; +}; + +/** + * @given AddSignatory creator has role permission to add signatory + * @when command is executed + * @then executor finishes successfully + */ +TEST_F(AddSignatoryTest, ValidWhenCreatorHasPermissions) { + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, add_signatory->accountId(), can_add_my_signatory)) + .WillOnce(Return(true)); + EXPECT_CALL(*wsv_command, insertSignatory(add_signatory->pubkey())) + .WillOnce(Return(WsvCommandResult())); + EXPECT_CALL(*wsv_command, + insertAccountSignatory(add_signatory->accountId(), + add_signatory->pubkey())) + .WillOnce(Return(WsvCommandResult())); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given AddSignatory with valid parameters + * @when creator is adding public key to his account + * @then executor finishes successfully + */ +TEST_F(AddSignatoryTest, ValidWhenSameAccount) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().addSignatory(creator->accountId(), kPubKey1)); + add_signatory = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_command, insertSignatory(add_signatory->pubkey())) + .WillOnce(Return(WsvCommandResult())); + EXPECT_CALL(*wsv_command, + insertAccountSignatory(add_signatory->accountId(), + add_signatory->pubkey())) + .WillOnce(Return(WsvCommandResult())); + + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given AddSignatory creator has not grantable permissions + * @when command is executed + * @then executor will be failed + */ +TEST_F(AddSignatoryTest, InvalidWhenNoPermissions) { + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, add_signatory->accountId(), can_add_my_signatory)) + .WillOnce(Return(false)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AddSignatory command with wrong account + * @when command is executed + * @then executor will be failed + */ +TEST_F(AddSignatoryTest, InvalidWhenNoAccount) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().addSignatory(kNoAcountId, kPubKey1)); + add_signatory = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, add_signatory->accountId(), can_add_my_signatory)) + .WillOnce(Return(false)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AddSignatory command with an existed public key + * @when command is executed + * @then executor will be failed + */ +TEST_F(AddSignatoryTest, InvalidWhenSameKey) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = + buildCommand(TestTransactionBuilder().addSignatory(kAccountId, kPubKey2)); + add_signatory = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, add_signatory->accountId(), can_add_my_signatory)) + .WillOnce(Return(true)); + EXPECT_CALL(*wsv_command, insertSignatory(add_signatory->pubkey())) + .WillOnce(Return(makeEmptyError())); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +class CreateAccountTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + role_permissions = {can_create_account}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().createAccount("test", kDomainId, kPubKey2)); + create_account = + getConcreteCommand(command); + + default_domain = clone(shared_model::proto::DomainBuilder() + .domainId(kDomainId) + .defaultRole(kAdminRole) + .build()); + } + std::shared_ptr default_domain; + + std::shared_ptr create_account; +}; + +/** + * @given CreateAccount with vaild parameters + * @when command is executed + * @then executor will be passed + */ +TEST_F(CreateAccountTest, ValidWhenNewAccount) { + // Valid case + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getDomain(kDomainId)) + .WillOnce(Return(default_domain)); + EXPECT_CALL(*wsv_command, insertSignatory(create_account->pubkey())) + .Times(1) + .WillOnce(Return(WsvCommandResult())); + EXPECT_CALL(*wsv_command, insertAccount(_)) + .WillOnce(Return(WsvCommandResult())); + EXPECT_CALL(*wsv_command, + insertAccountSignatory(kAccountId, create_account->pubkey())) + .WillOnce(Return(WsvCommandResult())); + EXPECT_CALL(*wsv_command, insertAccountRole(kAccountId, kAdminRole)) + .WillOnce(Return(WsvCommandResult())); + + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given CreateAccount command and creator has not roles + * @when command is executed + * @then executor will be failed + */ +TEST_F(CreateAccountTest, InvalidWhenNoPermissions) { + // Creator has no permission + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(boost::none)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given CreateAccount command + * @when command tries to create account in a non-existing domain + * @then execute fails + */ +TEST_F(CreateAccountTest, InvalidWhenNoDomain) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getDomain(kDomainId)).WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +class CreateAssetTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + role_permissions = {can_create_asset}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().createAsset("fcoin", kDomainId, 2)); + create_asset = + getConcreteCommand(command); + } + + std::shared_ptr create_asset; +}; + +/** + * @given CreateAsset with valid parameters + * @when command is executed + * @then executor will be passed + */ +TEST_F(CreateAssetTest, ValidWhenCreatorHasPermissions) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_command, insertAsset(_)) + .WillOnce(Return(WsvCommandResult())); + + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given CreateAsset and creator has not role permissions + * @when command is executed + * @then executor will be failed + */ +TEST_F(CreateAssetTest, InvalidWhenNoPermissions) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given CreateAsset + * @when command tries to create asset, but insertion fails + * @then execute() fails + */ +TEST_F(CreateAssetTest, InvalidWhenAssetInsertionFails) { + EXPECT_CALL(*wsv_command, insertAsset(_)).WillOnce(Return(makeEmptyError())); + + ASSERT_TRUE(err(execute(command))); +} + +class CreateDomainTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + role_permissions = {can_create_domain}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = + buildCommand(TestTransactionBuilder().createDomain("cn", kDomainId)); + create_domain = + getConcreteCommand(command); + } + + std::shared_ptr create_domain; +}; + +/** + * @given CreateDomain with valid parameters + * @when command is executed + * @then executor will be passed + */ +TEST_F(CreateDomainTest, ValidWhenCreatorHasPermissions) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + + EXPECT_CALL(*wsv_command, insertDomain(_)) + .WillOnce(Return(WsvCommandResult())); + + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given CreateDomain and creator has not account roles + * @when command is executed + * @then executor will be failed + */ +TEST_F(CreateDomainTest, InvalidWhenNoPermissions) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(boost::none)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given CreateDomain + * @when command tries to create domain, but insertion fails + * @then execute() fails + */ +TEST_F(CreateDomainTest, InvalidWhenDomainInsertionFails) { + EXPECT_CALL(*wsv_command, insertDomain(_)).WillOnce(Return(makeEmptyError())); + + ASSERT_TRUE(err(execute(command))); +} + +class RemoveSignatoryTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + auto creator_key = kPubKey1; + auto account_key = kPubKey2; + + account_pubkeys = {account_key}; + + many_pubkeys = {creator_key, account_key}; + + role_permissions = {can_remove_signatory}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().removeSignatory(kAccountId, kPubKey1)); + remove_signatory = + getConcreteCommand(command); + } + + std::vector account_pubkeys; + std::vector many_pubkeys; + + std::shared_ptr remove_signatory; +}; + +/** + * @given RemoveSignatory with valid parameters + * @when command is executed + * @then executor will be passed + */ +TEST_F(RemoveSignatoryTest, ValidWhenMultipleKeys) { + EXPECT_CALL( + *wsv_query, + hasAccountGrantablePermission( + kAdminId, remove_signatory->accountId(), can_remove_my_signatory)) + .WillOnce(Return(true)); + + EXPECT_CALL(*wsv_query, getAccount(remove_signatory->accountId())) + .WillOnce(Return(account)); + + EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->accountId())) + .WillOnce(Return(many_pubkeys)); + + EXPECT_CALL(*wsv_command, + deleteAccountSignatory(remove_signatory->accountId(), + remove_signatory->pubkey())) + .WillOnce(Return(WsvCommandResult())); + EXPECT_CALL(*wsv_command, deleteSignatory(remove_signatory->pubkey())) + .WillOnce(Return(WsvCommandResult())); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given RemoveSignatory with valid parameters + * @when command is executed and return single signatory pubkey + * @then executor will be failed + */ +TEST_F(RemoveSignatoryTest, InvalidWhenSingleKey) { + EXPECT_CALL( + *wsv_query, + hasAccountGrantablePermission( + kAdminId, remove_signatory->accountId(), can_remove_my_signatory)) + .WillOnce(Return(true)); + + EXPECT_CALL(*wsv_query, getAccount(remove_signatory->accountId())) + .WillOnce(Return(account)); + + EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->accountId())) + .WillOnce(Return(account_pubkeys)); + + // delete methods must not be called because the account quorum is 1. + EXPECT_CALL(*wsv_command, + deleteAccountSignatory(remove_signatory->accountId(), + remove_signatory->pubkey())) + .Times(0); + EXPECT_CALL(*wsv_command, deleteSignatory(remove_signatory->pubkey())) + .Times(0); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given RemoveSignatory and creator has not grantable permissions + * @when command is executed + * @then executor will be passed + */ +TEST_F(RemoveSignatoryTest, InvalidWhenNoPermissions) { + EXPECT_CALL( + *wsv_query, + hasAccountGrantablePermission( + kAdminId, remove_signatory->accountId(), can_remove_my_signatory)) + .WillOnce(Return(false)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given RemoveSignatory with signatory is not present in account + * @when command is executed + * @then executor will be passed + */ +TEST_F(RemoveSignatoryTest, InvalidWhenNoKey) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + std::unique_ptr wrong_key_command = + buildCommand( + TestTransactionBuilder().removeSignatory(kAccountId, kPubKey1)); + auto wrong_key_remove_signatory = + getConcreteCommand( + wrong_key_command); + + EXPECT_CALL( + *wsv_query, + hasAccountGrantablePermission(kAdminId, + wrong_key_remove_signatory->accountId(), + can_remove_my_signatory)) + .WillOnce(Return(true)); + + EXPECT_CALL(*wsv_query, getAccount(wrong_key_remove_signatory->accountId())) + .WillOnce(Return(account)); + + EXPECT_CALL(*wsv_query, + getSignatories(wrong_key_remove_signatory->accountId())) + .WillOnce(Return(account_pubkeys)); + + ASSERT_TRUE(err(validateAndExecute(wrong_key_command))); +} + +/** + * @given RemoveSignatory + * @when command tries to remove signatory from non-existing account + * @then execute fails + */ +TEST_F(RemoveSignatoryTest, InvalidWhenNoAccount) { + EXPECT_CALL( + *wsv_query, + hasAccountGrantablePermission( + kAdminId, remove_signatory->accountId(), can_remove_my_signatory)) + .WillOnce(Return(true)); + + EXPECT_CALL(*wsv_query, getAccount(remove_signatory->accountId())) + .WillOnce(Return(boost::none)); + + EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->accountId())) + .WillOnce(Return(many_pubkeys)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given RemoveSignatory + * @when command tries to remove signatory from account which does not have + * any signatories + * @then execute fails + */ +TEST_F(RemoveSignatoryTest, InvalidWhenNoSignatories) { + EXPECT_CALL( + *wsv_query, + hasAccountGrantablePermission( + kAdminId, remove_signatory->accountId(), can_remove_my_signatory)) + .WillOnce(Return(true)); + + EXPECT_CALL(*wsv_query, getAccount(remove_signatory->accountId())) + .WillOnce(Return(account)); + + EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->accountId())) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given RemoveSignatory + * @when command tries to remove signatory from non-existing account and it + * has no signatories + * @then execute fails + */ +TEST_F(RemoveSignatoryTest, InvalidWhenNoAccountAndSignatories) { + EXPECT_CALL( + *wsv_query, + hasAccountGrantablePermission( + kAdminId, remove_signatory->accountId(), can_remove_my_signatory)) + .WillOnce(Return(true)); + + EXPECT_CALL(*wsv_query, getAccount(remove_signatory->accountId())) + .WillOnce(Return(boost::none)); + + EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->accountId())) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given RemoveSignatory + * @when command tries to remove signatory from creator's account but has no + * permissions and no grantable permissions to do that + * @then execute fails + */ +TEST_F(RemoveSignatoryTest, InvalidWhenNoPermissionToRemoveFromSelf) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().removeSignatory(creator->accountId(), kPubKey1)); + auto remove_signatory = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(std::vector{})); + EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) + .WillOnce(Return(std::vector{kAdminRole})); + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, kAdminId, can_remove_my_signatory)) + .WillOnce(Return(false)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given RemoveSignatory + * @when command tries to remove signatory but deletion fails + * @then execute() fails + */ +TEST_F(RemoveSignatoryTest, InvalidWhenAccountSignatoryDeletionFails) { + EXPECT_CALL(*wsv_command, + deleteAccountSignatory(remove_signatory->accountId(), + remove_signatory->pubkey())) + .WillOnce(Return(makeEmptyError())); + + ASSERT_TRUE(err(execute(command))); +} + +class SetQuorumTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + account_pubkeys = {kPubKey1, kPubKey2}; + role_permissions = {can_set_quorum}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = + buildCommand(TestTransactionBuilder().setAccountQuorum(kAccountId, 2)); + set_quorum = + getConcreteCommand(command); + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + creator_command = buildCommand( + TestTransactionBuilder().setAccountQuorum(creator->accountId(), 2)); + creator_set_quorum = + getConcreteCommand(creator_command); + } + + std::vector account_pubkeys; + + std::shared_ptr set_quorum; + std::unique_ptr creator_command; + std::shared_ptr creator_set_quorum; +}; + +/** + * @given SetQuorum and command creator is admin + * @when command executes + * @then execute successes + */ +TEST_F(SetQuorumTest, ValidWhenCreatorHasPermissions) { + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, set_quorum->accountId(), can_set_my_quorum)) + .WillOnce(Return(true)); + EXPECT_CALL(*wsv_query, getAccount(set_quorum->accountId())) + .WillOnce(Return(account)); + EXPECT_CALL(*wsv_query, getSignatories(set_quorum->accountId())) + .WillOnce(Return(account_pubkeys)); + EXPECT_CALL(*wsv_command, updateAccount(_)) + .WillOnce(Return(WsvCommandResult())); + + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given SetQuorum and creator is the account parameter + * @when command executes + * @then execute successes + */ +TEST_F(SetQuorumTest, ValidWhenSameAccount) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getAccount(creator_set_quorum->accountId())) + .WillOnce(Return(account)); + EXPECT_CALL(*wsv_query, getSignatories(creator_set_quorum->accountId())) + .WillOnce(Return(account_pubkeys)); + EXPECT_CALL(*wsv_command, updateAccount(_)) + .WillOnce(Return(WsvCommandResult())); + + ASSERT_TRUE(val(validateAndExecute(creator_command))); +} +/** + * @given SetQuorum and creator has not grantable permissions + * @when command executes + * @then execute fails + */ +TEST_F(SetQuorumTest, InvalidWhenNoPermissions) { + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, set_quorum->accountId(), can_set_my_quorum)) + .WillOnce(Return(false)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} +/** + * @given SetQuorum and account parameter is invalid + * @when command executes + * @then execute fails + */ +TEST_F(SetQuorumTest, InvalidWhenNoAccount) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = + buildCommand(TestTransactionBuilder().setAccountQuorum(kNoAcountId, 2)); + set_quorum = getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, set_quorum->accountId(), can_set_my_quorum)) + .WillOnce(Return(false)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given SetQuorum + * @when command tries to set quorum for non-existing account + * @then execute fails + */ +TEST_F(SetQuorumTest, InvalidWhenNoAccountButPassedPermissions) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getSignatories(creator_set_quorum->accountId())) + .WillOnce(Return(account_pubkeys)); + + EXPECT_CALL(*wsv_query, getAccount(creator_set_quorum->accountId())) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(creator_command))); +} + +/** + * @given SetQuorum + * @when command tries to set quorum for account which does not have any + * signatories + * @then execute fails + */ +TEST_F(SetQuorumTest, InvalidWhenNoSignatories) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getSignatories(creator_set_quorum->accountId())) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(creator_command))); +} + +/** + * @given SetQuorum + * @when command tries to set quorum for account which does not have enough + * signatories + * @then execute fails + */ +TEST_F(SetQuorumTest, InvalidWhenNotEnoughSignatories) { + // Creator is the account + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getAccount(creator_set_quorum->accountId())).Times(0); + std::vector acc_pubkeys = { + kPubKey1}; + EXPECT_CALL(*wsv_query, getSignatories(creator_set_quorum->accountId())) + .WillOnce(Return(acc_pubkeys)); + EXPECT_CALL(*wsv_command, updateAccount(_)).Times(0); + + ASSERT_TRUE(err(validateAndExecute(creator_command))); +} + +class TransferAssetTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + src_wallet = clone(shared_model::proto::AccountAssetBuilder() + .assetId(kAssetId) + .accountId(kAdminId) + .balance(*balance) + .build()); + + dst_wallet = clone(shared_model::proto::AccountAssetBuilder() + .assetId(kAssetId) + .accountId(kAccountId) + .balance(*balance) + .build()); + + role_permissions = {can_transfer, can_receive}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().transferAsset( + kAdminId, kAccountId, kAssetId, kDescription, kAmount)); + transfer_asset = + getConcreteCommand(command); + } + + std::shared_ptr src_wallet, dst_wallet; + + std::shared_ptr transfer_asset; +}; + +/** + * @given TransferAsset and destination account has not AccountAsset + * @when command is executed and new AccountAsset will be created + * @then execute successes + */ +TEST_F(TransferAssetTest, ValidWhenNewWallet) { + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .Times(2) + .WillRepeatedly(Return(role_permissions)); + + EXPECT_CALL(*wsv_query, getAccountAsset(transfer_asset->destAccountId(), _)) + .WillOnce(Return(boost::none)); + + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->srcAccountId(), + transfer_asset->assetId())) + .Times(2) + .WillRepeatedly(Return(src_wallet)); + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .Times(2) + .WillRepeatedly(Return(asset)); + EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) + .WillOnce(Return(account)); + + EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) + .Times(2) + .WillRepeatedly(Return(WsvCommandResult())); + + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given TransferAsset + * @when command is executed + * @then execute successes + */ +TEST_F(TransferAssetTest, ValidWhenExistingWallet) { + // When there is a wallet - no new accountAsset created + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .Times(2) + .WillRepeatedly(Return(role_permissions)); + + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->destAccountId(), + transfer_asset->assetId())) + .WillOnce(Return(dst_wallet)); + + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->srcAccountId(), + transfer_asset->assetId())) + .Times(2) + .WillRepeatedly(Return(src_wallet)); + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .Times(2) + .WillRepeatedly(Return(asset)); + EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) + .WillOnce(Return(account)); + + EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) + .Times(2) + .WillRepeatedly(Return(WsvCommandResult())); + + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given TransferAsset and creator has permissions + * @when command tries to transfer + * @then execute succeses + */ +TEST_F(TransferAssetTest, ValidWhenCreatorHasPermission) { + // Transfer creator is not connected to account + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().transferAsset( + kAccountId, kAdminId, kAssetId, kDescription, kAmount)); + auto transfer_asset = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, kAccountId, can_transfer_my_assets)) + .WillOnce(Return(true)); + + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + + EXPECT_CALL(*wsv_query, getAccountAsset(transfer_asset->destAccountId(), _)) + .WillOnce(Return(boost::none)); + + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->srcAccountId(), + transfer_asset->assetId())) + .Times(2) + .WillRepeatedly(Return(src_wallet)); + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .Times(2) + .WillRepeatedly(Return(asset)); + EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) + .WillOnce(Return(account)); + + EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) + .Times(2) + .WillRepeatedly(Return(WsvCommandResult())); + + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given TransferAsset and creator has not account roles + * @when command is executed + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenNoPermissions) { + // Creator has no permissions + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given TransferAsset and destination account doesn't have any role + * @when command is executed + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenNoDestAccount) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().transferAsset( + kAdminId, kNoAcountId, kAssetId, kDescription, kAmount)); + auto transfer_asset = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(boost::none)); + + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given TransferAsset and source account doesn't have asset + * @when command is executed + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenNoSrcAccountAsset) { + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .Times(2) + .WillRepeatedly(Return(role_permissions)); + + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .WillOnce(Return(asset)); + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->srcAccountId(), + transfer_asset->assetId())) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given TransferAsset + * @when command tries to transfer asset from non-existing account + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenNoSrcAccountAssetDuringExecute) { + // No source account asset exists + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .Times(2) + .WillRepeatedly(Return(role_permissions)); + + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .WillOnce(Return(asset)); + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->srcAccountId(), + transfer_asset->assetId())) + .Times(2) + .WillOnce(Return(src_wallet)) + .WillOnce(Return(boost::none)); + EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) + .WillOnce(Return(account)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given TransferAsset + * @when command tries to transfer non-existent asset + * @then isValid fails + */ +TEST_F(TransferAssetTest, InvalidWhenNoAssetDuringValidation) { + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .Times(2) + .WillRepeatedly(Return(role_permissions)); + + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .WillOnce(Return(boost::none)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given TransferAsset + * @when command tries to transfer non-existent asset + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenNoAssetId) { + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .Times(2) + .WillRepeatedly(Return(role_permissions)); + + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .WillOnce(Return(asset)) + .WillOnce(Return(boost::none)); + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->srcAccountId(), + transfer_asset->assetId())) + .Times(2) + .WillRepeatedly(Return(src_wallet)); + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->destAccountId(), + transfer_asset->assetId())) + .WillOnce(Return(dst_wallet)); + EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) + .WillOnce(Return(account)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given TransferAsset + * @when command tries to transfer amount which is less than source balance + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenInsufficientFunds) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().transferAsset( + kAdminId, kAccountId, kAssetId, kDescription, kAmountOverflow)); + auto transfer_asset = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .Times(2) + .WillRepeatedly(Return(role_permissions)); + + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->srcAccountId(), + transfer_asset->assetId())) + .WillOnce(Return(src_wallet)); + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .WillOnce(Return(asset)); + EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) + .WillOnce(Return(account)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given TransferAsset + * @when command tries to transfer amount which is less than source balance + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenInsufficientFundsDuringExecute) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().transferAsset( + kAdminId, kAccountId, kAssetId, kDescription, kAmountOverflow)); + auto transfer_asset = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .WillOnce(Return(asset)); + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->srcAccountId(), + transfer_asset->assetId())) + .WillOnce(Return(src_wallet)); + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->destAccountId(), + transfer_asset->assetId())) + .WillOnce(Return(dst_wallet)); + + ASSERT_TRUE(err(execute(command))); +} + +/** + * @given TransferAsset + * @when command tries to transfer amount which has wrong precesion (must be 2) + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenWrongPrecision) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().transferAsset( + kAdminId, kAccountId, kAssetId, kDescription, kAmountWrongPrecision)); + auto transfer_asset = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .Times(2) + .WillRepeatedly(Return(role_permissions)); + + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .WillOnce(Return(asset)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given TransferAsset + * @when command tries to transfer amount with wrong precision + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenWrongPrecisionDuringExecute) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().transferAsset( + kAdminId, kAccountId, kAssetId, kDescription, kAmountWrongPrecision)); + auto transfer_asset = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .WillOnce(Return(asset)); + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->srcAccountId(), + transfer_asset->assetId())) + .WillOnce(Return(src_wallet)); + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->destAccountId(), + transfer_asset->assetId())) + .WillOnce(Return(dst_wallet)); + + ASSERT_TRUE(err(execute(command))); +} + +/** + * @given TransferAsset + * @when command tries to transfer asset which overflows destination balance + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenAmountOverflow) { + std::shared_ptr max_balance = clone( + shared_model::proto::AmountBuilder() + .intValue( + std::numeric_limits::max()) + .precision(2) + .build()); + + std::shared_ptr max_wallet = + clone(shared_model::proto::AccountAssetBuilder() + .assetId(src_wallet->assetId()) + .accountId(src_wallet->accountId()) + .balance(*max_balance) + .build()); + + EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) + .WillOnce(Return(asset)); + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->srcAccountId(), + transfer_asset->assetId())) + .WillOnce(Return(src_wallet)); + EXPECT_CALL(*wsv_query, + getAccountAsset(transfer_asset->destAccountId(), + transfer_asset->assetId())) + .WillOnce(Return(max_wallet)); + + ASSERT_TRUE(err(execute(command))); +} + +/** + * @given TransferAsset and creator has not grantable permissions + * @when command tries to transfer + * @then execute fails + */ +TEST_F(TransferAssetTest, InvalidWhenCreatorHasNoPermission) { + // Transfer creator is not connected to account + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().transferAsset( + kAccountId, kAdminId, kAssetId, kDescription, kAmount)); + auto transfer_asset = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, kAccountId, can_transfer_my_assets)) + .WillOnce(Return(false)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +class AddPeerTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + role_permissions = {can_add_peer}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().addPeer("iroha_node:10001", kPubKey1)); + } +}; + +/** + * @given AddPeer and all parameters are valid + * @when command tries to transfer + * @then execute succeses + */ +TEST_F(AddPeerTest, ValidCase) { + // Valid case + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_command, insertPeer(_)).WillOnce(Return(WsvCommandResult())); + + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given AddPeer and creator has not role permissions + * @when command tries to transfer + * @then execute failed + */ +TEST_F(AddPeerTest, InvalidCaseWhenNoPermissions) { + // Valid case + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(boost::none)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AddPeer + * @when command tries to insert peer but insertion fails + * @then execute failed + */ +TEST_F(AddPeerTest, InvalidCaseWhenInsertPeerFails) { + EXPECT_CALL(*wsv_command, insertPeer(_)).WillOnce(Return(makeEmptyError())); + + ASSERT_TRUE(err(execute(command))); +} + +class CreateRoleTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + std::set perm = {can_create_role}; + role_permissions = {can_create_role}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = + buildCommand(TestTransactionBuilder().createRole(kAccountId, perm)); + create_role = + getConcreteCommand(command); + } + std::shared_ptr create_role; +}; + +/** + * @given CreateRole and all parameters are valid + * @when command tries to transfer + * @then execute succeses + */ +TEST_F(CreateRoleTest, ValidCase) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillRepeatedly(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillRepeatedly(Return(role_permissions)); + EXPECT_CALL(*wsv_command, insertRole(create_role->roleName())) + .WillOnce(Return(WsvCommandResult())); + EXPECT_CALL(*wsv_command, + insertRolePermissions(create_role->roleName(), + create_role->rolePermissions())) + .WillOnce(Return(WsvCommandResult())); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given CreateRole and creator has not role permissions + * @when command tries to transfer + * @then execute is failed + */ +TEST_F(CreateRoleTest, InvalidCaseWhenNoPermissions) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillRepeatedly(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillRepeatedly(Return(boost::none)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given CreateRole with wrong permissions + * @when command tries to transfer + * @then execute is failed + */ +TEST_F(CreateRoleTest, InvalidCaseWhenRoleSuperset) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + std::set master_perms = {can_add_peer, can_append_role}; + command = buildCommand( + TestTransactionBuilder().createRole(kMasterRole, master_perms)); + + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillRepeatedly(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillRepeatedly(Return(role_permissions)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given CreateRole + * @when command tries to create new role, but insertion fails + * @then execute failed + */ +TEST_F(CreateRoleTest, InvalidCaseWhenRoleInsertionFails) { + EXPECT_CALL(*wsv_command, insertRole(create_role->roleName())) + .WillOnce(Return(makeEmptyError())); + ASSERT_TRUE(err(execute(command))); +} + +class AppendRoleTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + role_permissions = {can_append_role}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().appendRole(kAccountId, kMasterRole)); + append_role = + getConcreteCommand(command); + } + std::shared_ptr append_role; +}; + +/** + * @given CreateRole and all parameters are valid + * @when command tries to transfer + * @then execute succeses + */ +TEST_F(AppendRoleTest, ValidCase) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .Times(2) + .WillRepeatedly(Return(admin_roles)); + + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .Times(2) + .WillRepeatedly(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getRolePermissions(kMasterRole)) + .WillOnce(Return(role_permissions)); + + EXPECT_CALL( + *wsv_command, + insertAccountRole(append_role->accountId(), append_role->roleName())) + .WillOnce(Return(WsvCommandResult())); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given CreateRole and creator has not role permissions + * @when command tries to transfer + * @then execute failed + */ +TEST_F(AppendRoleTest, InvalidCaseNoPermissions) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(boost::none)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AppendRole + * @when command tries to append non-existing role + * @then execute() fails + */ +TEST_F(AppendRoleTest, InvalidCaseNoAccountRole) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .Times(2) + .WillOnce(Return(admin_roles)) + .WillOnce((Return(boost::none))); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getRolePermissions(kMasterRole)) + .WillOnce(Return(role_permissions)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AppendRole + * @when command tries to append non-existing role and creator does not have + any + * roles + * @then execute() fails + */ +TEST_F(AppendRoleTest, InvalidCaseNoAccountRoleAndNoPermission) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .Times(2) + .WillOnce(Return(admin_roles)) + .WillOnce((Return(boost::none))); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, getRolePermissions(kMasterRole)) + .WillOnce(Return(boost::none)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AppendRole + * @when command tries to append role, but creator account does not have + * necessary permission + * @then execute() fails + */ +TEST_F(AppendRoleTest, InvalidCaseRoleHasNoPermissions) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .Times(2) + .WillOnce(Return(admin_roles)) + .WillOnce((Return(admin_roles))); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .Times(2) + .WillOnce(Return(role_permissions)) + .WillOnce(Return(boost::none)); + EXPECT_CALL(*wsv_query, getRolePermissions(kMasterRole)) + .WillOnce(Return(role_permissions)); + + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given AppendRole + * @when command tries to append role, but insertion of account fails + * @then execute() fails + */ +TEST_F(AppendRoleTest, InvalidCaseInsertAccountRoleFails) { + EXPECT_CALL( + *wsv_command, + insertAccountRole(append_role->accountId(), append_role->roleName())) + .WillOnce(Return(makeEmptyError())); + ASSERT_TRUE(err(execute(command))); +} + +class DetachRoleTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + role_permissions = {can_detach_role}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().detachRole(kAccountId, kMasterRole)); + detach_role = + getConcreteCommand(command); + } + std::shared_ptr detach_role; +}; + +/** + * @given DetachRole and all parameters are valid + * @when command tries to transfer + * @then execute succeses + */ +TEST_F(DetachRoleTest, ValidCase) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL( + *wsv_command, + deleteAccountRole(detach_role->accountId(), detach_role->roleName())) + .WillOnce(Return(WsvCommandResult())); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given DetachRole and creator has not role permissions + * @when command tries to transfer + * @then execute failed + */ +TEST_F(DetachRoleTest, InvalidCase) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(boost::none)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given DetachRole + * @when deletion of account role fails + * @then execute fails() + */ +TEST_F(DetachRoleTest, InvalidCaseWhenDeleteAccountRoleFails) { + EXPECT_CALL( + *wsv_command, + deleteAccountRole(detach_role->accountId(), detach_role->roleName())) + .WillOnce(Return(makeEmptyError())); + ASSERT_TRUE(err(execute(command))); +} + +class GrantPermissionTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + expected_permission = can_add_my_signatory; + role_permissions = {can_grant + expected_permission}; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().grantPermission( + kAccountId, expected_permission)); + grant_permission = + getConcreteCommand(command); + } + std::shared_ptr grant_permission; + std::string expected_permission; +}; + +/** + * @given GrantPermission and all parameters are valid + * @when command tries to transfer + * @then execute succeses + */ +TEST_F(GrantPermissionTest, ValidCase) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_command, + insertAccountGrantablePermission(grant_permission->accountId(), + creator->accountId(), + expected_permission)) + .WillOnce(Return(WsvCommandResult())); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given GrantPermission and creator has not role permissions + * @when command tries to transfer + * @then execute failed + */ +TEST_F(GrantPermissionTest, InvalidCaseWhenNoPermissions) { + EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(boost::none)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given GrantPermission + * @when command tries to grant permission but insertion fails + * @then execute() fails + */ +TEST_F(GrantPermissionTest, InvalidCaseWhenInsertGrantablePermissionFails) { + EXPECT_CALL(*wsv_command, + insertAccountGrantablePermission(grant_permission->accountId(), + creator->accountId(), + expected_permission)) + .WillOnce(Return(makeEmptyError())); + ASSERT_TRUE(err(execute(command))); +} + +class RevokePermissionTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + expected_permission = can_add_my_signatory; + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand(TestTransactionBuilder().revokePermission( + kAccountId, expected_permission)); + revoke_permission = + getConcreteCommand(command); + } + + std::shared_ptr revoke_permission; + std::string expected_permission; +}; + +/** + * @given RevokePermission and all parameters are valid + * @when command tries to transfer + * @then execute succeses + */ +TEST_F(RevokePermissionTest, ValidCase) { + EXPECT_CALL( + *wsv_query, + hasAccountGrantablePermission( + revoke_permission->accountId(), kAdminId, expected_permission)) + .WillOnce(Return(true)); + EXPECT_CALL(*wsv_command, + deleteAccountGrantablePermission(revoke_permission->accountId(), + creator->accountId(), + expected_permission)) + .WillOnce(Return(WsvCommandResult())); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given RevokePermission and creator has not grantable permissions + * @when command tries to transfer + * @then execute failed + */ +TEST_F(RevokePermissionTest, InvalidCaseNoPermissions) { + EXPECT_CALL( + *wsv_query, + hasAccountGrantablePermission( + revoke_permission->accountId(), kAdminId, expected_permission)) + .WillOnce(Return(false)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given RevokePermission + * @when deleting permission fails + * @then execute fails + */ +TEST_F(RevokePermissionTest, InvalidCaseDeleteAccountPermissionvFails) { + EXPECT_CALL(*wsv_command, + deleteAccountGrantablePermission(revoke_permission->accountId(), + creator->accountId(), + expected_permission)) + .WillOnce(Return(makeEmptyError())); + ASSERT_TRUE(err(execute(command))); +} + +class SetAccountDetailTest : public CommandValidateExecuteTest { + public: + void SetUp() override { + CommandValidateExecuteTest::SetUp(); + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().setAccountDetail(kAdminId, kKey, kValue)); + set_aacount_detail = + getConcreteCommand(command); + + role_permissions = {can_set_quorum}; + } + + const std::string kKey = "key"; + const std::string kValue = "val"; + const std::string kNeededPermission = can_set_my_account_detail; + + std::shared_ptr set_aacount_detail; +}; + +/** + * @given SetAccountDetail and all parameters are valid + * @when creator is setting details to their account + * @then successfully execute the command + */ +TEST_F(SetAccountDetailTest, ValidWhenSetOwnAccount) { + EXPECT_CALL(*wsv_command, + setAccountKV(set_aacount_detail->accountId(), + creator->accountId(), + set_aacount_detail->key(), + set_aacount_detail->value())) + .WillOnce(Return(WsvCommandResult())); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given SetAccountDetail + * @when creator is setting details to their account + * @then execute fails + */ +TEST_F(SetAccountDetailTest, InValidWhenOtherCreator) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().setAccountDetail(kAccountId, kKey, kValue)); + set_aacount_detail = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, set_aacount_detail->accountId(), kNeededPermission)) + .WillOnce(Return(false)); + ASSERT_TRUE(err(validateAndExecute(command))); +} + +/** + * @given SetAccountDetail + * @when creator is setting details to their account + * @then successfully execute the command + */ +TEST_F(SetAccountDetailTest, ValidWhenHasPermissions) { + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder + command = buildCommand( + TestTransactionBuilder().setAccountDetail(kAccountId, kKey, kValue)); + set_aacount_detail = + getConcreteCommand(command); + + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + kAdminId, set_aacount_detail->accountId(), kNeededPermission)) + .WillOnce(Return(true)); + EXPECT_CALL(*wsv_command, + setAccountKV(set_aacount_detail->accountId(), + creator->accountId(), + set_aacount_detail->key(), + set_aacount_detail->value())) + .WillOnce(Return(WsvCommandResult())); + ASSERT_TRUE(val(validateAndExecute(command))); +} + +/** + * @given SetAccountDetail + * @when command tries to set details, but setting key-value fails + * @then execute fails + */ +TEST_F(SetAccountDetailTest, InvalidWhenSetAccountKVFails) { + EXPECT_CALL(*wsv_command, + setAccountKV(set_aacount_detail->accountId(), + creator->accountId(), + set_aacount_detail->key(), + set_aacount_detail->value())) + .WillOnce(Return(makeEmptyError())); + ASSERT_TRUE(err(execute(command))); +} diff --git a/test/module/irohad/model/CMakeLists.txt b/test/module/irohad/model/CMakeLists.txt index 5a862ecd3c..5c041f0414 100644 --- a/test/module/irohad/model/CMakeLists.txt +++ b/test/module/irohad/model/CMakeLists.txt @@ -48,13 +48,6 @@ target_link_libraries(model_operators_test model ) -addtest(command_validate_execute_test command_validate_execute_test.cpp) -target_link_libraries(command_validate_execute_test - model - command_execution - shared_model_stateless_validation - ) - addtest(json_command_converter_test converters/json_commands_test.cpp) target_link_libraries(json_command_converter_test json_model_converters diff --git a/test/module/irohad/model/command_validate_execute_test.cpp b/test/module/irohad/model/command_validate_execute_test.cpp deleted file mode 100644 index b82d35f85d..0000000000 --- a/test/module/irohad/model/command_validate_execute_test.cpp +++ /dev/null @@ -1,2016 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. - */ - -#include -#include "backend/protobuf/from_old_model.hpp" -#include "builders/default_builders.hpp" -#include "execution/command_executor.hpp" -#include "framework/result_fixture.hpp" -#include "model/commands/add_asset_quantity.hpp" -#include "model/commands/add_peer.hpp" -#include "model/commands/add_signatory.hpp" -#include "model/commands/append_role.hpp" -#include "model/commands/create_account.hpp" -#include "model/commands/create_asset.hpp" -#include "model/commands/create_domain.hpp" -#include "model/commands/create_role.hpp" -#include "model/commands/detach_role.hpp" -#include "model/commands/grant_permission.hpp" -#include "model/commands/remove_signatory.hpp" -#include "model/commands/revoke_permission.hpp" -#include "model/commands/set_account_detail.hpp" -#include "model/commands/set_quorum.hpp" -#include "model/commands/subtract_asset_quantity.hpp" -#include "model/commands/transfer_asset.hpp" -#include "validators/permissions.hpp" -#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" - -using ::testing::AllOf; -using ::testing::AtLeast; -using ::testing::Return; -using ::testing::StrictMock; -using ::testing::_; - -using namespace iroha; -using namespace iroha::ametsuchi; -using namespace iroha::model; -using namespace framework::expected; - -class CommandValidateExecuteTest : public ::testing::Test { - public: - void SetUp() override { - wsv_query = std::make_shared>(); - wsv_command = std::make_shared>(); - - executor = std::make_shared( - iroha::CommandExecutor(wsv_query, wsv_command)); - validator = std::make_shared( - iroha::CommandValidator(wsv_query)); - - shared_model::builder::AccountBuilder< - shared_model::proto::AccountBuilder, - shared_model::validation::FieldValidator>() - .accountId(admin_id) - .domainId(domain_id) - .quorum(1) - .build() - .match( - [&](expected::Value< - std::shared_ptr> &v) { - creator = v.value; - }, - [](expected::Error> &e) { - FAIL() << *e.error; - }); - - shared_model::builder::AccountBuilder< - shared_model::proto::AccountBuilder, - shared_model::validation::FieldValidator>() - .accountId(account_id) - .domainId(domain_id) - .quorum(1) - .build() - .match( - [&](expected::Value< - std::shared_ptr> &v) { - account = v.value; - }, - [](expected::Error> &e) { - FAIL() << *e.error; - }); - - default_domain = clone(shared_model::proto::DomainBuilder() - .domainId(domain_id) - .defaultRole(admin_role) - .build()); - } - - iroha::ExecutionResult validateAndExecute() { - shared_model::proto::Command new_command = - shared_model::proto::from_old(*command); - validator->setCreatorAccountId(creator->accountId()); - executor->setCreatorAccountId(creator->accountId()); - - if (boost::apply_visitor(*validator, new_command.get())) { - return boost::apply_visitor(*executor, new_command.get()); - } - return expected::makeError( - iroha::ExecutionError{"Validate", "validation of a command failed"}); - } - - iroha::ExecutionResult execute() { - shared_model::proto::Command new_command = - shared_model::proto::from_old(*command); - executor->setCreatorAccountId(creator->accountId()); - return boost::apply_visitor(*executor, new_command.get()); - } - - /// return result with empty error message - WsvCommandResult makeEmptyError() { - return WsvCommandResult(iroha::expected::makeError("")); - } - - /// Returns error from result or throws error in case result contains value - iroha::ExecutionResult::ErrorType checkErrorCase( - const iroha::ExecutionResult &result) { - return boost::get(result); - } - - Amount max_amount{ - std::numeric_limits::max(), 2}; - std::string admin_id = "admin@test", account_id = "test@test", - asset_id = "coin#test", domain_id = "test", - description = "test transfer"; - - std::string admin_role = "admin"; - - std::vector admin_roles = {admin_role}; - std::vector role_permissions; - std::shared_ptr default_domain; - - std::shared_ptr wsv_query; - std::shared_ptr wsv_command; - - std::shared_ptr creator, account; - - std::shared_ptr command; - - std::shared_ptr executor; - std::shared_ptr validator; -}; - -class AddAssetQuantityTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - - shared_model::builder::AssetBuilder< - shared_model::proto::AssetBuilder, - shared_model::validation::FieldValidator>() - .assetId(asset_id) - .domainId(domain_id) - .precision(2) - .build() - .match( - [&](expected::Value> - &v) { asset = v.value; }, - [](expected::Error> &e) { - FAIL() << *e.error; - }); - - shared_model::builder::AmountBuilder< - shared_model::proto::AmountBuilder, - shared_model::validation::FieldValidator>() - .intValue(150) - .precision(2) - .build() - .match( - [&](expected::Value< - std::shared_ptr> &v) { - balance = v.value; - }, - [](expected::Error> &e) { - FAIL() << *e.error; - }); - - shared_model::builder::AccountAssetBuilder< - shared_model::proto::AccountAssetBuilder, - shared_model::validation::FieldValidator>() - .assetId(asset_id) - .accountId(account_id) - .balance(*balance) - .build() - .match( - [&](expected::Value< - std::shared_ptr> &v) { - wallet = v.value; - }, - [](expected::Error> &e) { - FAIL() << *e.error; - }); - - add_asset_quantity = std::make_shared(); - add_asset_quantity->account_id = creator->accountId(); - Amount amount(350, 2); - add_asset_quantity->amount = amount; - add_asset_quantity->asset_id = asset_id; - - command = add_asset_quantity; - role_permissions = {can_add_asset_qty}; - } - - std::shared_ptr balance; - std::shared_ptr asset; - std::shared_ptr wallet; - - std::shared_ptr add_asset_quantity; -}; - -TEST_F(AddAssetQuantityTest, ValidWhenNewWallet) { - // Add asset first time - no wallet - // When there is no wallet - new accountAsset will be created - EXPECT_CALL(*wsv_query, getAccountAsset(add_asset_quantity->account_id, _)) - .WillOnce(Return(boost::none)); - - EXPECT_CALL(*wsv_query, getAsset(add_asset_quantity->asset_id)) - .WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->account_id)) - .WillOnce(Return(account)); - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(AddAssetQuantityTest, ValidWhenExistingWallet) { - // There is already asset- there is a wallet - // When there is a wallet - no new accountAsset created - EXPECT_CALL(*wsv_query, - getAccountAsset(add_asset_quantity->account_id, - add_asset_quantity->asset_id)) - .WillOnce(Return(wallet)); - - EXPECT_CALL(*wsv_query, getAsset(asset_id)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->account_id)) - .WillOnce(Return(account)); - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(AddAssetQuantityTest, InvalidWhenNoRoles) { - // Creator has no roles - EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->account_id)) - .WillOnce(Return(boost::none)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(AddAssetQuantityTest, InvalidWhenZeroAmount) { - // Amount is zero - Amount amount(0); - add_asset_quantity->amount = amount; - EXPECT_CALL(*wsv_query, getAsset(asset->assetId())).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(AddAssetQuantityTest, InvalidWhenWrongPrecision) { - // Amount is with wrong precision (must be 2) - Amount amount(add_asset_quantity->amount.getIntValue(), 30); - add_asset_quantity->amount = amount; - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(asset_id)).WillOnce(Return(asset)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given AddAssetQuantity - * @when command references non-existing account - * @then execute fails and returns false - */ -TEST_F(AddAssetQuantityTest, InvalidWhenNoAccount) { - // Account to add does not exist - EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(asset_id)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->account_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(AddAssetQuantityTest, InvalidWhenNoAsset) { - // Asset doesn't exist - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - add_asset_quantity->asset_id = "noass"; - - EXPECT_CALL(*wsv_query, getAsset(add_asset_quantity->asset_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given AddAssetQuantity - * @when command adds value which overflows account balance - * @then execute fails and returns false - */ -TEST_F(AddAssetQuantityTest, InvalidWhenAssetAdditionFails) { - // amount overflows - add_asset_quantity->amount = max_amount; - - EXPECT_CALL(*wsv_query, - getAccountAsset(add_asset_quantity->account_id, - add_asset_quantity->asset_id)) - .WillOnce(Return(wallet)); - - EXPECT_CALL(*wsv_query, getAsset(asset_id)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->account_id)) - .WillOnce(Return(account)); - EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -class SubtractAssetQuantityTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - - shared_model::builder::AmountBuilder< - shared_model::proto::AmountBuilder, - shared_model::validation::FieldValidator>() - .intValue(150ul) - .precision(2) - .build() - .match( - [&](expected::Value< - std::shared_ptr> &v) { - balance = v.value; - }, - [](expected::Error> &e) { - FAIL() << *e.error; - }); - ; - - shared_model::builder::AssetBuilder< - shared_model::proto::AssetBuilder, - shared_model::validation::FieldValidator>() - .assetId(asset_id) - .domainId(domain_id) - .precision(2) - .build() - .match( - [&](expected::Value> - &v) { asset = v.value; }, - [](expected::Error> &e) { - FAIL() << *e.error; - }); - - shared_model::builder::AccountAssetBuilder< - shared_model::proto::AccountAssetBuilder, - shared_model::validation::FieldValidator>() - .assetId(asset_id) - .accountId(account_id) - .balance(*balance) - .build() - .match( - [&](expected::Value< - std::shared_ptr> &v) { - wallet = v.value; - }, - [](expected::Error> &e) { - FAIL() << *e.error; - }); - - subtract_asset_quantity = std::make_shared(); - subtract_asset_quantity->account_id = creator->accountId(); - Amount amount(100, 2); - subtract_asset_quantity->amount = amount; - subtract_asset_quantity->asset_id = asset_id; - - command = subtract_asset_quantity; - role_permissions = {can_subtract_asset_qty}; - } - - std::shared_ptr balance; - std::shared_ptr asset; - std::shared_ptr wallet; - - std::shared_ptr subtract_asset_quantity; -}; - -/** - * @given SubtractAssetQuantity - * @when account doesn't have wallet of target asset - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenNoWallet) { - // Subtract asset - no wallet - // When there is no wallet - Failed - EXPECT_CALL(*wsv_query, - getAccountAsset(subtract_asset_quantity->account_id, - subtract_asset_quantity->asset_id)) - .WillOnce(Return(boost::none)); - - EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(asset_id)).WillOnce(Return(asset)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given SubtractAssetQuantity - * @when correct arguments - * @then executor will be passed - */ -TEST_F(SubtractAssetQuantityTest, ValidWhenExistingWallet) { - // There is already asset- there is a wallet - // When there is a wallet - no new accountAsset created - EXPECT_CALL(*wsv_query, - getAccountAsset(subtract_asset_quantity->account_id, - subtract_asset_quantity->asset_id)) - .WillOnce(Return(wallet)); - - EXPECT_CALL(*wsv_query, getAsset(asset_id)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -/** - * @given SubtractAssetQuantity - * @when arguments amount is greater than wallet's amount - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenOverAmount) { - Amount amount(1204, 2); - subtract_asset_quantity->amount = amount; - EXPECT_CALL(*wsv_query, - getAccountAsset(subtract_asset_quantity->account_id, - subtract_asset_quantity->asset_id)) - .WillOnce(Return(wallet)); - - EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(asset_id)).WillOnce(Return(asset)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given SubtractAssetQuantity - * @when account doesn't have role - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenNoRoles) { - // Creator has no roles - EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->account_id)) - .WillOnce(Return(boost::none)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given SubtractAssetQuantity - * @when arguments amount is zero - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenZeroAmount) { - // Amount is zero - Amount amount(0); - subtract_asset_quantity->amount = amount; - EXPECT_CALL(*wsv_query, getAsset(asset->assetId())).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given SubtractAssetQuantity - * @when arguments amount precision is invalid - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenWrongPrecision) { - // Amount is with wrong precision (must be 2) - Amount amount(subtract_asset_quantity->amount.getIntValue(), 30); - subtract_asset_quantity->amount = amount; - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(asset_id)).WillOnce(Return(asset)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given SubtractAssetQuantity - * @when account doesn't exist - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenNoAccount) { - // Account to subtract doesn't exist - subtract_asset_quantity->account_id = "noacc"; - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given SubtractAssetQuantity - * @when asset doesn't exist - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenNoAsset) { - // Asset doesn't exist - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - subtract_asset_quantity->asset_id = "noass"; - - EXPECT_CALL(*wsv_query, getAsset(subtract_asset_quantity->asset_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -class AddSignatoryTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - - add_signatory = std::make_shared(); - add_signatory->account_id = account_id; - add_signatory->pubkey.fill(1); // Such Pubkey exist - role_permissions = {can_add_signatory}; - command = add_signatory; - } - - std::shared_ptr add_signatory; -}; - -TEST_F(AddSignatoryTest, ValidWhenCreatorHasPermissions) { - // Creator has role permissions to add signatory - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, add_signatory->account_id, can_add_signatory)) - .WillOnce(Return(true)); - EXPECT_CALL( - *wsv_command, - insertSignatory(shared_model::crypto::PublicKey( - {add_signatory->pubkey.begin(), add_signatory->pubkey.end()}))) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_command, - insertAccountSignatory(add_signatory->account_id, - shared_model::crypto::PublicKey( - {add_signatory->pubkey.begin(), - add_signatory->pubkey.end()}))) - .WillOnce(Return(WsvCommandResult())); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(AddSignatoryTest, ValidWhenSameAccount) { - // When creator is adding public keys to his account - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - add_signatory->account_id = creator->accountId(); - - EXPECT_CALL( - *wsv_command, - insertSignatory(shared_model::crypto::PublicKey( - {add_signatory->pubkey.begin(), add_signatory->pubkey.end()}))) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_command, - insertAccountSignatory(add_signatory->account_id, - shared_model::crypto::PublicKey( - {add_signatory->pubkey.begin(), - add_signatory->pubkey.end()}))) - .WillOnce(Return(WsvCommandResult())); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(AddSignatoryTest, InvalidWhenNoPermissions) { - // Creator has no permission - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, add_signatory->account_id, can_add_signatory)) - .WillOnce(Return(false)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(AddSignatoryTest, InvalidWhenNoAccount) { - // Add to nonexistent account - add_signatory->account_id = "noacc"; - - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, add_signatory->account_id, can_add_signatory)) - .WillOnce(Return(false)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(AddSignatoryTest, InvalidWhenSameKey) { - // Add same signatory - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, add_signatory->account_id, can_add_signatory)) - .WillOnce(Return(true)); - add_signatory->pubkey.fill(2); - EXPECT_CALL( - *wsv_command, - insertSignatory(shared_model::crypto::PublicKey( - {add_signatory->pubkey.begin(), add_signatory->pubkey.end()}))) - .WillOnce(Return(makeEmptyError())); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -class CreateAccountTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - - create_account = std::make_shared(); - create_account->account_name = "test"; - create_account->domain_id = domain_id; - create_account->pubkey.fill(2); - - command = create_account; - role_permissions = {can_create_account}; - } - - std::shared_ptr create_account; -}; - -TEST_F(CreateAccountTest, ValidWhenNewAccount) { - // Valid case - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getDomain(domain_id)) - .WillOnce(Return(default_domain)); - - EXPECT_CALL( - *wsv_command, - insertSignatory(shared_model::crypto::PublicKey( - {create_account->pubkey.begin(), create_account->pubkey.end()}))) - .Times(1) - .WillOnce(Return(WsvCommandResult())); - - EXPECT_CALL(*wsv_command, insertAccount(_)) - .WillOnce(Return(WsvCommandResult())); - - EXPECT_CALL(*wsv_command, - insertAccountSignatory(account_id, - shared_model::crypto::PublicKey( - {create_account->pubkey.begin(), - create_account->pubkey.end()}))) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_command, insertAccountRole(account_id, admin_role)) - .WillOnce(Return(WsvCommandResult())); - - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(CreateAccountTest, InvalidWhenNoPermissions) { - // Creator has no permission - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(boost::none)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(CreateAccountTest, InvalidWhenLongName) { - // Not valid name for account - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - create_account->account_name = - "aAccountNameMustBeLessThan64characters00000000000000000000000000"; - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(CreateAccountTest, InvalidWhenNameWithSystemSymbols) { - // Not valid name for account (system symbols) - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - create_account->account_name = "test@"; - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given CreateAccountCommand - * @when command tries to create account in a non-existing domain - * @then execute fails and returns false - */ -TEST_F(CreateAccountTest, InvalidWhenNoDomain) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getDomain(domain_id)).WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -class CreateAssetTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - - create_asset = std::make_shared(); - create_asset->asset_name = "fcoin"; - create_asset->domain_id = domain_id; - create_asset->precision = 2; - - command = create_asset; - role_permissions = {can_create_asset}; - } - - std::shared_ptr create_asset; -}; - -TEST_F(CreateAssetTest, ValidWhenCreatorHasPermissions) { - // Creator is money creator - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - - EXPECT_CALL(*wsv_command, insertAsset(_)) - .WillOnce(Return(WsvCommandResult())); - - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(CreateAssetTest, InvalidWhenNoPermissions) { - // Creator has no permissions - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given CreateAsset - * @when command tries to create asset, but insertion fails - * @then execute() fails - */ -TEST_F(CreateAssetTest, InvalidWhenAssetInsertionFails) { - EXPECT_CALL(*wsv_command, insertAsset(_)).WillOnce(Return(makeEmptyError())); - - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -class CreateDomainTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - - create_domain = std::make_shared(); - create_domain->domain_id = "cn"; - create_domain->user_default_role = "default"; - - command = create_domain; - role_permissions = {can_create_domain}; - } - - std::shared_ptr create_domain; -}; - -TEST_F(CreateDomainTest, ValidWhenCreatorHasPermissions) { - // Valid case - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - - EXPECT_CALL(*wsv_command, insertDomain(_)) - .WillOnce(Return(WsvCommandResult())); - - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(CreateDomainTest, InvalidWhenNoPermissions) { - // Creator has no permissions - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(boost::none)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given CreateDomain - * @when command tries to create domain, but insertion fails - * @then execute() fails - */ -TEST_F(CreateDomainTest, InvalidWhenDomainInsertionFails) { - EXPECT_CALL(*wsv_command, insertDomain(_)).WillOnce(Return(makeEmptyError())); - - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -class RemoveSignatoryTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - - pubkey_t creator_key, account_key; - creator_key.fill(0x1); - account_key.fill(0x2); - - account_pubkeys = { - shared_model::interface::types::PubkeyType(account_key.to_string())}; - many_pubkeys = { - shared_model::interface::types::PubkeyType(creator_key.to_string()), - shared_model::interface::types::PubkeyType(account_key.to_string())}; - - remove_signatory = std::make_shared(); - remove_signatory->account_id = account_id; - remove_signatory->pubkey.fill(1); - - command = remove_signatory; - role_permissions = {can_remove_signatory}; - } - - std::vector account_pubkeys; - std::vector many_pubkeys; - std::shared_ptr remove_signatory; -}; - -TEST_F(RemoveSignatoryTest, ValidWhenMultipleKeys) { - // Creator is admin - // Add same signatory - - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, remove_signatory->account_id, can_remove_signatory)) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getAccount(remove_signatory->account_id)) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->account_id)) - .WillOnce(Return(many_pubkeys)); - - EXPECT_CALL(*wsv_command, - deleteAccountSignatory(remove_signatory->account_id, - shared_model::crypto::PublicKey( - {remove_signatory->pubkey.begin(), - remove_signatory->pubkey.end()}))) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL( - *wsv_command, - deleteSignatory(shared_model::crypto::PublicKey( - {remove_signatory->pubkey.begin(), remove_signatory->pubkey.end()}))) - .WillOnce(Return(WsvCommandResult())); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(RemoveSignatoryTest, InvalidWhenSingleKey) { - // Creator is admin - - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, remove_signatory->account_id, can_remove_signatory)) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getAccount(remove_signatory->account_id)) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->account_id)) - .WillOnce(Return(account_pubkeys)); - - // delete methods must not be called because the account quorum is 1. - EXPECT_CALL(*wsv_command, - deleteAccountSignatory(remove_signatory->account_id, - shared_model::crypto::PublicKey( - {remove_signatory->pubkey.begin(), - remove_signatory->pubkey.end()}))) - .Times(0); - EXPECT_CALL( - *wsv_command, - deleteSignatory(shared_model::crypto::PublicKey( - {remove_signatory->pubkey.begin(), remove_signatory->pubkey.end()}))) - .Times(0); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(RemoveSignatoryTest, InvalidWhenNoPermissions) { - // Creator has no permissions - // Add same signatory - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, remove_signatory->account_id, can_remove_signatory)) - .WillOnce(Return(false)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(RemoveSignatoryTest, InvalidWhenNoKey) { - // Remove signatory not present in account - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, remove_signatory->account_id, can_remove_signatory)) - .WillOnce(Return(true)); - remove_signatory->pubkey.fill(0xF); - - EXPECT_CALL(*wsv_query, getAccount(remove_signatory->account_id)) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->account_id)) - - .WillOnce(Return(account_pubkeys)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given RemoveSignatory - * @when command tries to remove signatory from non-existing account - * @then execute fails and returns false - */ -TEST_F(RemoveSignatoryTest, InvalidWhenNoAccount) { - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, remove_signatory->account_id, can_remove_signatory)) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getAccount(remove_signatory->account_id)) - .WillOnce(Return(boost::none)); - - EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->account_id)) - .WillOnce(Return(many_pubkeys)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given RemoveSignatory - * @when command tries to remove signatory from account which does not have any - * signatories - * @then execute fails and returns false - */ -TEST_F(RemoveSignatoryTest, InvalidWhenNoSignatories) { - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, remove_signatory->account_id, can_remove_signatory)) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getAccount(remove_signatory->account_id)) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->account_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given RemoveSignatory - * @when command tries to remove signatory from non-existing account and it has - * no signatories - * @then execute fails and returns false - */ -TEST_F(RemoveSignatoryTest, InvalidWhenNoAccountAndSignatories) { - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, remove_signatory->account_id, can_remove_signatory)) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getAccount(remove_signatory->account_id)) - .WillOnce(Return(boost::none)); - - EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->account_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given RemoveSignatory - * @when command tries to remove signatory from creator's account but has no - * permissions and no grantable permissions to do that - * @then execute fails and returns false - */ -TEST_F(RemoveSignatoryTest, InvalidWhenNoPermissionToRemoveFromSelf) { - remove_signatory->account_id = creator->accountId(); - - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(std::vector{})); - EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) - .WillOnce(Return(std::vector{admin_role})); - EXPECT_CALL( - *wsv_query, - hasAccountGrantablePermission(admin_id, admin_id, can_remove_signatory)) - .WillOnce(Return(false)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given RemoveSignatory - * @when command tries to remove signatory but deletion fails - * @then execute() fails - */ -TEST_F(RemoveSignatoryTest, InvalidWhenAccountSignatoryDeletionFails) { - EXPECT_CALL(*wsv_command, - deleteAccountSignatory(remove_signatory->account_id, - shared_model::crypto::PublicKey( - {remove_signatory->pubkey.begin(), - remove_signatory->pubkey.end()}))) - .WillOnce(Return(makeEmptyError())); - - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -class SetQuorumTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - - pubkey_t creator_key, account_key; - creator_key.fill(0x1); - account_key.fill(0x2); - account_pubkeys = { - shared_model::interface::types::PubkeyType(creator_key.to_string()), - shared_model::interface::types::PubkeyType(account_key.to_string())}; - set_quorum = std::make_shared(); - set_quorum->account_id = account_id; - set_quorum->new_quorum = 2; - - command = set_quorum; - role_permissions = {can_set_quorum}; - } - - std::vector account_pubkeys; - std::shared_ptr set_quorum; -}; - -TEST_F(SetQuorumTest, ValidWhenCreatorHasPermissions) { - // Creator is admin - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, set_quorum->account_id, can_set_quorum)) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getAccount(set_quorum->account_id)) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_query, getSignatories(set_quorum->account_id)) - .WillOnce(Return(account_pubkeys)); - - EXPECT_CALL(*wsv_command, updateAccount(_)) - .WillOnce(Return(WsvCommandResult())); - - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(SetQuorumTest, ValidWhenSameAccount) { - // Creator is the account - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - set_quorum->account_id = creator->accountId(); - EXPECT_CALL(*wsv_query, getAccount(set_quorum->account_id)) - .WillOnce(Return(account)); - EXPECT_CALL(*wsv_query, getSignatories(set_quorum->account_id)) - .WillOnce(Return(account_pubkeys)); - EXPECT_CALL(*wsv_command, updateAccount(_)) - .WillOnce(Return(WsvCommandResult())); - - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(SetQuorumTest, InvalidWhenNoPermissions) { - // Creator has no permissions - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, set_quorum->account_id, can_set_quorum)) - .WillOnce(Return(false)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(SetQuorumTest, InvalidWhenNoAccount) { - // No such account exists - set_quorum->account_id = "noacc"; - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, set_quorum->account_id, can_set_quorum)) - .WillOnce(Return(false)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given SetQuorum - * @when command tries to set quorum for non-existing account - * @then execute fails and returns false - */ -TEST_F(SetQuorumTest, InvalidWhenNoAccountButPassedPermissions) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - set_quorum->account_id = creator->accountId(); - EXPECT_CALL(*wsv_query, getSignatories(set_quorum->account_id)) - .WillOnce(Return(account_pubkeys)); - - EXPECT_CALL(*wsv_query, getAccount(set_quorum->account_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given SetQuorum - * @when command tries to set quorum for account which does not have any - * signatories - * @then execute fails and returns false - */ -TEST_F(SetQuorumTest, InvalidWhenNoSignatories) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - set_quorum->account_id = creator->accountId(); - EXPECT_CALL(*wsv_query, getSignatories(set_quorum->account_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(SetQuorumTest, InvalidWhenNotEnoughSignatories) { - // Creator is the account - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - set_quorum->account_id = creator->accountId(); - EXPECT_CALL(*wsv_query, getAccount(set_quorum->account_id)).Times(0); - pubkey_t key; - key.fill(0x1); - std::vector acc_pubkeys = { - shared_model::interface::types::PubkeyType(key.to_string())}; - EXPECT_CALL(*wsv_query, getSignatories(set_quorum->account_id)) - .WillOnce(Return(acc_pubkeys)); - EXPECT_CALL(*wsv_command, updateAccount(_)).Times(0); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -class TransferAssetTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - - asset = clone(shared_model::proto::AssetBuilder() - .assetId(asset_id) - .domainId(domain_id) - .precision(2) - .build()); - - balance = clone(shared_model::proto::AmountBuilder() - .intValue(150) - .precision(2) - .build()); - - src_wallet = clone(shared_model::proto::AccountAssetBuilder() - .assetId(asset_id) - .accountId(admin_id) - .balance(*balance) - .build()); - - dst_wallet = clone(shared_model::proto::AccountAssetBuilder() - .assetId(asset_id) - .accountId(account_id) - .balance(*balance) - .build()); - - transfer_asset = std::make_shared(); - transfer_asset->src_account_id = admin_id; - transfer_asset->dest_account_id = account_id; - transfer_asset->asset_id = asset_id; - transfer_asset->description = description; - Amount amount(150, 2); - transfer_asset->amount = amount; - command = transfer_asset; - role_permissions = {can_transfer, can_receive}; - } - - std::shared_ptr balance; - std::shared_ptr asset; - std::shared_ptr src_wallet, dst_wallet; - - std::shared_ptr transfer_asset; -}; - -TEST_F(TransferAssetTest, ValidWhenNewWallet) { - // When there is no wallet - new accountAsset will be created - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->dest_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->src_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAccountAsset(transfer_asset->dest_account_id, _)) - .WillOnce(Return(boost::none)); - - EXPECT_CALL( - *wsv_query, - getAccountAsset(transfer_asset->src_account_id, transfer_asset->asset_id)) - .Times(2) - .WillRepeatedly(Return(src_wallet)); - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .Times(2) - .WillRepeatedly(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->dest_account_id)) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .Times(2) - .WillRepeatedly(Return(WsvCommandResult())); - - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(TransferAssetTest, ValidWhenExistingWallet) { - // When there is a wallet - no new accountAsset created - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->dest_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->src_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->dest_account_id, - transfer_asset->asset_id)) - .WillOnce(Return(dst_wallet)); - - EXPECT_CALL( - *wsv_query, - getAccountAsset(transfer_asset->src_account_id, transfer_asset->asset_id)) - .Times(2) - .WillRepeatedly(Return(src_wallet)); - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .Times(2) - .WillRepeatedly(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->dest_account_id)) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .Times(2) - .WillRepeatedly(Return(WsvCommandResult())); - - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(TransferAssetTest, InvalidWhenNoPermissions) { - // Creator has no permissions - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->src_account_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(TransferAssetTest, InvalidWhenNoDestAccount) { - // No destination account exists - transfer_asset->dest_account_id = "noacc"; - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->dest_account_id)) - .WillOnce(Return(boost::none)); - - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->src_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(TransferAssetTest, InvalidWhenNoSrcAccountAsset) { - // No source account asset exists - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->dest_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->src_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .WillOnce(Return(asset)); - EXPECT_CALL( - *wsv_query, - getAccountAsset(transfer_asset->src_account_id, transfer_asset->asset_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given TransferAsset - * @when command tries to transfer asset from non-existing account - * @then execute fails and returns false - */ -TEST_F(TransferAssetTest, InvalidWhenNoSrcAccountAssetDuringExecute) { - // No source account asset exists - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->dest_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->src_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .WillOnce(Return(asset)); - EXPECT_CALL( - *wsv_query, - getAccountAsset(transfer_asset->src_account_id, transfer_asset->asset_id)) - .Times(2) - .WillOnce(Return(src_wallet)) - .WillOnce(Return(boost::none)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->dest_account_id)) - .WillOnce(Return(account)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given TransferAsset - * @when command tries to transfer non-existent asset - * @then isValid fails and returns false - */ -TEST_F(TransferAssetTest, InvalidWhenNoAssetDuringValidation) { - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->dest_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->src_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given TransferAsset - * @when command tries to transfer non-existent asset - * @then execute fails and returns false - */ -TEST_F(TransferAssetTest, InvalidWhenNoAssetId) { - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->dest_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->src_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .WillOnce(Return(asset)) - .WillOnce(Return(boost::none)); - EXPECT_CALL( - *wsv_query, - getAccountAsset(transfer_asset->src_account_id, transfer_asset->asset_id)) - .Times(2) - .WillRepeatedly(Return(src_wallet)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->dest_account_id, - transfer_asset->asset_id)) - .WillOnce(Return(dst_wallet)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->dest_account_id)) - .WillOnce(Return(account)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(TransferAssetTest, InvalidWhenInsufficientFunds) { - // No sufficient funds - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->dest_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->src_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - Amount amount(155, 2); - - EXPECT_CALL( - *wsv_query, - getAccountAsset(transfer_asset->src_account_id, transfer_asset->asset_id)) - .WillOnce(Return(src_wallet)); - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->dest_account_id)) - .WillOnce(Return(boost::none)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given TransferAsset - * @when command tries to transfer amount which is less than source balance - * @then execute fails and returns false - */ -TEST_F(TransferAssetTest, InvalidWhenInsufficientFundsDuringExecute) { - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .WillOnce(Return(asset)); - EXPECT_CALL( - *wsv_query, - getAccountAsset(transfer_asset->src_account_id, transfer_asset->asset_id)) - .WillOnce(Return(src_wallet)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->dest_account_id, - transfer_asset->asset_id)) - .WillOnce(Return(dst_wallet)); - - // More than account balance - transfer_asset->amount = Amount(155, 2); - - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -TEST_F(TransferAssetTest, InvalidWhenWrongPrecision) { - // Amount has wrong precision - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->dest_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->src_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - Amount amount(transfer_asset->amount.getIntValue(), 30); - transfer_asset->amount = amount; - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .WillOnce(Return(asset)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given TransferAsset - * @when command tries to transfer amount with wrong precision - * @then execute fails and returns false - */ -TEST_F(TransferAssetTest, InvalidWhenWrongPrecisionDuringExecute) { - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .WillOnce(Return(asset)); - EXPECT_CALL( - *wsv_query, - getAccountAsset(transfer_asset->src_account_id, transfer_asset->asset_id)) - .WillOnce(Return(src_wallet)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->dest_account_id, - transfer_asset->asset_id)) - .WillOnce(Return(dst_wallet)); - - Amount amount(transfer_asset->amount.getIntValue(), 30); - transfer_asset->amount = amount; - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -/** - * @given TransferAsset - * @when command tries to transfer asset which overflows destination balance - * @then execute fails and returns false - */ -TEST_F(TransferAssetTest, InvalidWhenAmountOverflow) { - std::shared_ptr max_balance = clone( - shared_model::proto::AmountBuilder() - .intValue( - std::numeric_limits::max()) - .precision(2) - .build()); - - src_wallet = clone(shared_model::proto::AccountAssetBuilder() - .assetId(src_wallet->assetId()) - .accountId(src_wallet->accountId()) - .balance(*max_balance) - .build()); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .WillOnce(Return(asset)); - EXPECT_CALL( - *wsv_query, - getAccountAsset(transfer_asset->src_account_id, transfer_asset->asset_id)) - .WillOnce(Return(src_wallet)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->dest_account_id, - transfer_asset->asset_id)) - .WillOnce(Return(dst_wallet)); - - // More than account balance - transfer_asset->amount = (max_amount - Amount(100, 2)).value(); - - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -TEST_F(TransferAssetTest, InvalidWhenCreatorHasNoPermission) { - // Transfer creator is not connected to account - transfer_asset->src_account_id = account_id; - transfer_asset->dest_account_id = admin_id; - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission(admin_id, account_id, can_transfer)) - .WillOnce(Return(false)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(TransferAssetTest, ValidWhenCreatorHasPermission) { - // Transfer creator is not connected to account - transfer_asset->src_account_id = account_id; - transfer_asset->dest_account_id = admin_id; - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission(admin_id, account_id, can_transfer)) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->dest_account_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAccountAsset(transfer_asset->dest_account_id, _)) - .WillOnce(Return(boost::none)); - - EXPECT_CALL( - *wsv_query, - getAccountAsset(transfer_asset->src_account_id, transfer_asset->asset_id)) - .Times(2) - .WillRepeatedly(Return(src_wallet)); - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->asset_id)) - .Times(2) - .WillRepeatedly(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->dest_account_id)) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .Times(2) - .WillRepeatedly(Return(WsvCommandResult())); - - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -class AddPeerTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - - add_peer = std::make_shared(); - add_peer->peer.address = "iroha_node:10001"; - add_peer->peer.pubkey.fill(4); - - command = add_peer; - role_permissions = {can_add_peer}; - } - - std::shared_ptr add_peer; -}; - -TEST_F(AddPeerTest, ValidCase) { - // Valid case - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_command, insertPeer(_)).WillOnce(Return(WsvCommandResult())); - - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(AddPeerTest, InvalidCaseWhenNoPermissions) { - // Valid case - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(boost::none)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given AddPeer - * @when command tries to insert peer but insertion fails - * @then execute returns false - */ -TEST_F(AddPeerTest, InvalidCaseWhenInsertPeerFails) { - EXPECT_CALL(*wsv_command, insertPeer(_)).WillOnce(Return(makeEmptyError())); - - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -class CreateRoleTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - std::set perm = {can_create_role}; - create_role = std::make_shared("master", perm); - command = create_role; - role_permissions = {can_create_role}; - } - std::shared_ptr create_role; -}; - -TEST_F(CreateRoleTest, ValidCase) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillRepeatedly(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillRepeatedly(Return(role_permissions)); - EXPECT_CALL(*wsv_command, insertRole(create_role->role_name)) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL( - *wsv_command, - insertRolePermissions(create_role->role_name, create_role->permissions)) - .WillOnce(Return(WsvCommandResult())); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(CreateRoleTest, InvalidCaseWhenNoPermissions) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillRepeatedly(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillRepeatedly(Return(boost::none)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -TEST_F(CreateRoleTest, InvalidCaseWhenRoleSuperset) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillRepeatedly(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillRepeatedly(Return(role_permissions)); - std::set perms = {can_add_peer, can_append_role}; - command = std::make_shared("master", perms); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given CreateRole - * @when command tries to create new role, but insertion fails - * @then execute returns false - */ -TEST_F(CreateRoleTest, InvalidCaseWhenRoleInsertionFails) { - EXPECT_CALL(*wsv_command, insertRole(create_role->role_name)) - .WillOnce(Return(makeEmptyError())); - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -class AppendRoleTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - exact_command = std::make_shared("yoda", "master"); - command = exact_command; - role_permissions = {can_append_role}; - } - std::shared_ptr exact_command; -}; - -TEST_F(AppendRoleTest, ValidCase) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .Times(2) - .WillRepeatedly(Return(admin_roles)); - - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getRolePermissions("master")) - .WillOnce(Return(role_permissions)); - - EXPECT_CALL( - *wsv_command, - insertAccountRole(exact_command->account_id, exact_command->role_name)) - .WillOnce(Return(WsvCommandResult())); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(AppendRoleTest, InvalidCaseNoPermissions) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(boost::none)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given AppendRole - * @when command tries to append non-existing role - * @then execute() fails and returns false - */ -TEST_F(AppendRoleTest, InvalidCaseNoAccountRole) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .Times(2) - .WillOnce(Return(admin_roles)) - .WillOnce((Return(boost::none))); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getRolePermissions("master")) - .WillOnce(Return(role_permissions)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given AppendRole - * @when command tries to append non-existing role and creator does not have any - * roles - * @then execute() fails and returns false - */ -TEST_F(AppendRoleTest, InvalidCaseNoAccountRoleAndNoPermission) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .Times(2) - .WillOnce(Return(admin_roles)) - .WillOnce((Return(boost::none))); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getRolePermissions("master")) - .WillOnce(Return(boost::none)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given AppendRole - * @when command tries to append role, but creator account does not have - * necessary permission - * @then execute() fails and returns false - */ -TEST_F(AppendRoleTest, InvalidCaseRoleHasNoPermissions) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .Times(2) - .WillOnce(Return(admin_roles)) - .WillOnce((Return(admin_roles))); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .Times(2) - .WillOnce(Return(role_permissions)) - .WillOnce(Return(boost::none)); - EXPECT_CALL(*wsv_query, getRolePermissions("master")) - .WillOnce(Return(role_permissions)); - - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given AppendRole - * @when command tries to append role, but insertion of account fails - * @then execute() fails - */ -TEST_F(AppendRoleTest, InvalidCaseInsertAccountRoleFails) { - EXPECT_CALL( - *wsv_command, - insertAccountRole(exact_command->account_id, exact_command->role_name)) - .WillOnce(Return(makeEmptyError())); - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -class DetachRoleTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - exact_command = std::make_shared("yoda", "master"); - command = exact_command; - role_permissions = {can_detach_role}; - } - std::shared_ptr exact_command; -}; - -TEST_F(DetachRoleTest, ValidCase) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL( - *wsv_command, - deleteAccountRole(exact_command->account_id, exact_command->role_name)) - .WillOnce(Return(WsvCommandResult())); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(DetachRoleTest, InvalidCase) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(boost::none)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given DetachRole - * @when deletion of account role fails - * @then execute fails() - */ -TEST_F(DetachRoleTest, InvalidCaseWhenDeleteAccountRoleFails) { - EXPECT_CALL( - *wsv_command, - deleteAccountRole(exact_command->account_id, exact_command->role_name)) - .WillOnce(Return(makeEmptyError())); - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -class GrantPermissionTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - const auto perm = "can_add_signatory"; - exact_command = std::make_shared("yoda", perm); - command = exact_command; - // It is different from 'perm' due to realisation of old/new model. See - // GrantablePermissions in primitive.proto - new_model_permission = "can_add_my_signatory"; - role_permissions = {can_grant + new_model_permission}; - } - std::shared_ptr exact_command; - std::string new_model_permission; -}; - -TEST_F(GrantPermissionTest, ValidCase) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_command, - insertAccountGrantablePermission(exact_command->account_id, - creator->accountId(), - new_model_permission)) - .WillOnce(Return(WsvCommandResult())); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(GrantPermissionTest, InvalidCaseWhenNoPermissions) { - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(boost::none)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given GrantPermission - * @when command tries to grant permission but insertion fails - * @then execute() fails - */ -TEST_F(GrantPermissionTest, InvalidCaseWhenInsertGrantablePermissionFails) { - EXPECT_CALL(*wsv_command, - insertAccountGrantablePermission(exact_command->account_id, - creator->accountId(), - new_model_permission)) - .WillOnce(Return(makeEmptyError())); - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -class RevokePermissionTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - exact_command = - std::make_shared("yoda", "can_add_signatory"); - new_model_permission = "can_add_my_signatory"; - command = exact_command; - } - std::shared_ptr exact_command; - std::string new_model_permission; -}; - -TEST_F(RevokePermissionTest, ValidCase) { - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - exact_command->account_id, admin_id, new_model_permission)) - .WillOnce(Return(true)); - EXPECT_CALL(*wsv_command, - deleteAccountGrantablePermission(exact_command->account_id, - creator->accountId(), - new_model_permission)) - .WillOnce(Return(WsvCommandResult())); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -TEST_F(RevokePermissionTest, InvalidCaseNoPermissions) { - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - exact_command->account_id, admin_id, new_model_permission)) - .WillOnce(Return(false)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @given RevokePermission - * @when deleting permission fails - * @then execute fails - */ -TEST_F(RevokePermissionTest, InvalidCaseDeleteAccountPermissionvFails) { - EXPECT_CALL(*wsv_command, - deleteAccountGrantablePermission(exact_command->account_id, - creator->accountId(), - new_model_permission)) - .WillOnce(Return(makeEmptyError())); - ASSERT_NO_THROW(checkErrorCase(execute())); -} - -class SetAccountDetailTest : public CommandValidateExecuteTest { - public: - void SetUp() override { - CommandValidateExecuteTest::SetUp(); - cmd = std::make_shared(); - cmd->account_id = admin_id; - cmd->key = "key"; - cmd->value = "val"; - command = cmd; - role_permissions = {can_set_quorum}; - } - std::shared_ptr cmd; - std::string needed_permission = can_set_detail; -}; - -/** - * @when creator is setting details to their account - * @then successfully execute the command - */ -TEST_F(SetAccountDetailTest, ValidWhenSetOwnAccount) { - EXPECT_CALL( - *wsv_command, - setAccountKV(cmd->account_id, creator->accountId(), cmd->key, cmd->value)) - .WillOnce(Return(WsvCommandResult())); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -/** - * @when creator is setting details to their account - * @then successfully execute the command - */ -TEST_F(SetAccountDetailTest, InValidWhenOtherCreator) { - cmd->account_id = account_id; - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, cmd->account_id, needed_permission)) - .WillOnce(Return(false)); - ASSERT_NO_THROW(checkErrorCase(validateAndExecute())); -} - -/** - * @when creator is setting details to their account - * @then successfully execute the command - */ -TEST_F(SetAccountDetailTest, ValidWhenHasPermissions) { - cmd->account_id = account_id; - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, cmd->account_id, needed_permission)) - .WillOnce(Return(true)); - EXPECT_CALL( - *wsv_command, - setAccountKV(cmd->account_id, creator->accountId(), cmd->key, cmd->value)) - .WillOnce(Return(WsvCommandResult())); - ASSERT_NO_THROW(checkValueCase(validateAndExecute())); -} - -/** - * @given SetAccountDetail - * @when command tries to set details, but setting key-value fails - * @then execute fails - */ -TEST_F(SetAccountDetailTest, InvalidWhenSetAccountKVFails) { - EXPECT_CALL( - *wsv_command, - setAccountKV(cmd->account_id, creator->accountId(), cmd->key, cmd->value)) - .WillOnce(Return(makeEmptyError())); - ASSERT_NO_THROW(checkErrorCase(execute())); -} diff --git a/test/module/irohad/model/converters/json_commands_test.cpp b/test/module/irohad/model/converters/json_commands_test.cpp index dd816056f9..df227e5eca 100644 --- a/test/module/irohad/model/converters/json_commands_test.cpp +++ b/test/module/irohad/model/converters/json_commands_test.cpp @@ -43,6 +43,7 @@ using namespace rapidjson; using namespace iroha; using namespace iroha::model; using namespace iroha::model::converters; +using namespace shared_model::permissions; class JsonCommandTest : public ::testing::Test { public: diff --git a/test/module/irohad/model/converters/json_query_factory_test.cpp b/test/module/irohad/model/converters/json_query_factory_test.cpp index 5d5826fee3..226e9a4895 100644 --- a/test/module/irohad/model/converters/json_query_factory_test.cpp +++ b/test/module/irohad/model/converters/json_query_factory_test.cpp @@ -101,8 +101,7 @@ TEST(QuerySerializerTest, DeserializeGetAccountAssetsWhenValid) { })"; auto res = querySerializer.deserialize(json_query); ASSERT_TRUE(res); - auto casted = - std::static_pointer_cast(*res); + auto casted = std::static_pointer_cast(*res); ASSERT_EQ("test@test", casted->account_id); ASSERT_EQ("coin#test", casted->asset_id); } @@ -127,8 +126,7 @@ TEST(QuerySerializerTest, DeserializeGetAccountDetailWhenValid) { })"; auto res = querySerializer.deserialize(json_query); ASSERT_TRUE(res); - auto casted = - std::static_pointer_cast(*res); + auto casted = std::static_pointer_cast(*res); ASSERT_EQ("test@test", casted->account_id); } @@ -162,7 +160,6 @@ TEST(QuerySerialzierTest, DeserializeGetTransactionsWithInvalidHash) { QueryGenerator queryGenerator; const auto val = queryGenerator.generateGetTransactions(0, "123", 0, {valid_size_hash}); - val->signature = generateSignature(42); const auto json = queryFactory.serialize(val); auto json_doc_opt = iroha::model::converters::stringToJson(json); ASSERT_TRUE(json_doc_opt); @@ -194,7 +191,6 @@ TEST(QuerySerializerTest, SerializeGetAccount) { JsonQueryFactory queryFactory; QueryGenerator queryGenerator; auto val = queryGenerator.generateGetAccount(0, "123", 0, "test"); - val->signature = generateSignature(42); auto json = queryFactory.serialize(val); auto ser_val = queryFactory.deserialize(json); ASSERT_TRUE(ser_val); @@ -207,7 +203,6 @@ TEST(QuerySerializerTest, SerializeGetAccountAssets) { QueryGenerator queryGenerator; auto val = queryGenerator.generateGetAccountAssets(0, "123", 0, "test", "coin"); - val->signature = generateSignature(42); auto json = queryFactory.serialize(val); auto ser_val = queryFactory.deserialize(json); ASSERT_TRUE(ser_val); @@ -219,7 +214,6 @@ TEST(QuerySerializerTest, SerializeGetAccountTransactions) { JsonQueryFactory queryFactory; QueryGenerator queryGenerator; auto val = queryGenerator.generateGetAccountTransactions(0, "123", 0, "test"); - val->signature = generateSignature(42); auto json = queryFactory.serialize(val); auto ser_val = queryFactory.deserialize(json); ASSERT_TRUE(ser_val); @@ -233,7 +227,6 @@ TEST(QuerySerializerTest, SerialiizeGetTransactions) { hash1[0] = 1, hash2[0] = 2; auto val = queryGenerator.generateGetTransactions(0, "admin", 0, {hash1, hash2}); - val->signature = generateSignature(42); runQueryTest(val); } @@ -241,7 +234,6 @@ TEST(QuerySerializerTest, SerializeGetSignatories) { JsonQueryFactory queryFactory; QueryGenerator queryGenerator; auto val = queryGenerator.generateGetSignatories(0, "123", 0, "test"); - val->signature = generateSignature(42); auto json = queryFactory.serialize(val); auto ser_val = queryFactory.deserialize(json); ASSERT_TRUE(ser_val); @@ -252,20 +244,17 @@ TEST(QuerySerializerTest, SerializeGetSignatories) { TEST(QuerySerializerTest, get_asset_info) { QueryGenerator queryGenerator; auto val = queryGenerator.generateGetAssetInfo(); - val->signature = generateSignature(42); runQueryTest(val); } TEST(QuerySerializerTest, get_roles) { QueryGenerator queryGenerator; auto val = queryGenerator.generateGetRoles(); - val->signature = generateSignature(42); runQueryTest(val); } TEST(QuerySerializerTest, get_role_permissions) { QueryGenerator queryGenerator; auto val = queryGenerator.generateGetRolePermissions(); - val->signature = generateSignature(42); runQueryTest(val); } diff --git a/test/module/irohad/model/converters/json_transaction_test.cpp b/test/module/irohad/model/converters/json_transaction_test.cpp index 095c01ed4a..dcb4486fc7 100644 --- a/test/module/irohad/model/converters/json_transaction_test.cpp +++ b/test/module/irohad/model/converters/json_transaction_test.cpp @@ -63,7 +63,6 @@ TEST_F(JsonTransactionTest, InvalidWhenNegativeAddAssetQuantity) { ], "created_ts": 1503845603221, "creator_account_id": "admin@test", - "tx_counter": 1, "commands": [ { "command_type": "CreateAsset", diff --git a/test/module/irohad/model/converters/pb_block_test.cpp b/test/module/irohad/model/converters/pb_block_test.cpp index 719653c664..4f6251983a 100644 --- a/test/module/irohad/model/converters/pb_block_test.cpp +++ b/test/module/irohad/model/converters/pb_block_test.cpp @@ -41,7 +41,6 @@ TEST(BlockTest, bl_test) { orig_tx.signatures = {siga}; orig_tx.created_ts = 2; - orig_tx.tx_counter = 1; auto c1 = iroha::model::CreateDomain(); c1.domain_id = "keker"; diff --git a/test/module/irohad/model/converters/pb_commands_test.cpp b/test/module/irohad/model/converters/pb_commands_test.cpp index 6fe0fb1543..3b8bf4c783 100644 --- a/test/module/irohad/model/converters/pb_commands_test.cpp +++ b/test/module/irohad/model/converters/pb_commands_test.cpp @@ -37,6 +37,7 @@ #include "validators/permissions.hpp" using namespace iroha::model; +using namespace shared_model::permissions; void command_converter_test(iroha::model::Command &abstract_command) { auto factory = iroha::model::converters::PbCommandFactory(); diff --git a/test/module/irohad/model/converters/pb_query_factory_test.cpp b/test/module/irohad/model/converters/pb_query_factory_test.cpp index 2560bc3185..60cb2a4f69 100644 --- a/test/module/irohad/model/converters/pb_query_factory_test.cpp +++ b/test/module/irohad/model/converters/pb_query_factory_test.cpp @@ -15,13 +15,14 @@ * limitations under the License. */ -#include "model/converters/pb_query_factory.hpp" #include -#include "model/generators/query_generator.hpp" -#include "model/sha3_hash.hpp" +#include "model/converters/pb_query_factory.hpp" +#include "model/converters/pb_transaction_factory.hpp" +#include "model/generators/query_generator.hpp" #include "model/queries/get_asset_info.hpp" #include "model/queries/get_roles.hpp" +#include "model/sha3_hash.hpp" using namespace iroha::model::converters; using namespace iroha::model::generators; diff --git a/test/module/irohad/model/converters/pb_query_responses_test.cpp b/test/module/irohad/model/converters/pb_query_responses_test.cpp index 1681808066..686022f830 100644 --- a/test/module/irohad/model/converters/pb_query_responses_test.cpp +++ b/test/module/irohad/model/converters/pb_query_responses_test.cpp @@ -143,7 +143,6 @@ TEST(QueryResponseTest, TransactionsResponseTest) { std::vector result; for (size_t i = 0; i < 3; ++i) { model::Transaction current; - current.tx_counter = i; result.push_back(current); } return result; @@ -153,14 +152,6 @@ TEST(QueryResponseTest, TransactionsResponseTest) { auto query_response = *pb_factory.serialize(shrd_tr); ASSERT_EQ(query_response.transactions_response().transactions().size(), 3); - for (size_t i = 0; i < 3; i++) { - ASSERT_EQ(query_response.transactions_response() - .transactions() - .Get(i) - .payload() - .tx_counter(), - i); - } } TEST(QueryResponseTest, roles_response) { diff --git a/test/module/irohad/model/converters/pb_transaction_test.cpp b/test/module/irohad/model/converters/pb_transaction_test.cpp index a286e434e4..a564f3b3dc 100644 --- a/test/module/irohad/model/converters/pb_transaction_test.cpp +++ b/test/module/irohad/model/converters/pb_transaction_test.cpp @@ -39,7 +39,6 @@ TEST(TransactionTest, tx_test) { orig_tx.signatures = {siga}; orig_tx.created_ts = 2; - orig_tx.tx_counter = 1; auto c1 = iroha::model::CreateDomain(); c1.domain_id = "keker"; diff --git a/test/module/irohad/model/model_crypto_provider_test.cpp b/test/module/irohad/model/model_crypto_provider_test.cpp index e8cc99a211..d86cc88972 100644 --- a/test/module/irohad/model/model_crypto_provider_test.cpp +++ b/test/module/irohad/model/model_crypto_provider_test.cpp @@ -33,7 +33,7 @@ namespace iroha { TEST_F(CryptoProviderTest, SignAndVerifyTransaction) { auto model_tx = - generators::TransactionGenerator().generateTransaction("test", 0, {}); + generators::TransactionGenerator().generateTransaction("test", {}); provider.sign(model_tx); ASSERT_TRUE(provider.verify(model_tx)); diff --git a/test/module/irohad/model/model_mocks.hpp b/test/module/irohad/model/model_mocks.hpp index d53da60240..094fb6db20 100644 --- a/test/module/irohad/model/model_mocks.hpp +++ b/test/module/irohad/model/model_mocks.hpp @@ -21,8 +21,8 @@ #include #include "ametsuchi/wsv_command.hpp" #include "ametsuchi/wsv_query.hpp" +#include "execution/query_execution.hpp" #include "model/command.hpp" -#include "model/query_execution.hpp" namespace iroha { namespace model { diff --git a/test/module/irohad/model/operators/model_operators_test.cpp b/test/module/irohad/model/operators/model_operators_test.cpp index f5acc205d3..059d4ca1ba 100644 --- a/test/module/irohad/model/operators/model_operators_test.cpp +++ b/test/module/irohad/model/operators/model_operators_test.cpp @@ -313,7 +313,15 @@ TEST(ModelOperatorTest, SignatureTest) { ASSERT_EQ(sig1, sig2); sig1.signature[0] = 0x23; - ASSERT_NE(sig1, sig2); + + // equals because public keys are same + ASSERT_EQ(sig1, sig2); + + auto sig3 = createSignature(); + sig3.pubkey[0] = 0x23; + + // not equals because public keys are different + ASSERT_NE(sig1, sig3); } // -----|Transaction|----- @@ -322,7 +330,6 @@ Transaction createTransaction() { Transaction transaction; transaction.created_ts = 1; transaction.creator_account_id = "132"; - transaction.tx_counter = 5; transaction.signatures.push_back(createSignature()); // commands diff --git a/test/module/irohad/network/block_loader_test.cpp b/test/module/irohad/network/block_loader_test.cpp index 6dd050cd65..633253ecf3 100644 --- a/test/module/irohad/network/block_loader_test.cpp +++ b/test/module/irohad/network/block_loader_test.cpp @@ -203,7 +203,7 @@ TEST_F(BlockLoaderTest, ValidWhenBlockPresent) { auto block = loader->retrieveBlock(peer_key, requested.hash()); ASSERT_TRUE(block); - ASSERT_EQ(*(*block), requested); + ASSERT_EQ(**block, requested); } /** diff --git a/test/module/irohad/ordering/CMakeLists.txt b/test/module/irohad/ordering/CMakeLists.txt index 943e5d3519..5dc742dba8 100644 --- a/test/module/irohad/ordering/CMakeLists.txt +++ b/test/module/irohad/ordering/CMakeLists.txt @@ -16,6 +16,7 @@ addtest(ordering_service_test ordering_service_test.cpp) target_link_libraries(ordering_service_test ordering_service shared_model_stateless_validation + iroha_amount ) addtest(ordering_gate_test ordering_gate_test.cpp) @@ -23,11 +24,5 @@ target_link_libraries(ordering_gate_test ordering_service shared_model_cryptography_model shared_model_stateless_validation - ) - -addtest(ordering_gate_service_test ordering_gate_service_test.cpp) -target_link_libraries(ordering_gate_service_test - ordering_service - shared_model_stateless_validation - shared_model_cryptography_model + iroha_amount ) diff --git a/test/module/irohad/ordering/ordering_gate_test.cpp b/test/module/irohad/ordering/ordering_gate_test.cpp index 8e488bbd65..5e3036e69b 100644 --- a/test/module/irohad/ordering/ordering_gate_test.cpp +++ b/test/module/irohad/ordering/ordering_gate_test.cpp @@ -38,6 +38,8 @@ using ::testing::_; using ::testing::InvokeWithoutArgs; using ::testing::Return; +using shared_model::interface::types::HeightType; + class MockOrderingGateTransportGrpcService : public proto::OrderingServiceTransportGrpc::Service { public: @@ -61,36 +63,26 @@ class OrderingGateTest : public ::testing::Test { } void SetUp() override { - thread = std::thread([this] { - grpc::ServerBuilder builder; - int port = 0; - builder.AddListeningPort( - "0.0.0.0:0", grpc::InsecureServerCredentials(), &port); - - builder.RegisterService(fake_service.get()); - - server = builder.BuildAndStart(); - auto address = "0.0.0.0:" + std::to_string(port); - // Initialize components after port has been bind - transport = std::make_shared(address); - gate_impl = std::make_shared(transport); - transport->subscribe(gate_impl); - - ASSERT_NE(port, 0); - ASSERT_TRUE(server); - cv.notify_one(); - server->Wait(); - }); - - std::unique_lock lock(m); - cv.wait_for(lock, std::chrono::seconds(1)); + grpc::ServerBuilder builder; + int port = 0; + builder.AddListeningPort( + "0.0.0.0:0", grpc::InsecureServerCredentials(), &port); + + builder.RegisterService(fake_service.get()); + + server = builder.BuildAndStart(); + auto address = "0.0.0.0:" + std::to_string(port); + // Initialize components after port has been bind + transport = std::make_shared(address); + gate_impl = std::make_shared(transport, 1, false); + transport->subscribe(gate_impl); + + ASSERT_NE(port, 0); + ASSERT_TRUE(server); } void TearDown() override { server->Shutdown(); - if (thread.joinable()) { - thread.join(); - } } std::unique_ptr server; @@ -98,7 +90,6 @@ class OrderingGateTest : public ::testing::Test { std::shared_ptr transport; std::shared_ptr gate_impl; std::shared_ptr fake_service; - std::thread thread; std::condition_variable cv; std::mutex m; }; @@ -137,9 +128,14 @@ TEST_F(OrderingGateTest, ProposalReceivedByGateWhenSent) { auto wrapper = make_test_subscriber(gate_impl->on_proposal(), 1); wrapper.subscribe(); + auto pcs = std::make_shared(); + rxcpp::subjects::subject commit_subject; + EXPECT_CALL(*pcs, on_commit()) + .WillOnce(Return(commit_subject.get_observable())); + gate_impl->setPcs(*pcs); + grpc::ServerContext context; auto tx = shared_model::proto::TransactionBuilder() - .txCounter(2) .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") .addAssetQuantity("admin@tu", "coin#coin", "1.0") @@ -162,6 +158,40 @@ TEST_F(OrderingGateTest, ProposalReceivedByGateWhenSent) { ASSERT_TRUE(wrapper.validate()); } +class QueueBehaviorTest : public ::testing::Test { + public: + QueueBehaviorTest() : ordering_gate(transport, 1, false){}; + + void SetUp() override { + transport = std::make_shared(); + pcs = std::make_shared(); + EXPECT_CALL(*pcs, on_commit()) + .WillOnce(Return(commit_subject.get_observable())); + + ordering_gate.setPcs(*pcs); + ordering_gate.on_proposal().subscribe( + [&](auto val) { messages.push_back(val); }); + } + + std::shared_ptr transport; + std::shared_ptr pcs; + rxcpp::subjects::subject commit_subject; + OrderingGateImpl ordering_gate; + std::vector messages; + + void pushCommit(HeightType height) { + commit_subject.get_subscriber().on_next(rxcpp::observable<>::just( + std::static_pointer_cast( + std::make_shared( + TestBlockBuilder().height(height).build())))); + } + + void pushProposal(HeightType height) { + ordering_gate.onProposal(std::make_shared( + TestProposalBuilder().height(height).build())); + }; +}; + /** * @given Initialized OrderingGate * AND MockPeerCommunicationService @@ -169,19 +199,7 @@ TEST_F(OrderingGateTest, ProposalReceivedByGateWhenSent) { * AND one commit in node * @then Check that send round appears after commit */ -TEST(OrderingGateQueueBehaviour, SendManyProposals) { - std::shared_ptr transport = - std::make_shared(); - - std::shared_ptr pcs = - std::make_shared(); - rxcpp::subjects::subject commit_subject; - EXPECT_CALL(*pcs, on_commit()) - .WillOnce(Return(commit_subject.get_observable())); - - OrderingGateImpl ordering_gate(transport); - ordering_gate.setPcs(*pcs); - +TEST_F(QueueBehaviorTest, SendManyProposals) { auto wrapper_before = make_test_subscriber(ordering_gate.on_proposal(), 1); wrapper_before.subscribe(); @@ -190,7 +208,6 @@ TEST(OrderingGateQueueBehaviour, SendManyProposals) { wrapper_after.subscribe(); auto tx = shared_model::proto::TransactionBuilder() - .txCounter(2) .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") .addAssetQuantity("admin@tu", "coin#coin", "1.0") @@ -207,7 +224,7 @@ TEST(OrderingGateQueueBehaviour, SendManyProposals) { .build()); auto proposal2 = std::make_shared( shared_model::proto::ProposalBuilder() - .height(2) + .height(3) .createdTime(iroha::time::now()) .transactions(txs) .build()); @@ -218,9 +235,120 @@ TEST(OrderingGateQueueBehaviour, SendManyProposals) { ASSERT_TRUE(wrapper_before.validate()); std::shared_ptr block = - std::make_shared(TestBlockBuilder().build()); + std::make_shared( + TestBlockBuilder().height(2).build()); commit_subject.get_subscriber().on_next(rxcpp::observable<>::just(block)); ASSERT_TRUE(wrapper_after.validate()); } + +/** + * @given Initialized OrderingGate + * AND MockPeerCommunicationService + * @when Receive proposals in random order + * @then on_proposal output is ordered + */ +TEST_F(QueueBehaviorTest, ReceiveUnordered) { + // this will set unlock_next_ to false, so proposals 3 and 4 are enqueued + pushProposal(2); + + pushProposal(4); + pushProposal(3); + + pushCommit(2); + pushCommit(3); + + ASSERT_EQ(3, messages.size()); + ASSERT_EQ(2, messages.at(0)->height()); + ASSERT_EQ(3, messages.at(1)->height()); + ASSERT_EQ(4, messages.at(2)->height()); +} + +/** + * @given Initialized OrderingGate + * AND MockPeerCommunicationService + * @when Receive commits which are newer than existing proposals + * @then on_proposal is not invoked on proposals + * which are older than last committed block + */ +TEST_F(QueueBehaviorTest, DiscardOldProposals) { + pushProposal(2); + pushProposal(3); + + pushProposal(4); + pushProposal(5); + pushCommit(4); + + // proposals 2 and 3 must not be forwarded down the pipeline. + EXPECT_EQ(2, messages.size()); + ASSERT_EQ(2, messages.at(0)->height()); + ASSERT_EQ(5, messages.at(1)->height()); +} + +/** + * @given Initialized OrderingGate + * AND MockPeerCommunicationService + * @when Proposals are newer than received commits + * @then newer proposals are kept in queue + */ +TEST_F(QueueBehaviorTest, KeepNewerProposals) { + pushProposal(2); + pushProposal(3); + pushProposal(4); + + pushCommit(2); + + // proposal 3 must be forwarded down the pipeline, 4 kept in queue. + EXPECT_EQ(2, messages.size()); + EXPECT_EQ(2, messages.at(0)->height()); + EXPECT_EQ(3, messages.at(1)->height()); + + pushCommit(3); + // Now proposal 4 is forwarded to the pipeline + EXPECT_EQ(3, messages.size()); + EXPECT_EQ(4, messages.at(2)->height()); +} + +/** + * @given Initialized OrderingGate + * AND MockPeerCommunicationService + * @when commit is received before any proposals + * @then old proposals are discarded and new is propagated + */ +TEST_F(QueueBehaviorTest, CommitBeforeProposal) { + pushCommit(4); + + // Old proposals should be discarded + pushProposal(2); + pushProposal(3); + pushProposal(4); + + EXPECT_EQ(0, messages.size()); + + // should be propagated + pushProposal(5); + + // should not be propagated + pushProposal(6); + + EXPECT_EQ(1, messages.size()); + EXPECT_EQ(5, messages.at(0)->height()); +} + +/** + * @given Initialized OrderingGate + * AND MockPeerCommunicationService + * @when commit is received which newer than all proposals + * @then all proposals are discarded and none are propagated + */ +TEST_F(QueueBehaviorTest, CommitNewerThanAllProposals) { + pushProposal(2); + // Old proposals should be discarded + pushProposal(3); + pushProposal(4); + + pushCommit(4); + EXPECT_EQ(1, messages.size()); + EXPECT_EQ(2, messages.at(0)->height()); +} diff --git a/test/module/irohad/ordering/ordering_service_test.cpp b/test/module/irohad/ordering/ordering_service_test.cpp index aa17145d15..e8ca3b585c 100644 --- a/test/module/irohad/ordering/ordering_service_test.cpp +++ b/test/module/irohad/ordering/ordering_service_test.cpp @@ -1,24 +1,13 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include #include "backend/protobuf/common_objects/peer.hpp" #include "builders/protobuf/common_objects/proto_peer_builder.hpp" +#include "builders/protobuf/transaction.hpp" #include "logger/logger.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" @@ -27,7 +16,6 @@ #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" #include "ordering/impl/ordering_service_impl.hpp" #include "ordering/impl/ordering_service_transport_grpc.hpp" -#include "builders/protobuf/transaction.hpp" using namespace iroha; using namespace iroha::ordering; @@ -35,14 +23,12 @@ using namespace iroha::network; using namespace iroha::ametsuchi; using namespace std::chrono_literals; +using ::testing::_; using ::testing::AtLeast; using ::testing::DoAll; using ::testing::Invoke; using ::testing::InvokeWithoutArgs; using ::testing::Return; -using ::testing::_; - -static logger::Logger log_ = logger::testLog("OrderingService"); class MockOrderingServiceTransport : public network::OrderingServiceTransport { public: @@ -82,16 +68,29 @@ class OrderingServiceTest : public ::testing::Test { } auto getTx() { - return std::make_shared( + return std::make_unique( shared_model::proto::TransactionBuilder() - .txCounter(2) - .createdTime(iroha::time::now()) - .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") - .build() - .signAndAddSignature( - shared_model::crypto::DefaultCryptoAlgorithmType:: - generateKeypair())); + .createdTime(iroha::time::now()) + .creatorAccountId("admin@ru") + .addAssetQuantity("admin@tu", "coin#coin", "1.0") + .build() + .signAndAddSignature( + shared_model::crypto::DefaultCryptoAlgorithmType:: + generateKeypair())); + } + + auto initOs(size_t max_proposal) { + return std::make_shared( + wsv, + max_proposal, + proposal_timeout.get_observable(), + fake_transport, + fake_persistent_state, + false); + } + + void makeProposalTimeout() { + proposal_timeout.get_subscriber().on_next(0); } std::shared_ptr fake_transport; @@ -101,34 +100,45 @@ class OrderingServiceTest : public ::testing::Test { std::string address{"0.0.0.0:50051"}; std::shared_ptr peer; std::shared_ptr wsv; + rxcpp::subjects::subject proposal_timeout; }; +/** + * @given OrderingService and MockOrderingServiceTransport + * @when publishProposal is called at transport + * @then publishProposalProxy is called + */ TEST_F(OrderingServiceTest, SimpleTest) { - // Direct publishProposal call, used for basic case test and for debug - // simplicity - const size_t max_proposal = 5; - const size_t commit_delay = 1000; EXPECT_CALL(*fake_persistent_state, loadProposalHeight()) .Times(1) .WillOnce(Return(boost::optional(2))); + EXPECT_CALL(*fake_transport, publishProposalProxy(_, _)).Times(1); - auto ordering_service = std::make_shared( - wsv, max_proposal, commit_delay, fake_transport, fake_persistent_state); + auto ordering_service = initOs(max_proposal); fake_transport->subscribe(ordering_service); - EXPECT_CALL(*fake_transport, publishProposalProxy(_, _)).Times(1); - fake_transport->publishProposal( std::make_unique( - TestProposalBuilder().height(1).createdTime(iroha::time::now()).build()), + TestProposalBuilder() + .height(1) + .createdTime(iroha::time::now()) + .build()), {}); } +/** + * @given OrderingService with max_proposal==5 and only self peer + * and MockOrderingServiceTransport + * and MockOrderingServicePersistentState + * @when OrderingService::onTransaction called 10 times + * @then publishProposalProxy called twice + * and proposal height was loaded once and saved twice + */ TEST_F(OrderingServiceTest, ValidWhenProposalSizeStrategy) { const size_t max_proposal = 5; - const size_t commit_delay = 1000; + const size_t tx_num = 10; EXPECT_CALL(*fake_persistent_state, saveProposalHeight(_)) .Times(2) @@ -136,88 +146,64 @@ TEST_F(OrderingServiceTest, ValidWhenProposalSizeStrategy) { EXPECT_CALL(*fake_persistent_state, loadProposalHeight()) .Times(1) .WillOnce(Return(boost::optional(2))); - - auto ordering_service = std::make_shared( - wsv, max_proposal, commit_delay, fake_transport, fake_persistent_state); - fake_transport->subscribe(ordering_service); - - // Init => proposal size 5 => 2 proposals after 10 transactions - size_t call_count = 0; EXPECT_CALL(*fake_transport, publishProposalProxy(_, _)) - .Times(2) - .WillRepeatedly(InvokeWithoutArgs([&] { - ++call_count; - cv.notify_one(); - })); - - shared_model::proto::PeerBuilder builder; - - auto key = shared_model::crypto::PublicKey(peer->pubkey().toString()); - auto tmp = builder.address(peer->address()).pubkey(key).build(); - + .Times(tx_num / max_proposal); EXPECT_CALL(*wsv, getLedgerPeers()) .WillRepeatedly(Return(std::vector{peer})); - for (size_t i = 0; i < 10; ++i) { + auto ordering_service = initOs(max_proposal); + fake_transport->subscribe(ordering_service); + + for (size_t i = 0; i < tx_num; ++i) { ordering_service->onTransaction(getTx()); } - - std::unique_lock lock(m); - cv.wait_for(lock, 10s, [&] { return call_count == 2; }); } +/** + * @given OrderingService with big enough max_proposal and only self peer + * and MockOrderingServiceTransport + * and MockOrderingServicePersistentState + * @when OrderingService::onTransaction called 8 times + * and after triggered timeout + * and then repeat with 2 onTransaction calls + * @then publishProposalProxy called twice + * and proposal height was loaded once and saved twice + */ TEST_F(OrderingServiceTest, ValidWhenTimerStrategy) { - // Init => proposal timer 400 ms => 10 tx by 50 ms => 2 proposals in 1 second + const size_t max_proposal = 100; EXPECT_CALL(*fake_persistent_state, saveProposalHeight(_)) .Times(2) .WillRepeatedly(Return(true)); - shared_model::proto::PeerBuilder builder; - - auto key = shared_model::crypto::PublicKey(peer->pubkey().toString()); - auto tmp = builder.address(peer->address()).pubkey(key).build(); - - EXPECT_CALL(*wsv, getLedgerPeers()) - .WillRepeatedly(Return(std::vector{peer})); - - const size_t max_proposal = 100; - const size_t commit_delay = 400; - EXPECT_CALL(*fake_persistent_state, loadProposalHeight()) .Times(1) .WillOnce(Return(boost::optional(2))); + EXPECT_CALL(*wsv, getLedgerPeers()) + .WillRepeatedly(Return(std::vector{peer})); + EXPECT_CALL(*fake_transport, publishProposalProxy(_, _)).Times(2); - auto ordering_service = std::make_shared( - wsv, max_proposal, commit_delay, fake_transport, fake_persistent_state); + auto ordering_service = initOs(max_proposal); fake_transport->subscribe(ordering_service); - EXPECT_CALL(*fake_transport, publishProposalProxy(_, _)) - .Times(2) - .WillRepeatedly(InvokeWithoutArgs([&] { - log_->info("Proposal send to grpc"); - cv.notify_one(); - })); - for (size_t i = 0; i < 8; ++i) { ordering_service->onTransaction(getTx()); } - - std::unique_lock lk(m); - cv.wait_for(lk, 10s); + makeProposalTimeout(); ordering_service->onTransaction(getTx()); ordering_service->onTransaction(getTx()); - cv.wait_for(lk, 10s); + makeProposalTimeout(); } /** - * @given Ordering service and the persistent state that cannot save proposals + * @given Ordering service and the persistent state that cannot save + * proposals * @when onTransaction is called * @then no published proposal */ TEST_F(OrderingServiceTest, BrokenPersistentState) { const size_t max_proposal = 1; - const size_t commit_delay = 100; + EXPECT_CALL(*fake_persistent_state, loadProposalHeight()) .Times(1) .WillOnce(Return(boost::optional(1))); @@ -225,10 +211,86 @@ TEST_F(OrderingServiceTest, BrokenPersistentState) { .Times(1) .WillRepeatedly(Return(false)); - auto ordering_service = std::make_shared( - wsv, max_proposal, commit_delay, fake_transport, fake_persistent_state); + auto ordering_service = initOs(max_proposal); ordering_service->onTransaction(getTx()); + makeProposalTimeout(); +} + +/** + * @given Ordering service up and running + * @when Send 1000 transactions from each of 2 threads + * @then Ordering service should not crash + */ +TEST_F(OrderingServiceTest, ConcurrentGenerateProposal) { + const auto max_proposal = 1; + EXPECT_CALL(*fake_persistent_state, loadProposalHeight()) + .Times(1) + .WillOnce(Return(boost::optional(1))); + EXPECT_CALL(*fake_persistent_state, saveProposalHeight(_)) + .WillRepeatedly(Return(false)); + + auto ordering_service = initOs(max_proposal); - std::unique_lock lk(m); - cv.wait_for(lk, 2s); + auto on_tx = [&]() { + for (int i = 0; i < 1000; ++i) { + ordering_service->onTransaction(getTx()); + } + }; + + const auto num_threads = 2; + + std::vector threads; + for (int i = 0; i < num_threads; ++i) { + threads.emplace_back(std::thread(on_tx)); + } + + for (int i = 0; i < num_threads; ++i) { + threads.at(i).join(); + } + makeProposalTimeout(); +} + +/** + * @given Ordering service up and running + * @when Send 1000 transactions from a separate thread and perform 5 second + * delay during generateProposal() so destructor of OrderingServiceImpl is + * called before generateProposal() finished + * @then Ordering service should not crash and publishProposal() should not be + * called after destructor call + */ +TEST_F(OrderingServiceTest, GenerateProposalDestructor) { + const auto max_proposal = 100000; + const auto commit_delay = 100ms; + EXPECT_CALL(*fake_persistent_state, loadProposalHeight()) + .Times(1) + .WillOnce(Return(boost::optional(1))); + EXPECT_CALL(*fake_persistent_state, saveProposalHeight(_)) + .WillRepeatedly(InvokeWithoutArgs([] { + std::this_thread::sleep_for(5s); + return true; + })); + EXPECT_CALL(*wsv, getLedgerPeers()) + .WillRepeatedly(Return(std::vector{peer})); + + { + EXPECT_CALL(*fake_transport, publishProposalProxy(_, _)).Times(AtLeast(1)); + OrderingServiceImpl ordering_service( + wsv, + max_proposal, + rxcpp::observable<>::interval(commit_delay, + rxcpp::observe_on_new_thread()), + fake_transport, + fake_persistent_state, + true); + + auto on_tx = [&]() { + for (int i = 0; i < 1000; ++i) { + ordering_service.onTransaction(getTx()); + } + }; + + std::thread thread(on_tx); + thread.join(); + } + EXPECT_CALL(*fake_transport, publishProposalProxy(_, _)).Times(0); } diff --git a/test/module/irohad/simulator/simulator_test.cpp b/test/module/irohad/simulator/simulator_test.cpp index fcdd8ddd65..71324bbeaf 100644 --- a/test/module/irohad/simulator/simulator_test.cpp +++ b/test/module/irohad/simulator/simulator_test.cpp @@ -77,13 +77,12 @@ shared_model::proto::Block makeBlock(int height) { return TestBlockBuilder() .transactions(std::vector()) .height(height) - .prevHash(shared_model::crypto::Hash(std::string("0", 32))) + .prevHash(shared_model::crypto::Hash(std::string(32, '0'))) .build(); } shared_model::proto::Proposal makeProposal(int height) { auto tx = shared_model::proto::TransactionBuilder() - .txCounter(2) .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") .addAssetQuantity("admin@tu", "coin#coin", "1.0") @@ -112,7 +111,6 @@ TEST_F(SimulatorTest, ValidWhenInitialized) { TEST_F(SimulatorTest, ValidWhenPreviousBlock) { // proposal with height 2 => height 1 block present => new block generated auto tx = shared_model::proto::TransactionBuilder() - .txCounter(2) .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") .addAssetQuantity("admin@tu", "coin#coin", "1.0") diff --git a/test/module/irohad/synchronizer/CMakeLists.txt b/test/module/irohad/synchronizer/CMakeLists.txt index 92279a485d..2c2d7b48da 100644 --- a/test/module/irohad/synchronizer/CMakeLists.txt +++ b/test/module/irohad/synchronizer/CMakeLists.txt @@ -2,6 +2,7 @@ addtest(synchronizer_test synchronizer_test.cpp) target_link_libraries(synchronizer_test synchronizer shared_model_cryptography + shared_model_proto_backend ) diff --git a/test/module/irohad/synchronizer/synchronizer_test.cpp b/test/module/irohad/synchronizer/synchronizer_test.cpp index 2b05ccffaf..dd40b31410 100644 --- a/test/module/irohad/synchronizer/synchronizer_test.cpp +++ b/test/module/irohad/synchronizer/synchronizer_test.cpp @@ -88,8 +88,13 @@ TEST_F(SynchronizerTest, ValidWhenInitialized) { init(); } + +/** + * @given A commit from consensus and initialized components + * @when a valid block that can be applied + * @then Successful commit + */ TEST_F(SynchronizerTest, ValidWhenSingleCommitSynchronized) { - // commit from consensus => chain validation passed => commit successful auto block = TestBlockBuilder().height(5).build(); std::shared_ptr test_block = std::make_shared(std::move(block)); @@ -127,8 +132,12 @@ TEST_F(SynchronizerTest, ValidWhenSingleCommitSynchronized) { ASSERT_TRUE(wrapper.validate()); } +/** + * @given A commit from consensus and initialized components + * @when Storage cannot be initialized + * @then No commit should be passed + */ TEST_F(SynchronizerTest, ValidWhenBadStorage) { - // commit from consensus => storage not created => no commit std::shared_ptr test_block = std::make_shared(TestBlockBuilder().build()); @@ -157,8 +166,12 @@ TEST_F(SynchronizerTest, ValidWhenBadStorage) { ASSERT_TRUE(wrapper.validate()); } -TEST_F(SynchronizerTest, ValidWhenBlockValidationFailure) { - // commit from consensus => chain validation failed => commit successful +/** + * @given A commit from consensus and initialized components + * @when A valid chain with expected ending + * @then Successful commit + */ +TEST_F(SynchronizerTest, ValidWhenValidChain) { TemplateMockBlockValidator mockBlockValidator; EXPECT_CALL(*mockBlockValidator.validator, validate(_)) .WillOnce(Return(shared_model::validation::Answer())); @@ -211,3 +224,67 @@ TEST_F(SynchronizerTest, ValidWhenBlockValidationFailure) { ASSERT_TRUE(wrapper.validate()); } + +/** + * @given A commit from consensus and initialized components + * @when A valid chain with unexpected ending + * @then No commit should be passed + */ +TEST_F(SynchronizerTest, InvalidWhenUnexpectedEnd) { + TemplateMockBlockValidator mockBlockValidator; + EXPECT_CALL(*mockBlockValidator.validator, validate(_)) + .WillRepeatedly(Return(shared_model::validation::Answer())); + using TestUnsignedBlockBuilder = shared_model::proto::TemplateBlockBuilder< + (1 << shared_model::proto::TemplateBlockBuilder<>::total) - 1, + TemplateMockBlockValidator, + shared_model::proto::UnsignedWrapper>; + + auto block = TestUnsignedBlockBuilder(mockBlockValidator) + .height(5) + .build() + .signAndAddSignature( + shared_model::crypto::DefaultCryptoAlgorithmType:: + generateKeypair()); + std::shared_ptr test_block = + std::make_shared(std::move(block)); + + DefaultValue, std::string>>:: + SetFactory(&createMockMutableStorage); + EXPECT_CALL(*mutable_factory, createMutableStorage()).Times(2); + + EXPECT_CALL(*mutable_factory, commit_(_)).Times(0); + + EXPECT_CALL(*chain_validator, validateBlock(testing::Ref(*test_block), _)) + .WillOnce(Return(false)); + + EXPECT_CALL(*chain_validator, validateChain(_, _)).WillOnce(Return(true)); + + // wrong block has different hash + auto wrong_block_end = + TestUnsignedBlockBuilder(mockBlockValidator) + .height(5) + .createdTime(iroha::time::now()) + .build() + .signAndAddSignature( + shared_model::crypto::DefaultCryptoAlgorithmType:: + generateKeypair()); + std::shared_ptr wrong_test_block = + std::make_shared(std::move(wrong_block_end)); + + EXPECT_CALL(*block_loader, retrieveBlocks(_)) + .WillOnce(Return(rxcpp::observable<>::just(wrong_test_block))); + + EXPECT_CALL(*consensus_gate, on_commit()) + .WillOnce(Return(rxcpp::observable<>::empty< + std::shared_ptr>())); + + init(); + + auto wrapper = + make_test_subscriber(synchronizer->on_commit_chain(), 0); + wrapper.subscribe(); + + synchronizer->process_commit(test_block); + + ASSERT_TRUE(wrapper.validate()); +} diff --git a/test/module/irohad/torii/CMakeLists.txt b/test/module/irohad/torii/CMakeLists.txt index 1abd0ee1d9..5c1b5c4f9e 100644 --- a/test/module/irohad/torii/CMakeLists.txt +++ b/test/module/irohad/torii/CMakeLists.txt @@ -31,6 +31,7 @@ target_link_libraries(torii_queries_test query_client server_runner processors + query_execution ) addtest(query_service_test query_service_test.cpp) diff --git a/test/module/irohad/torii/processor/CMakeLists.txt b/test/module/irohad/torii/processor/CMakeLists.txt index 3a909ef1ea..07aee2ade0 100644 --- a/test/module/irohad/torii/processor/CMakeLists.txt +++ b/test/module/irohad/torii/processor/CMakeLists.txt @@ -10,4 +10,5 @@ addtest(query_processor_test query_processor_test.cpp) target_link_libraries(query_processor_test processors shared_model_cryptography + query_execution ) diff --git a/test/module/irohad/torii/processor/query_processor_test.cpp b/test/module/irohad/torii/processor/query_processor_test.cpp index b842f0a3a5..ed5652de9f 100644 --- a/test/module/irohad/torii/processor/query_processor_test.cpp +++ b/test/module/irohad/torii/processor/query_processor_test.cpp @@ -15,21 +15,19 @@ * limitations under the License. */ -#include "validators/permissions.hpp" -#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" -#include "module/irohad/validation/validation_mocks.hpp" - -#include "framework/test_subscriber.hpp" -#include "model/queries/responses/error_response.hpp" -#include "model/query_execution.hpp" -#include "network/ordering_gate.hpp" -#include "torii/processor/query_processor_impl.hpp" - #include "backend/protobuf/query_responses/proto_error_query_response.hpp" #include "builders/protobuf/common_objects/proto_account_builder.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" #include "cryptography/keypair.hpp" +#include "execution/query_execution.hpp" +#include "framework/test_subscriber.hpp" +#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/irohad/validation/validation_mocks.hpp" #include "module/shared_model/builders/protobuf/test_query_builder.hpp" +#include "module/shared_model/builders/protobuf/test_query_response_builder.hpp" +#include "network/ordering_gate.hpp" +#include "torii/processor/query_processor_impl.hpp" +#include "validators/permissions.hpp" using namespace iroha; using namespace iroha::ametsuchi; @@ -54,9 +52,6 @@ class QueryProcessorTest : public ::testing::Test { shared_model::crypto::Keypair keypair = shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair(); - decltype(shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair()) - pair = - shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair(); std::vector signatories = { keypair.publicKey()}; }; @@ -70,8 +65,8 @@ TEST_F(QueryProcessorTest, QueryProcessorWhereInvokeInvalidQuery) { auto wsv_queries = std::make_shared(); auto block_queries = std::make_shared(); auto storage = std::make_shared(); - auto qpf = std::make_unique(wsv_queries, - block_queries); + auto qpf = + std::make_unique(wsv_queries, block_queries); iroha::torii::QueryProcessorImpl qpi(storage); @@ -83,15 +78,13 @@ TEST_F(QueryProcessorTest, QueryProcessorWhereInvokeInvalidQuery) { .build() .signAndAddSignature(keypair); - auto qry_resp = std::make_shared(); - auto account = model::Account(); - account.account_id = account_id; - qry_resp->account = account; std::shared_ptr shared_account = clone( shared_model::proto::AccountBuilder().accountId(account_id).build()); + auto role = "admin"; std::vector roles = {role}; - std::vector perms = {iroha::model::can_get_my_account}; + std::vector perms = { + shared_model::permissions::can_get_my_account}; EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_queries)); EXPECT_CALL(*storage, getBlockQuery()).WillRepeatedly(Return(block_queries)); @@ -118,14 +111,14 @@ TEST_F(QueryProcessorTest, QueryProcessorWhereInvokeInvalidQuery) { /** * @given account, ametsuchi queries and query processing factory * @when signed with wrong key - * @then Query Processor should return StatefullFailed + * @then Query Processor should return StatefulFailed */ TEST_F(QueryProcessorTest, QueryProcessorWithWrongKey) { auto wsv_queries = std::make_shared(); auto block_queries = std::make_shared(); auto storage = std::make_shared(); - auto qpf = std::make_unique(wsv_queries, - block_queries); + auto qpf = + std::make_unique(wsv_queries, block_queries); iroha::torii::QueryProcessorImpl qpi(storage); @@ -139,15 +132,12 @@ TEST_F(QueryProcessorTest, QueryProcessorWithWrongKey) { shared_model::crypto::DefaultCryptoAlgorithmType:: generateKeypair()); - auto qry_resp = std::make_shared(); - auto account = model::Account(); - account.account_id = account_id; - qry_resp->account = account; std::shared_ptr shared_account = clone( shared_model::proto::AccountBuilder().accountId(account_id).build()); auto role = "admin"; std::vector roles = {role}; - std::vector perms = {iroha::model::can_get_my_account}; + std::vector perms = { + shared_model::permissions::can_get_my_account}; EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_queries)); EXPECT_CALL(*storage, getBlockQuery()).WillRepeatedly(Return(block_queries)); diff --git a/test/module/irohad/torii/processor/transaction_processor_test.cpp b/test/module/irohad/torii/processor/transaction_processor_test.cpp index f0905112bf..6664d4ea8e 100644 --- a/test/module/irohad/torii/processor/transaction_processor_test.cpp +++ b/test/module/irohad/torii/processor/transaction_processor_test.cpp @@ -1,21 +1,10 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * 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 - * - * http://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. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include + #include "builders/protobuf/proposal.hpp" #include "builders/protobuf/transaction.hpp" #include "framework/test_subscriber.hpp" @@ -102,15 +91,7 @@ class TransactionProcessorTest : public ::testing::Test { TEST_F(TransactionProcessorTest, TransactionProcessorOnProposalTest) { std::vector txs; for (size_t i = 0; i < proposal_size; i++) { - auto &&tx = shared_model::proto::TransactionBuilder() - .txCounter(i + 1) - .createdTime(iroha::time::now()) - .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") - .build() - .signAndAddSignature( - shared_model::crypto::DefaultCryptoAlgorithmType:: - generateKeypair()); + auto &&tx = TestTransactionBuilder().createdTime(i).build(); txs.push_back(tx); status_map[tx.hash()] = status_builder.notReceived().txHash(tx.hash()).build(); @@ -131,11 +112,7 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnProposalTest) { // create proposal and notify about it auto proposal = std::make_shared( - shared_model::proto::ProposalBuilder() - .height(2) - .createdTime(iroha::time::now()) - .transactions(txs) - .build()); + TestProposalBuilder().transactions(txs).build()); prop_notifier.get_subscriber().on_next(proposal); prop_notifier.get_subscriber().on_completed(); @@ -156,15 +133,7 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnProposalTest) { TEST_F(TransactionProcessorTest, TransactionProcessorBlockCreatedTest) { std::vector txs; for (size_t i = 0; i < proposal_size; i++) { - auto &&tx = shared_model::proto::TransactionBuilder() - .txCounter(i + 1) - .createdTime(iroha::time::now()) - .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") - .build() - .signAndAddSignature( - shared_model::crypto::DefaultCryptoAlgorithmType:: - generateKeypair()); + auto &&tx = TestTransactionBuilder().createdTime(i).build(); txs.push_back(tx); status_map[tx.hash()] = status_builder.notReceived().txHash(tx.hash()).build(); @@ -187,21 +156,12 @@ TEST_F(TransactionProcessorTest, TransactionProcessorBlockCreatedTest) { // 1. Create proposal and notify transaction processor about it auto proposal = std::make_shared( - shared_model::proto::ProposalBuilder() - .height(2) - .createdTime(iroha::time::now()) - .transactions(txs) - .build()); + TestProposalBuilder().transactions(txs).build()); prop_notifier.get_subscriber().on_next(proposal); prop_notifier.get_subscriber().on_completed(); - auto block = TestBlockBuilder() - .height(1) - .createdTime(iroha::time::now()) - .transactions(txs) - .prevHash(shared_model::crypto::Hash(std::string(32, '0'))) - .build(); + auto block = TestBlockBuilder().transactions(txs).build(); // 2. Create block and notify transaction processor about it rxcpp::subjects::subject> @@ -231,15 +191,7 @@ TEST_F(TransactionProcessorTest, TransactionProcessorBlockCreatedTest) { TEST_F(TransactionProcessorTest, TransactionProcessorOnCommitTest) { std::vector txs; for (size_t i = 0; i < proposal_size; i++) { - auto &&tx = shared_model::proto::TransactionBuilder() - .txCounter(i + 1) - .createdTime(iroha::time::now()) - .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") - .build() - .signAndAddSignature( - shared_model::crypto::DefaultCryptoAlgorithmType:: - generateKeypair()); + auto &&tx = TestTransactionBuilder().createdTime(i).build(); txs.push_back(tx); status_map[tx.hash()] = status_builder.notReceived().txHash(tx.hash()).build(); @@ -263,21 +215,12 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnCommitTest) { // 1. Create proposal and notify transaction processor about it auto proposal = std::make_shared( - shared_model::proto::ProposalBuilder() - .height(2) - .createdTime(iroha::time::now()) - .transactions(txs) - .build()); + TestProposalBuilder().transactions(txs).build()); prop_notifier.get_subscriber().on_next(proposal); prop_notifier.get_subscriber().on_completed(); - auto block = TestBlockBuilder() - .height(1) - .createdTime(iroha::time::now()) - .transactions(txs) - .prevHash(shared_model::crypto::Hash(std::string(32, '0'))) - .build(); + auto block = TestBlockBuilder().transactions(txs).build(); // 2. Create block and notify transaction processor about it Commit single_commit = rxcpp::observable<>::just( @@ -302,15 +245,7 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnCommitTest) { TEST_F(TransactionProcessorTest, TransactionProcessorInvalidTxsTest) { std::vector block_txs; for (size_t i = 0; i < block_size; i++) { - auto &&tx = shared_model::proto::TransactionBuilder() - .txCounter(i + 1) - .createdTime(iroha::time::now()) - .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") - .build() - .signAndAddSignature( - shared_model::crypto::DefaultCryptoAlgorithmType:: - generateKeypair()); + auto &&tx = TestTransactionBuilder().createdTime(i).build(); block_txs.push_back(tx); status_map[tx.hash()] = status_builder.notReceived().txHash(tx.hash()).build(); @@ -320,15 +255,7 @@ TEST_F(TransactionProcessorTest, TransactionProcessorInvalidTxsTest) { invalid_txs; // transactions will be stateful invalid if appeared // in proposal but didn't appear in block for (size_t i = block_size; i < proposal_size; i++) { - auto &&tx = shared_model::proto::TransactionBuilder() - .txCounter(i + 1) - .createdTime(iroha::time::now()) - .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") - .build() - .signAndAddSignature( - shared_model::crypto::DefaultCryptoAlgorithmType:: - generateKeypair()); + auto &&tx = TestTransactionBuilder().createdTime(i).build(); invalid_txs.push_back(tx); status_map[tx.hash()] = status_builder.notReceived().txHash(tx.hash()).build(); @@ -350,21 +277,14 @@ TEST_F(TransactionProcessorTest, TransactionProcessorInvalidTxsTest) { }); auto proposal = std::make_shared( - shared_model::proto::ProposalBuilder() - .height(2) - .createdTime(iroha::time::now()) + TestProposalBuilder() .transactions(boost::join(block_txs, invalid_txs)) .build()); prop_notifier.get_subscriber().on_next(proposal); prop_notifier.get_subscriber().on_completed(); - auto block = TestBlockBuilder() - .height(1) - .createdTime(iroha::time::now()) - .transactions(block_txs) - .prevHash(shared_model::crypto::Hash(std::string(32, '0'))) - .build(); + auto block = TestBlockBuilder().transactions(block_txs).build(); Commit single_commit = rxcpp::observable<>::just( std::shared_ptr(clone(block))); diff --git a/test/module/irohad/torii/query_service_test.cpp b/test/module/irohad/torii/query_service_test.cpp index 8bddc449f8..b30ea1fc33 100644 --- a/test/module/irohad/torii/query_service_test.cpp +++ b/test/module/irohad/torii/query_service_test.cpp @@ -17,8 +17,11 @@ #include "torii/query_service.hpp" #include "backend/protobuf/query_responses/proto_query_response.hpp" +#include "builders/protobuf/common_objects/proto_account_builder.hpp" #include "builders/protobuf/queries.hpp" #include "module/irohad/torii/torii_mocks.hpp" +#include "module/shared_model/builders/protobuf/test_query_response_builder.hpp" +#include "utils/query_error_response_visitor.hpp" using namespace torii; @@ -48,19 +51,16 @@ class QueryServiceTest : public ::testing::Test { shared_model::crypto::DefaultCryptoAlgorithmType:: generateKeypair())); - // TODO: IR-1041 Update to query response builders (kamilsa, 04.03.2018) - protocol::QueryResponse response; - response.set_query_hash( - shared_model::crypto::toBinaryString(query->hash())); - auto account_response = response.mutable_account_response(); - account_response->add_account_roles("user"); - auto account = account_response->mutable_account(); - account->set_domain_id("ru"); - account->set_account_id("a"); - account->set_quorum(2); - - model_response = std::make_shared( - std::move(response)); + auto account = shared_model::proto::AccountBuilder() + .accountId("a") + .domainId("ru") + .quorum(2) + .build(); + + model_response = clone(TestQueryResponseBuilder() + .accountResponse(account, {"user"}) + .queryHash(query->hash()) + .build()); } void init() { @@ -99,8 +99,8 @@ TEST_F(QueryServiceTest, ValidWhenUniqueHash) { protocol::QueryResponse response; query_service->Find(query->getTransport(), response); - ASSERT_EQ(response.SerializeAsString(), - model_response->getTransport().SerializeAsString()); + auto resp = shared_model::proto::QueryResponse(response); + ASSERT_EQ(resp, *model_response); } /** @@ -129,8 +129,11 @@ TEST_F(QueryServiceTest, InvalidWhenUniqueHash) { protocol::QueryResponse response; query_service->Find(query->getTransport(), response); ASSERT_TRUE(response.has_error_response()); - ASSERT_EQ(response.error_response().reason(), - protocol::ErrorResponse::NOT_SUPPORTED); + auto resp = shared_model::proto::QueryResponse(response); + ASSERT_TRUE(boost::apply_visitor( + shared_model::interface::QueryErrorResponseChecker< + shared_model::interface::NotSupportedErrorResponse>(), + resp.get())); } /** @@ -154,6 +157,9 @@ TEST_F(QueryServiceTest, InvalidWhenDuplicateHash) { // second call of the same query query_service->Find(query->getTransport(), response); ASSERT_TRUE(response.has_error_response()); - ASSERT_EQ(response.error_response().reason(), - protocol::ErrorResponse::STATELESS_INVALID); + auto resp = shared_model::proto::QueryResponse(response); + ASSERT_TRUE(boost::apply_visitor( + shared_model::interface::QueryErrorResponseChecker< + shared_model::interface::StatelessFailedErrorResponse>(), + resp.get())); } diff --git a/test/module/irohad/torii/torii_mocks.hpp b/test/module/irohad/torii/torii_mocks.hpp index 0889042328..10fb2ded6e 100644 --- a/test/module/irohad/torii/torii_mocks.hpp +++ b/test/module/irohad/torii/torii_mocks.hpp @@ -24,6 +24,7 @@ namespace iroha { namespace torii { + class MockQueryProcessor : public QueryProcessor { public: MOCK_METHOD1(queryHandle, diff --git a/test/module/irohad/torii/torii_queries_test.cpp b/test/module/irohad/torii/torii_queries_test.cpp index 3a23bca782..5ac4106ac1 100644 --- a/test/module/irohad/torii/torii_queries_test.cpp +++ b/test/module/irohad/torii/torii_queries_test.cpp @@ -14,27 +14,25 @@ See the License for the specific language governing permissions and limitations under the License. */ +#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/irohad/network/network_mocks.hpp" +#include "module/irohad/torii/torii_mocks.hpp" +#include "module/irohad/validation/validation_mocks.hpp" + #include "builders/protobuf/common_objects/proto_account_asset_builder.hpp" #include "builders/protobuf/common_objects/proto_account_builder.hpp" #include "builders/protobuf/common_objects/proto_amount_builder.hpp" #include "builders/protobuf/common_objects/proto_asset_builder.hpp" -#include "generator/generator.hpp" -#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" -#include "module/irohad/network/network_mocks.hpp" -#include "module/irohad/validation/validation_mocks.hpp" +#include "builders/protobuf/queries.hpp" +#include "module/shared_model/builders/protobuf/test_query_builder.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" -// to compare pb amount and iroha amount -#include "model/converters/pb_common.hpp" #include "main/server_runner.hpp" -#include "validators/permissions.hpp" #include "torii/processor/query_processor_impl.hpp" #include "torii/query_client.hpp" #include "torii/query_service.hpp" - -#include "builders/protobuf/queries.hpp" -#include "cryptography/crypto_provider/crypto_defaults.hpp" -#include "module/shared_model/builders/protobuf/test_query_builder.hpp" +#include "utils/query_error_response_visitor.hpp" +#include "validators/permissions.hpp" constexpr const char *Ip = "0.0.0.0"; constexpr int Port = 50051; @@ -47,6 +45,7 @@ using ::testing::AtLeast; using ::testing::Return; using namespace iroha::ametsuchi; +using namespace iroha::torii; using wTransaction = std::shared_ptr; @@ -77,20 +76,14 @@ class ToriiQueriesTest : public testing::Test { } ServerRunner *runner; - decltype(shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair()) - pair = - shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair(); + shared_model::crypto::Keypair pair = + shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair(); std::vector signatories = { pair.publicKey()}; std::shared_ptr wsv_query; std::shared_ptr block_query; std::shared_ptr storage; - - // just random hex strings - const std::string pubkey_test = generator::random_blob<16>(0).to_hexstring(); - const std::string signature_test = - generator::random_blob<32>(0).to_hexstring(); }; /** @@ -100,12 +93,11 @@ class ToriiQueriesTest : public testing::Test { */ TEST_F(ToriiQueriesTest, QueryClient) { iroha::protocol::QueryResponse response; - auto query = iroha::protocol::Query(); - - query.mutable_payload()->set_creator_account_id("accountA"); - query.mutable_payload()->mutable_get_account()->set_account_id("accountB"); - query.mutable_signature()->set_pubkey(pubkey_test); - query.mutable_signature()->set_signature(signature_test); + auto query = TestUnsignedQueryBuilder() + .creatorAccountId("accountA") + .getAccount("accountB") + .build() + .signAndAddSignature(pair); auto client1 = torii_utils::QuerySyncClient(Ip, Port); // Copy ctor @@ -116,7 +108,7 @@ TEST_F(ToriiQueriesTest, QueryClient) { torii_utils::QuerySyncClient client4(std::move(client3)); // move assignment auto client5 = std::move(client4); - auto stat = client5.Find(query, response); + auto stat = client5.Find(query.getTransport(), response); ASSERT_TRUE(stat.ok()); } @@ -126,19 +118,23 @@ TEST_F(ToriiQueriesTest, QueryClient) { TEST_F(ToriiQueriesTest, FindWhenResponseInvalid) { iroha::protocol::QueryResponse response; - auto query = iroha::protocol::Query(); - - query.mutable_payload()->set_creator_account_id("accountA"); - query.mutable_payload()->mutable_get_account()->set_account_id("accountB"); - query.mutable_signature()->set_pubkey(pubkey_test); - query.mutable_signature()->set_signature(signature_test); - - auto stat = torii_utils::QuerySyncClient(Ip, Port).Find(query, response); + auto query = TestUnsignedQueryBuilder() + .creatorAccountId("accountA") + .getAccount("accountB") + .build() + .signAndAddSignature(pair); + + auto stat = torii_utils::QuerySyncClient(Ip, Port).Find(query.getTransport(), + response); + auto resp = shared_model::proto::QueryResponse(response); ASSERT_TRUE(stat.ok()); // Must return Error Response - ASSERT_EQ(response.error_response().reason(), - iroha::model::ErrorResponse::STATELESS_INVALID); - ASSERT_EQ(iroha::hash(query).to_string(), response.query_hash()); + ASSERT_TRUE(boost::apply_visitor( + shared_model::interface::QueryErrorResponseChecker< + shared_model::interface::StatelessFailedErrorResponse>(), + resp.get())); + + ASSERT_EQ(query.hash(), resp.queryHash()); } /** @@ -146,15 +142,15 @@ TEST_F(ToriiQueriesTest, FindWhenResponseInvalid) { */ TEST_F(ToriiQueriesTest, FindAccountWhenNoGrantPermissions) { - // TODO: kamilsa 19.02.2017 remove old model - iroha::model::Account account; - account.account_id = "b@domain"; + auto account = + shared_model::proto::AccountBuilder().accountId("b@domain").build(); auto creator = "a@domain"; - EXPECT_CALL( - *wsv_query, - hasAccountGrantablePermission( - creator, account.account_id, iroha::model::can_get_my_account)) + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + creator, + account.accountId(), + shared_model::permissions::can_get_my_account)) .WillOnce(Return(false)); EXPECT_CALL(*wsv_query, getSignatories(creator)) @@ -168,7 +164,7 @@ TEST_F(ToriiQueriesTest, FindAccountWhenNoGrantPermissions) { .creatorAccountId(creator) .queryCounter(1) .createdTime(iroha::time::now()) - .getAccount(account.account_id) + .getAccount(account.accountId()) .build() .signAndAddSignature(pair); @@ -176,13 +172,15 @@ TEST_F(ToriiQueriesTest, FindAccountWhenNoGrantPermissions) { model_query.getTransport(), response); ASSERT_TRUE(stat.ok()); - + auto resp = shared_model::proto::QueryResponse(response); // Must be invalid due to failed stateful validation caused by no permission // to read account - ASSERT_EQ(response.error_response().reason(), - iroha::model::ErrorResponse::STATEFUL_INVALID); - ASSERT_EQ(iroha::hash(model_query.getTransport()).to_string(), - response.query_hash()); + ASSERT_TRUE(boost::apply_visitor( + shared_model::interface::QueryErrorResponseChecker< + shared_model::interface::StatefulFailedErrorResponse>(), + resp.get())); + + ASSERT_EQ(model_query.hash(), resp.queryHash()); } TEST_F(ToriiQueriesTest, FindAccountWhenHasReadPermissions) { @@ -193,10 +191,11 @@ TEST_F(ToriiQueriesTest, FindAccountWhenHasReadPermissions) { EXPECT_CALL(*wsv_query, getSignatories(creator)) .WillRepeatedly(Return(signatories)); - EXPECT_CALL( - *wsv_query, - hasAccountGrantablePermission( - creator, accountB->accountId(), iroha::model::can_get_my_account)) + EXPECT_CALL(*wsv_query, + hasAccountGrantablePermission( + creator, + accountB->accountId(), + shared_model::permissions::can_get_my_account)) .WillOnce(Return(true)); // Should be called once, after successful stateful validation @@ -218,14 +217,18 @@ TEST_F(ToriiQueriesTest, FindAccountWhenHasReadPermissions) { auto stat = torii_utils::QuerySyncClient(Ip, Port).Find( model_query.getTransport(), response); + auto resp = shared_model::proto::QueryResponse(response); + ASSERT_TRUE(stat.ok()); // Should not return Error Response because tx is stateless and stateful valid ASSERT_FALSE(response.has_error_response()); - ASSERT_EQ(response.account_response().account().account_id(), - accountB->accountId()); - ASSERT_EQ(response.account_response().account_roles().size(), 1); - ASSERT_EQ(iroha::hash(model_query.getTransport()).to_string(), - response.query_hash()); + + auto account_resp = boost::get>(resp.get()); + + ASSERT_EQ(account_resp->account().accountId(), accountB->accountId()); + ASSERT_EQ(account_resp->roles().size(), 1); + ASSERT_EQ(model_query.hash(), resp.queryHash()); } TEST_F(ToriiQueriesTest, FindAccountWhenHasRolePermission) { @@ -239,7 +242,8 @@ TEST_F(ToriiQueriesTest, FindAccountWhenHasRolePermission) { std::vector roles = {"test"}; EXPECT_CALL(*wsv_query, getAccountRoles(creator)) .WillRepeatedly(Return(roles)); - std::vector perm = {iroha::model::can_get_my_account}; + std::vector perm = { + shared_model::permissions::can_get_my_account}; EXPECT_CALL(*wsv_query, getRolePermissions("test")).WillOnce(Return(perm)); iroha::protocol::QueryResponse response; @@ -254,16 +258,17 @@ TEST_F(ToriiQueriesTest, FindAccountWhenHasRolePermission) { auto stat = torii_utils::QuerySyncClient(Ip, Port).Find( model_query.getTransport(), response); + auto resp = shared_model::proto::QueryResponse(response); ASSERT_TRUE(stat.ok()); // Should not return Error Response because tx is stateless and stateful valid ASSERT_FALSE(response.has_error_response()); - // ASSERT_EQ(response.account_detail_response().detail(), "value"); - ASSERT_EQ(response.account_response().account().account_id(), - account->accountId()); - ASSERT_EQ(response.account_response().account().domain_id(), - account->domainId()); - ASSERT_EQ(iroha::hash(model_query.getTransport()).to_string(), - response.query_hash()); + + auto detail_resp = boost::get>(resp.get()); + + ASSERT_EQ(detail_resp->account().accountId(), account->accountId()); + ASSERT_EQ(detail_resp->account().domainId(), account->domainId()); + ASSERT_EQ(model_query.hash(), resp.queryHash()); } /** @@ -276,9 +281,10 @@ TEST_F(ToriiQueriesTest, FindAccountAssetWhenNoGrantPermissions) { EXPECT_CALL(*wsv_query, getSignatories(creator)) .WillRepeatedly(Return(signatories)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - creator, accountb_id, iroha::model::can_get_my_acc_ast)) + EXPECT_CALL( + *wsv_query, + hasAccountGrantablePermission( + creator, accountb_id, shared_model::permissions::can_get_my_acc_ast)) .WillOnce(Return(false)); EXPECT_CALL(*wsv_query, getAccountRoles(creator)) .WillOnce(Return(boost::none)); @@ -298,13 +304,16 @@ TEST_F(ToriiQueriesTest, FindAccountAssetWhenNoGrantPermissions) { auto stat = torii_utils::QuerySyncClient(Ip, Port).Find( model_query.getTransport(), response); + auto resp = shared_model::proto::QueryResponse(response); ASSERT_TRUE(stat.ok()); // Must be invalid due to failed stateful validation caused by no permission // to read account asset - ASSERT_EQ(response.error_response().reason(), - iroha::model::ErrorResponse::STATEFUL_INVALID); - ASSERT_EQ(iroha::hash(model_query.getTransport()).to_string(), - response.query_hash()); + ASSERT_TRUE(boost::apply_visitor( + shared_model::interface::QueryErrorResponseChecker< + shared_model::interface::StatefulFailedErrorResponse>(), + resp.get())); + + ASSERT_EQ(model_query.hash(), resp.queryHash()); } TEST_F(ToriiQueriesTest, FindAccountAssetWhenHasRolePermissions) { @@ -332,7 +341,8 @@ TEST_F(ToriiQueriesTest, FindAccountAssetWhenHasRolePermissions) { .WillRepeatedly(Return(signatories)); std::vector roles = {"test"}; EXPECT_CALL(*wsv_query, getAccountRoles(creator)).WillOnce(Return(roles)); - std::vector perm = {iroha::model::can_get_my_acc_ast}; + std::vector perm = { + shared_model::permissions::can_get_my_acc_ast}; EXPECT_CALL(*wsv_query, getRolePermissions("test")).WillOnce(Return(perm)); EXPECT_CALL(*wsv_query, getAccountAsset(_, _)) .WillOnce(Return(account_asset)); @@ -356,18 +366,15 @@ TEST_F(ToriiQueriesTest, FindAccountAssetWhenHasRolePermissions) { // Should not return Error Response because tx is stateless and stateful valid ASSERT_FALSE(response.has_error_response()); + auto resp = shared_model::proto::QueryResponse(response); + auto asset_resp = boost::get>(resp.get()); + // Check if the fields in account asset response are correct - ASSERT_EQ(response.account_assets_response().account_asset().asset_id(), - account_asset->assetId()); - ASSERT_EQ(response.account_assets_response().account_asset().account_id(), - account_asset->accountId()); - auto iroha_amount_asset = iroha::model::converters::deserializeAmount( - response.account_assets_response().account_asset().balance()); - ASSERT_EQ( - iroha_amount_asset, - *std::unique_ptr(account_asset->balance().makeOldModel())); - ASSERT_EQ(iroha::hash(model_query.getTransport()).to_string(), - response.query_hash()); + ASSERT_EQ(asset_resp->accountAsset().assetId(), account_asset->assetId()); + ASSERT_EQ(asset_resp->accountAsset().accountId(), account_asset->accountId()); + ASSERT_EQ(asset_resp->accountAsset().balance(), account_asset->balance()); + ASSERT_EQ(model_query.hash(), resp.queryHash()); } /** @@ -386,7 +393,9 @@ TEST_F(ToriiQueriesTest, FindSignatoriesWhenNoGrantPermissions) { .WillRepeatedly(Return(signatories)); EXPECT_CALL(*wsv_query, hasAccountGrantablePermission( - creator, "b@domain", iroha::model::can_get_my_signatories)) + creator, + "b@domain", + shared_model::permissions::can_get_my_signatories)) .WillOnce(Return(false)); EXPECT_CALL(*wsv_query, getAccountRoles(creator)) .WillOnce(Return(boost::none)); @@ -406,10 +415,13 @@ TEST_F(ToriiQueriesTest, FindSignatoriesWhenNoGrantPermissions) { ASSERT_TRUE(stat.ok()); // Must be invalid due to failed stateful validation caused by no permission // to read account - ASSERT_EQ(response.error_response().reason(), - iroha::model::ErrorResponse::STATEFUL_INVALID); - ASSERT_EQ(iroha::hash(model_query.getTransport()).to_string(), - response.query_hash()); + auto resp = shared_model::proto::QueryResponse(response); + ASSERT_TRUE(boost::apply_visitor( + shared_model::interface::QueryErrorResponseChecker< + shared_model::interface::StatefulFailedErrorResponse>(), + resp.get())); + + ASSERT_EQ(model_query.hash(), resp.queryHash()); } TEST_F(ToriiQueriesTest, FindSignatoriesHasRolePermissions) { @@ -425,7 +437,8 @@ TEST_F(ToriiQueriesTest, FindSignatoriesHasRolePermissions) { std::vector roles = {"test"}; EXPECT_CALL(*wsv_query, getAccountRoles(creator)).WillOnce(Return(roles)); - std::vector perm = {iroha::model::can_get_my_signatories}; + std::vector perm = { + shared_model::permissions::can_get_my_signatories}; EXPECT_CALL(*wsv_query, getRolePermissions("test")).WillOnce(Return(perm)); iroha::protocol::QueryResponse response; @@ -452,8 +465,7 @@ TEST_F(ToriiQueriesTest, FindSignatoriesHasRolePermissions) { ASSERT_FALSE(response.has_error_response()); // check if fields in response are valid ASSERT_EQ(*resp_pubkey, signatories.back()); - ASSERT_EQ(iroha::hash(model_query.getTransport()).to_string(), - response.query_hash()); + ASSERT_EQ(model_query.hash(), shared_response.queryHash()); } /** @@ -461,17 +473,15 @@ TEST_F(ToriiQueriesTest, FindSignatoriesHasRolePermissions) { */ TEST_F(ToriiQueriesTest, FindTransactionsWhenValid) { - // TODO 19.02.2018 kamilsa remove old model - iroha::model::Account account; - account.account_id = "accountA"; + auto account = + shared_model::proto::AccountBuilder().accountId("accountA").build(); auto creator = "a@domain"; - auto txs_observable = rxcpp::observable<>::iterate([account] { + auto txs_observable = rxcpp::observable<>::iterate([&account] { std::vector result; for (size_t i = 0; i < 3; ++i) { std::shared_ptr current = clone(TestTransactionBuilder() - .creatorAccountId(account.account_id) - .txCounter(i) + .creatorAccountId(account.accountId()) .build()); result.push_back(current); } @@ -482,7 +492,8 @@ TEST_F(ToriiQueriesTest, FindTransactionsWhenValid) { .WillRepeatedly(Return(signatories)); std::vector roles = {"test"}; EXPECT_CALL(*wsv_query, getAccountRoles(creator)).WillOnce(Return(roles)); - std::vector perm = {iroha::model::can_get_my_acc_txs}; + std::vector perm = { + shared_model::permissions::can_get_my_acc_txs}; EXPECT_CALL(*wsv_query, getRolePermissions("test")).WillOnce(Return(perm)); EXPECT_CALL(*block_query, getAccountTransactions(creator)) .WillOnce(Return(txs_observable)); @@ -502,19 +513,15 @@ TEST_F(ToriiQueriesTest, FindTransactionsWhenValid) { ASSERT_TRUE(stat.ok()); // Should not return Error Response because tx is stateless and stateful valid ASSERT_FALSE(response.has_error_response()); - for (auto i = 0; i < response.transactions_response().transactions_size(); - i++) { - ASSERT_EQ(response.transactions_response() - .transactions(i) - .payload() - .creator_account_id(), - account.account_id); - ASSERT_EQ( - response.transactions_response().transactions(i).payload().tx_counter(), - i); + auto resp = shared_model::proto::QueryResponse(response); + auto tx_resp = boost::get>(resp.get()); + + const auto &txs = tx_resp->transactions(); + for (auto i = 0ul; i < txs.size(); i++) { + ASSERT_EQ(txs.at(i)->creatorAccountId(), account.accountId()); } - ASSERT_EQ(iroha::hash(model_query.getTransport()).to_string(), - response.query_hash()); + ASSERT_EQ(model_query.hash(), resp.queryHash()); } TEST_F(ToriiQueriesTest, FindManyTimesWhereQueryServiceSync) { @@ -533,10 +540,13 @@ TEST_F(ToriiQueriesTest, FindManyTimesWhereQueryServiceSync) { auto stat = client.Find(model_query.getTransport(), response); ASSERT_TRUE(stat.ok()); + auto resp = shared_model::proto::QueryResponse(response); // Must return Error Response - ASSERT_EQ(response.error_response().reason(), - iroha::model::ErrorResponse::STATELESS_INVALID); - ASSERT_EQ(iroha::hash(model_query.getTransport()).to_string(), - response.query_hash()); + ASSERT_TRUE(boost::apply_visitor( + shared_model::interface::QueryErrorResponseChecker< + shared_model::interface::StatelessFailedErrorResponse>(), + resp.get())); + + ASSERT_EQ(model_query.hash(), resp.queryHash()); } } diff --git a/test/module/irohad/torii/torii_service_test.cpp b/test/module/irohad/torii/torii_service_test.cpp index 865948544d..7f8d3fa1f2 100644 --- a/test/module/irohad/torii/torii_service_test.cpp +++ b/test/module/irohad/torii/torii_service_test.cpp @@ -15,16 +15,16 @@ limitations under the License. */ #include "builders/protobuf/block.hpp" -#include "builders/protobuf/transaction.hpp" #include "builders/protobuf/proposal.hpp" +#include "builders/protobuf/transaction.hpp" #include "endpoint.pb.h" #include "main/server_runner.hpp" -#include "model/converters/pb_transaction_factory.hpp" -#include "model/sha3_hash.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" #include "module/shared_model/builders/protobuf/test_block_builder.hpp" #include "module/shared_model/builders/protobuf/test_proposal_builder.hpp" +#include "module/shared_model/builders/protobuf/test_query_response_builder.hpp" +#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" #include "torii/command_client.hpp" #include "torii/command_service.hpp" #include "torii/processor/transaction_processor_impl.hpp" @@ -86,8 +86,6 @@ class ToriiServiceTest : public testing::Test { auto tx_processor = std::make_shared(pcsMock); - auto pb_tx_factory = - std::make_shared(); EXPECT_CALL(*block_query, getTxByHashSync(_)) .WillRepeatedly(Return(boost::none)); @@ -127,7 +125,7 @@ class ToriiServiceTest : public testing::Test { */ TEST_F(ToriiServiceTest, CommandClient) { iroha::protocol::TxStatusRequest tx_request; - tx_request.set_tx_hash(std::string('1', 32)); + tx_request.set_tx_hash(std::string(32, '1')); iroha::protocol::ToriiResponse toriiResponse; auto client1 = torii::CommandSyncClient(Ip, Port); @@ -149,22 +147,16 @@ TEST_F(ToriiServiceTest, CommandClient) { * @then ensure those are not received */ TEST_F(ToriiServiceTest, StatusWhenTxWasNotReceivedBlocking) { - std::vector txs; - std::vector tx_hashes; - - iroha::model::converters::PbTransactionFactory tx_factory; + std::vector txs; + std::vector tx_hashes; // create transactions, but do not send them for (size_t i = 0; i < TimesToriiBlocking; ++i) { - auto new_tx = iroha::protocol::Transaction(); - auto payload = new_tx.mutable_payload(); - payload->set_tx_counter(i); - payload->set_creator_account_id("accountA"); - - auto iroha_tx = tx_factory.deserialize(new_tx); - txs.push_back(*iroha_tx); - auto tx_hash = iroha::hash(*iroha_tx); - tx_hashes.push_back(tx_hash.to_string()); + auto tx = TestTransactionBuilder() + .creatorAccountId("accountA") + .build(); + txs.push_back(tx); + tx_hashes.push_back(tx.hash()); } // get statuses of unsent transactions @@ -172,10 +164,10 @@ TEST_F(ToriiServiceTest, StatusWhenTxWasNotReceivedBlocking) { for (size_t i = 0; i < TimesToriiBlocking; ++i) { iroha::protocol::TxStatusRequest tx_request; - tx_request.set_tx_hash(tx_hashes.at(i)); + tx_request.set_tx_hash( + shared_model::crypto::toBinaryString(tx_hashes.at(i))); iroha::protocol::ToriiResponse toriiResponse; client.Status(tx_request, toriiResponse); - ASSERT_EQ(toriiResponse.tx_status(), iroha::protocol::TxStatus::NOT_RECEIVED); } @@ -196,7 +188,7 @@ TEST_F(ToriiServiceTest, StatusWhenTxWasNotReceivedBlocking) { */ TEST_F(ToriiServiceTest, StatusWhenBlocking) { std::vector txs; - std::vector tx_hashes; + std::vector tx_hashes; auto client1 = torii::CommandSyncClient(Ip, Port); @@ -205,26 +197,28 @@ TEST_F(ToriiServiceTest, StatusWhenBlocking) { for (size_t i = 0; i < TimesToriiBlocking; ++i) { auto shm_tx = shared_model::proto::TransactionBuilder() .creatorAccountId(account_id) - .txCounter(i + 1) .createdTime(iroha::time::now()) .setAccountQuorum(account_id, 2) .build() .signAndAddSignature( shared_model::crypto::DefaultCryptoAlgorithmType:: generateKeypair()); - auto new_tx = shm_tx.getTransport(); + const auto &new_tx = shm_tx.getTransport(); auto stat = client1.Torii(new_tx); txs.push_back(shm_tx); - auto tx_hash = shared_model::crypto::toBinaryString(shm_tx.hash()); - tx_hashes.push_back(tx_hash); + tx_hashes.push_back(shm_tx.hash()); ASSERT_TRUE(stat.ok()); } // create proposal from these transactions auto proposal = std::make_shared( - TestProposalBuilder().height(1).createdTime(iroha::time::now()).transactions(txs).build()); + TestProposalBuilder() + .height(1) + .createdTime(iroha::time::now()) + .transactions(txs) + .build()); prop_notifier_.get_subscriber().on_next(proposal); torii::CommandSyncClient client2(client1); @@ -232,7 +226,8 @@ TEST_F(ToriiServiceTest, StatusWhenBlocking) { // check if stateless validation passed for (size_t i = 0; i < TimesToriiBlocking; ++i) { iroha::protocol::TxStatusRequest tx_request; - tx_request.set_tx_hash(tx_hashes.at(i)); + tx_request.set_tx_hash( + shared_model::crypto::toBinaryString(tx_hashes.at(i))); iroha::protocol::ToriiResponse toriiResponse; client2.Status(tx_request, toriiResponse); @@ -242,13 +237,13 @@ TEST_F(ToriiServiceTest, StatusWhenBlocking) { // create block from the all transactions but the last one txs.pop_back(); - auto block = std::make_shared( - TestBlockBuilder() - .transactions(txs) - .height(1) - .createdTime(0) - .prevHash(shared_model::crypto::Hash(std::string(32, '0'))) - .build()); + auto block = + clone(TestBlockBuilder() + .transactions(txs) + .height(1) + .createdTime(0) + .prevHash(shared_model::crypto::Hash(std::string(32, '0'))) + .build()); // create commit from block notifier's observable rxcpp::subjects::subject> @@ -257,13 +252,14 @@ TEST_F(ToriiServiceTest, StatusWhenBlocking) { // invoke on next of commit_notifier by sending new block to commit commit_notifier_.get_subscriber().on_next(commit); - block_notifier_.get_subscriber().on_next(block); + block_notifier_.get_subscriber().on_next(std::move(block)); auto client3 = client2; // check if all transactions but the last one passed stateful validation for (size_t i = 0; i < TimesToriiBlocking - 1; ++i) { iroha::protocol::TxStatusRequest tx_request; - tx_request.set_tx_hash(tx_hashes.at(i)); + tx_request.set_tx_hash( + shared_model::crypto::toBinaryString(tx_hashes.at(i))); iroha::protocol::ToriiResponse toriiResponse; client3.Status(tx_request, toriiResponse); @@ -278,7 +274,8 @@ TEST_F(ToriiServiceTest, StatusWhenBlocking) { // check if all transactions but the last have committed state for (size_t i = 0; i < TimesToriiBlocking - 1; ++i) { iroha::protocol::TxStatusRequest tx_request; - tx_request.set_tx_hash(tx_hashes.at(i)); + tx_request.set_tx_hash( + shared_model::crypto::toBinaryString(tx_hashes.at(i))); iroha::protocol::ToriiResponse toriiResponse; client4.Status(tx_request, toriiResponse); @@ -288,7 +285,8 @@ TEST_F(ToriiServiceTest, StatusWhenBlocking) { torii::CommandSyncClient client5(client4); // check if the last transaction from txs has failed stateful validation iroha::protocol::TxStatusRequest last_tx_request; - last_tx_request.set_tx_hash(tx_hashes.at(TimesToriiBlocking - 1)); + last_tx_request.set_tx_hash(shared_model::crypto::toBinaryString( + tx_hashes.at(TimesToriiBlocking - 1))); iroha::protocol::ToriiResponse stful_invalid_response; client5.Status(last_tx_request, stful_invalid_response); ASSERT_EQ(stful_invalid_response.tx_status(), @@ -302,19 +300,13 @@ TEST_F(ToriiServiceTest, StatusWhenBlocking) { */ TEST_F(ToriiServiceTest, CheckHash) { // given - std::vector tx_hashes; + std::vector tx_hashes; const int tx_num = 10; - iroha::model::converters::PbTransactionFactory tx_factory; - // create transactions, but do not send them for (size_t i = 0; i < tx_num; ++i) { - auto new_tx = iroha::protocol::Transaction(); - auto payload = new_tx.mutable_payload(); - payload->set_tx_counter(i); - auto tx = tx_factory.deserialize(new_tx); - - tx_hashes.push_back(iroha::hash(*tx).to_string()); + auto tx = TestTransactionBuilder().build(); + tx_hashes.push_back(tx.hash()); } auto client = torii::CommandSyncClient(Ip, Port); @@ -322,12 +314,13 @@ TEST_F(ToriiServiceTest, CheckHash) { // get statuses of transactions for (auto &hash : tx_hashes) { iroha::protocol::TxStatusRequest tx_request; - tx_request.set_tx_hash(hash); + tx_request.set_tx_hash(shared_model::crypto::toBinaryString(hash)); iroha::protocol::ToriiResponse toriiResponse; // when client.Status(tx_request, toriiResponse); // then - ASSERT_EQ(toriiResponse.tx_hash(), hash); + ASSERT_EQ(toriiResponse.tx_hash(), + shared_model::crypto::toBinaryString(hash)); } } @@ -341,16 +334,8 @@ TEST_F(ToriiServiceTest, CheckHash) { TEST_F(ToriiServiceTest, StreamingFullPipelineTest) { using namespace shared_model; - iroha::model::converters::PbTransactionFactory tx_factory; auto client = torii::CommandSyncClient(Ip, Port); - - auto new_tx = iroha::protocol::Transaction(); - auto payload = new_tx.mutable_payload(); - payload->set_tx_counter(1); - payload->set_creator_account_id("accountA"); - auto iroha_tx = proto::TransactionBuilder() - .txCounter(1) .creatorAccountId("a@domain") .setAccountQuorum("a@domain", 2) .createdTime(iroha::time::now()) @@ -369,7 +354,7 @@ TEST_F(ToriiServiceTest, StreamingFullPipelineTest) { client.StatusStream(tx_request, torii_response); }); - client.Torii(new_tx); + client.Torii(iroha_tx.getTransport()); std::vector txs; txs.push_back(iroha_tx); @@ -381,14 +366,13 @@ TEST_F(ToriiServiceTest, StreamingFullPipelineTest) { .build()); prop_notifier_.get_subscriber().on_next(proposal); - auto block = std::make_shared( - proto::BlockBuilder() - .height(1) - .createdTime(iroha::time::now()) - .transactions(txs) - .prevHash(crypto::Hash(std::string(32, '0'))) - .build() - .signAndAddSignature(keypair)); + auto block = clone(proto::BlockBuilder() + .height(1) + .createdTime(iroha::time::now()) + .transactions(txs) + .prevHash(crypto::Hash(std::string(32, '0'))) + .build() + .signAndAddSignature(keypair)); // create commit from block notifier's observable rxcpp::subjects::subject> @@ -397,12 +381,12 @@ TEST_F(ToriiServiceTest, StreamingFullPipelineTest) { // invoke on next of commit_notifier by sending new block to commit commit_notifier_.get_subscriber().on_next(commit); - block_notifier_.get_subscriber().on_next(block); + block_notifier_.get_subscriber().on_next(std::move(block)); block_notifier_.get_subscriber().on_completed(); t.join(); - ASSERT_GE(torii_response.size(), 3); + ASSERT_GE(torii_response.size(), 2); ASSERT_EQ(torii_response.back().tx_status(), iroha::protocol::TxStatus::COMMITTED); } diff --git a/test/module/irohad/validation/CMakeLists.txt b/test/module/irohad/validation/CMakeLists.txt index 49c6c12e7c..c679cf40a4 100644 --- a/test/module/irohad/validation/CMakeLists.txt +++ b/test/module/irohad/validation/CMakeLists.txt @@ -15,9 +15,10 @@ # limitations under the License. # -addtest(query_execution query_execution.cpp) -target_link_libraries(query_execution - model +addtest(query_execution_test query_execution.cpp) +target_link_libraries(query_execution_test + query_execution + shared_model_interfaces shared_model_stateless_validation ) diff --git a/test/module/irohad/validation/chain_validation_test.cpp b/test/module/irohad/validation/chain_validation_test.cpp index 8908b168c5..f14c4df853 100644 --- a/test/module/irohad/validation/chain_validation_test.cpp +++ b/test/module/irohad/validation/chain_validation_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. * http://soramitsu.co.jp * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -76,8 +76,7 @@ TEST_F(ChainValidationTest, ValidCase) { // Valid previous hash, has supermajority, correct peers subset => valid auto block = getBlockBuilder().build(); - EXPECT_CALL(*supermajority_checker, - hasSupermajority(testing::Ref(block.signatures()), _)) + EXPECT_CALL(*supermajority_checker, hasSupermajority(block.signatures(), _)) .WillOnce(Return(true)); EXPECT_CALL(*query, getPeers()).WillOnce(Return(peers)); @@ -118,8 +117,7 @@ TEST_F(ChainValidationTest, FailWhenNoSupermajority) { // Valid previous hash, no supermajority, correct peers subset => invalid auto block = getBlockBuilder().build(); - EXPECT_CALL(*supermajority_checker, - hasSupermajority(testing::Ref(block.signatures()), _)) + EXPECT_CALL(*supermajority_checker, hasSupermajority(block.signatures(), _)) .WillOnce(Return(false)); EXPECT_CALL(*query, getPeers()).WillOnce(Return(peers)); diff --git a/test/module/irohad/validation/query_execution.cpp b/test/module/irohad/validation/query_execution.cpp index 996882270b..ba9b12177f 100644 --- a/test/module/irohad/validation/query_execution.cpp +++ b/test/module/irohad/validation/query_execution.cpp @@ -25,10 +25,10 @@ #include "builders/protobuf/common_objects/proto_account_builder.hpp" #include "builders/protobuf/common_objects/proto_amount_builder.hpp" #include "builders/protobuf/common_objects/proto_asset_builder.hpp" +#include "execution/query_execution.hpp" #include "framework/test_subscriber.hpp" -#include "validators/permissions.hpp" -#include "model/query_execution.hpp" #include "module/shared_model/builders/protobuf/test_query_builder.hpp" +#include "validators/permissions.hpp" using ::testing::AllOf; using ::testing::AtLeast; @@ -36,9 +36,10 @@ using ::testing::Return; using ::testing::StrictMock; using ::testing::_; +using namespace iroha; using namespace iroha::ametsuchi; -using namespace iroha::model; using namespace framework::test_subscriber; +using namespace shared_model::permissions; using wTransaction = std::shared_ptr; @@ -75,20 +76,16 @@ class QueryValidateExecuteTest : public ::testing::Test { std::shared_ptr validateAndExecute( const shared_model::interface::Query &query) { - return factory->execute(query); + return factory->validateAndExecute(query); } /** * Make transaction with specified parameters - * @param counter * @param creator * @return wrapper with created transaction */ - wTransaction makeTransaction(int counter, std::string creator) { - return clone(TestTransactionBuilder() - .creatorAccountId(creator) - .txCounter(counter) - .build()); + wTransaction makeTransaction(std::string creator) { + return clone(TestTransactionBuilder().creatorAccountId(creator).build()); } /** @@ -101,7 +98,7 @@ class QueryValidateExecuteTest : public ::testing::Test { return rxcpp::observable<>::iterate([&creator, &N, this] { std::vector result; for (size_t i = 0; i < N; ++i) { - auto current = makeTransaction(i, creator); + auto current = makeTransaction(creator); result.push_back(current); } return result; @@ -120,7 +117,6 @@ class QueryValidateExecuteTest : public ::testing::Test { std::shared_ptr block_query; std::shared_ptr factory; - std::shared_ptr query; }; class GetAccountTest : public QueryValidateExecuteTest { @@ -1279,7 +1275,6 @@ class GetRolesTest : public QueryValidateExecuteTest { roles = {admin_role, "some_role"}; } std::vector roles; - std::shared_ptr qry; }; /** @@ -1361,7 +1356,6 @@ class GetRolePermissionsTest : public QueryValidateExecuteTest { } std::string role_id = "user"; std::vector perms; - std::shared_ptr qry; }; /** diff --git a/test/module/libs/cache/cache_test.cpp b/test/module/libs/cache/cache_test.cpp index cc390238c0..825fc77d0d 100644 --- a/test/module/libs/cache/cache_test.cpp +++ b/test/module/libs/cache/cache_test.cpp @@ -170,3 +170,24 @@ TEST(CacheTest, CustomHasher) { ASSERT_TRUE(val); ASSERT_EQ(val.value(), value); } + +/** + * @given initialized cache with given parameters + * @when insert cache.getIndexSizeHigh() items into it + 1 + * @then after the last insertion amount of items should decrease to + * cache.getIndexSizeLow() + */ +TEST(CacheTest, InsertCustomSize) { + Cache cache(1, 1); + cache.addItem("key", "value"); + ASSERT_EQ(cache.getCacheItemCount(), cache.getIndexSizeHigh()); + auto val = cache.findItem("key"); + ASSERT_TRUE(val); + ASSERT_EQ(val.value(), "value"); + cache.addItem("key2", "value2"); + ASSERT_EQ(cache.getCacheItemCount(), cache.getIndexSizeLow()); + val = cache.findItem("key"); + ASSERT_FALSE(val); + ASSERT_TRUE(cache.findItem("key2")); + ASSERT_EQ(cache.findItem("key2").value(), "value2"); +} diff --git a/test/module/libs/common/result_test.cpp b/test/module/libs/common/result_test.cpp index c5335bd3d6..61cf7117b7 100644 --- a/test/module/libs/common/result_test.cpp +++ b/test/module/libs/common/result_test.cpp @@ -158,6 +158,102 @@ TEST(ResultTest, ResultVoidError) { makeFailCase>(kErrorCaseMessage)); } +/** + * @given Pair of results with some values + * @when and_res function is invoked + * @then result contains the last value + */ +TEST(ResultTest, AndResWithValVal) { + Result result = makeValue(5); + Result new_res = makeValue(4); + result.and_res(new_res).match([](Value v) { ASSERT_EQ(4, v.value); }, + makeFailCase>(kErrorCaseMessage)); +} + +/** + * @given Pair of results: first with error, second with value + * @when and_res function is invoked + * @then result contains the first error + */ +TEST(ResultTest, AndResWithErrVal) { + Result result = makeError(5); + Result new_res = makeValue(4); + result.and_res(new_res).match(makeFailCase>(kValueCaseMessage), + [](Error e) { ASSERT_EQ(5, e.error); }); +} + +/** + * @given Pair of results: first with value, second with error + * @when and_res function is invoked + * @then result contains the second error + */ +TEST(ResultTest, AndResWithValErr) { + Result result = makeValue(5); + Result new_res = makeError(4); + result.and_res(new_res).match(makeFailCase>(kValueCaseMessage), + [](Error e) { ASSERT_EQ(4, e.error); }); +} + +/** + * @given Pair of results with some values + * @when or_res function is invoked + * @then result contains the first value + */ +TEST(ResultTest, OrResWithValVal) { + Result result = makeValue(5); + Result new_res = makeValue(4); + result.or_res(new_res).match([](Value v) { ASSERT_EQ(5, v.value); }, + makeFailCase>(kErrorCaseMessage)); +} + +/** + * @given Pair of results: first with error, second with value + * @when or_res function is invoked + * @then result contains the second value + */ +TEST(ResultTest, OrResWithErrVal) { + Result result = makeError(5); + Result new_res = makeValue(4); + result.or_res(new_res).match([](Value v) { ASSERT_EQ(4, v.value); }, + makeFailCase>(kErrorCaseMessage)); +} + +/** + * @given Pair of results with some errors + * @when or_res function is invoked + * @then result contains the last value + */ +TEST(ResultTest, OrResWithErrErr) { + Result result = makeError(5); + Result new_res = makeError(4); + result.or_res(new_res).match(makeFailCase>(kValueCaseMessage), + [](Error e) { ASSERT_EQ(4, e.error); }); +} + +/** + * @given Result with some error and some function + * @when map_error function is invoked + * @then result contains error after function application + */ +TEST(ResultTest, MapError) { + Result result = makeError(5); + map_error(result, [](auto i) { return i * 2; }) + .match(makeFailCase>(kValueCaseMessage), + [](Error e) { ASSERT_EQ(10, e.error); }); +} + +/** + * @given Result with some error and some function + * @when map_error function is invoked + * @then result contains the same error + */ +TEST(ResultTest, MapErrorBlank) { + Result result = makeValue(5); + map_error(result, [](auto i) { return i * 2; }) + .match([](Value v) { ASSERT_EQ(5, v.value); }, + makeFailCase>(kErrorCaseMessage)); +} + /// Polymorphic result tests /// Base and Derived are classes, which can be used to test polymorphic behavior @@ -188,7 +284,7 @@ TEST(PolyMorphicResultTest, PolymorphicValueConstruction) { [](Value> &v) { ASSERT_EQ(1, v.value->getNumber()); }, - makeFailCase>>(kErrorCaseMessage)); + makeFailCase>>(kErrorCaseMessage)); } /** @@ -199,9 +295,8 @@ TEST(PolyMorphicResultTest, PolymorphicValueConstruction) { TEST(PolyMorphicResultTest, PolymorphicErrorConstruction) { PolymorphicResult result = makeError(std::make_shared(kErrorMessage)); - result.match( - makeFailCase>>(kValueCaseMessage), - [](Error> &e) { - ASSERT_EQ(kErrorMessage, *e.error); - }); + result.match(makeFailCase>>(kValueCaseMessage), + [](Error> &e) { + ASSERT_EQ(kErrorMessage, *e.error); + }); } diff --git a/test/module/libs/converter/CMakeLists.txt b/test/module/libs/converter/CMakeLists.txt index 7dcfa20ba7..e2c192e281 100644 --- a/test/module/libs/converter/CMakeLists.txt +++ b/test/module/libs/converter/CMakeLists.txt @@ -1 +1,5 @@ addtest(string_converter_test string_converter_test.cpp) + +target_link_libraries(string_converter_test + common + ) diff --git a/test/module/libs/crypto/keys_manager_test.cpp b/test/module/libs/crypto/keys_manager_test.cpp index 1244b5783e..ffeba986df 100644 --- a/test/module/libs/crypto/keys_manager_test.cpp +++ b/test/module/libs/crypto/keys_manager_test.cpp @@ -52,17 +52,18 @@ class KeyManager : public ::testing::Test { const path test_dir = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); const std::string filepath = (test_dir / "keymanager_test_file").string(); - const path pub_key_path = filepath + KeysManagerImpl::kPubExt; - const path pri_key_path = filepath + KeysManagerImpl::kPrivExt; + const path pub_key_path = filepath + KeysManagerImpl::kPublicKeyExtension; + const path pri_key_path = filepath + KeysManagerImpl::kPrivateKeyExtension; const std::string pubkey = "00576e02f23c8c694c322796cb3ef494829fdf484f4b42312fb7d776fbd5123b"s; const std::string prikey = "36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab80"s; KeysManagerImpl manager = KeysManagerImpl(filepath); const std::string passphrase = "test"; - const std::string nonexistent = (boost::filesystem::temp_directory_path() - / "path" / "that" / "doesnt" / "exist") - .string(); + const std::string nonexistent = + (boost::filesystem::temp_directory_path() / "path" / "that" / "doesnt" + / "exist") + .string(); }; TEST_F(KeyManager, LoadNonExistentKeyFile) { diff --git a/test/module/shared_model/CMakeLists.txt b/test/module/shared_model/CMakeLists.txt index c3221c213d..aac86f3bde 100644 --- a/test/module/shared_model/CMakeLists.txt +++ b/test/module/shared_model/CMakeLists.txt @@ -35,3 +35,9 @@ AddTest(reference_holder_test target_link_libraries(reference_holder_test boost ) + +AddTest(amount_utils_test amount_utils_test.cpp) +target_link_libraries(amount_utils_test + shared_model_default_builders + shared_model_amount_utils + ) diff --git a/test/module/shared_model/amount_utils_test.cpp b/test/module/shared_model/amount_utils_test.cpp new file mode 100644 index 0000000000..114eabd626 --- /dev/null +++ b/test/module/shared_model/amount_utils_test.cpp @@ -0,0 +1,174 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * 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 + * + * http://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. + */ + +#include + +#include "builders/default_builders.hpp" +#include "utils/amount_utils.hpp" + +using namespace shared_model::detail; + +class AmountTest : public testing::Test {}; + +/** + * @given two amounts + * @when tries to sum it up + * @then correct amount is given + */ +TEST_F(AmountTest, PlusTest) { + iroha::expected::Result, + std::shared_ptr> + a = shared_model::builder::DefaultAmountBuilder::fromString("1234.567"); + + iroha::expected::Result, + std::shared_ptr> + b = shared_model::builder::DefaultAmountBuilder::fromString("100"); + + a.match( + [&b](const iroha::expected::Value< + std::shared_ptr> &a_value) { + b.match( + [&a_value](const iroha::expected::Value> &b_value) { + auto c = *a_value.value + *b_value.value; + c.match( + [](const iroha::expected::Value> &c_value) { + ASSERT_EQ(c_value.value->intValue(), 1334567); + ASSERT_EQ(c_value.value->precision(), 3); + }, + [](const iroha::expected::Error> + &e) { FAIL() << *e.error; }); + }, + [](const iroha::expected::Error> &e) { + FAIL() << *e.error; + }); + }, + [](const iroha::expected::Error> &e) { + FAIL() << *e.error; + }); +} + +/** + * @given two amounts + * @when tries to subtract it up + * @then correct amount is given + */ +TEST_F(AmountTest, MinusTest) { + iroha::expected::Result, + std::shared_ptr> + a = shared_model::builder::DefaultAmountBuilder::fromString("1234.567"); + + iroha::expected::Result, + std::shared_ptr> + b = shared_model::builder::DefaultAmountBuilder::fromString("100"); + + a.match( + [&b](const iroha::expected::Value< + std::shared_ptr> &a_value) { + b.match( + [&a_value](const iroha::expected::Value> &b_value) { + auto c = *a_value.value - *b_value.value; + c.match( + [](const iroha::expected::Value> &c_value) { + ASSERT_EQ(c_value.value->intValue(), 1134567); + ASSERT_EQ(c_value.value->precision(), 3); + }, + [](const iroha::expected::Error> + &e) { FAIL() << *e.error; }); + }, + [](const iroha::expected::Error> &e) { + FAIL() << *e.error; + }); + }, + [](const iroha::expected::Error> &e) { + FAIL() << *e.error; + }); +} + +/** + * @given an amount + * @when tries change its precision + * @then correct amount is given + */ +TEST_F(AmountTest, PrecisionTest) { + iroha::expected::Result, + std::shared_ptr> + a = shared_model::builder::DefaultAmountBuilder::fromString("1234.567"); + + a.match( + [](const iroha::expected::Value< + std::shared_ptr> &a_value) { + auto c = makeAmountWithPrecision(*a_value.value, 4); + c.match( + [](const iroha::expected::Value< + std::shared_ptr> &c_value) { + ASSERT_EQ(c_value.value->intValue(), 12345670); + ASSERT_EQ(c_value.value->precision(), 4); + }, + [](const iroha::expected::Error> &e) { + FAIL() << *e.error; + }); + }, + [](const iroha::expected::Error> &e) { + FAIL() << *e.error; + }); +} + +/** + * @given two amounts which sum overflows amount type + * @when tries to sum it up + * @then an error occurs + */ +TEST_F(AmountTest, PlusOverflowsTest) { + const std::string &uint256_halfmax = + "723700557733226221397318656304299424082937404160253525246609900049457060" + "2495.0"; // 2**252 - 1 + iroha::expected::Result, + std::shared_ptr> + a = shared_model::builder::DefaultAmountBuilder::fromString( + uint256_halfmax); + + iroha::expected::Result, + std::shared_ptr> + b = shared_model::builder::DefaultAmountBuilder::fromString("100.00"); + + a.match( + [&b](const iroha::expected::Value< + std::shared_ptr> &a_value) { + b.match( + [&a_value](const iroha::expected::Value> &b_value) { + auto c = *a_value.value + *b_value.value; + c.match( + [](const iroha::expected::Value> &c_value) { + FAIL() << "Operation successful but shouldn't"; + }, + [](const iroha::expected::Error> + &e) { SUCCEED() << *e.error; }); + }, + [](const iroha::expected::Error> &e) { + FAIL() << *e.error; + }); + }, + [](const iroha::expected::Error> &e) { + FAIL() << *e.error; + }); +} diff --git a/test/module/shared_model/backend_proto/shared_proto_query_responses_test.cpp b/test/module/shared_model/backend_proto/shared_proto_query_responses_test.cpp index 4e5d7bf091..359fc0be63 100644 --- a/test/module/shared_model/backend_proto/shared_proto_query_responses_test.cpp +++ b/test/module/shared_model/backend_proto/shared_proto_query_responses_test.cpp @@ -30,7 +30,7 @@ TEST(QueryResponse, QueryResponseLoad) { iroha::protocol::QueryResponse response; const std::string hash = "123"; - response.set_query_hash(hash); + response.set_query_hash(iroha::bytestringToHexstring(hash)); auto refl = response.GetReflection(); auto desc = response.GetDescriptor(); auto resp_status = desc->FindOneofByName("response"); @@ -54,7 +54,7 @@ TEST(QueryResponse, QueryResponseLoad) { TEST(QueryResponse, ErrorResponseLoad) { iroha::protocol::QueryResponse response; const std::string hash = "123"; - response.set_query_hash(hash); + response.set_query_hash(iroha::bytestringToHexstring(hash)); auto error_resp = response.mutable_error_response(); auto refl = error_resp->GetReflection(); auto desc = error_resp->GetDescriptor(); diff --git a/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp b/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp index 297b0a2067..cc4e8421cf 100644 --- a/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp +++ b/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp @@ -23,21 +23,8 @@ #include -/** - * @given protobuf transaction with transaction counter set - * @when converted to shared model - * @then shared model is created correctly - */ -TEST(ProtoTransaction, Create) { - iroha::protocol::Transaction transaction; - transaction.mutable_payload()->set_tx_counter(1); - shared_model::proto::Transaction proto(transaction); - ASSERT_EQ(proto.transactionCounter(), transaction.payload().tx_counter()); -} - // common data for tests auto created_time = iroha::time::now(); -shared_model::interface::types::CounterType tx_counter = 1; std::string creator_account_id = "admin@test"; /** @@ -47,7 +34,6 @@ std::string creator_account_id = "admin@test"; iroha::protocol::Transaction generateEmptyTransaction() { iroha::protocol::Transaction proto_tx; auto &payload = *proto_tx.mutable_payload(); - payload.set_tx_counter(tx_counter); payload.set_creator_account_id(creator_account_id); payload.set_created_time(created_time); @@ -93,12 +79,11 @@ TEST(ProtoTransaction, Builder) { shared_model::crypto::Blob(proto_tx.payload().SerializeAsString()), keypair); - auto sig = proto_tx.add_signature(); + auto sig = proto_tx.add_signatures(); sig->set_pubkey(shared_model::crypto::toBinaryString(keypair.publicKey())); sig->set_signature(shared_model::crypto::toBinaryString(signedProto)); auto tx = shared_model::proto::TransactionBuilder() - .txCounter(tx_counter) .creatorAccountId(creator_account_id) .addAssetQuantity(account_id, asset_id, amount) .createdTime(created_time) @@ -123,7 +108,6 @@ TEST(ProtoTransaction, BuilderWithInvalidTx) { ASSERT_THROW( shared_model::proto::TransactionBuilder() - .txCounter(tx_counter) .creatorAccountId(invalid_account_id) .addAssetQuantity(invalid_account_id, invalid_asset_id, amount) .createdTime(created_time) diff --git a/test/module/shared_model/bindings/BuilderTest.java b/test/module/shared_model/bindings/BuilderTest.java index 965006d194..c537b0a1d1 100644 --- a/test/module/shared_model/bindings/BuilderTest.java +++ b/test/module/shared_model/bindings/BuilderTest.java @@ -1,5 +1,11 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Disabled; import java.math.BigInteger; @@ -27,8 +33,97 @@ public class BuilderTest { private Keypair keys; private ModelTransactionBuilder builder; + + /* Stateless validation rules are listed + * here https://github.com/hyperledger/iroha/pull/1148 + */ + + // Symbols of type 1 (format [a-z_0-9]{1,32}) are used + // as account_name, asset_name and role_id. + private final String[] validNameSymbols1 = { + "a", + "asset", + "234234", + "_", + "_123", + "123_23", + "234asset_", + "__", + "12345678901234567890123456789012" + }; + private final String[] invalidNameSymbols1 = { + "", + "A", + "assetV", + "asSet", + "asset%", + "^123", + "verylongassetname_thenameislonger", + "verylongassetname_thenameislongerthanitshouldbe", + "assset-01" + }; + + // Symbols of type 2 (format [A-Za-z0-9_]{1,64}) + // are used as key identifier for setAccountDetail command + private final String[] validNameSymbols2 = { + "a", + "A", + "1", + "_", + "Key", + "Key0_", + "verylongAndValidKeyName___1110100010___veryveryveryverylongvalid" + }; + private final String[] invalidNameSymbols2 = { + "", + "Key&", + "key-30", + "verylongAndValidKeyName___1110100010___veryveryveryverylongvalid1", + "@@@" + }; + + private final String[] validDomains = { + "test", + "u9EEA432F", + "a-hyphen", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad", + "endWith0", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad" + }; + + private final String[] invalidDomains = { + "", + " ", + " ", + "9start.with.digit", + "-startWithDash", + "@.is.not.allowed", + "no space is allowed", + "endWith-", + "label.endedWith-.is.not.allowed", + "aLabelMustNotExceeds63charactersALabelMustNotExceeds63characters", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPadP", + "257.257.257.257", + "domain#domain", + "asd@asd", + "ab..cd" + }; + + private final String[] invalidKeysBytes = { + "", + "a", + "1111111111111111111111111111111", + "111111111111111111111111111111111" + }; + ModelTransactionBuilder base() { - return new ModelTransactionBuilder().txCounter(BigInteger.valueOf(123)) + return new ModelTransactionBuilder() .createdTime(BigInteger.valueOf(System.currentTimeMillis())) .creatorAccountId("admin@test"); } @@ -37,6 +132,33 @@ void setAddPeer() { builder.addPeer("123.123.123.123", keys.publicKey()); } + Blob proto(UnsignedTx tx) { + return new ModelProtoTransaction().signAndAddSignature(tx, keys); + } + + /** + * Performs check that Blob contains valid proto Transaction + * @param serialized blob with binary data for check + * @return true if valid + */ + private boolean checkProtoTx(Blob serialized) { + ByteVector blob = serialized.blob(); + byte bs[] = new byte[(int)blob.size()]; + + for (int i = 0; i < blob.size(); i++) { + bs[i] = (byte)blob.get(i); + } + + try { + BlockOuterClass.Transaction.parseFrom(bs); + } catch (InvalidProtocolBufferException e) { + System.out.print("Exception: "); + System.out.println(e.getMessage()); + return false; + } + return true; + } + @BeforeEach void setUp() { keys = new ModelCrypto().generateKeypair(); @@ -49,10 +171,20 @@ void emptyTx() { assertThrows(IllegalArgumentException.class, builder::build); } + /* ====================== AddPeer Tests ====================== */ + + @Test + void addPeer() { + for (String domain: validDomains) { + UnsignedTx tx = builder.addPeer(domain + ":123", keys.publicKey()).build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + @Test void outdatedAddPeer() { setAddPeer(); - for (BigInteger i : new BigInteger[]{BigInteger.valueOf(0), + for (BigInteger i: new BigInteger[]{BigInteger.valueOf(0), BigInteger.valueOf(System.currentTimeMillis() - 100_000_000), BigInteger.valueOf(System.currentTimeMillis() + 1000)}) { builder.createdTime(i); @@ -63,108 +195,887 @@ void outdatedAddPeer() { @Test void addPeerWithInvalidCreator() { setAddPeer(); - for (String i : new String[]{"", "invalid", "@invalid", "invalid@"}) { - builder.creatorAccountId(i); + for (String account: invalidNameSymbols1) { + builder.creatorAccountId(account + "@test"); assertThrows(IllegalArgumentException.class, builder::build); } } @Test - void addPeerWithInvalidKeySize() { + void addPeerWithEmptyCreator() { + setAddPeer(); + builder.creatorAccountId(""); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void addPeerWithInvalidCreatorDomain() { setAddPeer(); - for (String i : new String[]{"", "a", "1111111111111111111111111111111", "111111111111111111111111111111111"}) { - ModelTransactionBuilder builder = base().addPeer("123.123.123.123", new PublicKey(i)); + for (String domain: invalidDomains) { + builder.creatorAccountId("admin@" + domain); assertThrows(IllegalArgumentException.class, builder::build); } } @Test - void addPeerWithInvalidHost() { + void addPeerWithInvalidKeySize() { setAddPeer(); - for (String i : new String[]{"257.257.257.257", "host#host", "asd@asd", "ab..cd"}) { - ModelTransactionBuilder builder = base().addPeer(i, keys.publicKey()); + for (String key: invalidKeysBytes) { + ModelTransactionBuilder builder = base().addPeer("123.123.123.123", new PublicKey(key)); assertThrows(IllegalArgumentException.class, builder::build); } } - Blob proto(UnsignedTx tx) { - return new ModelProtoTransaction().signAndAddSignature(tx, keys); + @Test + void addPeerWithInvalidDomain() { + setAddPeer(); + for (String domain: invalidDomains) { + ModelTransactionBuilder builder = base().addPeer(domain, keys.publicKey()); + assertThrows(IllegalArgumentException.class, builder::build); + } } - /** - * Performs check that Blob contains valid proto Transaction - * @param serialized blob with binary data for check - * @return true if valid - */ - private boolean checkProtoTx(Blob serialized) { - ByteVector blob = serialized.blob(); - byte bs[] = new byte[(int)blob.size()]; + /* ====================== AddSignatory Tests ====================== */ - for (int i = 0; i < blob.size(); i++) { - bs[i] = (byte)blob.get(i); + @Test + void addSignatory() { + for (String accountName: validNameSymbols1) { + for (String domain: validDomains) { + String accountId = accountName + "@" + domain; + UnsignedTx tx = builder.addSignatory(accountId, keys.publicKey()).build(); + assertTrue(checkProtoTx(proto(tx))); + } } + } - try { - BlockOuterClass.Transaction.parseFrom(bs); - } catch (InvalidProtocolBufferException e) { - System.out.print("Exception: "); - System.out.println(e.getMessage()); - return false; + @Test + void addSignatoryInvalidAccountName() { + for (String accountName: invalidNameSymbols1) { + String accountId = accountName + "@domain"; + ModelTransactionBuilder builder = base().addSignatory(accountId, keys.publicKey()); + assertThrows(IllegalArgumentException.class, builder::build); } - return true; } @Test - void addPeer() { - UnsignedTx tx = builder.addPeer("123.123.123.123:123", keys.publicKey()).build(); - assertTrue(checkProtoTx(proto(tx))); + void addSignatoryEmptyAccountId() { + ModelTransactionBuilder builder = base().addSignatory("", keys.publicKey()); + assertThrows(IllegalArgumentException.class, builder::build); } @Test - void addSignatory() { - UnsignedTx tx = builder.addSignatory("admin@test", keys.publicKey()).build(); - assertTrue(checkProtoTx(proto(tx))); + void addSignatoryInvalidDomain() { + for (String domain: invalidDomains) { + String accountId = "user@" + domain; + ModelTransactionBuilder builder = base().addSignatory(accountId, keys.publicKey()); + assertThrows(IllegalArgumentException.class, builder::build); + } } + @Test + void addSignatoryInvalidKey() { + for (String invalidKeyBytes: invalidKeysBytes) { + ModelTransactionBuilder builder = base().addSignatory("admin@test", new PublicKey(invalidKeyBytes)); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + /* ====================== AddAssetQuantity Tests ====================== */ + @Test void addAssetQuantity() { UnsignedTx tx = builder.addAssetQuantity("admin@test", "asset#domain", "12.345").build(); assertTrue(checkProtoTx(proto(tx))); } + @Test + void addAssetQuantityValidAccountsAndAssets() { + for (String domain: validDomains) { + for (String name: validNameSymbols1) { + UnsignedTx tx = builder.addAssetQuantity(name + "@" + domain, name + "#" + domain, "100").build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void addAssetQuantityInvalidAccountDomain() { + for (String domain: invalidDomains) { + ModelTransactionBuilder builder = base().addAssetQuantity("admin@" + domain, "asset#test", "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void addAssetQuantityInvalidAssetDomain() { + for (String domain: invalidDomains) { + ModelTransactionBuilder builder = base().addAssetQuantity("admin@test", "asset#" + domain, "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void addAssetZeroQuantity() { + ModelTransactionBuilder builder = base().addAssetQuantity("admin@test", "asset#domain", "0"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void addAssetQuantityInvalidAccountName() { + for (String accountName: invalidNameSymbols1) { + String accountId = accountName + "@test"; + ModelTransactionBuilder builder = base().addAssetQuantity(accountId, "asset#domain", "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void addAssetQuantityEmptyAccount() { + ModelTransactionBuilder builder = base().addAssetQuantity("", "asset#test", "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void addAssetQuantityInvalidAssetName() { + for (String assetName: invalidNameSymbols1) { + String assetId = assetName + "#test"; + ModelTransactionBuilder builder = base().addAssetQuantity("account@test", assetId, "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void addAssetQuantityEmptyAsset() { + ModelTransactionBuilder builder = base().addAssetQuantity("account@test", "", "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void addAssetQuantityInvalidAmount() { + for (String amount: new String[]{"", "-12", "-13.45", "chars", "chars10"}) { + ModelTransactionBuilder builder = base().addAssetQuantity("account@test", "asset#test", amount); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + /* ====================== RemoveSignatory Tests ====================== */ + @Test void removeSignatory() { - UnsignedTx tx = builder.removeSignatory("admin@test", keys.publicKey()).build(); - assertTrue(checkProtoTx(proto(tx))); + for (String accountName: validNameSymbols1) { + for (String domain: validDomains) { + String accountId = accountName + "@" + domain; + UnsignedTx tx = builder.removeSignatory(accountId, keys.publicKey()).build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void removeSignatoryEmptyAccount() { + ModelTransactionBuilder builder = base().removeSignatory("", keys.publicKey()); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void removeSignatoryInvalidAccount() { + for (String accountName: invalidNameSymbols1) { + String accountId = accountName + "@test"; + ModelTransactionBuilder builder = base().removeSignatory(accountId, keys.publicKey()); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void removeSignatoryInvalidAccountDomain() { + for (String domain: invalidDomains) { + ModelTransactionBuilder builder = base().removeSignatory("admin@" + domain, keys.publicKey()); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void removeSignatoryInvalidKey() { + for (String invalidKeyBytes: invalidKeysBytes) { + ModelTransactionBuilder builder = base().removeSignatory("admin@test", new PublicKey(invalidKeyBytes)); + assertThrows(IllegalArgumentException.class, builder::build); + } } + /* ====================== CreateAccount Tests ====================== */ + @Test void createAccount() { - UnsignedTx tx = builder.createAccount("admin", "domain", keys.publicKey()).build(); - assertTrue(checkProtoTx(proto(tx))); + for (String accountName: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedTx tx = builder.createAccount(accountName, domain, keys.publicKey()).build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void createAccountInvalidAccountName() { + for (String accountName: invalidNameSymbols1) { + ModelTransactionBuilder builder = base().createAccount(accountName, "domain", keys.publicKey()); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void createAccountInvalidDomain() { + for (String domain: invalidDomains) { + ModelTransactionBuilder builder = base().createAccount("admin", domain, keys.publicKey()); + assertThrows(IllegalArgumentException.class, builder::build); + } } + @Test + void createAccountInvalidKey() { + for (String invalidKeyBytes: invalidKeysBytes) { + ModelTransactionBuilder builder = base().createAccount("admin", "test", new PublicKey(invalidKeyBytes)); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + /* ====================== CreateDomain Tests ====================== */ + @Test void createDomain() { - UnsignedTx tx = builder.createDomain("domain", "role").build(); - assertTrue(checkProtoTx(proto(tx))); + for (String role: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedTx tx = builder.createDomain(domain, role).build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void createDomainInvalidDomain() { + for (String domain: invalidDomains) { + ModelTransactionBuilder builder = base().createDomain(domain, "role"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void createDomainInvalidRole() { + for (String role: invalidNameSymbols1) { + ModelTransactionBuilder builder = base().createDomain("domain", role); + assertThrows(IllegalArgumentException.class, builder::build); + } } + /* ====================== SetAccountQuorum Tests ====================== */ + + @Test void setAccountQuorum() { - UnsignedTx tx = builder.setAccountQuorum("admin@test", 123).build(); - assertTrue(checkProtoTx(proto(tx))); + for (String accountName: validNameSymbols1) { + for (String domain: validDomains) { + String accountId = accountName + "@" + domain; + UnsignedTx tx = builder.setAccountQuorum(accountId, 128).build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void setAccountQuorumInvalidAccount() { + for (String accountName: invalidNameSymbols1) { + String accountId = accountName + "@test"; + ModelTransactionBuilder builder = base().setAccountQuorum(accountId, 123); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void setAccountQuorumInvalidDomain() { + for (String domain: invalidDomains) { + String accountId = "admin@" + domain; + ModelTransactionBuilder builder = base().setAccountQuorum(accountId, 123); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void setAccountQuorumEmptyAccount() { + ModelTransactionBuilder builder = base().setAccountQuorum("", 123); + assertThrows(IllegalArgumentException.class, builder::build); } + @Test + void setAccountQuorumInvalidQuantity() { + for (int quorumSize: new int[]{0, 129, -100}) { + ModelTransactionBuilder builder = base().setAccountQuorum("admin@test", quorumSize); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + /* ====================== TransferAsset Tests ====================== */ + @Test void transferAsset() { - UnsignedTx tx = builder.transferAsset("from@test", "to@test", "asset#test", "description", "123.456").build(); - assertTrue(checkProtoTx(proto(tx))); + for (String domain: validDomains) { + for (int i = 0; i < validNameSymbols1.length; i++) { + String from = validNameSymbols1[i] + "@" + domain; + String to = validNameSymbols1[(i + 1) % validNameSymbols1.length] + "@" + domain; + String asset = validNameSymbols1[(i + 2) % validNameSymbols1.length] + "#" + domain; + UnsignedTx tx = builder.transferAsset(from, to, asset, "description", "123.456").build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void transferAssetWithValidName() { + for (String assetName: validNameSymbols1) { + String assetId = assetName + "#test"; + UnsignedTx tx = builder.transferAsset("from@test", "to@test", assetId, "description", "100").build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + + @Test + void transferAssetWithInvalidName() { + for (String assetName: invalidNameSymbols1) { + String assetId = assetName + "#test"; + ModelTransactionBuilder builder = base().transferAsset("from@test", "to@test", assetId, "description", "100"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void transferAssetWithInvalidFromAccount() { + for (String accountName: invalidNameSymbols1) { + String accountId = accountName + "@test"; + ModelTransactionBuilder builder = base().transferAsset(accountId, "to@test", "asset#test", "description", "100"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void transferAssetWithInvalidToAccount() { + for (String accountName: invalidNameSymbols1) { + String accountId = accountName + "@test"; + ModelTransactionBuilder builder = base().transferAsset("from@test", accountId, "asset#test", "description", "100"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void transferAssetWithEmptyFromAccount() { + ModelTransactionBuilder builder = base().transferAsset("", "to@test", "asset#test", "description", "100"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void transferAssetWithEmptyToAccount() { + ModelTransactionBuilder builder = base().transferAsset("from@test", "", "asset#test", "description", "100"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void transferAssetWithInvalidFromDomain() { + for (String domain: invalidDomains) { + String accountId = "from@" + domain; + ModelTransactionBuilder builder = base().transferAsset(accountId, "to@test", "asset#test", "description", "100"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void transferAssetWithInvalidToDomain() { + for (String domain: invalidDomains) { + String accountId = "to@" + domain; + ModelTransactionBuilder builder = base().transferAsset("from@test", accountId, "asset#test", "description", "100"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void transferAssetWithInvalidAssetDomain() { + for (String domain: invalidDomains) { + String assetId = "asset#" + domain; + ModelTransactionBuilder builder = base().transferAsset("from@test", "to@test", assetId, "description", "100"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void transferAssetWithEmptyAssetName() { + ModelTransactionBuilder builder = base().transferAsset("from@test", "to@test", "", "description", "100"); + assertThrows(IllegalArgumentException.class, builder::build); } + @Test + void transferAssetDescriptionBoundaryValues() { + for (String description1: new String[]{"", "abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde1234"}) { + UnsignedTx tx = builder.transferAsset("from@test", "to@test", "asset#test", description1, "100").build(); + assertTrue(checkProtoTx(proto(tx))); + } + + String description2 = "abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde12345"; + ModelTransactionBuilder builder = base().transferAsset("from@test", "to@test", "asset#test", description2, "100"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void transferAssetMaximumAmount() { + BigInteger base = new BigInteger("2"); + BigInteger one = new BigInteger("1"); + BigInteger maxUint256 = base.pow(256).subtract(one); + BigInteger oversizedInt = maxUint256.add(one); + + String maxAmount1 = maxUint256.toString(); + String maxAmount2 = maxAmount1.substring(0, 10) + "." + maxAmount1.substring(10, maxAmount1.length()); + + for (String amount: new String[]{maxAmount1, maxAmount2}) { + UnsignedTx tx = builder.transferAsset("from@test", "to@test", "asset#test", "description", amount).build(); + assertTrue(checkProtoTx(proto(tx))); + } + + ModelTransactionBuilder mtb = base().transferAsset("from@test", "to@test", "asset#test", "description", oversizedInt.toString()); + assertThrows(IllegalArgumentException.class, mtb::build); + } + + + /* ====================== SetAccountDetail Tests ====================== */ + @Test void setAccountDetail() { - UnsignedTx tx = builder.setAccountDetail("admin@test", "fyodor", "kek").build(); + for (String accountName: validNameSymbols1) { + for (String domain: validDomains) { + String accountId = accountName + "@" + domain; + UnsignedTx tx = builder.setAccountDetail(accountId, "fyodor", "kek").build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void setAccountDetailInvalidAccount() { + for (String accountName: invalidNameSymbols1) { + String accountId = accountName + "@test"; + ModelTransactionBuilder builder = base().setAccountDetail(accountId, "fyodor", "true"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void setAccountDetailEmptyAccount() { + ModelTransactionBuilder builder = base().setAccountDetail("", "fyodor", "true"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void setAccountDetailInvalidAccountDomain() { + for (String domain: invalidDomains) { + String accountId = "admin@" + domain; + ModelTransactionBuilder builder = base().setAccountDetail(accountId, "fyodor", "true"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void setAccountDetailValidKey() { + for (String key: validNameSymbols2) { + UnsignedTx tx = builder.setAccountDetail("admin@test", key, "true").build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + + @Test + void setAccountDetailInvalidKey() { + for (String key: invalidNameSymbols2) { + ModelTransactionBuilder builder = base().setAccountDetail("admin@test", key, "true"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void setAccountDetailValidValue() { + int length = 4 * 1024 * 1024; + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; i++) { + sb.append("a"); + } + + for (String value: new String[]{"", sb.toString()}) { + UnsignedTx tx = builder.setAccountDetail("admin@test", "fyodor", value).build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + + @Test + void setAccountDetailWithOversizedValue() { + int length = 4 * 1024 * 1024 + 1; + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; i++) { + sb.append("a"); + } + + ModelTransactionBuilder builder = base().setAccountDetail("admin@test", "fyodor", sb.toString()); + assertThrows(IllegalArgumentException.class, builder::build); + } + + /* ====================== AppendRole Tests ====================== */ + + @Test + void appendRole() { + for (String account: validNameSymbols1) { + for (String domain: validDomains) { + String accountId = account + "@" + domain; + UnsignedTx tx = builder.appendRole(accountId, account).build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void appendRoleInvalidAccount() { + for (String account: invalidNameSymbols1) { + String accountId = account + "@test"; + ModelTransactionBuilder builder = base().appendRole(accountId, "user"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void appendRoleInvalidDomain() { + for (String domain: invalidDomains) { + String accountId = "admin@" + domain; + ModelTransactionBuilder builder = base().appendRole(accountId, "user"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void appendRoleWithInvalidName() { + for (String role: invalidNameSymbols1) { + ModelTransactionBuilder builder = base().appendRole("admin@test", role); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + + /* ====================== CreateAsset Tests ====================== */ + + @Test + void createAsset() { + for (String assetName: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedTx tx = builder.createAsset(assetName, domain, (short) 6).build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void createAssetWithInvalidName() { + for (String asset: invalidNameSymbols1) { + ModelTransactionBuilder builder = base().createAsset(asset, "test", (short) 6); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void createAssetWithInvalidDomain() { + for (String domain: invalidDomains) { + ModelTransactionBuilder builder = base().createAsset("asset", domain, (short) 6); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void createAssetWithZeroPrecision() { + UnsignedTx tx = builder.createAsset("asset", "test", (short) 0).build(); assertTrue(checkProtoTx(proto(tx))); } + + /* ====================== CreateRole Tests ====================== */ + + @Test + void createRole() { + StringVector permissions = new StringVector(); + permissions.add("can_receive"); + permissions.add("can_get_roles"); + assertTrue(permissions.size() == 2); + + for (String role: validNameSymbols1) { + UnsignedTx tx = builder.createRole(role, permissions).build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + + @Test + void createRoleWithInvalidName() { + StringVector permissions = new StringVector(); + permissions.add("can_receive"); + permissions.add("can_get_roles"); + assertTrue(permissions.size() == 2); + + for (String role: invalidNameSymbols1) { + ModelTransactionBuilder mtb = base().createRole(role, permissions); + assertThrows(IllegalArgumentException.class, mtb::build); + } + } + + @Test + void createRoleEmptyPermissions() { + StringVector permissions = new StringVector(); + + ModelTransactionBuilder builder = new ModelTransactionBuilder(); + builder.createRole("new_role", permissions); + assertThrows(IllegalArgumentException.class, builder::build); + } + + /* Disabled till IR-1267 will be fixed. */ + /* Please run this test on mac host after enabling, + because the test was passing on Linux host and failing on macOs. + */ + @Disabled + @Test + void createRoleWrongPermissions() { + StringVector permissions = new StringVector(); + permissions.add("wrong_permission"); + permissions.add("can_receive"); + + ModelTransactionBuilder builder = base().createRole("new_role", permissions); + assertThrows(IllegalArgumentException.class, builder::build); + } + + + /* Test differs from the previous by the order of permissions' mames in vector. + * Test is disabled because there is no exception thrown when it should be. + */ + /* Disabled till IR-1267 will be fixed. */ + @Disabled + @Test + void createRoleWrongPermissions2() { + StringVector permissions = new StringVector(); + permissions.add("can_receive"); + permissions.add("wrong_permission"); + + ModelTransactionBuilder builder = base().createRole("new_role", permissions); + assertThrows(IllegalArgumentException.class, builder::build); + } + + /* ====================== DetachRole Tests ====================== */ + + @Test + void detachRole() { + for (String name: validNameSymbols1) { + for (String domain: validDomains) { + String accountId = name + "@" + domain; + UnsignedTx tx = builder.detachRole(accountId, name).build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void detachRoleInvalidAccountName() { + for (String accountName: invalidNameSymbols1) { + String accountId = accountName + "@test"; + ModelTransactionBuilder builder = base().detachRole(accountId, "role"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void detachRoleInvalidDomain() { + for (String domain: invalidDomains) { + String accountId = "admin@" + domain; + ModelTransactionBuilder builder = base().detachRole(accountId, "role"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void detachRoleWithEmptyAccount() { + ModelTransactionBuilder builder = base().detachRole("", "role"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void detachRoleWithInvalidName() { + for (String role: invalidNameSymbols1) { + ModelTransactionBuilder builder = base().detachRole("admin@test", role); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + /* ====================== GrantPermission Tests ====================== */ + + @Test + void grantPermission() { + for (String accountName: validNameSymbols1) { + for (String domain: validDomains) { + String accountId = accountName + "@" + domain; + UnsignedTx tx = builder.grantPermission(accountId, "can_set_my_quorum").build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void grantPermissionInvalidAccount() { + for (String accountName: invalidNameSymbols1) { + for (String domain: validDomains) { + String accountId = accountName + "@" + domain; + ModelTransactionBuilder builder = base().grantPermission(accountId, "can_set_my_quorum"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + } + + @Test + void grantPermissionEmptyAccount() { + ModelTransactionBuilder builder = base().grantPermission("", "can_set_my_quorum"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + // TODO igor-egorov, 14.05.2018 IR-1267 + // Please test using clang on macOS before enabling + // This test does not fail on Linux with GCC 5.4.0 + @Disabled + @Test + void grantPermissionWithInvalidName() { + String permissions[] = { + "", + "random", + "can_read_assets" // non-grantable permission + }; + + for (String permission: permissions) { + ModelTransactionBuilder builder = base().grantPermission("admin@test", permission); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + /* ====================== RevokePermission Tests ====================== */ + + @Test + void revokePermission() { + for (String accountName: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedTx tx = builder.revokePermission(accountName + "@" + domain, "can_set_my_quorum").build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void revokePermissionInvalidAccount() { + for (String accountName: invalidNameSymbols1) { + ModelTransactionBuilder builder = base().revokePermission(accountName + "@test", "can_set_my_quorum"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void revokePermissionInvalidDomain() { + for (String domain: invalidDomains) { + ModelTransactionBuilder builder = base().revokePermission("admin@" + domain, "can_set_my_quorum"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void revokePermissionEmptyAccount() { + ModelTransactionBuilder builder = base().revokePermission("", "can_set_my_quorum"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + // TODO igor-egorov, 14.05.2018 IR-1267 + // Please test using clang on macOS before enabling + // This test does not fail on Linux with GCC 5.4.0 + @Disabled + @Test + void revokePermissionWithInvalidName() { + String permissions[] = { + "", + "random", + "can_read_assets" // non-grantable permission + }; + + for (String permission: permissions) { + ModelTransactionBuilder builder = base().revokePermission("admin@test", permission); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + /* ====================== SubtractAssetQuantity Tests ====================== */ + + @Test + void subtractAssetQuantity() { + for (String name: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedTx tx = builder.subtractAssetQuantity(name + "@" + domain, name + "#" + domain, "10.22").build(); + assertTrue(checkProtoTx(proto(tx))); + } + } + } + + @Test + void subtractAssetQuantityInvalidAccount() { + for (String account: invalidNameSymbols1) { + ModelTransactionBuilder builder = base().subtractAssetQuantity(account + "@test", "coin#test", "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void subtractAssetQuantityInvalidAccountDomain() { + for (String domain: invalidDomains) { + ModelTransactionBuilder builder = base().subtractAssetQuantity("admin@" + domain, "coin#test", "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void subtractAssetQuantityInvalidAsset() { + for (String asset: invalidNameSymbols1) { + ModelTransactionBuilder builder = base().subtractAssetQuantity("admin@test", asset + "#test", "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void subtractAssetQuantityInvalidAssetDomain() { + for (String domain: invalidDomains) { + ModelTransactionBuilder builder = base().subtractAssetQuantity("admin@test", "coin#" + domain, "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void subtractAssetQuantityEmptyAccount() { + ModelTransactionBuilder builder = base().subtractAssetQuantity("", "coin#test", "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void subtractAssetQuantityEmptyAsset() { + ModelTransactionBuilder builder = base().subtractAssetQuantity("admin@test", "", "10"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void subtractAssetQuantityInvalidAmount() { + String invalidAmounts[] = { + "", + "0", + "chars", + "-10", + "10chars", + "10.10.10" + }; + + for (String amount: invalidAmounts) { + ModelTransactionBuilder builder = base().subtractAssetQuantity("admin@test", "coin#test", amount); + assertThrows(IllegalArgumentException.class, builder::build); + } + } } diff --git a/test/module/shared_model/bindings/CMakeLists.txt b/test/module/shared_model/bindings/CMakeLists.txt index d3933f70e8..cb2a0da431 100644 --- a/test/module/shared_model/bindings/CMakeLists.txt +++ b/test/module/shared_model/bindings/CMakeLists.txt @@ -36,7 +36,7 @@ if (SWIG_PYTHON) if(SUPPORT_PYTHON2) find_package(PythonInterp 2.7 REQUIRED) else() - find_package(PythonInterp 3.6 REQUIRED) + find_package(PythonInterp 3.5 REQUIRED) endif() add_test(NAME python_transaction_test COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/builder-test.py @@ -68,3 +68,10 @@ if (SWIG_JAVA) set_tests_properties(java_builders_test PROPERTIES DEPENDS builders) endif() + +if (SWIG_NODE) + find_package (nodejs REQUIRED) + add_test(NAME javascript_tests + COMMAND npm run build-and-test + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/shared_model/packages/javascript) +endif() diff --git a/test/module/shared_model/bindings/QueryTest.java b/test/module/shared_model/bindings/QueryTest.java index 2f14305e8b..0c5ef9bd9c 100644 --- a/test/module/shared_model/bindings/QueryTest.java +++ b/test/module/shared_model/bindings/QueryTest.java @@ -1,5 +1,11 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Disabled; import java.math.BigInteger; @@ -27,12 +33,123 @@ public class QueryTest { private Keypair keys; private ModelQueryBuilder builder; + // Symbols of type 1 (format [a-z_0-9]{1,32}) are used + // as account_name, asset_name and role_id. + private final String[] validNameSymbols1 = { + "a", + "asset", + "234234", + "_", + "_123", + "123_23", + "234asset_", + "__", + "12345678901234567890123456789012" + }; + private final String[] invalidNameSymbols1 = { + "", + "A", + "assetV", + "asSet", + "asset%", + "^123", + "verylongassetname_thenameislonger", + "verylongassetname_thenameislongerthanitshouldbe", + "assset-01" + }; + + // Symbols of type 2 (format [A-Za-z0-9_]{1,64}) + // are used as key identifier for setAccountDetail command + private final String[] validNameSymbols2 = { + "a", + "A", + "1", + "_", + "Key", + "Key0_", + "verylongAndValidKeyName___1110100010___veryveryveryverylongvalid" + }; + private final String[] invalidNameSymbols2 = { + "", + "Key&", + "key-30", + "verylongAndValidKeyName___1110100010___veryveryveryverylongvalid1", + "@@@" + }; + + private final String[] validDomains = { + "test", + "u9EEA432F", + "a-hyphen", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad", + "endWith0", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad" + }; + + private final String[] invalidDomains = { + "", + " ", + " ", + "9start.with.digit", + "-startWithDash", + "@.is.not.allowed", + "no space is allowed", + "endWith-", + "label.endedWith-.is.not.allowed", + "aLabelMustNotExceeds63charactersALabelMustNotExceeds63characters", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPadP", + "257.257.257.257", + "domain#domain", + "asd@asd", + "ab..cd" + }; + + private final String[] invalidKeysBytes = { + "", + "a", + "1111111111111111111111111111111", + "111111111111111111111111111111111" + }; + ModelQueryBuilder base() { return new ModelQueryBuilder().queryCounter(BigInteger.valueOf(123)) .createdTime(BigInteger.valueOf(System.currentTimeMillis())) .creatorAccountId("admin@test"); } + Blob proto(UnsignedQuery query) { + return new ModelProtoQuery().signAndAddSignature(query, keys); + } + + /** + * Performs check that Blob contains valid proto Query + * @param serialized blob with binary data for check + * @return true if valid + */ + private boolean checkProtoQuery(Blob serialized) { + ByteVector blob = serialized.blob(); + byte bs[] = new byte[(int)blob.size()]; + + for (int i = 0; i < blob.size(); i++) { + bs[i] = (byte)blob.get(i); + } + + try { + Queries.Query.parseFrom(bs); + } catch (InvalidProtocolBufferException e) { + System.out.print("Exception: "); + System.out.println(e.getMessage()); + return false; + } + return true; + } + void setGetAccount() { builder.getAccount("user@test"); } @@ -49,8 +166,11 @@ void emptyQuery() { assertThrows(IllegalArgumentException.class, builder::build); } + /* ====================== GetAccount Tests ====================== */ + + @Test - void outdatedAddPeer() { + void outdatedGetAccount() { setGetAccount(); for (BigInteger i : new BigInteger[]{BigInteger.valueOf(0), BigInteger.valueOf(System.currentTimeMillis() - 100_000_000), @@ -63,87 +183,302 @@ void outdatedAddPeer() { @Test void getAccountWithInvalidCreator() { setGetAccount(); - for (String i : new String[]{"", "invalid", "@invalid", "invalid@"}) { - builder.creatorAccountId(i); - assertThrows(IllegalArgumentException.class, builder::build); + for (String accountName: invalidNameSymbols1) { + for (String domain: validDomains) { + ModelQueryBuilder builder = base().creatorAccountId(accountName + "@" + domain); + assertThrows(IllegalArgumentException.class, builder::build); + } } } - Blob proto(UnsignedQuery query) { - return new ModelProtoQuery().signAndAddSignature(query, keys); + @Test + void getAccountWithInvalidCreatorDomain() { + setGetAccount(); + for (String domain: invalidDomains) { + ModelQueryBuilder builder = base().creatorAccountId("user@" + domain); + assertThrows(IllegalArgumentException.class, builder::build); + } } - /** - * Performs check that Blob contains valid proto Query - * @param serialized blob with binary data for check - * @return true if valid - */ - private boolean checkProtoQuery(Blob serialized) { - ByteVector blob = serialized.blob(); - byte bs[] = new byte[(int)blob.size()]; + @Test + void getAccountWithEmptyCreator() { + setGetAccount(); + builder.creatorAccountId(""); + assertThrows(IllegalArgumentException.class, builder::build); + } - for (int i = 0; i < blob.size(); i++) { - bs[i] = (byte)blob.get(i); + @Test + void getAccount() { + for (String accountName: validNameSymbols1) { + UnsignedQuery query = builder.getAccount(accountName + "@test").build(); + assertTrue(checkProtoQuery(proto(query))); } + } - try { - Queries.Query.parseFrom(bs); - } catch (InvalidProtocolBufferException e) { - System.out.print("Exception: "); - System.out.println(e.getMessage()); - return false; + @Test + void getAccountWithInvalidName() { + for (String accountName: invalidNameSymbols1) { + ModelQueryBuilder builder = base().getAccount(accountName + "@test"); + assertThrows(IllegalArgumentException.class, builder::build); } - return true; } @Test - void getAccount() { - UnsignedQuery query = builder.getAccount("user@test").build(); - assertTrue(checkProtoQuery(proto(query))); + void getAccountWithInvalidDomain() { + for (String domain: invalidDomains) { + ModelQueryBuilder builder = base().getAccount("admin@" + domain); + assertThrows(IllegalArgumentException.class, builder::build); + } } + /* ====================== GetSignatories Tests ====================== */ + @Test void getSignatories() { - UnsignedQuery query = builder.getSignatories("user@test").build(); - assertTrue(checkProtoQuery(proto(query))); + for (String account: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedQuery query = builder.getSignatories(account + "@" + domain).build(); + assertTrue(checkProtoQuery(proto(query))); + } + } } + @Test + void getSignatoriesInvalidAccount() { + for (String account: invalidNameSymbols1) { + ModelQueryBuilder builder = base().getSignatories(account + "@test"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getSignatoriesEmptyAccount() { + builder.getSignatories(""); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void getSignatoriesInvalidDomain() { + for (String domain: invalidDomains) { + ModelQueryBuilder builder = base().getSignatories("user@" + domain); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + /* ====================== GetAccountTransactions Tests ====================== */ + @Test void getAccountTransactions() { - UnsignedQuery query = builder.getAccountTransactions("user@test").build(); - assertTrue(checkProtoQuery(proto(query))); + for (String account: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedQuery query = builder.getAccountTransactions(account + "@" + domain).build(); + assertTrue(checkProtoQuery(proto(query))); + } + } + } + + @Test + void getAccountTransactionsInvalidName() { + for (String account: invalidNameSymbols1) { + ModelQueryBuilder builder = base().getAccountTransactions(account + "@test"); + assertThrows(IllegalArgumentException.class, builder::build); + } } + @Test + void getAccountTransactionsInvalidDomain() { + for (String domain: invalidDomains) { + ModelQueryBuilder builder = base().getAccountTransactions("user@" + domain); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAccountTransactionsEmptyAccount() { + ModelQueryBuilder builder = base().getAccountTransactions(""); + assertThrows(IllegalArgumentException.class, builder::build); + } + + /* ====================== GetAccountAssetTransactions Tests ====================== */ + @Test void getAccountAssetTransactions() { - UnsignedQuery query = builder.getAccountAssetTransactions("user@test", "coin#test").build(); - assertTrue(checkProtoQuery(proto(query))); + for (String name: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedQuery query = builder.getAccountAssetTransactions(name + "@" + domain, name + "#" + domain).build(); + assertTrue(checkProtoQuery(proto(query))); + } + } + } + + @Test + void getAccountAssetTransactionsInvalidAccountName() { + for (String account: invalidNameSymbols1) { + ModelQueryBuilder builder = base().getAccountAssetTransactions(account + "@test", "coin#test"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAccountAssetTransactionsInvalidAccountDomain() { + for (String domain: invalidDomains) { + ModelQueryBuilder builder = base().getAccountAssetTransactions("user@" + domain, "coin#test"); + assertThrows(IllegalArgumentException.class, builder::build); + } } + @Test + void getAccountAssetTransactionsEmptyAccount() { + ModelQueryBuilder builder = base().getAccountAssetTransactions("", "coin#test"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void getAccountAssetTransactionsInvalidAssetName() { + for (String asset: invalidNameSymbols1) { + ModelQueryBuilder builder = base().getAccountAssetTransactions("user@test", asset + "#test"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAccountAssetTransactionsInvalidAssetDomain() { + for (String domain: invalidDomains) { + ModelQueryBuilder builder = base().getAccountAssetTransactions("user@test", "coin#" + domain); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAccountAssetTransactionsEmptyAsset() { + ModelQueryBuilder builder = base().getAccountAssetTransactions("user@test", ""); + assertThrows(IllegalArgumentException.class, builder::build); + } + + /* ====================== GetAccountAssets Tests ====================== */ + @Test void getAccountAssets() { - UnsignedQuery query = builder.getAccountAssets("user@test", "coin#test").build(); - assertTrue(checkProtoQuery(proto(query))); + for (String name: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedQuery query = builder.getAccountAssets(name + "@" + domain, name + "#" + domain).build(); + assertTrue(checkProtoQuery(proto(query))); + } + } + } + + @Test + void getAccountAssetsWithInvalidAccountName() { + for (String account: invalidNameSymbols1) { + ModelQueryBuilder builder = base().getAccountAssets(account + "@test", "coin#test"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAccountAssetsWithInvalidAccountDomain() { + for (String domain: invalidDomains) { + ModelQueryBuilder builder = base().getAccountAssets("user@" + domain, "coin#test"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAccountAssetsWithEmptyAccount() { + ModelQueryBuilder builder = base().getAccountAssets("", "coin#test"); + assertThrows(IllegalArgumentException.class, builder::build); + } + + @Test + void getAccountAssetsWithInvalidAssetName() { + for (String asset: invalidNameSymbols1) { + ModelQueryBuilder builder = base().getAccountAssets("user@test", asset + "#test"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAccountAssetsWithInvalidAssetDomain() { + for (String domain: invalidDomains) { + ModelQueryBuilder builder = base().getAccountAssets("user@test", "coin#" + domain); + assertThrows(IllegalArgumentException.class, builder::build); + } } + @Test + void getAccountAssetsWithEmptyAsset() { + ModelQueryBuilder builder = base().getAccountAssets("user@test", ""); + assertThrows(IllegalArgumentException.class, builder::build); + } + + /* ====================== GetRoles Tests ====================== */ + @Test void getRoles() { UnsignedQuery query = builder.getRoles().build(); assertTrue(checkProtoQuery(proto(query))); } + /* ====================== GetAssetInfo Tests ====================== */ + @Test void getAssetInfo() { - UnsignedQuery query = builder.getAssetInfo("coin#test").build(); - assertTrue(checkProtoQuery(proto(query))); + for (String asset: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedQuery query = builder.getAssetInfo(asset + "#" + domain).build(); + assertTrue(checkProtoQuery(proto(query))); + } + } } + @Test + void getAssetInfoWithInvalidName() { + for (String asset: invalidNameSymbols1) { + ModelQueryBuilder builder = base().getAssetInfo(asset + "#test"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAssetInfoWithInvalidDomain() { + for (String domain: invalidDomains) { + ModelQueryBuilder builder = base().getAssetInfo("coin#" + domain); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAssetInfoWithEmptyName() { + ModelQueryBuilder builder = base().getAssetInfo(""); + assertThrows(IllegalArgumentException.class, builder::build); + } + + /* ====================== GetRolePermissions Tests ====================== */ + @Test void getRolePermissions() { - UnsignedQuery query = builder.getRolePermissions("user").build(); - assertTrue(checkProtoQuery(proto(query))); + for (String role: validNameSymbols1) { + UnsignedQuery query = builder.getRolePermissions(role).build(); + assertTrue(checkProtoQuery(proto(query))); + } + } + + @Test + void getRolePermissionsWithInvalidName() { + for (String role: invalidNameSymbols1) { + ModelQueryBuilder builder = base().getRolePermissions(role); + assertThrows(IllegalArgumentException.class, builder::build); + } } + @Test + void getRolePermissionsWithEmptyName() { + ModelQueryBuilder builder = base().getRolePermissions(""); + assertThrows(IllegalArgumentException.class, builder::build); + } + + /* ====================== GetTransactions Tests ====================== */ + @Test void getTransactions() { HashVector hv = new HashVector(); @@ -156,9 +491,94 @@ void getTransactions() { assertTrue(checkProtoQuery(proto(query))); } + // The following four tests are disabled because there is a need to + // clarify desired behavior. + // TODO igor-egorov, 08.05.2018, IR-1322 + @Disabled + @Test + void getTransactionsWithEmptyVector() { + ModelQueryBuilder builder = base().getTransactions(new HashVector()); + assertThrows(IllegalArgumentException.class, builder::build); + } + + // TODO igor-egorov, 08.05.2018, IR-1325 + @Disabled + @Test + void getTransactionsWithInvalidHashSizes() { + String hashes[] = { + "", + "1", + "1234567890123456789012345678901", + "123456789012345678901234567890123" + }; + + for (String hash: hashes) { + HashVector hv = new HashVector(); + hv.add(new Hash(hash)); + ModelQueryBuilder builder = base().getTransactions(hv); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + // TODO igor-egorov, 08.05.2018, IR-1325 + @Disabled + @Test + void getTransactionsWithOneValidAndOneInvalidHash1() { + Hash valid = new Hash("12345678901234567890123456789012"); + Hash invalid = new Hash("1"); + + HashVector hv = new HashVector(); + hv.add(valid); + hv.add(invalid); + ModelQueryBuilder builder = base().getTransactions(hv); + assertThrows(IllegalArgumentException.class, builder::build); + } + + // TODO igor-egorov, 08.05.2018, IR-1325 + @Disabled + @Test + void getTransactionsWithOneValidAndOneInvalidHash2() { + Hash valid = new Hash("12345678901234567890123456789012"); + Hash invalid = new Hash("1"); + + HashVector hv = new HashVector(); + hv.add(invalid); + hv.add(valid); + ModelQueryBuilder builder = base().getTransactions(hv); + assertThrows(IllegalArgumentException.class, builder::build); + } + + /* ====================== GetAccountDetail Tests ====================== */ + @Test void getAccountDetail() { - UnsignedQuery query = builder.getAccountDetail("user@test").build(); - assertTrue(checkProtoQuery(proto(query))); + for (String account: validNameSymbols1) { + for (String domain: validDomains) { + UnsignedQuery query = builder.getAccountDetail(account + "@" + domain).build(); + assertTrue(checkProtoQuery(proto(query))); + } + } + } + + @Test + void getAccountDetailWithInvalidName() { + for (String account: invalidNameSymbols1) { + ModelQueryBuilder builder = base().getAccountDetail(account + "@test"); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAccountDetailWithInvalidDomain() { + for (String domain: invalidDomains) { + ModelQueryBuilder builder = base().getAccountDetail("user@" + domain); + assertThrows(IllegalArgumentException.class, builder::build); + } + } + + @Test + void getAccountDetailWithEmptyName() { + ModelQueryBuilder builder = base().getAccountDetail(""); + assertThrows(IllegalArgumentException.class, builder::build); } } diff --git a/test/module/shared_model/bindings/builder-test.py b/test/module/shared_model/bindings/builder-test.py index ee261f045c..429d8af667 100644 --- a/test/module/shared_model/bindings/builder-test.py +++ b/test/module/shared_model/bindings/builder-test.py @@ -1,3 +1,8 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + import iroha import unittest import time @@ -9,43 +14,103 @@ # TODO luckychess 8.08.2018 add test for number of methods # in interface and proto implementation IR-1080 -class BuilderTest(unittest.TestCase): - def test_empty_tx(self): - with self.assertRaises(ValueError): - iroha.ModelTransactionBuilder().build() - def generate_base(self): - return iroha.ModelTransactionBuilder().txCounter(123)\ - .createdTime(int(time.time() * 1000))\ - .creatorAccountId("admin@test")\ +# Symbols of type 1 (format [a-z_0-9]{1,32}) are used +# as account_name, asset_name and role_id. +VALID_NAMES_1 = [ + "a", + "asset", + "234234", + "_", + "_123", + "123_23", + "234asset_", + "__", + "12345678901234567890123456789012" +] - def setUp(self): - self.keys = iroha.ModelCrypto().generateKeypair() - self.builder = self.generate_base() +INVALID_NAMES_1 = [ + "", + "A", + "assetV", + "asSet", + "asset%", + "^123", + "verylongassetname_thenameislonger", + "verylongassetname_thenameislongerthanitshouldbe", + "assset-01" +] - def set_add_peer(self): - self.builder.addPeer("123.123.123.123", self.keys.publicKey()) +# Symbols of type 2 (format [A-Za-z0-9_]{1,64}) +# are used as key identifier for setAccountDetail command +VALID_NAMES_2 = [ + "a", + "A", + "1", + "_", + "Key", + "Key0_", + "verylongAndValidKeyName___1110100010___veryveryveryverylongvalid" +] - def test_outdated_add_peer(self): - self.set_add_peer() - for i in [0, int((time.time() - 100000) * 1000), int((time.time() + 1) * 1000)]: - with self.assertRaises(ValueError): - self.builder.createdTime(i).build() +INVALID_NAMES_2 = [ + "", + "Key&", + "key-30", + "verylongAndValidKeyName___1110100010___veryveryveryverylongvalid1", + "@@@" +] - def test_add_peer_with_invalid_creator(self): - self.set_add_peer() - for s in ["invalid", "@invalid", "invalid"]: - with self.assertRaises(ValueError): - self.builder.creatorAccountId(s).build() +VALID_DOMAINS = [ + "test", + "u9EEA432F", + "a-hyphen", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad", + "endWith0", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad" +] - def test_add_peer_with_invalid_key_size(self): - for k in ['9' * 13, '9' * (len(self.keys.publicKey().blob()) - 1), '9' * (len(self.keys.publicKey().blob()) + 1), '']: - with self.assertRaises(ValueError): - self.generate_base().addPeer("123.123.123.123", iroha.PublicKey(k)).build() +INVALID_DOMAINS = [ + "", + " ", + " ", + "9start.with.digit", + "-startWithDash", + "@.is.not.allowed", + "no space is allowed", + "endWith-", + "label.endedWith-.is.not.allowed", + "aLabelMustNotExceeds63charactersALabelMustNotExceeds63characters", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPadP", + "257.257.257.257", + "domain#domain", + "asd@asd", + "ab..cd" +] - def test_add_peer_with_invalid_host(self): - for k in ["257.257.257.257", "host#host", "asd@asd", 'a' * 257, "ab..cd"]: - with self.assertRaises(ValueError) as err: - self.generate_base().addPeer(k, self.keys.publicKey()).build() +INVALID_KEYS = [ + "", + "a", + "1" * 31, + "1" * 33 +] + +class BuilderTest(unittest.TestCase): + + def setUp(self): + self.keys = iroha.ModelCrypto().generateKeypair() + self.pub_key = self.keys.publicKey() + self.builder = self.base() + + def base(self): + return iroha.ModelTransactionBuilder()\ + .createdTime(int(time.time() * 1000))\ + .creatorAccountId("admin@test") def proto(self, tx): return iroha.ModelProtoTransaction().signAndAddSignature(tx, self.keys) @@ -62,41 +127,580 @@ def check_proto_tx(self, blob): return False return True + def set_add_peer(self): + self.builder.addPeer("123.123.123.123", self.keys.publicKey()) + + def test_empty_tx(self): + with self.assertRaises(ValueError): + iroha.ModelTransactionBuilder().build() + + # ====================== AddPeer Tests ====================== + def test_add_peer(self): tx = self.builder.addPeer("123.123.123.123:123", self.keys.publicKey()).build() self.assertTrue(self.check_proto_tx(self.proto(tx))) + def test_add_peer_valid_domains(self): + for domain in VALID_DOMAINS: + tx = self.builder.addPeer("{}:123".format(domain), self.keys.publicKey()).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_outdated_add_peer(self): + self.set_add_peer() + for i in [0, int((time.time() - 100000) * 1000), int((time.time() + 1) * 1000)]: + with self.assertRaises(ValueError): + self.builder.createdTime(i).build() + + def test_add_peer_with_invalid_creator(self): + self.set_add_peer() + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.builder.creatorAccountId("{}@test".format(name)).build() + + def test_add_peer_with_invalid_creator_domain(self): + self.set_add_peer() + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.builder.creatorAccountId("admin@{}".format(domain)).build() + + def test_add_peer_with_empty_creator(self): + self.set_add_peer() + with self.assertRaises(ValueError): + self.builder.creatorAccountId("").build() + + def test_add_peer_with_invalid_key_size(self): + for k in INVALID_KEYS: + with self.assertRaises(ValueError): + self.base().addPeer("123.123.123.123", iroha.PublicKey(k)).build() + + def test_add_peer_with_invalid_domain(self): + for k in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().addPeer(k, self.keys.publicKey()).build() + + # ====================== AddSignatory Tests ====================== + def test_add_signatory(self): - tx = self.builder.addSignatory("admin@test", self.keys.publicKey()).build() - self.assertTrue(self.check_proto_tx(self.proto(tx))) + for name in VALID_NAMES_1: + for domain in VALID_DOMAINS: + tx = self.builder.addSignatory("{}@{}".format(name, domain), self.keys.publicKey()).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_add_signatory_invalid_account_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().addSignatory("{}@test".format(name), self.keys.publicKey()).build() + + def test_add_signatory_empty_account(self): + with self.assertRaises(ValueError): + self.base().addSignatory("", self.keys.publicKey()).build() + + def test_add_signatory_invalid_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().addSignatory("admin@{}".format(domain), self.keys.publicKey()).build() + + def test_add_signatory_invalid_key(self): + for key in INVALID_KEYS: + with self.assertRaises(ValueError): + self.base().addSignatory("admin@test", iroha.PublicKey(key)).build() + + # ====================== AddAssetQuantity Tests ====================== def test_add_asset_quantity(self): tx = self.builder.addAssetQuantity("admin@test", "asset#domain", "12.345").build() self.assertTrue(self.check_proto_tx(self.proto(tx))) + def test_add_asset_quantity_valid_account_and_asset(self): + for name in VALID_NAMES_1: + for domain in VALID_DOMAINS: + tx = self.builder.addAssetQuantity("{}@{}".format(name, domain), "{}#{}".format(name, domain), "100").build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_add_asset_quantity_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().addAssetQuantity("{}@test".format(name), "coin#test", "10").build() + + def test_add_asset_quantity_invalid_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().addAssetQuantity("admin@{}".format(domain), "coin#test", "10").build() + + def test_add_asset_quantity_empty_account(self): + with self.assertRaises(ValueError): + self.base().addAssetQuantity("", "coin#test", "10").build() + + def test_add_asset_quantity_invalid_asset(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().addAssetQuantity("admin@test", "{}#test".format(name), "10").build() + + def test_add_asset_quantity_invalid_asset_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().addAssetQuantity("admin@test", "coin#{}".format(domain), "10").build() + + def test_add_asset_quantity_empty_asset(self): + with self.assertRaises(ValueError): + self.base().addAssetQuantity("admin@test", "", "10").build() + + def test_add_asset_quantity_invalid_amount(self): + for amount in ["", "-12", "-13.45", "chars", "chars10"]: + with self.assertRaises(ValueError): + self.base().addAssetQuantity("admin@test", "coin#test", amount).build() + + # ====================== RemoveSignatory Tests ====================== + def test_remove_signatory(self): - tx = self.builder.removeSignatory("admin@test", self.keys.publicKey()).build() - self.assertTrue(self.check_proto_tx(self.proto(tx))) + for name in VALID_NAMES_1: + for domain in VALID_DOMAINS: + tx = self.builder.removeSignatory("{}@{}".format(name, domain), self.keys.publicKey()).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_remove_signatory_empty_account(self): + with self.assertRaises(ValueError): + self.base().removeSignatory("", self.keys.publicKey()).build() + + def test_remove_signatory_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().removeSignatory("{}@test".format(name), self.keys.publicKey()).build() + + def test_remove_signatory_invalid_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().removeSignatory("admin@{}".format(domain), self.keys.publicKey()).build() + + def test_remove_signatory_invalid_key(self): + for key in INVALID_KEYS: + with self.assertRaises(ValueError): + self.base().removeSignatory("admin@test", iroha.PublicKey(key)).build() + + # ====================== CreateAccount Tests ====================== def test_create_account(self): - tx = self.builder.createAccount("admin", "domain", self.keys.publicKey()).build() - self.assertTrue(self.check_proto_tx(self.proto(tx))) + for name in VALID_NAMES_1: + for domain in VALID_DOMAINS: + tx = self.builder.createAccount(name, domain, self.keys.publicKey()).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_create_account_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().createAccount(name, "domain", self.keys.publicKey()).build() + + def test_create_account_invalid_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().createAccount("admin", domain, self.keys.publicKey()).build() + + def test_create_account_invalid_key(self): + for key in INVALID_KEYS: + with self.assertRaises(ValueError): + self.base().createAccount("admin", "test", iroha.PublicKey(key)).build() + + # ====================== CreateDomain Tests ====================== def test_create_domain(self): - tx = self.builder.createDomain("domain", "role").build() - self.assertTrue(self.check_proto_tx(self.proto(tx))) + for domain in VALID_DOMAINS: + for role in VALID_NAMES_1: + tx = self.builder.createDomain(domain, role).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_create_domain_with_invalid_name(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().createDomain(domain, "role").build() + + def test_create_domain_invalid_role(self): + for role in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().createDomain("test", role).build() + + # ====================== SetAccountQuorum Tests ====================== def test_set_account_quorum(self): - tx = self.builder.setAccountQuorum("admin@test", 123).build() - self.assertTrue(self.check_proto_tx(self.proto(tx))) + for name in VALID_NAMES_1: + for domain in VALID_DOMAINS: + tx = self.builder.setAccountQuorum("{}@{}".format(name, domain), 128).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_set_account_quorum_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().setAccountQuorum("{}@test".format(name), 123).build() + + def test_set_account_quorum_invalid_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().setAccountQuorum("admin@{}".format(domain), 123).build() + + def test_set_account_quorum_empty_account(self): + with self.assertRaises(ValueError): + self.base().setAccountQuorum("", 123).build() + + def test_set_account_quorum_invalid_quantity(self): + with self.assertRaises(OverflowError): + self.base().setAccountQuorum("admin@test", -100).build() + + for amount in [0, 129]: + with self.assertRaises(ValueError): + self.base().setAccountQuorum("admin@test", amount).build() + + # ====================== TransferAsset Tests ====================== def test_transfer_asset(self): - tx = self.builder.transferAsset("from@test", "to@test", "asset#test", "description", "123.456").build() - self.assertTrue(self.check_proto_tx(self.proto(tx))) + for domain in VALID_DOMAINS: + for i in range(0, len(VALID_NAMES_1)): + from_acc = "{}@{}".format(VALID_NAMES_1[i], domain) + to = "{}@{}".format(VALID_NAMES_1[(i + 1) % len(VALID_NAMES_1)], domain) + asset = "{}#{}".format(VALID_NAMES_1[(i + 2) % len(VALID_NAMES_1)], domain) + tx = self.builder.transferAsset(from_acc, to, asset, "description", "123.456").build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_transfer_asset_with_invalid_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().transferAsset("from@test", "to@test", "{}#test".format(name), "description", "100").build() + + def test_transfer_asset_with_invalid_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().transferAsset("from@test", "to@test", "coin#{}".format(domain), "description", "100").build() + + def test_transfer_asset_with_empty_name(self): + with self.assertRaises(ValueError): + self.base().transferAsset("from@test", "to@test", "", "description", "100").build() + + def test_transfer_asset_invalid_from_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().transferAsset("{}@test".format(name), "to@test", "coin#test", "description", "100").build() + + def test_transfer_asset_invalid_from_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().transferAsset("from@{}".format(domain), "to@test", "coin#test", "description", "100").build() + + def test_transfer_asset_empty_from_account(self): + with self.assertRaises(ValueError): + self.base().transferAsset("", "to@test", "coin#test", "description", "100").build() + + def test_transfer_asset_invalid_to_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().transferAsset("from@test", "{}@test".format(name), "coin#test", "description", "100").build() + + def test_transfer_asset_invalid_to_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().transferAsset("from@test", "to@{}".format(domain), "coin#test", "description", "1").build() + + def test_transfer_asset_empty_to_account(self): + with self.assertRaises(ValueError): + self.base().transferAsset("from@test", "", "coin#test", "description", "1").build() + + def test_transfer_asset_description_valid_values(self): + for descr in ["", "a" * 64]: + tx = self.builder.transferAsset("from@test", "to@test", "coin#test", descr, "1").build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_transfer_asset_invalid_description(self): + descr = "a" * 65 + with self.assertRaises(ValueError): + self.base().transferAsset("from@test", "to@test", "coin#test", descr, "1").build() + + def test_transfer_asset_maximum_amount(self): + max_uint_256 = str(2 ** 256 - 1) + max_uint_256_2 = max_uint_256[:10] + '.' + max_uint_256[10:] + oversized = str(2 ** 256) + + for amount in [max_uint_256, max_uint_256_2]: + tx = self.builder.transferAsset("from@test", "to@test", "coin#test", "descr", amount).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + with self.assertRaises(ValueError): + self.base().transferAsset("from@test", "to@test", "coin#test", "descr", oversized).build() + + # ====================== SetAccountDetail Tests ====================== def test_set_account_detail(self): - tx = self.builder.setAccountDetail("admin@test", "fyodor", "kek").build() + for name in VALID_NAMES_1: + for domain in VALID_DOMAINS: + tx = self.builder.setAccountDetail("{}@{}".format(name, domain), "fyodor", "kek").build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_set_account_detail_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().setAccountDetail("{}@test".format(name), "fyodor", "true").build() + + def test_set_account_detail_invalid_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().setAccountDetail("admin@{}".format(domain), "fyodor", "true").build() + + def test_set_account_detail_empty_account(self): + with self.assertRaises(ValueError): + self.base().setAccountDetail("", "fyodor", "true").build() + + def test_set_account_detail_valid_key(self): + for key in VALID_NAMES_2: + tx = self.builder.setAccountDetail("admin@test", key, "true").build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_set_account_detail_invalid_key(self): + for key in INVALID_NAMES_2: + with self.assertRaises(ValueError): + self.base().setAccountDetail("admin@test", key, "true").build() + + def test_set_account_detail_valid_value(self): + length = 4 * 1024 * 1024 + value = "a" * length + + for v in ["", value]: + tx = self.builder.setAccountDetail("admin@test", "fyodor", v).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_set_account_detail_oversized_value(self): + length = 4 * 1024 * 1024 + 1 + value = "a" * length + + with self.assertRaises(ValueError): + self.base().setAccountDetail("admin@test", "fyodor", value).build() + + # ====================== AppendRole Tests ====================== + + def test_append_role(self): + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + tx = self.builder.appendRole("{}@{}".format(name, domain), name).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_append_role_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().appendRole("{}@test".format(name), "user").build() + + def test_append_role_invalid_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().appendRole("admin@{}".format(domain), "user").build() + + def test_append_role_empty_account(self): + with self.assertRaises(ValueError): + self.base().appendRole("", "user").build() + + def test_append_role_with_invalid_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().appendRole("admin@test", name).build() + + # ====================== CreateAsset Tests ====================== + + def test_create_asset(self): + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + tx = self.builder.createAsset(name, domain, 6).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_create_asset_invalid_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().createAsset(name, "test", 6).build() + + def test_create_asset_invalid_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().createAsset("asset", domain, 6).build() + + def test_create_asset_zero_precision(self): + tx = self.builder.createAsset("asset", "test", 0).build() self.assertTrue(self.check_proto_tx(self.proto(tx))) + # ====================== CreateRole Tests ====================== + + def test_create_role(self): + permissions = iroha.StringVector() + permissions.append("can_receive") + permissions.append("can_get_roles") + self.assertTrue(permissions.size() == 2) + + for name in VALID_NAMES_1: + tx = self.builder.createRole(name, permissions).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_create_role_with_invalid_name(self): + permissions = iroha.StringVector() + permissions.append("can_receive") + permissions.append("can_get_roles") + + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().createRole(name, permissions).build() + + def test_create_role_with_empty_permissions(self): + permissions = iroha.StringVector() + self.assertTrue(permissions.size() == 0) + + with self.assertRaises(ValueError): + self.base().createRole("user", permissions).build() + + # TODO igor-egorov, 11.05.2018, IR-1267 + @unittest.skip("Disabled till IR-1267 will be fixed") + def test_create_role_wrong_permissions(self): + permissions = iroha.StringVector() + permissions.append("wrong_permission") + permissions.append("can_receive") + + with self.assertRaises(ValueError): + self.base().createRole("user", permissions).build() + + # TODO igor-egorov, 11.05.2018, IR-1267 + @unittest.skip("Disabled till IR-1267 will be fixed") + def test_create_role_wrong_permissions_2(self): + permissions = iroha.StringVector() + permissions.append("can_receive") + permissions.append("wrong_permission") + + with self.assertRaises(ValueError): + self.base().createRole("user", permissions).build() + + # ====================== DetachRole Tests ====================== + + def test_detach_role(self): + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + tx = self.builder.detachRole("{}@{}".format(name, domain), "role").build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_detach_role_with_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().detachRole("{}@test".format(name), "role").build() + + def test_detach_role_with_invalid_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().detachRole("admin@{}".format(domain), "role").build() + + def test_detach_role_with_empty_account(self): + with self.assertRaises(ValueError): + self.base().detachRole("", "role").build() + + def test_detach_role_with_invalid_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().detachRole("admin@test", name).build() + + # ====================== GrantPermission Tests ====================== + + def test_grant_permission(self): + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + tx = self.builder.grantPermission("{}@{}".format(name, domain), "can_set_my_quorum").build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_grant_permission_empty_account(self): + with self.assertRaises(ValueError): + self.base().grantPermission("", "can_set_my_quorum").build() + + def test_grant_permission_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().grantPermission("{}@test".format(name), "can_set_my_quorum").build() + + def test_grant_permission_invalid_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().grantPermission("admin@{}".format(domain), "can_set_my_quorum").build() + + def test_grant_permission_with_invalid_name(self): + permissions = [ + "", + "random", + "can_read_assets" # non-grantable permission + ] + + for perm in permissions: + with self.assertRaises(ValueError): + self.base().grantPermission("admin@test", perm).build() + + # ====================== RevokePermission Tests ====================== + + def test_revoke_permission(self): + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + tx = self.builder.revokePermission("{}@{}".format(name, domain), "can_set_my_quorum").build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_revoke_permission_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().revokePermission("{}@test".format(name), "can_set_my_quorum").build() + + def test_revoke_permission_invalid_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().revokePermission("admin@{}".format(domain), "can_set_my_quorum").build() + + def test_revoke_permission_empty_account(self): + with self.assertRaises(ValueError): + self.base().revokePermission("", "can_set_my_quorum").build() + + def test_revoke_permission_with_invalid_name(self): + permissions = [ + "", + "random", + "can_read_assets" # non-grantable permission + ] + + for perm in permissions: + with self.assertRaises(ValueError): + self.base().revokePermission("admin@test", perm).build() + + # ====================== SubtractAssetQuantity Tests ====================== + + def test_subtract_asset_quantity(self): + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + tx = self.builder.subtractAssetQuantity("{}@{}".format(name, domain), "{}#{}".format(name, domain), "10").build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) + + def test_subtract_asset_quantity_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().subtractAssetQuantity("{}@test".format(name), "coin#test", "10").build() + + def test_subtract_asset_quantity_invalid_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().subtractAssetQuantity("admin@{}".format(domain), "coin#test", "10").build() + + def test_subtract_asset_quantity_with_empty_account(self): + with self.assertRaises(ValueError): + self.base().subtractAssetQuantity("", "coin#test", "10").build() + + def test_subtract_asset_quantity_invalid_asset_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().subtractAssetQuantity("admin@test", "{}#test".format(name), "10").build() + + def test_subtract_asset_quantity_invalid_asset_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().subtractAssetQuantity("admin@test", "coin#{}".format(domain), "10").build() + + def test_subtract_asset_quantity_empty_account(self): + with self.assertRaises(ValueError): + self.base().subtractAssetQuantity("admin@test", "", "10").build() + + def test_subtract_asset_quantity_invalid_amount(self): + amounts = ["", "0", "chars", "-10", "10chars", "10.10.10"] + for amount in amounts: + with self.assertRaises(ValueError): + self.base().subtractAssetQuantity("admin@test", "coin#test", amount).build() + if __name__ == '__main__': unittest.main() diff --git a/test/module/shared_model/bindings/query-test.py b/test/module/shared_model/bindings/query-test.py index a85854d1f4..42d38faa8e 100644 --- a/test/module/shared_model/bindings/query-test.py +++ b/test/module/shared_model/bindings/query-test.py @@ -1,3 +1,8 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + import iroha import unittest import time @@ -8,34 +13,105 @@ # TODO luckychess 8.08.2018 add test for number of methods # in interface and proto implementation IR-1080 -class BuilderTest(unittest.TestCase): - def test_empty_query(self): - with self.assertRaises(ValueError): - iroha.ModelQueryBuilder().build() +# Symbols of type 1 (format [a-z_0-9]{1,32}) are used +# as account_name, asset_name and role_id. +VALID_NAMES_1 = [ + "a", + "asset", + "234234", + "_", + "_123", + "123_23", + "234asset_", + "__", + "12345678901234567890123456789012" +] - def generate_base(self): - return iroha.ModelQueryBuilder().queryCounter(123)\ - .createdTime(int(time.time() * 1000))\ - .creatorAccountId("admin@test")\ +INVALID_NAMES_1 = [ + "", + "A", + "assetV", + "asSet", + "asset%", + "^123", + "verylongassetname_thenameislonger", + "verylongassetname_thenameislongerthanitshouldbe", + "assset-01" +] + +# Symbols of type 2 (format [A-Za-z0-9_]{1,64}) +# are used as key identifier for setAccountDetail command +VALID_NAMES_2 = [ + "a", + "A", + "1", + "_", + "Key", + "Key0_", + "verylongAndValidKeyName___1110100010___veryveryveryverylongvalid" +] + +INVALID_NAMES_2 = [ + "", + "Key&", + "key-30", + "verylongAndValidKeyName___1110100010___veryveryveryverylongvalid1", + "@@@" +] + +VALID_DOMAINS = [ + "test", + "u9EEA432F", + "a-hyphen", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad", + "endWith0", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad" +] + +INVALID_DOMAINS = [ + "", + " ", + " ", + "9start.with.digit", + "-startWithDash", + "@.is.not.allowed", + "no space is allowed", + "endWith-", + "label.endedWith-.is.not.allowed", + "aLabelMustNotExceeds63charactersALabelMustNotExceeds63characters", + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPad." + + "maxLabelLengthIs63paddingPaddingPaddingPaddingPaddingPaddingPadP", + "257.257.257.257", + "domain#domain", + "asd@asd", + "ab..cd" +] + +INVALID_KEYS = [ + "", + "a", + "1" * 31, + "1" * 33 +] + +class BuilderTest(unittest.TestCase): def setUp(self): self.keys = iroha.ModelCrypto().generateKeypair() - self.builder = self.generate_base() + self.builder = self.base() def set_get_account(self): self.builder.getAccount("user@test") - def test_outdated_add_peer(self): - self.set_get_account() - for i in [0, int((time.time() - 100000) * 1000), int((time.time() + 1) * 1000)]: - with self.assertRaises(ValueError): - self.builder.createdTime(i).build() - - def test_add_peer_with_invalid_creator(self): - self.set_get_account() - for s in ["invalid", "@invalid", "invalid"]: - with self.assertRaises(ValueError): - self.builder.creatorAccountId(s).build() + def base(self): + return iroha.ModelQueryBuilder().queryCounter(123)\ + .createdTime(int(time.time() * 1000))\ + .creatorAccountId("admin@test") def proto(self, query): return iroha.ModelProtoQuery().signAndAddSignature(query, self.keys) @@ -46,51 +122,291 @@ def check_proto_query(self, blob): tmp = ''.join(map(chr, blob.blob())) else: tmp = bytes(blob.blob()) - qry.Query.FromString(tmp) + qry.Query.FromString(tmp) except DecodeError as e: print(e) return False return True + def test_empty_query(self): + with self.assertRaises(ValueError): + iroha.ModelQueryBuilder().build() + + # ====================== GetAccount Tests ====================== + def test_get_account(self): - query = self.builder.getAccount("user@test").build() - self.assertTrue(self.check_proto_query(self.proto(query))) + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + query = self.builder.getAccount("{}@{}".format(name, domain)).build() + self.assertTrue(self.check_proto_query(self.proto(query))) + + def test_outdated_get_account(self): + self.set_get_account() + for i in [0, int((time.time() - 100000) * 1000), int((time.time() + 1) * 1000)]: + with self.assertRaises(ValueError): + self.builder.createdTime(i).build() + + def test_get_account_with_invalid_creator(self): + self.set_get_account() + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.builder.creatorAccountId("{}@test".format(name)).build() + + def test_get_account_with_invalid_creator_domain(self): + self.set_get_account() + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.builder.creatorAccountId("admin@{}".format(domain)).build() + + def test_get_account_with_empty_creator_domain(self): + self.set_get_account() + with self.assertRaises(ValueError): + self.builder.creatorAccountId("").build() + + def test_get_account_invalid_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().getAccount("{}@test".format(name)).build() + + def test_get_account_invalid_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().getAccount("admin@{}".format(domain)).build() + + def test_get_account_with_empty_name(self): + with self.assertRaises(ValueError): + self.base().getAccount("").build() + + # ====================== GetSignatories Tests ====================== def test_get_signatories(self): - query = self.builder.getSignatories("user@test").build() - self.assertTrue(self.check_proto_query(self.proto(query))) + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + query = self.builder.getSignatories("{}@{}".format(name, domain)).build() + self.assertTrue(self.check_proto_query(self.proto(query))) + + def test_get_signatories_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().getSignatories("{}@test".format(name)).build() + + def test_get_signatories_invalid_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().getSignatories("admin@{}".format(domain)).build() + + def test_get_signatories_empty_account(self): + with self.assertRaises(ValueError): + self.base().getSignatories("").build() + + # ====================== GetAccountTransactions Tests ====================== def test_get_account_transactions(self): - query = self.builder.getAccountTransactions("user@test").build() - self.assertTrue(self.check_proto_query(self.proto(query))) + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + query = self.builder.getAccountTransactions("{}@{}".format(name, domain)).build() + self.assertTrue(self.check_proto_query(self.proto(query))) + + def test_get_account_transactions_invalid_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().getAccountTransactions("{}@test".format(name)).build() + + def test_get_account_transactions_invalid_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().getAccountTransactions("admin@{}".format(domain)).build() + + def test_get_account_transactions_with_empty_account(self): + with self.assertRaises(ValueError): + self.base().getAccountTransactions("").build() + + # ====================== GetAccountAssetTransactions Tests ====================== def test_get_account_asset_transactions(self): - query = self.builder.getAccountAssetTransactions("user@test", "coin#test").build() - self.assertTrue(self.check_proto_query(self.proto(query))) + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + query = self.builder.getAccountAssetTransactions("{}@{}".format(name, domain), "{}#{}".format(name, domain)).build() + self.assertTrue(self.check_proto_query(self.proto(query))) + + def test_get_account_asset_transactions_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().getAccountAssetTransactions("{}@test".format(name), "coin#test").build() + + def test_get_account_asset_transactions_invalid_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().getAccountAssetTransactions("admin@{}".format(domain), "coin#test").build() + + def test_get_account_asset_transactions_empty_account(self): + with self.assertRaises(ValueError): + self.base().getAccountAssetTransactions("", "coin#test").build() + + def test_get_account_asset_transactions_invalid_asset_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().getAccountAssetTransactions("admin@test", "{}#test".format(name)).build() + + def test_get_account_asset_transactions_invalid_asset_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().getAccountAssetTransactions("admin@test", "admin#{}".format(domain)).build() + + def test_get_account_asset_transactions_empty_asset(self): + with self.assertRaises(ValueError): + self.base().getAccountAssetTransactions("admin@test", "").build() + + # ====================== GetAccountAssets Tests ====================== def test_get_account_assets(self): - query = self.builder.getAccountAssets("user@test", "coin#test").build() - self.assertTrue(self.check_proto_query(self.proto(query))) + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + query = self.builder.getAccountAssets("{}@{}".format(name, domain), "{}#{}".format(name, domain)).build() + self.assertTrue(self.check_proto_query(self.proto(query))) + + def test_get_account_assets_invalid_account(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().getAccountAssets("{}@test".format(name), "coin#test").build() + + def test_get_account_assets_invalid_account_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().getAccountAssets("admin@{}".format(domain), "coin#test").build() + + def test_get_account_assets_empty_account(self): + with self.assertRaises(ValueError): + self.base().getAccountAssets("", "coin#test").build() + + def test_get_account_assets_invalid_asset_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().getAccountAssets("admin@test", "{}#test".format(name)).build() + + def test_get_account_assets_invalid_asset_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().getAccountAssets("admin@test", "admin#{}".format(domain)).build() + + def test_get_account_assets_empty_asset(self): + with self.assertRaises(ValueError): + self.base().getAccountAssets("admin@test", "").build() + + # ====================== GetRoles Tests ====================== def test_get_roles(self): query = self.builder.getRoles().build() self.assertTrue(self.check_proto_query(self.proto(query))) + # ====================== GetAssetInfo Tests ====================== + def test_get_asset_info(self): - query = self.builder.getAssetInfo("coin#test").build() - self.assertTrue(self.check_proto_query(self.proto(query))) + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + query = self.builder.getAssetInfo("{}#{}".format(name, domain)).build() + self.assertTrue(self.check_proto_query(self.proto(query))) + + def test_get_asset_info_invalid_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().getAssetInfo("{}#test".format(name)).build() + + def test_get_asset_info_invalid_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().getAssetInfo("admin#{}".format(domain)).build() + + def test_get_asset_info_empty_asset_name(self): + with self.assertRaises(ValueError): + self.base().getAssetInfo("").build() + + # ====================== GetRolePermissions Tests ====================== def test_get_role_permissions(self): - query = self.builder.getRolePermissions("user").build() - self.assertTrue(self.check_proto_query(self.proto(query))) + for name in VALID_NAMES_1: + query = self.builder.getRolePermissions(name).build() + self.assertTrue(self.check_proto_query(self.proto(query))) + + def test_get_role_permissions_with_invalid_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().getRolePermissions(name).build() + + # ====================== GetTransactions Tests ====================== def test_get_transactions(self): - query = self.builder.getTransactions([iroha.Hash("1" * 32), iroha.Hash("2" * 32)]).build() + hv = iroha.HashVector() + hv.append(iroha.Hash("1" * 32)) + hv.append(iroha.Hash("2" * 32)) + self.assertTrue(hv.size() == 2) + + query = self.builder.getTransactions(hv).build() self.assertTrue(self.check_proto_query(self.proto(query))) + # TODO igor-egorov, 11.05.2018, IR-1322 + @unittest.skip("IR-1322") + def test_get_transactions_with_empty_vector(self): + with self.assertRaises(ValueError): + self.base().getTransactions(iroha.HashVector()).build() + + # TODO igor-egorov, 11.05.2018, IR-1325 + @unittest.skip("IR-1325") + def test_get_transactions_with_invalid_hash_sizes(self): + hashes = [ + "", + "1", + "1" * 31, + "1" * 33 + ] + for h in hashes: + hv = iroha.HashVector() + hv.append(iroha.Hash(h)) + with self.assertRaises(ValueError): + self.base().getTransactions(hv).build() + + # TODO igor-egorov, 11.05.2018, IR-1325 + @unittest.skip("IR-1325") + def test_get_transactions_with_one_valid_and_one_invalid_hash_1(self): + hv = iroha.HashVector() + hv.append(iroha.Hash("1" * 32)) + hv.append(iroha.Hash("1")) + + with self.assertRaises(ValueError): + self.base().getTransactions(hv).build() + + # TODO igor-egorov, 11.05.2018, IR-1325 + @unittest.skip("IR-1325") + def test_get_transactions_with_one_valid_and_one_invalid_hash_2(self): + hv = iroha.HashVector() + hv.append(iroha.Hash("1")) + hv.append(iroha.Hash("1" * 32)) + + with self.assertRaises(ValueError): + self.base().getTransactions(hv).build() + + + # ====================== GetAccountDetail Tests ====================== + def test_get_account_detail(self): - query = self.builder.getAccountDetail("user@test").build() - self.assertTrue(self.check_proto_query(self.proto(query))) + for domain in VALID_DOMAINS: + for name in VALID_NAMES_1: + query = self.builder.getAccountDetail("{}@{}".format(name, domain)).build() + self.assertTrue(self.check_proto_query(self.proto(query))) + + def test_get_account_detail_invalid_name(self): + for name in INVALID_NAMES_1: + with self.assertRaises(ValueError): + self.base().getAccountDetail("{}@test".format(name)).build() + + def test_get_account_detail_invalid_domain(self): + for domain in INVALID_DOMAINS: + with self.assertRaises(ValueError): + self.base().getAccountDetail("admin@{}".format(domain)).build() + + def test_get_account_detail_with_empty_name(self): + with self.assertRaises(ValueError): + self.base().getAccountDetail("").build() if __name__ == '__main__': unittest.main() diff --git a/test/module/shared_model/builders/common_objects/query_response_builder_test.cpp b/test/module/shared_model/builders/common_objects/query_response_builder_test.cpp index ef67637091..770210f90e 100644 --- a/test/module/shared_model/builders/common_objects/query_response_builder_test.cpp +++ b/test/module/shared_model/builders/common_objects/query_response_builder_test.cpp @@ -33,7 +33,6 @@ const auto hash = std::string(32, '0'); uint64_t height = 1; uint8_t quorum = 2; -uint64_t counter = 1048576; boost::multiprecision::uint256_t valid_value = 1000; auto valid_precision = 1; @@ -337,7 +336,6 @@ TEST(QueryResponseBuilderTest, SignatoriesResponse) { TEST(QueryResponseBuilderTest, TransactionsResponse) { auto transaction = TestTransactionBuilder() .createdTime(created_time) - .txCounter(counter) .creatorAccountId(account_id) .setAccountQuorum(account_id, quorum) .build(); diff --git a/test/module/shared_model/builders/protobuf/test_account_builder.hpp b/test/module/shared_model/builders/protobuf/test_account_builder.hpp new file mode 100644 index 0000000000..1389a3daed --- /dev/null +++ b/test/module/shared_model/builders/protobuf/test_account_builder.hpp @@ -0,0 +1,29 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * 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 + * + * http://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. + */ + +#include "builders/protobuf/common_objects/proto_account_builder.hpp" + +#ifndef IROHA_TEST_ACCOUNT_BUILDER_HPP +#define IROHA_TEST_ACCOUNT_BUILDER_HPP + +/** + * Builder alias, to build shared model proto block object avoiding validation + * and "required fields" check + */ +using TestAccountBuilder = shared_model::proto::AccountBuilder; + +#endif //IROHA_TEST_ACCOUNT_BUILDER_HPP diff --git a/test/module/shared_model/builders/protobuf/test_block_builder.hpp b/test/module/shared_model/builders/protobuf/test_block_builder.hpp index 9fe486a673..b53f88bca9 100644 --- a/test/module/shared_model/builders/protobuf/test_block_builder.hpp +++ b/test/module/shared_model/builders/protobuf/test_block_builder.hpp @@ -27,6 +27,6 @@ */ using TestBlockBuilder = shared_model::proto::TemplateBlockBuilder< (1 << shared_model::proto::TemplateBlockBuilder<>::total) - 1, - shared_model::validation::BlockAlwaysValidValidator, + shared_model::validation::AlwaysValidValidator, shared_model::proto::Block>; #endif // IROHA_TEST_BLOCK_BUILDER_HPP diff --git a/test/module/shared_model/builders/protobuf/test_domain_builder.hpp b/test/module/shared_model/builders/protobuf/test_domain_builder.hpp new file mode 100644 index 0000000000..257538beae --- /dev/null +++ b/test/module/shared_model/builders/protobuf/test_domain_builder.hpp @@ -0,0 +1,29 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * 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 + * + * http://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. + */ + +#include "builders/protobuf/common_objects/proto_domain_builder.hpp" + +#ifndef IROHA_TEST_DOMAIN_BUILDER_HPP +#define IROHA_TEST_DOMAIN_BUILDER_HPP + +/** + * Builder alias, to build shared model proto block object avoiding validation + * and "required fields" check + */ +using TestDomainBuilder = shared_model::proto::DomainBuilder; + +#endif //IROHA_TEST_DOMAIN_BUILDER_HPP diff --git a/test/module/shared_model/builders/protobuf/test_peer_builder.hpp b/test/module/shared_model/builders/protobuf/test_peer_builder.hpp new file mode 100644 index 0000000000..af5c75d130 --- /dev/null +++ b/test/module/shared_model/builders/protobuf/test_peer_builder.hpp @@ -0,0 +1,29 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * 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 + * + * http://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. + */ + +#include "builders/protobuf/common_objects/proto_peer_builder.hpp" + +#ifndef IROHA_TEST_PEER_BUILDER_HPP +#define IROHA_TEST_PEER_BUILDER_HPP + +/** + * Builder alias, to build shared model proto block object avoiding validation + * and "required fields" check + */ +using TestPeerBuilder = shared_model::proto::PeerBuilder; + +#endif //IROHA_TEST_PEER_BUILDER_HPP diff --git a/test/module/shared_model/builders/protobuf/test_proposal_builder.hpp b/test/module/shared_model/builders/protobuf/test_proposal_builder.hpp index fb8da46423..11bc2f3eba 100644 --- a/test/module/shared_model/builders/protobuf/test_proposal_builder.hpp +++ b/test/module/shared_model/builders/protobuf/test_proposal_builder.hpp @@ -19,6 +19,7 @@ #define IROHA_TEST_PROPOSAL_BUILDER_HPP #include "builders/protobuf/builder_templates/proposal_template.hpp" +#include "module/shared_model/validators/validators.hpp" /** * Builder alias, to build shared model proto proposal object avoiding "required @@ -26,6 +27,6 @@ */ using TestProposalBuilder = shared_model::proto::TemplateProposalBuilder< (1 << shared_model::proto::TemplateProposalBuilder<>::total) - 1, - shared_model::validation::DefaultProposalValidator>; + shared_model::validation::AlwaysValidValidator>; #endif // IROHA_TEST_PROPOSAL_BUILDER_HPP diff --git a/test/module/shared_model/builders/protobuf/test_query_builder.hpp b/test/module/shared_model/builders/protobuf/test_query_builder.hpp index 5324c1d288..3e567eb0a9 100644 --- a/test/module/shared_model/builders/protobuf/test_query_builder.hpp +++ b/test/module/shared_model/builders/protobuf/test_query_builder.hpp @@ -27,12 +27,12 @@ */ using TestQueryBuilder = shared_model::proto::TemplateQueryBuilder< (1 << shared_model::proto::TemplateQueryBuilder<>::total) - 1, - shared_model::validation::QueryAlwaysValidValidator, + shared_model::validation::AlwaysValidValidator, shared_model::proto::Query>; using TestUnsignedQueryBuilder = shared_model::proto::TemplateQueryBuilder< (1 << shared_model::proto::TemplateQueryBuilder<>::total) - 1, - shared_model::validation::QueryAlwaysValidValidator, + shared_model::validation::AlwaysValidValidator, shared_model::proto::UnsignedWrapper>; #endif // IROHA_TEST_QUERY_BUILDER_HPP diff --git a/test/module/shared_model/builders/protobuf/test_query_response_builder.hpp b/test/module/shared_model/builders/protobuf/test_query_response_builder.hpp index f904a80f93..8d1b03d8fe 100644 --- a/test/module/shared_model/builders/protobuf/test_query_response_builder.hpp +++ b/test/module/shared_model/builders/protobuf/test_query_response_builder.hpp @@ -26,7 +26,6 @@ */ using TestQueryResponseBuilder = shared_model::proto::TemplateQueryResponseBuilder< - (1 << shared_model::proto::TemplateQueryResponseBuilder<>::total) - 1, - shared_model::proto::QueryResponse>; + (1 << shared_model::proto::TemplateQueryResponseBuilder<>::total) - 1>; #endif // IROHA_TEST_QUERY_RESPONSE_BUILDER_HPP diff --git a/test/module/shared_model/builders/protobuf/test_signature_builder.hpp b/test/module/shared_model/builders/protobuf/test_signature_builder.hpp new file mode 100644 index 0000000000..55b28182cd --- /dev/null +++ b/test/module/shared_model/builders/protobuf/test_signature_builder.hpp @@ -0,0 +1,29 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * 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 + * + * http://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. + */ + +#ifndef IROHA_TEST_SIGNATURE_BUILDER_HPP +#define IROHA_TEST_SIGNATURE_BUILDER_HPP + +#include "builders/protobuf/common_objects/proto_signature_builder.hpp" + +/** + * Builder alias, for building shared model proto block object avoiding validation + * and "required fields" check + */ +using TestSignatureBuilder = shared_model::proto::SignatureBuilder; + +#endif // IROHA_TEST_SIGNATURE_BUILDER_HPP diff --git a/test/module/shared_model/builders/protobuf/test_transaction_builder.hpp b/test/module/shared_model/builders/protobuf/test_transaction_builder.hpp index 6de9f472f4..9b9f74da50 100644 --- a/test/module/shared_model/builders/protobuf/test_transaction_builder.hpp +++ b/test/module/shared_model/builders/protobuf/test_transaction_builder.hpp @@ -27,13 +27,13 @@ */ using TestTransactionBuilder = shared_model::proto::TemplateTransactionBuilder< (1 << shared_model::proto::TemplateTransactionBuilder<>::total) - 1, - shared_model::validation::TransactionAlwaysValidValidator, + shared_model::validation::AlwaysValidValidator, shared_model::proto::Transaction>; using TestUnsignedTransactionBuilder = shared_model::proto::TemplateTransactionBuilder< (1 << shared_model::proto::TemplateTransactionBuilder<>::total) - 1, - shared_model::validation::TransactionAlwaysValidValidator, + shared_model::validation::AlwaysValidValidator, shared_model::proto::UnsignedWrapper>; #endif // IROHA_TEST_TRANSACTION_BUILDER_HPP diff --git a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp index e8ba2bd544..c48569ea66 100644 --- a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp @@ -44,7 +44,6 @@ class TransportBuilderTest : public ::testing::Test { auto createTransaction() { return TestUnsignedTransactionBuilder() .createdTime(created_time) - .txCounter(counter) .creatorAccountId(account_id) .setAccountQuorum(account_id, quorum) .build() @@ -54,7 +53,6 @@ class TransportBuilderTest : public ::testing::Test { auto createInvalidTransaction() { return TestUnsignedTransactionBuilder() .createdTime(created_time) - .txCounter(counter) .creatorAccountId(invalid_account_id) .setAccountQuorum(account_id, quorum) .build() diff --git a/test/module/shared_model/converters/json_proto_converter_test.cpp b/test/module/shared_model/converters/json_proto_converter_test.cpp index 2dec88ed48..93e7f88415 100644 --- a/test/module/shared_model/converters/json_proto_converter_test.cpp +++ b/test/module/shared_model/converters/json_proto_converter_test.cpp @@ -35,11 +35,9 @@ using namespace shared_model; TEST(JsonProtoConverterTest, JsonToProtoTxTest) { TestTransactionBuilder builder; - shared_model::interface::types::CounterType tx_counter = 1; std::string creator_account_id = "admin@test"; - auto orig_tx = builder.txCounter(tx_counter) - .creatorAccountId(creator_account_id) + auto orig_tx = builder.creatorAccountId(creator_account_id) .createdTime(123) .build(); diff --git a/test/module/shared_model/cryptography/CMakeLists.txt b/test/module/shared_model/cryptography/CMakeLists.txt index 4c7d50cb05..f704652d62 100644 --- a/test/module/shared_model/cryptography/CMakeLists.txt +++ b/test/module/shared_model/cryptography/CMakeLists.txt @@ -10,3 +10,8 @@ target_link_libraries(crypto_usage_test schema iroha_amount ) + +addtest(security_signatures_test security_signatures_test.cpp) +target_link_libraries(security_signatures_test + shared_model_proto_builders + ) diff --git a/test/module/shared_model/cryptography/crypto_usage_test.cpp b/test/module/shared_model/cryptography/crypto_usage_test.cpp index 211258f743..e426eec7ec 100644 --- a/test/module/shared_model/cryptography/crypto_usage_test.cpp +++ b/test/module/shared_model/cryptography/crypto_usage_test.cpp @@ -49,7 +49,6 @@ class CryptoUsageTest : public ::testing::Test { transaction = std::make_unique( TestTransactionBuilder() .creatorAccountId(account_id) - .txCounter(1) .setAccountQuorum(account_id, 2) .build()); @@ -66,17 +65,15 @@ class CryptoUsageTest : public ::testing::Test { template bool verify(const T &signable) const { - return signable.signatures().size() > 0 - and std::all_of( - signable.signatures().begin(), - signable.signatures().end(), - [&signable](const shared_model::detail::PolymorphicWrapper< - shared_model::interface::Signature> &signature) { - return shared_model::crypto::CryptoVerifier<>::verify( - signature->signedData(), - signable.payload(), - signature->publicKey()); - }); + return boost::size(signable.signatures()) > 0 + and std::all_of(signable.signatures().begin(), + signable.signatures().end(), + [&signable](const auto &signature) { + return shared_model::crypto::CryptoVerifier<>::verify( + signature.signedData(), + signable.payload(), + signature.publicKey()); + }); } Blob data; diff --git a/test/module/shared_model/cryptography/security_signatures_test.cpp b/test/module/shared_model/cryptography/security_signatures_test.cpp new file mode 100644 index 0000000000..6be8824a5b --- /dev/null +++ b/test/module/shared_model/cryptography/security_signatures_test.cpp @@ -0,0 +1,58 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "module/shared_model/builders/protobuf/test_block_builder.hpp" +#include "module/shared_model/builders/protobuf/test_signature_builder.hpp" +#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" + +/** + * @given Two signatures with same pub key but different signed + * @when Invoke operator== + * @then Expect true + */ +TEST(SecuritySignature, SignatureOperatorEqual) { + auto first_signature = + TestSignatureBuilder() + .publicKey(shared_model::crypto::PublicKey("one")) + .signedData(shared_model::crypto::Signed("signed_one")) + .build(); + + auto second_signature = + TestSignatureBuilder() + .publicKey(shared_model::crypto::PublicKey("one")) + .signedData(shared_model::crypto::Signed("signed_two")) + .build(); + + ASSERT_TRUE(first_signature == second_signature); +} + +/** + * @given Transaction with given signature + * @when Invoke ::addSignature with same public key but different signed + * @then Expect that second signature wasn't added + */ +TEST(SecuritySignature, TransactionAddsignature) { + auto tx = TestTransactionBuilder().build(); + ASSERT_TRUE(tx.addSignature(shared_model::crypto::Signed("sign_one"), + shared_model::crypto::PublicKey("key_one"))); + ASSERT_FALSE(tx.addSignature(shared_model::crypto::Signed("sign_two"), + shared_model::crypto::PublicKey("key_one"))); +} + +/** + * @given Block with given signature + * @when Invoke ::addSignature with same public key but different signed + * @then Expect that second signature wasn't added + */ +TEST(SecuritySignature, BlockAddSignature) { + auto block = TestBlockBuilder().build(); + ASSERT_TRUE(block.addSignature(shared_model::crypto::Signed("sign_one"), + shared_model::crypto::PublicKey("key_one"))); + ASSERT_FALSE(block.addSignature(shared_model::crypto::Signed("sign_two"), + shared_model::crypto::PublicKey("key_one"))); +} diff --git a/test/module/shared_model/validators/field_validator_test.cpp b/test/module/shared_model/validators/field_validator_test.cpp index 5c801c92f5..201c5de0db 100644 --- a/test/module/shared_model/validators/field_validator_test.cpp +++ b/test/module/shared_model/validators/field_validator_test.cpp @@ -29,10 +29,10 @@ #include "backend/protobuf/common_objects/peer.hpp" #include "builders/protobuf/queries.hpp" #include "builders/protobuf/transaction.hpp" -#include "validators/permissions.hpp" #include "module/shared_model/validators/validators_fixture.hpp" #include "utils/lazy_initializer.hpp" #include "validators/field_validator.hpp" +#include "validators/permissions.hpp" using namespace shared_model; @@ -113,11 +113,11 @@ class FieldValidatorTest : public ValidatorsTest { &FieldValidator::validateDomainId, &FieldValidatorTest::domain_id, domain_id_test_cases)); - for (const auto &field : {"tx_counter", "query_counter"}) { + for (const auto &field : {"query_counter"}) { field_validators.insert(makeValidator(field, &FieldValidator::validateCounter, &FieldValidatorTest::counter, - tx_counter_test_cases)); + counter_test_cases)); } field_validators.insert(makeValidator("quorum", @@ -466,7 +466,7 @@ class FieldValidatorTest : public ValidatorsTest { makeValidCase(&FieldValidatorTest::role_permission, iroha::protocol::RolePermission::can_append_role)}; - std::vector tx_counter_test_cases{ + std::vector counter_test_cases{ makeValidCase(&FieldValidatorTest::counter, 5), makeTestCase("zero_counter", &FieldValidatorTest::counter, @@ -495,10 +495,12 @@ class FieldValidatorTest : public ValidatorsTest { makeValidCase(&FieldValidatorTest::detail_value, "valid value"), makeValidCase(&FieldValidatorTest::detail_value, std::string(4096, '0')), makeValidCase(&FieldValidatorTest::detail_value, ""), - makeInvalidCase("long_value", - "value", - &FieldValidatorTest::detail_value, - std::string(4097, '0'))}; + makeInvalidCase( + "long_value", + "value", + &FieldValidatorTest::detail_value, + // 5 Mb, value greater than can put into one setAccountDetail + std::string(5 * 1024 * 1024, '0'))}; std::vector description_test_cases{ makeValidCase(&FieldValidatorTest::description, "valid description"), @@ -516,14 +518,15 @@ class FieldValidatorTest : public ValidatorsTest { "too big quorum size", &FieldValidatorTest::quorum, 129, false, "")}; std::vector permissionTestCases() { - auto valid_cases = iroha::model::role_perm_group + auto valid_cases = + shared_model::permissions::role_perm_group | boost::adaptors::transformed([&](const auto &permission) { - return this->makeTestCase(permission, - &FieldValidatorTest::permission, - permission, - true, - ""); - }); + return this->makeTestCase(permission, + &FieldValidatorTest::permission, + permission, + true, + ""); + }); std::vector invalid_cases = { makeInvalidCase("non existing permission", "permission", @@ -654,7 +657,7 @@ TEST_F(FieldValidatorTest, CommandFieldsValidation) { */ TEST_F(FieldValidatorTest, TransactionFieldsValidation) { iroha::protocol::Transaction proto_tx; - proto_tx.add_signature(); // at least one signature in message + proto_tx.add_signatures(); // at least one signature in message // iterate over all fields in transaction iterateContainer( diff --git a/test/module/shared_model/validators/transaction_validator_test.cpp b/test/module/shared_model/validators/transaction_validator_test.cpp index 2704c1e108..c6fda28442 100644 --- a/test/module/shared_model/validators/transaction_validator_test.cpp +++ b/test/module/shared_model/validators/transaction_validator_test.cpp @@ -30,12 +30,10 @@ using namespace shared_model; class TransactionValidatorTest : public ValidatorsTest { protected: iroha::protocol::Transaction generateEmptyTransaction() { - shared_model::interface::types::CounterType tx_counter = 1; std::string creator_account_id = "admin@test"; TestTransactionBuilder builder; - auto tx = builder.txCounter(tx_counter) - .creatorAccountId(creator_account_id) + auto tx = builder.creatorAccountId(creator_account_id) .createdTime(created_time) .build() .getTransport(); diff --git a/test/module/shared_model/validators/validators.hpp b/test/module/shared_model/validators/validators.hpp index 86d7e3c775..3ceefbca90 100644 --- a/test/module/shared_model/validators/validators.hpp +++ b/test/module/shared_model/validators/validators.hpp @@ -18,27 +18,18 @@ #ifndef IROHA_VALIDATOR_MOCKS_HPP #define IROHA_VALIDATOR_MOCKS_HPP -#include -#include "interfaces/transaction.hpp" - namespace shared_model { namespace validation { // TODO: kamilsa 01.02.2018 IR-873 Replace all these validators with mock // classes - template struct AlwaysValidValidator { - Answer validate(const Iface &) const { + template + Answer validate(const T &) const { return {}; } }; - using TransactionAlwaysValidValidator = - AlwaysValidValidator; - using BlockAlwaysValidValidator = AlwaysValidValidator; - using ProposalAlwaysValidValidator = - AlwaysValidValidator; - using QueryAlwaysValidValidator = AlwaysValidValidator; } // namespace validation } // namespace shared_model diff --git a/test/regression/CMakeLists.txt b/test/regression/CMakeLists.txt new file mode 100644 index 0000000000..c3e22283c7 --- /dev/null +++ b/test/regression/CMakeLists.txt @@ -0,0 +1,6 @@ +addtest(regression_test regression_test.cpp) + +target_link_libraries(regression_test + application + integration_framework + ) diff --git a/test/regression/regression_test.cpp b/test/regression/regression_test.cpp new file mode 100644 index 0000000000..18fe37f882 --- /dev/null +++ b/test/regression/regression_test.cpp @@ -0,0 +1,92 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * 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 + * + * http://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. + */ + +#include +#include "builders/protobuf/transaction.hpp" +#include "cryptography/crypto_provider/crypto_defaults.hpp" +#include "framework/integration_framework/integration_test_framework.hpp" +#include "interfaces/utils/specified_visitor.hpp" + +constexpr auto kUser = "user@test"; +constexpr auto kAsset = "asset#domain"; +const auto kAdminKeypair = + shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair(); + +/** + * @given ITF instance with Iroha + * @when existing ITF instance was not gracefully shutdown + * @then following ITF instantiation should not cause any errors + */ +TEST(RegressionTest, SequentialInitialization) { + auto tx = shared_model::proto::TransactionBuilder() + .createdTime(iroha::time::now()) + .creatorAccountId(kUser) + .addAssetQuantity(kUser, kAsset, "1.0") + .build() + .signAndAddSignature( + shared_model::crypto::DefaultCryptoAlgorithmType:: + generateKeypair()); + + auto checkStatelessValid = [](auto &status) { + ASSERT_TRUE(boost::apply_visitor( + shared_model::interface::SpecifiedVisitor< + shared_model::interface::StatelessValidTxResponse>(), + status.get())); + }; + auto checkProposal = [](auto &proposal) { + ASSERT_EQ(proposal->transactions().size(), 1); + }; + auto checkBlock = [](auto &block) { + ASSERT_EQ(block->transactions().size(), 0); + }; + { + integration_framework::IntegrationTestFramework(1, [](auto &) {}) + .setInitialState(kAdminKeypair) + .sendTx(tx, checkStatelessValid) + .skipProposal() + .skipBlock(); + } + { + integration_framework::IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) + .sendTx(tx, checkStatelessValid) + .checkProposal(checkProposal) + .checkBlock(checkBlock) + .done(); + } +} + +/** + * @given ITF instance with Iroha + * @when done method is called twice + * @then no errors are caused as the result + */ +TEST(RegressionTest, DoubleCallOfDone) { + integration_framework::IntegrationTestFramework itf(1); + itf.setInitialState(kAdminKeypair).done(); + itf.done(); +} + +/** + * @given non initialized ITF instance + * @when done method is called inside destructor + * @then no exceptions are risen + */ +TEST(RegressionTest, DestructionOfNonInitializedItf) { + integration_framework::IntegrationTestFramework itf( + 1, [](auto &itf) { itf.done(); }); +} diff --git a/test/system/CMakeLists.txt b/test/system/CMakeLists.txt index 9df9aa2e6c..6462cd0118 100644 --- a/test/system/CMakeLists.txt +++ b/test/system/CMakeLists.txt @@ -2,7 +2,7 @@ addtest(irohad_test irohad_test.cpp) target_link_libraries(irohad_test boost pqxx - json_model_converters + rapidjson integration_framework_config_helper libs_common ) diff --git a/test/system/irohad_test.cpp b/test/system/irohad_test.cpp index 0771b1bd9d..26e5473aa9 100644 --- a/test/system/irohad_test.cpp +++ b/test/system/irohad_test.cpp @@ -17,6 +17,8 @@ #include #include +#include +#include #include #include #include @@ -30,7 +32,6 @@ #undef RAPIDJSON_HAS_STDSTRING #include "framework/config_helper.hpp" -#include "model/converters/json_common.hpp" using namespace boost::process; using namespace boost::filesystem; @@ -52,8 +53,10 @@ class IrohadTest : public testing::Test { auto config_copy_json = parse_iroha_config(path_config_.string()); config_copy_json[config_members::PgOpt].SetString(pgopts_.data(), pgopts_.size()); - auto config_copy_string = - iroha::model::converters::jsonToString(config_copy_json); + rapidjson::StringBuffer sb; + rapidjson::PrettyWriter writer(sb); + config_copy_json.Accept(writer); + std::string config_copy_string = sb.GetString(); std::ofstream copy_file(config_copy_); copy_file.write(config_copy_string.data(), config_copy_string.size()); }