diff --git a/.cicd/create-docker-from-binary.sh b/.cicd/create-docker-from-binary.sh index 98fa89dabb..465a4312ad 100755 --- a/.cicd/create-docker-from-binary.sh +++ b/.cicd/create-docker-from-binary.sh @@ -1,53 +1,70 @@ #!/bin/bash echo '--- :evergreen_tree: Configuring Environment' set -euo pipefail - +. ./.cicd/helpers/sanitize.sh buildkite-agent artifact download '*.deb' --step ':ubuntu: Ubuntu 18.04 - Package Builder' . -echo ":done: download successful" - -SANITIZED_BRANCH=$(echo "$BUILDKITE_BRANCH" | sed 's.^/..' | sed 's/[:/]/_/g') -SANITIZED_TAG=$(echo "$BUILDKITE_TAG" | sed 's.^/..' | tr '/' '_') -echo "$SANITIZED_BRANCH" -echo "$SANITIZED_TAG" - -# do docker build -echo '+++ :docker: Building Container' -echo ":docker::build: Building image..." +SANITIZED_BRANCH="$(sanitize "$BUILDKITE_BRANCH")" +echo "Branch '$BUILDKITE_BRANCH' sanitized as '$SANITIZED_BRANCH'." +SANITIZED_TAG="$(sanitize "$BUILDKITE_TAG")" +[[ -z "$SANITIZED_TAG" ]] || echo "Branch '$BUILDKITE_TAG' sanitized as '$SANITIZED_TAG'." +# docker build +echo '+++ :docker: Build Docker Container' DOCKERHUB_REGISTRY="docker.io/eosio/eosio.cdt" - -BUILD_TAG=${BUILDKITE_BUILD_NUMBER:-latest} -DOCKER_BUILD_GEN="docker build -t eosio_cdt_image:$BUILD_TAG -f ./docker/dockerfile ." -echo "$ $DOCKER_BUILD_GEN" -eval $DOCKER_BUILD_GEN - -#tag and push on each destination AWS & DOCKERHUB -echo '+++ :arrow_up: Pushing Container' -EOSIO_REGS=("$EOSIO_CDT_REGISTRY" "$DOCKERHUB_REGISTRY") -for REG in ${EOSIO_REGS[@]}; do - DOCKER_TAG_COMMIT="docker tag eosio_cdt_image:$BUILD_TAG $REG:$BUILDKITE_COMMIT" - DOCKER_TAG_BRANCH="docker tag eosio_cdt_image:$BUILD_TAG $REG:$SANITIZED_BRANCH" - echo -e "$ Tagging Images: \n$DOCKER_TAG_COMMIT \n$DOCKER_TAG_BRANCH" - eval $DOCKER_TAG_COMMIT +IMAGE="${DOCKERHUB_REGISTRY}:${BUILDKITE_COMMIT:-latest}" +DOCKER_BUILD="docker build -t '$IMAGE' -f ./docker/dockerfile ." +echo "$ $DOCKER_BUILD" +eval $DOCKER_BUILD +# docker tag +echo '--- :label: Tag Container' +if [[ "$BUILDKITE_PIPELINE_SLUG" =~ "-sec" ]] ; then + REGISTRIES=("$EOSIO_CDT_REGISTRY") +else + REGISTRIES=("$EOSIO_CDT_REGISTRY" "$DOCKERHUB_REGISTRY") +fi +for REG in ${REGISTRIES[@]}; do + DOCKER_TAG_BRANCH="docker tag '$IMAGE' '$REG:$SANITIZED_BRANCH'" + echo "$ $DOCKER_TAG_BRANCH" eval $DOCKER_TAG_BRANCH - DOCKER_PUSH_COMMIT="docker push $REG:$BUILDKITE_COMMIT" - DOCKER_PUSH_BRANCH="docker push $REG:$SANITIZED_BRANCH" - echo -e "$ Pushing Images: \n$DOCKER_PUSH_COMMIT \n$DOCKER_PUSH_BRANCH" - eval $DOCKER_PUSH_COMMIT + DOCKER_TAG_COMMIT="docker tag '$IMAGE' '$REG:$BUILDKITE_COMMIT'" + echo "$ $DOCKER_TAG_COMMIT" + eval $DOCKER_TAG_COMMIT + if [[ ! -z "$SANITIZED_TAG" && "$SANITIZED_BRANCH" != "$SANITIZED_TAG" ]]; then + DOCKER_TAG="docker tag '$IMAGE' '$REG:$SANITIZED_TAG'" + echo "$ $DOCKER_TAG" + eval $DOCKER_TAG + fi +done +# docker push +echo '--- :arrow_up: Push Container' +for REG in ${REGISTRIES[@]}; do + DOCKER_PUSH_BRANCH="docker push '$REG:$SANITIZED_BRANCH'" + echo "$ $DOCKER_PUSH_BRANCH" eval $DOCKER_PUSH_BRANCH - CLEAN_IMAGE_COMMIT="docker rmi $REG:$BUILDKITE_COMMIT" - CLEAN_IMAGE_BRANCH="docker rmi $REG:$SANITIZED_BRANCH" - echo -e "Cleaning Up: \n$CLEAN_IMAGE_COMMIT \n$CLEAN_IMAGE_BRANCH$" - eval $CLEAN_IMAGE_COMMIT + DOCKER_PUSH_COMMIT="docker push '$REG:$BUILDKITE_COMMIT'" + echo "$ $DOCKER_PUSH_COMMIT" + eval $DOCKER_PUSH_COMMIT + if [[ ! -z "$SANITIZED_TAG" && "$SANITIZED_BRANCH" != "$SANITIZED_TAG" ]]; then + DOCKER_PUSH_TAG="docker push '$REG:$SANITIZED_TAG'" + echo "$ $DOCKER_PUSH_TAG" + eval $DOCKER_PUSH_TAG + fi +done +# docker rmi +echo '--- :put_litter_in_its_place: Cleanup' +for REG in ${REGISTRIES[@]}; do + CLEAN_IMAGE_BRANCH="docker rmi '$REG:$SANITIZED_BRANCH' || :" + echo "$ $CLEAN_IMAGE_BRANCH" eval $CLEAN_IMAGE_BRANCH - if [[ ! -z "$SANITIZED_TAG" ]]; then - DOCKER_TAG="docker tag eosio_cdt_image $REG:$SANITIZED_TAG" - DOCKER_REM="docker rmi $REG:$SANITIZED_TAG" - echo -e "$ \n Tagging Image: \n$DOCKER_TAG \n Cleaning Up: \n$DOCKER_REM" - eval $DOCKER_TAG - eval $DOCKER_REM + CLEAN_IMAGE_COMMIT="docker rmi '$REG:$BUILDKITE_COMMIT' || :" + echo "$ $CLEAN_IMAGE_COMMIT" + eval $CLEAN_IMAGE_COMMIT + if [[ ! -z "$SANITIZED_TAG" && "$SANITIZED_BRANCH" != "$SANITIZED_TAG" ]]; then + DOCKER_RMI="docker rmi '$REG:$SANITIZED_TAG' || :" + echo "$ $DOCKER_RMI" + eval $DOCKER_RMI fi done - -DOCKER_GEN="docker rmi eosio_cdt_image:$BUILD_TAG" -echo "Clean up base image" -eval $DOCKER_GEN +DOCKER_RMI="docker rmi '$IMAGE' || :" +echo "$ $DOCKER_RMI" +eval $DOCKER_RMI +echo 'Done.' diff --git a/.cicd/generate-base-images.sh b/.cicd/generate-base-images.sh index 94abce495c..7a141640a9 100755 --- a/.cicd/generate-base-images.sh +++ b/.cicd/generate-base-images.sh @@ -9,7 +9,7 @@ TAG=$(echo $FULL_TAG | cut -d: -f2) EXISTS=$(curl -s -H "Authorization: Bearer $(curl -sSL "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${ORG_REPO}:pull" | jq --raw-output .token)" "https://registry.hub.docker.com/v2/${ORG_REPO}/manifests/$TAG") # build, if neccessary if [[ $EXISTS =~ '404 page not found' || $EXISTS =~ 'manifest unknown' ]]; then # if we cannot pull the image, we build and push it first - docker build -t $FULL_TAG -f $CICD_DIR/docker/${IMAGE_TAG}.dockerfile . + docker build -t $FULL_TAG -f $CICD_DIR/platforms/${IMAGE_TAG}.dockerfile . docker push $FULL_TAG else echo "$FULL_TAG already exists." diff --git a/.cicd/helpers/docker-hash.sh b/.cicd/helpers/docker-hash.sh index 6dcd24b3c2..de838e8e65 100644 --- a/.cicd/helpers/docker-hash.sh +++ b/.cicd/helpers/docker-hash.sh @@ -16,7 +16,7 @@ function determine-hash() { } if [[ ! -z $IMAGE_TAG ]]; then - determine-hash "$CICD_DIR/docker/${IMAGE_TAG}.dockerfile" + determine-hash "$CICD_DIR/platforms/${IMAGE_TAG}.dockerfile" export FULL_TAG="eosio/ci:eosio-cdt-$HASHED_IMAGE_TAG" else echo "Please set ENV::IMAGE_TAG to match the name of a platform dockerfile..." diff --git a/.cicd/helpers/sanitize.sh b/.cicd/helpers/sanitize.sh new file mode 100755 index 0000000000..43e0858efe --- /dev/null +++ b/.cicd/helpers/sanitize.sh @@ -0,0 +1,13 @@ +#!/bin/bash + + ##### sanitize branch names for use in URIs (docker containers) ##### +# tr '/' '_' # convert forward-slashes '/' to underscores '_' +# sed -E 's/[^-_.a-zA-Z0-9]+/-/g' # convert invalid docker chars to '-' +# sed -E 's/-+/-/g' # replace multiple dashes in a series "----" with a single dash '-' +# sed -E 's/-*_+-*/_/g' # replace dashes '-' and underscores '_' in-series with a single underscore '_' +# sed -E 's/_+/_/g' # replace multiple underscores in a row "___" with a single underscore '_' +# sed -E 's/(^[-_.]+|[-_.]+$)//g' # ensure tags do not begin or end with separator characters [-_.] +function sanitize() +{ + echo "$1" | tr '/' '_' | sed -E 's/[^-_.a-zA-Z0-9]+/-/g' | sed -E 's/-+/-/g' | sed -E 's/-*_+-*/_/g' | sed -E 's/_+/_/g' | sed -E 's/(^[-_.]+|[-_.]+$)//g' +} diff --git a/.cicd/pipeline-upload.sh b/.cicd/pipeline-upload.sh new file mode 100755 index 0000000000..202cefcec1 --- /dev/null +++ b/.cicd/pipeline-upload.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -eou pipefail + +export MACOS_10_14_TAG="eosio-cdt-macos-10.14-$(sha1sum ./.cicd/platforms/macos-10.14.sh | awk '{print $1}')" +export MACOS_10_15_TAG="eosio-cdt-macos-10.15-$(sha1sum ./.cicd/platforms/macos-10.15.sh | awk '{print $1}')" +export VARS='$MACOS_10_14_TAG:$MACOS_10_15_TAG' +envsubst "$VARS" < "./.cicd/pipeline.yml" > "./.cicd/pipeline.yml.out" +buildkite-agent artifact upload ./.cicd/pipeline.yml.out +buildkite-agent pipeline upload ./.cicd/pipeline.yml.out diff --git a/.cicd/pipeline.yml b/.cicd/pipeline.yml index c0df6e5efc..049ecb3624 100644 --- a/.cicd/pipeline.yml +++ b/.cicd/pipeline.yml @@ -1,5 +1,4 @@ steps: - - wait - label: ":aws: Amazon_Linux 2 - Build" @@ -11,7 +10,7 @@ steps: agents: queue: "automation-eks-eos-builder-fleet" timeout: ${TIMEOUT:-60} - skip: $SKIP_AMAZON_LINUX_2 + skip: ${SKIP_AMAZON_LINUX_2}${SKIP_LINUX} - label: ":centos: CentOS 7.7 - Build" command: @@ -22,7 +21,7 @@ steps: agents: queue: "automation-eks-eos-builder-fleet" timeout: ${TIMEOUT:-60} - skip: $SKIP_CENTOS_7 + skip: ${SKIP_CENTOS_7}${SKIP_LINUX} - label: ":centos: CentOS 8 - Build" command: @@ -33,7 +32,7 @@ steps: agents: queue: "automation-eks-eos-builder-fleet" timeout: ${TIMEOUT:-60} - skip: $SKIP_CENTOS_8 + skip: ${SKIP_CENTOS_8}${SKIP_LINUX} - label: ":ubuntu: Ubuntu 16.04 - Build" command: @@ -44,7 +43,7 @@ steps: agents: queue: "automation-eks-eos-builder-fleet" timeout: ${TIMEOUT:-60} - skip: $SKIP_UBUNTU_16 + skip: ${SKIP_UBUNTU_16}${SKIP_LINUX} - label: ":ubuntu: Ubuntu 18.04 - Build" command: @@ -55,7 +54,7 @@ steps: agents: queue: "automation-eks-eos-builder-fleet" timeout: ${TIMEOUT:-60} - skip: $SKIP_UBUNTU_18 + skip: ${SKIP_UBUNTU_18}${SKIP_LINUX} - label: ":ubuntu: Ubuntu 20.04 - Build" command: @@ -66,12 +65,10 @@ steps: agents: queue: "automation-eks-eos-builder-fleet" timeout: ${TIMEOUT:-60} - skip: $SKIP_UBUNTU_20 + skip: ${SKIP_UBUNTU_20}${SKIP_LINUX} - label: ":darwin: macOS 10.14 - Build" command: - - "brew update && brew upgrade" - - "brew install git automake libtool wget cmake gmp gettext doxygen graphviz lcov python@3" - "git clone $BUILDKITE_REPO eosio.cdt" - "cd eosio.cdt && if [[ $BUILDKITE_BRANCH =~ ^pull/[0-9]+/head: ]]; then git fetch -v --prune origin refs/pull/$(echo $BUILDKITE_BRANCH | cut -d/ -f2)/head; fi" - "cd eosio.cdt && git checkout -f $BUILDKITE_COMMIT && git submodule update --init --recursive" @@ -82,22 +79,35 @@ steps: no-volume: true inherit-environment-vars: true vm-name: 10.14.6_6C_14G_80G - vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent" + vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent::${MACOS_10_14_TAG}" modify-cpu: 12 modify-ram: 24 always-pull: true debug: true wait-network: true + pre-execute-sleep: 5 + pre-execute-ping-sleep: github.com + failover-registries: + - 'registry-1' + - 'registry-2' + pre-commands: + - "rm -rf mac-anka-fleet; git clone git@github.com:EOSIO/mac-anka-fleet.git && cd mac-anka-fleet && . ./ensure-tag.bash -u 12 -r 25G -a '-n'" - EOSIO/skip-checkout#v0.1.1: cd: ~ + env: + PROJECT_TAG: ${MACOS_10_14_TAG} + REPO: ${BUILDKITE_PULL_REQUEST_REPO:-$BUILDKITE_REPO} + REPO_COMMIT: $BUILDKITE_COMMIT + TAG_COMMANDS: "git clone ${BUILDKITE_PULL_REQUEST_REPO:-$BUILDKITE_REPO} eosio.cdt && cd eosio.cdt && git checkout -f $BUILDKITE_COMMIT && git submodule update --init --recursive && . ./.cicd/platforms/macos-10.14.sh && cd ~/eosio.cdt && cd .. && rm -rf eosio.cdt" + TEMPLATE: 10.14.6_6C_14G_80G + TEMPLATE_TAG: clean::cicd::git-ssh::nas::brew::buildkite-agent + timeout: ${TIMEOUT:-120} agents: - "queue=mac-anka-large-node-fleet" - skip: $SKIP_MACOS_10_14 + skip: ${SKIP_MACOS_10_14}${SKIP_MAC} - label: ":darwin: macOS 10.15 - Build" command: - - "brew update && brew upgrade" - - "brew install git automake libtool wget cmake gmp gettext doxygen graphviz lcov python@3" - "git clone $BUILDKITE_REPO eosio.cdt" - "cd eosio.cdt && if [[ $BUILDKITE_BRANCH =~ ^pull/[0-9]+/head: ]]; then git fetch -v --prune origin refs/pull/$(echo $BUILDKITE_BRANCH | cut -d/ -f2)/head; fi" - "cd eosio.cdt && git checkout -f $BUILDKITE_COMMIT && git submodule update --init --recursive" @@ -108,16 +118,32 @@ steps: no-volume: true inherit-environment-vars: true vm-name: 10.15.5_6C_14G_80G - vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent" + vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent::${MACOS_10_15_TAG}" modify-cpu: 12 modify-ram: 24 always-pull: true debug: true wait-network: true + pre-execute-sleep: 5 + pre-execute-ping-sleep: github.com + failover-registries: + - 'registry-1' + - 'registry-2' + pre-commands: + - "rm -rf mac-anka-fleet; git clone git@github.com:EOSIO/mac-anka-fleet.git && cd mac-anka-fleet && . ./ensure-tag.bash -u 12 -r 25G -a '-n'" + - EOSIO/skip-checkout#v0.1.1: + cd: ~ + env: + PROJECT_TAG: ${MACOS_10_15_TAG} + REPO: ${BUILDKITE_PULL_REQUEST_REPO:-$BUILDKITE_REPO} + REPO_COMMIT: $BUILDKITE_COMMIT + TAG_COMMANDS: "git clone ${BUILDKITE_PULL_REQUEST_REPO:-$BUILDKITE_REPO} eosio.cdt && cd eosio.cdt && git checkout -f $BUILDKITE_COMMIT && git submodule update --init --recursive && . ./.cicd/platforms/macos-10.15.sh && cd ~/eosio.cdt && cd .. && rm -rf eosio.cdt" + TEMPLATE: 10.15.5_6C_14G_80G + TEMPLATE_TAG: clean::cicd::git-ssh::nas::brew::buildkite-agent + timeout: ${TIMEOUT:-120} agents: - "queue=mac-anka-large-node-fleet" - skip: $SKIP_MACOS_10_15 - + skip: ${SKIP_MACOS_10_15}${SKIP_MAC} - wait @@ -130,7 +156,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_AMAZON_LINUX_2}${SKIP_UNIT_TESTS} + skip: ${SKIP_AMAZON_LINUX_2}${SKIP_LINUX}${SKIP_UNIT_TESTS} - label: ":centos: CentOS 7.7 - Unit Tests" command: @@ -141,7 +167,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_CENTOS_7}${SKIP_UNIT_TESTS} + skip: ${SKIP_CENTOS_7}${SKIP_LINUX}${SKIP_UNIT_TESTS} - label: ":centos: CentOS 8 - Unit Tests" command: @@ -152,7 +178,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_CENTOS_8}${SKIP_UNIT_TESTS} + skip: ${SKIP_CENTOS_8}${SKIP_LINUX}${SKIP_UNIT_TESTS} - label: ":ubuntu: Ubuntu 16.04 - Unit Tests" command: @@ -163,7 +189,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_UBUNTU_16}${SKIP_UNIT_TESTS} + skip: ${SKIP_UBUNTU_16}${SKIP_LINUX}${SKIP_UNIT_TESTS} - label: ":ubuntu: Ubuntu 18.04 - Unit Tests" command: @@ -174,7 +200,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_UBUNTU_18}${SKIP_UNIT_TESTS} + skip: ${SKIP_UBUNTU_18}${SKIP_LINUX}${SKIP_UNIT_TESTS} - label: ":ubuntu: Ubuntu 20.04 - Unit Tests" command: @@ -185,12 +211,10 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_UBUNTU_20}${SKIP_UNIT_TESTS} + skip: ${SKIP_UBUNTU_20}${SKIP_LINUX}${SKIP_UNIT_TESTS} - label: ":darwin: macOS 10.14 - Unit Tests" command: - - "brew update && brew upgrade" - - "brew install git automake libtool wget cmake gmp gettext doxygen graphviz lcov python@3" - "git clone $BUILDKITE_REPO eosio.cdt" - "cd eosio.cdt && if [[ $BUILDKITE_BRANCH =~ ^pull/[0-9]+/head: ]]; then git fetch -v --prune origin refs/pull/$(echo $BUILDKITE_BRANCH | cut -d/ -f2)/head; fi" - "cd eosio.cdt && git checkout -f $BUILDKITE_COMMIT && git submodule update --init --recursive" @@ -201,22 +225,23 @@ steps: no-volume: true inherit-environment-vars: true vm-name: 10.14.6_6C_14G_80G - vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent" - modify-cpu: 12 - modify-ram: 24 + vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent::${MACOS_10_14_TAG}" always-pull: true debug: true wait-network: true + pre-execute-sleep: 5 + pre-execute-ping-sleep: github.com + failover-registries: + - 'registry-1' + - 'registry-2' - EOSIO/skip-checkout#v0.1.1: cd: ~ agents: - - "queue=mac-anka-large-node-fleet" - skip: ${SKIP_MACOS_10_14}${SKIP_UNIT_TESTS} + - "queue=mac-anka-node-fleet" + skip: ${SKIP_MACOS_10_14}${SKIP_MAC}${SKIP_UNIT_TESTS} - label: ":darwin: macOS 10.15 - Unit Tests" command: - - "brew update && brew upgrade" - - "brew install git automake libtool wget cmake gmp gettext doxygen graphviz lcov python@3" - "git clone $BUILDKITE_REPO eosio.cdt" - "cd eosio.cdt && if [[ $BUILDKITE_BRANCH =~ ^pull/[0-9]+/head: ]]; then git fetch -v --prune origin refs/pull/$(echo $BUILDKITE_BRANCH | cut -d/ -f2)/head; fi" - "cd eosio.cdt && git checkout -f $BUILDKITE_COMMIT && git submodule update --init --recursive" @@ -227,15 +252,20 @@ steps: no-volume: true inherit-environment-vars: true vm-name: 10.15.5_6C_14G_80G - vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent" - modify-cpu: 12 - modify-ram: 24 + vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent::${MACOS_10_15_TAG}" always-pull: true debug: true wait-network: true + pre-execute-sleep: 5 + pre-execute-ping-sleep: github.com + failover-registries: + - 'registry-1' + - 'registry-2' + - EOSIO/skip-checkout#v0.1.1: + cd: ~ agents: - - "queue=mac-anka-large-node-fleet" - skip: ${SKIP_MACOS_10_15}${SKIP_UNIT_TESTS} + - "queue=mac-anka-node-fleet" + skip: ${SKIP_MACOS_10_15}${SKIP_MAC}${SKIP_UNIT_TESTS} - label: ":aws: Amazon_Linux 2 - Toolchain Tests" command: @@ -246,7 +276,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_AMAZON_LINUX_2}${SKIP_TOOLCHAIN_TESTS} + skip: ${SKIP_AMAZON_LINUX_2}${SKIP_LINUX}${SKIP_TOOLCHAIN_TESTS} - label: ":centos: CentOS 7.7 - Toolchain Tests" command: @@ -257,7 +287,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_CENTOS_7}${SKIP_TOOLCHAIN_TESTS} + skip: ${SKIP_CENTOS_7}${SKIP_LINUX}${SKIP_TOOLCHAIN_TESTS} - label: ":centos: CentOS 8 - Toolchain Tests" command: @@ -268,7 +298,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_CENTOS_8}${SKIP_TOOLCHAIN_TESTS} + skip: ${SKIP_CENTOS_8}${SKIP_LINUX}${SKIP_TOOLCHAIN_TESTS} - label: ":ubuntu: Ubuntu 16.04 - Toolchain Tests" command: @@ -279,7 +309,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_UBUNTU_16}${SKIP_TOOLCHAIN_TESTS} + skip: ${SKIP_UBUNTU_16}${SKIP_LINUX}${SKIP_TOOLCHAIN_TESTS} - label: ":ubuntu: Ubuntu 18.04 - Toolchain Tests" command: @@ -290,7 +320,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_UBUNTU_18}${SKIP_TOOLCHAIN_TESTS} + skip: ${SKIP_UBUNTU_18}${SKIP_LINUX}${SKIP_TOOLCHAIN_TESTS} - label: ":ubuntu: Ubuntu 20.04 - Toolchain Tests" command: @@ -301,12 +331,10 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_UBUNTU_20}${SKIP_TOOLCHAIN_TESTS} + skip: ${SKIP_UBUNTU_20}${SKIP_LINUX}${SKIP_TOOLCHAIN_TESTS} - label: ":darwin: macOS 10.14 - Toolchain Tests" command: - - "brew update && brew upgrade" - - "brew install git automake libtool wget cmake gmp gettext doxygen graphviz lcov python@3" - "git clone $BUILDKITE_REPO eosio.cdt" - "cd eosio.cdt && if [[ $BUILDKITE_BRANCH =~ ^pull/[0-9]+/head: ]]; then git fetch -v --prune origin refs/pull/$(echo $BUILDKITE_BRANCH | cut -d/ -f2)/head; fi" - "cd eosio.cdt && git checkout -f $BUILDKITE_COMMIT && git submodule update --init --recursive" @@ -317,22 +345,23 @@ steps: no-volume: true inherit-environment-vars: true vm-name: 10.14.6_6C_14G_80G - vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent" - modify-cpu: 12 - modify-ram: 24 + vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent::${MACOS_10_14_TAG}" always-pull: true debug: true wait-network: true + pre-execute-sleep: 5 + pre-execute-ping-sleep: github.com + failover-registries: + - 'registry-1' + - 'registry-2' - EOSIO/skip-checkout#v0.1.1: cd: ~ agents: - - "queue=mac-anka-large-node-fleet" - skip: ${SKIP_MACOS_10_14}${SKIP_TOOLCHAIN_TESTS} + - "queue=mac-anka-node-fleet" + skip: ${SKIP_MACOS_10_14}${SKIP_MAC}${SKIP_TOOLCHAIN_TESTS} - label: ":darwin: macOS 10.15 - Toolchain Tests" command: - - "brew update && brew upgrade" - - "brew install git automake libtool wget cmake gmp gettext doxygen graphviz lcov python@3" - "git clone $BUILDKITE_REPO eosio.cdt" - "cd eosio.cdt && if [[ $BUILDKITE_BRANCH =~ ^pull/[0-9]+/head: ]]; then git fetch -v --prune origin refs/pull/$(echo $BUILDKITE_BRANCH | cut -d/ -f2)/head; fi" - "cd eosio.cdt && git checkout -f $BUILDKITE_COMMIT && git submodule update --init --recursive" @@ -343,17 +372,20 @@ steps: no-volume: true inherit-environment-vars: true vm-name: 10.15.5_6C_14G_80G - vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent" - modify-cpu: 12 - modify-ram: 24 + vm-registry-tag: "clean::cicd::git-ssh::nas::brew::buildkite-agent::${MACOS_10_15_TAG}" always-pull: true debug: true wait-network: true + pre-execute-sleep: 5 + pre-execute-ping-sleep: github.com + failover-registries: + - 'registry-1' + - 'registry-2' - EOSIO/skip-checkout#v0.1.1: cd: ~ agents: - - "queue=mac-anka-large-node-fleet" - skip: ${SKIP_MACOS_10_14}${SKIP_TOOLCHAIN_TESTS} + - "queue=mac-anka-node-fleet" + skip: ${SKIP_MACOS_10_14}${SKIP_MAC}${SKIP_TOOLCHAIN_TESTS} - label: ":ubuntu: Ubuntu 18.04 - Integration Tests" command: @@ -364,7 +396,7 @@ steps: agents: queue: "automation-eks-eos-tester-fleet" timeout: ${TIMEOUT:-10} - skip: ${SKIP_UBUNTU_18}${SKIP_INTEGRATION_TESTS} + skip: ${SKIP_UBUNTU_18}${SKIP_LINUX}${SKIP_INTEGRATION_TESTS} - wait: continue_on_failure: true @@ -393,8 +425,22 @@ steps: PKGTYPE: "rpm" agents: queue: "automation-eks-eos-tester-fleet" + key: "amazonlinux2pb" timeout: ${TIMEOUT:-10} - skip: ${SKIP_AMAZON_LINUX_2}${SKIP_PACKAGE_BUILDER} + skip: ${SKIP_AMAZON_LINUX_2}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} + + - label: ":aws: Amazon_Linux 2 - Test Package" + command: | + buildkite-agent artifact download '*.rpm' . --step ':aws: Amazon_Linux 2 - Package Builder' --agent-access-token $$BUILDKITE_AGENT_ACCESS_TOKEN + ./.cicd/test-package.sh + env: + IMAGE: "amazonlinux:2" + agents: + queue: "automation-eks-eos-tester-fleet" + depends_on: "amazonlinux2pb" + allow_dependency_failure: false + timeout: ${TIMEOUT:-10} + skip: ${SKIP_AMAZON_LINUX_2}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} - label: ":centos: Centos 7.7 - Package Builder" command: @@ -407,8 +453,22 @@ steps: PKGTYPE: "rpm" agents: queue: "automation-eks-eos-tester-fleet" + key: "centos7pb" timeout: ${TIMEOUT:-10} - skip: ${SKIP_CENTOS_7}${SKIP_PACKAGE_BUILDER} + skip: ${SKIP_CENTOS_7}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} + + - label: ":centos: Centos 7.7 - Test Package" + command: | + buildkite-agent artifact download '*.rpm' . --step ':centos: Centos 7.7 - Package Builder' --agent-access-token $$BUILDKITE_AGENT_ACCESS_TOKEN + ./.cicd/test-package.sh + env: + IMAGE: "centos:7" + agents: + queue: "automation-eks-eos-tester-fleet" + depends_on: "centos7pb" + allow_dependency_failure: false + timeout: ${TIMEOUT:-10} + skip: ${SKIP_CENTOS_7}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} - label: ":centos: Centos 8 - Package Builder" command: @@ -421,8 +481,22 @@ steps: PKGTYPE: "rpm" agents: queue: "automation-eks-eos-tester-fleet" + key: "centos8pb" + timeout: ${TIMEOUT:-10} + skip: ${SKIP_CENTOS_8}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} + + - label: ":centos: Centos 8 - Test Package" + command: | + buildkite-agent artifact download '*.rpm' . --step ':centos: Centos 8 - Package Builder' --agent-access-token $$BUILDKITE_AGENT_ACCESS_TOKEN + ./.cicd/test-package.sh + env: + IMAGE: "centos:8" + agents: + queue: "automation-eks-eos-tester-fleet" + depends_on: "centos8pb" + allow_dependency_failure: false timeout: ${TIMEOUT:-10} - skip: ${SKIP_CENTOS_8}${SKIP_PACKAGE_BUILDER} + skip: ${SKIP_CENTOS_8}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} - label: ":ubuntu: Ubuntu 16.04 - Package Builder" command: @@ -435,8 +509,22 @@ steps: PKGTYPE: "deb" agents: queue: "automation-eks-eos-tester-fleet" + key: "ubuntu1604pb" timeout: ${TIMEOUT:-10} - skip: ${SKIP_UBUNTU_16}${SKIP_PACKAGE_BUILDER} + skip: ${SKIP_UBUNTU_16}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} + + - label: ":ubuntu: Ubuntu 16.04 - Test Package" + command: | + buildkite-agent artifact download '*.deb' . --step ':ubuntu: Ubuntu 16.04 - Package Builder' --agent-access-token $$BUILDKITE_AGENT_ACCESS_TOKEN + ./.cicd/test-package.sh + env: + IMAGE: "ubuntu:16.04" + agents: + queue: "automation-eks-eos-tester-fleet" + depends_on: "ubuntu1604pb" + allow_dependency_failure: false + timeout: ${TIMEOUT:-10} + skip: ${SKIP_UBUNTU_16}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} - label: ":ubuntu: Ubuntu 18.04 - Package Builder" command: @@ -449,8 +537,22 @@ steps: PKGTYPE: "deb" agents: queue: "automation-eks-eos-tester-fleet" + key: "ubuntu1804pb" + timeout: ${TIMEOUT:-10} + skip: ${SKIP_UBUNTU_18}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} + + - label: ":ubuntu: Ubuntu 18.04 - Test Package" + command: | + buildkite-agent artifact download '*.deb' . --step ':ubuntu: Ubuntu 18.04 - Package Builder' --agent-access-token $$BUILDKITE_AGENT_ACCESS_TOKEN + ./.cicd/test-package.sh + env: + IMAGE: "ubuntu:18.04" + agents: + queue: "automation-eks-eos-tester-fleet" + depends_on: "ubuntu1804pb" + allow_dependency_failure: false timeout: ${TIMEOUT:-10} - skip: ${SKIP_UBUNTU_18}${SKIP_PACKAGE_BUILDER} + skip: ${SKIP_UBUNTU_18}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} - label: ":ubuntu: Ubuntu 20.04 - Package Builder" command: @@ -463,8 +565,22 @@ steps: PKGTYPE: "deb" agents: queue: "automation-eks-eos-tester-fleet" + key: "ubuntu2004pb" + timeout: ${TIMEOUT:-10} + skip: ${SKIP_UBUNTU_20}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} + + - label: ":ubuntu: Ubuntu 20.04 - Test Package" + command: | + buildkite-agent artifact download '*.deb' . --step ':ubuntu: Ubuntu 20.04 - Package Builder' --agent-access-token $$BUILDKITE_AGENT_ACCESS_TOKEN + ./.cicd/test-package.sh + env: + IMAGE: "ubuntu:20.04" + agents: + queue: "automation-eks-eos-tester-fleet" + depends_on: "ubuntu2004pb" + allow_dependency_failure: false timeout: ${TIMEOUT:-10} - skip: ${SKIP_UBUNTU_20}${SKIP_PACKAGE_BUILDER} + skip: ${SKIP_UBUNTU_20}${SKIP_LINUX}${SKIP_PACKAGE_BUILDER} - label: ":darwin: Mojave - Package Builder" command: @@ -482,12 +598,17 @@ steps: always-pull: true debug: true wait-network: true + pre-execute-sleep: 5 + pre-execute-ping-sleep: github.com + failover-registries: + - 'registry-1' + - 'registry-2' - EOSIO/skip-checkout#v0.1.1: cd: ~ agents: - "queue=mac-anka-node-fleet" - timeout: 10 - skip: ${SKIP_MACOS_10_14}${SKIP_PACKAGE_BUILDER} + timeout: ${TIMEOUT:-20} + skip: ${SKIP_MACOS_10_14}${SKIP_MAC}${SKIP_PACKAGE_BUILDER} - label: ":darwin: Catalina - Package Builder" command: @@ -505,10 +626,17 @@ steps: always-pull: true debug: true wait-network: true + pre-execute-sleep: 5 + pre-execute-ping-sleep: github.com + failover-registries: + - 'registry-1' + - 'registry-2' + - EOSIO/skip-checkout#v0.1.1: + cd: ~ agents: - "queue=mac-anka-node-fleet" - timeout: 20 - skip: ${SKIP_MACOS_10_15}${SKIP_PACKAGE_BUILDER} + timeout: ${TIMEOUT:-20} + skip: ${SKIP_MACOS_10_15}${SKIP_MAC}${SKIP_PACKAGE_BUILDER} - wait @@ -518,6 +646,19 @@ steps: queue: "automation-eks-eos-tester-fleet" timeout: 10 + - label: ":beer: Brew Updater" + command: | + buildkite-agent artifact download eosio.cdt.rb . --step ':darwin: Mojave - Package Builder' + mv eosio.cdt.rb eosio_cdt_mojave.rb + buildkite-agent artifact upload eosio_cdt_mojave.rb + buildkite-agent artifact download eosio.cdt.rb . --step ':darwin: Catalina - Package Builder' + mv eosio.cdt.rb eosio_cdt_catalina.rb + buildkite-agent artifact upload eosio_cdt_catalina.rb + agents: + queue: "automation-basic-builder-fleet" + timeout: "${TIMEOUT:-5}" + skip: ${SKIP_MACOS_10_14}${SKIP_MACOS_10_15}${SKIP_MAC}${SKIP_PACKAGE_BUILDER} + - label: ":git: Git Submodule Regression Check" command: - "./.cicd/submodule-regression-checker.sh" diff --git a/.cicd/docker/amazonlinux-2.dockerfile b/.cicd/platforms/amazonlinux-2.dockerfile similarity index 100% rename from .cicd/docker/amazonlinux-2.dockerfile rename to .cicd/platforms/amazonlinux-2.dockerfile diff --git a/.cicd/docker/centos-7.7.dockerfile b/.cicd/platforms/centos-7.7.dockerfile similarity index 100% rename from .cicd/docker/centos-7.7.dockerfile rename to .cicd/platforms/centos-7.7.dockerfile diff --git a/.cicd/docker/centos-8.dockerfile b/.cicd/platforms/centos-8.dockerfile similarity index 100% rename from .cicd/docker/centos-8.dockerfile rename to .cicd/platforms/centos-8.dockerfile diff --git a/.cicd/platforms/macos-10.14.sh b/.cicd/platforms/macos-10.14.sh new file mode 100755 index 0000000000..3c91271a9c --- /dev/null +++ b/.cicd/platforms/macos-10.14.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -eou pipefail +VERSION=1 + +brew update && brew upgrade +brew install automake cmake doxygen gettext git gmp graphviz lcov libtool python@3 wget diff --git a/.cicd/platforms/macos-10.15.sh b/.cicd/platforms/macos-10.15.sh new file mode 100755 index 0000000000..3c91271a9c --- /dev/null +++ b/.cicd/platforms/macos-10.15.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -eou pipefail +VERSION=1 + +brew update && brew upgrade +brew install automake cmake doxygen gettext git gmp graphviz lcov libtool python@3 wget diff --git a/.cicd/docker/ubuntu-16.04.dockerfile b/.cicd/platforms/ubuntu-16.04.dockerfile similarity index 100% rename from .cicd/docker/ubuntu-16.04.dockerfile rename to .cicd/platforms/ubuntu-16.04.dockerfile diff --git a/.cicd/docker/ubuntu-18.04.dockerfile b/.cicd/platforms/ubuntu-18.04.dockerfile similarity index 100% rename from .cicd/docker/ubuntu-18.04.dockerfile rename to .cicd/platforms/ubuntu-18.04.dockerfile diff --git a/.cicd/docker/ubuntu-20.04.dockerfile b/.cicd/platforms/ubuntu-20.04.dockerfile similarity index 100% rename from .cicd/docker/ubuntu-20.04.dockerfile rename to .cicd/platforms/ubuntu-20.04.dockerfile diff --git a/.cicd/test-package.run.sh b/.cicd/test-package.run.sh new file mode 100755 index 0000000000..1f571d2e02 --- /dev/null +++ b/.cicd/test-package.run.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -eu + +echo '+++ :minidisc: Installing EOSIO CDT' + +if [[ $(apt-get --version 2>/dev/null) ]]; then # debian family + UPDATE='apt-get update' + echo "$ $UPDATE" + eval $UPDATE + INSTALL="apt-get install -y /eos/*.deb" + echo "$ $INSTALL" + eval $INSTALL +elif [[ $(yum --version 2>/dev/null) ]]; then # RHEL family + UPDATE='yum check-update || :' + echo "$ $UPDATE" + eval $UPDATE + INSTALL="yum install -y /eos/*.rpm" + echo "$ $INSTALL" + eval $INSTALL +else + echo 'ERROR: Package manager not detected!' + exit 3 +fi + +eosio-cpp --version diff --git a/.cicd/test-package.sh b/.cicd/test-package.sh new file mode 100755 index 0000000000..039f939fa4 --- /dev/null +++ b/.cicd/test-package.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -eu + +echo '--- :docker: Selecting Container' + +docker pull $IMAGE +docker run --rm -v "$(pwd):/eos" -w '/eos' -it $IMAGE ./.cicd/test-package.run.sh diff --git a/.gitmodules b/.gitmodules index 9b0f8369c1..d4d6791da8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,6 +9,7 @@ [submodule "eosio_llvm"] path = eosio_llvm url = https://github.com/eosio/llvm + branch = eosio [submodule "libraries/native/softfloat"] path = libraries/native/softfloat url = https://github.com/EOSIO/berkeley-softfloat-3.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 871bf33972..3c81ebdc07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ endif() set(VERSION_MAJOR 1) set(VERSION_MINOR 8) -set(VERSION_PATCH 0) +set(VERSION_PATCH 1) #set(VERSION_SUFFIX rc2) if (VERSION_SUFFIX) diff --git a/README.md b/README.md index 1dd41a3157..7e2c621260 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # EOSIO.CDT (Contract Development Toolkit) -## Version : 1.8.0 +## Version : 1.8.1 EOSIO.CDT is a toolchain for WebAssembly (WASM) and a set of tools to facilitate smart contract development for the EOSIO platform. In addition to being a general purpose WebAssembly toolchain, [EOSIO](https://github.com/eosio/eos) specific optimizations are available to support building EOSIO smart contracts. This new toolchain is built around [Clang 9](https://github.com/eosio/llvm), which means that EOSIO.CDT has the most currently available optimizations and analyses from LLVM, but as the WASM target is still considered experimental, some optimizations are incomplete or not available. @@ -28,8 +28,8 @@ brew remove eosio.cdt ### Debian Package Install ```sh -wget https://github.com/eosio/eosio.cdt/releases/download/v1.8.0/eosio.cdt_1.8.0-1-ubuntu-18.04_amd64.deb -sudo apt install ./eosio.cdt_1.8.0-1-ubuntu-18.04_amd64.deb +wget https://github.com/eosio/eosio.cdt/releases/download/v1.8.1/eosio.cdt_1.8.1-1-ubuntu-18.04_amd64.deb +sudo apt install ./eosio.cdt_1.8.1-1-ubuntu-18.04_amd64.deb ``` ### Debian Package Uninstall @@ -39,8 +39,8 @@ sudo apt remove eosio.cdt ### RPM Package Install ```sh -wget https://github.com/eosio/eosio.cdt/releases/download/v1.8.0/eosio.cdt-1.8.0-1.el7.x86_64.rpm -sudo yum install ./eosio.cdt-1.8.0-1.el7.x86_64.rpm +wget https://github.com/eosio/eosio.cdt/releases/download/v1.8.1/eosio.cdt-1.8.1-1.el7.x86_64.rpm +sudo yum install ./eosio.cdt-1.8.1-1.el7.x86_64.rpm ``` ### RPM Package Uninstall diff --git a/docs/02_installation.md b/docs/02_installation/index.md similarity index 84% rename from docs/02_installation.md rename to docs/02_installation/index.md index a8128eaa99..3b8fd6e2ff 100644 --- a/docs/02_installation.md +++ b/docs/02_installation/index.md @@ -22,8 +22,8 @@ brew remove eosio.cdt ## Debian Package Install ```sh -wget https://github.com/EOSIO/eosio.cdt/releases/download/v1.8.0/eosio.cdt_1.8.0-1-ubuntu-18.04_amd64.deb -sudo apt install ./eosio.cdt_1.8.0-1-ubuntu-18.04_amd64.deb +wget https://github.com/EOSIO/eosio.cdt/releases/download/v1.8.1/eosio.cdt_1.8.1-ubuntu-18.04_amd64.deb +sudo apt install ./eosio.cdt_1.8.1-ubuntu-18.04_amd64.deb ``` ## Debian Package Uninstall @@ -35,8 +35,8 @@ sudo apt remove eosio.cdt ## RPM Package Install ```sh -wget https://github.com/EOSIO/eosio.cdt/releases/download/v1.8.0/eosio.cdt-1.8.0-1.el7.x86_64.rpm -sudo yum install ./eosio.cdt-1.8.0-1.el7.x86_64.rpm +wget https://github.com/EOSIO/eosio.cdt/releases/download/v1.8.1/eosio.cdt-1.8.1.el7.x86_64.rpm +sudo yum install ./eosio.cdt-1.8.1.el7.x86_64.rpm ``` ## RPM Package Uninstall diff --git a/docs/03_command-reference/eosio-cc.md b/docs/03_command-reference/eosio-cc.md index 11b08050d3..a1c35daea4 100644 --- a/docs/03_command-reference/eosio-cc.md +++ b/docs/03_command-reference/eosio-cc.md @@ -71,4 +71,5 @@ compiler options: -sysroot= - Set the system root directory -v - Show commands to run and use verbose output -w - Suppress all warnings + --warn-action-read-only - Issue a warning if a read-only action uses a write API and continue compilation ``` diff --git a/docs/03_command-reference/eosio-cpp.md b/docs/03_command-reference/eosio-cpp.md index 27b9053abd..5bac526d02 100644 --- a/docs/03_command-reference/eosio-cpp.md +++ b/docs/03_command-reference/eosio-cpp.md @@ -71,4 +71,10 @@ compiler options: -sysroot= - Set the system root directory -v - Show commands to run and use verbose output -w - Suppress all warnings + -no-missing-ricardian-clause - Defaults to false, disables warnings for missing Ricardian clauses + --warn-action-read-only - Issue a warning if a read-only action uses a write API and continue compilation ``` + +## Notes + +* `-no-missing-ricardian-clause`: Defaults to false, if enabled, it suppresses warnings for missing Ricardian clauses on contracts and contract actions. That includes the warnings generated when there is no independent Ricardian clause file. diff --git a/docs/05_features/10_resource_payer.md b/docs/05_features/10_resource_payer.md new file mode 100644 index 0000000000..e78756fae8 --- /dev/null +++ b/docs/05_features/10_resource_payer.md @@ -0,0 +1,46 @@ +--- +content_title: Resource Payer +--- + +## Overview + +The *Resource Payer* feature, also known as *Transaction Sponsorship*, allows to specify the resource payer for a transaction. As a direct consequence the application developers have a simple and secure way to sponsor, or decline to sponsor, transactions on behalf of their users. + +## Concept + +Before the `EOSIO 2.2` version, the CPU and NET resource costs for transactions on EOSIO-based blockchains were paid by the end users of the application. This fact made attracting and onboarding new users difficult. + +The *Resource Payer* feature makes it easier for both smart contract and full stack developers on EOSIO-based blockchains to allow their users to transact without having to pay for CPU and NET resource costs in a simple and secure manner. This new feature makes the process of onboarding new users straightforward. + +Blockchain application developers can use the *Resource Payer* on any EOSIO-based blockchain that enables it via the newly introduced `RESOURCE_PAYER` upgrade protocol feature. + +### Related Concepts + +A [transaction](https://developers.eos.io/welcome/latest/glossary/index/#transaction) consists of one or more actions executed atomically on the blockchain which alter the state of the blockchain. + +You need system resources, [CPU](https://developers.eos.io/welcome/latest/glossary/index/#cpu) and [NET](https://developers.eos.io/welcome/latest/glossary/index/#net), to be able to execute transactions. + +The *Resource Payer* feature allows the application developers to designate the payer of the CPU and NET resources cost to a random blockchain [account](https://developers.eos.io/welcome/latest/glossary/index/#account). + +The *Resource Payer* feature is not available by default. To enable the feature, the `RESOURCE_PAYER` upgrade protocol feature must be enabled. For more information about upgrading protocol features, read the [Consensus Protocol Upgrade Process](https://developers.eos.io/manuals/eos/latest/nodeos/upgrade-guides/1.8-upgrade-guide/#upgrade-process-for-all-eosio-networks-including-test-networks) documentation. + +When you send the transaction to the blockchain, if you want to designate the resource payer, you must specify in the transaction definition the payer information which consists of the following: + +* The payer account name +* The maximum CPU limit amount the payer supports expressed in microseconds +* The maximum NET limit amount the payer supports expressed in bytes + +The resource payer information must be defined as a transaction extension and placed in the transaction’s extension list. For more details please see the examples provided in the following section. + +## Examples + +Find below one example that illustrate how to send transaction to the blockchain which contain resource payer information. + +### EOSJS + +To send a transaction to the blockchain using javascript via the `eosjs` library, follow the [How To Send A Transaction](https://developers.eos.io/manuals/eos/latest/cleos/how-to-guides/how-to-submit-a-transaction) steps and note in the json example the resource payer information set in the ``transaction_extensions`` field. + +### Cleos + +[[info | Cleos Support Will Be Available Starting With v2.2 RC2 Onwards]] +| The ability to send transaction with resource payer information using `cleos` will be added in the v2.2 RC2 release version. diff --git a/docs/05_best-practices/12_return_values_from_actions.md b/docs/05_features/20_return_values_from_actions.md similarity index 100% rename from docs/05_best-practices/12_return_values_from_actions.md rename to docs/05_features/20_return_values_from_actions.md diff --git a/docs/05_best-practices/13_binary-extension.md b/docs/05_features/30_binary-extension.md similarity index 99% rename from docs/05_best-practices/13_binary-extension.md rename to docs/05_features/30_binary-extension.md index c7783641ac..d92e114d0e 100644 --- a/docs/05_best-practices/13_binary-extension.md +++ b/docs/05_features/30_binary-extension.md @@ -1,5 +1,5 @@ --- -content_title: The eosio::binary_extension type +content_title: The eosio::binary_extension Type --- Let's fully explain what the `eosio::binary_extension` type is, what it does, and why we need it for contract upgrades in certain situations. @@ -327,7 +327,7 @@ struct [[eosio::table]] structure { } ``` -And their corresponding sections in the `.abi` files: +Find below their corresponding sections in the `.abi` files: **binary_extension_contract.abi** diff --git a/docs/05_best-practices/10_native-tester-compilation.md b/docs/05_features/40_native-tester-compilation.md similarity index 99% rename from docs/05_best-practices/10_native-tester-compilation.md rename to docs/05_features/40_native-tester-compilation.md index c1dc48e348..3391424991 100644 --- a/docs/05_best-practices/10_native-tester-compilation.md +++ b/docs/05_features/40_native-tester-compilation.md @@ -1,5 +1,5 @@ --- -content_title: How to use native tester/compilation +content_title: Native Tester And Compilation --- As of v1.5.0 native compilation can be performed and a new set of libraries to facilitate native testing and native "scratch pad" compilation. [`eosio-cc`](../03_command-reference/eosio-cc.md), [`eosio-cpp`](../03_command-reference/eosio-cpp.md) and [`eosio-ld`](../03_command-reference/eosio-ld.md) now support building "smart contracts" and unit tests natively for quick tests to help facilitate faster development \(note the default implementations of eosio `intrinsics` are currently asserts that state they are unavailable, these are user definable.\) diff --git a/docs/05_best-practices/09_deferred_transactions.md b/docs/05_features/99_deferred_transactions.md similarity index 97% rename from docs/05_best-practices/09_deferred_transactions.md rename to docs/05_features/99_deferred_transactions.md index 82036a6734..12e5d82c5b 100644 --- a/docs/05_best-practices/09_deferred_transactions.md +++ b/docs/05_features/99_deferred_transactions.md @@ -1,5 +1,5 @@ --- -content_title: Deferred transactions +content_title: Deferred Transactions --- Deferred communication conceptually takes the form of action notifications sent to a peer transaction. Deferred actions get scheduled to run, at best, at a later time, at the producer's discretion. There is no guarantee that a deferred action will be executed. diff --git a/docs/06_how-to-guides/10_compile/01_compile-a-contract-via-cli.md b/docs/06_how-to-guides/10_compile/01_compile-a-contract-via-cli.md index 115ab29613..f17b2693db 100644 --- a/docs/06_how-to-guides/10_compile/01_compile-a-contract-via-cli.md +++ b/docs/06_how-to-guides/10_compile/01_compile-a-contract-via-cli.md @@ -10,7 +10,7 @@ This guide provides instructions how to compile a smart contract using the comma See the following code reference: -* The [`eosio-cpp`](https://developers.eos.io/manuals/eosio.cdt/latest/command-reference/eosio-cpp) tool. +* The [`eosio-cpp`](https://developers.eos.io/manuals/eosio.cdt/v1.8/command-reference/eosio-cpp) tool. ## Before you begin @@ -32,11 +32,11 @@ Follow the following steps to compile your contract. ``` Where: - - `eosio-cpp` = Is the [`eosio-cpp`](https://developers.eos.io/manuals/eosio.cdt/latest/command-reference/eosio-cpp) tool. + - `eosio-cpp` = Is the [`eosio-cpp`](https://developers.eos.io/manuals/eosio.cdt/v1.8/command-reference/eosio-cpp) tool. - `-abigen` = It instructs the `eosio-cpp` tool to generate ABI file. - `../src/hello.cpp` = Is the input cpp source file to be compiled. - `-o hello.wasm` = It instructs the `eosio-cpp` tool who to name the output wasm file. - - `-I ../include/` = It tells `eosio-cpp` tool what the include folder path is, in this particular case it is relative path. + - `-I ../include/` = It tells `eosio-cpp` tool what the include folder path is, in this particular case it is a relative path. 3. Verify the following two files were generated: diff --git a/docs/06_how-to-guides/10_compile/03_compiling-contracts-with-cmake.md b/docs/06_how-to-guides/10_compile/03_compiling-contracts-with-cmake.md index 612c0e3425..4ed3cf6593 100644 --- a/docs/06_how-to-guides/10_compile/03_compiling-contracts-with-cmake.md +++ b/docs/06_how-to-guides/10_compile/03_compiling-contracts-with-cmake.md @@ -4,7 +4,7 @@ content_title: How to compile a smart contract with CMake ## Overview -This guide provides instructions on how to compile a smart contract with CMake. +This guide provides instructions to compile a smart contract with CMake. ## Before you begin diff --git a/docs/06_how-to-guides/20_authorization/how_to_restrict_access_to_an_action_by_user.md b/docs/06_how-to-guides/20_authorization/how_to_restrict_access_to_an_action_by_user.md index dc509c2aaa..e26e72f46d 100644 --- a/docs/06_how-to-guides/20_authorization/how_to_restrict_access_to_an_action_by_user.md +++ b/docs/06_how-to-guides/20_authorization/how_to_restrict_access_to_an_action_by_user.md @@ -16,10 +16,10 @@ This guide provides instructions how to perform authorization checks in a smart See the following code reference guides for functions which can be used to implement authorization checks in a smart contract: -* function [has_auth(name n)](https://developers.eos.io/manuals/eosio.cdt/v1.8/namespaceeosio#function-has_auth) -* function [require_auth(name n)](https://developers.eos.io/manuals/eosio.cdt/v1.8/namespaceeosio/#function-require_auth-12) +* function [has_auth(name n)](https://developers.eos.io/manuals/eosio.cdt/latest/namespaceeosio#function-has_auth) +* function [require_auth(name n)](https://developers.eos.io/manuals/eosio.cdt/latest/namespaceeosio/#function-require_auth-12) * function [require_auth2(capi_name name, capi_name permission)](https://developers.eos.io/manuals/eosio.cdt/v1.8/group__action__c#function-require_auth2) -* function [check(bool pred, ...)](https://developers.eos.io/manuals/eosio.cdt/v1.8/group__system/#function-check) +* function [check(bool pred, ...)](https://developers.eos.io/manuals/eosio.cdt/latest/group__system/#function-check) ## Procedure diff --git a/docs/06_how-to-guides/30_key-value-api/index.md b/docs/06_how-to-guides/30_key-value-api/index.md index ad16996214..561c0bf465 100644 --- a/docs/06_how-to-guides/30_key-value-api/index.md +++ b/docs/06_how-to-guides/30_key-value-api/index.md @@ -12,7 +12,7 @@ The Key-Value API provides a set of C++ classes and structures which facilitates ## Concept -The Key-Value API, or KV API, is a new option for developers to create datastore key value tables on-chain. KV API is more flexible than multi-index and allows developers to search the table in a more human-readable way, unlike multi-index tables where search is over 64-bit values. +The Key-Value API, or KV API, is a new option for developers to create datastore key value tables on-chain. KV API is more flexible than multi-index and allows developers to search for the table in a more human-readable way, unlike multi-index tables where search is over 64-bit values. On top of flexibility, this new API has a simpler interface and it helps the developer to avoid complex C++ templates constructs. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_map/10_how-to-use-kv-map.md b/docs/06_how-to-guides/30_key-value-api/kv_map/10_how-to-use-kv-map.md index 8a88547d92..3b062c5e4c 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_map/10_how-to-use-kv-map.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_map/10_how-to-use-kv-map.md @@ -19,7 +19,7 @@ To accomplish this task do the following: See the following code reference: -* The [`kv::map`](https://developers.eos.io/manuals/eosio.cdt/v1.8/classeosio_1_1kv_1_1map) class. +* The [`kv::map`](https://developers.eos.io/manuals/eosio.cdt/latest/classeosio_1_1kv_1_1map) class. ## Before you begin @@ -27,7 +27,7 @@ Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) * A smart contract named `smrtcontract` -* A user defined type named `person`, which defines the data stored in the map +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` Refer to the following reference implementation for your starting point: @@ -83,6 +83,4 @@ In conclusion, the above instructions show how to define and use a `Key-Value Ma ## Next Steps -The following option is available when you complete the procedure: - * You [can add values](30_how-to-upsert-into-kv-map.md) in the map object created. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_map/30_how-to-upsert-into-kv-map.md b/docs/06_how-to-guides/30_key-value-api/kv_map/30_how-to-upsert-into-kv-map.md index 87447effd0..d041e22c90 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_map/30_how-to-upsert-into-kv-map.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_map/30_how-to-upsert-into-kv-map.md @@ -13,7 +13,7 @@ Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) * A smart contract named `smrtcontract` -* A user defined type named `person`, which defines the data stored in the map +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` * A `kv map` object, name `my_map`, which stores objects of type `person`, with unique keys of type `int` Refer to the following reference implementation for your starting point: @@ -111,7 +111,5 @@ In conclusion, the above instructions show how to upsert into `Key-Value Map` (` ## Next Steps -The following options are available when you complete the procedure: - * [Verify](70_how-to-find-in-kv-map.md) if the newly inserted `person` actually exists in the map. To accomplish this task use the `find()` function of the `kv_map`. * [Delete](40_how-to-delete-from-kv-map.md) the newly created or updated `person` from the map. To accomplish this task, use the `erase()` function of the `kv map`. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_map/40_how-to-delete-from-kv-map.md b/docs/06_how-to-guides/30_key-value-api/kv_map/40_how-to-delete-from-kv-map.md index aeb31cc58a..5c8faf29a9 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_map/40_how-to-delete-from-kv-map.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_map/40_how-to-delete-from-kv-map.md @@ -13,7 +13,7 @@ Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) * A smart contract named `smrtcontract` -* A user defined type named `person`, which defines the data stored in the map +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` * A `kv map` object, name `my_map`, which stores objects of type `person`, with unique keys of type `int` Refer to the following reference implementation for your starting point: diff --git a/docs/06_how-to-guides/30_key-value-api/kv_map/50_how-to-iterate-kv-map.md b/docs/06_how-to-guides/30_key-value-api/kv_map/50_how-to-iterate-kv-map.md index 48a6307da9..2b1a5b5284 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_map/50_how-to-iterate-kv-map.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_map/50_how-to-iterate-kv-map.md @@ -13,7 +13,7 @@ Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) * A smart contract named `smrtcontract` -* A user defined type named `person`, which defines the data stored in the map +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` * A `kv map` object, name `my_map`, which stores objects of type `person`, with unique keys of type `int`. Refer to the following reference implementation for your starting point: diff --git a/docs/06_how-to-guides/30_key-value-api/kv_map/70_how-to-find-in-kv-map.md b/docs/06_how-to-guides/30_key-value-api/kv_map/70_how-to-find-in-kv-map.md index ad3ffc4cd6..2ecec70ad1 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_map/70_how-to-find-in-kv-map.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_map/70_how-to-find-in-kv-map.md @@ -13,7 +13,7 @@ Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) * A smart contract named `smrtcontract` -* A user defined type named `person`, which defines the data stored in the map +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` * A `kv map` object, name `my_map`, which stores objects of type `person`, with unique keys of type `int`. Refer to the following reference implementation for your starting point: @@ -106,7 +106,5 @@ In conclusion, the above instructions show how to find an object in `Key-Value M ## Next Steps -The following options are available when you complete the procedure: - * [Update](30_how-to-upsert-into-kv-map.md) the `person` found. * [Delete](40_how-to-delete-from-kv-map.md) the `person` found. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_map/90_how-to-allow-users-to-pay-kv-map.md b/docs/06_how-to-guides/30_key-value-api/kv_map/90_how-to-allow-users-to-pay-kv-map.md index ba5943fa1e..d499ef1d56 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_map/90_how-to-allow-users-to-pay-kv-map.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_map/90_how-to-allow-users-to-pay-kv-map.md @@ -11,7 +11,7 @@ Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) * A smart contract named `smrtcontract` -* A user defined type named `person`, which defines the data stored in the map +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` * A `kv map` object, name `my_map`, which stores objects of type `person`, with unique keys of type `int`. Refer to the following reference implementation for your starting point: @@ -108,7 +108,5 @@ In conclusion, the above instructions show how to require the user to pay for th ## Next Steps -The following options are available when you complete the procedure: - * [Verify](70_how-to-find-in-kv-map.md) if the newly inserted `person` actually exists in the map. To accomplish this task use the `find()` function of the `kv_map`. * [Delete](40_how-to-delete-from-kv-map.md) the newly created or updated `person` from the map. To accomplish this task, use the `erase()` function of the `kv map`. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_table/10_how-to-use-kv-table.md b/docs/06_how-to-guides/30_key-value-api/kv_table/10_how-to-use-kv-table.md index 60516007aa..adf20acfca 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_table/10_how-to-use-kv-table.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_table/10_how-to-use-kv-table.md @@ -17,8 +17,8 @@ To accomplish this task, define the user type which will be stored in the `kv ta Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index). -* A smart contract named `smrtcontract`. -* A user defined type named `person`, which defines the data stored in the table. +* A smart contract named `smrtcontract` +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` Refer to the following reference implementation for your starting point: @@ -48,7 +48,7 @@ Complete the following steps to define the `address_table` type, based on the `e 1. Define the structure or class `address_table` in the scope of your smart contract class, for the abi generation to find it and place it into the abi file. 2. Derive `address_table` from `eosio::`kv::table` class template. Pass the `person` user defined type as the type parameter for `eosio::`kv::table` base class and the name of the `kv table`, let’s say `kvaddrbook`. 3. Annotate `address_table` type with `[[eosio::table]]`, and make sure it is placed after the `struct` keyword but before the name of the type. -4. Define a primary index `first_name_idx` based on the property `person::first_name`. Every `kv table` requires a primary index to be defined based on a property that stores unique values. +4. Define a primary index `first_name_idx` based on the data member `person::first_name`. Every `kv table` requires a primary index to be defined on a data member that stores unique values. 5. In the `address_table` constructor, call the `init(...)` base class method with the two parameters: 1. The first parameter, of type `eosio::name`, is the contract that owns the table. 2. The second parameter is the `account_name_uidx` primary index. @@ -80,8 +80,6 @@ In conclusion, the above instructions show how to define and use a `Key-Value Ta ## Next Steps -The following options are available when you complete the procedure: - * You can [create one or more unique indexes](20_how-to-create-indexes-kv-table.md) using the `KV_NAMED_INDEX` macro or the `eosio::kv::table::index` template class. * You can [create one or more non-unique indexes](20_how-to-create-indexes-kv-table.md) using the `KV_NAMED_INDEX` macro or the `eosio::kv::table::index` template class. * You can access the defined `kv table` and perform operations on it and its defined indexes: diff --git a/docs/06_how-to-guides/30_key-value-api/kv_table/20_how-to-create-indexes-kv-table.md b/docs/06_how-to-guides/30_key-value-api/kv_table/20_how-to-create-indexes-kv-table.md index 4e5867bcf8..9b40bf2216 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_table/20_how-to-create-indexes-kv-table.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_table/20_how-to-create-indexes-kv-table.md @@ -23,9 +23,9 @@ Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) * A smart contract named `smrtcontract` -* A user defined type which defines the data stored in the table, named `person` -* A `kv table` type which stores objects of type `person`, named `address_table` -* Each `person` object has the following properties: +* A user defined data type, `struct` or `class`, which defines the data stored in the table, named `person` +* A `kv table` data type, `struct` or `class`, which inherits `eosio::kv::table`, and stores objects of type `person`, named `address_table` +* Each `person` instance has the following data members: * `account_name`, * `first_name`, * `last_name`, @@ -54,14 +54,15 @@ class [[eosio::contract]] smrtcontract : public contract { ## Procedure -### Define a unique index on property account_name using the macro KV_NAMED_INDEX +### Define a unique index on the data member account_name using the macro KV_NAMED_INDEX 1. Use the `KV_NAMED_INDEX` macro with two parameters. -2. Pass the name of the index as the first parameter. The parameter must be a qualified `eosio::name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name). -3. Pass the name of the property for which the index is defined as the second parameter. -4. Call `init()` of the base class in the constructor of `address_table` type and pass the contract name as the first parameter and `account_name` index defined previously, by the KV_NAMED_INDEX macro, as the second parameter. +2. The first parameter is the name of the index and must be a qualified `eosio::name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name). +3. The second parameter is the name of the data member the index is defined for. +4. Define the constructor of the `address_table` structure. It must have as input parameter the name of the contract, `contract_name`, which owns this table. +5. In the constructor of `address_table` structure call the base class `kv::table::init(...)` method. Pass the `contract_name` as the first parameter and the `account_name` as the second parameter. The `account_name` is the data member for which the index was defined for using the `KV_NAMED_INDEX` macro. -Refer to the following reference implementation of a unique index on property `account_name` using macro `KV_NAMED_INDEX`: +Refer to the following reference implementation of a unique index on `account_name` data member using macro `KV_NAMED_INDEX`: `smartcontract.hpp file` @@ -80,14 +81,15 @@ class [[eosio::contract]] smrtcontract : public contract { }; ``` -### Define a unique index on property personal_id using the eosio::kv::table::index template class +### Define a unique index on the data member personal_id using the eosio::kv::table::index template class 1. Use the `eosio::kv::table::index` template class with two parameters. -2. Pass the name of the index as the first parameter. The parameter must be a qualified `eosio:name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name). -3. Pass the reference to the property for which the index is defined, `&person::personal_id`, as the second parameter. -4. Call `init()` of the base class in the constructor of `address_table` type and pass the contract name as the first parameter and the `personal_id_idx` index defined previously as the second parameter. +2. The first parameter is the name of the index and must be a qualified `eosio:name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name). +3. The second parameter is the name of the data member the index is defined for. +4. Define the constructor of the `address_table` structure. It must have as input parameter the name of the contract, `contract_name`, which owns this table. +5. In the constructor of `address_table` structure call the base class `kv::table::init(...)` method. Pass the `contract_name` as the first parameter and the `personal_id_idx` as the second parameter. The `personal_id_idx` is the index defined previously. -Refer to the following reference implementation of a unique index on property `personal_id` using `eosio::kv::table::index` template class: +Refer to the following reference implementation of a unique index on `personal_id` data member using `eosio::kv::table::index` template class: `smartcontract.hpp file` @@ -108,16 +110,15 @@ class [[eosio::contract]] smrtcontract : public contract { }; ``` -### Define a non-unique index on property first_name using the macro KV_NAMED_INDEX +### Define a non-unique index on the data member first_name using the macro KV_NAMED_INDEX 1. Use the `KV_NAMED_INDEX` with two parameters. -2. Pass the name of the index as the first parameter. The parameter must be a qualified `eosio::name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name). -3. Pass the name of the property for which the index is defined as the second parameter. -4. Call `init()` of the base class in the constructor of `address_table` type and pass the contract name as the first parameter and `first_name` index defined previously, by the KV_NAMED_INDEX macro, as the second parameter. +2. The first parameter is the name of the index and must be a qualified `eosio::name`. See documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name). +3. The second parameter is the name of the data member the index is defined for. The type of this data member must be `std::tuple<>`. The first parameter of the tuple must be the type of the data member indexed non-uniquely, in our case the type `std::string` is used because `first_name` is the data member indexed non-uniquely. The last parameter of the tuple type must be the type of a data member name which is unique. In our case the type `eosio::name` is used because data member `account_name` is unique. Multiple properties can be indexed non-uniquely as well. In this case the first parameter types correspond to the multiple properties being indexed. As previously already mentioned, the last parameter correspond to the type of a data member name which is unique. +4. Define the constructor of the `address_table` structure. It must have as input parameter the name of the contract, `contract_name`, which owns this table. +5. In the constructor of `address_table` structure call the base class `kv::table::init(...)` method. Pass the `contract_name` as the first parameter and the `first_name` as the second parameter. The `first_name` is the data member for which the index was defined for using the `KV_NAMED_INDEX` macro. -The property used for the second parameter must be of template type `std::tuple`. The first parameter must be the type of the property indexed non-uniquely, in our case the type `std::string` is used because `first_name` is the property indexed non-uniquely. And the last parameter of the tuple type must be the type of a property name which is unique. In our case the type `eosio::name` is used because property `account_name` is unique. Multiple properties can be indexed non-uniquely as well. In this case the first parameter types correspond to the properties being indexed. And, as previously already mentioned, the last parameter correspond to the type of a property name which is unique. - -Refer to the following reference implementation of a non-unique index on property `account_name` using macro `KV_NAMED_INDEX`: +Refer to the following reference implementation of a non-unique index on `account_name` data member using macro `KV_NAMED_INDEX`: `smartcontract.hpp file` @@ -143,17 +144,15 @@ class [[eosio::contract]] smrtcontract : public contract { }; ``` -### Define a non-unique index on property last_name using the eosio::kv::table::index template class - -1. Use the `eosio::kv::table::index` template class. -2. Pass as the first parameter the name of the index. It must be a qualified `eosio:name`, see documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name). -3. Pass the reference to the property for which the index is defined, `&person::last_name`, as the second parameter. -4. Call `init()` of the base class in the constructor of `address_table` type and pass the contract name as the first parameter and the `last_name_idx` index defined previously as the second parameter. - +### Define a non-unique index on the data member last_name using the eosio::kv::table::index template class -The property used for the second parameter must be of template type `std::tuple`. The first parameter must be the type of the property indexed non-uniquely, in our case the type `std::string` is used because `first_name` is the property indexed non-uniquely. And the last parameter of the tuple type must be the type of a property name which is unique. In our case the type `eosio::name` is used because property `account_name` is unique. Multiple properties can be indexed non-uniquely as well. In this case the first parameter types correspond to the properties being indexed. And, as previously already mentioned, the last parameter correspond to the type of a property name which is unique. +1. Use the `eosio::kv::table::index` template class with two parameters. +2. The first parameter is the name of the index and must be a qualified `eosio:name`, see documentation for the [eosio name restrictions](https://developers.eos.io/welcome/latest/glossary/index#account-name). +3. The second parameter is the name of the data member the index is defined for. The data member used for the second parameter must be of template type `std::tuple`. The first parameter of the tuple must be the type of the data member indexed non-uniquely. In our case the type `std::string` is used because `first_name` is the data member indexed non-uniquely. The last parameter of the tuple must be the type of a data member name which is unique. In our case the type `eosio::name` is used because data member `account_name` is unique. Multiple properties can be indexed non-uniquely as well. In this case the first parameter types correspond to the multiple properties being indexed. As previously already mentioned, the last parameter correspond to the type of a data member name which is unique. +4. Define the constructor of the `address_table` structure. It must have as input parameter the name of the contract, `contract_name`, which owns this table. +5. In the constructor of `address_table` structure call the base class `kv::table::init(...)` method. Pass the `contract_name` as the first parameter and the `last_name_idx` as the second parameter. The `last_name_idx` is the index defined previously. -Refer to the following reference implementation of a non-unique index on property `last_name` using `eosio::kv::table::index` template class: +Refer to the following reference implementation of a non-unique index `last_name` data member using `eosio::kv::table::index` template class: `smartcontract.hpp file` @@ -170,7 +169,8 @@ class [[eosio::contract]] smrtcontract : public contract { index> last_name_idx { name{"persid"_n}, - &person::last_name}; + &person::last_name + }; address_table(eosio::name contract_name) { init(contract_name, last_name_idx) @@ -187,7 +187,5 @@ In conclusion, the above instructions show how to create indexes on a `Key-Value ## Next Steps -The following options are available when you complete the procedure: - * [Search](70_how-to-find-in-kv-table.md) by index key for values or range of values in the defined `kv table`. * [Check](60_how-to-check-a-record-kv-table.md) if a particular key exists in an index. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_table/30_how-to-upsert-into-kv-table.md b/docs/06_how-to-guides/30_key-value-api/kv_table/30_how-to-upsert-into-kv-table.md index f9cda5a238..9a7a5ffdae 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_table/30_how-to-upsert-into-kv-table.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_table/30_how-to-upsert-into-kv-table.md @@ -18,8 +18,9 @@ Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) * A smart contract named `smrtcontract` -* A user defined type named `person`, which defines the data stored in the table -* A `kv table` type which stores objects of type `person`, named `address_table`. The primary index of the `kv table` is defined based on the `person::account_name` property. +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` +* A `kv table` data type, `struct` or `class`, which inherits `eosio::kv::table`, and stores objects of type `person`, named `address_table` +* A primary index is defined for the `kv table` for the `person::account_name` data member Refer to the following reference implementation for your starting point: @@ -55,7 +56,7 @@ Complete the following steps to insert a new `person` object, and then update it 1. Create a new action `upsert` in your smart contact class, which takes as input parameters an account name, a first name, a last name and a personal id. 2. In the `upsert` action access the instance of `address_table` by declaring a local variable of `address_table` type. -3. And then call the `put` method of the `address_table` and pass to it a newly created `person` object based on the action’s input parameters. +3. Call the `put` method of the `address_table` and pass to it a newly created `person` object based on the action’s input parameters. Refer to the following reference implementation to insert a new `person` object, and then update it, in the `kv table`: @@ -111,8 +112,6 @@ In conclusion, the above instructions show how to upsert into `Key-Value Table` ## Next Steps -The following options are available when you complete the procedure: - * [Check](60_how-to-check-a-record-kv-table.md) if the newly inserted `person` actually exists in the table. To accomplish this task, use the `exists()` function of any index defined for the table. * [Retrieve](70_how-to-find-in-kv-table.md) the newly inserted or updated `person` from the table. To accomplish this task, use the `find()` function of any index defined for the table. * [Delete](40_how-to-delete-from-kv-table.md) the newly created or updated `person` from the table. To accomplish this task, use the `erase()` function of the `kv table`. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_table/40_how-to-delete-from-kv-table.md b/docs/06_how-to-guides/30_key-value-api/kv_table/40_how-to-delete-from-kv-table.md index 37f9e37f58..e8da1e0252 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_table/40_how-to-delete-from-kv-table.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_table/40_how-to-delete-from-kv-table.md @@ -18,8 +18,9 @@ Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) * A smart contract named `smrtcontract` -* A user defined type named `person`, which defines the data stored in the table -* A `kv table` type which stores objects of type `person`, named `address_table`. The primary index of the `kv table` is defined based on the `person::account_name` property. +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` +* A `kv table` data type, `struct` or `class`, which inherits `eosio::kv::table`, and stores objects of type `person`, named `address_table` +* A primary index is defined for the `kv table` for the `person::account_name` data member Refer to the following reference implementation for your starting point: @@ -102,6 +103,5 @@ In conclusion, the above instructions show how to delete an object from a `Key-V ## Next Steps -The following options are available when you complete the procedure: * [Check](60_how-to-check-a-record-kv-table.md) if the newly inserted `person` was actually deleted from the table. To accomplish this task, use the `exists()` function of any index defined for the table. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_table/50_how-to-iterate-kv-table.md b/docs/06_how-to-guides/30_key-value-api/kv_table/50_how-to-iterate-kv-table.md index 0e4575def4..17f9c64294 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_table/50_how-to-iterate-kv-table.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_table/50_how-to-iterate-kv-table.md @@ -16,16 +16,16 @@ Use the `iterator` defined by the `eosio::kv::table::index` class to accomplish Make sure you have the following prerequisites in place: -* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index). -* A smart contract named `smrtcontract`. -* A user defined type which defines the data stored in the table, named `person`. -* A `kv table` type which stores objects of type `person`, named `address_table`. -* Each `person` object has the following properties: +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) +* A smart contract named `smrtcontract` +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` +* A `kv table` data type, `struct` or `class`, which inherits `eosio::kv::table`, and stores objects of type `person`, named `address_table` +* Each `person` object has the following data members: * `account_name`, * `first_name`, * `last_name`, * `personal_id`. -* A unique index, named `account_name_uidx`, defined on the `account_name` property.. +* A unique index, named `account_name_uidx`, defined on the `account_name` data member Refer to the following reference implementation for your starting point: @@ -63,7 +63,7 @@ Complete the following steps to implement an action which iterates through the f 2. In the `iterate` action access the instance of `address_table` by declaring a local variable of `address_table` type. 3. Capture the `begin` and the `end` of the `account_name_uidx` index defined. 4. Use the iterator `value` to access the current value of the iterator. -5. And then increment the iterator until the first N `person` objects stored in `address_table` are visited. +5. Increment the iterator until the first N `person` objects stored in `address_table` are visited. Refer to the following reference implementation to implement an action which iterates through the first N `person` objects in `address_table` and prints their first and last names: diff --git a/docs/06_how-to-guides/30_key-value-api/kv_table/60_how-to-check-a-record-kv-table.md b/docs/06_how-to-guides/30_key-value-api/kv_table/60_how-to-check-a-record-kv-table.md index 58da59df1a..72b2cf8c22 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_table/60_how-to-check-a-record-kv-table.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_table/60_how-to-check-a-record-kv-table.md @@ -16,16 +16,16 @@ Use the method `exists` defined by the `eosio::kv::table::index` class to accom Make sure you have the following prerequisites in place: -* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index). -* A smart contract named `smrtcontract`. -* A user defined type which defines the data stored in the table, named `person`. -* A `kv table` type which stores objects of type `person`, named `address_table`. -* Each `person` object has the following properties: +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) +* A smart contract named `smrtcontract` +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` +* A `kv table` data type, `struct` or `class`, which inherits `eosio::kv::table`, and stores objects of type `person`, named `address_table` +* Each `person` object has the following data members: * `account_name`, * `first_name`, * `last_name`, * `personal_id`. -* A unique index, named `account_name_uidx`, defined on the `account_name` property.. +* A unique index, named `account_name_uidx`, defined on the `account_name` data member Refer to the following reference implementation for your starting point: @@ -108,6 +108,4 @@ In conclusion, the above instructions show how to check if a specific object exi ## Next Steps -The following options are available when you complete the procedure: - * Implement business logic and rely on the information that the `person` object exists in the table. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_table/70_how-to-find-in-kv-table.md b/docs/06_how-to-guides/30_key-value-api/kv_table/70_how-to-find-in-kv-table.md index d365c73cb8..b55bf8127a 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_table/70_how-to-find-in-kv-table.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_table/70_how-to-find-in-kv-table.md @@ -16,16 +16,16 @@ Use the method `find()` defined by the `eosio::kv::table::index` class to accom Make sure you have the following prerequisites in place: -* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index). -* A smart contract named `smrtcontract`. -* A user defined type which defines the data stored in the table, named `person`. -* A `kv table` type which stores objects of type `person`, named `address_table`. -* Each `person` object has the following properties: +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) +* A smart contract named `smrtcontract` +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` +* A `kv table` data type, `struct` or `class`, which inherits `eosio::kv::table`, and stores objects of type `person`, named `address_table` +* Each `person` object has the following data members: * `account_name`, * `first_name`, * `last_name`, * `personal_id`. -* A unique index, named `account_name_uidx`, defined on the `account_name` property.. +* A unique index, named `account_name_uidx`, defined on the `account_name` data member Refer to the following reference implementation for your starting point: @@ -125,7 +125,5 @@ In conclusion, the above instructions show how to find a specific object in a `K ## Next Steps -The following options are available when you complete the procedure: - * [Update](30_how-to-upsert-into-kv-table.md) the `person` found and returned. * [Delete](40_how-to-delete-from-kv-table.md) the `person` found and returned. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_table/80_how-to-query-range-in-kv-table.md b/docs/06_how-to-guides/30_key-value-api/kv_table/80_how-to-query-range-in-kv-table.md index d7f632b276..df03e7553c 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_table/80_how-to-query-range-in-kv-table.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_table/80_how-to-query-range-in-kv-table.md @@ -16,17 +16,17 @@ Use the method `range` defined by the `eosio::kv::table::index` class to accompl Make sure you have the following prerequisites in place: -* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index). -* A smart contract named `smrtcontract`. -* A user defined type which defines the data stored in the table, named `person`. -* A `kv table` type which stores objects of type `person`, named `address_table`. -* Each `person` object has the following properties: +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) +* A smart contract named `smrtcontract` +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` +* A `kv table` data type, `struct` or `class`, which inherits `eosio::kv::table`, and stores objects of type `person`, named `address_table` +* Each `person` object has the following data members: * `account_name`, * `first_name`, * `last_name`, * `personal_id`. -* A unique index, named `account_name_uidx`, defined on the `account_name` property.. -* A non-unique index defined on the `last_name` property, named `last_name_idx`. +* A unique index, named `account_name_uidx`, defined on the `account_name` data member +* A non-unique index defined on the `last_name` data member, named `last_name_idx` Refer to the following reference implementation for your starting point: @@ -125,6 +125,4 @@ In conclusion, the above instructions show how to retrieve a list of values, fro ## Next Steps -The following options are available when you complete the procedure: - * Access the list of objects returned by the `getbylastname` action. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_table/90_how-to-allow-users-to-pay-kv-table.md b/docs/06_how-to-guides/30_key-value-api/kv_table/90_how-to-allow-users-to-pay-kv-table.md index bfe8d133f6..1fe391e2c9 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_table/90_how-to-allow-users-to-pay-kv-table.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_table/90_how-to-allow-users-to-pay-kv-table.md @@ -18,8 +18,9 @@ Make sure you have the following prerequisites in place: * An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index) * A smart contract named `smrtcontract` -* A user defined type named `person`, which defines the data stored in the table -* A `kv table` type which stores objects of type `person`, named `address_table`. The primary index of the `kv table` is defined based on the `person::account_name` property. +* A user defined type, `struct` or `class`, which defines the data stored in the map, named `person` +* A `kv table` data type, `struct` or `class`, which inherits `eosio::kv::table`, and stores objects of type `person`, named `address_table` +* A primary index is defined for the `kv table` for the `person::account_name` data member Refer to the following reference implementation for your starting point: @@ -55,7 +56,7 @@ Complete the following steps to allow the `payer` account, to be the payer for t 1. Create a new action `upsert` in your smart contact class, which takes as input parameters an `account name, a first name, a last name, a personal id` which define a person data, and an `account name` for the payer. 2. In the `upsert` action access the instance of `address_table` belonging to this contract by declaring a local variable of `address_table` type and pass the contract name as paramter. -3. And then call the `put` method of the `address_table` and pass to it a newly created `person` object based on the action’s input parameters and the payer account name. +3. Call the `put` method of the `address_table` and pass to it a newly created `person` object based on the action’s input parameters and the payer account name. Refer to the following reference implementation to allow a specific account name to be the payer for the resources needed to store a person object in the `kv table`: @@ -113,8 +114,6 @@ In conclusion, the above instructions show how to create an action which require ## Next Steps -The following options are available when you complete the procedure: - * [Check](60_how-to-check-a-record-kv-table.md) if the newly inserted `person` actually exists in the table. To accomplish this task, use the `exists()` function of any index defined for the table. * [Retrieve](70_how-to-find-in-kv-table.md) the newly inserted or updated `person` from the table. To accomplish this task, use the `find()` function of any index defined for the table. * [Delete](40_how-to-delete-from-kv-table.md) the newly created or updated `person` from the table. To accomplish this task, use the `erase()` function of the `kv table`. diff --git a/docs/06_how-to-guides/30_key-value-api/kv_table/index.md b/docs/06_how-to-guides/30_key-value-api/kv_table/index.md index 2c8dfcea36..58f41fd334 100644 --- a/docs/06_how-to-guides/30_key-value-api/kv_table/index.md +++ b/docs/06_how-to-guides/30_key-value-api/kv_table/index.md @@ -5,12 +5,12 @@ link_text: "Key-Value Table" #### KV Table -A `datastore key value table on-chain`, or a `KV Table`, serves as a storage location which is organized as a table of rows where each row stores objects with the same definition. +A `datastore key value table on-chain`, or a `KV Table`, serves as a storage location which is organized as a table of rows and columns where each row stores objects with the same definition. [[caution | Alpha version]] | `Key-Value Table` is designated as `alpha` and should not be used in production code. -The `object definition` consists of a list of `properties` and each property is stored on the corresponding row in the table. The properties of the objects are also referred to as `fields`. +The `object definition` consists of a list of `data members`. One object is stored on one row in the table, which each `data member` stored in one column. The data members of the objects are also referred to as `fields`. A `KV Table` requires one unique index, of any type, that can be serialized to a binary representation. @@ -18,12 +18,12 @@ A `KV Table` supports zero or more secondary indexes, of any type, that can be s Two types of indexes can be defined, unique or non-unique. -A unique index can be defined just for one property, and it will sort the objects stored in the `KV Table` based on the `specified property`. The unique index also ensures only one instance of an object is stored with a particular value for the `specified property`, and thus ensures the uniqueness of the property for which it is defined. +A unique index can be defined just for one data member, and it will sort the objects stored in the `KV Table` based on the `specified data member`. The unique index also ensures only one instance of an object is stored with a particular value for the `specified data member`, and thus ensures the uniqueness of the data member for which it is defined. -A non-unique index can be defined for one or multiple properties, and it will sort the objects stored in the `KV Table` based on the `specified property` or the combination of the `specified properties`. Very important though, a non-unique index requires as the last property of its definition a property which has unique values. Therefore although the non-unique index is intended for just one property, its definition will have two properties specified: +A non-unique index can be defined for one or multiple data members, and it will sort the objects stored in the `KV Table` based on the `specified data member` or the combination of the `specified data members`. Very important though, a non-unique index requires as the last data member of its definition a data member which has unique values. Therefore although the non-unique index is intended for just one data member, its definition will have two properties specified: -1. the first property, the one for which the non-unique index is built, -2. and the last property which must have unique values. +1. the first data member, the one for which the non-unique index is built, +2. and the last data member which must have unique values. The main operations provided by the KV API are the following: diff --git a/docs/06_how-to-guides/40_multi-index/how-to-define-a-primary-index.md b/docs/06_how-to-guides/40_multi-index/how-to-define-a-primary-index.md index 7bf18d8491..cc84cbbc05 100644 --- a/docs/06_how-to-guides/40_multi-index/how-to-define-a-primary-index.md +++ b/docs/06_how-to-guides/40_multi-index/how-to-define-a-primary-index.md @@ -2,76 +2,127 @@ content_title: How to define a primary index --- -A primary key is required when defining a multi index table structure. See the following example: - -1. Include the `eosio.hpp` header and declare the `eosio` namespace usage -``` -#include -using namespace eosio; -``` -2. Define the data structure for the multi index table -```cpp - struct [[eosio::table]] test_table { - }; -``` -3. Add to the data structure the fields which define the multi index table -```diff - // the data structure which defines each row of the table - struct [[eosio::table]] test_table { -+ // this field stores a name for each row of the multi index table -+ name test_primary; -+ // additional data stored in table row, e.g. an uint64_t type data -+ uint64_t datum; - }; -``` -4. Add the definition of the primary index for the multi index table. The primary index type must be uint64_t and must be unique -```diff - // the data structure which defines each row of the table - struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table - name test_primary; - // additional data stored in table row - uint64_t datum; -+ // mandatory definition for primary key getter -+ uint64_t primary_key( ) const { return test_primary.value; } - }; -``` +## Overview + +This guide provides instructions to define a primary index for a multi-index table. + +## Reference + +See the following code reference: + +* The [`multi-index`](../../classeosio_1_1multi__index) class. + +## Before you begin + +Make sure you have the following prerequisites in place: + +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index), + +## Procedure + +Complete the following steps to define a primary index for the multi-index table `testtab`. + +### 1. Preparation And Initialization + +Include the `eosio.hpp` header and use the `using` directive to access the `eosio` namespace. + + ```cpp + #include + using namespace eosio; + ``` + +### 2. Define The Table Data Structure + +Define the data structure for the multi-index table. + + ```cpp + struct [[eosio::table]] test_table { + }; + ``` + +Add the data structure data members. Each data member corresponds to a field of the multi-index table. A primary key is required when defining a multi-index table structure, therefore you need to know which is the multi-index table field that is the primary key for your multi-index table. The corresponding data member for the primary key field must store unique values. In this case it is the `test_primary` data member of type `eosio::name`. + + ```diff + // the data structure which defines each row of the table + struct [[eosio::table]] test_table { + + // this data member stores a name for each row of the multi-index table + + name test_primary; + + // additional data stored in table row, e.g. an uint64_t type data + + uint64_t datum; + }; + ``` + +### 3. Define The Primary Index + +Add the definition of the primary index for the multi-index table. The primary index type must be uint64_t, it must be unique and must be named `primary_key()`, otherwise the compiler (eosio-cpp) will generate an error saying it can not find the field to use as the primary key: + + ```diff + // the data structure which defines each row of the table + struct [[eosio::table]] test_table { + // this data member stores a name for each row of the multi-index table + name test_primary; + // additional data stored in table row + uint64_t datum; + + // mandatory definition for primary key getter + + uint64_t primary_key( ) const { return test_primary.value; } + }; + ``` [[info | Secondary indexes information]] -| Other, secondary, indexes if they will be defined can have duplicates. You can have up to 16 additional indexes and the field types can be uint64_t, uint128_t, uint256_t, double or long double. - -5. For ease of use, define a type alias `test_tables` based on the `eosio::multi_index` template type, parametarized with a random name `"testtaba"` and the `test_table` data structure defined above -```diff - // the data structure which defines each row of the table - struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table - name test_primary; - // additional data stored in table row - uint64_t datum; - // mandatory definition for primary key getter - uint64_t primary_key( ) const { return test_primary.value; } - }; - -+ typedef eosio::multi_index<"testtaba"_n, test_table> test_tables; -``` - -Declare the multi index table as a data member of type `test_tables`, as defined above. -```diff - // the data structure which defines each row of the table - struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table - name test_primary; - // additional data stored in table row - uint64_t datum; - // mandatory definition for primary key getter - uint64_t primary_key( ) const { return test_primary.value; } - }; - - typedef eosio::multi_index<"testtaba"_n, test_table> test_tables; -+ test_tables testtab; -``` - -Now you have instantiated the `testtab` as a multi index table which has a primary index defined for its `test_primary` data member. +| Secondary indexes may be defined which are not unique. There can be up to 16 secondary indexes. Secondary indices support the following types: +* uint64_t +* uint128_t +* uint256_t +* double +* long double + +### 4. Define A Multi-Index Type Alias + +For ease of use, define a type alias `test_table_t` based on the `eosio::multi_index` template type, parametarized with a random name `"testtaba"` and the `test_table` data structure. The names must adhere to `EOSIO` account name restrictions. + + ```diff + // the data structure which defines each row of the table + struct [[eosio::table]] test_table { + // this data member stores a name for each row of the multi-index table + name test_primary; + // additional data stored in table row + uint64_t datum; + // mandatory definition for primary key getter + uint64_t primary_key( ) const { return test_primary.value; } + }; + + + typedef eosio::multi_index<"testtaba"_n, test_table> test_table_t; + ``` + +### 5. Instantiate The Multi-Index Table + +Declare the `testtab` multi-index table as a data member of type `test_table_t`. + + ```diff + // the data structure which defines each row of the table + struct [[eosio::table]] test_table { + // this data member stores a name for each row of the multi-index table + name test_primary; + // additional data stored in table row + uint64_t datum; + // mandatory definition for primary key getter + uint64_t primary_key( ) const { return test_primary.value; } + }; + + typedef eosio::multi_index<"testtaba"_n, test_table> test_table_t; + + test_table_t testtab; + ``` + +Now you have instantiated a multi-index table, and assigned to `testtab` variable, which has a primary index defined for its `test_primary` data member. [[info | Full example location]] -| A full example project demonstrating the instantiation and usage of multi index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). \ No newline at end of file +| A full example project demonstrating the instantiation and usage of multi-index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). + +## Summary + +In conclusion, the above instructions show how to define a primary index for a multi-index table. + +## Next Steps + +* You can [insert data in the multi-index table](./how-to-insert-data-into-a-multi-index-table). +* You can [iterate and retrieve data](./how-to-iterate-and-retrieve-a-multi_index-table) from the multi-index table. diff --git a/docs/06_how-to-guides/40_multi-index/how-to-define-a-secondary-index.md b/docs/06_how-to-guides/40_multi-index/how-to-define-a-secondary-index.md index c6a36082d5..68dea63de0 100644 --- a/docs/06_how-to-guides/40_multi-index/how-to-define-a-secondary-index.md +++ b/docs/06_how-to-guides/40_multi-index/how-to-define-a-secondary-index.md @@ -2,91 +2,130 @@ content_title: How to define a secondary index --- -## Preconditions -- It is assumed you already have a multi index table instance defined along with its mandatory primary index, otherwise take a look at the section [How to instantiate a multi index table](./how-to-instantiate-a-multi-index-table.md). - -The steps below show how to add a secondary index to the existing multi index table. - -1. Add a second field, `secondary`, to the data structure that defines the row of the table, in your case `test_table` -```diff - struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table - name test_primary; -+ name secondary; - // additional data stored in table row - uint64_t datum; - // mandatory definition for primary key getter - uint64_t primary_key( ) const { return test_primary.value; } - }; -``` - -2. Add `by_secondary( )` method, which is the index accessor method to the new field value added. The secondary index, that will be added in step 3, will index this new data structure field. -```diff - struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table - name test_primary; - name secondary; - // additional data stored in table row - uint64_t datum; - // mandatory definition for primary key getter - uint64_t primary_key( ) const { return test_primary.value; } -+ uint64_t by_secondary( ) const { return secondary.value; } - }; -``` +## Overview + +This guide provides instructions to define a secondary index for a multi-index table. + +## Reference + +See the following code reference: + +* The [`multi-index`](../../classeosio_1_1multi__index) class. + +## Before you begin + +Make sure you have the following prerequisites in place: + +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index), +* A multi-index table `testtab` along with its `test_table` data structure, its mandatory primary index, and the type alias definition `test_table_t`. Please see [How To Define A Primary Index](./how-to-define-a-primary-index) to set up these prerequisites. + +## Procedure + +Complete the following steps to define a secondary index for the multi-index table `testtab`. + +### 1. Extend The Multi-Index Data Structure + +Add a second data member `secondary`, of type `eosio::name`, to the `test_table` data structure that defines the `testtab` data. + + ```diff + struct [[eosio::table]] test_table { + // this data member stores a name for each row of the multi-index table + name test_primary; + + name secondary; + // additional data stored in table row + uint64_t datum; + // mandatory definition for primary key getter + uint64_t primary_key( ) const { return test_primary.value; } + }; + ``` -3. In the `test_table` alias definition (typedef), add the definition of the secondary index by making use of the `eosio::indexed_by` template. `eosio::index_by` needs two parameters: the name of the index, `"secid"_n`, and a function call operator which extracts the value from the secondary data member as an index key. The function call operator is achieved by employing the `eosio::const_mem_fun` template which receives two parameters: the data structure `test_table` and the reference to the getter function member `by_secondary`. +### 2. Add The Secondary Index Accessor Method -```diff -- typedef eosio::multi_index<"testtaba"_n, test_table> test_tables; -+ typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_tables; -``` +Add `by_secondary()` method, which is the index accessor method to the new data member added. The secondary index, that will be added in the next step, will index this new data structure data member. -The full contract definition code with all the changes described above could look like this: + ```diff + struct [[eosio::table]] test_table { + // this data member stores a name for each row of the multi-index table + name test_primary; + name secondary; + // additional data stored in table row + uint64_t datum; + // mandatory definition for primary key getter + uint64_t primary_key( ) const { return test_primary.value; } + + uint64_t by_secondary( ) const { return secondary.value; } + }; + ``` + +### 3. Define The Secondary Index + +In the `test_table_t` type definition, add the definition of the secondary index with the use of the `eosio::indexed_by` template. `eosio::index_by` needs two parameters: the name of the index, `"secid"_n`, and a function call operator which extracts the value from the secondary data member as an index key. The function call operator is defined with the use of `eosio::const_mem_fun` template which receives two parameters: the data structure `test_table` and the reference to the getter function member `by_secondary`. + + ```diff + - typedef eosio::multi_index<"testtaba"_n, test_table> test_table_t; + + typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_table_t; + ``` + +For reference see below the full contract definition code with all the changes described above. __multi_index_example.hpp__ -```cpp -#include -using namespace eosio; - -// multi index example contract class -class [[eosio::contract]] multi_index_example : public contract { - public: - using contract::contract; - - // contract class constructor - multi_index_example( name receiver, name code, datastream ds ) : - // contract base class contructor - contract(receiver, code, ds), - // instantiate multi index instance as data member (find it defined below) - testtab(receiver, receiver.value) - { } - - struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table - name test_primary; - name secondary; - // additional data stored in table row - uint64_t datum; - // mandatory definition for primary key getter - uint64_t primary_key( ) const { return test_primary.value; } - uint64_t by_secondary( ) const { return secondary.value; } - }; - - // the multi index type definition, for ease of use a type alias `test_tables` is defined, - // based on the multi_index template type, parametarized with a random name, the - // test_table data structure, and the secondary index - typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_tables; - - // the multi index table instance declared as a data member of type test_tables - test_tables testtab; - - [[eosio::action]] void set( name user ); - [[eosio::action]] void print( name user ); - - using set_action = action_wrapper<"set"_n, &multi_index_example::set>; - using print_action = action_wrapper<"print"_n, &multi_index_example::print>; -}; -``` + + ```cpp + #include + using namespace eosio; + + // multi-index example contract class + class [[eosio::contract]] multi_index_example : public contract { + public: + using contract::contract; + + // contract class constructor + multi_index_example( name receiver, name code, datastream ds ) : + // contract base class contructor + contract(receiver, code, ds), + // instantiate multi-index instance as data member (find it defined below) + testtab(receiver, receiver.value) + { } + + struct [[eosio::table]] test_table { + // this data member stores a name for each row of the multi-index table + name test_primary; + name secondary; + // additional data stored in table row + uint64_t datum; + // mandatory definition for primary key getter + uint64_t primary_key( ) const { return test_primary.value; } + uint64_t by_secondary( ) const { return secondary.value; } + }; + + // the multi-index type definition, for ease of use a type alias `test_table_t` is defined, + // based on the multi_index template type, parametarized with a random name, the + // test_table data structure, and the secondary index + typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_table_t; + + // the multi-index table instance declared as a data member of type test_table_t + test_table_t testtab; + + [[eosio::action]] void set( name user ); + [[eosio::action]] void print( name user ); + + using set_action = action_wrapper<"set"_n, &multi_index_example::set>; + using print_action = action_wrapper<"print"_n, &multi_index_example::print>; + }; + ``` + +Now you have instantiated the `testtab` as a multi-index table which has a primary index defined for its `test_primary` data member and a secondary index for its `secondary` data member. [[info | Full example location]] -| A full example project demonstrating the instantiation and usage of multi index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). \ No newline at end of file +| A full example project demonstrating the instantiation and usage of multi-index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). + +[[warning | Do not add a secondary index to an existing table]] +| Adding a secondary index to an existing multi-index table it will have unpredictable outcome. Consult the [Data design and migration](../best-practices/data-design-and-migration) documentation for more details. + + +## Summary + +In conclusion, the above instructions show how to define a secondary index for a multi-index table. + +## Next Steps + +* You can [iterate and retrieve data using the secondary index](./how-to-iterate-and-retrieve-a-multi_index-table-based-on-secondary-index) from the multi-index table. diff --git a/docs/06_how-to-guides/40_multi-index/how-to-define-a-singleton.md b/docs/06_how-to-guides/40_multi-index/how-to-define-a-singleton.md index f8ee5b51a2..e57cebe0a0 100644 --- a/docs/06_how-to-guides/40_multi-index/how-to-define-a-singleton.md +++ b/docs/06_how-to-guides/40_multi-index/how-to-define-a-singleton.md @@ -2,56 +2,90 @@ content_title: How to define a singleton --- -To define a simple singleton, which is storing an account name as primary value and a uint64_t as secondary value in structure `testtable`, follow the steps below: +## Overview -1. Include the `eosio.hpp` and `singleton.hpp` headers and declare the `eosio` namespace usage -``` -#include -#include -using namespace eosio; -``` +This guide provides instructions to define a singleton. -2. Define the data structure for the multi index table -```cpp -struct [[eosio::table]] testtable { - name primary_value; - uint64_t secondary_value; -}; -``` +## Reference -3. For ease of use, define a type alias `singleton_type` based on the `eosio::singleton` template type, parametarized with a random name `"testtable"`, which has to respect the EOSIO account name restrictions, and the `testtable` data structure defined above -```diff -struct [[eosio::table]] testtable { - name primary_value; - uint64_t secondary_value; -}; -+using singleton_type = eosio::singleton<"testtable"_n, testtable>; -``` +See the following code reference: -4. Define the singleton table instance declared as a data member of type `singleton_type` defined in the privious step -```diff -struct [[eosio::table]] testtable { - name primary_value; - uint64_t secondary_value; -}; +* The [`singleton`](../../classeosio_1_1singleton) class. -using singleton_type = eosio::singleton<"testtable"_n, testtable>; -+singleton_type singleton_instance; -``` +## Before you begin -5. Instantiate the data member `singleton_instance` by passing to its constructor the `receiver` and the `code` (in this case `receiver.value`) parameters; these two combined with "testtable" provide access to the partition of the RAM cache used by this singleton. In this example you will initialize the `singleton_instance` data member in the smart contract constructor, see below: -```diff -// singleton contract constructor -singleton_example( name receiver, name code, datastream ds ) : - contract(receiver, code, ds), -+ singleton_instance(receiver, receiver.value) - { } -} -``` +Make sure you have the following prerequisites in place: + +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index), + +## Procedure + +A singleton uses a single multi-index table to store named objects of various types. To define a simple singleton, which is storing an account `name` as primary value and a `uint64_t` as secondary value in structure `testtable`, follow the steps below: + +### 1. Preparation And Initialization + +Include the `eosio.hpp` and `singleton.hpp` headers and declare the `eosio` namespace usage + + ```cpp + #include + #include + using namespace eosio; + ``` -Now you have defined and instantiated a singleton. Below you can find a possible implementation for the full class singleton example contract. +### 2. Define The Table Data Structure + +Define the data structure for the multi-index table: + + ```cpp + struct [[eosio::table]] testtable { + name primary_value; + uint64_t secondary_value; + }; + ``` + +### 3. Define A Singleton Type Alias + +For ease of use, define a type alias `singleton_type` based on the `eosio::singleton` template type, parametarized with a random name `"testtable"` and the `testtable` data structure. The names must adhere to `EOSIO` account name restrictions. + + ```diff + struct [[eosio::table]] testtable { + name primary_value; + uint64_t secondary_value; + }; + +using singleton_type = eosio::singleton<"testtable"_n, testtable>; + ``` + +### 4. Define The Singleton Instance + +Define the singleton table instance as a data member of type `singleton_type`. + + ```diff + struct [[eosio::table]] testtable { + name primary_value; + uint64_t secondary_value; + }; + + using singleton_type = eosio::singleton<"testtable"_n, testtable>; + +singleton_type singleton_instance; + ``` + +### 5. Initialize And Use The Singleton Instance + +Initialize the `singleton_instance` using the constructor with the parameters `receiver` and `code` (the last one in in this case is `receiver.value`). These parameters, combined with `testtable`, provide access to the partition of the RAM cache used by this singleton. In our example you initialize the `singleton_instance` data member in the smart contract constructor, see below: + + ```diff + // singleton contract constructor + singleton_example( name receiver, name code, datastream ds ) : + contract(receiver, code, ds), + + singleton_instance(receiver, receiver.value) + { } + } + ``` + +Now you have defined and initialized a singleton as a data member for the smart contract class. You can access it from any of the smart contract methods via `singleton_instance` data member. Below you can find a possible implementation for the full class singleton example contract. __singleton_example.hpp__ + ```cpp #include #include @@ -84,9 +118,10 @@ class [[eosio::contract]] singleton_example : public contract { }; ``` -And below is a possible implementation for the two `get` and `set` actions defined above. It also demonstrates the usage of a couple of singleton methods. Note that the `set` action makes use of the singleton's `set` method, for which parameter is the account to pay for the new value stored. In this case, the same account name that is stored in the primary value is the payer. However, it can be a different account if so required. +Below is an example for the `get` and `set` actions. It also demonstrates the usage of the `get` and `set` singleton methods. Note that the `set` action makes use of the singleton's `set` method, for which the second parameter is the payer account for the RAM needed to store the new value. __singleton_example.cpp__ + ```cpp #include @@ -112,3 +147,11 @@ __singleton_example.cpp__ [[info | Full example location]] | A full example project demonstrating the instantiation and usage of singleton can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/singleton_example). + +## Summary + +In conclusion, the above instructions show how to define a singleton. + +## Next Steps + +* Singleton uses as underlying structure a multi-index table therefore you can [iterate and retrieve data](./how-to-iterate-and-retrieve-a-multi_index-table) from the singleton the same way you would with a multi-index table. diff --git a/docs/06_how-to-guides/40_multi-index/how-to-delete-data-from-a-multi-index-table.md b/docs/06_how-to-guides/40_multi-index/how-to-delete-data-from-a-multi-index-table.md index 3f2fbc5050..80db74dcee 100644 --- a/docs/06_how-to-guides/40_multi-index/how-to-delete-data-from-a-multi-index-table.md +++ b/docs/06_how-to-guides/40_multi-index/how-to-delete-data-from-a-multi-index-table.md @@ -1,13 +1,34 @@ --- -content_title: How to delete data from a multi index table +content_title: How to delete data from a multi-index table --- -## Preconditions -- It is assumed you already have a multi index table instance defined along with its mandatory primary index, otherwise take a look at the section [How to instantiate a multi index table](./how-to-instantiate-a-multi-index-table.md). +## Overview -To delete data from a multi index table follow the steps below: +This guide provides instructions to to delete data from a multi-index table. + +## Reference + +See the following code reference: + +* The [`multi-index`](../../classeosio_1_1multi__index) class. +* The [`multi-index::find(...)`](../../group__multiindex#function-find) method. +* The [`multi-index::erase(...)`](../../group__multiindex/#function-erase) method. + +## Before you begin + +Make sure you have the following prerequisites in place: + +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index), +* A multi-index `testab` table instance which stores `user` objects indexed by the primary key which is of type `eosio::name`. Consult the section [How to instantiate a multi-index table](./how-to-instantiate-a-multi-index-table.md) to learn how to set it up. + +## Procedure + +Complete the following steps to implement a `del` action which deletes an user object, identified by its account name, from the multi-index table. + +### 1. Find The User You Want To Delete + +Use the multi-index [`find(...)`](../../group__multiindex#function-find) method to locate the user object you want to delete. The targeted user is searched based on its account name. -1. Make use of the multi index table iterator to find out if the data exists ```cpp [[eosio::action]] void multi_index_example::del( name user ) { // check if the user already exists @@ -15,13 +36,16 @@ To delete data from a multi index table follow the steps below: } ``` -2. If the data exists use the `delete` method to delete the row from table +### 2. Delete The User If Found + +Check to see if the user exists and use [`erase`(...)](../../group__multiindex/#function-erase) method to delete the row from table. Otherwise print an informational message and return. + ```diff [[eosio::action]] void multi_index_example::del( name user ) { // check if the user already exists auto itr = testtab.find(user.value); + if ( itr == testtab.end() ) { -+ printf("user does not exist in table, nothing to delete" ); ++ printf("User does not exist in table, nothing to delete"); + return; + } @@ -30,4 +54,23 @@ To delete data from a multi index table follow the steps below: ``` [[info | Full example location]] -| A full example project demonstrating the instantiation and usage of multi index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). \ No newline at end of file +| A full example project demonstrating the instantiation and usage of multi-index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). + +## Summary + +In conclusion, the above instructions show how to delete data from a multi-index table. + +## Next Steps + +* You can verify if the user object was deleted from the multi-index table. . + +```cpp + // check if the user was deleted + auto itr = testtab.find(user.value); + if ( itr == testtab.end() ) { + printf("User was deleted successfully."); + } + else { + printf("User was NOT deleted!"); + } +``` diff --git a/docs/06_how-to-guides/40_multi-index/how-to-insert-data-into-a-multi-index-table.md b/docs/06_how-to-guides/40_multi-index/how-to-insert-data-into-a-multi-index-table.md index 37254edb51..f2c8537692 100644 --- a/docs/06_how-to-guides/40_multi-index/how-to-insert-data-into-a-multi-index-table.md +++ b/docs/06_how-to-guides/40_multi-index/how-to-insert-data-into-a-multi-index-table.md @@ -1,22 +1,45 @@ --- -content_title: How to insert data into a multi index table +content_title: How to insert data into a multi-index table --- -## Preconditions -- It is assumed you already have a multi index table instance defined along with its mandatory primary index, otherwise take a look at the section [How to instantiate a multi index table](./how-to-instantiate-a-multi-index-table.md). +## Overview -To insert data into a multi index table follow the following steps +This guide provides instructions to insert data into a multi-index table. + +## Reference + +See the following code reference: + +* The [`multi-index`](../../classeosio_1_1multi__index) class. +* The [`multi-index::find(...)`](../../group__multiindex#function-find) method. +* The [`multi-index::emplace(...)`](../../group__multiindex/#function-emplace) method. + +## Before you begin + +Make sure you have the following prerequisites in place: + +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index), +* A multi-index `testab` table instance which stores `user` objects indexed by the primary key which is of type `eosio::name`. Consult the section [How to instantiate a multi-index table](./how-to-instantiate-a-multi-index-table.md) to learn how to set it up. + +## Procedure + +Complete the following steps to insert an user object in the `testtab` multi-index table: + +### 1. Verify If The User Already Exists + +Use of the multi-index table iterator to find out if the user object already exists. The targeted user is searched based on its account name. -1. Make use of the multi index table iterator to find out if the data doesn't already exist ```cpp [[eosio::action]] void multi_index_example::set( name user ) { // check if the user already exists auto itr = testtab.find(user.value); - } ``` -2. Use the `emplace` method to make the insertion if the user is not already in table +### 2. Insert The User If Not Found In Table + +Use the [`emplace`](../../group__multiindex/#function-emplace) method to make the insertion if the user object is not already in the multi-index table. Otherwise print an informational message. + ```diff [[eosio::action]] void multi_index_example::set( name user ) { // check if the user already exists @@ -29,8 +52,19 @@ To insert data into a multi index table follow the following steps + u.datum = 0; + }); + } ++ else { ++ printf("User already exists."); ++ } } ``` [[info | Full example location]] -| A full example project demonstrating the instantiation and usage of multi index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). \ No newline at end of file +| A full example project demonstrating the instantiation and usage of multi-index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). + +## Summary + +In conclusion, the above instructions show how to insert data in a multi-index table. + +## Next Steps + +* You can [iterate and retrieve newly inserted data](./how-to-iterate-and-retrieve-a-multi_index-table) from the multi-index table. diff --git a/docs/06_how-to-guides/40_multi-index/how-to-instantiate-a-multi-index-table.md b/docs/06_how-to-guides/40_multi-index/how-to-instantiate-a-multi-index-table.md index 742917f092..101f6169ef 100644 --- a/docs/06_how-to-guides/40_multi-index/how-to-instantiate-a-multi-index-table.md +++ b/docs/06_how-to-guides/40_multi-index/how-to-instantiate-a-multi-index-table.md @@ -1,32 +1,65 @@ --- -content_title: How to instantiate a multi index table +content_title: How to instantiate a multi-index table --- -1. Include the `eosio.hpp` header and declare the `eosio` namespace usage -``` +## Overview + +This guide provides instructions to instantiate a multi-index table. + +## Reference + +See the following code reference: + +* The [`multi-index`](../../classeosio_1_1multi__index) class. + +## Before you begin + +Make sure you have the following prerequisites in place: + +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index), + +## Procedure + +Complete the following steps to instantiate a multi-index table `testtab`. + +### 1. Preparation And Initialization + +Include the `eosio.hpp` header and use the `using` directive to access the `eosio` namespace. + +```cpp #include using namespace eosio; ``` -2. Define the data structure for the multi index table + +### 2. Define The Table Data Structure + +Define the data structure for the multi-index table. + ```cpp struct [[eosio::table]] test_table { }; ``` -3. Add to the data structure the fields which define the multi index table -```diff - // the data structure which defines each row of the table - struct [[eosio::table]] test_table { -+ // this field stores a name for each row of the multi index table -+ name test_primary; -+ // additional data stored in table row, e.g. an uint64_t type data -+ uint64_t datum; - }; -``` -4. Add definition of the primary index for the multi index table. The primary index type must be uint64_t, it must be unique and it must be named `primary_key()`, if you don't have this the compiler (eosio-cpp) will generate an error saying it can't find the field to use as the primary key: + +Add the data structure data members. Each data member corresponds to a field of the multi-index table. A primary key is required when defining a multi-index table structure, therefore you need to know which is the multi-index table field that is the primary key for your multi-index table. The corresponding data member for the primary key field must store unique values. In this case it is the `test_primary` data member of type `eosio::name`. + + ```diff + // the data structure which defines each row of the table + struct [[eosio::table]] test_table { + + // this data member stores a name for each row of the multi-index table + + name test_primary; + + // additional data stored in table row, e.g. an uint64_t type data + + uint64_t datum; + }; + ``` + +### 3. Define The Primary Index + +Add the definition of the primary index for the multi-index table. The primary index type must be uint64_t, it must be unique and must be named `primary_key()`, otherwise the compiler (eosio-cpp) will generate an error saying it can not find the field to use as the primary key: + ```diff // the data structure which defines each row of the table struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table + // this data member stores a name for each row of the multi-index table name test_primary; // additional data stored in table row uint64_t datum; @@ -38,11 +71,14 @@ using namespace eosio; [[info | Additional indexes information]] | Other, secondary, indexes if they will be defined can have duplicates. You can have up to 16 additional indexes and the field types can be uint64_t, uint128_t, uint256_t, double or long double. -5. For ease of use, define a type alias `test_tables` based on the multi_index template type, parametarized with a random name `"testtaba"` and the `test_table` data structure defined above +### 4. Define A Multi-Index Type Alias + +For ease of use, define a type alias `test_table_t` based on the `eosio::multi_index` template type, parametarized with a random name `"testtaba"` and the `test_table` data structure. The names must adhere to `EOSIO` account name restrictions. + ```diff // the data structure which defines each row of the table struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table + // this data member stores a name for each row of the multi-index table name test_primary; // additional data stored in table row uint64_t datum; @@ -50,14 +86,17 @@ using namespace eosio; uint64_t primary_key( ) const { return test_primary.value; } }; -+ typedef eosio::multi_index<"testtaba"_n, test_table> test_tables; ++ typedef eosio::multi_index<"testtaba"_n, test_table> test_table_t; ``` -6. Define the multi index table data member of type `test_tables` defined in the previous step +### 5. Instantiate The Multi-Index Table + +Declare the `testtab` multi-index table as a data member of type `test_table_t`. + ```diff // the data structure which defines each row of the table struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table + // this data member stores a name for each row of the multi-index table name test_primary; // additional data stored in table row uint64_t datum; @@ -65,31 +104,35 @@ using namespace eosio; uint64_t primary_key( ) const { return test_primary.value; } }; - typedef eosio::multi_index<"testtaba"_n, test_table> test_tables; -+ test_tables testtab; + typedef eosio::multi_index<"testtaba"_n, test_table> test_table_t; ++ test_table_t testtab; ``` -7. Instantiate the data member `testtab` by passing to its constructor the `scope` (in this case `receiver`) and the `code` parameters, these two combined with table name `"testtaba"` provide access to the partition of the RAM cache used by this multi index table, in this example you will initialize the `testtab` data member in the smart contract constructor +# 6. Initialize The Multi-Index Table Instance + +Initialize the data member `testtab` by passing to its constructor these two values: `receiver` for the `code` parameter and `receiver.value` for the `scope` parameter. These two parameters combined with table name `"testtaba"` provide access to the partition of the RAM cache used by this multi-index table, in this example you will initialize the `testtab` data member in the smart contract constructor ```diff // contract class constructor multi_index_example( name receiver, name code, datastream ds ) : // contract base class contructor contract(receiver, code, ds), - // instantiate multi index instance as data member (find it defined below) + // instantiate multi-index instance as data member (find it defined below) + testtab(receiver, receiver.value) { } ``` -Now you have instantiated the `testtab` variable as a multi index table which has a primary index defined for its `test_primary` data member. -Here is how the definition of a `multi_index_example` contract containing a multi index table could look like after following all the steps above. +Now you have instantiated a multi-index table, and assigned to `testtab` variable, which has a primary index defined for its `test_primary` data member. + +Here is how the definition of a `multi_index_example` contract containing a multi-index table could look like after following all the steps above. __multi_index_example.hpp__ + ```cpp #include using namespace eosio; -// multi index example contract class +// multi-index example contract class class [[eosio::contract]] multi_index_example : public contract { public: using contract::contract; @@ -98,14 +141,14 @@ class [[eosio::contract]] multi_index_example : public contract { multi_index_example( name receiver, name code, datastream ds ) : // contract base class contructor contract(receiver, code, ds), - // instantiate multi index instance as data member (find it defined below) + // instantiate multi-index instance as data member (find it defined below) testtab(receiver, receiver.value) { } - // the row structure of the multi index table, that is, each row of the table + // the row structure of the multi-index table, that is, each row of the table // will contain an instance of this type of structure struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table + // this data member stores a name for each row of the multi-index table name test_primary; // additional data stored in table row uint64_t datum; @@ -113,13 +156,13 @@ class [[eosio::contract]] multi_index_example : public contract { uint64_t primary_key( ) const { return test_primary.value; } }; - // the multi index type definition, for ease of use define a type alias `test_tables`, + // the multi-index type definition, for ease of use define a type alias `test_table_t`, // based on the multi_index template type, parametarized with a random name and // the test_table data structure - typedef eosio::multi_index<"testtaba"_n, test_table> test_tables; + typedef eosio::multi_index<"testtaba"_n, test_table> test_table_t; - // the multi index table instance declared as a data member of type test_tables - test_tables testtab; + // the multi-index table instance declared as a data member of type test_table_t + test_table_t testtab; [[eosio::action]] void set( name user ); [[eosio::action]] void print( name user ); @@ -130,4 +173,13 @@ class [[eosio::contract]] multi_index_example : public contract { ``` [[info | Full example location]] -| A full example project demonstrating the instantiation and usage of multi index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). +| A full example project demonstrating the instantiation and usage of multi-index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). + +## Summary + +In conclusion, the above instructions show how to define and instantiate a multi-index table instance. + +## Next Steps + +* You can [insert data in the multi-index table](./how-to-insert-data-into-a-multi-index-table). +* You can [iterate and retrieve data](./how-to-iterate-and-retrieve-a-multi_index-table) from the multi-index table. diff --git a/docs/06_how-to-guides/40_multi-index/how-to-iterate-and-retrieve-a-multi_index-table-based-on-secondary-index.md b/docs/06_how-to-guides/40_multi-index/how-to-iterate-and-retrieve-a-multi_index-table-based-on-secondary-index.md index 939abbd7e8..c4205a1cbe 100644 --- a/docs/06_how-to-guides/40_multi-index/how-to-iterate-and-retrieve-a-multi_index-table-based-on-secondary-index.md +++ b/docs/06_how-to-guides/40_multi-index/how-to-iterate-and-retrieve-a-multi_index-table-based-on-secondary-index.md @@ -1,72 +1,51 @@ --- -content_title: How to iterate and retrieve a multi index table based on secondary index +content_title: How to iterate and retrieve from multi-index table based on secondary index --- -## Preconditions -- It is assumed you already have a multi index table defined with a primary index and a secondary index, if not you can find an example [here](./how-to-define-a-secondary-index.md). +## Overview -You'll start with this example below which shows the definition of a `multi_index_example` contract class which has defined a multi index table with two indexes, a mandatory primary one and a secondary one: +This guide provides instructions on how iterate and retrieve data from a multi-index table based on a secondary index defined in the multi-index table. -```cpp -#include -using namespace eosio; +## Reference -// multi index example contract class -class [[eosio::contract]] multi_index_example : public contract { - public: - using contract::contract; +See the following code reference: - // contract class constructor - multi_index_example( name receiver, name code, datastream ds ) : - // contract base class contructor - contract(receiver, code, ds), - // instantiate multi index instance as data member (find it defined below) - testtab(receiver, receiver.value) - { } +* The [`multi-index`](../../classeosio_1_1multi__index) class. +* The [`multi-index::find(...)`](../../group__multiindex#function-find) method. - // the row structure of the multi index table, that is, each row of the table - // will contain an instance of this type of structure - struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table - name test_primary; - name secondary; - // additional data stored in table row - uint64_t datum; - // mandatory definition for primary key getter - uint64_t primary_key( ) const { return test_primary.value; } - uint64_t by_secondary( ) const { return secondary.value; } - }; +## Before you begin - // the multi index type definition, for ease of use define a type alias `test_tables`, - // based on the multi_index template type, parametarized with a random name, the - // test_table data structure, and the secondary index - typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_tables; +Make sure you have the following prerequisites in place: - // the multi index table instance declared as a data member of type test_tables - test_tables testtab; +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index), +* A multi-index `testab` table instance which stores `user` objects indexed by the primary key which is of type `eosio::name` and a secondary index for data member `secondary` of type `eosio::name` accessible through `by_secondary()` method. Consult the section [How to define a secondary index](./how-to-define-a-secondary-index) to learn how to set it up. - [[eosio::action]] void set( name user ); - [[eosio::action]] void print( name user ); +## Procedure - using set_action = action_wrapper<"set"_n, &multi_index_example::set>; - using print_action = action_wrapper<"print"_n, &multi_index_example::print>; -}; -``` +Complete the following steps to iterate, retrieve and print data from the `testtab` multi-index table using the secondary index. -To iterate and retreive the multi index table `testtab` defined in `multi_index_example` contract based on secondary index `by_secondary`, define a third action `bysec` which will do exactly that. +### 1. Define The bysec(...) Action -1. In the contract definition, add the new action definition, using the `[[eosio::action]] void` and the `eosio::action_wrapper` template like this: +Add an action to the definition of the multi-index table which takes as parameter an account name. This action will retrieve the user object stored in the multi-index based on the passed in account name parameter. The action will use the secondary index. ```cpp [[eosio::action]] void bysec( name secid ); +``` + +Optionally, for ease of use add the action wrapper definition as well. - using bysec_action = action_wrapper<"bysec"_n, &multi_index_example::bysec>; +```diff + [[eosio::action]] void bysec( name secid ); + + +using bysec_action = action_wrapper<"bysec"_n, &multi_index_example::bysec>; ``` -2. In the contract implementation add the new action implementation like this: +### 2. Implement The `bysec(...)` Action + +Search for the `user` name in the multi-index table using the secondary index. If found, print out the value of field `datum`. Otherwise raise and error with a custom message. In the contract definition add the following implementation for `print` action: ```cpp -// iterates the multi index table rows using the secondary index and prints the row's values +// iterates the multi-index table rows using the secondary index and prints the row's values [[eosio::action]] void multi_index_example::bysec( name secid ) { // access the secondary index auto idx = testtab.get_index<"secid"_n>(); @@ -78,14 +57,17 @@ To iterate and retreive the multi index table `testtab` defined in `multi_index_ } ``` -3. The full code for both the contract definition and contract implementation follow: +### 3. Put It All Together + +The full definition and implementation files for the contract should look like this: __multi_index_example.hpp__ + ```cpp #include using namespace eosio; -// multi index example contract class +// multi-index example contract class class [[eosio::contract]] multi_index_example : public contract { public: using contract::contract; @@ -94,14 +76,14 @@ class [[eosio::contract]] multi_index_example : public contract { multi_index_example( name receiver, name code, datastream ds ) : // contract base class contructor contract(receiver, code, ds), - // instantiate multi index instance as data member (find it defined below) + // instantiate multi-index instance as data member (find it defined below) testtab(receiver, receiver.value) { } - // the row structure of the multi index table, that is, each row of the table + // the row structure of the multi-index table, that is, each row of the table // will contain an instance of this type of structure struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table + // this data member stores a name for each row of the multi-index table name test_primary; name secondary; // additional data stored in table row @@ -111,13 +93,13 @@ class [[eosio::contract]] multi_index_example : public contract { uint64_t by_secondary( ) const { return secondary.value; } }; - // the multi index type definition, for ease of use define a type alias `test_tables`, + // the multi-index type definition, for ease of use define a type alias `test_table_t`, // based on the multi_index template type, parametarized with a random name, the // test_table data structure, and the secondary index - typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_tables; + typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_table_t; - // the multi index table instance declared as a data member of type test_tables - test_tables testtab; + // the multi-index table instance declared as a data member of type test_table_t + test_table_t testtab; [[eosio::action]] void set( name user ); [[eosio::action]] void print( name user ); @@ -130,6 +112,7 @@ class [[eosio::contract]] multi_index_example : public contract { ``` __multi_index_example.cpp__ + ```cpp #include @@ -158,7 +141,7 @@ __multi_index_example.cpp__ eosio::print_f("Test Table : {%, %}\n", itr->test_primary, itr->datum); } -// iterates the multi index table rows using the secondary index and prints the row's values +// iterates the multi-index table rows using the secondary index and prints the row's values [[eosio::action]] void multi_index_example::bysec( name secid ) { // access the secondary index auto idx = testtab.get_index<"secid"_n>(); @@ -172,4 +155,13 @@ __multi_index_example.cpp__ ``` [[info | Full example location]] -| A full example project demonstrating the instantiation and usage of multi index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). \ No newline at end of file +| A full example project demonstrating the instantiation and usage of multi-index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). + +## Summary + +In conclusion, the above instructions show how to iterate and retrieve a multi-index table based on secondary index. + +## Next Steps + +* You can [insert data](./how-to-insert-data-into-a-multi-index-table) into the multi-index table. +* You can [delete data](./how-to-delete-data-from-a-multi-index-table) from the multi-index table. diff --git a/docs/06_how-to-guides/40_multi-index/how-to-iterate-and-retrieve-a-multi_index-table.md b/docs/06_how-to-guides/40_multi-index/how-to-iterate-and-retrieve-a-multi_index-table.md index 3654701d8a..e821261c42 100644 --- a/docs/06_how-to-guides/40_multi-index/how-to-iterate-and-retrieve-a-multi_index-table.md +++ b/docs/06_how-to-guides/40_multi-index/how-to-iterate-and-retrieve-a-multi_index-table.md @@ -1,69 +1,49 @@ --- -content_title: How to iterate and retrieve a multi index table +content_title: How to iterate and retrieve from multi-index table --- -## Preconditions -- It is assumed you already have a multi index table instance defined along with its mandatory primary index, otherwise take a look at the section [How to instantiate a multi index table](./how-to-instantiate-a-multi-index-table.md). +## Overview -For exemplification define the multi index contract definition like below: +This guide provides instructions to iterate and retrieve data from a multi-index table. -__multi_index_example.hpp__ -```cpp -#include -using namespace eosio; +## Reference -// multi index example contract class -class [[eosio::contract]] multi_index_example : public contract { - public: - using contract::contract; +See the following code reference: - // contract class constructor - multi_index_example( name receiver, name code, datastream ds ) : - // contract base class contructor - contract(receiver, code, ds), - // instantiate multi index instance as data member (find it defined below) - testtab(receiver, receiver.value) - { } +* The [`multi-index`](../../classeosio_1_1multi__index) class. +* The [`multi-index::find(...)`](../../group__multiindex#function-find) method. - // the row structure of the multi index table, that is, each row of the table - // will contain an instance of this type of structure - struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table - name test_primary; - // additional data stored in table row - uint64_t datum; - // mandatory definition for primary key getter - uint64_t primary_key( ) const { return test_primary.value; } - }; +## Before you begin - // the multi index type definition, for ease of use define a type alias `test_tables`, - // based on the multi_index template type, parametarized with a random name and - // the test_table data structure - typedef eosio::multi_index<"testtaba"_n, test_table> test_tables; +Make sure you have the following prerequisites in place: - // the multi index table instance declared as a data member of type test_tables - test_tables testtab; +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index), +* A multi-index `testab` table instance which stores `user` objects indexed by the primary key which is of type `eosio::name`. Consult the section [How to instantiate a multi-index table](./how-to-instantiate-a-multi-index-table) to learn how to set it up. - [[eosio::action]] void set( name user ); +## Procedure - using set_action = action_wrapper<"set"_n, &multi_index_example::set>; -}; -``` +Complete the following steps to iterate, retrieve and print data from the `testtab` multi-index table. -The steps below show how to iterate and retrieve a multi index table. +### 1. Define The print(...) Action -1. Add to the above multi index example contract an action `print` which gets as parameter an acount name +Add a `print` action to the `testtab` multi-index table. The `print` action takes an account name as a parameter. ```cpp [[eosio::action]] void print( name user ); ``` -2. For ease of use add the action wrapper defition as well + +Optionally, for ease of use add the action wrapper definition as well. + ```diff [[eosio::action]] void print( name user ); +using print_action = action_wrapper<"print"_n, &multi_index_example::print>; ``` -3. Implement the action code, by searching for the `user` name in the multi index table using the primary index. If found, print out the value stored in that row for field `datum`. Otherwise asserts with a custom message. In the contract definition add the following implementation for `print` action: + +### 2. Implement The `print(...)` Action + +Search for the `user` name in the multi-index table using the primary index. If found, print out the value of field `datum`. Otherwise raise an error with a custom message. In the contract definition add the following implementation for `print` action: + ```cpp [[eosio::action]] void multi_index_example::print( name user ) { // searches for the row that corresponds to the user parameter @@ -76,14 +56,18 @@ The steps below show how to iterate and retrieve a multi index table. eosio::print_f("Test Table : {%, %}\n", itr->test_primary, itr->datum); } ``` -4. Finally the whole definition and implementation files for the contract should look like this: + +### 3. Put It All Together + +The full definition and implementation files for the contract should look like this: __multi_index_example.hpp__ + ```cpp #include using namespace eosio; -// multi index example contract class +// multi-index example contract class class [[eosio::contract]] multi_index_example : public contract { public: using contract::contract; @@ -92,14 +76,14 @@ class [[eosio::contract]] multi_index_example : public contract { multi_index_example( name receiver, name code, datastream ds ) : // contract base class contructor contract(receiver, code, ds), - // instantiate multi index instance as data member (find it defined below) + // instantiate multi-index instance as data member (find it defined below) testtab(receiver, receiver.value) { } - // the row structure of the multi index table, that is, each row of the table + // the row structure of the multi-index table, that is, each row of the table // will contain an instance of this type of structure struct [[eosio::table]] test_table { - // this field stores a name for each row of the multi index table + // this data member stores a name for each row of the multi-index table name test_primary; // additional data stored in table row uint64_t datum; @@ -107,13 +91,13 @@ class [[eosio::contract]] multi_index_example : public contract { uint64_t primary_key( ) const { return test_primary.value; } }; - // the multi index type definition, for ease of use define a type alias `test_tables`, + // the multi-index type definition, for ease of use define a type alias `test_table_t`, // based on the multi_index template type, parametarized with a random name and // the test_table data structure - typedef eosio::multi_index<"testtaba"_n, test_table> test_tables; + typedef eosio::multi_index<"testtaba"_n, test_table> test_table_t; - // the multi index table instance declared as a data member of type test_tables - test_tables testtab; + // the multi-index table instance declared as a data member of type test_table_t + test_table_t testtab; [[eosio::action]] void set( name user ); [[eosio::action]] void print( name user ); @@ -124,6 +108,7 @@ class [[eosio::contract]] multi_index_example : public contract { ``` __multi_index_example.cpp__ + ```cpp #include @@ -154,4 +139,13 @@ __multi_index_example.cpp__ ``` [[info | Full example location]] -| A full example project demonstrating the instantiation and usage of multi index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). \ No newline at end of file +| A full example project demonstrating the instantiation and usage of multi-index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). + +## Summary + +In conclusion, the above instructions show how to iterate and retrieve a multi-index table. + +## Next Steps + +* You can [insert data](./how-to-insert-data-into-a-multi-index-table) into the multi-index table. +* You can [delete data](./how-to-delete-data-from-a-multi-index-table) from the multi-index table. diff --git a/docs/06_how-to-guides/40_multi-index/how-to-modify-data-in-a-multi-index-table.md b/docs/06_how-to-guides/40_multi-index/how-to-modify-data-in-a-multi-index-table.md index 62a0250811..496a841ec6 100644 --- a/docs/06_how-to-guides/40_multi-index/how-to-modify-data-in-a-multi-index-table.md +++ b/docs/06_how-to-guides/40_multi-index/how-to-modify-data-in-a-multi-index-table.md @@ -1,20 +1,59 @@ --- -content_title: How to modify data in a multi index table +content_title: How to modify data in a multi-index table --- -## Preconditions -- It is assumed you already have a multi index table instance defined along with its mandatory primary index, otherwise take a look at the section [How to instantiate a multi index table](./how-to-instantiate-a-multi-index-table.md). +## Overview -To modify data in the multi index table defined in the above tutorial, you will implement an action `mod` which it will receive as parameter the `user` which is the key of the row you want to modify and the `value` param which is the value to update with the row. +This guide provides instructions to modify data in a multi-index table. + +## Reference + +See the following code reference: + +* The [`multi-index`](../../classeosio_1_1multi__index) class. +* The [`multi-index::modify(...)`](../../group__multiindex/#function-modify) method. + +## Before you begin + +Make sure you have the following prerequisites in place: + +* An EOSIO development environment, for details consult the [Get Started Guide](https://developers.eos.io/welcome/latest/getting-started-guide/index), +* A multi-index `testab` table instance which stores `user` objects indexed by the primary key which is of type `eosio::name`. Consult the section [How to instantiate a multi-index table](./how-to-instantiate-a-multi-index-table) to learn how to set it up. + +## Procedure + +Complete the following steps to modify data in the `testtab` multi-index table. + +### 1. Define The mod(...) Action + +Add a `mod` action to the `testtab` multi-index table. The `mod` action takes as input parameters a `user` of type `eosio::name` and a `value` of type `uint32_t`. The `mod` action updates the `user` object `datum` data member with the `uint32_t` value. + +```cpp +[[eosio::action]] void mod( name user, uint32_t value ); +``` + +Optionally, for ease of use add the action wrapper definition as well. + +```diff +[[eosio::action]] void mod( name user, uint32_t value ); + ++using mod_action = action_wrapper<"mod"_n, &multi_index_example::mod>; +``` + +### 2. Find The User You Want To Modify + +Use the multi-index [`find(...)`](../../group__multiindex#function-find) method to locate the user object you want to modify. The targeted user is searched based on its account name. -1. Make use of the multi index table iterator to find out if the data exists ```cpp [[eosio::action]] void multi_index_example::mod( name user, uint32_t value ) { auto itr = testtab.find(user.value); } ``` -2. If the row you want to update is not found, then assert by using the `check` method and yield an error message +### 3. Yield Error If User Not Found + +If the `user` object you want to update is not found then raise an error message by using the [`eosio::check`](../../namespaceeosio/#function-check-17) method. + ```diff [[eosio::action]] void multi_index_example::mod( name user, uint32_t value ) { auto itr = testtab.find(user.value); @@ -22,7 +61,9 @@ To modify data in the multi index table defined in the above tutorial, you will } ``` -3. If the row you want to update is found, the `check` method will do nothing and the iterator `itr` will be pointing at the row which you want to update, so then use the multi index `modify` method to make the update like below +### 4. Update The User If Found + +If the `user` object you want to update is found, the [`eosio::check`](../../namespaceeosio/#function-check-17) method will do nothing and the iterator `itr` will be pointing at the object which you want to update. Use the [`multi-index::modify(...)`](../../group__multiindex/#function-modify) method to update the user object `datum` data member with the `value` parameter. ```diff [[eosio::action]] void multi_index_example::mod( name user, uint32_t value ) { @@ -31,11 +72,21 @@ To modify data in the multi index table defined in the above tutorial, you will check( itr != testtab.end(), "user does not exist in table" ); + testtab.modify( itr, _self, [&]( auto& row ) { -+ row.secondary = user; + row.datum = value; + }); } ``` +Now you have implemented a new action `mod`. Call `mod` to update the `datum` data member for the user object identified by the `user` name parameter. + [[info | Full example location]] -| A full example project demonstrating the instantiation and usage of multi index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). +| A full example project demonstrating the instantiation and usage of multi-index table can be found [here](https://github.com/EOSIO/eosio.cdt/tree/master/examples/multi_index_example). + +## Summary + +In conclusion, the above instructions show how to modify data in a multi-index table. + +## Next Steps + +* You can [insert data](./how-to-insert-data-into-a-multi-index-table) into the multi-index table. +* You can [delete data](./how-to-delete-data-from-a-multi-index-table) from the multi-index table. diff --git a/docs/06_how-to-guides/50_how-to-create-and-use-action-wrappers.md b/docs/06_how-to-guides/50_how-to-create-and-use-action-wrappers.md index cce2bca91c..ddd97794b8 100644 --- a/docs/06_how-to-guides/50_how-to-create-and-use-action-wrappers.md +++ b/docs/06_how-to-guides/50_how-to-create-and-use-action-wrappers.md @@ -5,7 +5,7 @@ link_text: "How to create and use action wrappers" ## Overview -This guide provides instructions on how to create and use an action wrapper in a smart contract. +This guide provides instructions to create and use an action wrapper in a smart contract. ## Code Reference @@ -43,7 +43,7 @@ Complete the following steps to create and use `mod_action` action wrapper for t ### 1. Define The Action Wrapper -To define an action wrapper for the `mod` action, make use of the `eosio::action_wrapper` template, with the first parameter the action name as a `eosio::name` and second parameter as the reference to the action method: +To define an action wrapper for the `mod` action, use the `eosio::action_wrapper` template, with the first parameter the action name as a `eosio::name` and second parameter as the reference to the action method: ```diff class [[eosio::contract]] multi_index_example : public contract { @@ -67,7 +67,7 @@ To use the action wrapper, you have to include the header file where the action #### 2.2. Instantiate The Action Wrapper -Instantiate the `mod_action` defined above, specifying the contract to send the action to as the first argument. In this case, it is assumed the contract is deployed to `multiindexex` account, and a structure which is defined by two parameters: the self account, obtained by `get_self()` call, and the `active` permission (you can modify these two parameters based on your requirements). +Instantiate the `mod_action`. Specify the contract to send the action to as the first argument. In this case, it is assumed the contract is deployed to `multiindexex` account. Specify a structure with two parameters: the self account, obtained by `get_self()` call, and the `active` permission (you can modify these two parameters based on your requirements). ```diff #include diff --git a/docs/06_how-to-guides/60_how-to-return-values-from-actions.md b/docs/06_how-to-guides/60_how-to-return-values-from-actions.md index 6a5c60cf4a..eaf7c8c805 100644 --- a/docs/06_how-to-guides/60_how-to-return-values-from-actions.md +++ b/docs/06_how-to-guides/60_how-to-return-values-from-actions.md @@ -68,8 +68,6 @@ For a complete example of a smart contract that implements an action which retur ## Next Steps -The following options are available when you complete the procedure: - * Compile the smart contract and deploy it to the EOSIO testnet or any EOSIO based blockchain. * Use the `cleos` command to send the `checkwithrv` action to the smart contract and observe the returned value in the `cleos` output. * Use other means (e.g. programmatically) to send the `checkwithrv` action to the smart contract and observe the returned value in the action trace. diff --git a/docs/05_best-practices/02_naming-conventions.md b/docs/07_best-practices/02_naming-conventions.md similarity index 100% rename from docs/05_best-practices/02_naming-conventions.md rename to docs/07_best-practices/02_naming-conventions.md diff --git a/docs/05_best-practices/03_resource-planning.md b/docs/07_best-practices/03_resource-planning.md similarity index 100% rename from docs/05_best-practices/03_resource-planning.md rename to docs/07_best-practices/03_resource-planning.md diff --git a/docs/05_best-practices/04_data-design-and-migration.md b/docs/07_best-practices/04_data-design-and-migration.md similarity index 69% rename from docs/05_best-practices/04_data-design-and-migration.md rename to docs/07_best-practices/04_data-design-and-migration.md index 5d847064a9..3f443073db 100644 --- a/docs/05_best-practices/04_data-design-and-migration.md +++ b/docs/07_best-practices/04_data-design-and-migration.md @@ -2,15 +2,16 @@ content_title: Data design and migration --- -EOSIO based blockchains allow developers to easily update their smart contract code. However, a few things need to be considered when it comes to data update and/or migration. The main structure for storing data in EOSIO based blockchains is the multi index table. Once a multi index table has been created with a first version of a smart contract, it has some limitations when it comes to changing its structure. Below you will find a few possible approaches which you can consider when you design your smart contract data and its migration. +EOSIO based blockchains allow developers to easily update their smart contract code. However, a few things need to be considered when it comes to data updates and/or migration. The multi-index table API is one of the mechanisms (Key-Value API being the other) for storing and updating blockchain state. The multi-index table API creates and uses data structures in RAM. Once created and deployed on the blockchain there are limitations if you want to update these structures. Below you will find a few approaches to your smart contract data design, updates to this design, and for data migration. -# How to modify the structure of a multi index table +# How to modify a data structure defined using multi-index table API -Modifying a multi-index table structure that has already been deployed to an EOSIO-based blockchain may be done by selecting one of the different strategies outlined below, depending on your requirements: +Modifying a deployed multi-index table structure may be done by selecting one of the different strategies outlined below: ## 1. If you don't mind losing the existing data If you don't mind losing the data from the initial table you can follow these two steps: + 1. Erase all records from first table 2. Deploy a new contract with modified table structure @@ -18,25 +19,27 @@ If you don't mind losing the data from the initial table you can follow these tw If you want to keep the existing data there are two ways to do it: -### 2.1. Using binary extentions +### 2.1. Using binary extensions + To learn how to modify the structure using binary extensions read this [tutorial](../09_tutorials/01_binary-extension.md). ### 2.2. Using ABI variants + To learn how to modify the structure using ABI variants read this [tutorial](../09_tutorials/02_abi-variants.md). ### 2.3. Migrate the existing data to a second table #### 2.3.1. Migration without downtime, but slower -1. Create the new version of your multi index table alongside the old one; +1. Create the new version of your multi-index table alongside the old one. 2. Transfer data from the old table to the new one. You may do so as part of your normal access pattern, first checking the new table to see if the entry you seek is present and if not, check the original table, and if it's present, migrate it while adding the data for the new field, then remove it from the original table to save RAM costs. -3. You must retain both versions of your multi index table until you have completed this migration, at which point you may update your contract to remove the original version of your multi index table. +3. You must retain both versions of your multi-index table until you have completed this migration, at which point you may update your contract to remove the original version of your multi-index table. #### 2.3.2. Migration with downtime, but faster If you prefer less code complexity and can accept downtime for your application: -1. Deploy a version of your contract solely for migration purposes, and run migration transactions on every row of your table until complete. If the first table is big, e.g. has a large number of rows, the transaction time limit could be reached while running the migration transactions. To mitigate this implement the migrate function to move a limited number of rows each time it runs; +1. Deploy a version of your contract solely for migration purposes, and run migration transactions on every row of your table until complete. If the first table is big, e.g. has a large number of rows, the transaction time limit could be reached while running the migration transactions. To mitigate this implement the migrate function to move a limited number of rows each time it runs. 2. Deploy a new contract using only the new version of the table, at which point, your migration and downtime is complete. [[caution]] diff --git a/docs/05_best-practices/05_securing_your_contract.md b/docs/07_best-practices/05_securing_your_contract.md similarity index 100% rename from docs/05_best-practices/05_securing_your_contract.md rename to docs/07_best-practices/05_securing_your_contract.md diff --git a/docs/05_best-practices/07_error_handling.md b/docs/07_best-practices/07_error_handling.md similarity index 100% rename from docs/05_best-practices/07_error_handling.md rename to docs/07_best-practices/07_error_handling.md diff --git a/docs/05_best-practices/08_abi/00_understanding-abi-files.md b/docs/07_best-practices/08_abi/00_understanding-abi-files.md similarity index 99% rename from docs/05_best-practices/08_abi/00_understanding-abi-files.md rename to docs/07_best-practices/08_abi/00_understanding-abi-files.md index 245a52e181..1cb7e9995e 100644 --- a/docs/05_best-practices/08_abi/00_understanding-abi-files.md +++ b/docs/07_best-practices/08_abi/00_understanding-abi-files.md @@ -35,7 +35,7 @@ Start with an empty ABI, for exemplification we will work based on the `eosio.to An ABI enables any client or interface to interpret and even generate a GUI for your contract. For this to work consistently, describe the custom types that are used as a parameter in any public action or struct that needs to be described in the ABI. [[info | Built-in Types]] -| EOSIO implements a number of custom built-ins. Built-in types don't need to be described in an ABI file. If you would like to familiarize yourself with EOSIO's built-ins, they are defined [here](https://github.com/EOSIO/eos/blob/release/2.1.x/libraries/chain/abi_serializer.cpp#L85) +| EOSIO implements a number of custom built-ins. Built-in types don't need to be described in an ABI file. If you would like to familiarize yourself with EOSIO's built-ins, they are defined [here](https://github.com/EOSIO/eos/blob/76565937064d5acccb089b50aa8ea797cd92dc82/libraries/chain/abi_serializer.cpp#L90) ```json diff --git a/docs/05_best-practices/08_abi/01_abi-code-generator-attributes-explained.md b/docs/07_best-practices/08_abi/01_abi-code-generator-attributes-explained.md similarity index 74% rename from docs/05_best-practices/08_abi/01_abi-code-generator-attributes-explained.md rename to docs/07_best-practices/08_abi/01_abi-code-generator-attributes-explained.md index f4931df137..737b9276ac 100644 --- a/docs/05_best-practices/08_abi/01_abi-code-generator-attributes-explained.md +++ b/docs/07_best-practices/08_abi/01_abi-code-generator-attributes-explained.md @@ -93,3 +93,24 @@ extern "C" { The code above will mark a function declaration as being a WebAssembly import. This allows for other compilation modes to specify which functions are import only (i.e. do not link) without having to maintain a secondary file with duplicate declarations. +## [[eosio::action, eosio::read-only]] +The `read-only` attribute marks a method which has been defined as an action as a read-only action. + +Example: + +```cpp +[[eosio::action, eosio::read-only]] +std::vector get() { + std::vector ret; + // retrieve blockchain state and populate the ret vector + return ret; +} +``` + +Contract actions tagged read-only: +* Cannot call insert/update (write) functions on the `Multi-Index API`, nor the `Key Value API`. +* Cannot call `deferred transactions`. +* Cannot call `inline actions`. +* The amount of data returned by read-only queries is limited by the action return value size. By default these are set to 256 bytes by `default_max_action_return_value_size`. + +The `eosio-cpp` and `eosio-cc` tools will generate an error and terminate compilation if an action tagged read-only attempts to call insert/update (write) functions, `deferred transactions` or `inline actions`. However, if the command-line override option `--warn-action-read-only` is used, the `eosio-cpp` and `eosio-cc` tools will issue a warning and continue compilation. diff --git a/docs/05_best-practices/08_abi/02_manually_write_an_ABI_file_explained.md b/docs/07_best-practices/08_abi/02_manually_write_an_ABI_file_explained.md similarity index 100% rename from docs/05_best-practices/08_abi/02_manually_write_an_ABI_file_explained.md rename to docs/07_best-practices/08_abi/02_manually_write_an_ABI_file_explained.md diff --git a/docs/05_best-practices/08_abi/index.md b/docs/07_best-practices/08_abi/index.md similarity index 100% rename from docs/05_best-practices/08_abi/index.md rename to docs/07_best-practices/08_abi/index.md diff --git a/docs/05_best-practices/11_debugging_a_smart_contract.md b/docs/07_best-practices/11_debugging_a_smart_contract.md similarity index 100% rename from docs/05_best-practices/11_debugging_a_smart_contract.md rename to docs/07_best-practices/11_debugging_a_smart_contract.md diff --git a/docs/05_best-practices/naming-conventions-format.png b/docs/07_best-practices/naming-conventions-format.png similarity index 100% rename from docs/05_best-practices/naming-conventions-format.png rename to docs/07_best-practices/naming-conventions-format.png diff --git a/docs/08_troubleshooting.md b/docs/08_troubleshooting/index.md similarity index 88% rename from docs/08_troubleshooting.md rename to docs/08_troubleshooting/index.md index e8da1593a9..b16eb7f621 100644 --- a/docs/08_troubleshooting.md +++ b/docs/08_troubleshooting/index.md @@ -61,9 +61,9 @@ Error 3160009: No wasm file found __Possible solution__: Verify that `abi` and `wasm` files exist in the directory specified in the `cleos set contract` command, and that their names match the directory name. -## Action triggers ram charge which cannot be initiated from a notification. +## Action triggers a ram charge which cannot be initiated from a notification -__Possible solution__: The reason for this error is because the notification action doesn't have authorization to buy the needed RAM. In the context of multi index tables, there’s a table payer and a row payer. Only the contract can modify rows. The contract can create rows with a payer that didn’t authorize the action if the total amount of ram charged that payer doesn’t increase (e.g. delete a row and add another with the same payer). The table payer can’t change until the last row is deleted. For the purposes of billing, a table is identified by the tuple `contract, scope, table`. When you create a row for a `contract, scope, table` tuple that doesn’t exist, you create a table with the same payer. This can outlive the original row which created it, if other rows were created with that combination and this prevents the original payer from getting their ram back. Secondary indexes throw in more complexity since they use the lower 4 bits of the table name, producing additional `contract, scope, table` tuples combinations. Key takeaway: payer is about billing, not access control +__Possible solution__: This error happens because the notification action has no authorization to buy the needed RAM. In the context of multi-index tables, there is a table payer and a row payer. Only the contract can modify rows. The contract can create rows with a payer that did not authorize the action if the total amount of ram charged does not increase (e.g. delete a row and add another with the same payer). The table payer can not change until the last row is deleted. For the purposes of billing, a table is identified by the tuple `contract, scope, table`. When you create a row for a `contract, scope, table` tuple that doesn’t exist, you create a table with the same payer. This can outlive the original row which created it, if other rows were created with that combination and this prevents the original payer from getting their ram back. Secondary indexes throw in more complexity since they use the lower 4 bits of the table name, producing additional `contract, scope, table` tuples combinations. Key takeaway: payer is about billing, not access control ## You successfully re-deployed the contract code, but when you query the table you get the custom message that you coded when the table is not initialized (doesn't exist), or the system error message below in case you do not have code that checks first if table exist @@ -74,11 +74,11 @@ assertion failure with message: singleton does not exist pending console output: ``` -__Possible solution__: It is possible that you changed the table name? That is the first, of `eosio::name` type, parameter which you passed to the `eosio::template` type alias definition. Or did you change the table structure definition at all? If you need to change the table structure definition there are some limitations and a couple of ways to do it which are explained in the [Data Design and Migration](./05_best-practices/04_data-design-and-migration.md) section. +__Possible solution__: It is possible that you changed the table name? That is the first, of `eosio::name` type, parameter which you passed to the `eosio::template` type alias definition. Or did you change the table structure definition at all? If you need to change the table structure definition there are some limitations and a couple of ways to do it which are explained in the [Data Design and Migration](./07_best-practices/04_data-design-and-migration.md) section. ## You successfully re-deployed the contract code, but when you query the table you get the fields of the row values swapped, that is, it appears the values stored in table rows are the same only that they are swapped between fields/columns -__Possible solution__: It is possible that you changed the order of the fields the table struct definition? If you change the order of the table struct definition, if the swapped fields have the same type you will see the data in the fields correctly, however if the types of the fields are different the results could be of something undefined. If you need to change the table structure definition there are some limitations and a couple of ways to do it which are explained in the [Data Design and Migration](./05_best-practices/04_data-design-and-migration.md) section. +__Possible solution__: It is possible that you changed the order of the fields the table struct definition? If you change the order of the table struct definition, if the swapped fields have the same type you will see the data in the fields correctly, however if the types of the fields are different the results could be of something undefined. If you need to change the table structure definition there are some limitations and a couple of ways to do it which are explained in the [Data Design and Migration](./07_best-practices/04_data-design-and-migration.md) section. ## You successfully re-deployed the contract code, but when you query the table you get a parse error, like the one below, or the returned data seems to be garbage @@ -87,7 +87,7 @@ error 2019-09-26T07:05:54.825 thread-0 main.cpp:3449 main Couldn't parse type_name ``` -__Possible solution__: It is possible that you changed the type of the fields for the table struct definition? If you need to change the table structure definition there are some limitations and a couple of ways to do it which are explained in the [Data Design and Migration](./05_best-practices/04_data-design-and-migration.md) section. +__Possible solution__: It is possible that you changed the type of the fields for the table struct definition? If you need to change the table structure definition there are some limitations and a couple of ways to do it which are explained in the [Data Design and Migration](./07_best-practices/04_data-design-and-migration.md) section. ## eosio-cpp process never completes diff --git a/docs/09_tutorials/01_binary-extension.md b/docs/09_tutorials/01_binary-extension.md index c149089790..5c7b291794 100644 --- a/docs/09_tutorials/01_binary-extension.md +++ b/docs/09_tutorials/01_binary-extension.md @@ -323,7 +323,7 @@ struct [[eosio::table]] structure { } ``` -And their corresponding sections in the `.abi` files: +Find below their corresponding sections in the `.abi` files: **binary_extension_contract.abi** diff --git a/docs/09_tutorials/02_abi-variants.md b/docs/09_tutorials/02_abi-variants.md index 2d286c4d39..d085b2dcaf 100644 --- a/docs/09_tutorials/02_abi-variants.md +++ b/docs/09_tutorials/02_abi-variants.md @@ -3,10 +3,11 @@ content_title: ABI variants link_text: ABI variants --- -ABI variants give the flexibility of using more than one type for a defined variable or data member. -In EOSIO, the variants make use of the standard template library `variant` which was introduced in C++ 17. An instance of `std::variant` at any given time either holds a value of one of its alternative types, or in the case of error - no value. Because of this trait, variants can be used to build the multi index table structure with flexibility. Used in conjunction with ABI extensions, it allows for modification of the structure of an exiting multi index table, a.k.a. table. +ABI variants give the flexibility of using more than one type for a defined variable or data member. -## Use variant when building the multi index table the first time +In EOSIO, the variants use the standard template library `variant` which was introduced in C++ 17. An instance of `std::variant` at any given time either holds a value of one of its alternative types, or in the case of error - no value. Because of this trait, variants can be used to build the multi-index table structure with flexibility. Used in conjunction with ABI extensions, it allows for modification of the structure of an existing multi-index table, a.k.a. table. + +## Use variant when building the multi-index table the first time To define a `variant` for your table structure one example is shown below @@ -45,9 +46,9 @@ class [[eosio::contract]] multi_index_example : public contract { } }; - typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_tables; + typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_table_t; - test_tables testtab; + test_table_t testtab; [[eosio::action]] void set(name user); [[eosio::action]] void print( name user ); @@ -89,9 +90,9 @@ class [[eosio::contract]] multi_index_example : public contract { } }; - typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_tables; + typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_table_t; - test_tables testtab; + test_table_t testtab; [[eosio::action]] void set(name user); [[eosio::action]] void print( name user ); @@ -101,9 +102,9 @@ class [[eosio::contract]] multi_index_example : public contract { }; ``` -Now you can deploy the contract and it will be backwards compatible with the previous existing multi index table. +Now you can deploy the contract and it will be backwards compatible with the previous existing multi-index table. -## Use variant when changing an already deployed multi index table +## Use variant when changing an already deployed multi-index table ### Preconditions - It is assumed you deployed the contract defined in [this section](../06_how-to-guides/40_multi-index/how-to-instantiate-a-multi-index-table.md) and now you are going to change its table structure. @@ -141,9 +142,9 @@ class [[eosio::contract]] multi_index_example : public contract { + } }; - typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_tables; + typedef eosio::multi_index<"testtaba"_n, test_table, eosio::indexed_by<"secid"_n, eosio::const_mem_fun>> test_table_t; - test_tables testtab; + test_table_t testtab; [[eosio::action]] void set(name user); [[eosio::action]] void print( name user ); diff --git a/docs/09_tutorials/03_create-an-abi-file.md b/docs/09_tutorials/03_create-an-abi-file.md index 42537a1ebc..92820e1540 100644 --- a/docs/09_tutorials/03_create-an-abi-file.md +++ b/docs/09_tutorials/03_create-an-abi-file.md @@ -4,14 +4,14 @@ content_title: Create an ABI File ## Overview -This tutorial provides instructions on how to create an ABI file. +This tutorial provides instructions to create an ABI file. [[warning]] | As of v1.2.0, the eosio.wasmsdk was decoupled from the core repository. This change has introduced an eosio-cpp regression where the legacy eosio-abigen is no longer bundled with eosio-cpp. Until a new ABI generator is introduced, you will need to hand-write your ABI files. ## Introduction -The Application Binary Interface (ABI) is a JSON-based description on how to convert user actions between their JSON and Binary representations. The ABI also describes how to convert the database state to/from JSON. Once you have described your contract via an ABI then developers and users will be able to interact with your contract seamlessly via JSON. +The Application Binary Interface (ABI) is a JSON-based description to convert user actions between their JSON and Binary representations. The ABI also describes how to convert the database state to/from JSON. Once you have described your contract via an ABI then developers and users will be able to interact with your contract seamlessly via JSON. This tutorial will use the [eosio.token](https://github.com/EOSIO/eosio.contracts/tree/master/eosio.token) contract as an example. *eosio.token contract does not cover every possible permutation of an ABI definition. diff --git a/docs/index.md b/docs/index.md index 57d2dcbd1d..560cca49cd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,3 +23,4 @@ There's been a round of braking changes, if you are upgrading from older version ## Important See LICENSE for copyright and license terms. Block.one makes its contribution on a voluntary basis as a member of the EOSIO community and is not responsible for ensuring the overall performance of the software or any related applications. We make no representation, warranty, guarantee or undertaking in respect of the software or any related documentation, whether expressed or implied, including but not limited to the warranties or merchantability, fitness for a particular purpose and noninfringement. In no event shall we be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or documentation or the use or other dealings in the software or documentation. Any test results or performance figures are indicative and will not reflect performance under all conditions. Any reference to any third party or third-party product, service or other resource is not an endorsement or recommendation by Block.one. We are not responsible, and disclaim any and all responsibility and liability, for your use of or reliance on any of these resources. Third-party resources may be updated, changed or terminated at any time, so the information here may be out of date or inaccurate. + diff --git a/eosio_llvm b/eosio_llvm index 423a4ac7a7..d700c78db1 160000 --- a/eosio_llvm +++ b/eosio_llvm @@ -1 +1 @@ -Subproject commit 423a4ac7a7074e139c4f4c2f786d6b71d1b0965e +Subproject commit d700c78db1918c7a2783c37a97d42d6fcd223e4d diff --git a/libraries/eosiolib/capi/eosio/security_group.h b/libraries/eosiolib/capi/eosio/security_group.h new file mode 100644 index 0000000000..9e362c63e6 --- /dev/null +++ b/libraries/eosiolib/capi/eosio/security_group.h @@ -0,0 +1,56 @@ +#pragma once +#include "types.h" +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Propose new participants to the security group. + * + * @param data - the buffer containing the packed participants. + * @param datalen - size of the packed participants + * @pre `data` is a valid pointer to a range of memory at least `datalen` bytes long that contains packed participants data + * + * @return -1 if proposing a new security group was unsuccessful, otherwise returns 0. +*/ +__attribute__((eosio_wasm_import)) +int64_t add_security_group_participants(const char* data, uint32_t datalen); + +/** + * Propose to remove participants from the security group. + * + * @param data - the buffer containing the packed participants. + * @param datalen - size of the packed participants + * @pre `data` is a valid pointer to a range of memory at least `datalen` bytes long that contains packed participants data + * + * @return -1 if proposing a new security group was unsuccessful, otherwise returns 0. +*/ +__attribute__((eosio_wasm_import)) +int64_t remove_security_group_participants(const char* data, uint32_t datalen); + +/** + * Check if the specified accounts are all in the active security group. + * + * @param data - the buffer containing the packed participants. + * @param datalen - size of the packed participants + * + * @return Returns true if the specified accounts are all in the active security group. +*/ +__attribute__((eosio_wasm_import)) +bool in_active_security_group(const char* data, uint32_t datalen); + +/** + * Gets the active security group + * + * @param[out] data - the output buffer containing the packed security group. + * @param datalen - size of the `data` buffer + * + * @return Returns the size required in the buffer (if the buffer is too small, nothing is written). + * +*/ +__attribute__((eosio_wasm_import)) +uint32_t get_active_security_group(char* data, uint32_t datalen); + +#ifdef __cplusplus +} +#endif diff --git a/libraries/eosiolib/contracts/eosio/security_group.hpp b/libraries/eosiolib/contracts/eosio/security_group.hpp new file mode 100644 index 0000000000..b5575b8835 --- /dev/null +++ b/libraries/eosiolib/contracts/eosio/security_group.hpp @@ -0,0 +1,86 @@ +#pragma once +#include +#include "../../core/eosio/name.hpp" +#include "../../core/eosio/serialize.hpp" + +namespace eosio { + +namespace internal_use_do_not_use { +extern "C" { +__attribute__((eosio_wasm_import)) int64_t add_security_group_participants(const char* data, uint32_t datalen); + +__attribute__((eosio_wasm_import)) int64_t remove_security_group_participants(const char* data, uint32_t datalen); + +__attribute__((eosio_wasm_import)) bool in_active_security_group(const char* data, uint32_t datalen); + +__attribute__((eosio_wasm_import)) uint32_t get_active_security_group(char* data, uint32_t datalen); +} +} // namespace internal_use_do_not_use + +/** + * @defgroup security_group Security Group + * @ingroup contracts + * @brief Defines C++ security group API + */ + +struct security_group { + uint32_t version; + std::set participants; + CDT_REFLECT(version, participants); +}; + +/** + * Propose new participants to the security group. + * + * @ingroup security_group + * @param participants - the participants. + * + * @return -1 if proposing a new security group was unsuccessful, otherwise returns 0. + */ +inline int64_t add_security_group_participants(const std::set& participants) { + auto packed_participants = eosio::pack( participants ); + return internal_use_do_not_use::add_security_group_participants( packed_participants.data(), packed_participants.size() ); +} + +/** + * Propose to remove participants from the security group. + *å + * @ingroup security_group + * @param participants - the participants. + *å + * @return -1 if proposing a new security group was unsuccessful, otherwise returns 0. + */ +inline int64_t remove_security_group_participants(const std::set& participants){ + auto packed_participants = eosio::pack( participants ); + return internal_use_do_not_use::remove_security_group_participants( packed_participants.data(), packed_participants.size() ); +} + +/** + * Check if the specified accounts are all in the active security group. + * + * @ingroup security_group + * @param participants - the participants. + * + * @return Returns true if the specified accounts are all in the active security group. + */ +inline bool in_active_security_group(const std::set& participants){ + auto packed_participants = eosio::pack( participants ); + return internal_use_do_not_use::in_active_security_group( packed_participants.data(), packed_participants.size() ); +} + +/** + * Gets the active security group + * + * @ingroup security_group + * @param[out] packed_security_group - the buffer containing the packed security_group. + * + * @return Returns the size required in the buffer (if the buffer is too small, nothing is written). + * + */ +inline security_group get_active_security_group() { + size_t buffer_size = internal_use_do_not_use::get_active_security_group(0, 0); + std::vector buffer(buffer_size); + internal_use_do_not_use::get_active_security_group(buffer.data(), buffer_size); + return eosio::unpack(buffer); +} +} // namespace eosio \ No newline at end of file diff --git a/libraries/eosiolib/contracts/eosio/system.hpp b/libraries/eosiolib/contracts/eosio/system.hpp index 5c5c21926b..84eb1ebb5b 100644 --- a/libraries/eosiolib/contracts/eosio/system.hpp +++ b/libraries/eosiolib/contracts/eosio/system.hpp @@ -33,19 +33,28 @@ namespace eosio { */ /** - * This method will abort execution of wasm without failing the contract. This is used to bypass all cleanup / destructors that would normally be called. + * This method will abort execution of wasm without failing the contract. + * This is used to bypass all cleanup / destructors that would normally be + * called. * +

+ WARNING: this method will immediately abort execution of wasm code that is on + the stack and would be executed as the method normally returned. + Problems can occur with write-caches, RAII, reference counting + when this method aborts execution of wasm code immediately. +

* @ingroup system + * * @param code - the exit code - * Example: + * Example: * - * @code - * eosio_exit(0); - * eosio_exit(1); - * eosio_exit(2); - * eosio_exit(3); - * @endcode - */ + * @code + * eosio_exit(0); + * eosio_exit(1); + * eosio_exit(2); + * eosio_exit(3); + * @endcode + */ inline void eosio_exit( int32_t code ) { internal_use_do_not_use::eosio_exit(code); } diff --git a/libraries/native/intrinsics.cpp b/libraries/native/intrinsics.cpp index 4ad6d335e7..86d956077f 100644 --- a/libraries/native/intrinsics.cpp +++ b/libraries/native/intrinsics.cpp @@ -892,4 +892,21 @@ extern "C" { eosio_assert(false, "abort"); } #pragma clang diagnostic pop + + int64_t add_security_group_participants(const char* data, uint32_t datalen) { + return intrinsics::get().call(data, datalen); + } + + int64_t remove_security_group_participants(const char* data, uint32_t datalen){ + return intrinsics::get().call(data, datalen); + } + + bool in_active_security_group(const char* data, uint32_t datalen){ + return intrinsics::get().call(data, datalen); + } + + uint32_t get_active_security_group(char* data, uint32_t datalen){ + return intrinsics::get().call(data, datalen); + } + } diff --git a/libraries/native/native/eosio/intrinsics_def.hpp b/libraries/native/native/eosio/intrinsics_def.hpp index cca4667242..772a0141c0 100644 --- a/libraries/native/native/eosio/intrinsics_def.hpp +++ b/libraries/native/native/eosio/intrinsics_def.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -159,7 +160,12 @@ intrinsic_macro(send_deferred) \ intrinsic_macro(cancel_deferred) \ intrinsic_macro(get_context_free_data) \ intrinsic_macro(get_sender) \ -intrinsic_macro(set_action_return_value) +intrinsic_macro(set_action_return_value) \ +intrinsic_macro(add_security_group_participants) \ +intrinsic_macro(remove_security_group_participants) \ +intrinsic_macro(in_active_security_group) \ +intrinsic_macro(get_active_security_group) + #define CREATE_ENUM(name) \ name, diff --git a/scripts/generate_bottle.sh b/scripts/generate_bottle.sh index 12a1db9b1f..343c6608d7 100644 --- a/scripts/generate_bottle.sh +++ b/scripts/generate_bottle.sh @@ -31,6 +31,8 @@ export SSUBPREFIX hash=`openssl dgst -sha256 ${NAME}.tar.gz | awk 'NF>1{print $NF}'` echo "class EosioCdt < Formula + # typed: false + # frozen_string_literal: true homepage \"${URL}\" revision 0 @@ -48,13 +50,13 @@ echo "class EosioCdt < Formula depends_on \"doxygen\" => :build depends_on \"graphviz\" => :build depends_on \"lcov\" => :build - depends_on :xcode => :build - depends_on :macos => :high_sierra - depends_on :arch => :intel + depends_on xcode: :build + depends_on macos: :mojave + depends_on arch: :intel bottle do root_url \"https://github.com/eosio/eosio.cdt/releases/download/v${VERSION}\" - sha256 \"${hash}\" => :${MAC_VERSION} + sha256 ${MAC_VERSION}: \"${hash}\" end def install raise \"Error, only supporting binary packages at this time\" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9b1e7fb946..c254fbbe64 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -34,6 +34,10 @@ add_unit_test( varint_tests ) add_test( NAME toolchain_tests COMMAND ${CMAKE_BINARY_DIR}/tools/toolchain-tester/toolchain-tester ${CMAKE_SOURCE_DIR}/tests/toolchain --cdt ${CMAKE_BINARY_DIR}/bin ) set_property(TEST toolchain_tests PROPERTY LABELS toolchain_tests) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unit/version_tests.sh ${CMAKE_BINARY_DIR}/tests/unit/version_tests.sh COPYONLY) +add_test(NAME version_tests COMMAND ${CMAKE_BINARY_DIR}/tests/unit/version_tests.sh "${VERSION_FULL}" WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +set_property(TEST version_tests PROPERTY LABELS unit_tests) + if (eosio_FOUND) add_test(integration_tests ${CMAKE_BINARY_DIR}/tests/integration/integration_tests) set_property(TEST integration_tests PROPERTY LABELS integration_tests) diff --git a/tests/toolchain/compile-fail/hf_indirect_call_tests.cpp b/tests/toolchain/compile-fail/hf_indirect_call_tests.cpp new file mode 100644 index 0000000000..befb8c98b6 --- /dev/null +++ b/tests/toolchain/compile-fail/hf_indirect_call_tests.cpp @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +/* + tests which create aliases and indirect calls for host functions. + at least one host function called within a C++ lambda. It can be a trivial lambda which simply calls the host function and nothing else. + at least one host function "renamed" with a using directive and called via the alternative name. + at least one host function "renamed" with a #define and called using that alternative name. + Possibly in two different versions, one which #defines just the function name and one which #defines both the function name and the parameters passed to it. + Yes, both versions I think. It may not be functionally different to the compiler but it looks different to a human, so let's try it. + And finally, combine all four in a chain of aliases +*/ + +extern "C" __attribute__((eosio_wasm_import)) void set_resource_limit(int64_t, int64_t, int64_t); +extern "C" __attribute__((eosio_wasm_import)) void foo_bar(int64_t, int64_t, int64_t); + +#define ACTION_TYPE [[eosio::action, eosio::read_only]] + +using func = void (*)(int64_t, int64_t, int64_t); + +func srl_g1 = set_resource_limit; +func srl_g2 = set_resource_limit; +func srl_g3; +func fb_g = foo_bar; + +class [[eosio::contract]] hf_indirect_call_tests : public eosio::contract { +public: + using contract::contract; + func srl_m1 = set_resource_limit; + func srl_m2 = set_resource_limit; + func srl_m3; + func fb_m = foo_bar; + + ACTION_TYPE + bool tlambda() { + auto set_rsc = [](){ + set_resource_limit(0, 0, 0); + }; + set_rsc(); + return true; + } + + ACTION_TYPE + bool tlambdap() { + auto set_rsc = [](int64_t a, int64_t b, int64_t c){ + set_resource_limit(a, b, c); + }; + set_rsc(0, 0, 0); + return true; + } + + ACTION_TYPE + bool tlambdap2(int64_t a, int64_t b, int64_t c) { + auto set_rsc = [=](int64_t x, int64_t y, int64_t z){ + set_resource_limit(x, y, z); + }; + set_rsc(a, b, c); + return true; + } + + ACTION_TYPE + bool talias () { + func srl = set_resource_limit; + srl(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasg1() { + srl_g1(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasg2 () { + srl_g2 = foo_bar; + srl_g2(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasg3 () { + srl_g3 = set_resource_limit; + srl_g3(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasg3re1 () { + srl_g3 = foo_bar; + srl_g3(0,0,0); + srl_g3 = set_resource_limit; + srl_g3(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasg3re2 () { + srl_g3 = set_resource_limit; + srl_g3 = foo_bar; + srl_g3(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasg3re3 () { + srl_g3 = foo_bar; + srl_g3 = set_resource_limit; + srl_g3(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasgr () { + fb_g(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasm1 () { + srl_m1(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasm2 () { + srl_m2 = foo_bar; + srl_m2(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasm3 () { + srl_m3 = set_resource_limit; + srl_m3(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasm3re1 () { + srl_m3 = foo_bar; + srl_m3(0,0,0); + srl_m3 = set_resource_limit; + srl_m3(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasm3re2 () { + srl_m3 = set_resource_limit; + srl_m3 = foo_bar; + srl_m3(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasm3re3 () { + srl_m3 = foo_bar; + srl_m3 = set_resource_limit; + srl_m3(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasmr () { + fb_m(0,0,0); + return true; + } + + ACTION_TYPE + bool taliasma () { + func srl_a1, srl_a2 = set_resource_limit; + srl_a2(0,0,0); + } + + #define setfun set_resource_limit + ACTION_TYPE + bool tdefine(){ + setfun(0,0,0); + return true; + } + + #define setfunp() set_resource_limit(0,0,0) + ACTION_TYPE + bool tdefinep(){ + setfunp(); + return true; + } + + #define setfunpi(a, b, c) set_resource_limit(a,b,c) + ACTION_TYPE + bool tdefinepi(){ + setfunpi(0,0,0); + return true; + } + + ACTION_TYPE + bool combine(){ + func srl = set_resource_limit; + srl(0,0,0); + setfun(0,0,0); + setfunp(); + setfunpi(0,0,0); + return true; + } + + ACTION_TYPE + bool combinea(){ + tlambdap(); + tlambdap2(0,0,0); + talias(); + tdefine(); + tdefinep(); + tdefinepi(); + combine(); + return true; + } + + ACTION_TYPE + bool combinec(){ + auto set_rsc = [](){ + func _srl = set_resource_limit; + #define mysrl _srl + mysrl(0, 0, 0); + }; + set_rsc(); + return true; + } + + ACTION_TYPE + bool combinecp(){ + auto set_rsc = [](int64_t a, int64_t b, int64_t c){ + func _srl = set_resource_limit; + #define mysrl _srl + mysrl(a, b, c); + }; + set_rsc(0,0,0); + return true; + } +}; diff --git a/tests/toolchain/compile-fail/hf_indirect_call_tests.json b/tests/toolchain/compile-fail/hf_indirect_call_tests.json new file mode 100644 index 0000000000..eb74c58a93 --- /dev/null +++ b/tests/toolchain/compile-fail/hf_indirect_call_tests.json @@ -0,0 +1,11 @@ +{ + "tests" : [ + { + "compile_flags": [], + "expected" : { + "exit-code": 255, + "stderr": "(codegen error.*){20}" + } + } + ] +} diff --git a/tests/toolchain/compile-fail/host_functions_tests.cpp b/tests/toolchain/compile-fail/host_functions_tests.cpp new file mode 100644 index 0000000000..a8cad07a11 --- /dev/null +++ b/tests/toolchain/compile-fail/host_functions_tests.cpp @@ -0,0 +1,283 @@ +/* host functions which are not allowed to use in read only query contract, so the functions should never return true. it should compile failed (compile time) or throw exception(run time). +set_resource_limits : yes +set_wasm_parameters_packed : yes +set_resource_limit : yes +set_proposed_producers : yes +set_proposed_producers_ex : yes +set_blockchain_parameters_packed : yes +set_parameters_packed : yes +set_kv_parameters_packed : yes +set_privileged : yes +kv_erase : yes +kv_set : yes +send_deferred : yes +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +extern "C" __attribute__((eosio_wasm_import)) void set_resource_limit(int64_t, int64_t, int64_t); +extern "C" __attribute__((eosio_wasm_import)) uint32_t get_kv_parameters_packed(void* params, uint32_t size, uint32_t max_version); +extern "C" __attribute__((eosio_wasm_import)) void set_kv_parameters_packed(const char* params, uint32_t size); +extern "C" __attribute__((eosio_wasm_import)) void set_blockchain_parameters_packed( char* data, uint32_t datalen ); +extern "C" __attribute__((eosio_wasm_import)) uint32_t get_blockchain_parameters_packed( char* data, uint32_t datalen ); + +typedef uint64_t capi_name; +extern "C" __attribute__((eosio_wasm_import)) int32_t db_store_i64(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const void* data, uint32_t len); +extern "C" __attribute__((eosio_wasm_import)) void db_update_i64(int32_t iterator, capi_name payer, const void* data, uint32_t len); +extern "C" __attribute__((eosio_wasm_import)) void db_remove_i64(int32_t iterator); +extern "C" __attribute__((eosio_wasm_import)) int32_t db_idx64_store(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const uint64_t* secondary); +extern "C" __attribute__((eosio_wasm_import)) void db_idx64_update(int32_t iterator, capi_name payer, const uint64_t* secondary); +extern "C" __attribute__((eosio_wasm_import)) void db_idx64_remove(int32_t iterator); +extern "C" __attribute__((eosio_wasm_import)) int32_t db_idx128_store(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const uint128_t* secondary); +extern "C" __attribute__((eosio_wasm_import)) void db_idx128_update(int32_t iterator, capi_name payer, const uint128_t* secondary); +extern "C" __attribute__((eosio_wasm_import)) void db_idx128_remove(int32_t iterator); +extern "C" __attribute__((eosio_wasm_import)) int32_t db_idx256_store(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const uint128_t* data, uint32_t data_len ); +extern "C" __attribute__((eosio_wasm_import)) void db_idx256_update(int32_t iterator, capi_name payer, const uint128_t* data, uint32_t data_len); +extern "C" __attribute__((eosio_wasm_import)) void db_idx256_remove(int32_t iterator); +extern "C" __attribute__((eosio_wasm_import)) int32_t db_idx_double_store(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const double* secondary); +extern "C" __attribute__((eosio_wasm_import)) void db_idx_double_update(int32_t iterator, capi_name payer, const double* secondary); +extern "C" __attribute__((eosio_wasm_import)) void db_idx_double_remove(int32_t iterator); +extern "C" __attribute__((eosio_wasm_import)) int32_t db_idx_long_double_store(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const long double* secondary); +extern "C" __attribute__((eosio_wasm_import)) void db_idx_long_double_update(int32_t iterator, capi_name payer, const long double* secondary); +extern "C" __attribute__((eosio_wasm_import)) void db_idx_long_double_remove(int32_t iterator); + +extern "C" __attribute__((eosio_wasm_import)) int64_t kv_erase(uint64_t contract, const char* key, uint32_t key_size); +extern "C" __attribute__((eosio_wasm_import)) int64_t kv_set(uint64_t contract, const char* key, uint32_t key_size, const char* value, uint32_t value_size, uint64_t payer); +extern "C" __attribute__((eosio_wasm_import)) void send_deferred(const uint128_t&, uint64_t, const char*, size_t, uint32_t); +extern "C" __attribute__((eosio_wasm_import)) int64_t set_proposed_producers( char*, uint32_t ); +extern "C" __attribute__((eosio_wasm_import)) int64_t set_proposed_producers_ex( uint64_t producer_data_format, char *producer_data, uint32_t producer_data_size ); +extern "C" __attribute__((eosio_wasm_import)) void set_wasm_parameters_packed(const void*, std::size_t); +extern "C" __attribute__((eosio_wasm_import)) void set_parameters_packed( const char* params, uint32_t params_size ); + +extern "C" __attribute__((eosio_wasm_import)) void send_inline(char *serialized_action, size_t size); +extern "C" __attribute__((eosio_wasm_import)) void send_context_free_inline(char *serialized_action, size_t size); + +#define ACTION_TYPE [[eosio::action, eosio::read_only]] + +class [[eosio::contract]] host_functions_tests : public eosio::contract { +public: + using contract::contract; + + ACTION_TYPE + bool resource() { + int64_t ram_bytes; + int64_t net_weight; + int64_t cpu_weight; + get_resource_limits( "eosio"_n, ram_bytes, net_weight, cpu_weight ) ; + eosio::cout << "Get resource: ram_bytes=" << ram_bytes << " net_weight=" << net_weight << " cpu_weight=" << cpu_weight << " \n"; + set_resource_limits( "eosio"_n, ram_bytes , net_weight , cpu_weight ); + get_resource_limits( "eosio"_n, ram_bytes, net_weight, cpu_weight ) ; + eosio::cout << "Get resource: ram_bytes=" << ram_bytes << " net_weight=" << net_weight << " cpu_weight=" << cpu_weight << " \n"; + return true; + } + ACTION_TYPE + bool setrelimit () { + int64_t ram_bytes; + int64_t net_weight; + int64_t cpu_weight; + get_resource_limits( "eosio"_n, ram_bytes, net_weight, cpu_weight ) ; + eosio::cout << "Get resource: ram_bytes=" << ram_bytes << " net_weight=" << net_weight << " cpu_weight=" << cpu_weight << " \n"; + set_resource_limit( "eosio"_n.value, "ram"_n.value , ram_bytes ); + get_resource_limits( "eosio"_n, ram_bytes, net_weight, cpu_weight ) ; + eosio::cout << "Get resource: ram_bytes=" << ram_bytes << " net_weight=" << net_weight << " cpu_weight=" << cpu_weight << " \n"; + return true; + } + ACTION_TYPE + bool bcpara () { + char buf[sizeof(eosio::blockchain_parameters)]; + size_t size = get_blockchain_parameters_packed( buf, sizeof(buf) ); + eosio::cout << "Block chain parameter size : " << size << "\n"; + set_blockchain_parameters_packed(buf, size); + return true; + } + ACTION_TYPE + bool setkvpara(){ + uint32_t limits[4]; + limits[0] = 0; + limits[1] = 1024; + limits[2] = 4096; + limits[3] = 1024; + char limits_buf[sizeof(limits)]; + memcpy(limits_buf, limits, sizeof(limits)); + set_kv_parameters_packed(limits_buf, sizeof(limits)); + return true; + } + ACTION_TYPE + bool setpriv() { + bool ispr = is_privileged("eosio"_n); + eosio::cout << "eosio is privileged : " << ispr << "\n"; + set_privileged("eosio"_n, ispr); + return true; + } +/* all tested +db_store_i64 +db_update_i64 +db_remove_i64 +db_idx64_store +db_idx64_update +db_idx64_remove +db_idx128_store +db_idx128_update +db_idx128_remove +db_idx256_store +db_idx256_update +db_idx256_remove +db_idx_double_store +db_idx_double_update +db_idx_double_remove +db_idx_long_double_store +db_idx_long_double_update +db_idx_long_double_remove +*/ +// abcde means 67890 a4 means 64 12c means 128 so as to no conflict with naming rule +// Name should be less than 13 characters and only contains the following symbol 12345abcdefghijklmnopqrstuvwxyz + ACTION_TYPE + bool dbia4s(){ + db_store_i64(0, 0, 0, 0, NULL, 0); + return true; + } + ACTION_TYPE + bool dbia4u(){ + db_update_i64(0, 0, NULL, 0); + return true; + } + ACTION_TYPE + bool dbia4r(){ + db_remove_i64(0); + return true; + } + ACTION_TYPE + bool dbidxa4s() { + db_idx64_store(0, 0, 0, 0, NULL); + return true; + } + ACTION_TYPE + bool dbidxa4u() { + db_idx64_update(0, 0, NULL); + return true; + } + ACTION_TYPE + bool dbidxa4r() { + db_idx64_remove(0); + return true; + } + ACTION_TYPE + bool dbidx12cs() { + db_idx128_store(0, 0, 0, 0, NULL); + return true; + } + ACTION_TYPE + bool dbidx12cu() { + db_idx128_update(0, 0, NULL); + return true; + } + ACTION_TYPE + bool dbidx12cr() { + db_idx128_remove(0); + return true; + } + ACTION_TYPE + bool dbidx25as() { + db_idx256_store(0, 0, 0, 0, NULL, 0); + return true; + } + ACTION_TYPE + bool dbidx25au() { + db_idx256_update(0, 0, NULL, 0); + return true; + } + ACTION_TYPE + bool dbidx25ar() { + db_idx256_remove(0); + return true; + } + ACTION_TYPE + bool dbidxdbs(){ + db_idx_double_store(0, 0, 0, 0, NULL); + return true; + } + ACTION_TYPE + bool dbidxdbu(){ + db_idx_double_update(0, 0, NULL); + return true; + } + ACTION_TYPE + bool dbidxdbr(){ + db_idx_double_remove(0); + return true; + } + ACTION_TYPE + bool dbidxldbs (){ + db_idx_long_double_store(0, 0, 0, 0, NULL); + return true; + } + ACTION_TYPE + bool dbidxldbu(){ + db_idx_long_double_update(0, 0, NULL); + return true; + } + ACTION_TYPE + bool dbidxldbr(){ + db_idx_long_double_remove(0); + return true; + } + ACTION_TYPE + bool kverase(){ + kv_erase(0, NULL, 0); + return true; + } + ACTION_TYPE + bool kvset(){ + kv_set(0, NULL, 0, NULL, 0, 0); + return true; + } + ACTION_TYPE + bool senddefer(){ + send_deferred(0, 0, NULL, 0, 0); + return true; + } + ACTION_TYPE + bool setpp(){ + set_proposed_producers(NULL, 0); + return true; + } + ACTION_TYPE + bool setppex(){ + set_proposed_producers_ex( 0, NULL, 0 ); + return true; + } + ACTION_TYPE + bool swpp(){ + set_wasm_parameters_packed(NULL, 0); + return true; + } + ACTION_TYPE + bool spp(){ + set_parameters_packed( NULL, 0 ); + return true; + } + ACTION_TYPE + bool sendil(){ + send_inline(NULL, 0); + return true; + } + ACTION_TYPE + bool sendcfiil(){ + send_context_free_inline(NULL, 0); + return true; + } +}; diff --git a/tests/toolchain/compile-fail/host_functions_tests.json b/tests/toolchain/compile-fail/host_functions_tests.json new file mode 100644 index 0000000000..1d02388a63 --- /dev/null +++ b/tests/toolchain/compile-fail/host_functions_tests.json @@ -0,0 +1,11 @@ +{ + "tests" : [ + { + "compile_flags": [], + "expected" : { + "exit-code": 255, + "stderr": "(codegen error.*){20}" + } + } + ] +} diff --git a/tests/toolchain/compile-pass/warn_action_read_only.cpp b/tests/toolchain/compile-pass/warn_action_read_only.cpp new file mode 100644 index 0000000000..5cc31b607e --- /dev/null +++ b/tests/toolchain/compile-pass/warn_action_read_only.cpp @@ -0,0 +1,15 @@ +#include +#include + +using namespace eosio; +extern "C" __attribute__((weak)) __attribute__((eosio_wasm_import)) void set_resource_limit(int64_t, int64_t, int64_t); + +class [[eosio::contract]] warn_action_read_only : public contract { + public: + using contract::contract; + + [[eosio::action, eosio::read_only]] + void test1( name user ) { + set_resource_limit(user.value, 0, 0); + } +}; diff --git a/tests/toolchain/compile-pass/warn_action_read_only.json b/tests/toolchain/compile-pass/warn_action_read_only.json new file mode 100644 index 0000000000..0710b598c2 --- /dev/null +++ b/tests/toolchain/compile-pass/warn_action_read_only.json @@ -0,0 +1,11 @@ +{ + "tests" : [ + { + "compile_flags": ["--warn-action-read-only"], + "expected" : { + "exit-code": 0, + "stderr": "codegen warning" + } + } + ] +} diff --git a/tests/unit/name_tests.cpp b/tests/unit/name_tests.cpp index 464a9bf855..09b23b06e6 100644 --- a/tests/unit/name_tests.cpp +++ b/tests/unit/name_tests.cpp @@ -14,11 +14,10 @@ using std::string; using eosio::name; -static constexpr uint64_t u64min = numeric_limits::min(); // 0ULL -static constexpr uint64_t u64max = numeric_limits::max(); // 18446744073709551615ULL +constexpr uint64_t u64max = numeric_limits::max(); // 18446744073709551615ULL // Definitions in `eosio.cdt/libraries/eosio/name.hpp` -EOSIO_TEST_BEGIN(name_type_test) +EOSIO_TEST_BEGIN(name_type_test_ctr_num) //// constexpr name() CHECK_EQUAL( name{}.value, 0ULL ) @@ -26,12 +25,17 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{0ULL}.value, 0ULL ) CHECK_EQUAL( name{1ULL}.value, 1ULL ) CHECK_EQUAL( name{u64max}.value, u64max ) - //// constexpr explicit name(name::raw) CHECK_EQUAL( name{name::raw{0ULL}}.value, 0ULL ) CHECK_EQUAL( name{name::raw{1ULL}}.value, 1ULL ) CHECK_EQUAL( name{name::raw{u64max}}.value, u64max ) + // test that constexpr constructor is evaluated at compile time + static_assert( name{0ULL}.value == 0ULL ); + static_assert( name{name::raw{1ULL}}.value == 1ULL ); +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(name_type_test_ctr_str_lit) //// constexpr explicit name(string_view) // Note: // These are the exact `uint64_t` value representations of the given string @@ -48,7 +52,6 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{"123."}.value, 614178399182651392ULL ) CHECK_EQUAL( name{"123........."}.value, 614178399182651392ULL ) CHECK_EQUAL( name{".a.b.c.1.2.3."}.value, 108209673814966320ULL ) - CHECK_EQUAL( name{"abc.123"}.value, 3589369488740450304ULL ) CHECK_EQUAL( name{"123.abc"}.value, 614181822271586304ULL ) @@ -61,13 +64,21 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{"aaaaaaaaaaaaj"}.value, 3570337562653461615ULL ) CHECK_EQUAL( name{"zzzzzzzzzzzzj"}.value, u64max ) + // test that constexpr constructor is evaluated at compile time + static_assert( name{"1"}.value == 576460752303423488ULL ); +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(name_type_test_str_not_allowed) CHECK_ASSERT( "character is not in allowed character set for names", ([]() {name{"-1"};}) ) CHECK_ASSERT( "character is not in allowed character set for names", ([]() {name{"0"};}) ) CHECK_ASSERT( "character is not in allowed character set for names", ([]() {name{"6"};}) ) CHECK_ASSERT( "thirteenth character in name cannot be a letter that comes after j", ([]() {name{"111111111111k"};}) ) CHECK_ASSERT( "thirteenth character in name cannot be a letter that comes after j", ([]() {name{"zzzzzzzzzzzzk"};}) ) CHECK_ASSERT( "string is too long to be a valid name", ([]() {name{"12345abcdefghj"};}) ) +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(name_type_test_char_to_value) // -------------------------------------------- // static constexpr uint8_t char_to_value(char) char c{'.'}; @@ -84,7 +95,9 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name::char_to_value(c), expected_value ) ++expected_value; } +EOSIO_TEST_END +EOSIO_TEST_BEGIN(name_type_test_allowed_chars) CHECK_ASSERT( "character is not in allowed character set for names", ([]() {name::char_to_value(char{'-'});}) ) CHECK_ASSERT( "character is not in allowed character set for names", ([]() {name::char_to_value(char{'/'});}) ) CHECK_ASSERT( "character is not in allowed character set for names", ([]() {name::char_to_value(char{'6'});}) ) @@ -92,7 +105,9 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_ASSERT( "character is not in allowed character set for names", ([]() {name::char_to_value(char{'Z'});}) ) CHECK_ASSERT( "character is not in allowed character set for names", ([]() {name::char_to_value(char{'`'});}) ) CHECK_ASSERT( "character is not in allowed character set for names", ([]() {name::char_to_value(char{'{'});}) ); +EOSIO_TEST_END +EOSIO_TEST_BEGIN(name_type_test_str_len) // ------------------------------- // constexpr uint8_t length()cosnt CHECK_EQUAL( name{""}.length(), 0 ) @@ -109,9 +124,13 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{"eosioaccoun"}.length(), 11 ) CHECK_EQUAL( name{"eosioaccount"}.length(), 12 ) CHECK_EQUAL( name{"eosioaccountj"}.length(), 13 ) +EOSIO_TEST_END +EOSIO_TEST_BEGIN(name_type_test_str_too_long) CHECK_ASSERT( "string is too long to be a valid name", ([]() {name{"12345abcdefghj"}.length();}) ) +EOSIO_TEST_END +EOSIO_TEST_BEGIN(name_type_test_suffix) // ---------------------------- // constexpr name suffix()const CHECK_EQUAL( name{".eosioaccounj"}.suffix(), name{"eosioaccounj"} ) @@ -129,7 +148,10 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{"e.o.s.i.o.a.c"}.suffix(), name{"c"} ) CHECK_EQUAL( name{"eos.ioa.cco"}.suffix(), name{"cco"} ) +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(name_type_test_prefix) // ---------------------------- // constexpr name prefix()const @@ -154,6 +176,10 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{"a.my.account"}.prefix(), name{"a.my"} ) CHECK_EQUAL( name{"a.my.account"}.prefix().prefix(), name{"a"} ) + static_assert( name{"e.osioaccounj"}.prefix() == name{"e"} ); +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(name_type_test_raw) // ----------------------------- // constexpr operator raw()const CHECK_EQUAL( name{"1"}.operator name::raw(), static_cast(576460752303423488ULL) ) @@ -181,7 +207,10 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{"555555555555j"}.operator name::raw(), static_cast(2975281302211218015ULL) ) CHECK_EQUAL( name{"aaaaaaaaaaaaj"}.operator name::raw(), static_cast(3570337562653461615ULL) ) CHECK_EQUAL( name{"zzzzzzzzzzzzj"}.operator name::raw(), static_cast(u64max) ) +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(name_type_test_op_bool) // --------------------------------------- // constexpr explicit operator bool()const // Note that I must be explicit about calling the operator because it is defined as `explicit` @@ -195,9 +224,13 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( !name{""}.operator bool(), true ) CHECK_EQUAL( !name{"1"}.operator bool(), false ) + static_assert( name{0}.operator bool() == false ); +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(name_type_test_memcmp) // ---------------------------------------- // char* write_as_string(char*, char*)const - static constexpr uint8_t buffer_size{32}; + constexpr uint8_t buffer_size{32}; char buffer[buffer_size]{}; string str{"1"}; @@ -215,6 +248,7 @@ EOSIO_TEST_BEGIN(name_type_test) name{str = "123"}.write_as_string( buffer, buffer + sizeof(buffer) ); CHECK_EQUAL( memcmp(str.c_str(), buffer, strlen(str.c_str())), 0 ) + // Note: // Any '.' characters at the end of a name are ignored name{str = ".abc"}.write_as_string( buffer, buffer + sizeof(buffer) ); @@ -233,6 +267,7 @@ EOSIO_TEST_BEGIN(name_type_test) name{str = "123.abc"}.write_as_string( buffer, buffer + sizeof(buffer) ); CHECK_EQUAL( memcmp(str.c_str(), buffer, strlen(str.c_str())), 0 ) + name{str = "12345abcdefgj"}.write_as_string( buffer, buffer + sizeof(buffer) ); CHECK_EQUAL( memcmp(str.c_str(), buffer, strlen(str.c_str())), 0 ) name{str = "hijklmnopqrsj"}.write_as_string( buffer, buffer + sizeof(buffer) ); @@ -248,7 +283,9 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( memcmp(str.c_str(), buffer, strlen(str.c_str())), 0 ) name{str = "zzzzzzzzzzzzj"}.write_as_string( buffer, buffer + sizeof(buffer) ); CHECK_EQUAL( memcmp(str.c_str(), buffer, strlen(str.c_str())), 0 ) +EOSIO_TEST_END +EOSIO_TEST_BEGIN(name_type_test_to_str) // ----------------------- // string to_string()const CHECK_EQUAL( name{"1"}.to_string(), "1" ) @@ -276,7 +313,10 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{"555555555555j"}.to_string(), "555555555555j" ) CHECK_EQUAL( name{"aaaaaaaaaaaaj"}.to_string(), "aaaaaaaaaaaaj" ) CHECK_EQUAL( name{"zzzzzzzzzzzzj"}.to_string(), "zzzzzzzzzzzzj" ) +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(name_type_test_equal) // ---------------------------------------------------------- // friend constexpr bool operator==(const name&, const name&) CHECK_EQUAL( name{"1"} == name{"1"}, true ) @@ -305,6 +345,11 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{"aaaaaaaaaaaaj"} == name{"aaaaaaaaaaaaj"}, true ) CHECK_EQUAL( name{"zzzzzzzzzzzzj"} == name{"zzzzzzzzzzzzj"}, true ) + // test constexpr + static_assert( name{"1"} == name{"1"} ); +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(name_type_test_not_equal) // ----------------------------------------------------------- // friend constexpr bool operator!=(const name&, const name&) CHECK_EQUAL( name{"1"} != name{}, true ) @@ -333,6 +378,11 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{"aaaaaaaaaaaaj"} != name{}, true ) CHECK_EQUAL( name{"zzzzzzzzzzzzj"} != name{}, true ) + // test constexpr + static_assert( name{"1"} != name{"2"} ); +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(name_type_test_less_than) // --------------------------------------------------------- // friend constexpr bool operator<(const name&, const name&) CHECK_EQUAL( name{} < name{"1"}, true ) @@ -361,6 +411,12 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{} < name{"aaaaaaaaaaaaj"}, true ) CHECK_EQUAL( name{} < name{"zzzzzzzzzzzzj"}, true ) + // test constexpr + static_assert( name{} < name{"1"} ); +EOSIO_TEST_END + + +EOSIO_TEST_BEGIN(name_type_test_op_n) // ------------------------------------ // inline constexpr name operator""_n() CHECK_EQUAL( name{}, ""_n ) @@ -390,6 +446,9 @@ EOSIO_TEST_BEGIN(name_type_test) CHECK_EQUAL( name{"555555555555j"}, "555555555555j"_n ) CHECK_EQUAL( name{"aaaaaaaaaaaaj"}, "aaaaaaaaaaaaj"_n ) CHECK_EQUAL( name{"zzzzzzzzzzzzj"}, "zzzzzzzzzzzzj"_n ) + + // test constexpr + static_assert( name{"1"} == "1"_n ); EOSIO_TEST_END int main(int argc, char* argv[]) { @@ -399,6 +458,22 @@ int main(int argc, char* argv[]) { } silence_output(!verbose); - EOSIO_TEST(name_type_test); + EOSIO_TEST(name_type_test_ctr_num) + EOSIO_TEST(name_type_test_ctr_str_lit) + EOSIO_TEST(name_type_test_str_not_allowed) + EOSIO_TEST(name_type_test_char_to_value) + EOSIO_TEST(name_type_test_allowed_chars) + EOSIO_TEST(name_type_test_str_len) + EOSIO_TEST(name_type_test_suffix) + EOSIO_TEST(name_type_test_prefix) + EOSIO_TEST(name_type_test_raw) + EOSIO_TEST(name_type_test_op_bool) + EOSIO_TEST(name_type_test_memcmp) + EOSIO_TEST(name_type_test_to_str) + EOSIO_TEST(name_type_test_equal) + EOSIO_TEST(name_type_test_not_equal) + EOSIO_TEST(name_type_test_less_than) + EOSIO_TEST(name_type_test_op_n) + return has_failed(); } diff --git a/tests/unit/string_tests1.cpp b/tests/unit/string_tests1.cpp index 351784e80c..e62014622b 100644 --- a/tests/unit/string_tests1.cpp +++ b/tests/unit/string_tests1.cpp @@ -17,9 +17,9 @@ using eosio::string; //// template //// string(const char (&str)[N]) -EOSIO_TEST_BEGIN(string_test_1) - static const string eostr0{"a"}; - static const string eostr1{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_ctr_lit) + const string eostr0{"a"}; + const string eostr1{"abcdef"}; CHECK_EQUAL( eostr0.size(), 1 ) CHECK_EQUAL( eostr0.capacity(), 1 ) @@ -31,23 +31,23 @@ EOSIO_TEST_BEGIN(string_test_1) EOSIO_TEST_END //// string() -EOSIO_TEST_BEGIN(string_test_2) - static const string eostr{}; +EOSIO_TEST_BEGIN(string_test_ctr_def) + const string eostr{}; CHECK_EQUAL( eostr.size(), 0 ) CHECK_EQUAL( eostr.capacity(), 0 ) CHECK_EQUAL( strcmp(eostr.c_str(), ""), 0) EOSIO_TEST_END -//// constexpr string(const char* str, const size_t n) -EOSIO_TEST_BEGIN(string_test_3) - static const char* str0{""}; - static const char* str1{"abc"}; - static const char* str2{"abcdef"}; +//// const string(const char* str, const size_t n) +EOSIO_TEST_BEGIN(string_test_ctr_char_ptr) + const char* str0{""}; + const char* str1{"abc"}; + const char* str2{"abcdef"}; - static const string eostr0(str0, 0); - static const string eostr1(str1, 1); - static const string eostr2(str2, 6); + const string eostr0(str0, 0); + const string eostr1(str1, 1); + const string eostr2(str2, 6); CHECK_EQUAL( eostr0.size(), 0 ) CHECK_EQUAL( eostr0.capacity(), 0 ) @@ -63,10 +63,10 @@ EOSIO_TEST_BEGIN(string_test_3) EOSIO_TEST_END //// string(const size_t n, const char c) -EOSIO_TEST_BEGIN(string_test_4) - static const string eostr0(0, 'c'); - static const string eostr1(1, 'c'); - static const string eostr2(3, 'c'); +EOSIO_TEST_BEGIN(string_test_ctr_char_rep) + const string eostr0(0, 'c'); + const string eostr1(1, 'c'); + const string eostr2(3, 'c'); CHECK_EQUAL( eostr0.size(), 0 ) CHECK_EQUAL( eostr0.capacity(), 0 ) @@ -82,17 +82,17 @@ EOSIO_TEST_BEGIN(string_test_4) EOSIO_TEST_END //// string(const string& str, const size_t pos, const size_t n = string::npos) -EOSIO_TEST_BEGIN(string_test_5) - static const string eostr{"abcdef"}; - static const string eostr0_sub(eostr, 0, 0); - static const string eostr1_sub(eostr, 1, 0); - static const string eostr2_sub(eostr, 0, 1); - static const string eostr3_sub(eostr, 0, 3); - static const string eostr4_sub(eostr, 0, 8); - static const string eostr5_sub(eostr, 0, 7); - static const string eostr6_sub(eostr, 0, 6); - static const string eostr7_sub(eostr, 3, 3); - static const string eostr8_sub(eostr, 3, 2); +EOSIO_TEST_BEGIN(string_test_ctr_str_sub) + const string eostr{"abcdef"}; + const string eostr0_sub(eostr, 0, 0); + const string eostr1_sub(eostr, 1, 0); + const string eostr2_sub(eostr, 0, 1); + const string eostr3_sub(eostr, 0, 3); + const string eostr4_sub(eostr, 0, 8); + const string eostr5_sub(eostr, 0, 7); + const string eostr6_sub(eostr, 0, 6); + const string eostr7_sub(eostr, 3, 3); + const string eostr8_sub(eostr, 3, 2); CHECK_EQUAL( eostr0_sub.size(), 0 ) CHECK_EQUAL( eostr0_sub.capacity(), 0 ) @@ -131,14 +131,14 @@ EOSIO_TEST_BEGIN(string_test_5) CHECK_EQUAL( strcmp(eostr8_sub.c_str(), "de"), 0) EOSIO_TEST_END -//// constexpr string(const string& str) -EOSIO_TEST_BEGIN(string_test_6) - static const string eostr0{""}; - static const string eostr1{"a"}; - static const string eostr2{"abcdef"}; - static const string eostr0_cpy{eostr0}; - static const string eostr1_cpy{eostr1}; - static const string eostr2_cpy{eostr2}; +//// const string(const string& str) +EOSIO_TEST_BEGIN(string_test_ctr_cpy) + const string eostr0{""}; + const string eostr1{"a"}; + const string eostr2{"abcdef"}; + const string eostr0_cpy{eostr0}; + const string eostr1_cpy{eostr1}; + const string eostr2_cpy{eostr2}; CHECK_EQUAL( eostr0_cpy.size(), 0 ) CHECK_EQUAL( eostr0_cpy.capacity(), 0 ) @@ -153,7 +153,7 @@ EOSIO_TEST_BEGIN(string_test_6) CHECK_EQUAL( strcmp(eostr2_cpy.c_str(), "abcdef"), 0) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_7) +EOSIO_TEST_BEGIN(string_test_op_pl) static string eostr0{""}; eostr0 += "a"; static string eostr1{"abc"}; @@ -170,14 +170,14 @@ EOSIO_TEST_BEGIN(string_test_7) CHECK_EQUAL( strcmp(eostr1_cpy.c_str(), "abcdef"), 0) EOSIO_TEST_END -//// constexpr string(const string&& str) -EOSIO_TEST_BEGIN(string_test_8) - static string eostr0{""}; - static string eostr1{"a"}; - static string eostr2{"abcdef"}; - static const string eostr0_mv{move(eostr0)}; - static const string eostr1_mv{move(eostr1)}; - static const string eostr2_mv{move(eostr2)}; +//// const string(const string&& str) +EOSIO_TEST_BEGIN(string_test_ctr_mv) + const string eostr0{""}; + const string eostr1{"a"}; + const string eostr2{"abcdef"}; + const string eostr0_mv{move(eostr0)}; + const string eostr1_mv{move(eostr1)}; + const string eostr2_mv{move(eostr2)}; CHECK_EQUAL( eostr0_mv.size(), 0 ) CHECK_EQUAL( eostr0_mv.capacity(), 0 ) @@ -192,13 +192,13 @@ EOSIO_TEST_BEGIN(string_test_8) CHECK_EQUAL( strcmp(eostr2.c_str(), "abcdef"), 0) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_9) - static string eostr0{""}; +EOSIO_TEST_BEGIN(string_test_op_pl_ctr_mv) + string eostr0{""}; eostr0 += "a"; - static string eostr1{"abc"}; + string eostr1{"abc"}; eostr1 += "def"; - static string eostr0_cpy{move(eostr0)}; - static string eostr1_cpy{move(eostr1)}; + string eostr0_cpy{move(eostr0)}; + string eostr1_cpy{move(eostr1)}; CHECK_EQUAL( eostr0_cpy.size(), 1 ) CHECK_EQUAL( eostr0_cpy.capacity(), 2 ) @@ -210,13 +210,13 @@ EOSIO_TEST_BEGIN(string_test_9) EOSIO_TEST_END //// string& operator=(const string& str); -EOSIO_TEST_BEGIN(string_test_10) - static const string eostr0{""}; - static const string eostr1{"a"}; - static const string eostr2{"abcdef"}; - static string eostr0_cpy_assig{}; - static string eostr1_cpy_assig{}; - static string eostr2_cpy_assig{}; +EOSIO_TEST_BEGIN(string_test_op_asgn_1) + const string eostr0{""}; + const string eostr1{"a"}; + const string eostr2{"abcdef"}; + string eostr0_cpy_assig{}; + string eostr1_cpy_assig{}; + string eostr2_cpy_assig{}; eostr0_cpy_assig = eostr0; eostr1_cpy_assig = eostr1; eostr2_cpy_assig = eostr2; @@ -234,13 +234,13 @@ EOSIO_TEST_BEGIN(string_test_10) CHECK_EQUAL( strcmp(eostr2_cpy_assig.c_str(), "abcdef"), 0) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_11) - static string eostr0{""}; +EOSIO_TEST_BEGIN(string_test_op_pl_asgn) + string eostr0{""}; eostr0 += "a"; - static string eostr1{"abc"}; + string eostr1{"abc"}; eostr1 += "def"; - static string eostr0_cpy_assig{}; - static string eostr1_cpy_assig{}; + string eostr0_cpy_assig{}; + string eostr1_cpy_assig{}; eostr0_cpy_assig = eostr0; eostr1_cpy_assig = eostr1; @@ -254,13 +254,13 @@ EOSIO_TEST_BEGIN(string_test_11) EOSIO_TEST_END //// string& operator=(string&& str) -EOSIO_TEST_BEGIN(string_test_12) - static string eostr0{""}; - static string eostr1{"a"}; - static string eostr2{"abcdef"}; - static string eostr0_mv_assig{}; - static string eostr1_mv_assig{}; - static string eostr2_mv_assig{}; +EOSIO_TEST_BEGIN(string_test_mv_asgn) + string eostr0{""}; + string eostr1{"a"}; + string eostr2{"abcdef"}; + string eostr0_mv_assig{}; + string eostr1_mv_assig{}; + string eostr2_mv_assig{}; eostr0_mv_assig = move(eostr0); eostr1_mv_assig = move(eostr1); eostr2_mv_assig = move(eostr2); @@ -278,13 +278,13 @@ EOSIO_TEST_BEGIN(string_test_12) CHECK_EQUAL( strcmp(eostr2_mv_assig.c_str(), "abcdef"), 0) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_13) - static string eostr0{""}; +EOSIO_TEST_BEGIN(string_test_op_pl_mv) + string eostr0{""}; eostr0 += "a"; - static string eostr1{"abc"}; + string eostr1{"abc"}; eostr1 += "def"; - static string eostr0_mv_assig{}; - static string eostr1_mv_assig{}; + string eostr0_mv_assig{}; + string eostr1_mv_assig{}; eostr0_mv_assig = move(eostr0); eostr1_mv_assig = move(eostr1); @@ -298,8 +298,8 @@ EOSIO_TEST_BEGIN(string_test_13) EOSIO_TEST_END //// string& operator=(const char* str) -EOSIO_TEST_BEGIN(string_test_14) - static string eostr{}; +EOSIO_TEST_BEGIN(string_test_op_asgn_2) + string eostr{}; eostr = "abcdef"; CHECK_EQUAL( eostr.size(), 6 ) @@ -312,8 +312,8 @@ EOSIO_TEST_BEGIN(string_test_14) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdef"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_15) - static string eostr{}; +EOSIO_TEST_BEGIN(string_test_op_asgn_pl) + string eostr{}; eostr = ""; eostr += "abcdef"; @@ -328,46 +328,46 @@ EOSIO_TEST_BEGIN(string_test_15) EOSIO_TEST_END //// char& operator[](const size_t n) -EOSIO_TEST_BEGIN(string_test_16) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_char_eq) + string eostr{"abcdef"}; CHECK_EQUAL( eostr[0], 'a' ) CHECK_EQUAL( eostr[5], 'f' ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_17) - static string eostr{"abc"}; +EOSIO_TEST_BEGIN(string_test_char_eq_pl) + string eostr{"abc"}; eostr += "def"; CHECK_EQUAL( eostr[0], 'a' ) CHECK_EQUAL( eostr[5], 'f' ) EOSIO_TEST_END //// const char& operator[](const size_t n) const -EOSIO_TEST_BEGIN(string_test_18) - static const string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_char_eq_ctr) + const string eostr{"abcdef"}; CHECK_EQUAL( eostr[0], 'a' ) CHECK_EQUAL( eostr[5], 'f' ) EOSIO_TEST_END //// char& at(const size_t n) -EOSIO_TEST_BEGIN(string_test_19) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_char_eq_at_1) + string eostr{"abcdef"}; CHECK_EQUAL( eostr.at(0), 'a' ) CHECK_EQUAL( eostr.at(5), 'f' ) - CHECK_ASSERT( "eosio::string::at", []() {eostr.at(6);} ) + CHECK_ASSERT( "eosio::string::at", [&]() {eostr.at(6);} ) EOSIO_TEST_END //// const char& at(const size_t n) const -EOSIO_TEST_BEGIN(string_test_20) - static const string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_char_eq_at_2) + const string eostr{"abcdef"}; CHECK_EQUAL( eostr.at(0), 'a' ) CHECK_EQUAL( eostr.at(5), 'f' ) - CHECK_ASSERT( "eosio::string::at const", []() {eostr.at(6);} ) + CHECK_ASSERT( "eosio::string::at const", [&]() {eostr.at(6);} ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_21) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_char_eq_at_3) + string eostr{""}; eostr += "abcdef"; const char c0{eostr.at(0)}; const char c1{eostr.at(5)}; @@ -376,35 +376,35 @@ EOSIO_TEST_BEGIN(string_test_21) EOSIO_TEST_END //// char& front() -EOSIO_TEST_BEGIN(string_test_22) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_front_1) + const string eostr{"abcdef"}; CHECK_EQUAL( eostr.front(), 'a' ) - static string empty_str; + const string empty_str; CHECK_EQUAL( eostr.front(), 'a' ) EOSIO_TEST_END //// const char& front() const -EOSIO_TEST_BEGIN(string_test_23) - static const string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_front_2) + const string eostr{"abcdef"}; CHECK_EQUAL( eostr.front(), 'a' ) EOSIO_TEST_END //// char& back() -EOSIO_TEST_BEGIN(string_test_24) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_back_1) + const string eostr{"abcdef"}; CHECK_EQUAL( eostr.back(), 'f' ) EOSIO_TEST_END //// const char& back() const -EOSIO_TEST_BEGIN(string_test_25) - static const string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_back_2) + const string eostr{"abcdef"}; CHECK_EQUAL( eostr.back(), 'f' ) EOSIO_TEST_END //// char* data() -EOSIO_TEST_BEGIN(string_test_26) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_data_1) + string eostr{"abcdef"}; CHECK_EQUAL( strcmp(eostr.data(), "abcdef"), 0 ) eostr = "abc"; @@ -412,36 +412,36 @@ EOSIO_TEST_BEGIN(string_test_26) EOSIO_TEST_END //// const char* data() const -EOSIO_TEST_BEGIN(string_test_27) - static const string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_data_2) + const string eostr{"abcdef"}; CHECK_EQUAL( strcmp(eostr.data(), "abcdef"), 0 ) EOSIO_TEST_END //// const char* c_str() const -EOSIO_TEST_BEGIN(string_test_28) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_null_term_1) + const string eostr{"abcdef"}; CHECK_EQUAL( strcmp(eostr.c_str(), "abcdef"), 0 ) CHECK_EQUAL( eostr.c_str()[eostr.size()], '\0' ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_29) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_null_term_2) + string eostr{""}; eostr += "abcdef"; CHECK_EQUAL( strcmp(eostr.c_str(), "abcdef"), 0 ) CHECK_EQUAL( eostr.c_str()[eostr.size()], '\0' ) EOSIO_TEST_END //// char* begin() -EOSIO_TEST_BEGIN(string_test_30) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_iter_begin_1) + string eostr{"abcdef"}; char* iter{eostr.begin()}; CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( eostr.capacity(), 12 ) CHECK_EQUAL( strcmp(eostr.c_str(), iter), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_31) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_iter_begin_2) + string eostr{""}; eostr += "abcdef"; char* iter{eostr.begin()}; CHECK_EQUAL( eostr.size(), 6 ) @@ -450,8 +450,8 @@ EOSIO_TEST_BEGIN(string_test_31) EOSIO_TEST_END //// const char* cbegin() const -EOSIO_TEST_BEGIN(string_test_32) - static const string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_iter_cbegin) + string eostr{"abcdef"}; const char* iter{eostr.cbegin()}; CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( eostr.capacity(), 6 ) @@ -459,16 +459,16 @@ EOSIO_TEST_BEGIN(string_test_32) EOSIO_TEST_END //// char* end() -EOSIO_TEST_BEGIN(string_test_33) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_iter_end_1) + string eostr{"abcdef"}; char* iter{eostr.end()}; CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( eostr.capacity(), 12 ) CHECK_EQUAL( strcmp(eostr.c_str()+eostr.size(), iter), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_34) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_iter_end_2) + string eostr{""}; eostr += "abcdef"; char* iter{eostr.end()}; CHECK_EQUAL( eostr.size(), 6 ) @@ -477,8 +477,8 @@ EOSIO_TEST_BEGIN(string_test_34) EOSIO_TEST_END //// const char* cend() const -EOSIO_TEST_BEGIN(string_test_35) - static const string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_iter_cend) + string eostr{"abcdef"}; const char* iter{eostr.cend()}; CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( eostr.capacity(), 6 ) @@ -486,32 +486,32 @@ EOSIO_TEST_BEGIN(string_test_35) EOSIO_TEST_END //// bool string::empty() const -EOSIO_TEST_BEGIN(string_test_36) - static string eostr{}; +EOSIO_TEST_BEGIN(string_test_empty) + string eostr{}; CHECK_EQUAL( eostr.empty(), true ) eostr += 'c'; CHECK_EQUAL( eostr.empty(), false ) EOSIO_TEST_END //// size_t string::size() const -EOSIO_TEST_BEGIN(string_test_37) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_op_plus_char) + string eostr{"abcdef"}; CHECK_EQUAL( eostr.size(), 6 ) eostr += 'g'; CHECK_EQUAL( eostr.size(), 7 ) EOSIO_TEST_END //// size_t string::length() const -EOSIO_TEST_BEGIN(string_test_38) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_length) + string eostr{"abcdef"}; CHECK_EQUAL( eostr.length(), 6 ) eostr += 'g'; CHECK_EQUAL( eostr.length(), 7 ) EOSIO_TEST_END //// size_t string::capacity() const -EOSIO_TEST_BEGIN(string_test_39) - static string eostr{"abc"}; +EOSIO_TEST_BEGIN(string_test_capacity) + string eostr{"abc"}; CHECK_EQUAL( eostr.capacity(), 3 ) eostr += 'd', eostr += 'e', eostr += 'f'; CHECK_EQUAL( eostr.capacity(), 8 ) @@ -520,14 +520,14 @@ EOSIO_TEST_BEGIN(string_test_39) EOSIO_TEST_END //// size_t string::max_size() const -EOSIO_TEST_BEGIN(string_test_40) - static const string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_max_size) + const string eostr{"abcdef"}; CHECK_EQUAL( eostr.max_size(), string::npos ) EOSIO_TEST_END //// void reserve(const size_t n) -EOSIO_TEST_BEGIN(string_test_41) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_reserve_1) + string eostr{"abcdef"}; CHECK_EQUAL( eostr.capacity(), 6 ) eostr.reserve(10); CHECK_EQUAL( eostr.capacity(), 10 ) @@ -537,8 +537,8 @@ EOSIO_TEST_BEGIN(string_test_41) CHECK_EQUAL( eostr.capacity(), 24 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_42) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_reserve_2) + string eostr{""}; eostr += "abcdef"; CHECK_EQUAL( eostr.capacity(), 12 ) eostr.reserve(10); @@ -550,10 +550,10 @@ EOSIO_TEST_BEGIN(string_test_42) EOSIO_TEST_END //// void string::shrink_to_fit() const -EOSIO_TEST_BEGIN(string_test_43) - static string eostr0{}; - static string eostr1{"a"}; - static string eostr2{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_shrink_to_fit) + string eostr0{}; + string eostr1{"a"}; + string eostr2{"abcdef"}; CHECK_EQUAL( eostr0.capacity(), 0 ) eostr0.reserve(100); @@ -575,8 +575,8 @@ EOSIO_TEST_BEGIN(string_test_43) EOSIO_TEST_END //// void string::clear() -EOSIO_TEST_BEGIN(string_test_44) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_clear_1) + string eostr{"abcdef"}; CHECK_EQUAL( eostr.empty(), false ) eostr.clear(); CHECK_EQUAL( eostr.empty(), true ) @@ -584,8 +584,8 @@ EOSIO_TEST_BEGIN(string_test_44) CHECK_EQUAL( eostr.data()[0], '\0' ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_45) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_clear_2) + string eostr{""}; eostr += "abcdef"; CHECK_EQUAL( eostr.empty(), false ) eostr.clear(); @@ -595,8 +595,8 @@ EOSIO_TEST_BEGIN(string_test_45) EOSIO_TEST_END //// void resize(size_t n) -EOSIO_TEST_BEGIN(string_test_46) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_resize_1) + string eostr{"abcdef"}; eostr.resize(3); CHECK_EQUAL( eostr.size(), 3 ) @@ -614,8 +614,8 @@ EOSIO_TEST_BEGIN(string_test_46) CHECK_EQUAL( strcmp(eostr.c_str(), "abc"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_47) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_resize_2) + string eostr{""}; eostr += "abcdef"; eostr.resize(3); @@ -635,9 +635,9 @@ EOSIO_TEST_BEGIN(string_test_47) EOSIO_TEST_END //// void swap(string& str) -EOSIO_TEST_BEGIN(string_test_48) - static string eostr_swap0{"abc"}; - static string eostr_swap1{"123456"}; +EOSIO_TEST_BEGIN(string_test_swap) + string eostr_swap0{"abc"}; + string eostr_swap1{"123456"}; eostr_swap0.swap(eostr_swap1); @@ -651,8 +651,8 @@ EOSIO_TEST_BEGIN(string_test_48) EOSIO_TEST_END //// void push_back(char c) -EOSIO_TEST_BEGIN(string_test_49) - static string eostr{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_push_back) + string eostr{"abcdef"}; CHECK_EQUAL( eostr.size(), 6 ) eostr.push_back('g'); CHECK_EQUAL( eostr.size(), 7 ) @@ -661,16 +661,16 @@ EOSIO_TEST_BEGIN(string_test_49) EOSIO_TEST_END //// void pop_back() -EOSIO_TEST_BEGIN(string_test_50) - static string eostr{"abcdefg"}; +EOSIO_TEST_BEGIN(string_test_pop_back_1) + string eostr{"abcdefg"}; CHECK_EQUAL( eostr.size(), 7 ) eostr.pop_back(); CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdef"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_51) - static string eostr{"abc"}; +EOSIO_TEST_BEGIN(string_test_pop_back_2) + string eostr{"abc"}; CHECK_EQUAL( eostr.size(), 3 ) eostr.pop_back(); eostr.pop_back(); @@ -684,7 +684,7 @@ EOSIO_TEST_BEGIN(string_test_51) EOSIO_TEST_END //// string substr(size_t pos = 0, size_t len = npos) const -EOSIO_TEST_BEGIN(string_test_52) +EOSIO_TEST_BEGIN(string_test_substr_1) static const string eostr{"abcdef"}; CHECK_EQUAL( strcmp(eostr.substr(0).c_str(), "abcdef"), 0 ) CHECK_EQUAL( strcmp(eostr.substr(0,0).c_str(), ""), 0 ) @@ -704,8 +704,8 @@ EOSIO_TEST_BEGIN(string_test_52) CHECK_EQUAL( strcmp(eostr.substr(1,6).c_str(), "bcdef"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_53) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_substr_2) + string eostr{""}; eostr += "abcdef"; CHECK_EQUAL( strcmp(eostr.substr(0).c_str(), "abcdef"), 0 ) CHECK_EQUAL( strcmp(eostr.substr(0,0).c_str(), ""), 0 ) @@ -726,9 +726,9 @@ EOSIO_TEST_BEGIN(string_test_53) EOSIO_TEST_END //// size_t copy(char* dest, size_t len, size_t pos = 0) const -EOSIO_TEST_BEGIN(string_test_54) - static const string eostr{"abcdef"}; - static char str[7]{}; +EOSIO_TEST_BEGIN(string_test_copy_1) + const string eostr{"abcdef"}; + char str[7]{}; CHECK_EQUAL( eostr.copy(str, 0), 0 ) CHECK_EQUAL( strcmp(str, ""), 0 ) @@ -758,10 +758,10 @@ EOSIO_TEST_BEGIN(string_test_54) CHECK_EQUAL( strcmp(str, ""), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_55) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_copy_2) + string eostr{""}; eostr += "abcdef"; - static char str[7]{}; + char str[7]{}; CHECK_EQUAL( eostr.copy(str, 0), 0 ) CHECK_EQUAL( strcmp(str, ""), 0 ) @@ -791,69 +791,69 @@ EOSIO_TEST_BEGIN(string_test_55) CHECK_EQUAL( strcmp(str, ""), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_56) - static const string eostr{"abcdef"}; - static char str[7]{}; - CHECK_ASSERT( "eosio::string::copy", []() {eostr.copy(str, 1, eostr.size()+1);} ) +EOSIO_TEST_BEGIN(string_test_copy_3) + const string eostr{"abcdef"}; + char str[7]{}; + CHECK_ASSERT( "eosio::string::copy", [&]() {eostr.copy(str, 1, eostr.size()+1);} ) EOSIO_TEST_END //// string& insert(const size_t pos, const char* str) -EOSIO_TEST_BEGIN(string_test_57) - static string eostr{"iii"}; - static const char* str{"ooo"}; +EOSIO_TEST_BEGIN(string_test_ins_1) + string eostr{"iii"}; + const char* str{"ooo"}; eostr.insert(0, str); CHECK_EQUAL( strcmp(eostr.c_str(), "oooiii"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_58) - static string eostr{"iii"}; - static const char* str{"ooo"}; +EOSIO_TEST_BEGIN(string_test_ins_2) + string eostr{"iii"}; + const char* str{"ooo"}; eostr.insert(1, str); CHECK_EQUAL( strcmp(eostr.c_str(), "ioooii"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_59) - static string eostr{"iii"}; - static const char* str{"ooo"}; +EOSIO_TEST_BEGIN(string_test_ins_3) + string eostr{"iii"}; + const char* str{"ooo"}; eostr.insert(2, str); CHECK_EQUAL( strcmp(eostr.c_str(), "iioooi"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_60) - static string eostr{"iii"}; - static const char* str{"ooo"}; +EOSIO_TEST_BEGIN(string_test_ins_4) + string eostr{"iii"}; + const char* str{"ooo"}; eostr.insert(3, str); CHECK_EQUAL( strcmp(eostr.c_str(), "iiiooo"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_61) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_ins_5) + string eostr{""}; eostr += "iii"; - static const char* str{"ooo"}; + const char* str{"ooo"}; eostr.insert(0, str); CHECK_EQUAL( strcmp(eostr.c_str(), "oooiii"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_62) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_ins_6) + string eostr{""}; eostr += "iii"; - static const char* str{"ooo"}; + const char* str{"ooo"}; eostr.insert(1, str); CHECK_EQUAL( strcmp(eostr.c_str(), "ioooii"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_63) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_ins_7) + string eostr{""}; eostr += "iii"; - static const char* str{"ooo"}; + const char* str{"ooo"}; eostr.insert(2, str); CHECK_EQUAL( strcmp(eostr.c_str(), "iioooi"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_64) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_ins_8) + string eostr{""}; eostr += "iii"; - static const char* str{"ooo"}; + const char* str{"ooo"}; eostr.insert(3, str); CHECK_EQUAL( strcmp(eostr.c_str(), "iiiooo"), 0 ) EOSIO_TEST_END @@ -865,69 +865,70 @@ int main(int argc, char* argv[]) { } silence_output(!verbose); - EOSIO_TEST(string_test_1) - EOSIO_TEST(string_test_2) - EOSIO_TEST(string_test_3) - EOSIO_TEST(string_test_4) - EOSIO_TEST(string_test_5) - EOSIO_TEST(string_test_6) - EOSIO_TEST(string_test_7) - EOSIO_TEST(string_test_8) - EOSIO_TEST(string_test_9) - EOSIO_TEST(string_test_10) - EOSIO_TEST(string_test_11) - EOSIO_TEST(string_test_12) - EOSIO_TEST(string_test_13) - EOSIO_TEST(string_test_14) - EOSIO_TEST(string_test_15) - EOSIO_TEST(string_test_16) - EOSIO_TEST(string_test_17) - EOSIO_TEST(string_test_18) - EOSIO_TEST(string_test_19) - EOSIO_TEST(string_test_20) - EOSIO_TEST(string_test_21) - EOSIO_TEST(string_test_22) - EOSIO_TEST(string_test_23) - EOSIO_TEST(string_test_24) - EOSIO_TEST(string_test_25) - EOSIO_TEST(string_test_26) - EOSIO_TEST(string_test_27) - EOSIO_TEST(string_test_28) - EOSIO_TEST(string_test_29) - EOSIO_TEST(string_test_30) - EOSIO_TEST(string_test_31) - EOSIO_TEST(string_test_32) - EOSIO_TEST(string_test_33) - EOSIO_TEST(string_test_34) - EOSIO_TEST(string_test_35) - EOSIO_TEST(string_test_36) - EOSIO_TEST(string_test_37) - EOSIO_TEST(string_test_38) - EOSIO_TEST(string_test_39) - EOSIO_TEST(string_test_40) - EOSIO_TEST(string_test_41) - EOSIO_TEST(string_test_42) - EOSIO_TEST(string_test_43) - EOSIO_TEST(string_test_44) - EOSIO_TEST(string_test_45) - EOSIO_TEST(string_test_46) - EOSIO_TEST(string_test_47) - EOSIO_TEST(string_test_48) - EOSIO_TEST(string_test_49) - EOSIO_TEST(string_test_50) - EOSIO_TEST(string_test_51) - EOSIO_TEST(string_test_52) - EOSIO_TEST(string_test_53) - EOSIO_TEST(string_test_54) - EOSIO_TEST(string_test_55) - EOSIO_TEST(string_test_56) - EOSIO_TEST(string_test_57) - EOSIO_TEST(string_test_58) - EOSIO_TEST(string_test_59) - EOSIO_TEST(string_test_60) - EOSIO_TEST(string_test_61) - EOSIO_TEST(string_test_62) - EOSIO_TEST(string_test_63) - EOSIO_TEST(string_test_64) + EOSIO_TEST(string_test_ctr_lit) + EOSIO_TEST(string_test_ctr_def) + EOSIO_TEST(string_test_ctr_char_ptr) + EOSIO_TEST(string_test_ctr_char_rep) + EOSIO_TEST(string_test_ctr_str_sub) + EOSIO_TEST(string_test_ctr_cpy) + EOSIO_TEST(string_test_op_pl) + EOSIO_TEST(string_test_ctr_mv) + EOSIO_TEST(string_test_op_pl_ctr_mv) + EOSIO_TEST(string_test_op_asgn_1) + EOSIO_TEST(string_test_op_pl_asgn) + EOSIO_TEST(string_test_mv_asgn) + EOSIO_TEST(string_test_op_pl_mv) + EOSIO_TEST(string_test_op_asgn_2) + EOSIO_TEST(string_test_op_asgn_pl) + EOSIO_TEST(string_test_char_eq) + EOSIO_TEST(string_test_char_eq_pl) + EOSIO_TEST(string_test_char_eq_ctr) + EOSIO_TEST(string_test_char_eq_at_1) + EOSIO_TEST(string_test_char_eq_at_2) + EOSIO_TEST(string_test_char_eq_at_3) + EOSIO_TEST(string_test_front_1) + EOSIO_TEST(string_test_front_2) + EOSIO_TEST(string_test_back_1) + EOSIO_TEST(string_test_back_2) + EOSIO_TEST(string_test_data_1) + EOSIO_TEST(string_test_data_2) + EOSIO_TEST(string_test_null_term_1) + EOSIO_TEST(string_test_null_term_2) + EOSIO_TEST(string_test_iter_begin_1) + EOSIO_TEST(string_test_iter_begin_2) + EOSIO_TEST(string_test_iter_cbegin) + EOSIO_TEST(string_test_iter_end_1) + EOSIO_TEST(string_test_iter_end_2) + EOSIO_TEST(string_test_iter_cend) + EOSIO_TEST(string_test_empty) + EOSIO_TEST(string_test_op_plus_char) + EOSIO_TEST(string_test_length) + EOSIO_TEST(string_test_capacity) + EOSIO_TEST(string_test_max_size) + EOSIO_TEST(string_test_reserve_1) + EOSIO_TEST(string_test_reserve_2) + EOSIO_TEST(string_test_shrink_to_fit) + EOSIO_TEST(string_test_clear_1) + EOSIO_TEST(string_test_clear_2) + EOSIO_TEST(string_test_resize_1) + EOSIO_TEST(string_test_resize_2) + EOSIO_TEST(string_test_swap) + EOSIO_TEST(string_test_push_back) + EOSIO_TEST(string_test_pop_back_1) + EOSIO_TEST(string_test_pop_back_2) + EOSIO_TEST(string_test_substr_1) + EOSIO_TEST(string_test_substr_2) + EOSIO_TEST(string_test_copy_1) + EOSIO_TEST(string_test_copy_2) + EOSIO_TEST(string_test_copy_3) + EOSIO_TEST(string_test_ins_1) + EOSIO_TEST(string_test_ins_2) + EOSIO_TEST(string_test_ins_3) + EOSIO_TEST(string_test_ins_4) + EOSIO_TEST(string_test_ins_5) + EOSIO_TEST(string_test_ins_6) + EOSIO_TEST(string_test_ins_7) + EOSIO_TEST(string_test_ins_8) + return has_failed(); } diff --git a/tests/unit/string_tests2.cpp b/tests/unit/string_tests2.cpp index cd75b80b8d..a13fcd7cb3 100644 --- a/tests/unit/string_tests2.cpp +++ b/tests/unit/string_tests2.cpp @@ -15,86 +15,86 @@ using eosio::string; // Definitions found in `eosio.cdt/libraries/eosiolib/core/eosio/string.hpp` -EOSIO_TEST_BEGIN(string_test_65) - static string eostr{"abcdefg"}; - static const char* null_man{nullptr}; - CHECK_ASSERT( "eosio::string::insert", []() {eostr.insert(0, null_man, 1);} ) - CHECK_ASSERT( "eosio::string::insert", []() {eostr.insert(-1, "ooo", 1);} ) +EOSIO_TEST_BEGIN(string_test_ins_null) + string eostr{"abcdefg"}; + const char* null_man{nullptr}; + CHECK_ASSERT( "eosio::string::insert", [&]() {eostr.insert(0, null_man, 1);} ) + CHECK_ASSERT( "eosio::string::insert", [&]() {eostr.insert(-1, "ooo", 1);} ) EOSIO_TEST_END //// string& insert(const size_t pos, const string& str) -EOSIO_TEST_BEGIN(string_test_66) - static string eostr{}; - static const string str{"ooo"}; +EOSIO_TEST_BEGIN(string_test_ins_to_blank) + string eostr{}; + const string str{"ooo"}; eostr.insert(0, str); CHECK_EQUAL( eostr.size(), 3 ) CHECK_EQUAL( eostr.capacity(), 6 ) CHECK_EQUAL( strcmp(eostr.c_str(), "ooo"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_67) - static string eostr{"abc"}; - static const string str{"d"}; +EOSIO_TEST_BEGIN(string_test_ins_at_bgn_single) + string eostr{"abc"}; + const string str{"d"}; eostr.insert(0, str); CHECK_EQUAL( eostr.size(), 4 ) CHECK_EQUAL( eostr.capacity(), 8 ) CHECK_EQUAL( strcmp(eostr.c_str(), "dabc"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_68) - static string eostr{"abc"}; - static const string str{"def"}; +EOSIO_TEST_BEGIN(string_test_ins_at_bgn_mul_1) + string eostr{"abc"}; + const string str{"def"}; eostr.insert(0, str); CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( eostr.capacity(), 12 ) CHECK_EQUAL( strcmp(eostr.c_str(), "defabc"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_69) - static string eostr{"iii"}; - static const string str{"ooo"}; +EOSIO_TEST_BEGIN(string_test_ins_at_bgn_mul_2) + string eostr{"iii"}; + const string str{"ooo"}; eostr.insert(0, str); CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( eostr.capacity(), 12 ) CHECK_EQUAL( strcmp(eostr.c_str(), "oooiii") , 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_70) - static string eostr{"iii"}; - static const string str{"ooo"}; +EOSIO_TEST_BEGIN(string_test_ins_in_middle_1) + string eostr{"iii"}; + const string str{"ooo"}; eostr.insert(1, str); CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( eostr.capacity(), 12 ) CHECK_EQUAL( strcmp(eostr.c_str(), "ioooii") , 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_71) - static string eostr{"iii"}; - static const string str{"ooo"}; +EOSIO_TEST_BEGIN(string_test_ins_in_middle_2) + string eostr{"iii"}; + const string str{"ooo"}; eostr.insert(2, str); CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( eostr.capacity(), 12 ) CHECK_EQUAL( strcmp(eostr.c_str(), "iioooi") , 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_72) - static string eostr{"iii"}; - static const string str{"ooo"}; +EOSIO_TEST_BEGIN(string_test_ins_at_end) + string eostr{"iii"}; + const string str{"ooo"}; eostr.insert(3, str); CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( eostr.capacity(), 12 ) CHECK_EQUAL( strcmp(eostr.c_str(), "iiiooo") , 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_73) - static string eostr{"abcdefg"}; - static const string str{"ooo"}; - CHECK_ASSERT( "eosio::string::insert", []() {eostr.insert(-1, str);} ) +EOSIO_TEST_BEGIN(string_test_ins_neg_index_1) + string eostr{"abcdefg"}; + const string str{"ooo"}; + CHECK_ASSERT( "eosio::string::insert", [&]() {eostr.insert(-1, str);} ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_74) - static string eostr{""}; - static string str{""}; +EOSIO_TEST_BEGIN(string_test_ins_op_pl_1) + string eostr{""}; + string str{""}; str += "ooo"; eostr.insert(0, str); CHECK_EQUAL( eostr.size(), 3 ) @@ -102,10 +102,10 @@ EOSIO_TEST_BEGIN(string_test_74) CHECK_EQUAL( strcmp(eostr.c_str(), "ooo"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_75) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_ins_op_pl_2) + string eostr{""}; eostr += "abc"; - static string str{""}; + string str{""}; str += "d"; eostr.insert(0, str); CHECK_EQUAL( eostr.size(), 4 ) @@ -113,10 +113,10 @@ EOSIO_TEST_BEGIN(string_test_75) CHECK_EQUAL( strcmp(eostr.c_str(), "dabc"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_76) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_ins_op_pl_3) + string eostr{""}; eostr += "abc"; - static string str{""}; + string str{""}; str += "def"; eostr.insert(0, str); CHECK_EQUAL( eostr.size(), 6 ) @@ -124,10 +124,10 @@ EOSIO_TEST_BEGIN(string_test_76) CHECK_EQUAL( strcmp(eostr.c_str(), "defabc"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_77) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_ins_op_pl_4) + string eostr{""}; eostr += "iii"; - static string str{""}; + string str{""}; str += "ooo"; eostr.insert(0, str); CHECK_EQUAL( eostr.size(), 6 ) @@ -135,10 +135,10 @@ EOSIO_TEST_BEGIN(string_test_77) CHECK_EQUAL( strcmp(eostr.c_str(), "oooiii") , 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_78) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_ins_op_pl_5) + string eostr{""}; eostr += "iii"; - static string str{""}; + string str{""}; str += "ooo"; eostr.insert(1, str); CHECK_EQUAL( eostr.size(), 6 ) @@ -146,10 +146,10 @@ EOSIO_TEST_BEGIN(string_test_78) CHECK_EQUAL( strcmp(eostr.c_str(), "ioooii") , 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_79) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_ins_op_pl_6) + string eostr{""}; eostr += "iii"; - static string str{""}; + string str{""}; str += "ooo"; eostr.insert(2, str); CHECK_EQUAL( eostr.size(), 6 ) @@ -157,10 +157,10 @@ EOSIO_TEST_BEGIN(string_test_79) CHECK_EQUAL( strcmp(eostr.c_str(), "iioooi") , 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_80) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_ins_op_pl_7) + string eostr{""}; eostr += "iii"; - static string str{""}; + string str{""}; str += "ooo"; eostr.insert(3, str); CHECK_EQUAL( eostr.size(), 6 ) @@ -168,14 +168,14 @@ EOSIO_TEST_BEGIN(string_test_80) CHECK_EQUAL( strcmp(eostr.c_str(), "iiiooo") , 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_81) - static string eostr{"abcdefg"}; - static string str{"ooo"}; - CHECK_ASSERT( "eosio::string::insert", []() {eostr.insert(-1, str);} ) +EOSIO_TEST_BEGIN(string_test_ins_neg_index_2) + string eostr{"abcdefg"}; + string str{"ooo"}; + CHECK_ASSERT( "eosio::string::insert", [&]() {eostr.insert(-1, str);} ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_82) - static string eostr = "hello"; +EOSIO_TEST_BEGIN(string_test_ins_capacity) + string eostr = "hello"; eostr.insert(0, "0", 1); /// `_capacity` is now 12; `_begin` now holds `std::unique_ptr` CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( eostr.capacity(), 12 ) @@ -188,97 +188,97 @@ EOSIO_TEST_BEGIN(string_test_82) EOSIO_TEST_END //// string& erase(size_t pos = 0, size_t len = npos) -EOSIO_TEST_BEGIN(string_test_83) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase) + string eostr{"abcdefgh"}; eostr.erase(); CHECK_EQUAL( eostr.size(), 0 ) CHECK_EQUAL( strcmp(eostr.c_str(), ""), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_84) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_at_zero) + string eostr{"abcdefgh"}; eostr.erase(0); CHECK_EQUAL( eostr.size(), 0 ) CHECK_EQUAL( strcmp(eostr.c_str(), ""), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_85) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_to_npos) + string eostr{"abcdefgh"}; eostr.erase(0, string::npos); CHECK_EQUAL( eostr.size(), 0 ) CHECK_EQUAL( strcmp(eostr.c_str(), ""), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_86) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_1) + string eostr{"abcdefgh"}; eostr.erase(1, string::npos); CHECK_EQUAL( eostr.size(), 1 ) CHECK_EQUAL( strcmp(eostr.c_str(), "a"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_87) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_2) + string eostr{"abcdefgh"}; eostr.erase(2, string::npos); CHECK_EQUAL( eostr.size(), 2 ) CHECK_EQUAL( strcmp(eostr.c_str(), "ab"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_88) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_3) + string eostr{"abcdefgh"}; eostr.erase(3, string::npos); CHECK_EQUAL( eostr.size(), 3 ) CHECK_EQUAL( strcmp(eostr.c_str(), "abc"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_89) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_4) + string eostr{"abcdefgh"}; eostr.erase(4, string::npos); CHECK_EQUAL( eostr.size(), 4 ) CHECK_EQUAL( strcmp(eostr.c_str(), "abcd"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_90) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_5) + string eostr{"abcdefgh"}; eostr.erase(5, string::npos); CHECK_EQUAL( eostr.size(), 5 ) CHECK_EQUAL( strcmp(eostr.c_str(), "abcde"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_91) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_6) + string eostr{"abcdefgh"}; eostr.erase(6, string::npos); CHECK_EQUAL( eostr.size(), 6 ) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdef"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_92) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_7) + string eostr{"abcdefgh"}; eostr.erase(7, string::npos); CHECK_EQUAL( eostr.size(), 7 ) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdefg"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_93) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_8) + string eostr{"abcdefgh"}; eostr.erase(8, string::npos); CHECK_EQUAL( eostr.size(), 8 ) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdefgh"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_94) - static string eostr{"abcdefgh"}; +EOSIO_TEST_BEGIN(string_test_erase_8_len_0) + string eostr{"abcdefgh"}; eostr.erase(8, 0); CHECK_EQUAL( eostr.size(), 8 ) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdefgh"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_95) - static string eostr{"abcdefg"}; - CHECK_ASSERT( "eosio::string::erase", []() {eostr.erase(-1, 1);} ) +EOSIO_TEST_BEGIN(string_test_erase_neg_index_1) + string eostr{"abcdefg"}; + CHECK_ASSERT( "eosio::string::erase", [&]() {eostr.erase(-1, 1);} ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_96) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(); @@ -286,8 +286,8 @@ EOSIO_TEST_BEGIN(string_test_96) CHECK_EQUAL( strcmp(eostr.c_str(), ""), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_97) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_at_0_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(0); @@ -295,8 +295,8 @@ EOSIO_TEST_BEGIN(string_test_97) CHECK_EQUAL( strcmp(eostr.c_str(), ""), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_98) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_at_0_op_pl_npos) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(0, string::npos); @@ -304,8 +304,8 @@ EOSIO_TEST_BEGIN(string_test_98) CHECK_EQUAL( strcmp(eostr.c_str(), ""), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_99) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_1_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(1, string::npos); @@ -313,8 +313,8 @@ EOSIO_TEST_BEGIN(string_test_99) CHECK_EQUAL( strcmp(eostr.c_str(), "a"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_100) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_2_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(2, string::npos); @@ -322,8 +322,8 @@ EOSIO_TEST_BEGIN(string_test_100) CHECK_EQUAL( strcmp(eostr.c_str(), "ab"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_101) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_3_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(3, string::npos); @@ -331,8 +331,8 @@ EOSIO_TEST_BEGIN(string_test_101) CHECK_EQUAL( strcmp(eostr.c_str(), "abc"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_102) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_4_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(4, string::npos); @@ -340,8 +340,8 @@ EOSIO_TEST_BEGIN(string_test_102) CHECK_EQUAL( strcmp(eostr.c_str(), "abcd"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_103) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_5_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(5, string::npos); @@ -349,8 +349,8 @@ EOSIO_TEST_BEGIN(string_test_103) CHECK_EQUAL( strcmp(eostr.c_str(), "abcde"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_104) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_6_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(6, string::npos); @@ -358,8 +358,8 @@ EOSIO_TEST_BEGIN(string_test_104) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdef"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_105) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_7_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(7, string::npos); @@ -367,8 +367,8 @@ EOSIO_TEST_BEGIN(string_test_105) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdefg"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_106) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_8_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(8, string::npos); @@ -376,8 +376,8 @@ EOSIO_TEST_BEGIN(string_test_106) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdefgh"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_107) - static string eostr{""}; +EOSIO_TEST_BEGIN(string_test_erase_at_8_op_pl) + string eostr{""}; eostr += "abcdefgh"; eostr.erase(8, 0); @@ -385,49 +385,49 @@ EOSIO_TEST_BEGIN(string_test_107) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdefgh"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_108) - static string eostr{"abcdefg"}; - CHECK_ASSERT( "eosio::string::erase", []() {eostr.erase(-1, 1);} ) +EOSIO_TEST_BEGIN(string_test_erase_neg_index_2) + string eostr{"abcdefg"}; + CHECK_ASSERT( "eosio::string::erase", [&]() {eostr.erase(-1, 1);} ) EOSIO_TEST_END //// string& append(const char* str) -EOSIO_TEST_BEGIN(string_test_109) - static string eostr{}; - static const char* str{"iii"}; +EOSIO_TEST_BEGIN(string_test_append_to_blank_1) + string eostr{}; + const char* str{"iii"}; eostr.append(str); CHECK_EQUAL( eostr.size(), 3 ) CHECK_EQUAL( eostr.capacity(), 6 ) CHECK_EQUAL( strcmp(eostr.c_str(), "iii"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_110) - static string eostr{"abcdefg"}; - static const char* str{"iii"}; +EOSIO_TEST_BEGIN(string_test_append_1) + string eostr{"abcdefg"}; + const char* str{"iii"}; eostr.append(str); CHECK_EQUAL( eostr.size(), 10 ) CHECK_EQUAL( eostr.capacity(), 20 ) CHECK_EQUAL( strcmp(eostr.c_str(), "abcdefgiii"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_111) - static string eostr{"abcdefg"}; - static const char* null_man{nullptr}; - CHECK_ASSERT( "eosio::string::append", []() {eostr.append(null_man);} ) +EOSIO_TEST_BEGIN(string_test_append_null) + string eostr{"abcdefg"}; + const char* null_man{nullptr}; + CHECK_ASSERT( "eosio::string::append", [&]() {eostr.append(null_man);} ) EOSIO_TEST_END //// string& append(const string& str) -EOSIO_TEST_BEGIN(string_test_112) - static string eostr{}; - static const string str{"iii"}; +EOSIO_TEST_BEGIN(string_test_append_to_blank_2) + string eostr{}; + const string str{"iii"}; eostr.append(str); CHECK_EQUAL( eostr.size(), 3 ) CHECK_EQUAL( eostr.capacity(), 6 ) CHECK_EQUAL( strcmp(eostr.c_str(), "iii"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_113) - static string eostr{"abcdefg"}; - static const string str{"iii"}; +EOSIO_TEST_BEGIN(string_test_append_2) + string eostr{"abcdefg"}; + const string str{"iii"}; eostr.append(str); CHECK_EQUAL( eostr.size(), 10 ) CHECK_EQUAL( eostr.capacity(), 20 ) @@ -435,10 +435,10 @@ EOSIO_TEST_BEGIN(string_test_113) EOSIO_TEST_END //// string& operator+=(const char c) -EOSIO_TEST_BEGIN(string_test_114) - static string eostr0{}; - static string eostr1{"a"}; - static string eostr2{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_append_3) + string eostr0{}; + string eostr1{"a"}; + string eostr2{"abcdef"}; eostr0 += 'c'; CHECK_EQUAL( eostr0.size(), 1 ) @@ -458,11 +458,11 @@ EOSIO_TEST_BEGIN(string_test_114) EOSIO_TEST_END //// string& operator+=(const char* rhs) -EOSIO_TEST_BEGIN(string_test_115) - static string eostr0{}; - static string eostr1{"a"}; - static string eostr2{"abcdef"}; - static string eostr3{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_append_op_pl_1) + string eostr0{}; + string eostr1{"a"}; + string eostr2{"abcdef"}; + string eostr3{"abcdef"}; eostr0 += "c"; CHECK_EQUAL( eostr0.size(), 1 ) @@ -487,11 +487,11 @@ EOSIO_TEST_BEGIN(string_test_115) EOSIO_TEST_END //// string& operator+=(const string& rhs) -EOSIO_TEST_BEGIN(string_test_116) - static string eostr0{}; - static string eostr1{"a"}; - static string eostr2{"abcdef"}; - static string eostr3{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_append_op_pl_2) + string eostr0{}; + string eostr1{"a"}; + string eostr2{"abcdef"}; + string eostr3{"abcdef"}; eostr0 += string{"c"}; CHECK_EQUAL( eostr0.size(), 1 ) @@ -516,18 +516,18 @@ EOSIO_TEST_BEGIN(string_test_116) EOSIO_TEST_END //// string& operator+=(const string& s) -EOSIO_TEST_BEGIN(string_test_117) - static string eostr0{"a"}; - static string eostr1{"b"}; +EOSIO_TEST_BEGIN(string_test_append_op_pl_3) + string eostr0{"a"}; + string eostr1{"b"}; CHECK_EQUAL( eostr0.size(), 1 ) eostr0 += eostr1; CHECK_EQUAL( eostr0.size(), 2 ) CHECK_EQUAL( strcmp(eostr0.c_str(), "ab"), 0 ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_118) - static string eostr0{"abc"}; - static string eostr1{"def"}; +EOSIO_TEST_BEGIN(string_test_append_op_pl_4) + string eostr0{"abc"}; + string eostr1{"def"}; CHECK_EQUAL( eostr0.size(), 3 ) eostr0 += eostr1; CHECK_EQUAL( eostr0.size(), 6 ) @@ -535,65 +535,65 @@ EOSIO_TEST_BEGIN(string_test_118) EOSIO_TEST_END //// inline void print(eosio::string str) -EOSIO_TEST_BEGIN(string_test_119) - static const string eostr0{""}; - static const string eostr1{"abc"}; - static const string eostr2{"abcdef"}; +EOSIO_TEST_BEGIN(string_test_print) + const string eostr0{""}; + const string eostr1{"abc"}; + const string eostr2{"abcdef"}; - CHECK_PRINT( "", [](){ print(eostr0); } ) - CHECK_PRINT( "abc", [](){ print(eostr1); } ) - CHECK_PRINT( "abcdef", [](){ print(eostr2); } ) + CHECK_PRINT( "", [&](){ print(eostr0); } ) + CHECK_PRINT( "abc", [&](){ print(eostr1); } ) + CHECK_PRINT( "abcdef", [&](){ print(eostr2); } ) EOSIO_TEST_END //// friend bool operator< (const string& lhs, const string& rhs) -EOSIO_TEST_BEGIN(string_test_120) - static const string eostr0{"abc"}; - static const string eostr1{"def"}; +EOSIO_TEST_BEGIN(string_test_less_than) + const string eostr0{"abc"}; + const string eostr1{"def"}; CHECK_EQUAL( (eostr0 < eostr0), false ) CHECK_EQUAL( (eostr1 < eostr1), false ) CHECK_EQUAL( (eostr0 < eostr1), true ) EOSIO_TEST_END //// friend bool operator> (const string& lhs, const string& rhs) -EOSIO_TEST_BEGIN(string_test_121) - static const string eostr0{"abc"}; - static const string eostr1{"def"}; +EOSIO_TEST_BEGIN(string_test_gt) + const string eostr0{"abc"}; + const string eostr1{"def"}; CHECK_EQUAL( (eostr0 > eostr0), false ) CHECK_EQUAL( (eostr1 > eostr1), false ) CHECK_EQUAL( (eostr0 > eostr1), false ) EOSIO_TEST_END //// friend bool operator<=(const string& lhs, const string& rhs) -EOSIO_TEST_BEGIN(string_test_122) - static const string eostr0{"abc"}; - static const string eostr1{"def"}; +EOSIO_TEST_BEGIN(string_test_lt_or_eq) + const string eostr0{"abc"}; + const string eostr1{"def"}; CHECK_EQUAL( (eostr0 <= eostr0), true ) CHECK_EQUAL( (eostr1 <= eostr1), true ) CHECK_EQUAL( (eostr0 <= eostr1), true ) EOSIO_TEST_END //// friend bool operator>=(const string& lhs, const string& rhs) -EOSIO_TEST_BEGIN(string_test_123) - static const string eostr0{"abc"}; - static const string eostr1{"def"}; +EOSIO_TEST_BEGIN(string_test_gt_or_eq) + const string eostr0{"abc"}; + const string eostr1{"def"}; CHECK_EQUAL( (eostr0 >= eostr0), true ) CHECK_EQUAL( (eostr1 >= eostr1), true ) CHECK_EQUAL( (eostr0 >= eostr1), false ) EOSIO_TEST_END //// friend bool operator==(const string& lhs, const string& rhs) -EOSIO_TEST_BEGIN(string_test_124) - static const string eostr0{"abc"}; - static const string eostr1{"def"}; +EOSIO_TEST_BEGIN(string_test_equal) + const string eostr0{"abc"}; + const string eostr1{"def"}; CHECK_EQUAL( (eostr0 == eostr0), true ) CHECK_EQUAL( (eostr1 == eostr1), true ) CHECK_EQUAL( (eostr0 == eostr1), false ) EOSIO_TEST_END //// friend bool operator!=(const string& lhs, const string& rhs) -EOSIO_TEST_BEGIN(string_test_125) - static const string eostr0{"abc"}; - static const string eostr1{"def"}; +EOSIO_TEST_BEGIN(string_test_not_equal) + const string eostr0{"abc"}; + const string eostr1{"def"}; CHECK_EQUAL( (eostr0 != eostr0), false ) CHECK_EQUAL( (eostr1 != eostr1), false ) CHECK_EQUAL( (eostr0 != eostr1), true ) @@ -602,48 +602,48 @@ EOSIO_TEST_END //// template //// DataStream& operator<<(DataStream& ds, const string& str) //// DataStream& operator>>(DataStream& ds, string& str) -EOSIO_TEST_BEGIN(string_test_126) - static constexpr uint16_t buffer_size{256}; - static char datastream_buffer[buffer_size]{}; // Buffer for the datastream to point to - static char buffer[buffer_size]; // Buffer to compare `datastream_buffer` with - static datastream ds{datastream_buffer, buffer_size}; +EOSIO_TEST_BEGIN(string_test_stream_io_1) + constexpr uint16_t buffer_size{256}; + char datastream_buffer[buffer_size]{}; // Buffer for the datastream to point to + char buffer[buffer_size]; // Buffer to compare `datastream_buffer` with + datastream ds{datastream_buffer, buffer_size}; ds.seekp(0); fill(std::begin(datastream_buffer), std::end(datastream_buffer), 0); - static const string cstr {""}; - static string str{}; + const string cstr {""}; + string str{}; ds << cstr; ds.seekp(0); ds >> str; CHECK_EQUAL( cstr, str ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_127) - static constexpr uint16_t buffer_size{256}; - static char datastream_buffer[buffer_size]{}; - static char buffer[buffer_size]; - static datastream ds{datastream_buffer, buffer_size}; +EOSIO_TEST_BEGIN(string_test_stream_io_2) + constexpr uint16_t buffer_size{256}; + char datastream_buffer[buffer_size]{}; + char buffer[buffer_size]; + datastream ds{datastream_buffer, buffer_size}; ds.seekp(0); fill(std::begin(datastream_buffer), std::end(datastream_buffer), 0); - static const string cstr {"a"}; - static string str{}; + const string cstr {"a"}; + string str{}; ds << cstr; ds.seekp(0); ds >> str; CHECK_EQUAL( cstr, str ) EOSIO_TEST_END -EOSIO_TEST_BEGIN(string_test_128) - static constexpr uint16_t buffer_size{256}; - static char datastream_buffer[buffer_size]{}; - static char buffer[buffer_size]; - static datastream ds{datastream_buffer, buffer_size}; +EOSIO_TEST_BEGIN(string_test_stream_io_3) + constexpr uint16_t buffer_size{256}; + char datastream_buffer[buffer_size]{}; + char buffer[buffer_size]; + datastream ds{datastream_buffer, buffer_size}; ds.seekp(0); fill(std::begin(datastream_buffer), std::end(datastream_buffer), 0); - static const string cstr {"abcdefghi"}; - static string str{}; + const string cstr {"abcdefghi"}; + string str{}; ds << cstr; ds.seekp(0); ds >> str; @@ -657,69 +657,70 @@ int main(int argc, char* argv[]) { } silence_output(!verbose); - EOSIO_TEST(string_test_65) - EOSIO_TEST(string_test_66) - EOSIO_TEST(string_test_67) - EOSIO_TEST(string_test_68) - EOSIO_TEST(string_test_69) - EOSIO_TEST(string_test_70) - EOSIO_TEST(string_test_71) - EOSIO_TEST(string_test_72) - EOSIO_TEST(string_test_73) - EOSIO_TEST(string_test_74) - EOSIO_TEST(string_test_75) - EOSIO_TEST(string_test_76) - EOSIO_TEST(string_test_77) - EOSIO_TEST(string_test_78) - EOSIO_TEST(string_test_79) - EOSIO_TEST(string_test_80) - EOSIO_TEST(string_test_81) - EOSIO_TEST(string_test_82) - EOSIO_TEST(string_test_83) - EOSIO_TEST(string_test_84) - EOSIO_TEST(string_test_85) - EOSIO_TEST(string_test_86) - EOSIO_TEST(string_test_87) - EOSIO_TEST(string_test_88) - EOSIO_TEST(string_test_89) - EOSIO_TEST(string_test_90) - EOSIO_TEST(string_test_91) - EOSIO_TEST(string_test_92) - EOSIO_TEST(string_test_93) - EOSIO_TEST(string_test_94) - EOSIO_TEST(string_test_95) - EOSIO_TEST(string_test_96) - EOSIO_TEST(string_test_97) - EOSIO_TEST(string_test_98) - EOSIO_TEST(string_test_99) - EOSIO_TEST(string_test_100) - EOSIO_TEST(string_test_101) - EOSIO_TEST(string_test_102) - EOSIO_TEST(string_test_103) - EOSIO_TEST(string_test_104) - EOSIO_TEST(string_test_105) - EOSIO_TEST(string_test_106) - EOSIO_TEST(string_test_107) - EOSIO_TEST(string_test_108) - EOSIO_TEST(string_test_109) - EOSIO_TEST(string_test_110) - EOSIO_TEST(string_test_111) - EOSIO_TEST(string_test_112) - EOSIO_TEST(string_test_113) - EOSIO_TEST(string_test_114) - EOSIO_TEST(string_test_115) - EOSIO_TEST(string_test_116) - EOSIO_TEST(string_test_117) - EOSIO_TEST(string_test_118) - EOSIO_TEST(string_test_119) - EOSIO_TEST(string_test_120) - EOSIO_TEST(string_test_121) - EOSIO_TEST(string_test_122) - EOSIO_TEST(string_test_123) - EOSIO_TEST(string_test_124) - EOSIO_TEST(string_test_125) - EOSIO_TEST(string_test_126) - EOSIO_TEST(string_test_127) - EOSIO_TEST(string_test_128) + EOSIO_TEST(string_test_ins_null) + EOSIO_TEST(string_test_ins_to_blank) + EOSIO_TEST(string_test_ins_at_bgn_single) + EOSIO_TEST(string_test_ins_at_bgn_mul_1) + EOSIO_TEST(string_test_ins_at_bgn_mul_2) + EOSIO_TEST(string_test_ins_in_middle_1) + EOSIO_TEST(string_test_ins_in_middle_2) + EOSIO_TEST(string_test_ins_at_end) + EOSIO_TEST(string_test_ins_neg_index_1) + EOSIO_TEST(string_test_ins_op_pl_1) + EOSIO_TEST(string_test_ins_op_pl_2) + EOSIO_TEST(string_test_ins_op_pl_3) + EOSIO_TEST(string_test_ins_op_pl_4) + EOSIO_TEST(string_test_ins_op_pl_5) + EOSIO_TEST(string_test_ins_op_pl_6) + EOSIO_TEST(string_test_ins_op_pl_7) + EOSIO_TEST(string_test_ins_neg_index_2) + EOSIO_TEST(string_test_ins_capacity) + EOSIO_TEST(string_test_erase) + EOSIO_TEST(string_test_erase_at_zero) + EOSIO_TEST(string_test_erase_to_npos) + EOSIO_TEST(string_test_erase_1) + EOSIO_TEST(string_test_erase_2) + EOSIO_TEST(string_test_erase_3) + EOSIO_TEST(string_test_erase_4) + EOSIO_TEST(string_test_erase_5) + EOSIO_TEST(string_test_erase_6) + EOSIO_TEST(string_test_erase_7) + EOSIO_TEST(string_test_erase_8) + EOSIO_TEST(string_test_erase_8_len_0) + EOSIO_TEST(string_test_erase_neg_index_1) + EOSIO_TEST(string_test_erase_op_pl) + EOSIO_TEST(string_test_erase_at_0_op_pl) + EOSIO_TEST(string_test_erase_at_0_op_pl_npos) + EOSIO_TEST(string_test_erase_1_op_pl) + EOSIO_TEST(string_test_erase_2_op_pl) + EOSIO_TEST(string_test_erase_3_op_pl) + EOSIO_TEST(string_test_erase_4_op_pl) + EOSIO_TEST(string_test_erase_5_op_pl) + EOSIO_TEST(string_test_erase_6_op_pl) + EOSIO_TEST(string_test_erase_7_op_pl) + EOSIO_TEST(string_test_erase_8_op_pl) + EOSIO_TEST(string_test_erase_at_8_op_pl) + EOSIO_TEST(string_test_erase_neg_index_2) + EOSIO_TEST(string_test_append_to_blank_1) + EOSIO_TEST(string_test_append_1) + EOSIO_TEST(string_test_append_null) + EOSIO_TEST(string_test_append_to_blank_2) + EOSIO_TEST(string_test_append_2) + EOSIO_TEST(string_test_append_3) + EOSIO_TEST(string_test_append_op_pl_1) + EOSIO_TEST(string_test_append_op_pl_2) + EOSIO_TEST(string_test_append_op_pl_3) + EOSIO_TEST(string_test_append_op_pl_4) + EOSIO_TEST(string_test_print) + EOSIO_TEST(string_test_less_than) + EOSIO_TEST(string_test_gt) + EOSIO_TEST(string_test_lt_or_eq) + EOSIO_TEST(string_test_gt_or_eq) + EOSIO_TEST(string_test_equal) + EOSIO_TEST(string_test_not_equal) + EOSIO_TEST(string_test_stream_io_1) + EOSIO_TEST(string_test_stream_io_2) + EOSIO_TEST(string_test_stream_io_3) + return has_failed(); } diff --git a/tests/unit/test_contracts/CMakeLists.txt b/tests/unit/test_contracts/CMakeLists.txt index 6f9586a257..dfd75dce4a 100644 --- a/tests/unit/test_contracts/CMakeLists.txt +++ b/tests/unit/test_contracts/CMakeLists.txt @@ -6,6 +6,7 @@ add_contract(transfer_contract transfer_contract transfer.cpp) add_contract(minimal_tests minimal_tests minimal_tests.cpp) add_contract(kv_single_index_tests kv_single_index_tests kv_single_index_tests.cpp) add_contract(kv_multiple_indices_tests kv_multiple_indices_tests kv_multiple_indices_tests.cpp) +add_contract(read_only_query_tests read_only_query_tests read_only_query_tests.cpp) add_contract(kv_make_key_tests kv_make_key_tests kv_make_key_tests.cpp) add_contract(kv_map_tests kv_map_tests kv_map_tests.cpp) add_contract(capi_tests capi_tests capi/capi.c capi/action.c capi/chain.c capi/crypto.c capi/db.c capi/permission.c diff --git a/tests/unit/test_contracts/read_only_query_tests.cpp b/tests/unit/test_contracts/read_only_query_tests.cpp new file mode 100644 index 0000000000..ac8b736602 --- /dev/null +++ b/tests/unit/test_contracts/read_only_query_tests.cpp @@ -0,0 +1,156 @@ +#include +#include + +class [[eosio::contract]] read_only_query_tests : public eosio::contract { +public: + struct my_struct { + uint32_t id; + std::string name; + uint32_t gender; + uint32_t age; + + bool operator==(const my_struct& b) const { + return id == b.id && + name == b.name && + gender == b.gender && + age == b.age; + } + }; + + struct [[eosio::table]] my_table_m : eosio::kv::table { + KV_NAMED_INDEX("id"_n, id) + + my_table_m(eosio::name contract_name) { + init(contract_name, id); + } + }; + struct [[eosio::table]] my_table_f : eosio::kv::table { + KV_NAMED_INDEX("id"_n, id) + + my_table_f(eosio::name contract_name) { + init(contract_name, id); + } + }; + using contract::contract; + my_struct s1{ + .id = 1, + .name = "Bob Smith", + .gender = 1, + .age = 25 + }; + my_struct s2{ + .id = 2, + .name = "Alice Smith", + .gender = 0, + .age = 20 + }; + my_struct s3{ + .id = 3, + .name = "John Smith", + .gender = 1, + .age = 42 + }; + my_struct s4{ + .id = 4, + .name = "Jack Smith", + .gender = 1, + .age = 27 + }; + my_struct s5{ + .id = 5, + .name = "Youko Niihara", + .gender = 0, + .age = 26 + }; + my_struct s6{ + .id = 6, + .name = "Rose Lee", + .gender = 0, + .age = 18, + }; + my_struct s7{ + .id = 7, + .name = "Youko Kawakami", + .gender = 0, + .age = 25, + }; + my_struct s8{ + .id = 8, + .name = "Yuu Yamada", + .gender = 0, + .age = 24, + }; + + [[eosio::action]] + void setup() { + my_table_m tm{get_self()}; + my_table_f tf{get_self()}; + + tm.put(s1, get_self()); + tf.put(s2, get_self()); + tm.put(s3, get_self()); + tm.put(s4, get_self()); + tf.put(s5, get_self()); + tf.put(s6, get_self()); + tf.put(s7, get_self()); + tf.put(s8, get_self()); + } + + [[eosio::action, eosio::read_only]] + std::vector get() { + my_table_m tm{get_self()}; + my_table_f tf{get_self()}; + + std::vector ret; + auto itm = tm.id.begin(); + auto itm_e = tm.id.end(); + eosio::cout << "Males: \n"; + while(itm != itm_e){ + auto row = itm.value(); + eosio::cout << "id=" << row.id << ", name=" << row.name << ",gender=" << row.gender << ", age=" << row.age << "\n"; + my_struct s; + s.id = row.id; + s.name = row.name; + s.gender = row.gender; + s.age = row.age; + ret.push_back(s); + ++itm; + } + eosio::cout << "Females: \n"; + auto itf = tf.id.begin(); + auto itf_e = tf.id.end(); + while(itf != itf_e){ + auto row = itf.value(); + eosio::cout << "id=" << row.id << ", name=" << row.name << ",gender=" << row.gender << ", age=" << row.age << "\n"; + my_struct s; + s.id = row.id; + s.name = row.name; + s.gender = row.gender; + s.age = row.age; + ret.push_back(s); + ++itf; + } + return ret; + } + [[eosio::action]] + // usage: cleos -v push action eosio put '{"id":10,"name":"GULU","gender":1,"age":128}' -p eosio@active + void put(uint32_t id, std::string name, uint32_t gender, uint32_t age ) { + my_table_m tm{get_self()}; + my_table_f tf{get_self()}; + if(gender == 0){ + tf.put({ + .id = id, + .name = name, + .gender = gender, + .age = age + }, get_self()); + } else { + tm.put({ + .id = id, + .name = name, + .gender = gender, + .age = age + }, get_self()); + } + } +}; diff --git a/tests/unit/version_tests.sh b/tests/unit/version_tests.sh new file mode 100755 index 0000000000..2ff40cefc1 --- /dev/null +++ b/tests/unit/version_tests.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -eo pipefail +# The purpose of this test is to ensure that the output of the "eosio-cpp --version" command matches the version string defined by our CMake files +echo '##### Eosio-cpp Version Label Test #####' +# orient ourselves +[[ -z "$BUILD_ROOT" ]] && export BUILD_ROOT="$(pwd)" +echo "Using BUILD_ROOT=\"$BUILD_ROOT\"." +# test expectations +if [[ -z "$EXPECTED" ]]; then + if [[ -z "$1" ]]; then + export VERSION="$(echo ${BUILDKITE_TAG:-$GIT_TAG} | sed "s/^v//")" + else + export VERSION="$1" + fi + export EXPECTED="eosio-cpp version $VERSION" +fi +if [[ -z "$EXPECTED" ]]; then + echo "Missing version input." + exit 1 +fi +echo "Expecting \"$EXPECTED\"..." +# get eosio-cpp version +ACTUAL=$($BUILD_ROOT/bin/eosio-cpp --version) +EXIT_CODE=$? +# verify 0 exit code explicitly +if [[ $EXIT_CODE -ne 0 ]]; then + echo "Eosio-cpp produced non-zero exit code \"$EXIT_CODE\"." + exit $EXIT_CODE +fi +# test version +if [[ "$EXPECTED" == "$ACTUAL" ]]; then + echo "Passed with \"$ACTUAL\"." + exit 0 +fi +echo 'Failed!' +echo "\"$EXPECTED\" != \"$ACTUAL\"" +exit 1 diff --git a/tools/cc/eosio-cpp.cpp.in b/tools/cc/eosio-cpp.cpp.in index 2c7f7eaafa..a486464144 100644 --- a/tools/cc/eosio-cpp.cpp.in +++ b/tools/cc/eosio-cpp.cpp.in @@ -41,7 +41,7 @@ using namespace eosio::cdt; void handle_empty_abigen(const std::string& contract_name, bool has_o_opt, bool has_contract_opt); -void generate(const std::vector& base_options, std::string input, std::string contract_name, const std::vector& resource_paths, const std::pair& abi_version, bool abigen, bool suppress_ricardian_warning, bool has_o_opt, bool has_contract_opt) { +void generate(const std::vector& base_options, std::string input, std::string contract_name, const std::vector& resource_paths, const std::pair& abi_version, bool abigen, bool suppress_ricardian_warning, bool has_o_opt, bool has_contract_opt, bool warn_action_read_only) { std::vector options; options.push_back("eosio-cpp"); options.push_back(input); // don't remove oddity of CommonOptionsParser? @@ -67,6 +67,7 @@ void generate(const std::vector& base_options, std::string input, s abigen::get().set_abi_version(std::get<0>(abi_version), std::get<1>(abi_version)); abigen::get().set_suppress_ricardian_warning(suppress_ricardian_warning); codegen::get().set_contract_name(contract_name); + codegen::get().set_warn_action_read_only(warn_action_read_only); int tool_run = -1; tool_run = ctool.run(newFrontendActionFactory().get()); @@ -136,7 +137,7 @@ int main(int argc, const char **argv) { tool_opts.erase(std::remove_if(tool_opts.begin(), tool_opts.end(), [&](const auto& opt){ return non_tool_opts.count(opt); }), tool_opts.end()); - generate(tool_opts, input, opts.abigen_contract, opts.abigen_resources, opts.abi_version, opts.abigen, opts.suppress_ricardian_warning, opts.has_o_opt, opts.has_contract_opt); + generate(tool_opts, input, opts.abigen_contract, opts.abigen_resources, opts.abi_version, opts.abigen, opts.suppress_ricardian_warning, opts.has_o_opt, opts.has_contract_opt, opts.warn_action_read_only); auto src = SmallString<64>(input); llvm::sys::path::remove_filename(src); diff --git a/tools/include/compiler_options.hpp.in b/tools/include/compiler_options.hpp.in index 6b7c8e55c3..a335f887a3 100644 --- a/tools/include/compiler_options.hpp.in +++ b/tools/include/compiler_options.hpp.in @@ -347,7 +347,10 @@ static cl::opt contract_name( "contract", cl::desc("Contract name"), cl::cat(EosioCompilerToolCategory)); - +static cl::opt warn_action_read_only_opt( + "warn-action-read-only", + cl::desc("Issue a warning if a read-only action uses a write API and continue compilation"), + cl::cat(EosioCompilerToolCategory)); /// end c/c++ options /// begin c++ options @@ -387,6 +390,7 @@ struct Options { std::pair abi_version; bool has_o_opt; bool has_contract_opt; + bool warn_action_read_only; }; static void GetCompDefaults(std::vector& copts) { @@ -548,6 +552,7 @@ static Options CreateOptions(bool add_defaults=true) { std::string abigen_contract; bool has_o_opt; bool has_contract_opt; + bool warn_action_read_only; #ifdef ONLY_LD bool abigen = false; @@ -905,6 +910,13 @@ static Options CreateOptions(bool add_defaults=true) { ldopts.emplace_back("-fnative"); if (fuse_main_opt) ldopts.emplace_back("-fuse-main"); + + if(warn_action_read_only_opt) { + warn_action_read_only = true; + } else { + warn_action_read_only = false; + } + #endif /* TODO add some way of defaulting these to the current highest version */ @@ -918,8 +930,8 @@ static Options CreateOptions(bool add_defaults=true) { } #ifndef ONLY_LD - return {output_fn, inputs, link, abigen, no_missing_ricardian_clause_opt, pp_only, pp_dir, abigen_output, abigen_contract, copts, ldopts, agopts, agresources, debug, fnative_opt, {abi_version_major, abi_version_minor}, has_o_opt, has_contract_opt}; + return {output_fn, inputs, link, abigen, no_missing_ricardian_clause_opt, pp_only, pp_dir, abigen_output, abigen_contract, copts, ldopts, agopts, agresources, debug, fnative_opt, {abi_version_major, abi_version_minor}, has_o_opt, has_contract_opt, warn_action_read_only}; #else - return {output_fn, {}, link, abigen, no_missing_ricardian_clause_opt, pp_only, pp_dir, abigen_output, abigen_contract, copts, ldopts, agopts, agresources, debug, fnative_opt, {abi_version_major, abi_version_minor}, has_o_opt, has_contract_opt}; + return {output_fn, {}, link, abigen, no_missing_ricardian_clause_opt, pp_only, pp_dir, abigen_output, abigen_contract, copts, ldopts, agopts, agresources, debug, fnative_opt, {abi_version_major, abi_version_minor}, has_o_opt, has_contract_opt, warn_action_read_only}; #endif } diff --git a/tools/include/eosio/codegen.hpp b/tools/include/eosio/codegen.hpp index 8e35b93cce..54c0c4d3d8 100644 --- a/tools/include/eosio/codegen.hpp +++ b/tools/include/eosio/codegen.hpp @@ -61,6 +61,7 @@ namespace eosio { namespace cdt { llvm::ArrayRef sources; size_t source_index = 0; std::map tmp_files; + bool warn_action_read_only; using generation_utils::generation_utils; @@ -76,6 +77,10 @@ namespace eosio { namespace cdt { void set_abi(std::string s) { abi = s; } + + void set_warn_action_read_only(bool w) { + warn_action_read_only = w; + } }; class eosio_codegen_visitor : public RecursiveASTVisitor, public generation_utils { @@ -91,11 +96,19 @@ namespace eosio { namespace cdt { std::vector action_decls; std::vector notify_decls; + using call_map_t = std::map>; + using indirect_func_map_t = std::map; + + std::set read_only_actions; + call_map_t func_calls; + indirect_func_map_t indi_func_map; + explicit eosio_codegen_visitor(CompilerInstance *CI) : generation_utils(), ci(CI) { cg.ast_context = &(CI->getASTContext()); cg.codegen_ci = CI; rewriter.setSourceMgr(CI->getASTContext().getSourceManager(), CI->getASTContext().getLangOpts()); + get_error_emitter().set_compiler_instance(CI); } void set_main_fid(FileID fid) { @@ -234,9 +247,12 @@ namespace eosio { namespace cdt { create_action_dispatch(decl); } cg.actions.insert(full_action_name); // insert the method action, so we don't create the dispatcher twice + + if (decl->isEosioReadOnly()) { + read_only_actions.insert(decl); + } } else if (decl->isEosioNotify()) { - name = generation_utils::get_notify_pair(decl); auto first = name.substr(0, name.find("::")); if (first != "*") @@ -265,16 +281,167 @@ namespace eosio { namespace cdt { return true; } + void process_indi_callee(FunctionDecl* fd, CallExpr *call) { + if (Expr *expr = call->getCallee()) { + while (auto* ice = dyn_cast(expr)) { + expr = ice->getSubExpr(); + } + if (auto* dre = dyn_cast(expr)) { + if (indi_func_map.count(dre->getFoundDecl()) != 0) { + func_calls[fd].push_back(call); + } + } else if (auto* me = dyn_cast(expr)) { + if (indi_func_map.count(me->getMemberDecl()) != 0) { + func_calls[fd].push_back(call); + } + } + } + } + + FunctionDecl* get_rhs_fd(Expr *rhs) const { + while (auto* ice = dyn_cast(rhs)) { + rhs = ice->getSubExpr(); + } + if (auto* rhs_dre = dyn_cast(rhs)) { + if (auto* fd = dyn_cast(rhs_dre->getFoundDecl())) { + return fd; + } + } + return nullptr; + } + + void update_indi_func_map(NamedDecl *nd, FunctionDecl *fd) { + if (func_calls.count(fd) != 0) { + indi_func_map[nd] = fd; + } else if (indi_func_map.count(nd)) { + indi_func_map.erase(nd); + } + } + + void process_decl_init(NamedDecl *nd, Expr *init) { + if (FunctionDecl *fd = get_rhs_fd(init)) { + if (func_calls.count(fd) != 0) { + indi_func_map[nd] = fd; + } + } + } + + void process_function(FunctionDecl* func_decl) { + if (func_decl->isThisDeclarationADefinition() && func_decl->hasBody()) { + Stmt *stmts = func_decl->getBody(); + for (auto it = stmts->child_begin(); it != stmts->child_end(); ++it) { + if (Stmt *s = *it) { + if (auto* ec = dyn_cast(s)) { + s = ec->getSubExpr(); + while (auto* ice = dyn_cast(s)) + s = ice->getSubExpr(); + } + + if (auto* call = dyn_cast(s)) { + if (FunctionDecl *fd = call->getDirectCallee()) { + if (func_calls.count(fd) == 0) { + process_function(fd); + } + if (!func_calls[fd].empty()) { + func_calls[func_decl].push_back(call); + break; + } + } else { + process_indi_callee(func_decl, call); + } + } else if (auto* ds = dyn_cast(s)) { + auto process_decl = [this]( DeclStmt *s ) { + for (auto it = s->decl_begin(); it != s->decl_end(); ++it) { + if (auto* vd = dyn_cast(*it)) { + if (Expr *init = vd->getInit()) { + process_decl_init(vd, init); + } + } + } + }; + process_decl(ds); + } else if (auto* bo = dyn_cast(s)) { + auto process_assignment = [this]( BinaryOperator *b ) { + Expr *lhs = nullptr, *rhs = nullptr; + if ((lhs = b->getLHS()) && (rhs = b->getRHS())) { + if (FunctionDecl *fd = get_rhs_fd(rhs)) { + if (auto* lhs_dre = dyn_cast(lhs)) { + update_indi_func_map(lhs_dre->getFoundDecl(), fd); + } else if (auto* lhs_me = dyn_cast(lhs)) { + update_indi_func_map(lhs_me->getMemberDecl(), fd); + } + } + } + }; + process_assignment(bo); + } + } + } + } + } + + virtual bool VisitFunctionDecl(FunctionDecl* func_decl) { + if (func_calls.count(func_decl) == 0 && is_write_host_func(func_decl)) { + func_calls[func_decl] = {(CallExpr*)func_decl}; + } else { + process_function(func_decl); + } + return true; + } + virtual bool VisitDecl(clang::Decl* decl) { if (auto* fd = dyn_cast(decl)) { if (fd->getNameInfo().getAsString() == "apply") apply_was_found = true; + } else { + auto process_global_var = [this]( clang::Decl* d ) { + if (auto* vd = dyn_cast(d)) { + if (vd->hasGlobalStorage()) { + if (Expr *init = vd->getInit()) { + process_decl_init(vd, init); + } + } + } + }; + process_global_var(decl); + } + return true; + } + + virtual bool VisitCXXRecordDecl(CXXRecordDecl* decl) { + if (decl->isEosioContract()) { + auto process_data_member = [this]( CXXRecordDecl* rd ) { + for (auto it = rd->decls_begin(); it != rd->decls_end(); ++it) { + if (auto* f = dyn_cast(*it) ) { + if (Expr *init = f->getInClassInitializer()) { + process_decl_init(f, init); + } + } + } + }; + process_data_member(decl); } return true; } + + void process_read_only_actions() const { + codegen& cg = codegen::get(); + for (auto const& ra : read_only_actions) { + auto it = func_calls.find(ra); + if (it != func_calls.end()) { + std::string msg = "read-only action cannot call write host function"; + if (cg.warn_action_read_only) { + CDT_WARN("codegen_warning", ra->getLocation(), msg); + } else { + CDT_ERROR("codegen_error", ra->getLocation(), msg); + } + } + } + } + }; - class eosio_codegen_consumer : public ASTConsumer { + class eosio_codegen_consumer : public ASTConsumer, public generation_utils { private: eosio_codegen_visitor *visitor; std::string main_file; @@ -295,6 +462,8 @@ namespace eosio { namespace cdt { visitor->set_main_fid(fid); visitor->set_main_name(main_fe->getName()); visitor->TraverseDecl(Context.getTranslationUnitDecl()); + visitor->process_read_only_actions(); + for (auto ad : visitor->action_decls) visitor->create_action_dispatch(ad); diff --git a/tools/include/eosio/gen.hpp b/tools/include/eosio/gen.hpp index 78f780af17..2e16cff282 100644 --- a/tools/include/eosio/gen.hpp +++ b/tools/include/eosio/gen.hpp @@ -5,6 +5,7 @@ #include "clang/Basic/Builtins.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Tooling.h" +#include "clang/AST/Decl.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -683,5 +684,62 @@ struct generation_utils { return in_kv_namespace && is_internal; } + + inline bool is_write_host_func( const clang::FunctionDecl *func_decl ) { + static const std::set write_host_funcs = + { + "set_resource_limits", + "set_wasm_parameters_packed", + "set_resource_limit", + "set_proposed_producers", + "set_proposed_producers_ex", + "set_blockchain_parameters_packed", + "set_parameters_packed", + "set_kv_parameters_packed", + "set_privileged", + "db_store_i64", + "db_update_i64", + "db_remove_i64", + "db_idx64_store", + "db_idx64_update", + "db_idx64_remove", + "db_idx128_store", + "db_idx128_update", + "db_idx128_remove", + "db_idx256_store", + "db_idx256_update", + "db_idx256_remove", + "db_idx_double_store", + "db_idx_double_update", + "db_idx_double_remove", + "db_idx_long_double_store", + "db_idx_long_double_update", + "db_idx_long_double_remove", + "kv_erase", + "kv_set", + "send_deferred", + "send_inline", + "send_context_free_inline" + }; + + return write_host_funcs.count(func_decl->getNameInfo().getAsString()) >= 1; + } + + inline bool is_deferred_transaction_func( const std::string& t ) { + static const std::set deferred_transaction_funcs = + { + "send_deferred", + }; + return deferred_transaction_funcs.count(t) >= 1; + } + + inline bool is_inline_action_func( const std::string& t ) { + static const std::set inline_action_funcs = + { + "send_inline", + "send_context_free_inline" + }; + return inline_action_funcs.count(t) >= 1; + } }; }} // ns eosio::cdt diff --git a/tools/toolchain-tester/tests.py b/tools/toolchain-tester/tests.py index 0a21695363..2385dbe636 100644 --- a/tools/toolchain-tester/tests.py +++ b/tools/toolchain-tester/tests.py @@ -7,6 +7,7 @@ import json import os import subprocess +import re from printer import Printer as P from errors import TestFailure @@ -89,7 +90,7 @@ def handle_expecteds(self, res: subprocess.CompletedProcess): expected_stderr = expected["stderr"] actual_stderr = res.stderr.decode("utf-8") - if expected_stderr not in actual_stderr: + if expected_stderr not in actual_stderr and not re.search(expected_stderr, actual_stderr, flags=re.S): self.success = False raise TestFailure( f"expected {expected_stderr} stderr but got {actual_stderr}",