diff --git a/README.md b/README.md index 41677adab..e890fe3b8 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,9 @@ Adds Sovrin's token functionality to HyperLedger's Indy-SDK. 1. cargo test +### How to build Libsovtoken from source +* [Windows](doc/build-guides/windows-build.md) + ## How To Contribute diff --git a/devops/Makefile b/devops/Makefile index 0ed194cfd..5f1427b62 100644 --- a/devops/Makefile +++ b/devops/Makefile @@ -45,7 +45,7 @@ FPM_P_VENDOR := Sovrin FPM_P_DESCRIPTION := libsovtoken written in Rust FPM_P_NAME = $(PACKAGE_NAME) FPM_P_VERSION ?= $(SRC_VERSION) -FPM_P_DEPENDS = libindy(>=1.8.2) +FPM_P_DEPENDS = libindy(>=1.8.3~1099) FPM_P_OUTPUT_DIR = $(LIB_TARGET_DIR) FPM_ARGS = $(LIB_DYNAMIC)=/usr/lib/ diff --git a/devops/aws-codebuild/Jenkinsfile.cd b/devops/aws-codebuild/Jenkinsfile.cd index f00bcff61..fa56b61d2 100644 --- a/devops/aws-codebuild/Jenkinsfile.cd +++ b/devops/aws-codebuild/Jenkinsfile.cd @@ -15,6 +15,8 @@ String srcVersion gitHubUserCredId = env.GITHUB_BOT_USER ?: 'sovbot-github' sovrinPackagingRepo = env.SOVRIN_PACKAGING_REPO ?: 'https://github.com/sovrin-foundation/sovrin-packaging' sovrinPackagingBranch = env.SOVRIN_PACKAGING_BRANCH ?: 'master' +LIBINDY_STREAM = "master" +LIBINDY_VERSION = "1.8.3-1099" def downloadPackagingUtils() { git branch: sovrinPackagingBranch, credentialsId: gitHubUserCredId, url: sovrinPackagingRepo @@ -24,7 +26,8 @@ def downloadPackagingUtils() { // TODO set proper labels def nodeLabels = [ codeBuild: env.LIBSOVTOKEN_CODEBUILD_NODE_LABEL ?: 'codebuild', - macos: env.LIBSOVTOKEN_MACOS_NODE_LABEL ?: 'macos', + macos : env.LIBSOVTOKEN_MACOS_NODE_LABEL ?: 'macos', + windows : env.LIBSOVTOKEN_WINDOWS_NODE_LABEL ?: 'win2016', ] def codeBuildPipelines = { @@ -160,11 +163,11 @@ def codeBuildPipelines = { stage('Set release parameters') { logger.info("Finding Release version") // def releaseVersion = env.BRANCH_NAME == 'stable' ? '' : "${lastRevision ? lastRevision[0] + 1: 1}.$BUILD_NUMBER" - def releaseVersion = env.BRANCH_NAME == 'stable' ? '' : "$BUILD_NUMBER" + def releaseVersion = env.BRANCH_NAME == 'stable' ? '' : "$BUILD_NUMBER" logger.info("Release version for sovrin repo: $releaseVersion") // debPVersion = utils.packageVersion('deb', srcVersion, releaseVersion, env.BRANCH_NAME == 'master') - debPVersion = env.BRANCH_NAME == 'stable' ? "$srcVersion": "$srcVersion~$releaseVersion" + debPVersion = env.BRANCH_NAME == 'stable' ? "$srcVersion" : "$srcVersion~$releaseVersion" logger.info("Package version for sovrin repo: $debPVersion") // TODO crate and rpm @@ -253,11 +256,11 @@ def codeBuildPipelines = { } Map builds = [ - xenial: xenialBuild, - android: [ - build: androidBuild, - nodeLabel: "$nodeLabels.codeBuild" - ] + xenial : xenialBuild, + android: [ + build : androidBuild, + nodeLabel: "$nodeLabels.codeBuild" + ] ] builds.failFast = false @@ -277,21 +280,21 @@ def macOSPipeline = { echo "===================== Checks for XCode and Rust environment ========================" INSTALLED_XCODE_VERSION = sh(script: '''xcodebuild -version | head -1 | cut -d' ' -f2''', returnStdout: true) echo "INSTALLED_XCODE_VERSION = ${INSTALLED_XCODE_VERSION} and xcodeMinVersion = ${xcodeMinVersion}" - if ( INSTALLED_XCODE_VERSION <= xcodeMinVersion ) { + if (INSTALLED_XCODE_VERSION <= xcodeMinVersion) { msg = "The XCode version must be greater or equal ${xcodeMinVersion}" echo "${msg}" error(msg) } RUST_HOME_EXIST = sh(script: "test -d ${RUST_PATH} && echo '1' || echo '0' ", returnStdout: true).trim() - if ( RUST_HOME_EXIST == '0' ) { + if (RUST_HOME_EXIST == '0') { msg = "Rust home dir does not exist. Make sure that rust is installed in the ${RUST_PATH}." echo "${msg}" error(msg) } RUSTC_VERSION = sh(script: "${RUST_PATH}/rustc --version || echo '0' ", returnStdout: true).trim() - if ( RUSTC_VERSION == '0' ) { + if (RUSTC_VERSION == '0') { msg = "rustc does not exist. Make sure that rust is installed in the ${RUST_PATH}." echo "${msg}" error(msg) @@ -354,18 +357,125 @@ def macosUpload = { } } -pipelineWrapper({ +def windowsOSPipeline = { + stage("Windows Testing") { + def ws_path = "workspace/${env.JOB_NAME}".replace(' ', '_') + ws(ws_path) { + try { + stage('Checkout sources from SCM') { + checkout scm + } + + stage('Setup dependencies') { + setupRust() + + bat 'wget -O prebuilt.zip "https://repo.sovrin.org/windows/libindy/deps/indy-sdk-deps.zip"' + bat 'unzip prebuilt.zip -d prebuilt' + + String mainVersion = LIBINDY_VERSION.split('-').first() + bat "wget -O indy.zip \"https://repo.sovrin.org/windows/libindy/$LIBINDY_STREAM/$LIBINDY_VERSION/libindy_${mainVersion}.zip\"" + bat 'unzip indy.zip -d indy' + } + + stage('Run Indy pool') { + bat "docker -H $INDY_SDK_SERVER_IP build --build-arg pool_ip=$INDY_SDK_SERVER_IP -f devops/indy-pool/Dockerfile -t indy_pool devops/indy-pool/" + bat "docker -H $INDY_SDK_SERVER_IP run -d --network host --name indy_pool -p 9701-9708:9701-9708 indy_pool" + } + + stage('Testing') { + dir('libsovtoken') { + echo "Libsovtoken Testing: Build" + withEnv([ + "OPENSSL_DIR=$WORKSPACE\\prebuilt", + "SODIUM_LIB_DIR=$WORKSPACE\\prebuilt\\lib", + "LIBINDY_DIR=$WORKSPACE\\indy\\lib", + "PATH=$WORKSPACE\\prebuilt\\lib;$WORKSPACE\\indy\\lib;$PATH", + "RUST_BACKTRACE=1" + ]) { + bat "cargo build --release" + bat "cargo test --release --no-run" + + echo "Libsovtoken Testing: Run tests" + withEnv([ + "RUST_TEST_THREADS=1", + "RUST_LOG=debug", + "TEST_POOL_IP=$INDY_SDK_SERVER_IP" + ]) { + bat "cargo test --release" + } + } + + stash includes: 'target/release/*.dll,target/release/*.dll.lib', name: 'windowsArtifact' + } + } + } finally { + try { + bat "docker -H $INDY_SDK_SERVER_IP stop indy_pool" + } catch (ignore) { + } + try { + bat "docker -H $INDY_SDK_SERVER_IP rm indy_pool" + } catch (ignore) { + } + cleanWs() + } + } + cleanWs() + } +} + +def windowsUpload = { + stage('Upload libsovtoken package to Sovrin repo') { + + if (!srcVersion) { + stage('Resolve current source version') { + srcVersion = utils.srcVersion(projectType: 'rust') + logger.info("Current source version: $srcVersion") + } + } + + sh 'chmod -R 777 devops/windows' + + unstash name: "windowsArtifact" + + withCredentials([file(credentialsId: 'SovrinRepoSSHKey', variable: 'repo_key')]) { + withEnv([ + "SOVRIN_REPO_HOST=$SOVRIN_REPO_HOST", + ]) { + def suffix = getSuffix() + sh "devops/windows/win-zip-and-upload.sh $srcVersion '${repo_key}' $env.BRANCH_NAME $suffix" + } + } + } +} + +def getSuffix() { + if (env.BRANCH_NAME == 'master') { + return "-$env.BUILD_NUMBER" + } else if (env.BRANCH_NAME == 'stable') { + return "" + } else { + error "Invalid branch ${env.BRANCH_NAME}" + } +} + + + pipelineWrapper({ //put code build containers inside a vpc under our dev account env.USE_VPC_CONFIG = true Map builds = [ codeBuild: [ - build: codeBuildPipelines, + build : codeBuildPipelines, nodeLabel: "$nodeLabels.codeBuild" ], - macos: [ - build: macOSPipeline, + macos : [ + build : macOSPipeline, nodeLabel: "$nodeLabels.macos" + ], + windows : [ + build : windowsOSPipeline, + nodeLabel: "$nodeLabels.windows" ] ] builds.failFast = false @@ -375,13 +485,17 @@ pipelineWrapper({ } Map publish = [ - macosPublish: [ - build: macosUpload, + macosPublish : [ + build : macosUpload, + nodeLabel: "$nodeLabels.codeBuild" + ], + windowsPublish: [ + build : windowsUpload, nodeLabel: "$nodeLabels.codeBuild" ] ] - stage ('Publish') { + stage('Publish') { utils.parallel publish } }, { err -> @@ -394,4 +508,8 @@ pipelineWrapper({ notifier.email() } } -}) \ No newline at end of file +}) + +def setupRust() { + shell("rustup default 1.32.0") +} \ No newline at end of file diff --git a/devops/aws-codebuild/Jenkinsfile.ci b/devops/aws-codebuild/Jenkinsfile.ci index e7818dae0..4957696e0 100644 --- a/devops/aws-codebuild/Jenkinsfile.ci +++ b/devops/aws-codebuild/Jenkinsfile.ci @@ -1,29 +1,97 @@ #!groovy def sovLibrary = library(identifier: 'sovrin-aws-codebuild@master', retriever: modernSCM( - github(credentialsId: 'sovbot-github', repoOwner: 'sovrin-foundation', repository: 'aws-codebuild-pipeline-plugin') + github(credentialsId: 'sovbot-github', repoOwner: 'sovrin-foundation', repository: 'aws-codebuild-pipeline-plugin') )).com.sovrin.pipeline logger = sovLibrary.Logger.new(this) notifier = sovLibrary.Notifier.new(this) logger.setGlobalLevel('TRACE') +LIBINDY_STREAM = "master" +LIBINDY_VERSION = "1.8.3-1099" def nodeLabels = [ codeBuild: env.LIBSOVTOKEN_CODEBUILD_NODE_LABEL ?: 'codebuild', macos: env.LIBSOVTOKEN_MACOS_NODE_LABEL ?: 'macos', + windows: env.LIBSOVTOKEN_WINDOWS_NODE_LABEL ?: 'win2016', ] +def windowsOSPipeline = { + stage("Windows Testing") { + def ws_path = "workspace/${env.JOB_NAME}".replace(' ', '_') + ws(ws_path) { + try { + stage('Checkout sources from SCM') { + checkout scm + } + + stage('Setup dependencies'){ + setupRust() + + bat 'wget -O prebuilt.zip "https://repo.sovrin.org/windows/libindy/deps/indy-sdk-deps.zip"' + bat 'unzip prebuilt.zip -d prebuilt' + + String mainVersion = LIBINDY_VERSION.split('-').first() + bat "wget -O indy.zip \"https://repo.sovrin.org/windows/libindy/$LIBINDY_STREAM/$LIBINDY_VERSION/libindy_${mainVersion}.zip\"" + bat 'unzip indy.zip -d indy' + } + + stage('Run Indy pool') { + bat "docker -H $INDY_SDK_SERVER_IP build --build-arg pool_ip=$INDY_SDK_SERVER_IP -f devops/indy-pool/Dockerfile -t indy_pool devops/indy-pool/" + bat "docker -H $INDY_SDK_SERVER_IP run -d --network host --name indy_pool -p 9701-9708:9701-9708 indy_pool" + } + + stage('Testing'){ + dir('libsovtoken') { + echo "Libsovtoken Testing: Build" + withEnv([ + "OPENSSL_DIR=$WORKSPACE\\prebuilt", + "SODIUM_LIB_DIR=$WORKSPACE\\prebuilt\\lib", + "LIBINDY_DIR=$WORKSPACE\\indy\\lib", + "PATH=$WORKSPACE\\prebuilt\\lib;$WORKSPACE\\indy\\lib;$PATH", + "RUST_BACKTRACE=1" + ]) { + bat "cargo build" + bat "cargo test --no-run" + + echo "Libsovtoken Testing: Run tests" + withEnv([ + "RUST_TEST_THREADS=1", + "RUST_LOG=debug", + "TEST_POOL_IP=$INDY_SDK_SERVER_IP" + ]) { + bat "cargo test" + } + } + } + } + } finally { + try { + bat "docker -H $INDY_SDK_SERVER_IP stop indy_pool" + } catch (ignore) { + } + try { + bat "docker -H $INDY_SDK_SERVER_IP rm indy_pool" + } catch (ignore) { + } + cleanWs() + } + } + cleanWs() + } +} + pipelineWrapper({ nodeWrapper(nodeLabels.codeBuild) { List _envBuildSrc = [ - 'devops', - 'libsovtoken/Cargo.toml', - 'libsovtoken/build_scripts/android/libsovtoken/libsovtoken.dependencies.txt', - 'libsovtoken/build_scripts/android/android_settings.txt' + 'devops', + 'libsovtoken/Cargo.toml', + 'libsovtoken/build_scripts/android/libsovtoken/libsovtoken.dependencies.txt', + 'libsovtoken/build_scripts/android/android_settings.txt' ] List osnames = [ - 'xenial', - //'centos7' + 'xenial', + //'centos7' ] List goals = ['test_dry', 'test'] @@ -47,83 +115,88 @@ pipelineWrapper({ Map builds = osnames.collectEntries { osname -> [(osname): [ "nodeLabel": nodeLabels.codeBuild, - "build": { - def buildImageTag - def prTag = "ci-$osname" + "build" : { + def buildImageTag + def prTag = "ci-$osname" - if (osname == 'xenial') { - stage('Download plugin debs') { - // TODO remove that code once repo.corp sovrin.com - // become available from AWS CodeBuild + if (osname == 'xenial') { + stage('Download plugin debs') { + // TODO remove that code once repo.corp sovrin.com + // become available from AWS CodeBuild - // TODO Aptly on repo.copr.sovrin.com removes '+' signs - // from debian packages making versions in filenames - // not accurate (it concatenates them): - // debian package version: + - // debian package name: + // TODO Aptly on repo.copr.sovrin.com removes '+' signs + // from debian packages making versions in filenames + // not accurate (it concatenates them): + // debian package version: + + // debian package name: - def sovtoken_deb_version = "0.9.5" - def sovtokenfees_deb_version = "0.9.5" + def sovtoken_deb_version = "0.9.5" + def sovtokenfees_deb_version = "0.9.5" - sh """ + sh """ cd ./devops/docker/ci/xenial/ wget --no-check-certificate https://repo.sovrin.org/deb/pool/xenial/stable/s/sovtoken/sovtoken_${sovtoken_deb_version}_amd64.deb wget --no-check-certificate https://repo.sovrin.org/deb/pool/xenial/stable/s/sovtokenfees/sovtokenfees_${sovtokenfees_deb_version}_amd64.deb """ - } - } + } + } - stage("$osname: Resolve image tag") { - def _imgVersion = utils.shStdout("OSNAME=$osname make -C devops image_lst_ci_version -s") - buildImageTag = "${_imgVersion}-${osname}-ci" - logger.info("CI docker image tag: $buildImageTag") - } + stage("$osname: Resolve image tag") { + def _imgVersion = utils.shStdout("OSNAME=$osname make -C devops image_lst_ci_version -s") + buildImageTag = "${_imgVersion}-${osname}-ci" + logger.info("CI docker image tag: $buildImageTag") + } - awsCBHelper.build() { - projectTag = prTag - - // env and build spec - imageTag = buildImageTag - buildspec = 'devops/aws-codebuild/buildspec.ci.yml' - envv = [ - [name: 'OSNAME', value: osname], - [name: 'MAKE_GOALS', value: "${goals.join(' ')}"], - [name: 'INDY_POOL_LOG_LEVEL', value: '10'], - [name: 'INDY_POOL_DIRS', value: '/tmp /var/lib/indy/sandbox /var/log/indy/sandbox'], - ] - computeType = 'medium' - - // build spec for env image - envBuildSrc = _envBuildSrc // TODO make more accurate - envBuildAddPaths = ['./devops/docker/ci/xenial/*.deb'] - envBuildCmds = [ - 'export PROJECT_DIR=$PWD', - 'make -C devops image_lst_ci' - ] - envBuildLocalName = "sovrin/libsovtoken:$buildImageTag" - envBuildEnvv = [ - [name: 'OSNAME', value: osname], - [name: 'LST_CI_DOCKER_TAG', value: buildImageTag], - ] - - onArtifacts = { - this.stage("$osname: Archive artifacts") { - // make targets' logs - utils.archiveArtifacts("logs/*.log*") { - truncate = true - allowEmptyArchive = true - truncateFileSuffix = 'trunc.log' - } - // nodes' logs and validators info - utils.archiveArtifacts("logs/pool/*") { - truncate = false - allowEmptyArchive = true + awsCBHelper.build() { + projectTag = prTag + + // env and build spec + imageTag = buildImageTag + buildspec = 'devops/aws-codebuild/buildspec.ci.yml' + envv = [ + [name: 'OSNAME', value: osname], + [name: 'MAKE_GOALS', value: "${goals.join(' ')}"], + [name: 'INDY_POOL_LOG_LEVEL', value: '10'], + [name: 'INDY_POOL_DIRS', value: '/tmp /var/lib/indy/sandbox /var/log/indy/sandbox'], + ] + computeType = 'medium' + + // build spec for env image + envBuildSrc = _envBuildSrc // TODO make more accurate + envBuildAddPaths = ['./devops/docker/ci/xenial/*.deb'] + envBuildCmds = [ + 'export PROJECT_DIR=$PWD', + 'make -C devops image_lst_ci' + ] + envBuildLocalName = "sovrin/libsovtoken:$buildImageTag" + envBuildEnvv = [ + [name: 'OSNAME', value: osname], + [name: 'LST_CI_DOCKER_TAG', value: buildImageTag], + ] + + onArtifacts = { + this.stage("$osname: Archive artifacts") { + // make targets' logs + utils.archiveArtifacts("logs/*.log*") { + truncate = true + allowEmptyArchive = true + truncateFileSuffix = 'trunc.log' + } + // nodes' logs and validators info + utils.archiveArtifacts("logs/pool/*") { + truncate = false + allowEmptyArchive = true + } + } } } - } - } - }]] + }]] } + + builds.put('windows', [ + build: windowsOSPipeline, + nodeLabel: "$nodeLabels.windows" + ]) stage("Build and test") { builds.failFast = false @@ -139,3 +212,7 @@ pipelineWrapper({ notifier.email() } }) + +def setupRust() { + shell("rustup default 1.32.0") +} diff --git a/devops/docker/base/xenial/Dockerfile b/devops/docker/base/xenial/Dockerfile index a7d72a88f..032dd1a13 100644 --- a/devops/docker/base/xenial/Dockerfile +++ b/devops/docker/base/xenial/Dockerfile @@ -21,9 +21,9 @@ RUN cd /tmp \ # need for libsodium to be reachable via pkg-config (sodiumoxide uses it) ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig:$PKG_CONFIG_PATH # TODO ??? is it really needed -ENV LIBINDY_VERSION=1.8.2 +ENV LIBINDY_VERSION=1.8.3~1099 RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 68DB5E88 \ - && echo "deb https://repo.sovrin.org/sdk/deb xenial stable" >> /etc/apt/sources.list \ + && echo "deb https://repo.sovrin.org/sdk/deb xenial master" >> /etc/apt/sources.list \ && apt-get update && apt-get install -y --no-install-recommends \ libssl-dev \ libindy=${LIBINDY_VERSION} \ @@ -47,4 +47,4 @@ RUN cd /tmp/libsovtoken \ # TODO CMD ENTRYPOINT ... -ENV LIBSOVTOKEN_BASE_ENV_VERSION=0.19.0 +ENV LIBSOVTOKEN_BASE_ENV_VERSION=0.20.0 diff --git a/devops/docker/ci/xenial/Dockerfile b/devops/docker/ci/xenial/Dockerfile index 0bebb4f64..f9e0e02d7 100644 --- a/devops/docker/ci/xenial/Dockerfile +++ b/devops/docker/ci/xenial/Dockerfile @@ -1,4 +1,4 @@ -FROM sovrin/libsovtoken:base-xenial-0.19.0 +FROM sovrin/libsovtoken:base-xenial-0.20.0 # TODO LABEL maintainer="Name " ARG LIBINDY_CRYPTO_VERSION @@ -36,12 +36,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # or python3-rocksdb are not specified here) ENV LIBINDY_CRYPTO_VERSION ${LIBINDY_CRYPTO_VERSION:-0.4.5} ENV PYTHON3_INDY_CRYPTO_VERSION ${PYTHON3_INDY_CRYPTO_VERSION:-0.4.5} -ENV INDY_PLENUM_VERSION ${INDY_PLENUM_VERSION:-1.6.53} -ENV INDY_ANONCREDS_VERSION ${INDY_ANONCREDS_VERSION:-1.0.11} -ENV INDY_NODE_VERSION ${INDY_NODE_VERSION:-1.6.78} -ENV TOKEN_VER ${TOKEN_VER:-0.9.5} -RUN echo "deb https://repo.sovrin.org/sdk/deb xenial stable" >> /etc/apt/sources.list -RUN echo "deb https://repo.sovrin.org/deb xenial stable" >> /etc/apt/sources.list \ +ENV INDY_PLENUM_VERSION ${INDY_PLENUM_VERSION:-1.8.0~dev794} +ENV INDY_ANONCREDS_VERSION ${INDY_ANONCREDS_VERSION:-1.0.32} +ENV INDY_NODE_VERSION ${INDY_NODE_VERSION:-1.8.0~dev935} +ENV TOKEN_VER ${TOKEN_VER:-0.9.6~25} +RUN echo "deb https://repo.sovrin.org/sdk/deb xenial master" >> /etc/apt/sources.list +RUN echo "deb https://repo.sovrin.org/deb xenial master" >> /etc/apt/sources.list \ && apt-get update && apt-get install -y --no-install-recommends \ libindy-crypto=${LIBINDY_CRYPTO_VERSION} \ python3-indy-crypto=${PYTHON3_INDY_CRYPTO_VERSION} \ @@ -69,4 +69,4 @@ COPY libsovtoken-ci-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/libsovtoken-ci-entrypoint.sh ENTRYPOINT ["libsovtoken-ci-entrypoint.sh"] -ENV LIBSOVTOKEN_CI_ENV_VERSION=0.55.0 +ENV LIBSOVTOKEN_CI_ENV_VERSION=0.57.0 diff --git a/devops/indy-pool/Dockerfile b/devops/indy-pool/Dockerfile index b995f0c45..d8e6ed98c 100644 --- a/devops/indy-pool/Dockerfile +++ b/devops/indy-pool/Dockerfile @@ -22,12 +22,12 @@ ARG uid=1000 ARG indy_stream=master -ARG indy_plenum_ver=1.6.735 -ARG indy_node_ver=1.6.874 +ARG indy_plenum_ver=1.8.0~dev794 +ARG indy_node_ver=1.8.0~dev935 ARG python3_indy_crypto_ver=0.4.5 ARG indy_crypto_ver=0.4.5 -ARG token_ver=0.9.6~2 -ARG fees_ver=0.9.6~2 +ARG token_ver=0.9.6~25 +ARG fees_ver=0.9.6~25 # Install environment RUN apt-get update -y && apt-get install -y \ diff --git a/devops/windows/win-zip-and-upload.sh b/devops/windows/win-zip-and-upload.sh new file mode 100755 index 000000000..e3c7ed38a --- /dev/null +++ b/devops/windows/win-zip-and-upload.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +set -e +set -x + +if [ "$1" = "--help" ] ; then + echo "Usage: " + return +fi + +version="$1" +key="$2" +type="$3" +suffix="$4" + +[ -z $version ] && exit 1 +[ -z $key ] && exit 2 +[ -z $type ] && exit 3 +[ -z $suffix ] && exit 4 + +PACKAGE_NAME="libsovtoken" +TEMP_ARCH_DIR=./${PACKAGE_NAME}-zip + +mkdir ${TEMP_ARCH_DIR} + +cp ./target/release/*.dll ${TEMP_ARCH_DIR}/ +cp ./target/release/*.dll.lib ${TEMP_ARCH_DIR}/ + +pushd ${TEMP_ARCH_DIR} + zip -r ${PACKAGE_NAME}_${version}.zip ./* + mv ${PACKAGE_NAME}_${version}.zip .. +popd + +rm -rf ${TEMP_ARCH_DIR} + +cat < panic!("Missing required environment variable LIBINDY_DIR") }; + let target = env::var("TARGET").unwrap(); + println!("target={}", target); + + if target.find("-windows-").is_some() { + println!("cargo:rustc-link-lib=indy.dll"); + println!("indy_dir={}", libindy_lib_path); + let libindy_lib_path = Path::new(libindy_lib_path.as_str()); + + println!("cargo:rustc-flags=-L {}", libindy_lib_path.as_os_str().to_str().unwrap()); + return; + } + println!("cargo:rustc-link-search=native={}",libindy_lib_path); if let Ok(_mode) = env::var("LIBINDY_STATIC") { @@ -19,8 +31,6 @@ fn main() { println!("cargo:rustc-link-lib=dylib=indy"); } - let target = env::var("TARGET").unwrap(); - println!("target={}", target); if target.contains("linux-android") { let openssl = match env::var("OPENSSL_LIB_DIR") { @@ -41,12 +51,5 @@ fn main() { println!("cargo:rustc-link-lib=dylib=ssl"); println!("cargo:rustc-link-search=native={}", sodium); println!("cargo:rustc-link-lib=static=sodium"); - }else if target.find("-windows-").is_some() { - println!("cargo:rustc-link-lib=dylib=ssleay32"); - println!("cargo:rustc-link-lib=dylib=zmq"); - println!("cargo:rustc-link-lib=dylib=sodium"); - let prebuilt_dir = env::var("INDY_PREBUILT_DEPS_DIR").unwrap(); - println!("cargo:rustc-flags=-L {}\\lib", prebuilt_dir); - return; } } diff --git a/libsovtoken/build_scripts/ios/mac/shared.functions.sh b/libsovtoken/build_scripts/ios/mac/shared.functions.sh index 6573cefb4..0fef3e720 100644 --- a/libsovtoken/build_scripts/ios/mac/shared.functions.sh +++ b/libsovtoken/build_scripts/ios/mac/shared.functions.sh @@ -1,6 +1,6 @@ #!/bin/sh -export LIBINDY_IOS_BUILD_URL="https://repo.sovrin.org/ios/libindy/stable/libindy-core/1.8.2/libindy.tar.gz" +export LIBINDY_IOS_BUILD_URL="https://repo.sovrin.org/ios/libindy/stable/libindy-core/1.8.3/libindy.tar.gz" export LIBINDY_FILE=$(basename ${LIBINDY_IOS_BUILD_URL}) export LIBINDY_VERSION=$(basename $(dirname ${LIBINDY_IOS_BUILD_URL})) export BUILD_CACHE=~/.build_libvxc/ioscache diff --git a/libsovtoken/src/api/mod.rs b/libsovtoken/src/api/mod.rs index 0a5313283..0e2046d72 100644 --- a/libsovtoken/src/api/mod.rs +++ b/libsovtoken/src/api/mod.rs @@ -189,7 +189,7 @@ pub extern "C" fn add_request_fees_handler( cb: JsonCallback ) -> i32 { - trace!("api::add_request_fees_handler called did (address) >> {:?}", did); + trace!("api::add_request_fees_handler called did (address) >> {:?}", secret!(&did)); let (inputs, outputs, extra, request_json_map, cb) = match add_request_fees::deserialize_inputs(req_json, inputs_json, outputs_json, extra, cb) { Ok(tup) => tup, Err(error_code) => { @@ -357,21 +357,22 @@ pub extern "C" fn build_payment_req_handler( extra: *const c_char, cb: JsonCallback ) -> i32 { - trace!("api::build_payment_req_handler called >> submitter_did (address) {:?}", submitter_did); - let (inputs, outputs, extra, cb) = match build_payment::deserialize_inputs(inputs_json, outputs_json, extra, cb) { - Ok(tup) => tup, - Err(error_code) => { - trace!("api::build_payment_req_handler << result: {:?}", error_code); - return error_code as i32; - } - }; + trace!("api::build_payment_req_handler called >> submitter_did (address) {:?}", secret!(&submitter_did)); + let (inputs, outputs, extra, submitter_did, cb) = + match build_payment::deserialize_inputs(inputs_json, outputs_json, extra, submitter_did, cb) { + Ok(tup) => tup, + Err(error_code) => { + trace!("api::build_payment_req_handler << result: {:?}", error_code); + return error_code as i32; + } + }; let payload = XferPayload::new(inputs, outputs, extra); let result = payload.sign_transfer( &CryptoSdk {}, wallet_handle, - Box::new(move |result| build_payment::handle_signing(command_handle, result, cb)) + Box::new(move |result| build_payment::handle_signing(command_handle, result, submitter_did.clone(), cb)) ); let ec = match result { @@ -479,7 +480,7 @@ pub extern "C" fn build_get_utxo_request_handler(command_handle: i32, return ErrorCode::CommonInvalidStructure as i32; } }; - debug!("api::build_get_utxo_request_handler >> wallet_handle: {:?}, payment_address: {:?}", wallet_handle, payment_address); + debug!("api::build_get_utxo_request_handler >> wallet_handle: {:?}, payment_address: {:?}", wallet_handle, secret!(&payment_address)); let utxo_request = GetUtxoOperationRequest::new(String::from(payment_address)); @@ -656,15 +657,16 @@ pub extern "C" fn build_get_txn_fees_handler( did.validate().map_err(map_err_trace!()).or(Err(ErrorCode::CommonInvalidStructure)) }); - debug!("api::build_get_txn_fees_handler >> wallet_handle: {:?}, submitter_did: {:?}", wallet_handle, did); + debug!("api::build_get_txn_fees_handler >> wallet_handle: {:?}, submitter_did: {:?}", wallet_handle, secret!(&did)); let did = match opt_res_to_res_opt!(did) { Ok(did) => did, - Err(e) => { return e as i32; } + Err(_) => None }; + let did = Some(did.unwrap_or(Did::new("LibsovtokenDid11111111".to_string()))); + let get_txn_request = GetFeesRequest::new().as_request(did); - info!("Built GET_TXN_FEES request: {:?}", get_txn_request); let request_pointer = match get_txn_request.serialize_to_pointer() { Ok(p) => p, diff --git a/libsovtoken/src/lib.rs b/libsovtoken/src/lib.rs index 91f2e9c67..c7c80e066 100644 --- a/libsovtoken/src/lib.rs +++ b/libsovtoken/src/lib.rs @@ -33,7 +33,8 @@ extern crate sha2; // ------------------------------------------ extern crate indy_sys; // lib-sdk project -extern crate indyrs as indy; // lib-sdk rust wrapper to get ErrorCodes +extern crate indyrs as indy; +extern crate core; // lib-sdk rust wrapper to get ErrorCodes // ------------------------------------------ // define our crate by defining the modules in the project diff --git a/libsovtoken/src/logic/api_internals/add_request_fees.rs b/libsovtoken/src/logic/api_internals/add_request_fees.rs index 42e42b36e..a4b08c4ca 100644 --- a/libsovtoken/src/logic/api_internals/add_request_fees.rs +++ b/libsovtoken/src/logic/api_internals/add_request_fees.rs @@ -2,7 +2,7 @@ use ErrorCode; use libc::c_char; -use logic::xfer_payload::{XferPayload, serialize_signature}; +use logic::xfer_payload::{XferPayload, Extra, serialize_signature}; use logic::input::Inputs; use logic::output::Outputs; use serde_json; @@ -11,12 +11,13 @@ use logic::indy_sdk_api::crypto_api::CryptoSdk; use utils::constants::txn_types::XFER_PUBLIC; use utils::constants::txn_fields::FEES; use utils::constants::general::JsonCallbackUnwrapped; +use utils::txn_author_agreement::TaaAcceptance; use sha2::{Sha256, Digest}; use hex::ToHex; type SerdeMap = serde_json::Map; type AddRequestFeesCb = extern fn(command_handle_: i32, err: i32, req_with_fees_json: *const c_char) -> i32; -type DeserializedArguments = (Inputs, Outputs, Option, SerdeMap, AddRequestFeesCb); +type DeserializedArguments = (Inputs, Outputs, Option, SerdeMap, AddRequestFeesCb); /** * Deserializes arguments of [`add_request_fees_handler`] @@ -28,27 +29,32 @@ pub fn deserialize_inputs ( extra: *const c_char, cb: Option ) -> Result { - debug!("logic::add_request_fees::deserialize_inputs >> req_json: {:?}, inputs_json: {:?}, outputs_json: {:?}", req_json, inputs_json, outputs_json); + debug!("logic::add_request_fees::deserialize_inputs >> req_json: {:?}, inputs_json: {:?}, outputs_json: {:?}", secret!(&req_json), secret!(&inputs_json), secret!(&outputs_json)); let cb = cb.ok_or(ErrorCode::CommonInvalidStructure).map_err(map_err_err!())?; let request_json = string_from_char_ptr(req_json).ok_or(ErrorCode::CommonInvalidStructure).map_err(map_err_err!())?; - debug!("Converted request_json pointer into string >>> {:?}", request_json); + debug!("Converted request_json pointer into string >>> {:?}", secret!(&request_json)); let inputs_json = string_from_char_ptr(inputs_json).ok_or(ErrorCode::CommonInvalidStructure).map_err(map_err_err!())?; - debug!("Converted inputs_json pointer to string >>> {:?}", inputs_json); + debug!("Converted inputs_json pointer to string >>> {:?}", secret!(&inputs_json)); let outputs_json = string_from_char_ptr(outputs_json).ok_or(ErrorCode::CommonInvalidStructure).map_err(map_err_err!())?; - debug!("Converted outputs_json pointer to string >>> {:?}", outputs_json); + debug!("Converted outputs_json pointer to string >>> {:?}", secret!(&outputs_json)); let extra = string_from_char_ptr(extra); debug!("Converted extra pointer to string >>> {:?}", extra); let inputs: Inputs = serde_json::from_str(&inputs_json).map_err(map_err_err!()).or(Err(ErrorCode::CommonInvalidStructure))?; - debug!("Deserialized input_json >>> {:?}", inputs); + debug!("Deserialized input_json >>> {:?}", secret!(&inputs)); let outputs: Outputs = serde_json::from_str(&outputs_json).map_err(map_err_err!()).or(Err(ErrorCode::CommonInvalidStructure))?; - debug!("Deserialized output_json >>> {:?}", outputs); + debug!("Deserialized output_json >>> {:?}", secret!(&outputs)); + + let extra: Option = if let Some(extra_) = extra { + serde_json::from_str(&extra_).map_err(map_err_err!()).or(Err(ErrorCode::CommonInvalidStructure))? + } else { None }; + debug!("Deserialized extra >>> {:?}", secret!(&extra)); let request_json_object: serde_json::Value = serde_json::from_str(&request_json).map_err(map_err_err!()).or(Err(ErrorCode::CommonInvalidStructure))?; trace!("Converted request_json to serde::json::Value"); @@ -56,7 +62,7 @@ pub fn deserialize_inputs ( let request_json_map = request_json_object.as_object().ok_or(ErrorCode::CommonInvalidStructure).map_err(map_err_err!())?; trace!("Converted request_json to hash_map"); - debug!("Deserialized values: inputs: {:?}, outputs: {:?}, request_json_map: {:?}", inputs, outputs, request_json_map); + debug!("Deserialized values: inputs: {:?}, outputs: {:?}, request_json_map: {:?}", secret!(&inputs), secret!(&outputs), secret!(&request_json_map)); return Ok(( inputs, outputs, @@ -87,11 +93,11 @@ pub fn add_fees_to_request_and_serialize( wallet_handle: i32, inputs: Inputs, outputs: Outputs, - extra: Option, + extra: Option, request_json_map: SerdeMap, cb: Box) + Send + Sync> ) -> Result<(), ErrorCode> { - trace!("logic::add_request_fees::add_fees_to_request_and_serialize >> wallet_handle: {:?}, inputs: {:?}, outputs: {:?}, request_json_map: {:?}", wallet_handle, inputs, outputs, request_json_map); + trace!("logic::add_request_fees::add_fees_to_request_and_serialize >> wallet_handle: {:?}, inputs: {:?}, outputs: {:?}, request_json_map: {:?}", wallet_handle, secret!(&inputs), secret!(&outputs), secret!(&request_json_map)); let res = add_fees(wallet_handle, inputs, outputs, extra, request_json_map, Box::new(move |request_json_map_updated|{ let rm_fees = request_json_map_updated.map(|request_json_map_with_fees| serialize_request_with_fees(request_json_map_with_fees)); match rm_fees { @@ -123,7 +129,7 @@ pub fn closure_cb_response(command_handle: i32, cb: JsonCallbackUnwrapped) -> im KEEP all public methods above */ -fn add_fees(wallet_handle: i32, inputs: Inputs, outputs: Outputs, extra: Option, request_json_map: SerdeMap, cb: Box) + Send + Sync>) -> Result<(), ErrorCode> { +fn add_fees(wallet_handle: i32, inputs: Inputs, outputs: Outputs, extra: Option, request_json_map: SerdeMap, cb: Box) + Send + Sync>) -> Result<(), ErrorCode> { let txn_serialized = serialize_signature(request_json_map.clone().into())?; let mut hasher = Sha256::default(); hasher.input(txn_serialized.as_bytes()); @@ -131,7 +137,7 @@ fn add_fees(wallet_handle: i32, inputs: Inputs, outputs: Outputs, extra: Option< signed_fees(wallet_handle, inputs, outputs, extra, &txn_digest, Box::new(move |fees| { trace!("Added fees to request_json."); match fees { - Ok(fees) => { + Ok((fees, _)) => { let mut map = request_json_map.clone(); map.insert(FEES.to_string(), json!([fees.inputs, fees.outputs, fees.signatures])); cb(Ok(map.clone())); @@ -146,7 +152,7 @@ fn add_fees(wallet_handle: i32, inputs: Inputs, outputs: Outputs, extra: Option< } fn serialize_request_with_fees(request_json_map_with_fees: SerdeMap) -> Result { - trace!("fee_map: {:?}", request_json_map_with_fees); + trace!("fee_map: {:?}", secret!(&request_json_map_with_fees)); let serialized_request_with_fees = serde_json::to_string(&json!(request_json_map_with_fees)) .or(Err(ErrorCode::CommonInvalidStructure))?; trace!("Serialized request_with_fees"); @@ -154,7 +160,7 @@ fn serialize_request_with_fees(request_json_map_with_fees: SerdeMap) -> Result, txn_digest: &Option, cb: Box) + Send + Sync>) -> Result<(), ErrorCode> { +fn signed_fees(wallet_handle: i32, inputs: Inputs, outputs: Outputs, extra: Option, txn_digest: &Option, cb: Box), ErrorCode>) + Send + Sync>) -> Result<(), ErrorCode> { let fees = XferPayload::new(inputs, outputs, extra); fees.sign_fees(&CryptoSdk{}, wallet_handle, txn_digest, cb)?; Ok(()) diff --git a/libsovtoken/src/logic/api_internals/create_address.rs b/libsovtoken/src/logic/api_internals/create_address.rs index e7a4ea8cd..2ba9a4a24 100644 --- a/libsovtoken/src/logic/api_internals/create_address.rs +++ b/libsovtoken/src/logic/api_internals/create_address.rs @@ -29,14 +29,14 @@ pub fn deserialize_arguments( .ok_or(ErrorCode::CommonInvalidStructure) .map_err(map_err_err!())?; - debug!("api::create_payment_address_handler json_config_string >> {:?}", json_config_string); + debug!("api::create_payment_address_handler json_config_string >> {:?}", secret!(&json_config_string)); // TODO: Only continue when seed is missing, not on any error. let config = PaymentAddressConfig::from_json(&json_config_string) .map_err(map_err_trace!()) .unwrap_or(PaymentAddressConfig { seed: "".to_string() }); - debug!("api::create_payment_address_handler PaymentAddressConfig >> {:?}", config); + debug!("api::create_payment_address_handler PaymentAddressConfig >> {:?}", secret!(&config)); Ok((config, cb)) } @@ -52,7 +52,7 @@ pub fn create_address_cb(command_handle: i32, cb: JsonCallbackUnwrapped) -> impl return; } - debug!("create_payment_address_handler returning payment address of '{}'", &payment_address); + debug!("create_payment_address_handler returning payment address of '{}'", secret!(&payment_address)); let payment_address_cstring = cstring_from_str(payment_address); let payment_address_ptr = payment_address_cstring.as_ptr(); diff --git a/libsovtoken/src/logic/build_payment.rs b/libsovtoken/src/logic/build_payment.rs index 8186a1f69..5a795d921 100644 --- a/libsovtoken/src/logic/build_payment.rs +++ b/libsovtoken/src/logic/build_payment.rs @@ -6,77 +6,100 @@ use serde_json; use logic::config::payment_config::PaymentRequest; use logic::input::Inputs; use logic::output::Outputs; -use logic::xfer_payload::XferPayload; +use logic::xfer_payload::{XferPayload, Extra}; use utils::base58::{IntoBase58, FromBase58}; +use utils::txn_author_agreement::TaaAcceptance; use ErrorCode; use utils::ffi_support::{string_from_char_ptr, c_pointer_from_str}; +use logic::did::Did; type BuildPaymentRequestCb = extern fn(ch: i32, err: i32, request_json: *const c_char) -> i32; -type DeserializedArguments = (Inputs, Outputs, Option, BuildPaymentRequestCb); +type DeserializedArguments = (Inputs, Outputs, Option, Option, BuildPaymentRequestCb); pub fn deserialize_inputs( inputs_json: *const c_char, outputs_json: *const c_char, extra: *const c_char, + did: *const c_char, cb: Option ) -> Result { - trace!("logic::build_payment::deserialize_inputs >> inputs_json: {:?}, outputs_json: {:?}, extra: {:?}", inputs_json, outputs_json, extra); + trace!("logic::build_payment::deserialize_inputs >> inputs_json: {:?}, outputs_json: {:?}, extra: {:?}", secret!(&inputs_json), secret!(&outputs_json), secret!(&extra)); let cb = cb.ok_or(ErrorCode::CommonInvalidStructure)?; let inputs_json = string_from_char_ptr(inputs_json) .ok_or(ErrorCode::CommonInvalidStructure).map_err(map_err_err!())?; - debug!("Converted inputs_json pointer to string >>> {:?}", inputs_json); - + debug!("Converted inputs_json pointer to string >>> {:?}", secret!(&inputs_json)); + + let did = if let Some(did) = Did::from_pointer(did) { + Some(did.validate().map_err(map_err_err!()).map_err(|_| ErrorCode::CommonInvalidStructure)?) + } else {None}; + debug!("Converted did pointer to string >>> {:?}", secret!(&did)); + let outputs_json = string_from_char_ptr(outputs_json) .ok_or(ErrorCode::CommonInvalidStructure).map_err(map_err_err!())?; - debug!("Converted outputs_json pointer to string >>> {:?}", outputs_json); + debug!("Converted outputs_json pointer to string >>> {:?}", secret!(&outputs_json)); let inputs: Inputs = serde_json::from_str(&inputs_json).map_err(map_err_err!()) .or(Err(ErrorCode::CommonInvalidStructure))?; - debug!("Deserialized input_json >>> {:?}", inputs); + debug!("Deserialized input_json >>> {:?}", secret!(&inputs)); let outputs: Outputs = serde_json::from_str(&outputs_json).map_err(map_err_err!()) .or(Err(ErrorCode::CommonInvalidStructure))?; - debug!("Deserialized output_json >>> {:?}", outputs); + debug!("Deserialized output_json >>> {:?}", secret!(&outputs)); let extra = string_from_char_ptr(extra); - debug!("Deserialized extra >>> {:?}", extra); + debug!("Converted extra pointer to string >>> {:?}", extra); + + let extra: Option = if let Some(extra_) = extra { + serde_json::from_str(&extra_).map_err(map_err_err!()).or(Err(ErrorCode::CommonInvalidStructure))? + } else { None }; + debug!("Deserialized extra >>> {:?}", secret!(&extra)); - trace!("logic::build_payment::deserialize_inputs << inputs: {:?}, outputs: {:?}, extra: {:?}", inputs, outputs, extra); - return Ok((inputs, outputs, extra, cb)); + trace!("logic::build_payment::deserialize_inputs << inputs: {:?}, outputs: {:?}, extra: {:?}", secret!(&inputs), secret!(&outputs), secret!(&extra)); + return Ok((inputs, outputs, extra, did, cb)); } pub fn handle_signing( command_handle: i32, - signed_payload: Result, + result: Result<(XferPayload, Option), ErrorCode>, + identifier: Option, cb: BuildPaymentRequestCb ) { - let (error_code, pointer) = match build_payment_request_pointer(signed_payload) { + let (error_code, pointer) = match build_payment_request_pointer(identifier, result) { Ok(request_pointer) => (ErrorCode::Success, request_pointer), Err(ec) => (ec, c_pointer_from_str("")), }; - + cb(command_handle, error_code as i32, pointer); } fn build_payment_request_pointer( - signed_payload: Result + identifier: Option, + result: Result<(XferPayload, Option), ErrorCode>, ) -> Result<*const c_char, ErrorCode> { - let signed_payload = signed_payload?; - debug!("Signed payload >>> {:?}", signed_payload); + let (signed_payload, taa_acceptance) = result?; + debug!("Signed payload >>> {:?}", secret!(&signed_payload)); if signed_payload.signatures.is_none() { error!("Building an unsigned payment request."); return Err(ErrorCode::CommonInvalidStructure); } - let identifier = signed_payload.inputs[0].address.clone(); - let identifier = identifier.as_bytes().from_base58_check(); - let identifier = identifier.map(|s| s.into_base58()).map_err(|_| ErrorCode::CommonInvalidStructure)?; + let identifier = match identifier.map(String::from) { + Some(idr) => idr, + None => { + let addr = signed_payload.inputs[0].address.clone(); + let idr = addr.as_bytes().from_base58_check(); + idr.map(|s| s.into_base58()).map_err(|_| ErrorCode::CommonInvalidStructure)? + } + }; - let payment_request = PaymentRequest::new(signed_payload) + let mut payment_request = PaymentRequest::new(signed_payload) .as_request(identifier); + + payment_request.set_taa_acceptance(taa_acceptance); + debug!("payment_request >>> {:?}", payment_request); return payment_request @@ -105,31 +128,40 @@ mod test_deserialize_inputs { inputs_json: Option<*const c_char>, outputs_json: Option<*const c_char>, extra: Option<*const c_char>, + did: Option<*const c_char>, cb: Option> ) -> Result { let inputs_json = inputs_json.unwrap_or_else(default::inputs_json_pointer); let outputs_json = outputs_json.unwrap_or_else(default::outputs_json_pointer); let extra = extra.unwrap_or(ptr::null()); + let did = did.unwrap_or_else(default::did); let cb = cb.unwrap_or(Some(default::empty_callback_string)); - return deserialize_inputs(inputs_json, outputs_json, extra, cb); + return deserialize_inputs(inputs_json, outputs_json, extra, did, cb); } #[test] fn deserialize_empty_inputs() { - let result = call_deserialize_inputs(Some(ptr::null()), None, None, None); + let result = call_deserialize_inputs(Some(ptr::null()), None, None, None, None); assert_eq!(ErrorCode::CommonInvalidStructure, result.unwrap_err()); } #[test] fn deserialize_empty_outputs() { - let result = call_deserialize_inputs(None, Some(ptr::null()), None, None); + let result = call_deserialize_inputs(None, Some(ptr::null()), None, None, None); assert_eq!(ErrorCode::CommonInvalidStructure, result.unwrap_err()); } + #[test] + fn deserialize_empty_did() { + let result = call_deserialize_inputs(None, None, None, Some(ptr::null()), None); + assert!(result.is_ok()); + } + + #[test] fn deserialize_empty_callback() { - let result = call_deserialize_inputs(None, None, None, Some(None)); + let result = call_deserialize_inputs(None, None, None, None, Some(None)); assert_eq!(ErrorCode::CommonInvalidStructure, result.unwrap_err()); } @@ -142,7 +174,7 @@ mod test_deserialize_inputs { "seqNo": 2 } }); - let result = call_deserialize_inputs(Some(inputs_json), None, None, None); + let result = call_deserialize_inputs(Some(inputs_json), None, None, None, None); assert_eq!(ErrorCode::CommonInvalidStructure, result.unwrap_err()); } @@ -156,13 +188,13 @@ mod test_deserialize_inputs { "seqNo": 5, } }); - let result = call_deserialize_inputs(None, Some(outputs_json), None, None); + let result = call_deserialize_inputs(None, Some(outputs_json), None, None, None); assert_eq!(ErrorCode::CommonInvalidStructure, result.unwrap_err()); } #[test] fn deserialize_valid() { - let result = call_deserialize_inputs(None, None, None, None); + let result = call_deserialize_inputs(None, None, None, None, None); assert!(result.is_ok()); } } @@ -175,9 +207,9 @@ mod test_handle_signing { use utils::results::ResultHandler; use utils::test::{default, callbacks}; - fn call_handle_signing(input_payload: Result) -> Result { + fn call_handle_signing(input_payload: Result<(XferPayload, Option), ErrorCode>) -> Result { let (receiver, command_handle, cb) = callbacks::cb_ec_string(); - handle_signing(command_handle, input_payload, cb.unwrap()); + handle_signing(command_handle, input_payload, None, cb.unwrap()); ResultHandler::one(ErrorCode::Success, receiver) } @@ -191,14 +223,14 @@ mod test_handle_signing { #[test] fn test_xfer_without_signatures() { let unsigned_payload = default::xfer_payload_unsigned(); - let result = call_handle_signing(Ok(unsigned_payload)); + let result = call_handle_signing(Ok((unsigned_payload, None))); assert_eq!(ErrorCode::CommonInvalidStructure, result.unwrap_err()); } #[test] fn test_signed_xfer_payload() { let signed_payload = default::xfer_payload_signed(); - let result = call_handle_signing(Ok(signed_payload)).unwrap(); + let result = call_handle_signing(Ok((signed_payload, None))).unwrap(); let request: Request = serde_json::from_str(&result).unwrap(); assert_eq!("10001", request.operation.get("type").unwrap()); assert_eq!( @@ -207,6 +239,6 @@ mod test_handle_signing { .get(0).unwrap().as_object().unwrap() .get("address").unwrap().as_str().unwrap() ); - assert_eq!("7LSfLv2S6K7zMPrgmJDkZoJNhWvWRzpU7qt9uMR5yz8G".to_string(), request.identifier); + assert_eq!(Some("7LSfLv2S6K7zMPrgmJDkZoJNhWvWRzpU7qt9uMR5yz8G".to_string()), request.identifier); } } \ No newline at end of file diff --git a/libsovtoken/src/logic/config/get_fees_config.rs b/libsovtoken/src/logic/config/get_fees_config.rs index dcafe986e..dd0547546 100644 --- a/libsovtoken/src/logic/config/get_fees_config.rs +++ b/libsovtoken/src/logic/config/get_fees_config.rs @@ -16,8 +16,8 @@ use utils::constants::txn_types::GET_FEES; use sovtoken::logic::config::get_fees_config::GetFeesRequest; use sovtoken::logic::did::Did; - let identifier = String::from("hgrhyNXqW4KNTz4wwiV8v"); - let did = Did::new(&identifier).validate().unwrap(); + let identifier = String::from("V4SGRU86Z58d6TV7PBUe6f"); + let did = Did::new(identifier).validate().unwrap(); let get_fees_request = GetFeesRequest::new().as_request(Some(did)); let json_pointer = get_fees_request.serialize_to_pointer().unwrap(); ``` @@ -65,7 +65,7 @@ mod get_fees_config_test { fn initial_get_fee_request() -> Request { let identifier: String = rand_string(21); - let did = Did::new(&identifier); + let did = Did::new(identifier); return GetFeesRequest::new().as_request(Some(did)); } @@ -86,7 +86,7 @@ mod get_fees_config_test { #[test] fn create_request_with_fees_config() { let identifier: String = rand_string(21); - let did = Did::new(&identifier); + let did = Did::new(identifier); let request = GetFeesRequest::new().as_request(Some(did)); assert_eq!(request.operation.txn_type, GET_FEES.to_string()); } diff --git a/libsovtoken/src/logic/config/output_mint_config.rs b/libsovtoken/src/logic/config/output_mint_config.rs index e0bc11123..97e598d2a 100644 --- a/libsovtoken/src/logic/config/output_mint_config.rs +++ b/libsovtoken/src/logic/config/output_mint_config.rs @@ -73,7 +73,7 @@ mod output_mint_config_test { fn initial_mint_request() -> Request { let identifier: String = rand_string(21); - let did = Did::new(&identifier); + let did = Did::new(identifier); let output = Output::new(String::from("E9LNHk8shQ6xe2RfydzXDSsyhWC6vJaUeKE2mmc6mWraDfmKm"), 10); let outputs = vec![output]; return MintRequest::new(outputs, Some(did), None); @@ -96,7 +96,7 @@ mod output_mint_config_test { #[test] fn create_request_with_mint_config() { let identifier: String = rand_string(21); - let did = Did::new(&identifier); + let did = Did::new(identifier); let output = Output::new(String::from("E9LNHk8shQ6xe2RfydzXDSsyhWC6vJaUeKE2mmc6mWraDfmKm"), 10); let outputs = vec![output]; let request = MintRequest::from_config(outputs.clone(), Some(did), None); diff --git a/libsovtoken/src/logic/config/payment_config.rs b/libsovtoken/src/logic/config/payment_config.rs index 3b764f00d..77912c1ef 100644 --- a/libsovtoken/src/logic/config/payment_config.rs +++ b/libsovtoken/src/logic/config/payment_config.rs @@ -54,7 +54,7 @@ use logic::xfer_payload::XferPayload; [`build_payment_req_handler`]: ../../../api/fn.build_payment_req_handler.html */ -#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] +#[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct PaymentRequest { #[serde(rename = "type")] txn_type: String, diff --git a/libsovtoken/src/logic/config/set_fees_config.rs b/libsovtoken/src/logic/config/set_fees_config.rs index fc2a56e55..c9262cf88 100644 --- a/libsovtoken/src/logic/config/set_fees_config.rs +++ b/libsovtoken/src/logic/config/set_fees_config.rs @@ -44,7 +44,7 @@ pub type SetFeesMap = HashMap; fees.insert(String::from(txn_types::XFER_PUBLIC), 10); fees.insert(String::from("15"), 3); let identifier = String::from("hgrhyNXqW4KNTz4wwiV8v"); - let did = Did::new(&identifier).validate().unwrap(); + let did = Did::new(identifier).validate().unwrap(); let set_fees = SetFees::new(fees).validate().unwrap(); let set_fees_request = set_fees.as_request(Some(did)); let json_pointer = set_fees_request.serialize_to_pointer().unwrap(); @@ -89,8 +89,7 @@ impl SetFees { /** Validate `self.fees`. - Checks `self.fees` is not empty and the keys are string - integers. + Checks `self.fees` is not empty. ## Examples @@ -110,25 +109,6 @@ impl SetFees { assert_eq!(SetFeesError::Empty, validated.unwrap_err()); ``` - #### Fees with non-string-integer keys - Returns a [`SetFeesError::KeyNotInteger`]. - ``` - use std::collections::HashMap; - use sovtoken::logic::config::set_fees_config::{ - SetFees, - SetFeesError, - }; - - let mut fees = HashMap::new(); - // Key should be "10001" - let key = String::from("XFER_PUBLIC"); - fees.insert(key.clone(), 10); - let set_fees = SetFees::new(fees); - let validated = set_fees.validate(); - - assert_eq!(SetFeesError::KeyNotInteger(key), validated.unwrap_err()) - ``` - #### Valid Fees ``` use std::collections::HashMap; @@ -148,23 +128,12 @@ impl SetFees { ``` [`SetFeesError::Empty`]: ./enum.SetFeesError.html#variant.Empty - [`SetFeesError::KeyNotInteger`]: ./enum.SetFeesError.html#variant.KeyNotInteger */ pub fn validate(self) -> Result { if self.fees.is_empty() { return Err(SetFeesError::Empty); } - { - let key_not_integer = self.fees - .keys() - .find(|&key| key.parse::().is_err()); - - if let Some(key) = key_not_integer { - return Err(SetFeesError::KeyNotInteger(key.to_owned())); - } - } - return Ok(self); } @@ -175,14 +144,12 @@ impl SetFees { ### Includes - `SetFeesError::Empty` - - `SetFeesError::KeyNotInteger(&str)` [`SetFees::validate`]: ./struct.SetFees.html#method.validate */ #[derive(Debug, PartialEq, Eq)] pub enum SetFeesError { Empty, - KeyNotInteger(String) } impl fmt::Display for SetFeesError { @@ -195,7 +162,6 @@ impl Error for SetFeesError { fn description(&self) -> &str { match self { &SetFeesError::Empty => "Set fees was empty.", - &SetFeesError::KeyNotInteger(_) => "A Set fees key wasn't a integer string.", } } } @@ -225,29 +191,40 @@ mod fees_config_test { } #[test] - fn test_validation_fees_key_not_string_integer() { + fn test_validation_fees_key_string_integer() { + let set_fees_json = json!({ + "1000": 10, + }); + let hash_map: SetFeesMap = serde_json::from_value(set_fees_json).unwrap(); + let set_fees = SetFees::new(hash_map); + assert!(set_fees.validate().is_ok()); + } + + #[test] + fn test_validation_fees_key_aliases() { let set_fees_json = json!({ "XFER_PUBLIC": 10, + "ALIAS": 10, }); - let expected = SetFeesError::KeyNotInteger(String::from("XFER_PUBLIC")); let hash_map: SetFeesMap = serde_json::from_value(set_fees_json).unwrap(); let set_fees = SetFees::new(hash_map); - assert_eq!(expected, set_fees.validate().unwrap_err()); + assert!(set_fees.validate().is_ok()); } #[test] fn create_valid_set_fees_request() { let set_fees_json = json!({ "3": 10, - "1000": 12 + "1000": 12, + "ALIAS": 10, }); let expected = set_fees_json.clone(); let hash_map: SetFeesMap = serde_json::from_value(set_fees_json).unwrap(); let set_fees = SetFees::new(hash_map).validate().unwrap(); - let identifier = String::from("hgrhyNXqW4KNTz4wwiV8v"); - let did = Did::new(&identifier).validate().unwrap(); + let identifier = String::from("V4SGRU86Z58d6TV7PBUe6f"); + let did = Did::new(identifier).validate().unwrap(); let request = set_fees.as_request(Some(did)); let fees_from_request = serde_json::to_value(&request.operation.fees).unwrap(); assert_eq!(expected, fees_from_request) diff --git a/libsovtoken/src/logic/did.rs b/libsovtoken/src/logic/did.rs index 65b5b0f35..fb46b6ed7 100644 --- a/libsovtoken/src/logic/did.rs +++ b/libsovtoken/src/logic/did.rs @@ -29,17 +29,17 @@ pub enum DidError { The did needs to be between 20 and 21 characters and contain only alphanumeric characters. */ -#[derive(Debug, PartialEq, Eq)] -pub struct Did<'a>(&'a str); +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Did(String); -impl<'a> Did<'a> { +impl Did { - pub fn new(did_string: &'a str) -> Self { + pub fn new(did_string: String) -> Self { return Did(did_string); } pub fn from_pointer(pointer: *const c_char) -> Option { - return str_from_char_ptr(pointer).map(Self::new); + return str_from_char_ptr(pointer).map(|st| st.to_string()).map(Self::new); } /** @@ -54,28 +54,31 @@ impl<'a> Did<'a> { use sovtoken::logic::did::Did; use sovtoken::logic::did::DidError; - let did_invalid = Did::new("123456789[11234567891"); + let did_invalid = Did::new("123456789[11234567891".to_string()); let error = did_invalid.validate().unwrap_err(); assert_eq!(DidError::InvalidChar('['), error); # } ``` */ pub fn validate(self) -> Result { - let Did(did_string) = self; - let res_did = did_string.from_base58().map_err(map_err_err!()); + + let (res_did, len) = { + let did_string = &self.0; + (did_string.from_base58().map_err(map_err_err!()), did_string.len()) + }; match res_did { Ok(ref vec) if vec.len() == 32 || vec.len() == 16 => Ok(self), Ok(ref vec) => Err(DidError::InvalidLength(vec.len())), Err(DecodeError::InvalidCharacter {character: b, index: _}) => Err(DidError::InvalidChar(b as char)), Err(DecodeError::NonAsciiCharacter {index: _}) => Err(DidError::InvalidChar(0 as char)), - Err(_) => Err(DidError::InvalidLength(did_string.len())) + Err(_) => Err(DidError::InvalidLength(len)) } } } -impl<'a> From> for String { - fn from(did: Did<'a>) -> String { +impl From for String { + fn from(did: Did) -> String { return String::from(did.0); } } @@ -109,22 +112,22 @@ mod test_did_validation { #[test] fn did_invalid_length() { - assert_eq!(Err(DidError::InvalidLength(17)), Did::new(&"1123456789abcdef1".as_bytes().into_base58()).validate()); + assert_eq!(Err(DidError::InvalidLength(17)), Did::new("1123456789abcdef1".as_bytes().into_base58()).validate()); } #[test] fn did_invalid_char() { - assert_eq!(Err(DidError::InvalidChar('!')), Did::new("123456789abcd!efghij").validate()); + assert_eq!(Err(DidError::InvalidChar('!')), Did::new("123456789abcd!efghij".to_string()).validate()); } #[test] fn did_valid_length_16() { - assert!(Did::new(&"1123456789abcdef".as_bytes().into_base58()).validate().is_ok()); + assert!(Did::new("1123456789abcdef".as_bytes().into_base58()).validate().is_ok()); } #[test] fn did_valid_length_32() { - assert!(Did::new(&"1123456789abcdef1123456789abcdef".as_bytes().into_base58()).validate().is_ok()); + assert!(Did::new("1123456789abcdef1123456789abcdef".as_bytes().into_base58()).validate().is_ok()); } #[test] diff --git a/libsovtoken/src/logic/minting.rs b/libsovtoken/src/logic/minting.rs index f805a5e45..5e4741823 100644 --- a/libsovtoken/src/logic/minting.rs +++ b/libsovtoken/src/logic/minting.rs @@ -8,15 +8,15 @@ use utils::constants::general::{JsonCallback, JsonCallbackUnwrapped}; use utils::ffi_support::{string_from_char_ptr}; use logic::output::Outputs; -type DeserializedArguments<'a> = (Option>, Outputs, Option, JsonCallbackUnwrapped); +type DeserializedArguments = (Option, Outputs, Option, JsonCallbackUnwrapped); -pub fn deserialize_inputs<'a>( +pub fn deserialize_inputs( did: *const c_char, outputs_json: *const c_char, extra: *const c_char, cb: JsonCallback -) -> Result, ErrorCode> { - trace!("logic::minting::deserialize_inputs >> did: {:?}, outputs_json: {:?}, extra: {:?}", did, outputs_json, extra); +) -> Result { + trace!("logic::minting::deserialize_inputs >> did: {:?}, outputs_json: {:?}, extra: {:?}", secret!(&did), secret!(&outputs_json), secret!(&extra)); let cb = cb.ok_or(ErrorCode::CommonInvalidStructure)?; trace!("Unwrapped callback."); @@ -26,20 +26,20 @@ pub fn deserialize_inputs<'a>( } ); let did = opt_res_to_res_opt!(did)?; - debug!("Converted did pointer to string >>> {:?}", did); + debug!("Converted did pointer to string >>> {:?}", secret!(&did)); let outputs_json = string_from_char_ptr(outputs_json) .ok_or(ErrorCode::CommonInvalidStructure)?; - debug!("Converted outputs_json pointer to string >>> {:?}", outputs_json); + debug!("Converted outputs_json pointer to string >>> {:?}", secret!(&outputs_json)); let outputs: Outputs = serde_json::from_str(&outputs_json) .or(Err(ErrorCode::CommonInvalidStructure))?; - debug!("Deserialized output_json >>> {:?}", outputs); + debug!("Deserialized output_json >>> {:?}", secret!(&outputs)); let extra = string_from_char_ptr(extra); - debug!("Deserialized extra >>> {:?}", extra); + debug!("Deserialized extra >>> {:?}", secret!(&extra)); - trace!("logic::minting::deserialize_inputs << did: {:?}, outputs: {:?}, extra: {:?}", did, outputs, extra); + trace!("logic::minting::deserialize_inputs << did: {:?}, outputs: {:?}, extra: {:?}", secret!(&did), secret!(&outputs), secret!(&extra)); return Ok((did, outputs, extra, cb)); } @@ -48,7 +48,7 @@ pub fn build_mint_request( mut outputs: Outputs, extra: Option, ) -> Result<*const c_char, ErrorCode> { - trace!("logic::minting::build_mint_request >> did: {:?}, outputs: {:?}", did, outputs); + trace!("logic::minting::build_mint_request >> did: {:?}, outputs: {:?}", secret!(&did), secret!(&outputs)); for output in &mut outputs { let address = address::unqualified_address_from_address(&output.recipient)?; @@ -57,7 +57,7 @@ pub fn build_mint_request( trace!("Stripped pay:sov: from outputs"); let mint_request = MintRequest::from_config(outputs, did, extra); - info!("Built a mint request >>> {:?}", mint_request); + info!("Built a mint request >>> {:?}", secret!(&mint_request)); let ptr = mint_request.serialize_to_pointer() .or(Err(ErrorCode::CommonInvalidStructure)); @@ -76,12 +76,12 @@ mod test_build_mint_request { use utils::ffi_support::{c_pointer_from_str}; use utils::test::default; - pub fn call_deserialize_inputs<'a>( + pub fn call_deserialize_inputs( did: Option<*const c_char>, outputs_json: Option<*const c_char>, extra: Option<*const c_char>, cb: Option - ) -> Result, ErrorCode> { + ) -> Result { let req_json = did.unwrap_or_else(default::did); let outputs_json = outputs_json.unwrap_or_else(default::outputs_json_pointer); let extra = extra.unwrap_or(null()); @@ -96,7 +96,7 @@ mod test_build_mint_request { Output::new(String::from("pad:sov:E9LNHk8shQ6xe2RfydzXDSsyhWC6vJaUeKE2mmc6mWraDfmKm"), 12) ]; - let did = Did::new(&"en32ansFeZNERIouv2xA"); + let did = Did::new("en32ansFeZNERIouv2xA".to_string()); let result = build_mint_request(Some(did), outputs, None); assert_eq!(ErrorCode::CommonInvalidStructure, result.unwrap_err()); } diff --git a/libsovtoken/src/logic/payments.rs b/libsovtoken/src/logic/payments.rs index 90c37512a..c0f4a9826 100644 --- a/libsovtoken/src/logic/payments.rs +++ b/libsovtoken/src/logic/payments.rs @@ -35,7 +35,7 @@ impl CreatePaymentHandler { trace!("calling self.injected_api.indy_create_key"); let verkey = self.injected_api.indy_create_key(wallet_id, config)?; - trace!("got verkey from self.injected_api.indy_create_key {}", verkey); + trace!("got verkey from self.injected_api.indy_create_key {}", secret!(&verkey)); return address::qualified_address_from_verkey(&verkey); } @@ -51,7 +51,7 @@ impl CreatePaymentHandler { let cb_closure = move | err: ErrorCode, verkey : String | { let res = if ErrorCode::Success == err { - trace!("got verkey from self.injected_api.indy_create_key_async {}", verkey); + trace!("got verkey from self.injected_api.indy_create_key_async {}", secret!(&verkey)); address::qualified_address_from_verkey(&verkey) } else { Err(err) diff --git a/libsovtoken/src/logic/request.rs b/libsovtoken/src/logic/request.rs index e761fd713..6e3061c68 100644 --- a/libsovtoken/src/logic/request.rs +++ b/libsovtoken/src/logic/request.rs @@ -10,6 +10,7 @@ use {IndyHandle, ErrorCode}; use utils::constants::general::PROTOCOL_VERSION; use utils::ffi_support::{cstring_from_str, c_pointer_from_string}; use utils::json_conversion::JsonSerialize; +use utils::txn_author_agreement::TaaAcceptance; use utils::random::rand_req_id; use logic::indy_sdk_api::ledger; @@ -24,7 +25,10 @@ pub struct Request pub operation: T, pub req_id: ReqId, pub protocol_version: ProtocolVersion, - pub identifier: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub identifier : Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub taa_acceptance: Option } impl Request @@ -36,10 +40,15 @@ impl Request operation, protocol_version: PROTOCOL_VERSION, req_id, - identifier: identifier.unwrap_or(DEFAULT_LIBSOVTOKEN_DID.to_string()) + identifier, + taa_acceptance: None, }; } + pub fn set_taa_acceptance(&mut self, taa_acceptance: Option) { + self.taa_acceptance = taa_acceptance; + } + pub fn serialize_to_cstring(&self) -> Result { return self.serialize_to_string().map_err(map_err_err!()) .map(|string| cstring_from_str(string)); diff --git a/libsovtoken/src/logic/set_fees.rs b/libsovtoken/src/logic/set_fees.rs index c9036e6cc..6c08b30bd 100644 --- a/libsovtoken/src/logic/set_fees.rs +++ b/libsovtoken/src/logic/set_fees.rs @@ -8,35 +8,14 @@ use serde_json; use utils::constants::general::{JsonCallback, JsonCallbackUnwrapped}; use utils::ffi_support::string_from_char_ptr; -const NYM: &'static str = "1"; -const ATTRIB: &'static str = "100"; -const SCHEMA: &'static str = "101"; -const CRED_DEF: &'static str = "102"; -const REVOC_REG_DEF: &'static str = "113"; -const REVOC_REG_ENTRY: &'static str = "114"; -const XFER_PUBLIC: &'static str = "10001"; - -fn txn_name_to_code(txn: &str) -> String { - match txn { - "NYM" => NYM.to_string(), - "ATTRIB" => ATTRIB.to_string(), - "SCHEMA" => SCHEMA.to_string(), - "CRED_DEF" => CRED_DEF.to_string(), - "REVOC_REG_DEF" => REVOC_REG_DEF.to_string(), - "REVOC_REG_ENTRY" => REVOC_REG_ENTRY.to_string(), - "XFER_PUBLIC" => XFER_PUBLIC.to_string(), - val @ _ => val.to_string() - } -} +type DeserializedArguments = (Option, SetFees, JsonCallbackUnwrapped); -type DeserializedArguments<'a> = (Option>, SetFees, JsonCallbackUnwrapped); - -pub fn deserialize_inputs<'a>( +pub fn deserialize_inputs( did: *const c_char, fees_json: *const c_char, cb: JsonCallback -) -> Result, ErrorCode> { - trace!("logic::set_fees::deserialize_inputs >> did: {:?}, fees_json: {:?}", did, fees_json); +) -> Result { + trace!("logic::set_fees::deserialize_inputs >> did: {:?}, fees_json: {:?}", secret!(&did), secret!(&fees_json)); let cb = cb.ok_or(ErrorCode::CommonInvalidStructure)?; let did = Did::from_pointer(did).map(|did| { @@ -51,9 +30,6 @@ pub fn deserialize_inputs<'a>( let set_fees_map: SetFeesMap = serde_json::from_str(&set_fees_json).map_err(map_err_err!()) .or(Err(ErrorCode::CommonInvalidStructure))?; - let set_fees_map: SetFeesMap = set_fees_map.iter() - .map(|(key, val)| (txn_name_to_code(key), val.clone())).collect(); - let set_fees = SetFees::new(set_fees_map) .validate().map_err(map_err_err!()) .or(Err(ErrorCode::CommonInvalidStructure))?; @@ -70,11 +46,11 @@ mod test_deserialize_inputs { use utils::test::default; use utils::ffi_support::{c_pointer_from_str}; - pub fn call_deserialize_inputs<'a>( + pub fn call_deserialize_inputs( did: Option<*const c_char>, set_fees_json: Option<*const c_char>, cb: Option - ) -> Result, ErrorCode> { + ) -> Result { let did_json = did.unwrap_or_else(default::did); let set_fees_json = set_fees_json.unwrap_or_else(default::set_fees_json); let cb = cb.unwrap_or(Some(default::empty_callback_string)); @@ -135,7 +111,7 @@ mod test_deserialize_inputs { } #[test] - fn deserialize_fees_key_not_string_int() { + fn deserialize_fees_key_alias() { let invalid_fees = json_c_pointer!({ "XFER_PUBLIC": 5, "3": 1, @@ -144,7 +120,7 @@ mod test_deserialize_inputs { let (_, fees, _) = call_deserialize_inputs(None, Some(invalid_fees), None).unwrap(); assert_eq!(fees.fees.len(), 2); - assert_eq!(fees.fees.get("10001"), Some(&5)); + assert_eq!(fees.fees.get("XFER_PUBLIC"), Some(&5)); assert_eq!(fees.fees.get("3"), Some(&1)); } diff --git a/libsovtoken/src/logic/verify.rs b/libsovtoken/src/logic/verify.rs index b207d8489..732715181 100644 --- a/libsovtoken/src/logic/verify.rs +++ b/libsovtoken/src/logic/verify.rs @@ -6,14 +6,14 @@ use utils::constants::general::{JsonCallback, JsonCallbackUnwrapped}; use utils::ffi_support::string_from_char_ptr; use logic::parsers::common::TXO; -type DeserializedArguments<'a> = (Option>, TXO, JsonCallbackUnwrapped); +type DeserializedArguments = (Option, TXO, JsonCallbackUnwrapped); -pub fn deserialize<'a>( +pub fn deserialize( did: *const c_char, txo: *const c_char, cb: JsonCallback -) -> Result, ErrorCode> { - trace!("logic::verify::deserialize >> did: {:?}, txo: {:?}", did, txo); +) -> Result { + trace!("logic::verify::deserialize >> did: {:?}, txo: {:?}", secret!(&did), secret!(&txo)); let cb = cb.ok_or(ErrorCode::CommonInvalidStructure)?; trace!("Unwrapped callback."); @@ -26,18 +26,18 @@ pub fn deserialize<'a>( }) )?; - debug!("Converted did pointer to string >>> {:?}", did); + debug!("Converted did pointer to string >>> {:?}", secret!(&did)); let txo = string_from_char_ptr(txo) .ok_or(ErrorCode::CommonInvalidStructure)?; - debug!("Converted txo pointer to string >>> {:?}", txo); + debug!("Converted txo pointer to string >>> {:?}", secret!(&txo)); let txo = TXO::from_libindy_string(&txo) .map_err(map_err_err!()) .map_err(|_| ErrorCode::CommonInvalidStructure)?; debug!("Deserialized txo: {:?}", txo); - trace!("logic::verify::deserialize << did: {:?}, txo: {:?}", did, txo); + trace!("logic::verify::deserialize << did: {:?}, txo: {:?}", secret!(&did), secret!(&txo)); Ok((did, txo, cb)) } diff --git a/libsovtoken/src/logic/xfer_payload.rs b/libsovtoken/src/logic/xfer_payload.rs index ada0d19d9..52011fef3 100644 --- a/libsovtoken/src/logic/xfer_payload.rs +++ b/libsovtoken/src/logic/xfer_payload.rs @@ -20,8 +20,10 @@ use ErrorCode; use logic::address; use logic::indy_sdk_api::crypto_api::CryptoAPI; use logic::input::{Input, Inputs}; -use logic::output::{Outputs}; +use logic::output::Outputs; use logic::hash::Hash; +use utils::constants::txn_types::{ATTRIB, GET_ATTRIB}; +use utils::txn_author_agreement::{TaaAcceptance, extract_taa_acceptance_from_extra}; /** * Holds `inputs` and `outputs` @@ -52,27 +54,31 @@ use logic::hash::Hash; * # } * ``` */ -#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)] +#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)] pub struct XferPayload { pub outputs: Outputs, pub inputs: Inputs, #[serde(skip_serializing_if = "Option::is_none")] - pub extra: Option, + pub extra: Option, pub signatures: Option> } +pub type Extra = serde_json::Value; + unsafe impl Send for XferPayload {} + unsafe impl Sync for XferPayload {} impl InputSigner for XferPayload {} + impl XferPayload { - pub fn new(inputs: Inputs, outputs: Outputs, extra: Option) -> Self + pub fn new(inputs: Inputs, outputs: Outputs, extra: Option) -> Self { return XferPayload { inputs, outputs, extra, signatures: None }; } // TODO: Add request hash to include while signature - pub fn sign_fees(self, crypto_api: &'static A, wallet_handle: IndyHandle, txn_digest: &Option, cb: Box) + Send + Sync>) -> Result<(), ErrorCode> { + pub fn sign_fees(self, crypto_api: &'static A, wallet_handle: IndyHandle, txn_digest: &Option, cb: Box), ErrorCode>) + Send + Sync>) -> Result<(), ErrorCode> { trace!("logic::xfer_payload::xfer_payload::sign_fees >> wallet_handle: {:?}", wallet_handle); if self.inputs.len() < 1 { return Err(ErrorCode::CommonInvalidStructure); @@ -90,7 +96,7 @@ impl XferPayload { * [`Input`]: Input * [`Inputs`]: Inputs */ - pub fn sign_transfer(self, crypto_api: &'static A, wallet_handle: IndyHandle, cb: Box) + Send + Sync>) -> Result<(), ErrorCode> { + pub fn sign_transfer(self, crypto_api: &'static A, wallet_handle: IndyHandle, cb: Box), ErrorCode>) + Send + Sync>) -> Result<(), ErrorCode> { trace!("logic::xfer_payload::xfer_payload::sign >> wallet_handle: {:?}", wallet_handle); if self.outputs.len() < 1 || self.inputs.len() < 1 { return Err(ErrorCode::CommonInvalidStructure); @@ -98,7 +104,7 @@ impl XferPayload { self.sign(crypto_api, wallet_handle, &None, cb) } - fn sign(mut self, crypto_api: &'static A, wallet_handle: IndyHandle, txn_digest: &Option, cb: Box) + Send + Sync>) -> Result<(), ErrorCode> { + fn sign(mut self, crypto_api: &'static A, wallet_handle: IndyHandle, txn_digest: &Option, cb: Box), ErrorCode>) + Send + Sync>) -> Result<(), ErrorCode> { for output in &mut self.outputs { output.recipient = address::unqualified_address_from_address(&output.recipient)?; } @@ -110,12 +116,15 @@ impl XferPayload { debug!("Indicator stripped from inputs"); - XferPayload::sign_inputs(crypto_api, wallet_handle, &self.inputs.clone(), &self.outputs.clone(), txn_digest, &self.extra.clone(),Box::new(move |signatures| { + let (extra, taa_acceptance) = extract_taa_acceptance_from_extra(self.extra.clone())?; + self.extra = extra; + + XferPayload::sign_inputs(crypto_api, wallet_handle, &self.inputs.clone(), &self.outputs.clone(), txn_digest, &self.extra.clone(), &taa_acceptance.clone(), Box::new(move |signatures| { match signatures { Ok(signatures) => { let payload = Self::clone_payload_add_signatures(&self, signatures); info!("Built XFER payload: {:?}", payload); - cb(Ok(payload)); + cb(Ok((payload, taa_acceptance.clone()))); } Err(err) => { error!("Got an error while signing utxos: {:?}", err); @@ -136,7 +145,7 @@ impl XferPayload { .filter(|signature| signature.is_some()) .map(|signature| signature.unwrap().to_owned()) .collect(); - + XferPayload { inputs: prev.inputs.clone(), outputs: prev.outputs.clone(), @@ -147,7 +156,7 @@ impl XferPayload { } trait InputSigner { - fn sign_inputs(crypto_api: &'static A, wallet_handle: IndyHandle, inputs: &Inputs, outputs: &Outputs, txn_digest: &Option, extra: &Option, cb: Box, ErrorCode>) + Send + Sync>) + fn sign_inputs(crypto_api: &'static A, wallet_handle: IndyHandle, inputs: &Inputs, outputs: &Outputs, txn_digest: &Option, extra: &Option, taa_acceptance: &Option, cb: Box, ErrorCode>) + Send + Sync>) -> Result<(), ErrorCode> { let inputs_result: Arc>> = Default::default(); @@ -168,7 +177,7 @@ trait InputSigner { for input in inputs { let cb = cb.clone(); - match Self::sign_input(crypto_api, wallet_handle, input, outputs, txn_digest, extra, Box::new(cb)) { + match Self::sign_input(crypto_api, wallet_handle, input, outputs, txn_digest, extra, taa_acceptance, Box::new(cb)) { err @ Err(_) => { return err; } _ => () } @@ -192,30 +201,33 @@ trait InputSigner { input: &Input, outputs: &Outputs, txn_digest: &Option, - _extra: &Option, + extra: &Option, + taa_acceptance: &Option, cb: Box, String) + Send + Sync>>, ) -> Result<(), ErrorCode> { - trace!("logic::xfer_payload::input_signer::sign_input >> input: {:?}, outputs: {:?}, wallet_handle {:?}", input, outputs, wallet_handle); + trace!("logic::xfer_payload::input_signer::sign_input >> input: {:?}, outputs: {:?}, wallet_handle {:?}", secret!(&input), secret!(&outputs), wallet_handle); let verkey = address::verkey_from_unqualified_address(&input.address.clone())?; - debug!("Received verkey for payment address >>> {:?}", verkey); + + debug!("Received verkey for payment address >>> {:?}", secret!(&verkey)); let vals: Vec = vec![ Some(json!([input])), Some(json!(outputs)), txn_digest.clone().map(|e| json!(e)), -// _extra.map(|e| json!(e)) + extra.clone().map(|e| json!(e)), + taa_acceptance.clone().map(|e| json!(e)), ].into_iter().filter_map(|e| e).collect(); let message = serialize_signature(json!(vals))?; - debug!("Message to sign >>> {:?}", &message); + debug!("Message to sign >>> {:?}", secret!(&message)); let input_key = input.to_string(); let ca = move |signature: Result| { let key = input_key.clone(); - debug!("Received encoded signature >>> {:?} for input {:?}", signature, key); + debug!("Received encoded signature >>> {:?} for input {:?}", secret!(&signature), secret!(&key)); cb(signature, key); }; @@ -236,24 +248,21 @@ trait InputSigner { } pub fn serialize_signature(v: serde_json::Value) -> Result { - do_serialize_signature(v, true) + let _type = v["operation"]["type"].clone(); + do_serialize_signature(v, true, _type.as_str()) } -fn do_serialize_signature(v: serde_json::Value, is_top_level: bool) -> Result { +fn do_serialize_signature(v: serde_json::Value, is_top_level: bool, _type: Option<&str>) -> Result { match v { serde_json::Value::Bool(value) => Ok(if value { "True".to_string() } else { "False".to_string() }), serde_json::Value::Number(value) => Ok(value.to_string()), serde_json::Value::String(value) => Ok(value), serde_json::Value::Array(array) => { - let mut result = "".to_string(); - let length = array.len(); - for (index, element) in array.iter().enumerate() { - result += &do_serialize_signature(element.clone(), false)?; - if index < length - 1 { - result += ","; - } - } - Ok(result) + array + .into_iter() + .map(|element| do_serialize_signature(element, false, _type)) + .collect::, ErrorCode>>() + .map(|res| res.join(",")) } serde_json::Value::Object(map) => { let mut result = "".to_string(); @@ -267,12 +276,19 @@ fn do_serialize_signature(v: serde_json::Value, is_top_level: bool) -> Result (Inputs, Outputs) { let outputs = vec![ Output::new(String::from("TKe9eXtchV71J2qXX5HwP8rbkTBStnEEkMwQkHie265VtRSbs"), 10), @@ -314,7 +330,7 @@ mod test_xfer_payload { let inputs = vec![ Input::new(String::from("E9LNHk8shQ6xe2RfydzXDSsyhWC6vJaUeKE2mmc6mWraDfmKm"), 1), Input::new(String::from("2oWxuFMbhPewEbCEeKnvjcpVq8qpHHrN5y4aU81MWG5dYfeM7V"), 1), - ]; + ]; return (inputs, outputs); } @@ -327,7 +343,7 @@ mod test_xfer_payload { return (inps, outs); } - fn sign_input_sync(input: &Input, outputs: &Outputs, extra: &Option) -> Result { + fn sign_input_sync(input: &Input, outputs: &Outputs, extra: &Option) -> Result { let wallet_handle = 1; let (sender, receiver) = channel(); let sender = Mutex::new(sender); @@ -335,12 +351,13 @@ mod test_xfer_payload { sender.lock().unwrap().send(result).unwrap(); }; XferPayload::sign_input( - &CryptoApiHandler{}, + &CryptoApiHandler {}, wallet_handle, input, outputs, &None, extra, + &None, Box::new(Arc::new(cb)) )?; let result = receiver.recv().unwrap(); @@ -352,7 +369,7 @@ mod test_xfer_payload { let (sender, receiver) = channel(); let sender = Mutex::new(sender); let cb = move |result| { sender.lock().unwrap().send(result).unwrap(); }; - XferPayload::sign_inputs(&CryptoApiHandler{}, wallet_handle, inputs, outputs, &None, &None, + XferPayload::sign_inputs(&CryptoApiHandler {}, wallet_handle, inputs, outputs, &None, &None, &None, Box::new(cb))?; receiver.recv().unwrap().map(|map| map.values().cloned().collect()) } @@ -380,7 +397,7 @@ mod test_xfer_payload { fn sign_multi_input_invalid_input_address() { let (mut inputs, outputs) = inputs_outputs_valid(); String::remove(&mut inputs[0].address, 5); - + let signatures = sign_inputs_sync(&inputs, &outputs).unwrap_err(); assert_eq!(ErrorCode::CommonInvalidStructure, signatures); } @@ -388,7 +405,7 @@ mod test_xfer_payload { #[test] fn sign_multi_input() { let (inputs, outputs) = inputs_outputs_valid(); - + let signed_inputs = sign_inputs_sync(&inputs, &outputs).unwrap(); assert!(signed_inputs.contains(&"31VzUm5vZRfWPk38W3YJaNjrkUeD6tELmjxv42cp7Vnksigned".to_string())); assert!(signed_inputs.contains(&"GyPZzuu8S1KMs5p6iE1wBzjQsFtaB7eigssW4YbdXdtesigned".to_string())); @@ -404,7 +421,7 @@ mod test_xfer_payload { let (sender, _receiver) = channel(); let sender = Mutex::new(sender); let cb = move |result| { sender.lock().unwrap().send(result).unwrap(); }; - let signed_payload = payload.sign_transfer(&CryptoApiHandler{}, wallet_handle, Box::new(cb)).unwrap_err(); + let signed_payload = payload.sign_transfer(&CryptoApiHandler {}, wallet_handle, Box::new(cb)).unwrap_err(); assert_eq!(ErrorCode::CommonInvalidStructure, signed_payload); } @@ -418,7 +435,7 @@ mod test_xfer_payload { let (sender, _receiver) = channel(); let sender = Mutex::new(sender); let cb = move |result| { sender.lock().unwrap().send(result).unwrap(); }; - let signed_payload = XferPayload::new(inputs, outputs, None).sign_transfer(&CryptoApiHandler{}, wallet_handle, Box::new(cb)).unwrap_err(); + let signed_payload = XferPayload::new(inputs, outputs, None).sign_transfer(&CryptoApiHandler {}, wallet_handle, Box::new(cb)).unwrap_err(); assert_eq!(ErrorCode::CommonInvalidStructure, signed_payload); } @@ -431,7 +448,7 @@ mod test_xfer_payload { let (sender, _receiver) = channel(); let sender = Mutex::new(sender); let cb = move |result| { sender.lock().unwrap().send(result).unwrap(); }; - let signed_payload = XferPayload::new(Vec::new(), outputs, None).sign_transfer(&CryptoApiHandler{}, wallet_handle, Box::new(cb)).unwrap_err(); + let signed_payload = XferPayload::new(Vec::new(), outputs, None).sign_transfer(&CryptoApiHandler {}, wallet_handle, Box::new(cb)).unwrap_err(); assert_eq!(ErrorCode::CommonInvalidStructure, signed_payload); } @@ -444,7 +461,7 @@ mod test_xfer_payload { let (sender, _receiver) = channel(); let sender = Mutex::new(sender); let cb = move |result| { sender.lock().unwrap().send(result).unwrap(); }; - let signed_payload = XferPayload::new(inputs, Vec::new(), None).sign_transfer(&CryptoApiHandler{}, wallet_handle, Box::new(cb)).unwrap_err(); + let signed_payload = XferPayload::new(inputs, Vec::new(), None).sign_transfer(&CryptoApiHandler {}, wallet_handle, Box::new(cb)).unwrap_err(); assert_eq!(ErrorCode::CommonInvalidStructure, signed_payload); } @@ -457,7 +474,7 @@ mod test_xfer_payload { let (sender, _receiver) = channel(); let sender = Mutex::new(sender); let cb = move |result| { sender.lock().unwrap().send(result).unwrap(); }; - let signed_payload = XferPayload::new(inputs, Vec::new(), None).sign_fees(&CryptoApiHandler{}, wallet_handle, &None, Box::new(cb)); + let signed_payload = XferPayload::new(inputs, Vec::new(), None).sign_fees(&CryptoApiHandler {}, wallet_handle, &None, Box::new(cb)); assert!(signed_payload.is_ok()); } @@ -470,7 +487,7 @@ mod test_xfer_payload { let (sender, _receiver) = channel(); let sender = Mutex::new(sender); let cb = move |result| { sender.lock().unwrap().send(result).unwrap(); }; - let signed_payload = XferPayload::new(inputs, outputs, None).sign_fees(&CryptoApiHandler{}, wallet_handle, &None, Box::new(cb)); + let signed_payload = XferPayload::new(inputs, outputs, None).sign_fees(&CryptoApiHandler {}, wallet_handle, &None, Box::new(cb)); assert!(signed_payload.is_ok()); } @@ -483,7 +500,7 @@ mod test_xfer_payload { let (sender, _receiver) = channel(); let sender = Mutex::new(sender); let cb = move |result| { sender.lock().unwrap().send(result).unwrap(); }; - let signed_payload = XferPayload::new(Vec::new(), outputs, None).sign_fees(&CryptoApiHandler{}, wallet_handle, &None, Box::new(cb)).unwrap_err(); + let signed_payload = XferPayload::new(Vec::new(), outputs, None).sign_fees(&CryptoApiHandler {}, wallet_handle, &None, Box::new(cb)).unwrap_err(); assert_eq!(ErrorCode::CommonInvalidStructure, signed_payload); } @@ -505,14 +522,14 @@ mod test_xfer_payload { ]; let expected_signatures = Some(vec![String::from("31VzUm5vZRfWPk38W3YJaNjrkUeD6tELmjxv42cp7Vnksigned"), - String::from("GyPZzuu8S1KMs5p6iE1wBzjQsFtaB7eigssW4YbdXdtesigned")]); + String::from("GyPZzuu8S1KMs5p6iE1wBzjQsFtaB7eigssW4YbdXdtesigned")]); let (sender, receiver) = channel(); let sender = Mutex::new(sender); let cb = move |result| { sender.lock().unwrap().send(result).unwrap(); }; - XferPayload::new(inputs, outputs, None).sign_transfer(&CryptoApiHandler{}, wallet_handle, Box::new(cb)).unwrap(); - let signed_payload = receiver.recv().unwrap().unwrap(); + XferPayload::new(inputs, outputs, None).sign_transfer(&CryptoApiHandler {}, wallet_handle, Box::new(cb)).unwrap(); + let (signed_payload, _) = receiver.recv().unwrap().unwrap(); assert_eq!(expected_inputs, signed_payload.inputs); assert_eq!(expected_outputs, signed_payload.outputs); @@ -527,7 +544,7 @@ mod test_xfer_payload { fn sign_multi_input_preserve_ordering() { let attempts = 5; let wallet_handle = 1; - let (mut inputs, outputs) = inputs_outputs_valid_qualified(); + let (mut inputs, outputs) = inputs_outputs_valid_qualified(); inputs.reverse(); let expected_signatures = vec![ @@ -543,14 +560,14 @@ mod test_xfer_payload { let sender = sender.clone(); let cb = move |result| { sender.lock().unwrap().send(result).unwrap(); }; payload.clone().sign_transfer( - &CryptoApiHandler{}, + &CryptoApiHandler {}, wallet_handle, Box::new(cb) ).unwrap(); } for _ in 0..attempts { - let signed_payload = receiver.recv().unwrap().unwrap(); + let (signed_payload, _) = receiver.recv().unwrap().unwrap(); assert_eq!(expected_signatures, signed_payload.signatures.unwrap()); } } diff --git a/libsovtoken/src/utils/constants/txn_types.rs b/libsovtoken/src/utils/constants/txn_types.rs index 1212625ed..1761551c3 100644 --- a/libsovtoken/src/utils/constants/txn_types.rs +++ b/libsovtoken/src/utils/constants/txn_types.rs @@ -29,14 +29,15 @@ pub const GET_UTXO: &'static str = "10002"; pub const SET_FEES: &'static str = "20000"; +pub const NYM: &'static str = "1"; + +pub const ATTRIB: &'static str = "100"; + +pub const GET_ATTRIB: &'static str = "104"; + /** #description A transaction type submitted by anyone to get the current Fees costs of every transaction */ -pub const GET_FEES: &'static str = "20001"; - - -pub const NYM: &'static str = "1"; - -pub const ATTRIB: &'static str = "100"; +pub const GET_FEES: &'static str = "20001"; \ No newline at end of file diff --git a/libsovtoken/src/utils/ffi_support.rs b/libsovtoken/src/utils/ffi_support.rs index 56c4294e9..00c16bd89 100644 --- a/libsovtoken/src/utils/ffi_support.rs +++ b/libsovtoken/src/utils/ffi_support.rs @@ -56,7 +56,6 @@ pub fn c_pointer_from_string(string: String) -> *const c_char { */ pub fn deserialize_from_char_ptr<'a, S: JsonDeserialize<'a>>(str_ptr: *const c_char) -> Result { let json_string = str_from_char_ptr(str_ptr).ok_or(ErrorCode::CommonInvalidStructure)?; - println!("deserializing = {:?}",json_string); let result = S::from_json(json_string).map_err(|_| ErrorCode::CommonInvalidStructure); return result; diff --git a/libsovtoken/src/utils/macros.rs b/libsovtoken/src/utils/macros.rs index 690be8a9e..b95d850ab 100644 --- a/libsovtoken/src/utils/macros.rs +++ b/libsovtoken/src/utils/macros.rs @@ -52,3 +52,15 @@ macro_rules! rust_slice { unsafe { slice::from_raw_parts($x, $y as usize) } } } + +#[cfg(debug_assertions)] +#[macro_export] +macro_rules! secret { + ($val:expr) => {{ $val }}; +} + +#[cfg(not(debug_assertions))] +#[macro_export] +macro_rules! secret { + ($val:expr) => {{ "_" }}; +} \ No newline at end of file diff --git a/libsovtoken/src/utils/mod.rs b/libsovtoken/src/utils/mod.rs index 8a23847ef..1cea16cc5 100644 --- a/libsovtoken/src/utils/mod.rs +++ b/libsovtoken/src/utils/mod.rs @@ -24,4 +24,5 @@ pub mod random; pub mod sequence; pub mod results; #[cfg(any(test, feature = "integration"))] -pub mod test; \ No newline at end of file +pub mod test; +pub mod txn_author_agreement; \ No newline at end of file diff --git a/libsovtoken/src/utils/txn_author_agreement.rs b/libsovtoken/src/utils/txn_author_agreement.rs new file mode 100644 index 000000000..5a80dd1cc --- /dev/null +++ b/libsovtoken/src/utils/txn_author_agreement.rs @@ -0,0 +1,85 @@ +use serde_json; +use ErrorCode; + +pub type TaaAcceptance = serde_json::Value; + +const META_FIELD_NAME: &str = "taaAcceptance"; + +pub fn extract_taa_acceptance_from_extra(extra: Option) -> Result<(Option, Option), ErrorCode> { + match extra { + Some(serde_json::Value::Object(mut extra)) => { + let meta = extra.remove(META_FIELD_NAME); + let extra = if extra.is_empty() { None } else { Some(json!(extra)) }; + Ok((extra, meta)) + } + Some(extra) => { + Ok((Some(extra), None)) + } + None => Ok((None, None)) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + pub fn extract_taa_acceptance_from_extra_works() { + let taa_acceptance = json!({ + "mechanism": "acceptance type 1", + "taaDigest": "050e52a57837fff904d3d059c8a123e3a04177042bf467db2b2c27abd8045d5e", + "time": 123456789, + }); + + let extra = json!({ + "taaAcceptance": taa_acceptance.clone() + }); + + let expected_taa = taa_acceptance.clone(); + + let (extra, taa_acceptance) = extract_taa_acceptance_from_extra(Some(extra)).unwrap(); + assert_eq!(None, extra); + assert_eq!(expected_taa, taa_acceptance.unwrap()); + } + + #[test] + pub fn extract_taa_acceptance_from_extra_works_for_some_extra_data() { + let taa_acceptance = json!({ + "mechanism": "acceptance type 1", + "taaDigest": "050e52a57837fff904d3d059c8a123e3a04177042bf467db2b2c27abd8045d5e", + "time": 123456789, + }); + + let extra = json!({ + "data": "some data", + "taaAcceptance": taa_acceptance.clone() + }); + + let expected_extra = json!({"data": "some data"}); + let expected_taa = taa_acceptance.clone(); + + let (extra, taa_acceptance) = extract_taa_acceptance_from_extra(Some(extra)).unwrap(); + assert_eq!(expected_extra, extra.unwrap()); + assert_eq!(expected_taa, taa_acceptance.unwrap()); + } + + #[test] + pub fn extract_taa_acceptance_from_extra_works_for_no_taa_acceptance() { + let extra = json!({ + "data": "some data", + }); + + let expected_extra = json!({"data": "some data"}); + + let (extra, taa_acceptance) = extract_taa_acceptance_from_extra(Some(extra)).unwrap(); + assert_eq!(expected_extra, extra.unwrap()); + assert_eq!(None, taa_acceptance); + } + + #[test] + pub fn extract_taa_acceptance_from_extra_works_for_empty() { + let (extra, taa_acceptance) = extract_taa_acceptance_from_extra(None).unwrap(); + assert_eq!(None, extra); + assert_eq!(None, taa_acceptance); + } +} \ No newline at end of file diff --git a/libsovtoken/tests/add_fees_for_attrib_test.rs b/libsovtoken/tests/add_fees_for_attrib_test.rs index 3efece2a8..5047c12e2 100644 --- a/libsovtoken/tests/add_fees_for_attrib_test.rs +++ b/libsovtoken/tests/add_fees_for_attrib_test.rs @@ -1,5 +1,6 @@ #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; +#[macro_use] extern crate lazy_static; extern crate indyrs as indy; extern crate sovtoken; diff --git a/libsovtoken/tests/add_fees_for_cred_def_test.rs b/libsovtoken/tests/add_fees_for_cred_def_test.rs index 0ecaf7e67..6a18d4a72 100644 --- a/libsovtoken/tests/add_fees_for_cred_def_test.rs +++ b/libsovtoken/tests/add_fees_for_cred_def_test.rs @@ -2,6 +2,8 @@ extern crate serde_json; #[macro_use] extern crate serde_derive; +#[macro_use] +extern crate lazy_static; extern crate indyrs as indy; extern crate sovtoken; diff --git a/libsovtoken/tests/add_fees_for_nym.rs b/libsovtoken/tests/add_fees_for_nym.rs index bb9723dab..f2a804cef 100644 --- a/libsovtoken/tests/add_fees_for_nym.rs +++ b/libsovtoken/tests/add_fees_for_nym.rs @@ -1,5 +1,6 @@ #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; +#[macro_use] extern crate lazy_static; extern crate indyrs as indy; extern crate sovtoken; diff --git a/libsovtoken/tests/add_fees_for_revoke_reg_def.rs b/libsovtoken/tests/add_fees_for_revoke_reg_def.rs index 691836cce..5352b9a5a 100644 --- a/libsovtoken/tests/add_fees_for_revoke_reg_def.rs +++ b/libsovtoken/tests/add_fees_for_revoke_reg_def.rs @@ -2,6 +2,8 @@ extern crate serde_json; #[macro_use] extern crate serde_derive; +#[macro_use] +extern crate lazy_static; extern crate sovtoken; extern crate indyrs as indy; diff --git a/libsovtoken/tests/add_fees_for_schema_test.rs b/libsovtoken/tests/add_fees_for_schema_test.rs index 3aabdd016..1b6b7fb3e 100644 --- a/libsovtoken/tests/add_fees_for_schema_test.rs +++ b/libsovtoken/tests/add_fees_for_schema_test.rs @@ -1,5 +1,6 @@ #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; +#[macro_use] extern crate lazy_static; extern crate indyrs as indy; extern crate sovtoken; diff --git a/libsovtoken/tests/build_add_fees_txn_handler_test.rs b/libsovtoken/tests/build_add_fees_txn_handler_test.rs index 2cc7fe451..decb8b4c9 100644 --- a/libsovtoken/tests/build_add_fees_txn_handler_test.rs +++ b/libsovtoken/tests/build_add_fees_txn_handler_test.rs @@ -4,6 +4,8 @@ extern crate serde_json; extern crate serde_derive; extern crate sovtoken; extern crate indyrs as indy; +#[macro_use] +extern crate lazy_static; use indy::future::Future; diff --git a/libsovtoken/tests/build_fees_txn_handler_test.rs b/libsovtoken/tests/build_fees_txn_handler_test.rs index e074c8a92..6da5e92d9 100644 --- a/libsovtoken/tests/build_fees_txn_handler_test.rs +++ b/libsovtoken/tests/build_fees_txn_handler_test.rs @@ -1,5 +1,6 @@ #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; +#[macro_use] extern crate lazy_static; extern crate libc; extern crate sovtoken; extern crate indyrs as indy; // lib-sdk project @@ -107,7 +108,29 @@ fn add_fees_json() { assert_eq!(&expected_operation, request_value.get("operation").unwrap()); } +#[test] +fn add_fees_json_for_any_key() { + sovtoken::api::sovtoken_init(); + let fees = json!({ + "3": 6, + "TXN_ALIAS": 12, + "TXN ALIAS WITH SPACE": 12, + }); + let expected_operation = json!({ + "type": "20000", + "fees": fees, + }); + let did = bs58::encode("1234567890123456").into_string(); + let (ec_initial, receiver) = call_set_fees(&did, fees); + let (ec_callback, fees_request) = receiver.recv().unwrap(); + + let request_value: serde_json::value::Value = serde_json::from_str(&fees_request).unwrap(); + + assert_eq!(ErrorCode::Success, ec_initial); + assert_eq!(ErrorCode::Success, ec_callback); + assert_eq!(&expected_operation, request_value.get("operation").unwrap()); +} #[test] pub fn build_and_submit_set_fees() { @@ -141,10 +164,8 @@ pub fn build_and_submit_set_fees() { }).to_string(); fees::set_fees(pool_handle, wallet.handle, &payment_method, &fees, &dids, Some(dids[0])); - } - #[test] pub fn build_and_submit_set_fees_with_names() { let payment_method = sovtoken::utils::constants::general::PAYMENT_METHOD_NAME; @@ -168,8 +189,8 @@ pub fn build_and_submit_set_fees_with_names() { let current_fees = fees::get_fees(&wallet, pool_handle, Some(dids[0])); let current_fees_value: serde_json::Value = serde_json::from_str(¤t_fees).unwrap(); - assert_eq!(current_fees_value["1"].as_u64().unwrap(), 1); - assert_eq!(current_fees_value["100"].as_u64().unwrap(), 2); + assert_eq!(current_fees_value["NYM"].as_u64().unwrap(), 1); + assert_eq!(current_fees_value["ATTRIB"].as_u64().unwrap(), 2); let fees = json!({ "NYM": 0, @@ -199,18 +220,18 @@ pub fn build_and_submit_set_fees_with_empty_did() { "ATTRIB": 2 }).to_string(); - fees::set_fees(pool_handle, wallet.handle, &payment_method, &fees, &dids, None); + fees::set_fees(pool_handle, wallet.handle, &payment_method, &fees, &dids, Some(dids[0])); let current_fees = fees::get_fees(&wallet, pool_handle, None); let current_fees_value: serde_json::Value = serde_json::from_str(¤t_fees).unwrap(); - assert_eq!(current_fees_value["1"].as_u64().unwrap(), 1); - assert_eq!(current_fees_value["100"].as_u64().unwrap(), 2); + assert_eq!(current_fees_value["NYM"].as_u64().unwrap(), 1); + assert_eq!(current_fees_value["ATTRIB"].as_u64().unwrap(), 2); let fees = json!({ "NYM": 0, "ATTRIB": 0 }).to_string(); - fees::set_fees(pool_handle, wallet.handle, &payment_method, &fees, &dids, None); + fees::set_fees(pool_handle, wallet.handle, &payment_method, &fees, &dids, Some(dids[0])); } \ No newline at end of file diff --git a/libsovtoken/tests/build_get_utxo_request_handler_test.rs b/libsovtoken/tests/build_get_utxo_request_handler_test.rs index d0870330f..2436048a9 100644 --- a/libsovtoken/tests/build_get_utxo_request_handler_test.rs +++ b/libsovtoken/tests/build_get_utxo_request_handler_test.rs @@ -1,5 +1,6 @@ #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; +#[macro_use] extern crate lazy_static; extern crate sovtoken; extern crate indyrs as indy; diff --git a/libsovtoken/tests/build_mint_txn_handler_test.rs b/libsovtoken/tests/build_mint_txn_handler_test.rs index 326b2a93b..63041f612 100644 --- a/libsovtoken/tests/build_mint_txn_handler_test.rs +++ b/libsovtoken/tests/build_mint_txn_handler_test.rs @@ -8,6 +8,8 @@ extern crate indyrs as indy; // lib-sdk project extern crate serde_json; #[macro_use] extern crate log; +#[macro_use] +extern crate lazy_static; use libc::c_char; use std::ptr; @@ -248,7 +250,7 @@ pub fn build_and_submit_mint_txn_works_with_empty_did() { let (mint_req, _) = indy::payments::build_mint_req( wallet.handle, - None, + Some(&dids[0]), &output_json, None, ).wait().unwrap(); diff --git a/libsovtoken/tests/build_payment_req_handler_test.rs b/libsovtoken/tests/build_payment_req_handler_test.rs index 434cbd6f9..16aff400a 100644 --- a/libsovtoken/tests/build_payment_req_handler_test.rs +++ b/libsovtoken/tests/build_payment_req_handler_test.rs @@ -6,6 +6,7 @@ extern crate bs58; #[macro_use] extern crate log; #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; +#[macro_use] extern crate lazy_static; use std::ptr; use std::ffi::CString; @@ -140,7 +141,7 @@ fn errors_with_no_submitter_did_json() { fn success_signed_request() { sovtoken::api::sovtoken_init(); - let did = String::from("287asdjkh2323kjnbakjs"); + let did = String::from("V4SGRU86Z58d6TV7PBUe6f"); let wallet = Wallet::new(); debug!("wallet id = {:?}", wallet.handle); @@ -187,7 +188,7 @@ fn success_signed_request() { let error_code = sovtoken::api::build_payment_req_handler( command_handle, wallet.handle, - c_pointer_from_string(did), + c_pointer_from_string(did.clone()), c_pointer_from_string(inputs.to_string()), c_pointer_from_string(outputs.to_string()), ptr::null(), @@ -202,9 +203,7 @@ fn success_signed_request() { debug!("Received request {:?}", request); assert_eq!(&expected_operation, request.get("operation").unwrap()); - let ident = bs58::decode(&addresses[0]).with_check(None).into_vec().unwrap(); - let ident = bs58::encode(ident).into_string(); - assert_eq!(&ident, request.get("identifier").unwrap().as_str().unwrap()); + assert_eq!(&did, request.get("identifier").unwrap().as_str().unwrap()); assert!(request.get("reqId").is_some()); } @@ -267,6 +266,69 @@ fn success_signed_request_from_libindy() { let request: serde_json::value::Value = serde_json::from_str(&request_string).unwrap(); debug!("Received request {:?}", request); + assert_eq!(&expected_operation, request.get("operation").unwrap()); + assert_eq!(&did, request.get("identifier").unwrap().as_str().unwrap()); + assert!(request.get("reqId").is_some()); + +} + +#[test] // TODO: look carefully on changes +fn success_signed_request_from_libindy_no_identifier() { + + sovtoken::api::sovtoken_init(); + + let wallet = Wallet::new(); + debug!("wallet id = {:?}", wallet.handle); + + let (payment_addresses, addresses) = generate_payment_addresses(&wallet); + + let txo_1 = TXO { address: payment_addresses[0].clone(), seq_no: 1 }.to_libindy_string().unwrap(); + let txo_2 = TXO { address: payment_addresses[1].clone(), seq_no: 1 }.to_libindy_string().unwrap(); + + let inputs = json!([ + txo_1, txo_2 + ]); + + let outputs = json!([ + { + "recipient": payment_addresses[2], + "amount": 10 + }, + { + "recipient": payment_addresses[3], + "amount": 22 + } + ]); + + let expected_operation = json!({ + "type": XFER_PUBLIC, + "inputs": [ + {"address": addresses[0], "seqNo": 1}, + {"address": addresses[1], "seqNo": 1}, + ], + "outputs": [ + {"address": addresses[2], "amount": 10}, + {"address": addresses[3], "amount": 22}, + ], + "signatures": [ + "bnuZUPAq5jgpqvaQBzXKBQ973yCpjL1pkqJjiBtVPybpzzKGnPv3uE3VufBVZtR6hq2y55b8MSJpPFVMqskBy3m", + "4HpwuknWrSpJCs2qXEMZA1kbAsP9WxJFaoHq1cH7W3yxLg5R2fHV8QPdY5Hz2bgDmGkRitLaPa3HbF65kTxNpNTe" + ] + }); + + trace!("Calling build_payment_req"); + + let (request_string, _) = indy::payments::build_payment_req( + wallet.handle, + None, + &inputs.to_string(), + &outputs.to_string(), + None, + ).wait().unwrap(); + + let request: serde_json::value::Value = serde_json::from_str(&request_string).unwrap(); + debug!("Received request {:?}", request); + assert_eq!(&expected_operation, request.get("operation").unwrap()); let ident = bs58::decode(&addresses[0]).with_check(None).into_vec().unwrap(); let ident = bs58::encode(ident).into_string(); @@ -466,4 +528,138 @@ pub fn build_payment_req_for_not_owned_payment_address() { let err = indy::payments::build_payment_req(wallet_2.handle, Some(dids[0]), &inputs, &outputs, None).wait().unwrap_err(); assert_eq!(err.error_code, ErrorCode::WalletItemNotFound); +} + +#[test] +pub fn build_payment_req_with_taa_acceptance() { + sovtoken::api::sovtoken_init(); + + let did = String::from("Th7MpTaRZVRYnPiabds81Y"); + + let wallet = Wallet::new(); + debug!("wallet id = {:?}", wallet.handle); + + let (payment_addresses, addresses) = generate_payment_addresses(&wallet); + let txo_1 = TXO { address: payment_addresses[0].clone(), seq_no: 1 }.to_libindy_string().unwrap(); + let txo_2 = TXO { address: payment_addresses[1].clone(), seq_no: 1 }.to_libindy_string().unwrap(); + + let inputs = json!([ + txo_1, txo_2 + ]); + + let outputs = json!([ + { + "recipient": payment_addresses[2], + "amount": 10 + }, + { + "recipient": payment_addresses[3], + "amount": 22 + } + ]); + + let taa_acceptance = json!({ + "mechanism": "acceptance type 1", + "taaDigest": "050e52a57837fff904d3d059c8a123e3a04177042bf467db2b2c27abd8045d5e", + "time": 123456789, + }); + + let extra = json!({ + "taaAcceptance": taa_acceptance.clone() + }); + + let expected_operation = json!({ + "type": XFER_PUBLIC, + "inputs": [ + {"address": addresses[0], "seqNo": 1}, + {"address": addresses[1], "seqNo": 1} + ], + "outputs": [ + {"address": addresses[2], "amount": 10}, + {"address": addresses[3], "amount": 22}, + ], + "signatures": [ + "39qpBrMNPsf8MVz8KfnipRjBTGp6zV5pqkdkN36eVXW6F7XFESZwEvpqYDAvmiejSJMhqRJRcigWns2weQ6J9KuA", + "3Q4pVUGPNADdJT273zpHA4hRnGRAsnRG1BBow5UktVxK8ZTKfw9M9FMkHJDv4ERgRqJx1Wtwfd5Rv3QvuMXon8iN" + ] + }); + + let (req, _) = indy::payments::build_payment_req(wallet.handle, + Some(&did), &inputs.to_string(), &outputs.to_string(), Some(&extra.to_string())).wait().unwrap(); + + let req_parsed: serde_json::Value = serde_json::from_str(&req).unwrap(); + + assert!(req_parsed["taaAcceptance"].as_object().is_some()); + assert_eq!(req_parsed["taaAcceptance"], taa_acceptance); + + assert_eq!(expected_operation, req_parsed["operation"]); +} + +#[test] +pub fn build_payment_req_with_taa_acceptance_and_additional_extra() { + sovtoken::api::sovtoken_init(); + + let did = String::from("Th7MpTaRZVRYnPiabds81Y"); + + let wallet = Wallet::new(); + debug!("wallet id = {:?}", wallet.handle); + + let (payment_addresses, addresses) = generate_payment_addresses(&wallet); + let txo_1 = TXO { address: payment_addresses[0].clone(), seq_no: 1 }.to_libindy_string().unwrap(); + let txo_2 = TXO { address: payment_addresses[1].clone(), seq_no: 1 }.to_libindy_string().unwrap(); + + let inputs = json!([ + txo_1, txo_2 + ]); + + let outputs = json!([ + { + "recipient": payment_addresses[2], + "amount": 10 + }, + { + "recipient": payment_addresses[3], + "amount": 22 + } + ]); + + let taa_acceptance = json!({ + "mechanism": "acceptance type 1", + "taaDigest": "050e52a57837fff904d3d059c8a123e3a04177042bf467db2b2c27abd8045d5e", + "time": 123456789, + }); + + let extra = json!({ + "data": "some extra data", + "taaAcceptance": taa_acceptance.clone() + }); + + let expected_operation = json!({ + "type": XFER_PUBLIC, + "inputs": [ + {"address": addresses[0], "seqNo": 1}, + {"address": addresses[1], "seqNo": 1} + ], + "outputs": [ + {"address": addresses[2], "amount": 10}, + {"address": addresses[3], "amount": 22}, + ], + "signatures": [ + "4FLEt14msxsfWoLe58ASjxh7M1h7CFwDFE7U3RMgBm6JGVqWYQ4GwMEkXL8G2WqhKF8TG61R7GMmpx3VP5op2uJ6", + "5EGxtU9THegBbQqKXDTv71VBcNRJQNS9N2HWS267dhRSorpQkJHPbnknHfLRxqnZJynVEZ9FpuzDRW4EtAQJDy5r" + ], + "extra": { + "data": "some extra data" + }, + }); + + let (req, _) = indy::payments::build_payment_req(wallet.handle, + Some(&did), &inputs.to_string(), &outputs.to_string(), Some(&extra.to_string())).wait().unwrap(); + + let req_parsed: serde_json::Value = serde_json::from_str(&req).unwrap(); + + assert!(req_parsed["taaAcceptance"].as_object().is_some()); + assert_eq!(req_parsed["taaAcceptance"], taa_acceptance); + + assert_eq!(expected_operation, req_parsed["operation"]); } \ No newline at end of file diff --git a/libsovtoken/tests/build_verify_req_test.rs b/libsovtoken/tests/build_verify_req_test.rs index 1dc321413..7744e0c9e 100644 --- a/libsovtoken/tests/build_verify_req_test.rs +++ b/libsovtoken/tests/build_verify_req_test.rs @@ -1,5 +1,6 @@ #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; +#[macro_use] extern crate lazy_static; extern crate sovtoken; extern crate indyrs as indy; diff --git a/libsovtoken/tests/create_payment_tests.rs b/libsovtoken/tests/create_payment_tests.rs index 8eae8acb2..af794072a 100644 --- a/libsovtoken/tests/create_payment_tests.rs +++ b/libsovtoken/tests/create_payment_tests.rs @@ -9,6 +9,7 @@ extern crate rand; #[macro_use] extern crate log; #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; +#[macro_use] extern crate lazy_static; extern crate indyrs as indy; // lib-sdk project extern crate sovtoken; diff --git a/libsovtoken/tests/payment_chaos_tests.rs b/libsovtoken/tests/payment_chaos_tests.rs index b3ebf8c4a..7bf931c0e 100644 --- a/libsovtoken/tests/payment_chaos_tests.rs +++ b/libsovtoken/tests/payment_chaos_tests.rs @@ -4,6 +4,7 @@ extern crate indyrs as indy; // lib-sdk project #[macro_use] extern crate serde_derive; #[macro_use] extern crate serde_json; +#[macro_use] extern crate lazy_static; use indy::future::Future; diff --git a/libsovtoken/tests/utils/mint.rs b/libsovtoken/tests/utils/mint.rs index 2cf69171a..2bac40837 100644 --- a/libsovtoken/tests/utils/mint.rs +++ b/libsovtoken/tests/utils/mint.rs @@ -29,7 +29,7 @@ pub fn mint_tokens(cfg: HashMap, pool_handle: i32, wallet_handle: i let mint_req = Request::::multi_sign_request(wallet_handle, &mint_req, trustee_dids.to_vec()).unwrap(); - let result = indy::ledger::sign_and_submit_request(pool_handle, wallet_handle, did, &mint_req).wait().unwrap(); + let result = indy::ledger::submit_request(pool_handle, &mint_req).wait().unwrap(); utils::parse_mint_response::ParseMintResponse::from_json(&result).map_err(|_| ErrorCode::CommonInvalidStructure) } diff --git a/libsovtoken/tests/utils/payment/fees.rs b/libsovtoken/tests/utils/payment/fees.rs index 9befd8471..a8d8dbdf4 100644 --- a/libsovtoken/tests/utils/payment/fees.rs +++ b/libsovtoken/tests/utils/payment/fees.rs @@ -1,26 +1,150 @@ -extern crate indyrs as indy; - +use sovtoken::utils::constants::general::PAYMENT_METHOD_NAME; use sovtoken::logic::config::set_fees_config::SetFees; use sovtoken::logic::request::Request; -use sovtoken::utils::constants::general::PAYMENT_METHOD_NAME; use utils::wallet::Wallet; use indy::future::Future; -pub fn set_fees(pool_handle: i32, wallet_handle: i32, payment_method: &str, fees: &str, dids: &Vec<&str>, submitter_did: Option<&str>) -> String { - let set_fees_req = indy::payments::build_set_txn_fees_req(wallet_handle, submitter_did, payment_method, &fees).wait().unwrap(); +use std::sync::{Once, ONCE_INIT}; +use std::sync::Mutex; +use std::collections::HashMap; +use std::collections::hash_map::Entry; + +lazy_static! { + static ref AUTH_RULES: Mutex>> = Default::default(); +} +#[derive(Debug)] +struct AuthRule { + action: String, + txn_type: String, + field: String, + old_value: Option, + new_value: Option, + constraint: serde_json::Value +} + +pub fn set_fees(pool_handle: i32, wallet_handle: i32, payment_method: &str, fees: &str, dids: &Vec<&str>, submitter_did: Option<&str>) { + let set_fees_req = ::indy::payments::build_set_txn_fees_req(wallet_handle, submitter_did, payment_method, &fees).wait().unwrap(); let set_fees_req = Request::::multi_sign_request(wallet_handle, &set_fees_req, dids.to_vec()).unwrap(); + ::indy::ledger::submit_request(pool_handle, &set_fees_req).wait().unwrap(); + + let txn_fees: HashMap = + ::serde_json::from_str::>(fees).unwrap() + .iter_mut() + .map(|(k, _v)| (k.to_string(), k.to_string())) + .collect(); + + set_auth_rules_fee(pool_handle, wallet_handle, &submitter_did.unwrap(), &json!(txn_fees).to_string()); +} + +// Helper to set fee alias for auth rules +pub fn set_auth_rules_fee(pool_handle: i32, wallet_handle: i32, submitter_did: &str, txn_fees: &str) { + _get_default_ledger_auth_rules(pool_handle); + + let auth_rules = AUTH_RULES.lock().unwrap(); + + let fees: HashMap = ::serde_json::from_str(txn_fees).unwrap(); + + let mut responses: Vec>> = Vec::new(); + + for (txn_, fee_alias) in fees { + if let Some(rules) = auth_rules.get(&txn_) { + for auth_rule in rules { + let mut constraint: ::serde_json::Value = auth_rule.constraint.clone(); + if !constraint.as_object().map(::serde_json::Map::is_empty).unwrap_or(true) { + _set_fee_to_constraint(&mut constraint, &fee_alias); + responses.push(_send_auth_rule(pool_handle, wallet_handle, submitter_did, auth_rule, &constraint)); + } + } + } + } + + let _response = responses + .into_iter() + .map(|response| _check_auth_rule_responses(response)) + .collect::>(); +} - indy::ledger::submit_request(pool_handle, &set_fees_req).wait().unwrap() +fn _send_auth_rule(pool_handle: i32, wallet_handle: i32, submitter_did: &str, + auth_rule: &AuthRule, constraint: &serde_json::Value) -> Box> { + let auth_rule_request = ::indy::ledger::build_auth_rule_request(submitter_did, + &auth_rule.txn_type, + &auth_rule.action, + &auth_rule.field, + auth_rule.old_value.as_ref().map(String::as_str), + auth_rule.new_value.as_ref().map(String::as_str), + &constraint.to_string(), + ).wait().unwrap(); + + ::indy::ledger::sign_and_submit_request(pool_handle, wallet_handle, submitter_did, &auth_rule_request) +} + +fn _check_auth_rule_responses(response: Box>) { + let response = response.wait().unwrap(); + let response: serde_json::Value = ::serde_json::from_str(&response).unwrap(); + assert_eq!("REPLY", response["op"].as_str().unwrap()); +} + +fn _get_default_ledger_auth_rules(pool_handle: i32) { + lazy_static! { + static ref GET_DEFAULT_AUTH_CONSTRAINTS: Once = ONCE_INIT; + + } + + GET_DEFAULT_AUTH_CONSTRAINTS.call_once(|| { + let get_auth_rule_request = ::indy::ledger::build_get_auth_rule_request(None, None, None, None, None, None).wait().unwrap(); + let get_auth_rule_response = ::indy::ledger::submit_request(pool_handle, &get_auth_rule_request).wait().unwrap(); + let mut get_auth_rule_response: serde_json::Value = ::serde_json::from_str(&get_auth_rule_response).unwrap(); + + let constraints = get_auth_rule_response["result"]["data"].as_object_mut().unwrap(); + + for (constraint_id, constraint) in constraints.iter_mut() { + let parts: Vec<&str> = constraint_id.split("--").collect(); + + let txn_type = parts[0].to_string(); + let action = parts[1].to_string(); + let field = parts[2].to_string(); + let old_value = if action == "ADD" { None } else { Some(parts[3].to_string()) }; + let new_value = if parts[4] == "" { None } else { Some(parts[4].to_string()) }; + + let mut map = AUTH_RULES.lock().unwrap(); + + let rule = AuthRule { action, txn_type: txn_type.clone(), field, old_value, new_value, constraint: constraint.clone() }; + + match map.entry(txn_type) { + Entry::Occupied(rules) => { + let &mut ref mut rules = rules.into_mut(); + rules.push(rule); + } + Entry::Vacant(rules) => { + rules.insert(vec![rule]); + } + }; + } + }) +} + +fn _set_fee_to_constraint(constraint: &mut serde_json::Value, fee_alias: &str) { + match constraint["constraint_id"].as_str().unwrap() { + "ROLE" => { + constraint["metadata"]["fees"] = json!(fee_alias); + } + "OR" | "AND" => { + for mut constraint in constraint["auth_constraints"].as_array_mut().unwrap() { + _set_fee_to_constraint(&mut constraint, fee_alias) + } + } + _ => { panic!() } + } } pub fn get_fees(wallet: &Wallet, pool_handle: i32, submitter_did: Option<&str>) -> String { - let get_fees_req = indy::payments::build_get_txn_fees_req( + let get_fees_req = ::indy::payments::build_get_txn_fees_req( wallet.handle, submitter_did, PAYMENT_METHOD_NAME ).wait().unwrap(); - let result = indy::ledger::submit_request(pool_handle, &get_fees_req).wait().unwrap(); - indy::payments::parse_get_txn_fees_response(PAYMENT_METHOD_NAME, &result).wait().unwrap() -} \ No newline at end of file + let result = ::indy::ledger::submit_request(pool_handle, &get_fees_req).wait().unwrap(); + ::indy::payments::parse_get_txn_fees_response(PAYMENT_METHOD_NAME, &result).wait().unwrap() +} diff --git a/libsovtoken/tests/utils/wallet.rs b/libsovtoken/tests/utils/wallet.rs index c45dbe3d6..6b5b3661a 100644 --- a/libsovtoken/tests/utils/wallet.rs +++ b/libsovtoken/tests/utils/wallet.rs @@ -65,8 +65,8 @@ impl Wallet { return config.to_string(); } - /* private instance methods for open/create/etc...*/ + /* private instance methods for open/create/etc...*/ fn open(&mut self) -> i32 { let config: String = Wallet::create_wallet_config(&self.name); let handle = wallet::open_wallet(&config, USEFUL_CREDENTIALS).wait().unwrap();