diff --git a/.github/workflows/python-app-ci.yml b/.github/workflows/python-app-ci.yml index 2df15d859..145adba45 100644 --- a/.github/workflows/python-app-ci.yml +++ b/.github/workflows/python-app-ci.yml @@ -17,14 +17,14 @@ jobs: strategy: fail-fast: false matrix: - os: [ macos-latest, ubuntu-latest ] + os: [ macos-13, ubuntu-latest ] steps: - uses: actions/checkout@v3 - - name: Set up Python 3.10.4 + - name: Set up Python 3.12.2 uses: actions/setup-python@v2 with: - python-version: 3.10.4 + python-version: 3.12.2 - name: Install dependencies run: | python -m pip install --upgrade pip @@ -47,10 +47,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python 3.10.4 + - name: Set up Python 3.12.2 uses: actions/setup-python@v2 with: - python-version: 3.10.4 + python-version: 3.12.2 - name: Install dependencies run: | python -m pip install --upgrade pip @@ -68,10 +68,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python 3.10.4 + - name: Set up Python 3.12.2 uses: actions/setup-python@v2 with: - python-version: 3.10.4 + python-version: 3.12.2 - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/Makefile b/Makefile index 48d770898..5a4ad3d5e 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,34 @@ .PHONY: build-keri -build-keri: - @docker buildx build --platform=linux/amd64 -f images/keripy.dockerfile --tag weboftrust/keri:1.1.6 . -.PHONY: build-witness-demo -build-witness-demo: - @@docker buildx build --platform=linux/amd64 -f images/witness.demo.dockerfile --tag weboftrust/keri-witness-demo:1.1.6 . +VERSION=1.1.33 -.PHONY: publish-keri -publish-keri: - @docker push weboftrust/keri --all-tags +define DOCKER_WARNING +In order to use the multi-platform build enable the containerd image store +The containerd image store is not enabled by default. +To enable the feature for Docker Desktop: + Navigate to Settings in Docker Desktop. + In the General tab, check Use containerd for pulling and storing images. + Select Apply and Restart." +endef + +build-keri: .warn + @docker build --platform=linux/amd64,linux/arm64 -f images/keripy.dockerfile -t weboftrust/keri:$(VERSION) . + +.PHONY: build-witness-demo +build-witness-demo: .warn + @docker build --platform=linux/amd64,linux/arm64 -f images/witness.demo.dockerfile -t weboftrust/keri-witness-demo:1.1.10 . .PHONY: publish-keri-witness-demo publish-keri-witness-demo: - @docker push weboftrust/keri-witness-demo --all-tags \ No newline at end of file + @docker push weboftrust/keri-witness-demo --all-tags + +publish-keri: + @docker push weboftrust/keri:$(VERSION) + +.warn: + @echo -e ${RED}"$$DOCKER_WARNING"${NO_COLOUR} + +RED="\033[0;31m" +NO_COLOUR="\033[0m" +export DOCKER_WARNING diff --git a/README.md b/README.md index 9e9771724..b281a2f11 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,10 @@ to get a version string similar to the following: ### Local installation - Docker build Run `make build-keri` to build your docker image. -Then run `docker run -it gleif/keri /bin/bash` and you can run `kli version` from within the running container to play with KERIpy. +Then run `docker run --pull=never -it --entrypoint /bin/bash weboftrust/keri:1.1.33` and you can run `kli version` from within the running container to play with KERIpy. + +Make sure the image tag matches the version used in the `Makefile`. +We use `--pull=never` to ensure that docker does not implicitly pull a remote image and relies on the local image tagged during `make build-keri`. ### Dependencies #### Binaries diff --git a/docs/keri_db.rst b/docs/keri_db.rst index e2354b170..14327b0fc 100644 --- a/docs/keri_db.rst +++ b/docs/keri_db.rst @@ -13,6 +13,8 @@ keri.db.dbing .. automodule:: keri.db.dbing :members: +The `KERI_LMDB_MAP_SIZE` environment variable can be used to set the size of the LMDB map. The default size is 4GB. + keri.db.escrowing ----------------- diff --git a/images/keripy.dockerfile b/images/keripy.dockerfile index ca12837fc..71b36b2ec 100644 --- a/images/keripy.dockerfile +++ b/images/keripy.dockerfile @@ -1,18 +1,21 @@ -# Builder layer -FROM python:3.10-alpine as builder +ARG BASE=python:3.12.2-alpine3.19 -# Install compilation dependencies -RUN apk --no-cache add \ - bash \ +FROM ${BASE} AS builder + +RUN apk add --no-cache bash + +SHELL ["/bin/bash", "-c"] + +RUN apk add --no-cache \ + curl \ + build-base \ alpine-sdk \ libffi-dev \ libsodium \ - libsodium-dev - -SHELL ["/bin/bash", "-c"] + libsodium-dev -# Setup Rust for blake3 dependency build -RUN curl https://sh.rustup.rs -sSf | bash -s -- -y +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" WORKDIR /keripy @@ -20,19 +23,16 @@ RUN python -m venv venv ENV PATH=/keripy/venv/bin:${PATH} -RUN pip install --upgrade pip && \ - mkdir /keripy/src +RUN pip install --upgrade pip +RUN mkdir /keripy/src -# Copy Python dependency files in COPY requirements.txt setup.py ./ -# Set up Rust environment and install Python dependencies -# Must source the Cargo environment for the blake3 library to see -# the Rust intallation during requirements install -RUN . ${HOME}/.cargo/env && \ - pip install -r requirements.txt + +RUN . ${HOME}/.cargo/env +RUN pip install -r requirements.txt # Runtime layer -FROM python:3.10.13-alpine3.18 +FROM ${BASE} RUN apk --no-cache add \ bash \ @@ -44,7 +44,6 @@ WORKDIR /keripy COPY --from=builder /keripy /keripy COPY src/ src/ -ENV PATH=/keripy/venv/bin:${PATH} - +ENV PATH="/keripy/venv/bin:${PATH}" ENTRYPOINT [ "kli" ] diff --git a/keri/cf/demo-witness-oobis.json b/keri/cf/demo-witness-oobis.json new file mode 100755 index 000000000..e69de29bb diff --git a/scripts/demo/basic/keri/cf/demo-witness-oobis.json b/scripts/demo/basic/keri/cf/demo-witness-oobis.json new file mode 100755 index 000000000..e69de29bb diff --git a/scripts/demo/basic/migrate-quadlet.sh b/scripts/demo/basic/migrate-quadlet.sh index 5339ffcec..b175996eb 100755 --- a/scripts/demo/basic/migrate-quadlet.sh +++ b/scripts/demo/basic/migrate-quadlet.sh @@ -1,13 +1,14 @@ #!/bin/bash -kli migrate --name multisig1 -kli migrate --name multisig2 -kli migrate --name multisig3 -kli migrate --name multisig4 +kli migrate --name delegator --force +kli migrate --name multisig1 --force +kli migrate --name multisig2 --force +kli migrate --name multisig3 --force +kli migrate --name multisig4 --force -kli migrate --name wil -kli migrate --name wan -kli migrate --name wes -kli migrate --name wit -kli migrate --name wub -kli migrate --name wyz \ No newline at end of file +kli migrate --name wil --force +kli migrate --name wan --force +kli migrate --name wes --force +kli migrate --name wit --force +kli migrate --name wub --force +kli migrate --name wyz --force \ No newline at end of file diff --git a/scripts/demo/basic/multisig-join.sh b/scripts/demo/basic/multisig-join.sh new file mode 100755 index 000000000..dc32a4bb3 --- /dev/null +++ b/scripts/demo/basic/multisig-join.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# WITNESSES +# To run the following scripts, open another console window and run: +# $ kli witness demo + +kli init --name multisigj1 --salt 0ACDEyMzQ1Njc4OWxtbm9aBc --nopasscode --config-dir "${KERI_SCRIPT_DIR}" --config-file demo-witness-oobis +kli incept --name multisigj1 --alias multisigj1 --file ${KERI_DEMO_SCRIPT_DIR}/data/multisig-1-sample.json + +kli init --name multisigj2 --salt 0ACDEyMzQ1Njc4OWdoaWpsaw --nopasscode --config-dir "${KERI_SCRIPT_DIR}" --config-file demo-witness-oobis +kli incept --name multisigj2 --alias multisigj2 --file ${KERI_DEMO_SCRIPT_DIR}/data/multisig-2-sample.json + +kli oobi resolve --name multisigj1 --oobi-alias multisigj2 --oobi http://127.0.0.1:5642/oobi/EKJ6tNVUGbdaiwx2nWDCFXG-_PY_AzESOcoKlm0kRNP3/witness/BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha +kli oobi resolve --name multisigj2 --oobi-alias multisigj1 --oobi http://127.0.0.1:5642/oobi/EFY7MixHb0so4WFFHw6btOPc5qeeWfPm7v5MJWcdcbyG/witness/BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha + +PID_LIST="" + +kli multisig incept --name multisigj1 --alias multisigj1 --group multisig --file ${KERI_DEMO_SCRIPT_DIR}/data/multisig-join-sample.json & +pid=$! +PID_LIST+=" $pid" + +kli multisig join --name multisigj2 --auto & +pid=$! +PID_LIST+=" $pid" + +wait $PID_LIST + +kli status --name multisigj1 --alias multisig + +kli rotate --name multisigj1 --alias multisigj1 +kli query --name multisigj2 --alias multisigj2 --prefix EFY7MixHb0so4WFFHw6btOPc5qeeWfPm7v5MJWcdcbyG +kli rotate --name multisigj2 --alias multisigj2 +kli query --name multisigj1 --alias multisigj1 --prefix EKJ6tNVUGbdaiwx2nWDCFXG-_PY_AzESOcoKlm0kRNP3 + +PID_LIST="" + +kli multisig rotate --name multisigj1 --alias multisig --smids EKJ6tNVUGbdaiwx2nWDCFXG-_PY_AzESOcoKlm0kRNP3 --smids EFY7MixHb0so4WFFHw6btOPc5qeeWfPm7v5MJWcdcbyG --isith '["1/2", "1/2"]' --nsith '["1/2", "1/2"]' --rmids EKJ6tNVUGbdaiwx2nWDCFXG-_PY_AzESOcoKlm0kRNP3 --rmids EFY7MixHb0so4WFFHw6btOPc5qeeWfPm7v5MJWcdcbyG & +pid=$! +PID_LIST+=" $pid" + +kli multisig join --name multisigj2 --auto & +pid=$! +PID_LIST+=" $pid" + +wait $PID_LIST + +kli status --name multisigj1 --alias multisig + +PID_LIST="" + +kli multisig interact --name multisigj1 --alias multisig --data '{"d": "potato"}' & +pid=$! +PID_LIST+=" $pid" + +kli multisig join --name multisigj2 --auto & +pid=$! +PID_LIST+=" $pid" + +wait $PID_LIST + +kli status --name multisigj1 --alias multisig diff --git a/scripts/demo/basic/multisig-rotation-join.sh b/scripts/demo/basic/multisig-rotation-in-third.sh similarity index 100% rename from scripts/demo/basic/multisig-rotation-join.sh rename to scripts/demo/basic/multisig-rotation-in-third.sh diff --git a/scripts/demo/basic/rotate-new-quartet.sh b/scripts/demo/basic/rotate-new-quartet.sh index 33c0b83b9..83ea76a48 100755 --- a/scripts/demo/basic/rotate-new-quartet.sh +++ b/scripts/demo/basic/rotate-new-quartet.sh @@ -7,8 +7,8 @@ kli query --name multisig1 --alias multisig1 --prefix EJccSRTfXYF6wrUVuenAIHzwcx # Perform rotation of mulisig AID from local kli AIDs that roll themselves out and the new AIDs in kli multisig rotate --name multisig1 --alias multisig \ - --smids EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4:2 \ - --smids EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1:2 \ + --smids EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4:1 \ + --smids EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1:1 \ --smids EBFg-5SGDCv5YfwpkArWRBdTxNRUXU8uVcDKNzizOQZc:0 \ --smids EBmW2bXbgsP3HITwW3FmITzAb3wVmHlxCusZ46vgGgP5:0 \ --smids EL4RpdS2Atb2Syu5xLdpz9CcNNYoFUUDlLHxHD09vcgh:0 \ @@ -22,8 +22,8 @@ kli multisig rotate --name multisig1 --alias multisig \ pid=$! PID_LIST="$pid" kli multisig rotate --name multisig2 --alias multisig \ - --smids EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4:2 \ - --smids EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1:2 \ + --smids EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4:1 \ + --smids EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1:1 \ --smids EBFg-5SGDCv5YfwpkArWRBdTxNRUXU8uVcDKNzizOQZc:0 \ --smids EBmW2bXbgsP3HITwW3FmITzAb3wVmHlxCusZ46vgGgP5:0 \ --smids EL4RpdS2Atb2Syu5xLdpz9CcNNYoFUUDlLHxHD09vcgh:0 \ diff --git a/scripts/demo/credentials/multisig-grant-multisig-admit.sh b/scripts/demo/credentials/multisig-grant-multisig-admit.sh new file mode 100755 index 000000000..713e93085 --- /dev/null +++ b/scripts/demo/credentials/multisig-grant-multisig-admit.sh @@ -0,0 +1,141 @@ +#!/bin/bash +# To run this script you need to run the following command in separate terminals: +# > kli witness demo +# and from the vLEI repo run: +# > vLEI-server -s ./schema/acdc -c ./samples/acdc/ -o ./samples/oobis/ +# + +# issuer 1 +# EEVlFHcMWAQNwezHjyKK5cKKzF6zgLlnrLyi_CcAEXCs +# issuer 2 +# EFJtDtSoE6XOOqLoLvYoB7ctCzMtJDiAJltnXiK_EdlM +# issuee 1 +# EI0IoYyHxXc7_uQyaN2WocSC3lRZsvrDAPbREOw7fM0_ +# issuee 2 +# EPw5WQAFcNXXSbg_pTKgh8-K_rfXnD1uDKS13OeNHkKE +# issuer group +# ELrnb8aI_wy2q_sSbCAwkgy2kOdMpRI1urFrhQiMJGLW +# issuee group +# ELkmm28zQEyxkryJZQ4WVT4fjukklM4dR91l2DQfQHZK + +# Create local environments for issuer group +kli init --name issuer1 --salt 0ACDEyMzQ1Njc4OWxtbm9aBc --nopasscode --config-dir ${KERI_SCRIPT_DIR} --config-file demo-witness-oobis +kli incept --name issuer1 --alias issuer1 --file ${KERI_DEMO_SCRIPT_DIR}/data/issuer-1-sample.json + +# Incept both local identifiers for issuer group +kli init --name issuer2 --salt 0ACDEyMzQ1Njc4OWdoaWpsaw --nopasscode --config-dir ${KERI_SCRIPT_DIR} --config-file demo-witness-oobis +kli incept --name issuer2 --alias issuer2 --file ${KERI_DEMO_SCRIPT_DIR}/data/issuer-2-sample.json + +# Exchange OOBIs between issuer group +kli oobi resolve --name issuer1 --oobi-alias issuer2 --oobi http://127.0.0.1:5642/oobi/EFJtDtSoE6XOOqLoLvYoB7ctCzMtJDiAJltnXiK_EdlM/witness +kli oobi resolve --name issuer2 --oobi-alias issuer1 --oobi http://127.0.0.1:5642/oobi/EEVlFHcMWAQNwezHjyKK5cKKzF6zgLlnrLyi_CcAEXCs/witness + +# Create the identifier to which the credential will be issued +kli init --name issuee1 --salt 0ACDEyMzQ1Njc4OWxtbm9qWc --nopasscode --config-dir ${KERI_SCRIPT_DIR} --config-file demo-witness-oobis +kli incept --name issuee1 --alias issuee1 --file ${KERI_DEMO_SCRIPT_DIR}/data/issuee-1-sample.json + +# Create the identifier to which the credential will be issued +kli init --name issuee2 --salt 0ACDEyMzQ1Njc4OWxtbm9qWc --nopasscode --config-dir ${KERI_SCRIPT_DIR} --config-file demo-witness-oobis +kli incept --name issuee2 --alias issuee2 --file ${KERI_DEMO_SCRIPT_DIR}/data/issuee-2-sample.json + +# Exchange OOBIs between issuee group +kli oobi resolve --name issuee1 --oobi-alias issuee2 --oobi http://127.0.0.1:5642/oobi/EPw5WQAFcNXXSbg_pTKgh8-K_rfXnD1uDKS13OeNHkKE/witness +kli oobi resolve --name issuee2 --oobi-alias issuee1 --oobi http://127.0.0.1:5642/oobi/EI0IoYyHxXc7_uQyaN2WocSC3lRZsvrDAPbREOw7fM0_/witness + +# Introduce issuer to issuee +kli oobi resolve --name issuee1 --oobi-alias issuer1 --oobi http://127.0.0.1:5642/oobi/EEVlFHcMWAQNwezHjyKK5cKKzF6zgLlnrLyi_CcAEXCs/witness +kli oobi resolve --name issuee2 --oobi-alias issuer1 --oobi http://127.0.0.1:5642/oobi/EEVlFHcMWAQNwezHjyKK5cKKzF6zgLlnrLyi_CcAEXCs/witness +kli oobi resolve --name issuee1 --oobi-alias issuer2 --oobi http://127.0.0.1:5642/oobi/EFJtDtSoE6XOOqLoLvYoB7ctCzMtJDiAJltnXiK_EdlM/witness +kli oobi resolve --name issuee2 --oobi-alias issuer2 --oobi http://127.0.0.1:5642/oobi/EFJtDtSoE6XOOqLoLvYoB7ctCzMtJDiAJltnXiK_EdlM/witness + +# Introduce the issuee to issuer +kli oobi resolve --name issuer1 --oobi-alias issuee1 --oobi http://127.0.0.1:5642/oobi/EI0IoYyHxXc7_uQyaN2WocSC3lRZsvrDAPbREOw7fM0_/witness +kli oobi resolve --name issuer2 --oobi-alias issuee1 --oobi http://127.0.0.1:5642/oobi/EI0IoYyHxXc7_uQyaN2WocSC3lRZsvrDAPbREOw7fM0_/witness +kli oobi resolve --name issuer1 --oobi-alias issuee2 --oobi http://127.0.0.1:5642/oobi/EPw5WQAFcNXXSbg_pTKgh8-K_rfXnD1uDKS13OeNHkKE/witness +kli oobi resolve --name issuer2 --oobi-alias issuee2 --oobi http://127.0.0.1:5642/oobi/EPw5WQAFcNXXSbg_pTKgh8-K_rfXnD1uDKS13OeNHkKE/witness + +## Load Data OOBI for schema of credential to issue +kli oobi resolve --name issuer1 --oobi-alias vc --oobi http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao +kli oobi resolve --name issuer2 --oobi-alias vc --oobi http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao +kli oobi resolve --name issuee1 --oobi-alias vc --oobi http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao +kli oobi resolve --name issuee2 --oobi-alias vc --oobi http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao + +# Run the follow in parallel and wait for the issuer group to be created: +kli multisig incept --name issuer1 --alias issuer1 --group issuer --file ${KERI_DEMO_SCRIPT_DIR}/data/issuer-multisig-sample.json & +pid=$! +PID_LIST+=" $pid" + +kli multisig join --name issuer2 +pid=$! +PID_LIST+=" $pid" + +wait $PID_LIST + +# Run the follow in parallel and wait for the issuee group to be created: +kli multisig incept --name issuee1 --alias issuee1 --group issuee --file ${KERI_DEMO_SCRIPT_DIR}/data/issuee-multisig-sample.json & +pid=$! +PID_LIST+=" $pid" + +kli multisig join --name issuee2 +pid=$! +PID_LIST+=" $pid" + +wait $PID_LIST + +# Introduce issuer issuer issuer to issuees +kli oobi resolve --name issuer1 --oobi-alias issuee --oobi http://127.0.0.1:5642/oobi/ELkmm28zQEyxkryJZQ4WVT4fjukklM4dR91l2DQfQHZK/witness +kli oobi resolve --name issuer2 --oobi-alias issuee --oobi http://127.0.0.1:5642/oobi/ELkmm28zQEyxkryJZQ4WVT4fjukklM4dR91l2DQfQHZK/witness + +kli oobi resolve --name issuee1 --oobi-alias issuer --oobi http://127.0.0.1:5642/oobi/ELrnb8aI_wy2q_sSbCAwkgy2kOdMpRI1urFrhQiMJGLW/witness +kli oobi resolve --name issuee2 --oobi-alias issuer --oobi http://127.0.0.1:5642/oobi/ELrnb8aI_wy2q_sSbCAwkgy2kOdMpRI1urFrhQiMJGLW/witness + +# Create a credential registry owned by the issuer issuer +kli vc registry incept --name issuer1 --alias issuer --registry-name vLEI --usage "Issue vLEIs" --nonce AHSNDV3ABI6U8OIgKaj3aky91ZpNL54I5_7-qwtC6q2s & +pid=$! +PID_LIST=" $pid" + +echo "issuer2 looking to join credential registry creation" +kli multisig join --name issuer2 + +wait $PID_LIST + +# Issue Credential +kli vc create --name issuer1 --alias issuer --registry-name vLEI --schema EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao --recipient ELkmm28zQEyxkryJZQ4WVT4fjukklM4dR91l2DQfQHZK --data @${KERI_DEMO_SCRIPT_DIR}/data/credential-data.json & +pid=$! +PID_LIST+=" $pid" + +echo "issuer2 looking to join credential creation" +kli multisig join --name issuer2 + +wait $PID_LIST + +SAID=$(kli vc list --name issuer1 --alias issuer --issued --said) + +kli ipex grant --name issuer1 --alias issuer --said "${SAID}" --recipient ELkmm28zQEyxkryJZQ4WVT4fjukklM4dR91l2DQfQHZK & +pid=$! +PID_LIST="$pid" + +kli ipex join --name issuer2 + +wait ${PID_LIST} + +echo "Polling for issuee1's IPEX message..." +SAID=$(kli ipex list --name issuee1 --alias issuee1 --poll --said | sed -n '1 p') + +echo "Polling for issuee2's IPEX message..." +kli ipex list --name issuee2 --alias issuee2 --poll --said + +echo "Issuee1 Admitting GRANT ${SAID}" +kli ipex admit --name issuee1 --alias issuee --said "${SAID}" & +pid=$! +PID_LIST="$pid" + +echo "Issuee2 Admitting GRANT ${SAID}" +kli ipex join --name issuee2 + +wait ${PID_LIST} + +kli vc list --name issuee1 --alias issuee + +echo "Issuer1 checking for ADMIT" +kli ipex list --name issuer1 --alias issuer --poll diff --git a/scripts/demo/credentials/multisig-issuer-interactive.sh b/scripts/demo/credentials/multisig-issuer-interactive.sh index 281d1bb6b..c26b265d0 100755 --- a/scripts/demo/credentials/multisig-issuer-interactive.sh +++ b/scripts/demo/credentials/multisig-issuer-interactive.sh @@ -39,9 +39,12 @@ kli multisig incept --name multisig1 --alias multisig1 --group multisig --file $ pid=$! PID_LIST+=" $pid" -kli multisig incept --name multisig2 --alias multisig2 --group multisig --file ${KERI_DEMO_SCRIPT_DIR}/data/multisig-sample.json & -pid=$! -PID_LIST+=" $pid" +#kli multisig incept --name multisig2 --alias multisig2 --group multisig --file ${KERI_DEMO_SCRIPT_DIR}/data/multisig-sample.json & +#pid=$! +#PID_LIST+=" $pid" +# +echo "Multisig2 looking to join inception event" +kli multisig join --name multisig2 wait $PID_LIST kli oobi resolve --name holder --oobi-alias multisig --oobi http://127.0.0.1:5642/oobi/EC61gZ9lCKmHAS7U5ehUfEbGId5rcY0D7MirFZHDQcE2/witness @@ -77,7 +80,7 @@ kli ipex grant --name multisig1 --alias multisig --said "${SAID}" --recipient EL pid=$! PID_LIST="$pid" -kli multisig join --name multisig2 +kli ipex join --name multisig2 wait ${PID_LIST} diff --git a/scripts/demo/credentials/multisig-issuer.sh b/scripts/demo/credentials/multisig-issuer.sh index 69b2da044..a9f550669 100755 --- a/scripts/demo/credentials/multisig-issuer.sh +++ b/scripts/demo/credentials/multisig-issuer.sh @@ -102,7 +102,7 @@ echo "Polling for holder's IPEX message..." SAID=$(kli ipex list --name holder --alias holder --poll --said) echo "Admitting GRANT ${SAID}" -kli ipex admit --name holder --alias holder --said "${SAID}" +kli ipex admit --name holder --alias holder --said "${SAID}" --time "${TIME}" kli vc list --name holder --alias holder --poll diff --git a/scripts/demo/data/issuee-1-sample.json b/scripts/demo/data/issuee-1-sample.json new file mode 100644 index 000000000..8cd480e60 --- /dev/null +++ b/scripts/demo/data/issuee-1-sample.json @@ -0,0 +1,14 @@ +{ + "transferable": true, + "wits": [ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ], + "toad": 2, + "icount": 1, + "ncount": 1, + "isith": "1", + "nsith": "1" +} + diff --git a/scripts/demo/data/issuee-2-sample.json b/scripts/demo/data/issuee-2-sample.json new file mode 100644 index 000000000..8cd480e60 --- /dev/null +++ b/scripts/demo/data/issuee-2-sample.json @@ -0,0 +1,14 @@ +{ + "transferable": true, + "wits": [ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ], + "toad": 2, + "icount": 1, + "ncount": 1, + "isith": "1", + "nsith": "1" +} + diff --git a/scripts/demo/data/issuee-multisig-sample.json b/scripts/demo/data/issuee-multisig-sample.json new file mode 100644 index 000000000..4866b0a1c --- /dev/null +++ b/scripts/demo/data/issuee-multisig-sample.json @@ -0,0 +1,15 @@ +{ + "aids": [ + "EI0IoYyHxXc7_uQyaN2WocSC3lRZsvrDAPbREOw7fM0_", + "EPw5WQAFcNXXSbg_pTKgh8-K_rfXnD1uDKS13OeNHkKE" + ], + "transferable": true, + "wits": [ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ], + "toad": 3, + "isith": "2", + "nsith": "2" +} diff --git a/scripts/demo/data/issuer-1-sample.json b/scripts/demo/data/issuer-1-sample.json new file mode 100644 index 000000000..ad8882295 --- /dev/null +++ b/scripts/demo/data/issuer-1-sample.json @@ -0,0 +1,13 @@ +{ + "transferable": true, + "wits": [ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ], + "toad": 2, + "icount": 1, + "ncount": 1, + "isith": "1", + "nsith": "1" +} diff --git a/scripts/demo/data/issuer-2-sample.json b/scripts/demo/data/issuer-2-sample.json new file mode 100644 index 000000000..ad8882295 --- /dev/null +++ b/scripts/demo/data/issuer-2-sample.json @@ -0,0 +1,13 @@ +{ + "transferable": true, + "wits": [ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ], + "toad": 2, + "icount": 1, + "ncount": 1, + "isith": "1", + "nsith": "1" +} diff --git a/scripts/demo/data/issuer-multisig-sample.json b/scripts/demo/data/issuer-multisig-sample.json new file mode 100644 index 000000000..6afab98c3 --- /dev/null +++ b/scripts/demo/data/issuer-multisig-sample.json @@ -0,0 +1,15 @@ +{ + "aids": [ + "EEVlFHcMWAQNwezHjyKK5cKKzF6zgLlnrLyi_CcAEXCs", + "EFJtDtSoE6XOOqLoLvYoB7ctCzMtJDiAJltnXiK_EdlM" + ], + "transferable": true, + "wits": [ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ], + "toad": 3, + "isith": "2", + "nsith": "2" +} diff --git a/scripts/demo/data/multisig-join-sample.json b/scripts/demo/data/multisig-join-sample.json new file mode 100644 index 000000000..1e9201cf4 --- /dev/null +++ b/scripts/demo/data/multisig-join-sample.json @@ -0,0 +1,15 @@ +{ + "aids": [ + "EKJ6tNVUGbdaiwx2nWDCFXG-_PY_AzESOcoKlm0kRNP3", + "EFY7MixHb0so4WFFHw6btOPc5qeeWfPm7v5MJWcdcbyG" + ], + "transferable": true, + "wits": [ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ], + "toad": 2, + "isith": "2", + "nsith": "2" +} diff --git a/scripts/demo/test_scripts.sh b/scripts/demo/test_scripts.sh index 129723aab..7138d6bd8 100755 --- a/scripts/demo/test_scripts.sh +++ b/scripts/demo/test_scripts.sh @@ -63,3 +63,9 @@ printf "Running challenge.sh" printf "\n************************************\n" "${script_dir}/basic/challenge.sh" isSuccess + +printf "\n************************************\n" +printf "Running multisig-join.sh" +printf "\n************************************\n" +"${script_dir}/basic/multisig-join.sh" +isSuccess diff --git a/setup.py b/setup.py index 565f98f88..95fd2e053 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ from setuptools import find_packages, setup setup( name='keri', - version='1.1.6', # also change in src/keri/__init__.py + version='1.1.33', # also change in src/keri/__init__.py license='Apache Software License 2.0', description='Key Event Receipt Infrastructure', long_description="KERI Decentralized Key Management Infrastructure", @@ -51,7 +51,7 @@ 'Operating System :: Unix', 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', - 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: Implementation :: CPython', # uncomment if you test on these interpreters: # 'Programming Language :: Python :: Implementation :: PyPy', @@ -68,32 +68,33 @@ keywords=[ # eg: 'keyword1', 'keyword2', 'keyword3', ], - python_requires='>=3.10.4', + python_requires='>=3.12.2', install_requires=[ - 'lmdb>=1.4.1', - 'pysodium>=0.7.17', - 'blake3>=0.4.1', - 'msgpack>=1.0.8', - 'cbor2>=5.6.2', - 'multidict>=6.0.5', - 'ordered-set>=4.1.0', - 'hio>=0.6.10', - 'multicommand>=1.0.0', - 'jsonschema>=4.21.1', - 'falcon>=3.1.3', - 'hjson>=3.1.0', - 'PyYaml>=6.0.1', - 'apispec>=6.6.0', - 'mnemonic>=0.21', - 'PrettyTable>=3.10.0', - 'http_sfv>=0.9.9', - 'cryptography>=42.0.5' + 'lmdb>=1.4.1', + 'pysodium>=0.7.17', + 'blake3>=0.4.1', + 'msgpack>=1.0.8', + 'cbor2>=5.6.2', + 'multidict>=6.0.5', + 'ordered-set>=4.1.0', + 'hio>=0.6.14', + 'multicommand>=1.0.0', + 'jsonschema>=4.21.1', + 'falcon>=3.1.3', + 'hjson>=3.1.0', + 'PyYaml>=6.0.1', + 'apispec>=6.6.0', + 'mnemonic>=0.21', + 'PrettyTable>=3.10.0', + 'http_sfv>=0.9.9', + 'cryptography>=42.0.5', + 'semver>=3.0.2' ], extras_require={ }, tests_require=[ - 'coverage>=6.5.0', - 'pytest>=7.2.0', + 'coverage>=7.4.4', + 'pytest>=8.1.1', 'pytest-shell>=0.3.2' ], setup_requires=[ diff --git a/src/keri/__init__.py b/src/keri/__init__.py index d39f4a1bd..55a0b4264 100644 --- a/src/keri/__init__.py +++ b/src/keri/__init__.py @@ -1,5 +1,5 @@ # -*- encoding: utf-8 -*- -__version__ = '1.1.6' # also change in setup.py +__version__ = '1.1.33' # also change in setup.py diff --git a/src/keri/app/apping.py b/src/keri/app/apping.py index 7478aa1d2..4464633db 100644 --- a/src/keri/app/apping.py +++ b/src/keri/app/apping.py @@ -4,8 +4,6 @@ keri.app.apping module """ -import cmd - from hio.base import doing from hio.core.serial import serialing diff --git a/src/keri/app/cli/commands/challenge/generate.py b/src/keri/app/cli/commands/challenge/generate.py index e26e6f10e..169ea93c7 100644 --- a/src/keri/app/cli/commands/challenge/generate.py +++ b/src/keri/app/cli/commands/challenge/generate.py @@ -6,7 +6,7 @@ import argparse import json -from hio import help +from keri import help from hio.base import doing from mnemonic import mnemonic diff --git a/src/keri/app/cli/commands/challenge/verify.py b/src/keri/app/cli/commands/challenge/verify.py index db27501ee..f82fbf1c4 100644 --- a/src/keri/app/cli/commands/challenge/verify.py +++ b/src/keri/app/cli/commands/challenge/verify.py @@ -8,7 +8,7 @@ import datetime import sys -from hio import help +from keri import help from hio.base import doing from keri.app import indirecting, challenging, connecting, signaling diff --git a/src/keri/app/cli/commands/contacts/list.py b/src/keri/app/cli/commands/contacts/list.py index fa7527d79..b93c1e248 100644 --- a/src/keri/app/cli/commands/contacts/list.py +++ b/src/keri/app/cli/commands/contacts/list.py @@ -7,8 +7,9 @@ import argparse import json -from hio import help +from keri import help from hio.base import doing +from keri import kering from keri.app import connecting from keri.app.cli.common import existing @@ -53,8 +54,14 @@ def list(tymth, tock=0.0, **opts): challenges = [] for said in valid: - exn = hby.db.exns.get(keys=(said,)) - challenges.append(dict(dt=exn.ked['dt'], words=exn.ked['a']['words'])) + try: + exn = hby.db.exns.get(keys=(said,)) + except kering.ValidationError: + val = hby.db.getVal(db=hby.db.exns.sdb, key=hby.db.exns._tokey((said,))) + d = json.loads(bytes(val).decode("utf-8")) + challenges.append(dict(dt=d['dt'], words=d['a']['words'])) + else: + challenges.append(dict(dt=exn.ked['dt'], words=exn.ked['a']['words'])) c["challenges"] = challenges diff --git a/src/keri/app/cli/commands/did/generate.py b/src/keri/app/cli/commands/did/generate.py index 9e0cb28d1..5398ca35b 100644 --- a/src/keri/app/cli/commands/did/generate.py +++ b/src/keri/app/cli/commands/did/generate.py @@ -9,7 +9,7 @@ from urllib.parse import urlparse import sys -from hio import help +from keri import help from hio.base import doing from keri import kering diff --git a/src/keri/app/cli/commands/ends/add.py b/src/keri/app/cli/commands/ends/add.py index e672abfda..76cb623ec 100644 --- a/src/keri/app/cli/commands/ends/add.py +++ b/src/keri/app/cli/commands/ends/add.py @@ -6,7 +6,7 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri import kering diff --git a/src/keri/app/cli/commands/ends/export.py b/src/keri/app/cli/commands/ends/export.py index e1d54a2f3..e340272d8 100644 --- a/src/keri/app/cli/commands/ends/export.py +++ b/src/keri/app/cli/commands/ends/export.py @@ -6,12 +6,12 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri import kering from keri.app.cli.common import existing -from keri.core import coring, eventing +from keri.core import eventing logger = help.ogler.getLogger() diff --git a/src/keri/app/cli/commands/ends/list.py b/src/keri/app/cli/commands/ends/list.py index 533e39d95..1f0035a3b 100644 --- a/src/keri/app/cli/commands/ends/list.py +++ b/src/keri/app/cli/commands/ends/list.py @@ -7,13 +7,11 @@ import argparse import json -from hio import help +from keri import help from hio.base import doing from keri import kering -from keri.app import indirecting, habbing, forwarding, grouping from keri.app.cli.common import existing -from keri.core import eventing, parsing, coring logger = help.ogler.getLogger() diff --git a/src/keri/app/cli/commands/escrow/__init__.py b/src/keri/app/cli/commands/escrow/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/keri/app/cli/commands/escrow/clear.py b/src/keri/app/cli/commands/escrow/clear.py new file mode 100644 index 000000000..e6e98924e --- /dev/null +++ b/src/keri/app/cli/commands/escrow/clear.py @@ -0,0 +1,57 @@ +# -*- encoding: utf-8 -*- +""" +KERI +keri.kli.commands.escrow module + +""" +import argparse + +from keri import help +from hio.base import doing +from keri.app.cli.common import existing +from keri.vdr import viring + +logger = help.ogler.getLogger() + +parser = argparse.ArgumentParser(description='Clear escrows') +parser.set_defaults(handler=lambda args: handler(args), + transferable=True) +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--passcode', '-p', help='21 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) # passcode => bran +parser.add_argument('--force', '-f', action="store_true", required=False, + help='True means perform clear without prompting the user') + + +def handler(args): + if not args.force: + print() + print("This command will clear all escrows and is not reversible.") + print() + yn = input("Are you sure you want to continue? [y|N]: ") + + if yn not in ("y", "Y"): + print("...exiting") + return [] + + kwa = dict(args=args) + return [doing.doify(clear, **kwa)] + + +def clear(tymth, tock=0.0, **opts): + """ Command line clear handler + """ + _ = (yield tock) + args = opts["args"] + name = args.name + base = args.base + bran = args.bran + logger.setLevel("INFO") + + with existing.existingHby(name=name, base=base, bran=bran) as hby: + hby.db.clearEscrows() + reger = viring.Reger(name=hby.name, db=hby.db, temp=False) + reger.clearEscrows() + diff --git a/src/keri/app/cli/commands/escrow.py b/src/keri/app/cli/commands/escrow/list.py similarity index 57% rename from src/keri/app/cli/commands/escrow.py rename to src/keri/app/cli/commands/escrow/list.py index 24d78f335..dd4aa8c5c 100644 --- a/src/keri/app/cli/commands/escrow.py +++ b/src/keri/app/cli/commands/escrow/list.py @@ -1,13 +1,13 @@ # -*- encoding: utf-8 -*- """ KERI -keri.kli.commands module +keri.kli.commands.escrow module """ import argparse import json -from hio import help +from keri import help from hio.base import doing from keri.core import eventing @@ -18,13 +18,13 @@ logger = help.ogler.getLogger() -parser = argparse.ArgumentParser(description='Initialize a prefix') +parser = argparse.ArgumentParser(description='Views events in escrow state.') parser.set_defaults(handler=lambda args: handler(args), transferable=True) parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', required=False, default="") -parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', +parser.add_argument('--passcode', '-p', help='21 character encryption passcode for keystore (is not saved)', dest="bran", default=None) # passcode => bran parser.add_argument("--escrow", "-e", help="show values for one specific escrow", default=None) @@ -49,18 +49,25 @@ def escrows(tymth, tock=0.0, **opts): try: with existing.existingHby(name=name, base=base, bran=bran) as hby: - reger = viring.Reger(name=hby.name, db=hby.db, temp=False) - escrows = dict() - if (not escrow) or escrow == "out-of-order-events": - oots = list() + + # KEL / Baser escrows + + if (not escrow) or escrow == "unverified-receipts": + escrows["unverified-receipts"] = sum(1 for key, _ in hby.db.getUreItemIter()) + + if (not escrow) or escrow == "verified-receipts": + escrows["verified-receipts"] = sum(1 for key, _ in hby.db.getVreItemIter()) + + if (not escrow) or escrow == "partially-signed-events": + pses = list() key = ekey = b'' # both start same. when not same means escrows found - while True: - for ekey, edig in hby.db.getOoeItemsNextIter(key=key): - pre, sn = dbing.splitKeySN(ekey) # get pre and sn from escrow item + while True: # break when done + for ekey, edig in hby.db.getPseItemsNextIter(key=key): + pre, sn = dbing.splitSnKey(ekey) # get pre and sn from escrow item try: - oots.append(eventing.loadEvent(hby.db, pre, edig)) + pses.append(eventing.loadEvent(hby.db, pre, edig)) except ValueError as e: raise e @@ -68,14 +75,14 @@ def escrows(tymth, tock=0.0, **opts): break key = ekey # setup next while iteration, with key after ekey - escrows["out-of-order-events"] = oots + escrows["partially-signed-events"] = pses if (not escrow) or escrow == "partially-witnessed-events": pwes = list() key = ekey = b'' # both start same. when not same means escrows found while True: # break when done - for ekey, edig in hby.db.getPweItemsNextIter(key=key): - pre, sn = dbing.splitKeySN(ekey) # get pre and sn from escrow item + for ekey, edig in hby.db.getPweItemIter(key=key): + pre, sn = dbing.splitSnKey(ekey) # get pre and sn from escrow item try: pwes.append(eventing.loadEvent(hby.db, pre, edig)) @@ -88,15 +95,18 @@ def escrows(tymth, tock=0.0, **opts): escrows["partially-witnessed-events"] = pwes - if (not escrow) or escrow == "partially-signed-events": - pses = list() + if (not escrow) or escrow == "unverified-event-indexed-couples": + escrows["unverified-event-indexed-couples"] = sum(1 for key, _ in hby.db.getUweItemIter()) + + if (not escrow) or escrow == "out-of-order-events": + oots = list() key = ekey = b'' # both start same. when not same means escrows found - while True: # break when done - for ekey, edig in hby.db.getPseItemsNextIter(key=key): - pre, sn = dbing.splitKeySN(ekey) # get pre and sn from escrow item + while True: + for ekey, edig in hby.db.getOoeItemIter(key=key): + pre, sn = dbing.splitSnKey(ekey) # get pre and sn from escrow item try: - pses.append(eventing.loadEvent(hby.db, pre, edig)) + oots.append(eventing.loadEvent(hby.db, pre, edig)) except ValueError as e: raise e @@ -104,14 +114,14 @@ def escrows(tymth, tock=0.0, **opts): break key = ekey # setup next while iteration, with key after ekey - escrows["partially-signed-events"] = pses + escrows["out-of-order-events"] = oots if (not escrow) or escrow == "likely-duplicitous-events": ldes = list() key = ekey = b'' # both start same. when not same means escrows found while True: # break when done - for ekey, edig in hby.db.getLdeItemsNextIter(key=key): - pre, sn = dbing.splitKeySN(ekey) # get pre and sn from escrow item + for ekey, edig in hby.db.getLdeItemIter(key=key): + pre, sn = dbing.splitSnKey(ekey) # get pre and sn from escrow item try: ldes.append(eventing.loadEvent(hby.db, pre, edig)) @@ -124,6 +134,48 @@ def escrows(tymth, tock=0.0, **opts): escrows["likely-duplicitous-events"] = ldes + if (not escrow) or escrow == "query-not-found": + escrows["query-not-found"] = sum(1 for key, _ in hby.db.getQnfItemsNextIter()) + + if (not escrow) or escrow == "partially-delegated-events": + escrows["partially-delegated-events"] = sum(1 for key, _ in hby.db.getPdeItemsNextIter()) + + if (not escrow) or escrow == "reply": + escrows["reply"] = sum(1 for key, _ in hby.db.rpes.getItemIter()) + + if (not escrow) or escrow == "failed-oobi": + escrows["failed-oobi"] = sum(1 for key, _ in hby.db.eoobi.getItemIter()) + + if (not escrow) or escrow == 'group-partial-witness': + escrows["group-partial-witness"] = sum(1 for key, _ in hby.db.gpwe.getItemIter()) + + if (not escrow) or escrow == 'group-delegate': + escrows["group-delegate"] = sum(1 for key, _ in hby.db.gdee.getItemIter()) + + if (not escrow) or escrow == 'delegated-partial-witness': + escrows["delegated-partial-witness"] = sum(1 for key, _ in hby.db.dpwe.getItemIter()) + + if (not escrow) or escrow == 'group-partial-signed': + escrows["group-partial-signed"] = sum(1 for key, _ in hby.db.gpse.getItemIter()) + + if (not escrow) or escrow == 'exchange-partial-signed': + escrows["exchange-partial-signed"] = sum(1 for key, _ in hby.db.epse.getItemIter()) + + if (not escrow) or escrow == 'delegated-unanchored': + escrows["delegated-unanchored"] = sum(1 for key, _ in hby.db.dune.getItemIter()) + + # TEL / Reger escrows + reger = viring.Reger(name=hby.name, db=hby.db, temp=False) + + if (not escrow) or escrow == 'tel-out-of-order': + escrows["tel-out-of-order"] = sum(1 for key, _ in reger.getOotItemIter()) + + if (not escrow) or escrow == 'tel-partially-witnessed': + escrows["tel-partially-witnessed"] = sum(1 for key, _ in reger.getAllItemIter(reger.twes)) + + if (not escrow) or escrow == 'tel-anchorless': + escrows["tel-anchorless"] = sum(1 for key, _ in reger.getAllItemIter(reger.taes)) + if (not escrow) or escrow == "missing-registry-escrow": creds = list() for (said,), dater in reger.mre.getItemIter(): @@ -148,11 +200,19 @@ def escrows(tymth, tock=0.0, **opts): escrows["missing-schema-escrow"] = creds - print(json.dumps(escrows, indent=2)) + if (not escrow) or escrow == 'tel-missing-signature': + escrows["tel-missing-signature"] = sum(1 for key, _ in reger.cmse.getItemIter()) + + if (not escrow) or escrow == 'tel-partial-witness-escrow': + escrows["tel-partial-witness-escrow"] = sum(1 for key, _ in reger.tpwe.getItemIter()) - if not(escrow) or escrow == 'tel-partial-witness-escrow': - for (regk, snq), (prefixer, seqner, saider) in reger.tpwe.getItemIter(): - pass + if (not escrow) or escrow == 'tel-multisig': + escrows["tel-multisig"] = sum(1 for key, _ in reger.tmse.getItemIter()) + + if (not escrow) or escrow == 'tel-event-dissemination': + escrows["tel-event-dissemination"] = sum(1 for key, _ in reger.tede.getItemIter()) + + print(json.dumps(escrows, indent=2)) except ConfigurationError as e: print(f"identifier prefix for {name} does not exist, incept must be run first", ) diff --git a/src/keri/app/cli/commands/export.py b/src/keri/app/cli/commands/export.py index 7fe7dfff9..a2ef4d2b7 100644 --- a/src/keri/app/cli/commands/export.py +++ b/src/keri/app/cli/commands/export.py @@ -7,11 +7,11 @@ import argparse import sys -from hio import help +from keri import help from hio.base import doing from keri.app.cli.common import existing -from keri.core import coring, serdering +from keri.core import serdering logger = help.ogler.getLogger() diff --git a/src/keri/app/cli/commands/incept.py b/src/keri/app/cli/commands/incept.py index fbc59560a..87977ebd5 100644 --- a/src/keri/app/cli/commands/incept.py +++ b/src/keri/app/cli/commands/incept.py @@ -6,7 +6,7 @@ import argparse from dataclasses import dataclass -from hio import help +from keri import help from hio.base import doing from keri.app import habbing, agenting, indirecting, configing, delegating, forwarding diff --git a/src/keri/app/cli/commands/init.py b/src/keri/app/cli/commands/init.py index aa8479ec6..04a1ae45c 100644 --- a/src/keri/app/cli/commands/init.py +++ b/src/keri/app/cli/commands/init.py @@ -6,7 +6,7 @@ import argparse import getpass -from hio import help +from keri import help from hio.base import doing import keri.app.oobiing diff --git a/src/keri/app/cli/commands/ipex/admit.py b/src/keri/app/cli/commands/ipex/admit.py index f45918aa2..842533805 100644 --- a/src/keri/app/cli/commands/ipex/admit.py +++ b/src/keri/app/cli/commands/ipex/admit.py @@ -31,6 +31,7 @@ parser.add_argument("--said", "-s", help="SAID of the exn grant message to admit", required=True) parser.add_argument("--message", "-m", help="optional human readable message to " "send to recipient", required=False, default="") +parser.add_argument("--time", help="timestamp", required=False, default=None) def handler(args): @@ -39,15 +40,17 @@ def handler(args): base=args.base, bran=args.bran, said=args.said, - message=args.message) + message=args.message, + timestamp=args.time) return [ed] class AdmitDoer(doing.DoDoer): - def __init__(self, name, alias, base, bran, said, message): + def __init__(self, name, alias, base, bran, said, message, timestamp ): self.said = said self.message = message + self.timestamp = timestamp self.hby = existing.setupHby(name=name, base=base, bran=bran) self.hab = self.hby.habByName(alias) self.rgy = credentialing.Regery(hby=self.hby, name=name, base=base) @@ -118,31 +121,36 @@ def admitDo(self, tymth, tock=0.0): yield self.tock recp = grant.ked['i'] - exn, atc = protocoling.ipexAdmitExn(hab=self.hab, message=self.message, grant=grant) + exn, atc = protocoling.ipexAdmitExn(hab=self.hab, message=self.message, grant=grant, dt=self.timestamp) msg = bytearray(exn.raw) msg.extend(atc) parsing.Parser().parseOne(ims=bytes(msg), exc=self.exc) + sender = self.hab if isinstance(self.hab, habbing.GroupHab): + sender = self.hab.mhab wexn, watc = grouping.multisigExn(self.hab, exn=msg) smids = self.hab.db.signingMembers(pre=self.hab.pre) smids.remove(self.hab.mhab.pre) - for recp in smids: # this goes to other participants only as a signaling mechanism - postman = forwarding.StreamPoster(hby=self.hby, hab=self.hab.mhab, recp=recp, topic="multisig") + for part in smids: # this goes to other participants only as a signaling mechanism + postman = forwarding.StreamPoster(hby=self.hby, hab=self.hab.mhab, recp=part, topic="multisig") postman.send(serder=wexn, attachment=watc) doer = doing.DoDoer(doers=postman.deliver()) self.extend([doer]) - while not self.exc.complete(said=wexn.said): + while not self.exc.complete(said=exn.said): yield self.tock if self.exc.lead(self.hab, said=exn.said): print(f"Sending admit message to {recp}") - postman = forwarding.StreamPoster(hby=self.hby, hab=self.hab, recp=recp, topic="credential") + postman = forwarding.StreamPoster(hby=self.hby, hab=sender, recp=recp, topic="credential") + + atc = exchanging.serializeMessage(self.hby, exn.said) + del atc[:exn.size] postman.send(serder=exn, attachment=atc) @@ -152,4 +160,7 @@ def admitDo(self, tymth, tock=0.0): while not doer.done: yield self.tock + print(f"... admit message sent") + self.remove([doer]) + self.remove(self.toRemove) diff --git a/src/keri/app/cli/commands/ipex/grant.py b/src/keri/app/cli/commands/ipex/grant.py index ba9dcb232..38fc0d33b 100644 --- a/src/keri/app/cli/commands/ipex/grant.py +++ b/src/keri/app/cli/commands/ipex/grant.py @@ -162,6 +162,6 @@ def grantDo(self, tymth, tock=0.0): yield self.tock print(f"... grant message sent") - self.remove([postman]) + self.remove([doer]) self.remove(self.toRemove) diff --git a/src/keri/app/cli/commands/ipex/join.py b/src/keri/app/cli/commands/ipex/join.py new file mode 100644 index 000000000..0f8401514 --- /dev/null +++ b/src/keri/app/cli/commands/ipex/join.py @@ -0,0 +1,275 @@ +# -*- encoding: utf-8 -*- +""" +KERI +keri.kli.commands.ipex module + +Join multisig ipex messages +""" +import argparse + +from hio.base import doing + +from keri import help +from keri.app import habbing, indirecting, agenting, notifying, grouping, connecting, forwarding +from keri.app.cli.common import existing +from keri.core import parsing, routing, serdering, coring +from keri.peer import exchanging +from keri.vdr import verifying, credentialing + +logger = help.ogler.getLogger() + +parser = argparse.ArgumentParser(description='Join group multisig ipex events') +parser.set_defaults(handler=lambda args: join(args)) +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) # passcode => bran +parser.add_argument("--auto", "-Y", help="auto approve any delegation request non-interactively", action="store_true") + + +def join(args): + """ Wait for and provide interactive confirmation of group multisig inception, rotation or interaction events + + Parameters: + args(Namespace): parsed arguements namespace object + + """ + name = args.name + base = args.base + bran = args.bran + auto = args.auto + + joinDoer = JoinDoer(name=name, base=base, bran=bran, auto=auto) + + doers = [joinDoer] + return doers + + +class JoinDoer(doing.DoDoer): + """ Doist doer capable of polling for group ipex events and prompting user for action + + """ + + def __init__(self, name, base, bran, auto=False): + """ Create doer for polling for group ipex events and either approve automatically or prompt user + + Parameters: + name (str): database environment name + base (str): database directory prefix + bran (str): passcode to unlock keystore + + """ + self.hby = existing.setupHby(name=name, base=base, bran=bran) + self.rgy = credentialing.Regery(hby=self.hby, name=name, base=base) + self.hbyDoer = habbing.HaberyDoer(habery=self.hby) # setup doer + self.witq = agenting.WitnessInquisitor(hby=self.hby) + self.org = connecting.Organizer(hby=self.hby) + self.notifier = notifying.Notifier(hby=self.hby) + self.exc = exchanging.Exchanger(hby=self.hby, handlers=[]) + self.verifier = verifying.Verifier(hby=self.hby, reger=self.rgy.reger) + self.rvy = routing.Revery(db=self.hby.db, lax=True) + self.hby.kvy.registerReplyRoutes(self.rvy.rtr) + self.psr = parsing.Parser(kvy=self.hby.kvy, tvy=self.rgy.tvy, rvy=self.rvy, vry=self.verifier, exc=self.exc) + + mux = grouping.Multiplexor(hby=self.hby, notifier=self.notifier) + grouping.loadHandlers(exc=self.exc, mux=mux) + self.counselor = grouping.Counselor(hby=self.hby) + + self.registrar = credentialing.Registrar(hby=self.hby, rgy=self.rgy, counselor=self.counselor) + self.credentialer = credentialing.Credentialer(hby=self.hby, rgy=self.rgy, registrar=self.registrar, + verifier=self.verifier) + + self.mbx = indirecting.MailboxDirector(hby=self.hby, exc=self.exc, topics=['/receipt', '/multisig', '/replay', + '/delegate']) + self.postman = forwarding.Poster(hby=self.hby) + + doers = [self.hbyDoer, self.witq, self.mbx, self.counselor, self.registrar, self.credentialer, self.postman] + self.toRemove = list(doers) + doers.extend([doing.doify(self.joinDo)]) + self.auto = auto + super(JoinDoer, self).__init__(doers=doers) + + def joinDo(self, tymth, tock=0.0): + """ + Parameters: + tymth (function): injected function wrapper closure returned by .tymen() of + Tymist instance. Calling tymth() returns associated Tymist .tyme. + tock (float): injected initial tock value + + Returns: doifiable Doist compatible generator method + """ + # enter context + self.wind(tymth) + self.tock = tock + _ = (yield self.tock) + + print("Waiting for group ipex events...") + + while True: + + found = False + for keys, notice in self.notifier.noter.notes.getItemIter(): + attrs = notice.attrs + route = attrs['r'] + + if route == '/multisig/exn': + said = attrs["d"] + exn, pathed = exchanging.cloneMessage(self.hby, said=said) + embeds = exn.ked['e'] + + if embeds['exn']['r'].startswith("/ipex"): + done = yield from self.ipex(exn, pathed) + + if done: + self.notifier.noter.notes.rem(keys=keys) + + else: + delete = input(f"\nDelete event [Y|n]? ") + if delete in ("Y", "y"): + self.notifier.noter.notes.rem(keys=keys) + found = True + if found: + break + + yield self.tock + + self.remove(self.toRemove) + + def ipex(self, exn, pathed): + """ Handle exn messages for ipex + + Parameters: + exn (SerderKERI): exn message + pathed (dict): pathed attachments dict + + Returns: + + """ + embeds = exn.ked['e'] + sender = exn.ked['i'] + + contact = self.org.get(sender) + senderAlias = contact['alias'] + + eexn = embeds['exn'] + + route = eexn['r'] + + group = eexn["i"] + hab = self.hby.habs[group] if group in self.hby.habs else None + if hab is None: + raise ValueError(f"message sender not a valid AID={group}") + + print() + print(f"Group IPEX Message proposal (from {senderAlias}):") + print(f" Message Type: {eexn['r']}") + print(f" Message SAID: {eexn['d']}") + print(f" Sending From: {hab.name} ({hab.pre})") + + match route: + case "/ipex/admit": + recp = yield from self.getAdmitRecp(eexn) + case "/ipex/agree": + recp = self.getAgreeRecp(eexn) + case "/ipex/apply": + recp = self.getApplyRecp(eexn) + case "/ipex/grant": + recp = self.getGrantRecp(eexn) + case "/ipex/offer": + recp = self.getOfferRecp(eexn) + case "/ipex/spurn": + recp = self.getSpurnRecp(eexn) + case _: + return False + + contact = self.org.get(recp) + if contact is not None and "alias" in contact: + print(f" Sending To: {contact['alias']} ({recp})") + else: + print(f" Sending To: Unknown AID ({recp})") + + if self.auto: + approve = True + else: + yn = input(f"\nApprove [Y|n]? ") + approve = yn in ('', 'y', 'Y') + + if approve: + eserder = serdering.SerderKERI(sad=eexn) + anc = bytearray(eserder.raw) + pathed["exn"] + self.psr.parseOne(ims=bytes(anc)) + + msg = hab.endorse(serder=eserder, last=False, pipelined=False) + msg = msg + pathed["exn"] + self.psr.parseOne(ims=bytes(msg)) + + smids = hab.db.signingMembers(pre=hab.pre) + smids.remove(hab.mhab.pre) + + for smid in smids: # this goes to other participants only as a signaling mechanism + rexn, atc = grouping.multisigExn(ghab=hab, exn=msg) + self.postman.send(src=hab.mhab.pre, + dest=smid, + topic="multisig", + serder=rexn, + attachment=atc) + while not self.postman.sent(said=rexn.said): + yield self.tock + + while not self.exc.complete(said=eserder.said): + self.exc.processEscrow() + yield self.tock + + if self.exc.lead(hab, said=exn.said): + print(f"Sending message {eserder.said} to {recp}") + atc = exchanging.serializeMessage(self.hby, eserder.said) + del atc[:eserder.size] + self.postman.send(src=hab.mhab.pre, + dest=recp, + topic="credential", + serder=eserder, + attachment=atc) + + while not self.postman.sent(said=eserder.said): + yield self.tock + + print("... ipex message sent") + return True + + return True + return False + + def getAdmitRecp(self, exn): + grant, pathed = exchanging.cloneMessage(self.hby, exn['p']) + if grant is None: + raise ValueError(f"exn message said={exn['p']} not found") + + embeds = grant.ked['e'] + acdc = embeds["acdc"] + for label in ("anc", "iss", "acdc"): + ked = embeds[label] + sadder = coring.Sadder(ked=ked) + ims = bytearray(sadder.raw) + pathed[label] + self.psr.parseOne(ims=ims) + + said = acdc["d"] + while not self.rgy.reger.saved.get(keys=said): + yield self.tock + + return acdc['i'] + + def getAgreeRecp(self, exn): + pass + + def getApplyRecp(self, exn): + return exn['a']['i'] + + def getGrantRecp(self, exn): + return exn['a']['i'] + + def getOfferRecp(self, exn): + pass + + def getSpurnRecp(self, exn): + pass diff --git a/src/keri/app/cli/commands/ipex/list.py b/src/keri/app/cli/commands/ipex/list.py index 4f59d9816..89449d87f 100644 --- a/src/keri/app/cli/commands/ipex/list.py +++ b/src/keri/app/cli/commands/ipex/list.py @@ -10,7 +10,7 @@ import os import sys -from hio import help +from keri import help from hio.base import doing from keri import kering diff --git a/src/keri/app/cli/commands/kevers.py b/src/keri/app/cli/commands/kevers.py index 3eaf42aca..3cc6eb905 100644 --- a/src/keri/app/cli/commands/kevers.py +++ b/src/keri/app/cli/commands/kevers.py @@ -8,12 +8,12 @@ import datetime import sys -from hio import help +from keri import help from hio.base import doing from keri.app import indirecting from keri.app.cli.common import displaying, existing -from keri.core import coring, serdering +from keri.core import serdering from keri.help import helping logger = help.ogler.getLogger() diff --git a/src/keri/app/cli/commands/list.py b/src/keri/app/cli/commands/list.py index d02f162ca..4d36d622f 100644 --- a/src/keri/app/cli/commands/list.py +++ b/src/keri/app/cli/commands/list.py @@ -6,11 +6,10 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app.cli.common import existing -from keri.kering import ConfigurationError logger = help.ogler.getLogger() diff --git a/src/keri/app/cli/commands/local/watch.py b/src/keri/app/cli/commands/local/watch.py index 6529f80e9..3558a0f44 100644 --- a/src/keri/app/cli/commands/local/watch.py +++ b/src/keri/app/cli/commands/local/watch.py @@ -9,12 +9,11 @@ import time from collections import namedtuple -from hio import help +from keri import help from hio.base import doing from keri.app import agenting, indirecting, habbing, forwarding from keri.app.cli.common import existing, terming from keri.app.habbing import GroupHab -from keri.core import coring logger = help.ogler.getLogger() diff --git a/src/keri/app/cli/commands/mailbox/debug.py b/src/keri/app/cli/commands/mailbox/debug.py index de7f425ba..6cf5fe964 100644 --- a/src/keri/app/cli/commands/mailbox/debug.py +++ b/src/keri/app/cli/commands/mailbox/debug.py @@ -6,15 +6,13 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri import kering -from keri.app import agenting, indirecting, habbing, httping -from keri.app.cli.common import displaying, existing +from keri.app import agenting, habbing, httping +from keri.app.cli.common import existing from keri.app.habbing import GroupHab -from keri.core import coring -from keri.kering import ConfigurationError logger = help.ogler.getLogger() diff --git a/src/keri/app/cli/commands/mailbox/list.py b/src/keri/app/cli/commands/mailbox/list.py new file mode 100644 index 000000000..369e749df --- /dev/null +++ b/src/keri/app/cli/commands/mailbox/list.py @@ -0,0 +1,68 @@ +# -*- encoding: utf-8 -*- +""" +KERI +keri.kli.commands module + +""" +import argparse + +from hio import help +from hio.base import doing + +from keri.app import connecting +from keri.app.cli.common import existing +from keri.kering import ConfigurationError, Roles + +logger = help.ogler.getLogger() + +parser = argparse.ArgumentParser(description='List current mailboxes') +parser.set_defaults(handler=lambda args: handle(args), + transferable=True) +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--alias', '-a', help='human readable alias for the identifier to whom the credential was issued', + required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) # passcode => bran + + +def handle(args): + """ Command line handler for adding an aid to a watcher's list of AIds to watch + + Parameters: + args(Namespace): parsed command line arguments + + """ + + kwa = dict(args=args) + return [doing.doify(listMailboxes, **kwa)] + + +def listMailboxes(tymth, tock=0.0, **opts): + """ Command line status handler + + """ + _ = (yield tock) + args = opts["args"] + name = args.name + alias = args.alias + base = args.base + bran = args.bran + + try: + with existing.existingHby(name=name, base=base, bran=bran) as hby: + org = connecting.Organizer(hby=hby) + if alias is None: + alias = existing.aliasInput(hby) + + hab = hby.habByName(alias) + + for (aid, role, eid), ender in hab.db.ends.getItemIter(keys=(hab.pre, Roles.mailbox)): + if ender.allowed: + contact = org.get(eid) + print(f"{contact['alias']}: {eid}") + + except ConfigurationError as e: + print(f"identifier prefix for {name} does not exist, incept must be run first", ) + return -1 diff --git a/src/keri/app/cli/commands/mailbox/update.py b/src/keri/app/cli/commands/mailbox/update.py index 0c191bb94..8ac16cd93 100644 --- a/src/keri/app/cli/commands/mailbox/update.py +++ b/src/keri/app/cli/commands/mailbox/update.py @@ -5,7 +5,7 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app.cli.common import existing diff --git a/src/keri/app/cli/commands/migrate.py b/src/keri/app/cli/commands/migrate.py deleted file mode 100644 index 0f4035f78..000000000 --- a/src/keri/app/cli/commands/migrate.py +++ /dev/null @@ -1,153 +0,0 @@ -# -*- encoding: utf-8 -*- -""" -KERI -keri.kli.commands module - -""" -import argparse - -from hio import help -from hio.base import doing - -from keri import kering -from keri.app.cli.common import existing -from keri.core import coring, serdering -from keri.db import koming, subing, dbing -from keri.db.basing import KeyStateRecord, StateEERecord -from keri.kering import ConfigurationError, Version -from keri.vdr import viring - -logger = help.ogler.getLogger() - -parser = argparse.ArgumentParser(description='View status of a local AID') -parser.set_defaults(handler=lambda args: handler(args), - transferable=True) -parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) -parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', - required=False, default="") -parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', - dest="bran", default=None) # passcode => bran -parser.add_argument('--force', action="store_true", required=False, - help='True means perform migration without prompting the user') - - -def handler(args): - if not args.force: - print() - print("This command will migrate your datastore to the next version of KERIpy and is not reversible.") - print("After this command, you will not be able to access your data store with this version.") - print() - yn = input("Are you sure you want to continue? [y|N]: ") - - if yn not in ("y", "Y"): - print("...exiting") - return [] - - kwa = dict(args=args) - return [doing.doify(migrate, **kwa)] - - -def migrate(tymth, tock=0.0, **opts): - """ Command line status handler - - """ - _ = (yield tock) - args = opts["args"] - name = args.name - base = args.base - bran = args.bran - - try: - with dbing.openLMDB(name=name, base=base, bran=bran, temp=False) as db: - print(db.path) - states = koming.Komer(db=db, - schema=dict, - subkey='stts.') - nstates = koming.Komer(db=db, - schema=KeyStateRecord, - subkey='stts.') - - for keys, sad in states.getItemIter(): - ksr = KeyStateRecord( - vn=Version, # version number as list [major, minor] - i=sad['i'], # qb64 prefix - s=sad['s'], # lowercase hex string no leading zeros - p=sad['p'], - d=sad['d'], - f=sad['f'], # lowercase hex string no leading zeros - dt=sad['dt'], - et=sad['et'], - kt=sad['kt'], - k=sad['k'], - nt=sad['nt'], - n=sad['n'], - bt=sad['bt'], - b=sad['b'], - c=sad['c'], - ee=StateEERecord._fromdict(sad['ee']), # latest est event dict - di=sad['di'] if sad['di'] else None - ) - - nstates.pin(keys=keys, val=ksr) - - with existing.existingHby(name=name, base=base, bran=bran) as hby: - rgy = viring.Reger(name=name, base=base, db=hby.db, temp=False, - reopen=True) - - rstates = koming.Komer(db=rgy, - schema=dict, - subkey='stts.') - - for _, sad in rstates.getItemIter(): - rsr = viring.RegStateRecord( - vn=list(Version), # version number as list [major, minor] - i=sad['i'], # qb64 registry SAID - s=sad['s'], # lowercase hex string no leading zeros - d=sad['d'], - ii=sad['ii'], - dt=sad['dt'], - et=sad['et'], - bt=sad['bt'], # hex string no leading zeros lowercase - b=sad['b'], # list of qb64 may be empty - c=sad['c'], - ) - # ksr = stateFromKever(kever) - rgy.states.pin(sad['i'], val=rsr) - - for (said,), _ in rgy.creds.getItemIter(): - snkey = dbing.snKey(said, 0) - dig = rgy.getTel(key=snkey) - - prefixer = coring.Prefixer(qb64=said) - seqner = coring.Seqner(sn=0) - saider = coring.Saider(qb64b=bytes(dig)) - rgy.cancs.pin(keys=said, val=[prefixer, seqner, saider]) - - migrateKeys(hby.db) - - except ConfigurationError: - print(f"identifier prefix for {name} does not exist, incept must be run first", ) - return -1 - - -def migrateKeys(db): - # public keys mapped to the AID and event seq no they appeared in - pubs = subing.CatCesrIoSetSuber(db=db, subkey="pubs.", - klas=(coring.Prefixer, coring.Seqner)) - - # next key digests mapped to the AID and event seq no they appeared in - digs = subing.CatCesrIoSetSuber(db=db, subkey="digs.", - klas=(coring.Prefixer, coring.Seqner)) - - for pre, fn, dig in db.getFelItemAllPreIter(key=b''): - dgkey = dbing.dgKey(pre, dig) # get message - if not (raw := db.getEvt(key=dgkey)): - raise kering.MissingEntryError("Missing event for dig={}.".format(dig)) - serder = serdering.SerderKERI(raw=bytes(raw)) - val = (coring.Prefixer(qb64b=serder.preb), coring.Seqner(sn=serder.sn)) - verfers = serder.verfers or [] - for verfer in verfers: - pubs.add(keys=(verfer.qb64,), val=val) - ndigers = serder.ndigers or [] - for diger in ndigers: - digs.add(keys=(diger.qb64,), val=val) diff --git a/src/keri/app/cli/commands/migrate/__init__.py b/src/keri/app/cli/commands/migrate/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/keri/app/cli/commands/migrate/list.py b/src/keri/app/cli/commands/migrate/list.py new file mode 100644 index 000000000..a88360e42 --- /dev/null +++ b/src/keri/app/cli/commands/migrate/list.py @@ -0,0 +1,64 @@ +# -*- encoding: utf-8 -*- +""" +keri.kli.commands.migrate.list module + +""" +import argparse +import logging + +from keri import help +from hio.base import doing +from prettytable import PrettyTable + +from keri.app.cli.common import existing + +logger = help.ogler.getLogger() + +def handler(args): + """ + List local LMDB database migrations and their completion status + + Args: + args(Namespace): arguments object from command line + """ + lister = ListDoer(args) + return [lister] + + +parser = argparse.ArgumentParser(description='Lists the local LMDB migrations and their completion status') +parser.set_defaults(handler=handler, + transferable=True) + +# Parameters for basic structure of database +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--temp', '-t', help='create a temporary keystore, used for testing', default=False) + +# Parameters for Manager creation +# passcode => bran +parser.add_argument('--passcode', '-p', help='21 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) + + +class ListDoer(doing.Doer): + + def __init__(self, args): + self.args = args + super(ListDoer, self).__init__() + + def recur(self, tyme): + tab = PrettyTable() + tab.field_names = ["Num", "Name", "Date Completed"] + tab.align["Name"] = "l" + + hby = existing.setupHby(name=self.args.name, base=self.args.base, + bran=self.args.bran if self.args.bran else None, temp=self.args.temp) + + for idx, (name, dater) in enumerate(hby.db.complete()): + print(name, dater) + date = dater.datetime.strftime("%Y-%m-%d %H:%M") if dater is not None else "Not Run" + tab.add_row((f"{idx + 1}", f"{name}", date)) + + print(tab) + return True diff --git a/src/keri/app/cli/commands/migrate/run.py b/src/keri/app/cli/commands/migrate/run.py new file mode 100644 index 000000000..de2a62eb4 --- /dev/null +++ b/src/keri/app/cli/commands/migrate/run.py @@ -0,0 +1,67 @@ +# -*- encoding: utf-8 -*- +""" +keri.kli.commands.migrate.run module + +""" +import argparse + +from hio.base import doing + +from keri import help +from keri import kering +from keri.db import basing + +logger = help.ogler.getLogger() + +def handler(args): + """ + Launch KERI database migrator + + Args: + args(Namespace): arguments object from command line + """ + migrator = MigrateDoer(args) + return [migrator] + + +parser = argparse.ArgumentParser(description='Migrates a database and keystore') +parser.set_defaults(handler=handler, + transferable=True) + +# Parameters for basic structure of database +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--temp', '-t', help='create a temporary keystore, used for testing', default=False) + +# Parameters for Manager creation +# passcode => bran +parser.add_argument('--passcode', '-p', help='21 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) + + +class MigrateDoer(doing.Doer): + + def __init__(self, args): + self.args = args + super(MigrateDoer, self).__init__() + + def recur(self, tyme): + name=self.args.name + base=self.args.base + temp=self.args.temp + hab_db = basing.Baser(name=name, + base=base, + temp=temp, + reopen=False) + + try: + hab_db.reopen() + except kering.DatabaseError as ex: + pass + + print(f"Migrating {name}...") + hab_db.migrate() + print(f"Finished migrating {name}") + + return True diff --git a/src/keri/app/cli/commands/migrate/show.py b/src/keri/app/cli/commands/migrate/show.py new file mode 100644 index 000000000..6bf3392c5 --- /dev/null +++ b/src/keri/app/cli/commands/migrate/show.py @@ -0,0 +1,59 @@ +# -*- encoding: utf-8 -*- +""" +keri.kli.commands module + +""" +import argparse + +from keri import help +from hio.base import doing + +from keri.app.cli.common import existing + +logger = help.ogler.getLogger() + +def handler(args): + """ + Launch KERI database initialization + + Args: + args(Namespace): arguments object from command line + """ + clean = CleanDoer(args) + return [clean] + + +parser = argparse.ArgumentParser(description='Cleans and migrates a database and keystore') +parser.set_defaults(handler=handler, + transferable=True) + +# Parameters for basic structure of database +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--temp', '-t', help='create a temporary keystore, used for testing', default=False) +parser.add_argument('--migration', '-m', help='migration name', required=True) + + +# Parameters for Manager creation +# passcode => bran +parser.add_argument('--passcode', '-p', help='21 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) + + +class CleanDoer(doing.Doer): + + def __init__(self, args): + self.args = args + super(CleanDoer, self).__init__() + + def recur(self, tyme): + hby = existing.setupHby(name=self.args.name, base=self.args.base, + bran=self.args.bran, temp=self.args.temp) + + [(name, dater)] = hby.db.complete(name=self.args.migration) + date = dater.datetime.strftime("%Y-%m-%d %H:%M") if dater is not None else "Not Run" + + print(f"{self.args.migration} -> {date}") + + return True diff --git a/src/keri/app/cli/commands/multisig/continue.py b/src/keri/app/cli/commands/multisig/continue.py index 2a2508f97..f419a7418 100644 --- a/src/keri/app/cli/commands/multisig/continue.py +++ b/src/keri/app/cli/commands/multisig/continue.py @@ -6,7 +6,7 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app import indirecting, grouping, agenting diff --git a/src/keri/app/cli/commands/multisig/incept.py b/src/keri/app/cli/commands/multisig/incept.py index 1c589ab53..605a91040 100644 --- a/src/keri/app/cli/commands/multisig/incept.py +++ b/src/keri/app/cli/commands/multisig/incept.py @@ -156,7 +156,7 @@ def inceptDo(self, tymth, tock=0.0): serder=exn, attachment=ims) - print(f"Group identifier inception initialized for {ghab.pre}") + logger.info(f"Group identifier inception initialized for {ghab.pre}") prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=0) saider = coring.Saider(qb64=prefixer.qb64) diff --git a/src/keri/app/cli/commands/multisig/interact.py b/src/keri/app/cli/commands/multisig/interact.py index 77b98ff73..b5abef1e8 100644 --- a/src/keri/app/cli/commands/multisig/interact.py +++ b/src/keri/app/cli/commands/multisig/interact.py @@ -7,7 +7,7 @@ import argparse from ordered_set import OrderedSet as oset -from hio import help +from keri import help from hio.base import doing from keri import kering diff --git a/src/keri/app/cli/commands/multisig/join.py b/src/keri/app/cli/commands/multisig/join.py index e52dae7a1..b85db6b75 100644 --- a/src/keri/app/cli/commands/multisig/join.py +++ b/src/keri/app/cli/commands/multisig/join.py @@ -1,11 +1,12 @@ # -*- encoding: utf-8 -*- """ KERI -keri.kli.commands.delegate module +keri.kli.commands.multisig module """ import argparse import json +from ordered_set import OrderedSet as oset from hio.base import doing from prettytable import PrettyTable @@ -15,51 +16,57 @@ from keri.app.cli.common import existing, displaying from keri.core import coring, eventing, scheming, parsing, routing, serdering from keri.peer import exchanging -from keri.vc import proving from keri.vdr import verifying, credentialing logger = help.ogler.getLogger() parser = argparse.ArgumentParser(description='Join group multisig inception, rotation or interaction event.') -parser.set_defaults(handler=lambda args: confirm(args)) +parser.set_defaults(handler=lambda args: join(args)) parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', required=False, default="") +parser.add_argument('--group', '-g', help='human-readable name for the multisig group identifier prefix', required=False, default=None) parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', dest="bran", default=None) # passcode => bran +parser.add_argument("--auto", "-Y", help="auto approve any delegation request non-interactively", action="store_true") -def confirm(args): - """ Wait for and provide interactive confirmation of group multisig inception, rotation or interaction events +def join(args): + """ Wait for and provide interactive confirmation of group multisig inception, rotation or interaction events Parameters: - args(Namespace): parsed arguements namespace object + args(Namespace): parsed arguments namespace object """ name = args.name base = args.base bran = args.bran + auto = args.auto + group = args.group - confirmDoer = ConfirmDoer(name=name, base=base, bran=bran) + joinDoer = JoinDoer(name=name, base=base, bran=bran, group=group, auto=auto) - doers = [confirmDoer] + doers = [joinDoer] return doers -class ConfirmDoer(doing.DoDoer): - """ Doist doer capable of polling for group multisig events and prompting user for action +class JoinDoer(doing.DoDoer): + """ Doist doer capable of polling for group multisig events and prompting user for action """ - def __init__(self, name, base, bran): + def __init__(self, name, base, bran, group, auto=False): """ Create doer for polling for group multisig events and either approve automatically or prompt user Parameters: name (str): database environment name base (str): database directory prefix bran (str): passcode to unlock keystore - + group (str): human-readable name for the multisig identifier prefix + auto (bool): non-interactively auto approve any inception, rotation, interaction, or other event + while using the default group of "default-group" """ + self.group = group self.hby = existing.setupHby(name=name, base=base, bran=bran) self.rgy = credentialing.Regery(hby=self.hby, name=name, base=base) self.hbyDoer = habbing.HaberyDoer(habery=self.hby) # setup doer @@ -86,11 +93,11 @@ def __init__(self, name, base, bran): doers = [self.hbyDoer, self.witq, self.mbx, self.counselor, self.registrar, self.credentialer, self.postman] self.toRemove = list(doers) - doers.extend([doing.doify(self.confirmDo)]) - - super(ConfirmDoer, self).__init__(doers=doers) + doers.extend([doing.doify(self.joinDo)]) + self.auto = auto + super(JoinDoer, self).__init__(doers=doers) - def confirmDo(self, tymth, tock=0.0): + def joinDo(self, tymth, tock=0.0): """ Parameters: tymth (function): injected function wrapper closure returned by .tymen() of @@ -106,50 +113,54 @@ def confirmDo(self, tymth, tock=0.0): print("Waiting for group multisig events...") - while True: - for keys, notice in self.notifier.noter.notes.getItemIter(): - attrs = notice.attrs - route = attrs['r'] - - match route: - case '/multisig/icp': - done = yield from self.incept(attrs) - case '/multisig/ixn': - done = yield from self.interact(attrs) - case '/multisig/rot': - done = yield from self.rotate(attrs) - case '/multisig/rpy': - done = yield from self.rpy(attrs) - case '/multisig/vcp': - done = yield from self.vcp(attrs) - case '/multisig/iss': - done = yield from self.iss(attrs) - case '/multisig/rev': - done = yield from self.rev(attrs) - case '/multisig/exn': - done = yield from self.exn(attrs) - case _: - continue - - if done: - self.notifier.noter.notes.rem(keys=keys) + while self.notifier.noter.notes.cntAll() == 0: + yield self.tock - else: - delete = input(f"\nDelete event [Y|n]? ") - if delete in ("Y", "y"): - self.notifier.noter.notes.rem(keys=keys) + for keys, notice in self.notifier.noter.notes.getItemIter(): + attrs = notice.attrs + route = attrs['r'] + + match route: + case '/multisig/icp': + done = yield from self.incept(attrs) + case '/multisig/ixn': + done = yield from self.interact(attrs) + case '/multisig/rot': + done = yield from self.rotate(attrs) + case '/multisig/rpy': + done = yield from self.rpy(attrs) + case '/multisig/vcp': + done = yield from self.vcp(attrs) + case '/multisig/iss': + done = yield from self.iss(attrs) + case '/multisig/rev': + done = yield from self.rev(attrs) + case _: + continue + + if done: + self.notifier.noter.notes.rem(keys=keys) + + else: + delete = input(f"\nDelete event [Y|n]? ") + if delete in ("Y", "y"): + self.notifier.noter.notes.rem(keys=keys) - yield self.tock yield self.tock + self.remove(self.toRemove) + def incept(self, attrs): - """ Incept group multisig + """ Join a group multisig inception event """ - smids = attrs["smids"] # change body mids for group member ids - rmids = attrs["rmids"] if "rmids" in attrs else None - ked = attrs["ked"] + said = attrs["d"] + exn, pathed = exchanging.cloneMessage(self.hby, said=said) + payload = exn.ked['a'] + smids = payload["smids"] + rmids = payload["rmids"] if "rmids" in payload else None + ked = exn.ked both = list(set(smids + (rmids or []))) mhab = None @@ -164,36 +175,72 @@ def incept(self, attrs): inits = dict() - inits["isith"] = ked["kt"] - inits["nsith"] = ked["nt"] + #original icp + embeds = exn.ked['e'] + oicp = serdering.SerderKERI(sad=embeds["icp"]) + + inits["isith"] = oicp.ked["kt"] + inits["nsith"] = oicp.ked["nt"] - inits["estOnly"] = eventing.TraitCodex.EstOnly in ked["c"] - inits["DnD"] = eventing.TraitCodex.DoNotDelegate in ked["c"] + inits["estOnly"] = eventing.TraitCodex.EstOnly in oicp.ked["c"] + inits["DnD"] = eventing.TraitCodex.DoNotDelegate in oicp.ked["c"] - inits["toad"] = ked["bt"] - inits["wits"] = ked["b"] - inits["delpre"] = ked["di"] if "di" in ked else None + inits["toad"] = oicp.ked["bt"] + inits["wits"] = oicp.ked["b"] + inits["delpre"] = oicp.ked["di"] if "di" in ked else None print() print("Group Multisig Inception proposed:") - self.showEvent(mhab, both, ked) - yn = input(f"\nJoin [Y|n]? ") - approve = yn in ('', 'y', 'Y') + self.showEvent(mhab, both, oicp.ked) + + if self.auto: + approve = True + else: + yn = input(f"\nJoin [Y|n]? ") + approve = yn in ('', 'y', 'Y') if approve: - while True: - alias = input(f"\nEnter alias for new AID: ") - if self.hby.habByName(alias) is not None: - print(f"AID alias {alias} is already in use, please try again") + if self.auto: + if self.group is None: + group = "default-group" else: - break + group = self.group + else: + while True: + group = input(f"\nEnter group name for new AID: ") + if self.hby.habByName(group) is not None: + print(f"AID group name {group} is already in use, please try again") + else: + break try: - ghab = self.hby.makeGroupHab(group=alias, mhab=mhab, + ghab = self.hby.makeGroupHab(group=group, mhab=mhab, smids=smids, rmids=rmids, **inits) except ValueError as e: return False + icp = ghab.makeOwnInception(allowPartiallySigned=True) + + exn, ims = grouping.multisigInceptExn(ghab.mhab, + smids=ghab.smids, + rmids=ghab.rmids, + icp=icp) + others = list(oset(smids + (rmids or []))) + + others.remove(ghab.mhab.pre) + + for recpt in others: # this goes to other participants only as a signaling mechanism + self.postman.send(src=ghab.mhab.pre, + dest=recpt, + topic="multisig", + serder=exn, + attachment=ims) + + while not self.postman.sent(said=exn.said): + yield self.tock + + self.postman.cues.clear() + prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=0) saider = coring.Saider(qb64=prefixer.qb64) @@ -205,10 +252,18 @@ def incept(self, attrs): return True def interact(self, attrs): - pre = attrs["gid"] - smids = attrs["aids"] # change attrs["aids"]" to "smids" - rmids = attrs["rmids"] if "rmids" in attrs else None - data = attrs["data"] + said = attrs["d"] + exn, pathed = exchanging.cloneMessage(self.hby, said=said) + payload = exn.ked['a'] + + pre = payload["gid"] + smids = payload["smids"] + rmids = payload["rmids"] if "rmids" in payload else None + + embeds = exn.ked['e'] + # original ixn + oixn = serdering.SerderKERI(sad=embeds["ixn"]) + data = oixn.ked['a'] if pre not in self.hby.habs: print(f"Invalid multisig group interaction request {pre} not in Habs") @@ -225,12 +280,36 @@ def interact(self, attrs): print(f"Group Multisig Interaction for {ghab.name} ({ghab.pre}) proposed:") print(f"Data:") print(json.dumps(data, indent=2)) - yn = input(f"\nJoin [Y|n]? ") - approve = yn in ('', 'y', 'Y') + + if self.auto: + approve = True + else: + yn = input(f"\nJoin [Y|n]? ") + approve = yn in ('', 'y', 'Y') if approve: ixn = ghab.interact(data=data) serder = serdering.SerderKERI(raw=ixn) + + ixn = ghab.makeOwnEvent(allowPartiallySigned=True, sn=oixn.sn) + + exn, ims = grouping.multisigInteractExn(ghab, aids=ghab.smids, ixn=ixn) + others = list(oset(smids + (rmids or []))) + + others.remove(ghab.mhab.pre) + + for recpt in others: # this goes to other participants only as a signaling mechanism + self.postman.send(src=ghab.mhab.pre, + dest=recpt, + topic="multisig", + serder=exn, + attachment=ims) + + while not self.postman.sent(said=exn.said): + yield self.tock + + self.postman.cues.clear() + prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=serder.sn) saider = coring.Saider(qb64b=serder.saidb) @@ -252,7 +331,6 @@ def startCounselor(self, hab, prefixer, seqner, saider): yield self.tock def showEvent(self, hab, mids, ked): - print() print("Participants:") thold = coring.Tholder(sith=ked["kt"]) @@ -284,9 +362,16 @@ def rotate(self, attrs): """ Rotate group multisig """ - smids = attrs["smids"] - rmids = attrs["rmids"] - ked = attrs["ked"] + said = attrs["d"] + exn, pathed = exchanging.cloneMessage(self.hby, said=said) + + payload = exn.ked['a'] + smids = payload["smids"] + rmids = payload["rmids"] + ked = exn.ked + + embeds = ked['e'] + orot = serdering.SerderKERI(sad=embeds["rot"]) both = list(set(smids + (rmids or []))) @@ -302,30 +387,61 @@ def rotate(self, attrs): print() print("Group Multisig Rotation proposed:") - self.showRotation(mhab, smids, rmids, ked) - yn = input(f"\nJoin [Y|n]? ") - approve = yn in ('', 'y', 'Y') + self.showRotation(mhab, smids, rmids, orot.ked) + + if self.auto: + approve = True + else: + yn = input(f"\nJoin [Y|n]? ") + approve = yn in ('', 'y', 'Y') if approve: - pre = ked['i'] + pre = orot.ked['i'] if pre in self.hby.habs: ghab = self.hby.habs[pre] else: - while True: - alias = input(f"\nEnter alias for new AID: ") - if self.hby.habByName(alias) is not None: - print(f"AID alias {alias} is already in use, please try again") + if self.auto: + if self.group is None: + group = "default-group" else: - break + group = self.group + else: + while True: + group = input(f"\nEnter group name for new AID: ") + if self.hby.habByName(group) is not None: + print(f"AID group name {group} is already in use, please try again") + else: + break - ghab = self.hby.joinGroupHab(pre, group=alias, mhab=mhab, smids=smids, rmids=rmids) + ghab = self.hby.joinGroupHab(pre, group=group, mhab=mhab, smids=smids, rmids=rmids) try: - serder = serdering.SerderKERI(sad=ked) - rot = ghab.rotate(serder=serder) - except ValueError as e: + ghab.rotate(serder=orot, smids=smids, rmids=rmids) + except ValueError: return False + rot = ghab.makeOwnEvent(allowPartiallySigned=True, sn=orot.sn) + + exn, ims = grouping.multisigRotateExn(ghab, + smids=ghab.smids, + rmids=ghab.rmids, + rot=rot) + others = list(oset(smids + (rmids or []))) + + others.remove(ghab.mhab.pre) + + for recpt in others: # this goes to other participants only as a signaling mechanism + self.postman.send(src=ghab.mhab.pre, + dest=recpt, + topic="multisig", + serder=exn, + attachment=ims) + + while not self.postman.sent(said=exn.said): + yield self.tock + + self.postman.cues.clear() + serder = serdering.SerderKERI(raw=rot) prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=serder.sn) @@ -442,8 +558,11 @@ def rpy(self, attrs): print(f" Role: {role.capitalize()}") print(f" Endpoint Provider: {endpointAlias} ({eid})") - yn = input(f"\nApprove [Y|n]? ") - approve = yn in ('', 'y', 'Y') + if self.auto: + approve = True + else: + yn = input(f"\nApprove [Y|n]? ") + approve = yn in ('', 'y', 'Y') if approve: # Create and parse the event with "their" signatures @@ -472,8 +591,9 @@ def rpy(self, attrs): yield self.tock print(f"End role authorization added for role {role}") + return True - yield self.tock + return False def vcp(self, attrs): """ Handle issue messages @@ -502,8 +622,11 @@ def vcp(self, attrs): print(f"\nGroup Credential Regitry Creation (from {senderAlias}):") print(f"Usage: {usage}:\n") - yn = input(f"\nApprove [Y|n]? ") - approve = yn in ('', 'y', 'Y') + if self.auto: + approve = True + else: + yn = input(f"\nApprove [Y|n]? ") + approve = yn in ('', 'y', 'Y') if approve: # Create and parse the event with "their" signatures @@ -545,8 +668,9 @@ def vcp(self, attrs): yield self.tock print(f"Registry {vserder.pre} created.") + return True - yield self.tock + return False def iss(self, attrs): """ Handle issue messages @@ -597,8 +721,11 @@ def iss(self, attrs): if k not in ('d', 'i'): print(f" {k}: {v}") - yn = input(f"\nApprove [Y|n]? ") - approve = yn in ('', 'y', 'Y') + if self.auto: + approve = True + else: + yn = input(f"\nApprove [Y|n]? ") + approve = yn in ('', 'y', 'Y') if approve: # Create and parse the event with "their" signatures @@ -644,8 +771,9 @@ def iss(self, attrs): yield self.tock print(f"Credential {creder.said} complete.") + return True - yield self.tock + return False def rev(self, attrs): """ Handle revocation messages @@ -695,8 +823,11 @@ def rev(self, attrs): else: print(f" Issued To: Unknown AID ({isse})") - yn = input(f"\nApprove Revocation [Y|n]? ") - approve = yn in ('', 'y', 'Y') + if self.auto: + approve = True + else: + yn = input(f"\nApprove Revocation [Y|n]? ") + approve = yn in ('', 'y', 'Y') if approve: # Create and parse the event with "their" signatures @@ -755,82 +886,6 @@ def rev(self, attrs): while not self.postman.sent(said=last.said): yield self.tock - yield self.tock - - def exn(self, attrs): - """ Handle exn messages - - Parameters: - attrs (dict): attributes of the reply message - - Returns: - - """ - said = attrs["d"] - exn, pathed = exchanging.cloneMessage(self.hby, said=said) - embeds = exn.ked['e'] - sender = exn.ked['i'] - - contact = self.org.get(sender) - senderAlias = contact['alias'] - - eexn = embeds['exn'] - - group = eexn["i"] - hab = self.hby.habs[group] if group in self.hby.habs else None - if hab is None: - raise ValueError(f"message sender not a valid AID={group}") - - print(f"Group Peer-2-Peer Message proposal (from {senderAlias}):") - print(f" Message Type: {eexn['r']}") - print(f" Sending From: {hab.name} ({hab.pre})") - recp = eexn['a']['i'] - contact = self.org.get(recp) - if contact is not None and "alias" in contact: - print(f" Sending To: {contact['alias']} ({recp})") - else: - print(f" Sending To: Unknown AID ({recp})") - - yn = input(f"\nApprove [Y|n]? ") - approve = yn in ('', 'y', 'Y') - - if approve: - eserder = serdering.SerderKERI(sad=eexn) - anc = bytearray(eserder.raw) + pathed["exn"] - self.psr.parseOne(ims=bytes(anc)) - - msg = hab.endorse(serder=eserder, last=False, pipelined=False) - msg = msg + pathed["exn"] - self.psr.parseOne(ims=bytes(msg)) - - smids = hab.db.signingMembers(pre=hab.pre) - smids.remove(hab.mhab.pre) - - for smid in smids: # this goes to other participants only as a signaling mechanism - rexn, atc = grouping.multisigExn(ghab=hab, exn=msg) - self.postman.send(src=hab.mhab.pre, - dest=smid, - topic="multisig", - serder=rexn, - attachment=atc) - - while not self.exc.complete(said=eserder.said): - self.exc.processEscrow() - yield self.tock - - if self.exc.lead(hab.mhab, said=exn.said): - print(f"Sending message {eserder.said} to {recp}") - atc = exchanging.serializeMessage(self.hby, eserder.said) - del atc[:eserder.size] - self.postman.send(src=hab.mhab.pre, - dest=recp, - topic="credential", - serder=eserder, - attachment=atc) - - while not self.postman.sent(said=eserder.said): - yield self.tock - - print("... grant message sent") + return True - yield self.tock + return False diff --git a/src/keri/app/cli/commands/multisig/notice.py b/src/keri/app/cli/commands/multisig/notice.py index a442eab0c..fe4ca2769 100644 --- a/src/keri/app/cli/commands/multisig/notice.py +++ b/src/keri/app/cli/commands/multisig/notice.py @@ -6,13 +6,13 @@ import argparse from ordered_set import OrderedSet as oset -from hio import help +from keri import help from hio.base import doing from keri.app import habbing, forwarding, grouping from keri.app.cli.common import existing -from keri.core import coring from keri.core.coring import Ilks +from keri.core import serdering logger = help.ogler.getLogger() @@ -86,7 +86,7 @@ def noticeDo(self, tymth, tock=0.0): (smids, rmids) = hab.members() serder = hab.kever.serder rot = hab.makeOwnEvent(sn=hab.kever.sn) - eserder = coring.Serder(raw=rot) + eserder = serdering.SerderKERI(raw=rot) del rot[:eserder.size] ilk = serder.ked['t'] diff --git a/src/keri/app/cli/commands/multisig/rotate.py b/src/keri/app/cli/commands/multisig/rotate.py index fba05fd64..a1727ff84 100644 --- a/src/keri/app/cli/commands/multisig/rotate.py +++ b/src/keri/app/cli/commands/multisig/rotate.py @@ -7,7 +7,7 @@ import argparse from ordered_set import OrderedSet as oset -from hio import help +from keri import help from hio.base import doing from keri import kering @@ -53,9 +53,9 @@ def rotateGroupIdentifier(args): """ data = config.parseData(args.data) if args.data is not None else None - - rotDoer = GroupMultisigRotate(name=args.name, base=args.base, alias=args.alias, smids=args.smids, rmids=args.rmids, - bran=args.bran, wits=args.witnesses, cuts=args.cuts, adds=args.witness_add, + hby = existing.setupHby(name=args.name, base=args.base, bran=args.bran) + rotDoer = GroupMultisigRotate(hby=hby, alias=args.alias, smids=args.smids, rmids=args.rmids, + wits=args.witnesses, cuts=args.cuts, adds=args.witness_add, isith=args.isith, nsith=args.nsith, toad=args.toad, data=data) doers = [rotDoer] @@ -70,7 +70,7 @@ class GroupMultisigRotate(doing.DoDoer): """ - def __init__(self, name, base, bran, alias, smids=None, rmids=None, isith=None, nsith=None, + def __init__(self, hby, alias, smids=None, rmids=None, isith=None, nsith=None, toad=None, wits=None, cuts=None, adds=None, data: list = None): self.alias = alias @@ -80,12 +80,12 @@ def __init__(self, name, base, bran, alias, smids=None, rmids=None, isith=None, self.smids = smids self.rmids = rmids self.data = data + self.hby = hby self.wits = wits if wits is not None else [] self.cuts = cuts if cuts is not None else [] self.adds = adds if adds is not None else [] - self.hby = existing.setupHby(name=name, base=base, bran=bran) self.hbyDoer = habbing.HaberyDoer(habery=self.hby) # setup doer notifier = Notifier(self.hby) mux = grouping.Multiplexor(self.hby, notifier=notifier) @@ -203,7 +203,7 @@ def rotateDo(self, tymth, tock=0.0, **opts): prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn+1) - rot = ghab.rotate(isith=self.isith, nsith=self.nsith, + rot = ghab.rotate(smids=smids, rmids=rmids, isith=self.isith, nsith=self.nsith, toad=self.toad, cuts=list(self.cuts), adds=list(self.adds), data=self.data, verfers=merfers, digers=migers) diff --git a/src/keri/app/cli/commands/multisig/update.py b/src/keri/app/cli/commands/multisig/update.py index 5d7284ea8..f528bcf18 100644 --- a/src/keri/app/cli/commands/multisig/update.py +++ b/src/keri/app/cli/commands/multisig/update.py @@ -6,7 +6,7 @@ import argparse import time -from hio import help +from keri import help from hio.base import doing from keri import kering @@ -119,8 +119,8 @@ def updateDo(self, tymth, tock=0.0, **opts): print("") witstate = self.hab.db.ksns.get((saider.qb64,)) - if witstate.sn != self.sn and witstate.ked['d'] != self.said: - print(f"Witness state ({witstate.sn}, {witstate.ked['d']}) does not match requested state.") + if int(witstate.s, 16) != self.sn and witstate.d != self.said: + print(f"Witness state ({witstate.s}, {witstate.d}) does not match requested state.") self.remove(self.toRemove) return diff --git a/src/keri/app/cli/commands/notifications/__init__.py b/src/keri/app/cli/commands/notifications/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/keri/app/cli/commands/notifications/list.py b/src/keri/app/cli/commands/notifications/list.py new file mode 100644 index 000000000..0788ea7c4 --- /dev/null +++ b/src/keri/app/cli/commands/notifications/list.py @@ -0,0 +1,93 @@ +# -*- encoding: utf-8 -*- +""" +KERI +keri.kli.commands module + +""" +import argparse +import json + +from keri import help +from hio.base import doing + +from keri import kering +from keri.app import agenting, habbing, httping, notifying +from keri.app.cli.common import existing +from keri.app.habbing import GroupHab + +logger = help.ogler.getLogger() + +parser = argparse.ArgumentParser(description='Display notifications for an identifier') +parser.set_defaults(handler=lambda args: handler(args), + transferable=True) +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--alias', '-a', help='human readable alias for the new identifier prefix', default=None) +parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) # passcode => bran +parser.add_argument("--verbose", "-V", help="print JSON of all current events", action="store_true") + + +def handler(args): + """ + List notifications for an identifier. + + Args: + args(Namespace): arguments object from command line + """ + + name = args.name + base = args.base + bran = args.bran + alias = args.alias + verbose = args.verbose + + notesDoer = NotesDoer(name=name, base=base, alias=alias, bran=bran, verbose=verbose) + + doers = [notesDoer] + return doers + + +class NotesDoer(doing.DoDoer): + def __init__(self, name, base, alias, bran, verbose): + + hby = existing.setupHby(name=name, base=base, bran=bran) + self.hbyDoer = habbing.HaberyDoer(habery=hby) # setup doer + self.alias = alias + self.hby = hby + self.verbose = verbose + self.notifier = notifying.Notifier(hby=self.hby) + + doers = [self.hbyDoer, doing.doify(self.readDo)] + + super(NotesDoer, self).__init__(doers=doers) + + def readDo(self, tymth, tock=0.0): + """ + Parameters: + tymth (function): injected function wrapper closure returned by .tymen() of + Tymist instance. Calling tymth() returns associated Tymist .tyme. + tock (float): injected initial tock value + + Returns: doifiable Doist compatible generator method + """ + # enter context + self.wind(tymth) + self.tock = tock + _ = (yield self.tock) + + print("Waiting for notifications...") + + while self.notifier.noter.notes.cntAll() == 0: + yield self.tock + + for keys, notice in self.notifier.noter.notes.getItemIter(): + if self.verbose: + print(keys) + print(json.dumps(notice.pad, indent=4)) + else: + print(keys, notice.attrs.get('r', 'no route')) + + self.remove([self.hbyDoer,]) + return diff --git a/src/keri/app/cli/commands/notifications/mark.py b/src/keri/app/cli/commands/notifications/mark.py new file mode 100644 index 000000000..0c70d9d22 --- /dev/null +++ b/src/keri/app/cli/commands/notifications/mark.py @@ -0,0 +1,101 @@ +# -*- encoding: utf-8 -*- +""" +KERI +keri.kli.commands module + +""" +import argparse + +from keri import help +from hio.base import doing + +from keri import kering +from keri.app import agenting, habbing, httping, notifying +from keri.app.cli.common import existing +from keri.app.habbing import GroupHab + +logger = help.ogler.getLogger() + +parser = argparse.ArgumentParser(description='Display notifications for an identifier') +parser.set_defaults(handler=lambda args: handler(args), + transferable=True) +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--alias', '-a', help='human readable alias for the new identifier prefix', default=None) +parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) # passcode => bran +parser.add_argument("--rid", '-r', help='notification SAID to mark as read', default=None) +parser.add_argument("--all", help="mark all notifications as read", action="store_true") + + +def handler(args): + """ + List notifications for an identifier. + + Args: + args(Namespace): arguments object from command line + """ + + name = args.name + base = args.base + bran = args.bran + alias = args.alias + rid = args.rid + all = args.all + + markDoer = MarkDoer(name=name, base=base, alias=alias, bran=bran, rid=rid, all=all) + + doers = [markDoer] + return doers + + +class MarkDoer(doing.DoDoer): + def __init__(self, name, base, alias, bran, rid, all): + + hby = existing.setupHby(name=name, base=base, bran=bran) + self.hbyDoer = habbing.HaberyDoer(habery=hby) # setup doer + self.alias = alias + self.hby = hby + self.notifier = notifying.Notifier(hby=self.hby) + self.rid = rid + self.all = all + + doers = [self.hbyDoer, doing.doify(self.markDo)] + + super(MarkDoer, self).__init__(doers=doers) + + def markDo(self, tymth, tock=0.0): + """ + Parameters: + tymth (function): injected function wrapper closure returned by .tymen() of + Tymist instance. Calling tymth() returns associated Tymist .tyme. + tock (float): injected initial tock value + + Returns: doifiable Doist compatible generator method + """ + # enter context + self.wind(tymth) + self.tock = tock + _ = (yield self.tock) + + if self.all: + print() + print("This command will mark all notifications as read") + print() + yn = input("Are you sure you want to continue? [y|N]: ") + + if yn not in ("y", "Y"): + print("...exiting") + else: + for n in self.notifier.getNotes(): + print(f"marking {n.rid} as read") + self.notifier.mar(rid=n.rid) + elif self.rid is not None: + print(f"marking {self.rid} as read") + self.notifier.mar(rid=self.rid) + else: + print("Must specify one of --rid or --all") + + self.remove([self.hbyDoer,]) + return diff --git a/src/keri/app/cli/commands/notifications/rem.py b/src/keri/app/cli/commands/notifications/rem.py new file mode 100644 index 000000000..b5f207e99 --- /dev/null +++ b/src/keri/app/cli/commands/notifications/rem.py @@ -0,0 +1,101 @@ +# -*- encoding: utf-8 -*- +""" +KERI +keri.kli.commands module + +""" +import argparse + +from keri import help +from hio.base import doing + +from keri import kering +from keri.app import agenting, habbing, httping, notifying +from keri.app.cli.common import existing +from keri.app.habbing import GroupHab + +logger = help.ogler.getLogger() + +parser = argparse.ArgumentParser(description='Display notifications for an identifier') +parser.set_defaults(handler=lambda args: handler(args), + transferable=True) +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--alias', '-a', help='human readable alias for the new identifier prefix', default=None) +parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) # passcode => bran +parser.add_argument("--rid", '-r', help='notification SAID to mark as read', default=None) +parser.add_argument("--all", help="mark all notifications as read", action="store_true") + + +def handler(args): + """ + List notifications for an identifier. + + Args: + args(Namespace): arguments object from command line + """ + + name = args.name + base = args.base + bran = args.bran + alias = args.alias + rid = args.rid + all = args.all + + removeDoer = RemoveDoer(name=name, base=base, alias=alias, bran=bran, rid=rid, all=all) + + doers = [removeDoer] + return doers + + +class RemoveDoer(doing.DoDoer): + def __init__(self, name, base, alias, bran, rid, all): + + hby = existing.setupHby(name=name, base=base, bran=bran) + self.hbyDoer = habbing.HaberyDoer(habery=hby) # setup doer + self.alias = alias + self.hby = hby + self.notifier = notifying.Notifier(hby=self.hby) + self.rid = rid + self.all = all + + doers = [self.hbyDoer, doing.doify(self.remDoer)] + + super(RemoveDoer, self).__init__(doers=doers) + + def remDoer(self, tymth, tock=0.0): + """ + Parameters: + tymth (function): injected function wrapper closure returned by .tymen() of + Tymist instance. Calling tymth() returns associated Tymist .tyme. + tock (float): injected initial tock value + + Returns: doifiable Doist compatible generator method + """ + # enter context + self.wind(tymth) + self.tock = tock + _ = (yield self.tock) + + if self.all: + print() + print("This command will remove all notifications") + print() + yn = input("Are you sure you want to continue? [y|N]: ") + + if yn not in ("y", "Y"): + print("...exiting") + else: + for n in self.notifier.getNotes(): + print(f"removing {n.rid}") + self.notifier.rem(rid=n.rid) + elif self.rid is not None: + print(f"removing {self.rid}") + self.notifier.rem(rid=self.rid) + else: + print("Must specify one of --rid or --all") + + self.remove([self.hbyDoer,]) + return diff --git a/src/keri/app/cli/commands/oobi/clean.py b/src/keri/app/cli/commands/oobi/clean.py index cd2dc1d46..2dead78d1 100644 --- a/src/keri/app/cli/commands/oobi/clean.py +++ b/src/keri/app/cli/commands/oobi/clean.py @@ -6,7 +6,7 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app.cli.common import existing diff --git a/src/keri/app/cli/commands/oobi/generate.py b/src/keri/app/cli/commands/oobi/generate.py index f2ac4ba68..2cb0082ee 100644 --- a/src/keri/app/cli/commands/oobi/generate.py +++ b/src/keri/app/cli/commands/oobi/generate.py @@ -7,7 +7,7 @@ from urllib.parse import urlparse import sys -from hio import help +from keri import help from hio.base import doing from keri import kering diff --git a/src/keri/app/cli/commands/oobi/resolve.py b/src/keri/app/cli/commands/oobi/resolve.py index 9b835b30f..b31640e19 100644 --- a/src/keri/app/cli/commands/oobi/resolve.py +++ b/src/keri/app/cli/commands/oobi/resolve.py @@ -5,14 +5,13 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing import keri.app.oobiing from keri.app import habbing, oobiing from keri.app.cli.common import existing from keri.db import basing -from keri.end import ending from keri.help import helping logger = help.ogler.getLogger() diff --git a/src/keri/app/cli/commands/passcode/remove.py b/src/keri/app/cli/commands/passcode/remove.py index 82b6b04f7..68c0aabe2 100644 --- a/src/keri/app/cli/commands/passcode/remove.py +++ b/src/keri/app/cli/commands/passcode/remove.py @@ -5,7 +5,7 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app.cli.common import existing diff --git a/src/keri/app/cli/commands/passcode/set.py b/src/keri/app/cli/commands/passcode/set.py index a31f978bb..3081a780b 100644 --- a/src/keri/app/cli/commands/passcode/set.py +++ b/src/keri/app/cli/commands/passcode/set.py @@ -6,7 +6,7 @@ import argparse import getpass -from hio import help +from keri import help from hio.base import doing from keri.app.cli.common import existing diff --git a/src/keri/app/cli/commands/query.py b/src/keri/app/cli/commands/query.py index dc9a1b220..53e6db690 100644 --- a/src/keri/app/cli/commands/query.py +++ b/src/keri/app/cli/commands/query.py @@ -7,7 +7,7 @@ import datetime import json -from hio import help +from keri import help from hio.base import doing from hio.help import decking diff --git a/src/keri/app/cli/commands/rename.py b/src/keri/app/cli/commands/rename.py index 60fff68ef..7d975d05c 100644 --- a/src/keri/app/cli/commands/rename.py +++ b/src/keri/app/cli/commands/rename.py @@ -6,7 +6,7 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app.cli.common import existing diff --git a/src/keri/app/cli/commands/rollback.py b/src/keri/app/cli/commands/rollback.py index f7d573f45..1cfce3b7c 100644 --- a/src/keri/app/cli/commands/rollback.py +++ b/src/keri/app/cli/commands/rollback.py @@ -6,7 +6,7 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri import kering diff --git a/src/keri/app/cli/commands/saidify.py b/src/keri/app/cli/commands/saidify.py index 280035fb8..c89fb664b 100644 --- a/src/keri/app/cli/commands/saidify.py +++ b/src/keri/app/cli/commands/saidify.py @@ -6,7 +6,7 @@ import argparse import json -from hio import help +from keri import help from hio.base import doing from keri.core import coring diff --git a/src/keri/app/cli/commands/ssh/export.py b/src/keri/app/cli/commands/ssh/export.py index 0f0eff8da..0b45ad472 100644 --- a/src/keri/app/cli/commands/ssh/export.py +++ b/src/keri/app/cli/commands/ssh/export.py @@ -11,7 +11,7 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ed25519 -from hio import help +from keri import help from hio.base import doing from keri.app.cli.common import existing diff --git a/src/keri/app/cli/commands/status.py b/src/keri/app/cli/commands/status.py index 787bae9ad..130bd50ee 100644 --- a/src/keri/app/cli/commands/status.py +++ b/src/keri/app/cli/commands/status.py @@ -6,11 +6,11 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app.cli.common import displaying, existing -from keri.core import coring, serdering +from keri.core import serdering from keri.kering import ConfigurationError logger = help.ogler.getLogger() diff --git a/src/keri/app/cli/commands/time.py b/src/keri/app/cli/commands/time.py new file mode 100644 index 000000000..fa48a1638 --- /dev/null +++ b/src/keri/app/cli/commands/time.py @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +""" +keri.app.cli.commands module + +""" +import argparse + +from hio.base import doing + +from keri.help import helping + +parser = argparse.ArgumentParser(description='Print a new time') +parser.set_defaults(handler=lambda args: handler(args)) + + +def handler(_): + return [doing.doify(time)] + + +def time(tymth, tock=0.0): + """ time + """ + _ = (yield tock) + + print(helping.nowIso8601()) diff --git a/src/keri/app/cli/commands/vc/create.py b/src/keri/app/cli/commands/vc/create.py index e30b632dc..ce8c5bf50 100644 --- a/src/keri/app/cli/commands/vc/create.py +++ b/src/keri/app/cli/commands/vc/create.py @@ -1,7 +1,8 @@ import argparse import json +from typing import Optional -from hio import help +from keri import help from hio.base import doing from keri import kering @@ -36,6 +37,8 @@ parser.add_argument('--alias', '-a', help='human readable alias for the new identifier prefix', required=True) parser.add_argument("--private", help="flag to indicate if this credential needs privacy preserving features", action="store_true") +parser.add_argument("--private-credential-nonce", help="(str) nonce for vc", action="store") +parser.add_argument("--private-subject-nonce", help="(str) nonce for subject", action="store") parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', dest="bran", default=None) # passcode => bran parser.add_argument("--time", help="timestamp for the credential creation", required=False, default=None) @@ -99,7 +102,10 @@ def issueCredential(args): rules=rules, credential=credential, timestamp=args.time, - private=args.private) + private=args.private, + private_credential_nonce=args.private_credential_nonce, + private_subject_nonce=args.private_subject_nonce, + ) doers = [issueDoer] return doers @@ -112,7 +118,8 @@ class CredentialIssuer(doing.DoDoer): """ def __init__(self, name, alias, base, bran, registryName=None, schema=None, edges=None, recipient=None, data=None, - rules=None, credential=None, timestamp=None, private=False): + rules=None, credential=None, timestamp=None, private:bool=False, private_credential_nonce:Optional[str]=None, + private_subject_nonce:Optional[str]=None,): """ Create DoDoer for issuing a credential and managing the processes needed to complete issuance Parameters: @@ -124,7 +131,9 @@ def __init__(self, name, alias, base, bran, registryName=None, schema=None, edge data: (dict) credential data dict credential: (dict) full credential to issue when joining a multisig issuance out (str): Filename for credential output - private: (bool) privacy preserving + private (bool): apply nonce used for privacy preserving ACDC + private_credential_nonce (Optional[str]): nonce used for privacy vc + private_subject_nonce (Optional[str]): nonce used for subject """ self.name = name @@ -173,7 +182,9 @@ def __init__(self, name, alias, base, bran, registryName=None, schema=None, edge source=edges, rules=rules, data=data, - private=private) + private=private, + private_credential_nonce=private_credential_nonce, + private_subject_nonce=private_subject_nonce) else: self.creder = serdering.SerderACDC(sad=credential) # proving.Creder(ked=credential) self.credentialer.validate(creder=self.creder) diff --git a/src/keri/app/cli/commands/vc/export.py b/src/keri/app/cli/commands/vc/export.py index f79e2b693..d4821dd7b 100644 --- a/src/keri/app/cli/commands/vc/export.py +++ b/src/keri/app/cli/commands/vc/export.py @@ -7,7 +7,7 @@ import argparse import sys -from hio import help +from keri import help from hio.base import doing from keri.app import signing diff --git a/src/keri/app/cli/commands/vc/list.py b/src/keri/app/cli/commands/vc/list.py index 45717c015..d1528b31f 100644 --- a/src/keri/app/cli/commands/vc/list.py +++ b/src/keri/app/cli/commands/vc/list.py @@ -9,7 +9,7 @@ import json import sys -from hio import help +from keri import help from hio.base import doing from keri import kering diff --git a/src/keri/app/cli/commands/vc/registry/incept.py b/src/keri/app/cli/commands/vc/registry/incept.py index 77c774f9a..6b6b3243d 100644 --- a/src/keri/app/cli/commands/vc/registry/incept.py +++ b/src/keri/app/cli/commands/vc/registry/incept.py @@ -1,13 +1,13 @@ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app import indirecting, habbing, grouping, forwarding from keri.app.cli.common import existing from keri.app.habbing import GroupHab from keri.app.notifying import Notifier -from keri.core import coring, serdering +from keri.core import serdering from keri.core.eventing import SealEvent from keri.peer import exchanging from keri.vdr import credentialing diff --git a/src/keri/app/cli/commands/vc/registry/list.py b/src/keri/app/cli/commands/vc/registry/list.py index 3c299895a..90d4c923e 100644 --- a/src/keri/app/cli/commands/vc/registry/list.py +++ b/src/keri/app/cli/commands/vc/registry/list.py @@ -6,7 +6,7 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app.cli.common import existing diff --git a/src/keri/app/cli/commands/vc/registry/status.py b/src/keri/app/cli/commands/vc/registry/status.py index f24f57a50..55de01ca2 100644 --- a/src/keri/app/cli/commands/vc/registry/status.py +++ b/src/keri/app/cli/commands/vc/registry/status.py @@ -1,11 +1,11 @@ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app import indirecting, habbing, grouping from keri.app.cli.common import existing -from keri.core import coring, serdering +from keri.core import serdering from keri.vdr import credentialing logger = help.ogler.getLogger() diff --git a/src/keri/app/cli/commands/vc/revoke.py b/src/keri/app/cli/commands/vc/revoke.py index ed2c41ec9..a37059255 100644 --- a/src/keri/app/cli/commands/vc/revoke.py +++ b/src/keri/app/cli/commands/vc/revoke.py @@ -122,9 +122,7 @@ def revokeDo(self, tymth, tock=0.0): aserder = serdering.SerderKERI(raw=bytes(anc)) self.registrar.revoke(creder, rserder, aserder) - senderHab = self.hab if isinstance(self.hab, GroupHab): - senderHab = self.hab.mhab smids = self.hab.db.signingMembers(pre=self.hab.pre) smids.remove(self.hab.mhab.pre) @@ -139,8 +137,13 @@ def revokeDo(self, tymth, tock=0.0): while not self.registrar.complete(creder.said, sn=1): yield self.tock - if self.hab.witnesser() and 'i' in creder.attrib: - recp = creder.attrib['i'] + recps = [creder.attrib['i']] if 'i' in creder.attrib else [] + if self.send is not None: + recps.extend(self.send) + + senderHab = self.hab.mhab if isinstance(self.hab, GroupHab) else self.hab + + if len(recps) > 0: msgs = [] for msg in self.hby.db.clonePreIter(pre=creder.issuer): serder = serdering.SerderKERI(raw=msg) @@ -151,12 +154,21 @@ def revokeDo(self, tymth, tock=0.0): atc = msg[serder.size:] msgs.append((serder, atc)) - for (serder, atc) in msgs: - self.postman.send(src=senderHab.pre, dest=recp, topic="credential", serder=serder, - attachment=atc) - - last = msgs[-1][0] - while not self.postman.sent(said=last.said): + sent = 0 + for send in recps: + if send in self.hby.kevers: + recp = send + else: + recp = self.org.find("alias", send) + if len(recp) != 1: + raise ValueError(f"invalid recipient {send}") + recp = recp[0]['id'] + for (serder, atc) in msgs: + self.postman.send(src=senderHab.pre, dest=recp, topic="credential", serder=serder, + attachment=atc) + sent += 1 + + while not len(self.postman.cues) == sent: yield self.tock except kering.ValidationError as ex: diff --git a/src/keri/app/cli/commands/version.py b/src/keri/app/cli/commands/version.py index 5ea6b775a..98cc81c5a 100644 --- a/src/keri/app/cli/commands/version.py +++ b/src/keri/app/cli/commands/version.py @@ -8,18 +8,35 @@ from hio.base import doing import keri +from keri.app.cli.common import existing parser = argparse.ArgumentParser(description='Print version of KLI') parser.set_defaults(handler=lambda args: handler(args)) +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=False, + default=None) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) # passcode => bran def handler(args): - return [doing.doify(version)] + kwa = dict(args=args) + return [doing.doify(version, **kwa)] -def version(tymth, tock=0.0): +def version(tymth, tock=0.0, **opts): """ Command line version handler """ _ = (yield tock) - print(keri.__version__) + args = opts["args"] + name = args.name + base = args.base + bran = args.bran + + print(f"Library version: {keri.__version__}") + + if name is not None: + with existing.existingHby(name=name, base=base, bran=bran) as hby: + print(f"Database version: {hby.db.version}") diff --git a/src/keri/app/cli/commands/witness/demo.py b/src/keri/app/cli/commands/witness/demo.py index dd83dc849..8a9a27a31 100644 --- a/src/keri/app/cli/commands/witness/demo.py +++ b/src/keri/app/cli/commands/witness/demo.py @@ -8,6 +8,7 @@ import argparse import logging +import os from hio.base import doing @@ -16,18 +17,26 @@ from keri import help parser = argparse.ArgumentParser(description="Run a demo collection of witnesses") +parser.add_argument("--loglevel", action="store", required=False, default=os.getenv("KERI_LOG_LEVEL", "CRITICAL"), + help="Set log level to DEBUG | INFO | WARNING | ERROR | CRITICAL. Default is CRITICAL") parser.set_defaults(handler=lambda args: demo(args)) -help.ogler.level = logging.INFO logger = help.ogler.getLogger() -def demo(_): +def demo(args): """ Run set of three witnesses for demo """ + base_formatter = logging.Formatter( + '%(asctime)s [keri] %(module)s.%(funcName)s-%(lineno)s %(levelname)-8s %(message)s') + base_formatter.default_msec_format = None + help.ogler.baseConsoleHandler.setFormatter(base_formatter) + help.ogler.level = logging.getLevelName(args.loglevel.upper()) + logger.setLevel(help.ogler.level) + help.ogler.reopen(name="keri", temp=True, clear=True) wancf = configing.Configer(name="wan", headDirPath="scripts", temp=False, reopen=True, clear=False) wilcf = configing.Configer(name="wil", headDirPath="scripts", temp=False, reopen=True, clear=False) diff --git a/src/keri/app/cli/commands/witness/submit.py b/src/keri/app/cli/commands/witness/submit.py index 1aaae2808..652b0541e 100644 --- a/src/keri/app/cli/commands/witness/submit.py +++ b/src/keri/app/cli/commands/witness/submit.py @@ -5,7 +5,7 @@ """ import argparse -from hio import help +from keri import help from hio.base import doing from keri.app import habbing, agenting, indirecting diff --git a/src/keri/app/cli/common/existing.py b/src/keri/app/cli/common/existing.py index 287d4e033..b2feba5e3 100644 --- a/src/keri/app/cli/common/existing.py +++ b/src/keri/app/cli/common/existing.py @@ -12,7 +12,7 @@ from keri.app import habbing, keeping -def setupHby(name, base="", bran=None, cf=None): +def setupHby(name, base="", bran=None, cf=None, temp=False): """ Create Habery off of existing directory Parameters: @@ -27,7 +27,7 @@ def setupHby(name, base="", bran=None, cf=None): """ ks = keeping.Keeper(name=name, base=base, - temp=False, + temp=temp, cf=cf, reopen=True) aeid = ks.gbls.get('aeid') diff --git a/src/keri/app/delegating.py b/src/keri/app/delegating.py index 107cb2e1b..ff0418ba4 100644 --- a/src/keri/app/delegating.py +++ b/src/keri/app/delegating.py @@ -6,13 +6,13 @@ module for enveloping and forwarding KERI message """ -from hio import help +from keri import help from hio.base import doing from . import agenting, forwarding from .habbing import GroupHab from .. import kering -from ..core import coring, eventing, serdering +from ..core import coring, serdering from ..db import dbing from ..peer import exchanging diff --git a/src/keri/app/forwarding.py b/src/keri/app/forwarding.py index 748a09a6a..b4105b2a1 100644 --- a/src/keri/app/forwarding.py +++ b/src/keri/app/forwarding.py @@ -9,17 +9,18 @@ from ordered_set import OrderedSet as oset from hio.base import doing -from hio.help import decking, ogler +from hio.help import decking from keri import kering from keri.app import agenting from keri.app.habbing import GroupHab +from keri import help from keri.core import coring, eventing, serdering from keri.db import dbing from keri.kering import Roles from keri.peer import exchanging -logger = ogler.getLogger() +logger = help.ogler.getLogger() class Poster(doing.DoDoer): diff --git a/src/keri/app/grouping.py b/src/keri/app/grouping.py index 1607ee29f..51f58ce0a 100644 --- a/src/keri/app/grouping.py +++ b/src/keri/app/grouping.py @@ -51,7 +51,7 @@ def start(self, ghab, prefixer, seqner, saider): serder = serdering.SerderKERI(raw=evt) del evt[:serder.size] - print(f"Waiting for other signatures for {serder.pre}:{seqner.sn}...") + logger.info(f"Waiting for other signatures for {serder.ilk} {serder.pre}:{seqner.sn}...") return self.hby.db.gpse.add(keys=(prefixer.qb64,), val=(seqner, saider)) def complete(self, prefixer, seqner, saider=None): @@ -133,7 +133,7 @@ def processPartialSignedEscrow(self): if kever.delegated and kever.ilk in (coring.Ilks.dip, coring.Ilks.drt): # We are a delegated identifier, must wait for delegator approval for dip and drt if witered: # We are elected to perform delegation and witnessing messaging - print(f"We are the witnesser, sending {pre} to delegator") + logger.info(f"We are the witnesser, sending {pre} to delegator") self.swain.delegation(pre=pre, sn=seqner.sn) else: anchor = dict(i=pre, s=seqner.snh, d=saider.qb64) @@ -142,15 +142,15 @@ def processPartialSignedEscrow(self): else: self.witq.query(src=ghab.mhab.pre, pre=kever.delegator, anchor=anchor) - print("Waiting for delegation approval...") + logger.info(f"Waiting for delegation approval...") self.hby.db.gdee.add(keys=(pre,), val=(seqner, saider)) else: # Non-delegation, move on to witnessing if witered: # We are elected witnesser, send off event to witnesses - print(f"We are the fully signed witnesser {seqner.sn}, sending to witnesses") + logger.info(f"We are the fully signed witnesser {seqner.sn}, sending to witnesses") self.witDoer.msgs.append(dict(pre=pre, sn=seqner.sn)) # Move to escrow waiting for witness receipts - print(f"Waiting for fully signed witness receipts for {seqner.sn}") + logger.info(f"Waiting for fully signed witness receipts for {seqner.sn}") self.hby.db.gpwe.add(keys=(pre,), val=(seqner, saider)) def processDelegateEscrow(self): @@ -170,7 +170,7 @@ def processDelegateEscrow(self): if witer: # We are elected witnesser, We've already done out part in Boatswain, we are done. if self.swain.complete(prefixer=kever.prefixer, seqner=coring.Seqner(sn=kever.sn)): self.hby.db.gdee.rem(keys=(pre,)) - print(f"Delegation approval for {pre} received.") + logger.info(f"Delegation approval for {pre} received.") self.hby.db.cgms.put(keys=(pre, seqner.qb64), val=saider) @@ -181,10 +181,10 @@ def processDelegateEscrow(self): dgkey = dbing.dgKey(pre, saider.qb64b) self.hby.db.setAes(dgkey, couple) # authorizer event seal (delegator/issuer) self.hby.db.gdee.rem(keys=(pre,)) - print(f"Delegation approval for {pre} received.") + logger.info(f"Delegation approval for {pre} received.") # Move to escrow waiting for witness receipts - print(f"Waiting for witness receipts for {pre}") + logger.info(f"Waiting for witness receipts for {pre}") self.hby.db.gdee.rem(keys=(pre,)) self.hby.db.gpwe.add(keys=(pre,), val=(seqner, saider)) @@ -212,7 +212,7 @@ def processPartialWitnessEscrow(self): witnessed = True if not witnessed: continue - print(f"Witness receipts complete, {pre} confirmed.") + logger.info(f"Witness receipts complete, {pre} confirmed.") self.hby.db.gpwe.rem(keys=(pre,)) self.hby.db.cgms.put(keys=(pre, seqner.qb64), val=saider) elif not witer: @@ -243,6 +243,7 @@ def handle(self, serder, attachments=None): attachments (list): list of tuples of pather, CESR SAD path attachments to the exn event """ + logger.info("handling %s event SAID=%s", self.resource, serder.said) self.mux.add(serder=serder) diff --git a/src/keri/app/habbing.py b/src/keri/app/habbing.py index 965f64f26..d30f143e3 100644 --- a/src/keri/app/habbing.py +++ b/src/keri/app/habbing.py @@ -330,7 +330,7 @@ def loadHabs(self): if habord.mid and not habord.sid: hab = GroupHab(ks=self.ks, db=self.db, cf=self.cf, mgr=self.mgr, rtr=self.rtr, rvy=self.rvy, kvy=self.kvy, psr=self.psr, - name=name, pre=pre, temp=self.temp, smids=habord.smids) + name=name, pre=pre, temp=self.temp, smids=habord.smids, rmids=habord.rmids) groups.append(habord) elif habord.sid and not habord.mid: hab = SignifyHab(ks=self.ks, db=self.db, cf=self.cf, mgr=self.mgr, @@ -2736,24 +2736,34 @@ def make(self, *, code=coring.MtrDex.Blake3_256, transferable=True, isith=None, self.inited = True - def rotate(self, serder=None, **kwargs): + def rotate(self, serder=None, smids=None, rmids=None, **kwargs): + + if (habord := self.db.habs.get(keys=(self.name,))) is None: + raise kering.ValidationError(f"Missing HabitatRecord for name={self.name}") if serder is None: - return super(GroupHab, self).rotate(**kwargs) + msg = super(GroupHab, self).rotate(**kwargs) + else: - # sign handles group hab with .mhab case - sigers = self.sign(ser=serder.raw, verfers=serder.verfers, rotated=True) + # sign handles group hab with .mhab case + sigers = self.sign(ser=serder.raw, verfers=serder.verfers, rotated=True) - # update own key event verifier state - msg = eventing.messagize(serder, sigers=sigers) + # update own key event verifier state + msg = eventing.messagize(serder, sigers=sigers) - try: - self.kvy.processEvent(serder=serder, sigers=sigers) - except MissingSignatureError: - pass - except Exception as ex: - raise kering.ValidationError("Improper Habitat rotation for " - "pre={self.pre}.") from ex + try: + self.kvy.processEvent(serder=serder, sigers=sigers) + except MissingSignatureError: + pass + except Exception as ex: + raise kering.ValidationError("Improper Habitat rotation for " + "pre={self.pre}.") from ex + + self.smids = smids + self.rmids = rmids + habord.smids = smids + habord.rmids = rmids + self.db.habs.pin(keys=self.name, val=habord) return msg @@ -2870,7 +2880,6 @@ def query(self, pre, src, query=None, **kwa): return self.mhab.endorse(serder, last=True) - def witnesser(self): """This method name does not match logic??? """ diff --git a/src/keri/app/oobiing.py b/src/keri/app/oobiing.py index 41a5e6c92..3a45c122b 100644 --- a/src/keri/app/oobiing.py +++ b/src/keri/app/oobiing.py @@ -415,7 +415,7 @@ def processClients(self): obr.cid = response["headers"][ending.OOBI_AID_HEADER] if obr.oobialias is not None and obr.cid: - self.org.replace(pre=obr.cid, data=dict(alias=obr.oobialias, oobi=url)) + self.org.update(pre=obr.cid, data=dict(alias=obr.oobialias, oobi=url)) self.hby.db.coobi.rem(keys=(url,)) obr.state = Result.resolved diff --git a/src/keri/core/eventing.py b/src/keri/core/eventing.py index 1f85a70dd..5bbe5f2ca 100644 --- a/src/keri/core/eventing.py +++ b/src/keri/core/eventing.py @@ -7,16 +7,16 @@ import json import logging from collections import namedtuple -from dataclasses import dataclass, astuple, asdict, field +from dataclasses import dataclass, astuple from urllib.parse import urlsplit from math import ceil from ordered_set import OrderedSet as oset from hio.help import decking -from . import coring, serdering -from .coring import (versify, Serials, Ilks, MtrDex, PreDex, DigDex, +from . import coring +from .coring import (versify, Serials, Ilks, PreDex, DigDex, NonTransDex, CtrDex, Counter, - Number, Seqner, Siger, Cigar, Dater, Indexer, IdrDex, + Number, Seqner, Siger, Cigar, Dater, Verfer, Diger, Prefixer, Tholder, Saider) from . import serdering from .. import help @@ -26,14 +26,13 @@ from ..db.dbing import dgKey, snKey, fnKey, splitKeySN, splitKey from ..kering import (MissingEntryError, - ValidationError, DerivationError, MissingSignatureError, + ValidationError, MissingSignatureError, MissingWitnessSignatureError, UnverifiedReplyError, MissingDelegationError, OutOfOrderError, LikelyDuplicitousError, UnverifiedWitnessReceiptError, UnverifiedReceiptError, UnverifiedTransferableReceiptError, QueryNotFoundError) from ..kering import Version, Versionage -from ..kering import (ICP_LABELS, DIP_LABELS, ROT_LABELS, DRT_LABELS, IXN_LABELS, - RPY_LABELS) +from ..kering import (ICP_LABELS, DIP_LABELS, IXN_LABELS) from ..help import helping @@ -2294,9 +2293,12 @@ def valSigsDelWigs(self, serder, sigers, verfers, tholder, self.escrowPSEvent(serder=serder, sigers=sigers, wigers=wigers) if delseqner and delsaider: self.escrowPACouple(serder=serder, seqner=delseqner, saider=delsaider) - raise MissingSignatureError(f"Failure satisfying sith = {tholder.sith}" - f" on sigs for {[siger.qb64 for siger in sigers]}" - f" for evt = {serder.ked}.") + msg=(f"Failure satisfying sith = {tholder.sith} " + f"on sigs {[siger.qb64 for siger in sigers]} " + f"for evt = {serder.said}") + logger.trace(msg) + logger.trace("Event Body=\n%s\n", serder.pretty()) + raise MissingSignatureError(msg) if serder.ilk in (Ilks.rot, Ilks.drt): # rotation so check prior next threshold # prior next threshold in .ntholder and digers in .ndigers @@ -2334,12 +2336,11 @@ def valSigsDelWigs(self, serder, sigers, verfers, tholder, seqner=delseqner, saider=delsaider): # cue to query for witness receipts self.cues.push(dict(kin="query", q=dict(pre=serder.pre, sn=serder.snh))) - raise MissingWitnessSignatureError(f"Failure satisfying toad={toader.num} " - f"on witness sigs=" - f"{[siger.qb64 for siger in wigers]} " - f"for event={serder.ked}.") - - + msg = (f"Failure satisfying toad={toader.num} for event={serder.said}\n" + f"on witness sigs=\n{[siger.qb64 for siger in wigers]}") + logger.trace(msg) + logger.trace("Event Body=\n%s\n", serder.pretty()) + raise MissingWitnessSignatureError(msg) return sigers, delegator, wigers @@ -2495,8 +2496,9 @@ def validateDelegation(self, serder, sigers, wigers=None, delseqner=None, delsai # during initial delegation we just escrow the delcept event if delseqner is None and delsaider is None and delegator is not None: self.escrowPSEvent(serder=serder, sigers=sigers, wigers=wigers) - raise MissingDelegationError("No delegation seal for delegator {} " - "with evt = {}.".format(delegator, serder.ked)) + msg = (f"No delegation seal for delegator {delegator} with event = {serder.said}") + logger.debug("Event Body=\n%s\n", serder.pretty()) + raise MissingDelegationError(msg) ssn = validateSN(sn=delseqner.snh, inceptive=False) # delseqner Number should already do this #ssn = sner.num sner is Number seqner is Seqner need to replace Seqners with Numbers @@ -2701,18 +2703,22 @@ def logEvent(self, serder, sigers=None, wigers=None, wits=None, first=False, if self.cues is not None: # cue to notice BadCloneFN self.cues.push(dict(kin="noticeBadCloneFN", serder=serder, fn=fn, firner=firner, dater=dater)) - logger.info("Kever Mismatch Cloned Replay FN: %s First seen " - "ordinal fn %s and clone fn %s \nEvent=\n%s\n", - serder.preb, fn, firner.sn, serder.pretty()) + logger.info("Mismatch Cloned Replay FN: First seen " + "ordinal fn %s dig %s and clone fn %s event=%s", + fn, serder.pre, firner.sn, serder.said) + logger.debug("Event Body=\n%s\n", serder.pretty()) if dater: # cloned replay use original's dts from dater dtsb = dater.dtsb self.db.setDts(dgkey, dtsb) # first seen so set dts to now self.db.fons.pin(keys=dgkey, val=Seqner(sn=fn)) - logger.info("Kever state: %s First seen ordinal %s at %s\nEvent=\n%s\n", - serder.preb, fn, dtsb.decode("utf-8"), serder.pretty()) + logger.debug("Kever: First seen %s %s SAID=%s for %s at %s", + fn, serder.ilk, serder.said, serder.pre, dtsb.decode("utf-8")) + logger.debug("Event Body=\n%s\n", serder.pretty()) self.db.addKe(snKey(serder.preb, serder.sn), serder.saidb) - logger.info("Kever state: %s Added to KEL valid event=\n%s\n", - serder.preb, serder.pretty()) + pre = self.prefixer.qb64 + logger.info("[AID %s...%s]: Added to KEL %s at sn=%s valid event SAID=%s for AID %s", + pre[:4], pre[-4:], serder.ilk, serder.sn, serder.said, serder.pre) + logger.debug("Event Body=\n%s\n", serder.pretty()) return (fn, dtsb.decode("utf-8")) # (fn int, dts str) if first else (None, dts str) def escrowPSEvent(self, serder, sigers, wigers=None): @@ -2733,8 +2739,9 @@ def escrowPSEvent(self, serder, sigers, wigers=None): self.db.putEvt(dgkey, serder.raw) snkey = snKey(serder.preb, serder.sn) self.db.addPse(snkey, serder.saidb) # b'EOWwyMU3XA7RtWdelFt-6waurOTH_aW_Z9VTaU-CshGk.00000000000000000000000000000001' - logger.info("Kever state: Escrowed partially signed or delegated " - "event = %s\n", serder.ked) + logger.debug("Kever: Escrowed partially signed or delegated " + "event %s for AID %s", serder.said, serder.pre) + logger.debug("Event Body=\n%s\n", serder.pretty()) def escrowPACouple(self, serder, seqner, saider): """ @@ -2756,7 +2763,8 @@ def escrowPACouple(self, serder, seqner, saider): couple = seqner.qb64b + saider.qb64b self.db.putPde(dgkey, couple) # idempotent logger.info("Kever state: Escrowed source couple for partially signed " - "or delegated event = %s\n", serder.ked) + "or delegated event %s for AID %s", serder.said, serder.pre) + logger.debug("Event Body=\n%s\n", serder.pretty()) def escrowPWEvent(self, serder, wigers, sigers=None, seqner=None, saider=None): """ @@ -2780,8 +2788,8 @@ def escrowPWEvent(self, serder, wigers, sigers=None, seqner=None, saider=None): self.db.putPde(dgkey, couple) self.db.putEvt(dgkey, serder.raw) - logger.info("Kever state: Escrowed partially witnessed " - "event = %s\n", serder.ked) + logger.trace("Kever state: Escrowed partially witnessed event = %s", serder.said) + logger.trace("Event Body=\n%s\n", serder.pretty()) return self.db.addPwe(snKey(serder.preb, serder.sn), serder.saidb) @@ -3203,7 +3211,8 @@ def processEvent(self, serder, sigers, *, wigers=None, else: # escrow likely duplicitous event self.escrowLDEvent(serder=serder, sigers=sigers) - raise LikelyDuplicitousError("Likely Duplicitous event={}.".format(ked)) + logger.debug("Likely Duplicitous event Body=%s", serder.pretty()) + raise LikelyDuplicitousError(f"Likely Duplicitous event={serder.said}") else: # rot, drt, or ixn, so sn matters kever = self.kevers[pre] # get existing kever for pre @@ -3213,7 +3222,8 @@ def processEvent(self, serder, sigers, *, wigers=None, # escrow out-of-order event self.escrowOOEvent(serder=serder, sigers=sigers, seqner=delseqner, saider=delsaider, wigers=wigers) - raise OutOfOrderError("Out-of-order event={}.".format(ked)) + logger.debug("Out of Order event Body=%s", serder.pretty()) + raise OutOfOrderError(f"Out-of-order event={serder.said}") elif ((sn == sno) or # inorder event (ixn, rot, drt) or (ilk == Ilks.rot and # superseding recovery rot or @@ -3275,7 +3285,8 @@ def processEvent(self, serder, sigers, *, wigers=None, else: # escrow likely duplicitous event self.escrowLDEvent(serder=serder, sigers=sigers) - raise LikelyDuplicitousError("Likely Duplicitous event={}.".format(ked)) + logger.debug("Likely Duplicitous event Body=%s", serder.pretty()) + raise LikelyDuplicitousError(f"Likely Duplicitous event={serder.said}") def processReceiptWitness(self, serder, wigers): """ @@ -3329,12 +3340,14 @@ def processReceiptWitness(self, serder, wigers): if not self.lax and wiger.verfer.qb64 in self.prefixes: # own is receiptor if pre in self.prefixes: # skip own receiptor of own event # sign own events not receipt them - logger.info("Kevery process: skipped own receipt attachment" - " on own event receipt=\n%s\n", serder.pretty()) + logger.info("Kevery: skipped own receipt attachment " + "on own event receipt=%s", serder.said) + logger.debug("Receipt Body=\n%s\n", serder.pretty()) continue # skip own receipt attachment on own event if not self.local: # own receipt on other event when not local - logger.info("Kevery process: skipped own receipt attachment" - " on nonlocal event receipt=\n%s\n", serder.pretty()) + logger.info("Kevery: skipped own receipt attachment " + "on non-local event receipt=%s", serder.said) + logger.debug("Receipt Body=\n%s\n", serder.pretty()) continue # skip own receipt attachment on non-local event if wiger.verfer.verify(wiger.raw, lserder.raw): @@ -3392,12 +3405,15 @@ def processReceipt(self, serder, cigars): if not self.lax and cigar.verfer.qb64 in self.prefixes: # own is receiptor if pre in self.prefixes: # skip own receipter of own event # sign own events not receipt them - logger.info("Kevery process: skipped own receipt attachment" - " on own event receipt=\n%s\n", serder.pretty()) + logger.info("Kevery: skipped own receipt attachment " + "on own event receipt=\n%s\n", serder.said) + logger.debug("Receipt Body=\n%s\n", serder.pretty()) continue # skip own receipt attachment on own event if not self.local: # own receipt on other event when not local - logger.info("Kevery process: skipped own receipt attachment" - " on nonlocal event receipt=\n%s\n", serder.pretty()) + logger.info("Kevery: skipped own receipt attachment " + "on non-local event receipt=%s", serder.said) + logger.debug("Kevery: skipped own receipt attachment " + "on non-local event receipt=\n%s\n", serder.pretty()) continue # skip own receipt attachment on non-local event if cigar.verfer.verify(cigar.raw, lserder.raw): @@ -3460,12 +3476,14 @@ def processReceiptCouples(self, serder, cigars, firner=None): if not self.lax and cigar.verfer.qb64 in self.prefixes: # own is receiptor if pre in self.prefixes: # skip own receipter on own event # sign own events not receipt them - logger.info("Kevery process: skipped own receipt attachment" - " on own event receipt=\n%s\n", serder.pretty()) + logger.info("Kevery: skipped own receipt attachment" + " on own event receipt=%s", serder.said) + logger.debug("Receipt Body=\n%s\n", serder.pretty()) continue # skip own receipt attachment on own event if not self.local: # own receipt on other event when not local - logger.info("Kevery process: skipped own receipt attachment" - " on nonlocal event receipt=\n%s\n", serder.pretty()) + logger.info("Kevery: skipped own receipt attachment" + " on nonlocal event receipt=\n%s\n", serder.said) + logger.debug("Receipt Body=\n%s\n", serder.pretty()) continue # skip own receipt attachment on non-local event if cigar.verfer.verify(cigar.raw, serder.raw): @@ -3508,7 +3526,8 @@ def processReceiptTrans(self, serder, tsgs): if ldig is None: # escrow because event does not yet exist in database # take advantage of fact that receipt and event have same pre, sn fields self.escrowTRGroups(serder, tsgs) - raise UnverifiedTransferableReceiptError("Unverified receipt={}.".format(ked)) + logger.debug("Unverified receipt body=\n%s\n", serder.pretty()) + raise UnverifiedTransferableReceiptError("Unverified receipt=%s", serder.said) # retrieve event by dig assumes if ldig is not None that event exists at ldig ldig = bytes(ldig).decode("utf-8") @@ -3516,18 +3535,18 @@ def processReceiptTrans(self, serder, tsgs): lserder = serdering.SerderKERI(raw=bytes(lraw)) # verify digs match if not lserder.compare(said=ldig): # mismatch events problem with replay - raise ValidationError("Mismatch receipt of event at sn = {} with db." - "".format(sn)) + raise ValidationError(f"Mismatch receipt of event at sn = {sn} with db.") for sprefixer, sseqner, saider, sigers in tsgs: # iterate over each tsg if not self.lax and sprefixer.qb64 in self.prefixes: # own is receipter if pre in self.prefixes: # skip own receipter of own event # sign own events not receipt them - raise ValidationError("Own pre={} receipter of own event" - " {}.".format(self.prefixes, serder.pretty())) + logger.debug("Own pre=%s receiptor of own event \n%s\n", + self.prefixes, serder.pretty()) + raise ValidationError(f"Own pre={self.prefixes} receiptor of own event {serder.said}") if not self.local: # skip own receipts of nonlocal events - raise ValidationError("Own pre={} receipter of nonlocal event " - "{}.".format(self.prefixes, serder.pretty())) + logger.debug("Own pre=%s receiptor of non-local event \n%s\n", serder.prefixes, serder.pretty()) + raise ValidationError(f"Own pre={self.prefixes} receiptor of non-local event {serder.said}") # receipted event in db so attempt to get receipter est evt # retrieve dig of last event at sn of est evt of receipter. @@ -3651,12 +3670,10 @@ def processReceiptQuadruples(self, serder, trqs, firner=None): siger.verfer = sverfers[siger.index] # assign verfer if not siger.verfer.verify(siger.raw, serder.raw): # verify sig - logger.info("Kevery unescrow error: Bad trans receipt sig." - "pre=%s sn=%x receipter=%s\n", pre, sn, sprefixer.qb64) - - raise ValidationError("Bad escrowed trans receipt sig at " - "pre={} sn={:x} receipter={}." - "".format(pre, sn, sprefixer.qb64)) + msg = (f"Kevery unescrow error: Bad escrowed trans receipt sig at " + f"pre={pre} sn={sn:x} receiptor={sprefixer.qb64}") + logger.trace(msg) + raise ValidationError(msg) # good sig so write receipt quadruple to database @@ -3667,10 +3684,12 @@ def processReceiptQuadruples(self, serder, trqs, firner=None): else: # escrow either receiptor or receipted event not yet in database self.escrowTRQuadruple(serder, sprefixer, sseqner, saider, siger) - raise UnverifiedTransferableReceiptError("Unverified receipt: " - "missing associated event for transferable " - "validator receipt quadruple for event={}." - "".format(ked)) + msg = (f"Unverified receipt: " + f"missing associated event for transferable " + f"validator receipt quadruple for event={serder.said}") + logger.info(msg) + logger.debug("Event Body=\n%s\n", serder.pretty()) + raise UnverifiedTransferableReceiptError(msg) def removeStaleReplyEndRole(self, saider): """ @@ -3780,7 +3799,7 @@ def processReplyEndRole(self, *, serder, saider, route, aid = cid # authorizing attribution id keys = (aid, role, eid) osaider = self.db.eans.get(keys=keys) # get old said if any - if osaider is not None and osaider.qb64b == saider.qb64b: + if osaider is not None and osaider.qb64b == saider.qb64b: # check idempotent osaider = None # BADA Logic accepted = self.rvy.acceptReply(serder=serder, saider=saider, route=route, @@ -3884,7 +3903,8 @@ def processReplyLocScheme(self, *, serder, saider, route, aid=aid, osaider=osaider, cigars=cigars, tsgs=tsgs) if not accepted: - raise UnverifiedReplyError(f"Unverified loc scheme reply. {serder.ked}") + logger.debug("Unverified loc scheme reply body=\n%s\n", serder.pretty()) + raise UnverifiedReplyError(f"Unverified loc scheme reply for URL {serder.ked['a']['url']}") self.updateLoc(keys=keys, saider=saider, url=url) # update .lans and .locs @@ -4110,18 +4130,21 @@ def processQuery(self, serder, source=None, sigers=None, cigars=None): if pre not in self.kevers: self.escrowQueryNotFoundEvent(serder=serder, prefixer=source, sigers=sigers, cigars=cigars) - raise QueryNotFoundError("Query not found error={}.".format(ked)) + logger.debug("Query not found error. Query Body=%s", ked) + raise QueryNotFoundError(f"Query not found error for at route {ked['r']} for evt {ked['d']}") kever = self.kevers[pre] if anchor: if not self.db.findAnchoringSealEvent(pre=pre, seal=anchor): self.escrowQueryNotFoundEvent(serder=serder, prefixer=source, sigers=sigers, cigars=cigars) - raise QueryNotFoundError("Query not found error={}.".format(ked)) + logger.debug("Query not found error. Query Body=%s", ked) + raise QueryNotFoundError(f"Query not found error at route {ked['r']} for evt {ked['d']}") elif sn is not None: if kever.sner.num < sn or not self.db.fullyWitnessed(kever.serder): self.escrowQueryNotFoundEvent(serder=serder, prefixer=source, sigers=sigers, cigars=cigars) - raise QueryNotFoundError("Query not found error={}.".format(ked)) + logger.debug("Query not found error. Query Body=%s", ked) + raise QueryNotFoundError(f"Query not found error at route {ked['r']} for evt {ked['d']}") msgs = list() # outgoing messages for msg in self.db.clonePreIter(pre=pre, fn=0): @@ -4141,7 +4164,8 @@ def processQuery(self, serder, source=None, sigers=None, cigars=None): if pre not in self.kevers: self.escrowQueryNotFoundEvent(serder=serder, prefixer=source, sigers=sigers, cigars=cigars) - raise QueryNotFoundError("Query not found error={}.".format(ked)) + logger.debug("Query not found error. Query Body=%s", ked) + raise QueryNotFoundError(f"Query not found error at route {ked['r']} for evt {ked['d']}") kever = self.kevers[pre] @@ -4151,7 +4175,8 @@ def processQuery(self, serder, source=None, sigers=None, cigars=None): if len(wigers) < kever.toader.num: self.escrowQueryNotFoundEvent(serder=serder, prefixer=source, sigers=sigers, cigars=cigars) - raise QueryNotFoundError("Query not found error={}.".format(ked)) + logger.debug("Query not found error. Query Body=%s", ked) + raise QueryNotFoundError(f"Query not found error at route {ked['r']} for evt {ked['d']}") rserder = reply(route=f"/ksn/{src}", data=kever.state()._asdict()) self.cues.push(dict(kin="reply", src=src, route="/ksn", serder=rserder, @@ -4164,7 +4189,8 @@ def processQuery(self, serder, source=None, sigers=None, cigars=None): if pre not in self.kevers: self.escrowQueryNotFoundEvent(serder=serder, prefixer=source, sigers=sigers, cigars=cigars) - raise QueryNotFoundError("Query not found error={}.".format(ked)) + logger.debug("Query not found error. Query Body=%s", ked) + raise QueryNotFoundError(f"Query not found error at route {ked['r']} for evt {ked['d']}") self.cues.push(dict(kin="stream", serder=serder, pre=pre, src=src, topics=topics)) # if pre in self.kevers: @@ -4173,7 +4199,8 @@ def processQuery(self, serder, source=None, sigers=None, cigars=None): # self.cues.push(dict(kin="stream", serder=serder, pre=pre, src=src, topics=topics)) else: self.cues.push(dict(kin="invalid", serder=serder)) - raise ValidationError("invalid query message {} for evt = {}".format(ilk, ked)) + logger.debug("Invalid Query error. Query Body=%s", ked) + raise ValidationError(f"invalid query message {ilk} at route {ked['r']} for evt = {ked['d']}") def fetchEstEvent(self, pre, sn): """ @@ -4227,8 +4254,8 @@ def escrowOOEvent(self, serder, sigers, seqner=None, saider=None, wigers=None): couple = seqner.qb64b + saider.qb64b self.db.putPde(dgkey, couple) # idempotent # log escrowed - logger.info("Kevery process: escrowed out of order event=\n%s\n", - json.dumps(serder.ked, indent=1)) + logger.info("Kevery: escrowed out of order event=\n%s\n", serder.said) + logger.debug("Event Body=\n%s\n", serder.pretty()) def escrowQueryNotFoundEvent(self, prefixer, serder, sigers, cigars=None): """ @@ -4251,8 +4278,8 @@ def escrowQueryNotFoundEvent(self, prefixer, serder, sigers, cigars=None): self.db.addRct(key=dgkey, val=cigar.verfer.qb64b + cigar.qb64b) # log escrowed - logger.info("Kevery process: escrowed query not found event=\n%s\n", - json.dumps(serder.ked, indent=1)) + logger.trace("Kevery: escrowed query not found event = %s", serder.said) + logger.trace("Event Body=\n%s\n", serder.pretty()) def escrowLDEvent(self, serder, sigers): """ @@ -4460,11 +4487,10 @@ def escrowTRQuadruple(self, serder, sprefixer, sseqner, saider, siger): def processEscrows(self): """ - Iterate throush escrows and process any that may now be finalized + Iterate through escrows and process any that may now be finalized Parameters: """ - try: self.processEscrowOutOfOrders() self.processEscrowUnverWitness() @@ -4474,12 +4500,10 @@ def processEscrows(self): self.processEscrowPartialSigs() self.processEscrowDuplicitous() self.processQueryNotFound() - except Exception as ex: # log diagnostics errors etc if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery escrow process error: %s\n", ex.args[0]) - else: - logger.error("Kevery escrow process error: %s\n", ex.args[0]) + logger.trace("Kevery: other escrow process error: %s\n", ex.args[0]) + logger.exception("Kevery other escrow process error: %s\n", ex.args[0]) raise ex def processEscrowOutOfOrders(self): @@ -4529,32 +4553,26 @@ def processEscrowOutOfOrders(self): dtb = self.db.getDts(dgKey(pre, bytes(edig))) if dtb is None: # othewise is a datetime as bytes # no date time so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event datetime" - " at dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed event datetime " - "at dig = {}.".format(bytes(edig))) + msg = f"Kevery ooo escrow unescrow error: Missing event datetime at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # do date math here and discard if stale nowIso8601() bytes dtnow = helping.nowUTC() dte = helping.fromIso8601(bytes(dtb)) if (dtnow - dte) > datetime.timedelta(seconds=self.TimeoutOOE): # escrow stale so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Stale event escrow " - " at dig = %s\n", bytes(edig)) - - raise ValidationError("Stale event escrow " - "at dig = {}.".format(bytes(edig))) + msg = f"Kevery ooo escrow unescrow error: Stale event escrow at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # get the escrowed event using edig eraw = self.db.getEvt(dgKey(pre, bytes(edig))) if eraw is None: # no event so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event at." - "dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed evt at dig = {}." - "".format(bytes(edig))) + msg = f"Kevery ooo escrow unescrow error: Missing event at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) eserder = serdering.SerderKERI(raw=bytes(eraw)) # escrowed event @@ -4562,11 +4580,9 @@ def processEscrowOutOfOrders(self): sigs = self.db.getSigs(dgKey(pre, bytes(edig))) if not sigs: # otherwise its a list of sigs # no sigs so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event sigs at." - "dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed evt sigs at " - "dig = {}.".format(bytes(edig))) + msg = f"Kevery ooo escrow unescrow error: Missing event sigs at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # process event sigers = [Siger(qb64b=bytes(sig)) for sig in sigs] @@ -4593,26 +4609,25 @@ def processEscrowOutOfOrders(self): except OutOfOrderError as ex: # still waiting on missing prior event to validate - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("Kevery ooo escrow unescrow failed: %s\n", ex.args[0]) + logger.exception("Kevery ooo escrow unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than out of order so remove from OO escrow self.db.delOoe(snKey(pre, sn), edig) # removes one escrow at key val if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrowed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrowed: %s\n", ex.args[0]) + logger.debug("Kevery: ooo escrow other error on escrow: %s\n", ex.args[0]) + logger.exception("Kevery: ooo escrow other error on : %s\n", ex.args[0]) else: # unescrow succeeded, remove from escrow # We don't remove all escrows at pre,sn because some might be # duplicitous so we process remaining escrows in spite of found # valid event escrow. self.db.delOoe(snKey(pre, sn), edig) # removes one escrow at key val - logger.info("Kevery unescrow succeeded in valid event: " - "event=\n%s\n", json.dumps(eserder.ked, indent=1)) + logger.info("Kevery ooo escrow unescrow succeeded in valid event: " + "key = %s \tdigest = %s", bytes(ekey).decode(), bytes(edig).decode()) + logger.debug("Event Body=\n%s\n", eserder.pretty()) if ekey == key: # still same so no escrows found on last while iteration break @@ -4662,45 +4677,37 @@ def processEscrowPartialSigs(self): dgkey = dgKey(pre, bytes(edig)) # check date if expired then remove escrow. dtb = self.db.getDts(dgkey) - if dtb is None: # othewise is a datetime as bytes - # no date time so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event datetime" - " at dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed event datetime " - "at dig = {}.".format(bytes(edig))) + if dtb is None: # otherwise is a datetime as bytes + # no date time so raise ValidationError which unescrows below] + msg = f"Kevery: partial sig escrow unescrow error: Missing event datetime at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # do date math here and discard if stale nowIso8601() bytes dtnow = helping.nowUTC() dte = helping.fromIso8601(bytes(dtb)) if (dtnow - dte) > datetime.timedelta(seconds=self.TimeoutPSE): # escrow stale so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Stale event escrow " - " at dig = %s\n", bytes(edig)) - - raise ValidationError("Stale event escrow " - "at dig = {}.".format(bytes(edig))) + msg = f"Kevery: partial sig escrow unescrow error: Stale event escrow at dig = {bytes(edig)}", + logger.trace(msg) + raise ValidationError(msg) # get the escrowed event using edig eraw = self.db.getEvt(dgkey) if eraw is None: - # no event so so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event at." - "dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed evt at dig = {}." - "".format(bytes(edig))) + # no event so raise ValidationError which unescrows below + msg = f"Kevery: partial sig escrow unescrow error: Missing event at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) eserder = serdering.SerderKERI(raw=bytes(eraw)) # escrowed event # get sigs and attach sigs = self.db.getSigs(dgkey) if not sigs: # otherwise its a list of sigs # no sigs so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event sigs at." - "dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed evt sigs at " - "dig = {}.".format(bytes(edig))) + msg = f"Kevery: partial sig escrow unescrow error: Missing event sigs at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # seal source (delegator issuer if any) delseqner = delsaider = None @@ -4743,10 +4750,9 @@ def processEscrowPartialSigs(self): except (MissingSignatureError, MissingDelegationError) as ex: # still waiting on missing sigs or missing seal to validate - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("Kevery: partial sig escrow unescrow failed: %s\n", ex.args[0]) + logger.exception("Kevery: partial sig escrow unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than waiting on sigs or seal so remove from escrow @@ -4756,9 +4762,8 @@ def processEscrowPartialSigs(self): self.cues.push(dict(kin="psUnescrow", serder=eserder)) if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrowed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrowed: %s\n", ex.args[0]) + logger.trace("Kevery: partial sig escrow other error on unescrow: %s\n", ex.args[0]) + logger.exception("Kevery: partial sig escrow other error on unescrow: %s\n", ex.args[0]) else: # unescrow succeeded, remove from escrow # We don't remove all escrows at pre,sn because some might be @@ -4770,8 +4775,9 @@ def processEscrowPartialSigs(self): if eserder is not None and eserder.ked["t"] in (Ilks.dip, Ilks.drt,): self.cues.push(dict(kin="psUnescrow", serder=eserder)) - logger.info("Kevery unescrow succeeded in valid event: " - "event=\n%s\n", json.dumps(eserder.ked, indent=1)) + logger.trace("Kevery: partial sig escrow unescrow succeeded in valid event: " + "key = %s \tdigest = %s", bytes(ekey).decode(), bytes(edig).decode()) + logger.trace("Event Body=\n%s\n", json.dumps(eserder.ked, indent=1)) if ekey == key: # still same so no escrows found on last while iteration break @@ -4823,32 +4829,26 @@ def processEscrowPartialWigs(self): dtb = self.db.getDts(dgKey(pre, bytes(edig))) if dtb is None: # othewise is a datetime as bytes # no date time so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event datetime" - " at dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed event datetime " - "at dig = {}.".format(bytes(edig))) + msg = f"Kevery: partial wig escrow unescrow error: Missing event datetime at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # do date math here and discard if stale nowIso8601() bytes dtnow = helping.nowUTC() dte = helping.fromIso8601(bytes(dtb)) if (dtnow - dte) > datetime.timedelta(seconds=self.TimeoutPWE): # escrow stale so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Stale event escrow " - " at dig = %s\n", bytes(edig)) - - raise ValidationError("Stale event escrow " - "at dig = {}.".format(bytes(edig))) + msg = f"Kevery: partial wig escrow unescrow error: Stale event escrow at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # get the escrowed event using edig eraw = self.db.getEvt(dgKey(pre, bytes(edig))) if eraw is None: # no event so so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event at." - "dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed evt at dig = {}." - "".format(bytes(edig))) + msg = f"Kevery: partial wig escrow unescrow error: Missing event at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) eserder = serdering.SerderKERI(raw=bytes(eraw)) # escrowed event @@ -4856,11 +4856,9 @@ def processEscrowPartialWigs(self): sigs = self.db.getSigs(dgKey(pre, bytes(edig))) # list of sigs if not sigs: # empty list # no sigs so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event sigs at." - "dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed evt sigs at " - "dig = {}.".format(bytes(edig))) + msg = f"Kevery: partial wig escrow unescrow error: Missing event sigs at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # get wigs wigs = self.db.getWigs(dgKey(pre, bytes(edig))) # list of wigs @@ -4870,8 +4868,8 @@ def processEscrowPartialWigs(self): # which may not arrive until some time after event is fully signed # so just log for debugging but do not unescrow by raising # ValidationError - logger.info("Kevery unescrow wigs: No event wigs yet at." - "dig = %s\n", bytes(edig)) + logger.trace("Kevery unescrow wigs: No event wigs yet at." + "dig = %s\n", bytes(edig)) # raise ValidationError("Missing escrowed evt wigs at " # "dig = {}.".format(bytes(edig))) @@ -4908,26 +4906,25 @@ def processEscrowPartialWigs(self): except MissingWitnessSignatureError as ex: # still waiting on missing witness sigs - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("Kevery: partial wig escrow unescrow failed: %s\n", ex.args[0]) + logger.exception("Kevery: partial wig escrow unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than waiting on sigs or seal so remove from escrow self.db.delPwe(snKey(pre, sn), edig) # removes one escrow at key val - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrowed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrowed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("Kevery: partial wig escrow other error on unescrow: %s\n", ex.args[0]) + logger.exception("Kevery: partial wig escrow other error unescrow: %s\n", ex.args[0]) else: # unescrow succeeded, remove from escrow # We don't remove all escrows at pre,sn because some might be # duplicitous so we process remaining escrows in spite of found # valid event escrow. self.db.delPwe(snKey(pre, sn), edig) # removes one escrow at key val - logger.info("Kevery unescrow succeeded in valid event: " - "event=\n%s\n", json.dumps(eserder.ked, indent=1)) + logger.trace("Kevery: partial wig escrow unescrow succeeded in valid event: " + "key = %s \tdigest = %s", bytes(ekey).decode(), bytes(edig).decode()) + logger.trace("Event Body=\n%s\n", json.dumps(eserder.ked, indent=1)) if ekey == key: # still same so no escrows found on last while iteration break @@ -4996,22 +4993,18 @@ def processEscrowUnverWitness(self): dtb = self.db.getDts(dgKey(pre, bytes(rdiger.qb64b))) if dtb is None: # othewise is a datetime as bytes # no date time so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event datetime" - " at dig = %s\n", rdiger.qb64b) - - raise ValidationError("Missing escrowed event datetime " - "at dig = {}.".format(rdiger.qb64b)) + msg = f"Kevery: unver wit escrow unescrow error: Missing event datetime at dig = {rdiger.qb64}" + logger.trace(msg) + raise ValidationError(msg) # do date math here and discard if stale nowIso8601() bytes dtnow = helping.nowUTC() dte = helping.fromIso8601(bytes(dtb)) if (dtnow - dte) > datetime.timedelta(seconds=self.TimeoutUWE): # escrow stale so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Stale event escrow " - " at dig = %s\n", rdiger.qb64b) - - raise ValidationError("Stale event escrow " - "at dig = {}.".format(rdiger.qb64b)) + msg = f"Kevery: unver wit escrow unescrow error: Stale event escrow at dig = {rdiger.qb64}" + logger.trace(msg) + raise ValidationError(msg) # lookup database dig of the receipted event in pwes escrow # using pre and sn lastEvt @@ -5022,35 +5015,32 @@ def processEscrowUnverWitness(self): if not found: # no partial witness escrow of event found # so keep in escrow by raising UnverifiedWitnessReceiptError - logger.info("Kevery unescrow error: Missing witness " - "receipted evt at pre=%s sn=%x\n", (pre, sn)) - - raise UnverifiedWitnessReceiptError("Missing witness " - "receipted evt at pre={} sn={:x}".format(pre, sn)) + msg = (f"Kevery: unver wit escrow unescrow error: " + f"Missing witness receipted evt at pre={pre} sn={sn:x}") + logger.trace(msg) + raise UnverifiedWitnessReceiptError(msg) except UnverifiedWitnessReceiptError as ex: # still waiting on missing prior event to validate # only happens if we process above - if logger.isEnabledFor(logging.DEBUG): # adds exception data - logger.exception("Kevery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): # adds exception data + logger.trace("Kevery: unver wit escrow unescrow failed: %s\n", ex.args[0]) + logger.exception("Kevery: unver wit escrow unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than out of order so remove from OO escrow self.db.delUwe(snKey(pre, sn), ecouple) # removes one escrow at key val - if logger.isEnabledFor(logging.DEBUG): # adds exception data - logger.exception("Kevery unescrowed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrowed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): # adds exception data + logger.trace("Kevery: unver wit escrow other unescrow error on unescrow: %s\n", ex.args[0]) + logger.exception("Kevery: unver wit escrow other unescrow error on unescrow: %s\n", ex.args[0]) else: # unescrow succeeded, remove from escrow # We don't remove all escrows at pre,sn because some might be # duplicitous so we process remaining escrows in spite of found # valid event escrow. self.db.delUwe(snKey(pre, sn), ecouple) # removes one escrow at key val - logger.info("Kevery unescrow succeeded for event pre=%s " - "sn=%s\n", pre, sn) + logger.info("Kevery: unver wit escrow unescrow succeeded for event pre=%s " + "sn=%s", pre, sn) if ekey == key: # still same so no escrows found on last while iteration break @@ -5116,22 +5106,20 @@ def processEscrowUnverNonTrans(self): dtb = self.db.getDts(dgKey(pre, bytes(rsaider.qb64b))) if dtb is None: # othewise is a datetime as bytes # no date time so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event datetime" - " at dig = %s\n", rsaider.qb64b) - - raise ValidationError("Missing escrowed event datetime " - "at dig = {}.".format(rsaider.qb64b)) + msg = (f"Kevery: unver nontrans escrow unescrow error: " + f"Missing event datetime at dig = {rsaider.qb64}") + logger.trace(msg) + raise ValidationError(msg) # do date math here and discard if stale nowIso8601() bytes dtnow = helping.nowUTC() dte = helping.fromIso8601(bytes(dtb)) if (dtnow - dte) > datetime.timedelta(seconds=self.TimeoutURE): # escrow stale so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Stale event escrow " - " at dig = %s\n", rsaider.qb64b) - - raise ValidationError("Stale event escrow " - "at dig = {}.".format(rsaider.qb64b)) + msg = (f"Kevery: unver nontrans escrow unescrow error: " + f"Stale event escrow at dig = {rsaider.qb64}") + logger.trace(msg) + raise ValidationError(msg) # Is receipt for unverified witnessed event in .Pwes escrow # if found then try else clause will remove from escrow @@ -5148,41 +5136,35 @@ def processEscrowUnverNonTrans(self): dig = self.db.getKeLast(snKey(pre, sn)) if dig is None: # no receipted event so keep in escrow - logger.info("Kevery unescrow error: Missing receipted " - "event at pre=%s sn=%x\n", pre, sn) - - raise UnverifiedReceiptError("Missing receipted evt " - "at pre={} sn={:x}".format(pre, sn)) + msg = (f"Kevery: unver nontrans escrow unescrow error: " + f"Missing receipted evt at pre={pre} sn={sn:x}") + logger.trace(msg) + raise UnverifiedReceiptError(msg) # get receipted event using pre and edig raw = self.db.getEvt(dgKey(pre, dig)) if raw is None: # receipted event superseded so remove from escrow - logger.info("Kevery unescrow error: Invalid receipted " - "event refereance at pre=%s sn=%x\n", pre, sn) - - raise ValidationError("Invalid receipted evt reference" - " at pre={} sn={:x}".format(pre, sn)) + msg = (f"Kevery: unver nontrans escrow unescrow error: " + f"Invalid receipted event reference at pre={pre} sn={sn:x}") + logger.trace(msg) + raise ValidationError(msg) serder = serdering.SerderKERI(raw=bytes(raw)) # receipted event # compare digs if rsaider.qb64b != serder.saidb: - logger.info("Kevery unescrow error: Bad receipt dig." - "pre=%s sn=%x receipter=%s\n", pre, sn, sprefixer.qb64) - - raise ValidationError("Bad escrowed receipt dig at " - "pre={} sn={:x} receipter={}." - "".format(pre, sn, sprefixer.qb64)) + msg = (f"Kevery: unver nontrans escrow unescrow error: Bad receipt dig" + f"pre={pre} sn={sn:x} receiptor={sprefixer.qb64}") + logger.trace(msg) + raise ValidationError(msg) # verify sig verfer key is prefixer from triple if not cigar.verfer.verify(cigar.raw, serder.raw): # no sigs so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Bad receipt sig." - "pre=%s sn=%x receipter=%s\n", pre, sn, sprefixer.qb64) - - raise ValidationError("Bad escrowed receipt sig at " - "pre={} sn={:x} receipter={}." - "".format(pre, sn, sprefixer.qb64)) + msg = (f"Kevery: unver nontrans escrow unescrow error: " + f"Bad receipt sig at pre={pre} sn={sn:x} receiptor={sprefixer.qb64}") + logger.trace(msg) + raise ValidationError(msg) # get current wits from kever state assuming not stale # receipt. Need function here to compute wits for actual @@ -5204,26 +5186,24 @@ def processEscrowUnverNonTrans(self): except UnverifiedReceiptError as ex: # still waiting on missing prior event to validate # only happens if we process above - if logger.isEnabledFor(logging.DEBUG): # adds exception data - logger.exception("Kevery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): # adds exception data + logger.trace("Kevery: unver nontrans escrow unescrow failed: %s\n", ex.args[0]) + logger.exception("Kevery: unver nontrans escrow unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than out of order so remove from OO escrow self.db.delUre(snKey(pre, sn), etriplet) # removes one escrow at key val - if logger.isEnabledFor(logging.DEBUG): # adds exception data - logger.exception("Kevery unescrowed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrowed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): # adds exception data + logger.trace("Kevery: unver nontrans escrow other error on unescrow: %s\n", ex.args[0]) + logger.exception("Kevery: unver nontrans escrow other error on unescrow: %s\n", ex.args[0]) else: # unescrow succeeded, remove from escrow # We don't remove all escrows at pre,sn because some might be # duplicitous so we process remaining escrows in spite of found # valid event escrow. self.db.delUre(snKey(pre, sn), etriplet) # removes one escrow at key val - logger.info("Kevery unescrow succeeded for event pre=%s " - "sn=%s\n", pre, sn) + logger.info("Kevery: unver nontrans escrow unescrow succeeded for event pre=%s " + "sn=%s", pre, sn) if ekey == key: # still same so no escrows found on last while iteration break @@ -5265,32 +5245,26 @@ def processQueryNotFound(self): dtb = self.db.getDts(dgKey(pre, bytes(edig))) if dtb is None: # othewise is a datetime as bytes # no date time so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event datetime" - " at dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed event datetime " - "at dig = {}.".format(bytes(edig))) + msg = f"Kevery: qnf escrow unescrow error: Missing event datetime at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # do date math here and discard if stale nowIso8601() bytes dtnow = helping.nowUTC() dte = helping.fromIso8601(bytes(dtb)) if (dtnow - dte) > datetime.timedelta(seconds=self.TimeoutQNF): # escrow stale so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Stale qry event escrow " - " at dig = %s\n", bytes(edig)) - - raise ValidationError("Stale qry event escrow " - "at dig = {}.".format(bytes(edig))) + msg = f"Kevery: qnf escrow unescrow error: Stale qry event escrow at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # get the escrowed event using edig eraw = self.db.getEvt(dgKey(pre, bytes(edig))) if eraw is None: # no event so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event at." - "dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed evt at dig = {}." - "".format(bytes(edig))) + msg = f"Kevery: qnf escrow unescrow error: Missing event at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) eserder = serdering.SerderKERI(raw=bytes(eraw)) # escrowed event @@ -5298,11 +5272,9 @@ def processQueryNotFound(self): sigs = self.db.getSigs(dgKey(pre, bytes(edig))) if not sigs: # otherwise its a list of sigs # no sigs so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event sigs at." - "dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed evt sigs at " - "dig = {}.".format(bytes(edig))) + msg = f"Kevery: qnf escrow unescrow error: Missing event sigs at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # process event sigers = [Siger(qb64b=bytes(sig)) for sig in sigs] @@ -5319,25 +5291,24 @@ def processQueryNotFound(self): except QueryNotFoundError as ex: # still waiting on missing prior event to validate - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("Kevery: qnf escrow unescrow failed: %s\n", ex.args[0]) + logger.exception("Kevery: qnf escrow unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than out of order so remove from OO escrow self.db.delQnf(dgKey(pre, edig), edig) # removes one escrow at key val - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrowed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrowed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("Kevery: qnf other error on unescrow: %s\n", ex.args[0]) + logger.exception("Kevery: qnf other error on unescrow: %s\n", ex.args[0]) else: # unescrow succeeded, remove from escrow # We don't remove all escrows at pre,sn because some might be # duplicitous so we process remaining escrows in spite of found # valid event escrow. self.db.delQnf(dgKey(pre, edig), edig) # removes one escrow at key val - logger.info("Kevery unescrow succeeded in valid event: " - "event=\n%s\n", json.dumps(eserder.ked, indent=1)) + logger.info("Kevery: qnf unescrow succeeded in valid event: " + "key = %s \tdigest = %s", bytes(ekey).decode(), bytes(edig).decode()) + logger.debug("Event Body=\n%s\n", eserder.pretty()) if ekey == key: # still same so no escrows found on last while iteration break @@ -5428,10 +5399,10 @@ def _processEscrowFindUnver(self, pre, sn, rsaider, wiger=None, cigar=None): elif wiger: # check index and assign verfier to wiger if wiger.index >= len(wits): # bad index # raise ValidationError which removes from escrow by caller - logger.info("Kevery unescrow error: Bad witness receipt" - " index=%i for pre=%s sn=%x\n", wiger.index, pre, sn) - raise ValidationError("Bad escrowed witness receipt index={}" - " at pre={} sn={:x}.".format(wiger.index, pre, sn)) + msg = (f"Kevery: find unver escrow unescrow error: Bad escrowed witness receipt" + f" index={wiger.index:i} for pre={pre} sn={sn:x}") + logger.trace(msg) + raise ValidationError(msg) wiger.verfer = Verfer(qb64=wits[wiger.index]) found = True @@ -5440,12 +5411,10 @@ def _processEscrowFindUnver(self, pre, sn, rsaider, wiger=None, cigar=None): if found: # verify signature and if verified write to .Wigs if not wiger.verfer.verify(wiger.raw, serder.raw): # not verify # raise ValidationError which unescrows .Uwes or .Ures in caller - logger.info("Kevery unescrow error: Bad witness receipt" - " wig. pre=%s sn=%x\n", pre, sn) - - raise ValidationError("Bad escrowed witness receipt wig" - " at pre={} sn={:x}." - "".format(pre, sn)) + msg = (f"Kevery: find unver escrow unescrow error: Bad witness receipt wig." + f" pre={pre} sn={sn:x}") + logger.trace(msg) + raise ValidationError(msg) self.db.addWig(key=dgKey(pre, serder.said), val=wiger.qb64b) # processEscrowPartialWigs removes from this .Pwes escrow # when fully witnessed using self.db.delPwe(snkey, dig) @@ -5509,53 +5478,44 @@ def processEscrowUnverTrans(self): dtb = self.db.getDts(dgKey(pre, bytes(esaider.qb64b))) if dtb is None: # othewise is a datetime as bytes # no date time so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event datetime" - " at dig = %s\n", esaider.qb64b) - - raise ValidationError("Missing escrowed event datetime " - "at dig = {}.".format(esaider.qb64b)) + msg = f"Kevery: unver trans escrow unescrow error: Missing event datetime at dig = {esaider.qb64}" + logger.trace(msg) + raise ValidationError(msg) # do date math here and discard if stale nowIso8601() bytes dtnow = helping.nowUTC() dte = helping.fromIso8601(bytes(dtb)) if (dtnow - dte) > datetime.timedelta(seconds=self.TimeoutVRE): # escrow stale so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Stale event escrow " - " at dig = %s\n", esaider.qb64b) - - raise ValidationError("Stale event escrow " - "at dig = {}.".format(esaider.qb64b)) + msg = f"Kevery: unver trans escrow unescrow error: Stale event escrow at dig = {esaider.qb64}" + logger.info(msg) + raise ValidationError(msg) # get dig of the receipted event using pre and sn lastEvt raw = self.db.getKeLast(snKey(pre, sn)) if raw is None: # no event so keep in escrow - logger.info("Kevery unescrow error: Missing receipted " - "event at pre=%s sn=%x\n", pre, sn) - - raise UnverifiedTransferableReceiptError("Missing receipted evt at pre={} " - " sn={:x}".format(pre, sn)) + msg = f"Kevery: unver trans escrow unescrow error: Missing receipted event at pre={pre} sn={sn:x}" + logger.trace(msg) + raise UnverifiedTransferableReceiptError(msg) dig = bytes(raw) # get receipted event using pre and edig raw = self.db.getEvt(dgKey(pre, dig)) if raw is None: # receipted event superseded so remove from escrow - logger.info("Kevery unescrow error: Invalid receipted " - "event referenace at pre=%s sn=%x\n", pre, sn) - - raise ValidationError("Invalid receipted evt reference " - "at pre={} sn={:x}".format(pre, sn)) + msg = (f"Kevery: unver trans escrow unescrow error: Invalid receipted " + f"event reference at pre={pre} sn={sn:x}") + logger.trace(msg) + raise ValidationError(msg) serder = serdering.SerderKERI(raw=bytes(raw)) # receipted event # compare digs if esaider.qb64b != serder.saidb: - logger.info("Kevery unescrow error: Bad receipt dig." - "pre=%s sn=%x receipter=%s\n", (pre, sn, sprefixer.qb64)) - - raise ValidationError("Bad escrowed receipt dig at " - "pre={} sn={:x} receipter={}." - "".format(pre, sn, sprefixer.qb64)) + msg = (f"Kevery: unver trans escrow unescrow error: Bad escrowed receipt dig at " + f"pre={pre} sn={sn:x} receipter={sprefixer.qb64}") + logger.trace(msg) + raise ValidationError(msg) # get receipter's last est event # retrieve dig of last event at sn of receipter. @@ -5563,11 +5523,9 @@ def processEscrowUnverTrans(self): sn=sseqner.sn)) if sdig is None: # no event so keep in escrow - logger.info("Kevery unescrow error: Missing receipted " - "event at pre=%s sn=%x\n", pre, sn) - - raise UnverifiedTransferableReceiptError("Missing receipted evt at pre={} " - " sn={:x}".format(pre, sn)) + msg = f"Kevery: unver trans escrow unescrow error: Missing receipted evt at pre={pre} sn={sn:x}" + logger.trace(msg) + raise UnverifiedTransferableReceiptError(msg) # retrieve last event itself of receipter sraw = self.db.getEvt(key=dgKey(pre=sprefixer.qb64b, dig=bytes(sdig))) @@ -5594,12 +5552,10 @@ def processEscrowUnverTrans(self): siger.verfer = verfers[siger.index] # assign verfer if not siger.verfer.verify(siger.raw, serder.raw): # verify sig - logger.info("Kevery unescrow error: Bad trans receipt sig." - "pre=%s sn=%x receipter=%s\n", pre, sn, sprefixer.qb64) - - raise ValidationError("Bad escrowed trans receipt sig at " - "pre={} sn={:x} receipter={}." - "".format(pre, sn, sprefixer.qb64)) + msg = (f"Kevery: unver trans escrow unescrow error: bad escrowed trans receipt sig at " + f"pre={pre} sn={sn:x} receipter={sprefixer.qb64}.") + logger.trace(msg) + raise ValidationError(msg) # good sig so write receipt quadruple to database quadruple = sealet + siger.qb64b @@ -5609,25 +5565,24 @@ def processEscrowUnverTrans(self): except UnverifiedTransferableReceiptError as ex: # still waiting on missing prior event to validate # only happens if we process above - if logger.isEnabledFor(logging.DEBUG): # adds exception data - logger.exception("Kevery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): # adds exception data + logger.trace("Kevery: unver trans escrow unescrow failed: %s\n", ex.args[0]) + logger.exception("Kevery: unver trans escrow unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than out of order so remove from OO escrow self.db.delVre(snKey(pre, sn), equinlet) # removes one escrow at key val - if logger.isEnabledFor(logging.DEBUG): # adds exception data - logger.exception("Kevery unescrowed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrowed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): # adds exception data + logger.trace("Kevery: unver trans escrow other error on unescrow: %s\n", ex.args[0]) + logger.exception("Kevery: unver trans escrow other error on unescrow: %s\n", ex.args[0]) else: # unescrow succeeded, remove from escrow # We don't remove all escrows at pre,sn because some might be # duplicitous so we process remaining escrows in spite of found # valid event escrow. self.db.delVre(snKey(pre, sn), equinlet) # removes one escrow at key val - logger.info("Kevery unescrow succeeded for event = %s\n", serder.ked) + logger.info("Kevery: unver trans escrow unescrow succeeded for event = %s", serder.said) + logger.debug("Event Body= \n%s\n", serder.pretty()) if ekey == key: # still same so no escrows found on last while iteration break @@ -5678,32 +5633,26 @@ def processEscrowDuplicitous(self): dtb = self.db.getDts(dgKey(pre, bytes(edig))) if dtb is None: # othewise is a datetime as bytes # no date time so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event datetime" - " at dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed event datetime " - "at dig = {}.".format(bytes(edig))) + msg = f"Kevery: duplicity escrow unescrow error: Missing event datetime at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # do date math here and discard if stale nowIso8601() bytes dtnow = helping.nowUTC() dte = helping.fromIso8601(bytes(dtb)) if (dtnow - dte) > datetime.timedelta(seconds=self.TimeoutLDE): # escrow stale so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Stale event escrow " - " at dig = %s\n", bytes(edig)) - - raise ValidationError("Stale event escrow " - "at dig = {}.".format(bytes(edig))) + msg = f"Kevery: duplicity escrow unescrow error: Stale event escrow at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) # get the escrowed event using edig eraw = self.db.getEvt(dgKey(pre, bytes(edig))) if eraw is None: # no event so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event at." - "dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed evt at dig = {}." - "".format(bytes(edig))) + msg = f"Kevery: duplicity escrow unescrow error: Missing event at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) eserder = serdering.SerderKERI(raw=bytes(eraw)) # escrowed event @@ -5711,11 +5660,9 @@ def processEscrowDuplicitous(self): sigs = self.db.getSigs(dgKey(pre, bytes(edig))) if not sigs: # otherwise its a list of sigs # no sigs so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Missing event sigs at." - "dig = %s\n", bytes(edig)) - - raise ValidationError("Missing escrowed evt sigs at " - "dig = {}.".format(bytes(edig))) + msg = f"Kevery: duplicity escrow unescrow error: Missing event sigs at dig = {bytes(edig)}" + logger.trace(msg) + raise ValidationError(msg) sigers = [Siger(qb64b=bytes(sig)) for sig in sigs] self.processEvent(serder=eserder, sigers=sigers) @@ -5736,26 +5683,25 @@ def processEscrowDuplicitous(self): except LikelyDuplicitousError as ex: # still can't determine if duplicitous - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("Kevery: duplicity escrow unescrow failed: %s\n", ex.args[0]) + logger.exception("Kevery: duplicity escrow unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than likely duplicitous so remove from escrow self.db.delLde(snKey(pre, sn), edig) # removes one escrow at key val - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrowed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrowed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("Kevery: duplicity escrow other error on unescrow: %s\n", ex.args[0]) + logger.exception("Kevery: duplicity escrow other error on unescrow: %s\n", ex.args[0]) else: # unescrow succeeded, remove from escrow # We don't remove all escrows at pre,sn because some might be # duplicitous so we process remaining escrows in spite of found # valid event escrow. self.db.delLde(snKey(pre, sn), edig) # removes one escrow at key val - logger.info("Kevery unescrow succeeded in valid event: " - "event=\n%s\n", json.dumps(eserder.ked, indent=1)) + logger.info("Kevery: duplicity escrow unescrow succeeded in valid event: " + "key = %s \tdigest = %s", bytes(ekey).decode(), bytes(edig).decode()) + logger.debug("Event Body=\n%s\n", eserder.pretty()) if ekey == key: # still same so no escrows found on last while iteration break diff --git a/src/keri/core/parsing.py b/src/keri/core/parsing.py index e3f18bb9f..145bbec46 100644 --- a/src/keri/core/parsing.py +++ b/src/keri/core/parsing.py @@ -6,7 +6,6 @@ """ import logging -import traceback from collections import namedtuple from dataclasses import dataclass, astuple @@ -1020,8 +1019,8 @@ def msgParsator(self, ims=None, framed=True, pipeline=False, # when present assumes this is source seal of delegating event in delegator's KEL delseqner, delsaider = sscs[-1] if sscs else (None, None) # use last one if more than one if not sigers: - raise kering.ValidationError("Missing attached signature(s) for evt " - "= {}.".format(serder.ked)) + logger.debug("Parser: Missing attached signature(s) for evt = \n%s\n", serder.ked) + raise kering.ValidationError(f"Missing attached signature(s) for evt={serder.ked['d']}") try: kvy.processEvent(serder=serder, sigers=sigers, @@ -1037,13 +1036,13 @@ def msgParsator(self, ims=None, framed=True, pipeline=False, kvy.processReceiptQuadruples(serder, trqs, firner=firner) except AttributeError as ex: - raise kering.ValidationError("No kevery to process so dropped msg" - "= {}.".format(serder.pretty())) from ex + logger.debug("Parser: No kevery to process so dropped msg = %s", serder.pretty()) + raise kering.ValidationError(f"No kevery to process so dropped msg={serder.ked['d']}") from ex elif ilk in [Ilks.rct]: # event receipt msg (nontransferable) if not (cigars or wigers or tsgs): - raise kering.ValidationError("Missing attached signatures on receipt" - "msg = {}.".format(serder.ked)) + logger.debug("Parser: Missing attached signatures on receipt msg event =\n%s\n", serder.pretty()) + raise kering.ValidationError(f"Missing attached sigs on receipt msg={serder.ked['d']}") try: if cigars: @@ -1056,13 +1055,17 @@ def msgParsator(self, ims=None, framed=True, pipeline=False, kvy.processReceiptTrans(serder=serder, tsgs=tsgs) except AttributeError: - raise kering.ValidationError("No kevery to process so dropped msg" - "= {}.".format(serder.pretty())) + msg = f"No kevery to process so dropped msg = {serder.said}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) elif ilk in (Ilks.rpy,): # reply message if not (cigars or tsgs): - raise kering.ValidationError("Missing attached endorser signature(s) " - "to reply msg = {}.".format(serder.pretty())) + msg = f"Missing attached endorser signature(s) to reply msg = {serder.said}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) try: if cigars: # process separately so do not clash on errors @@ -1072,8 +1075,16 @@ def msgParsator(self, ims=None, framed=True, pipeline=False, rvy.processReply(serder, tsgs=tsgs) # trans except AttributeError as e: - raise kering.ValidationError("No kevery to process so dropped msg" - "= {}.".format(serder.pretty())) + msg = f"No kevery to process so dropped msg = {serder.said}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) + except kering.UnverifiedReplyError as e: + if logger.isEnabledFor(logging.DEBUG): + logger.exception("Error processing reply = %s", e) + logger.debug("Reply Body=\n%s\n", serder.pretty()) + else: + logger.error("Error processing reply = %s", e) elif ilk in (Ilks.qry,): # query message args = dict(serder=serder) @@ -1086,27 +1097,41 @@ def msgParsator(self, ims=None, framed=True, pipeline=False, args["cigars"] = cigars else: - raise kering.ValidationError("Missing attached requester signature(s) " - "to key log query msg = {}.".format(serder.pretty())) + msg = f"Missing attached requester signature(s) to key log query msg = {serder.said}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) route = serder.ked["r"] if route in ["logs", "ksn", "mbx"]: try: kvy.processQuery(**args) except AttributeError: - raise kering.ValidationError("No kevery to process so dropped msg" - "= {}.".format(serder.pretty())) + msg = f"No kevery to process so dropped msg = {serder.said}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) + except kering.QueryNotFoundError as e: # catch escrow error and log it + if logger.isEnabledFor(logging.TRACE): + logger.exception("Error processing query = %s", e) + logger.trace("Query Body=\n%s\n", serder.pretty()) + else: + logger.error("Error processing query = %s", e) elif route in ["tels", "tsn"]: try: tvy.processQuery(**args) except AttributeError as e: - raise kering.ValidationError("No tevery to process so dropped msg" - "= {} from {}.".format(serder.pretty(), e)) + msg = f"No tevery to process so dropped msg = {serder.said} from {e}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) else: - raise kering.ValidationError("Invalid resource type {} so dropped msg" - "= {}.".format(route, serder.pretty())) + msg = f"Invalid resource type {route} so dropped msg = {serder.said}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) elif ilk in (Ilks.exn,): args = dict(serder=serder) @@ -1121,8 +1146,10 @@ def msgParsator(self, ims=None, framed=True, pipeline=False, exc.processEvent(tsgs=tsgs, **args) except AttributeError: - raise kering.ValidationError("No Exchange to process so dropped msg" - "= {}.".format(serder.pretty())) + msg = "No Exchange to process so dropped msg = {serder.said}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) elif ilk in (Ilks.vcp, Ilks.vrt, Ilks.iss, Ilks.rev, Ilks.bis, Ilks.brv): # TEL msg @@ -1132,11 +1159,15 @@ def msgParsator(self, ims=None, framed=True, pipeline=False, tvy.processEvent(serder=serder, seqner=seqner, saider=saider, wigers=wigers) except AttributeError as e: - raise kering.ValidationError("No tevery to process so dropped msg" - "= {}.".format(serder.pretty())) + msg = f"No Tevery to process so dropped msg = {serder.said}" + logger.debug(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) else: - raise kering.ValidationError("Unexpected message ilk = {} for evt =" - " {}.".format(ilk, serder.pretty())) + msg = f"Unexpected message ilk = {ilk} for evt = {serder.said}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) elif isinstance(serder, serdering.SerderACDC): ilk = serder.ilk # dispatch based on ilk @@ -1146,14 +1177,20 @@ def msgParsator(self, ims=None, framed=True, pipeline=False, prefixer, seqner, saider = ssts[-1] if ssts else (None, None, None) # use last one if more than one vry.processCredential(creder=serder, prefixer=prefixer, seqner=seqner, saider=saider) except AttributeError as e: - raise kering.ValidationError("No verifier to process so dropped credential" - "= {}.".format(serder.pretty())) + msg = f"No verifier to process so dropped credential {serder.said}" + logger.debug(msg) + logger.debug("Credential body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) else: - raise kering.ValidationError("Unexpected message ilk = {} for evt =" - " {}.".format(ilk, serder.pretty())) + msg = f"Unexpected message ilk = {ilk} for evt = {serder.said}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) else: - raise kering.ValidationError("Unexpected protocol type = {} for event message =" - " {}.".format(serder.proto, serder.pretty())) + msg = f"Unexpected protocol type = {serder.proto} for event message = {serder.said}" + logger.info(msg) + logger.debug("Event body=\n%s\n", serder.pretty()) + raise kering.ValidationError(msg) return True # done state diff --git a/src/keri/core/routing.py b/src/keri/core/routing.py index c67092b65..686929684 100644 --- a/src/keri/core/routing.py +++ b/src/keri/core/routing.py @@ -183,7 +183,7 @@ def processReply(self, serder, cigars=None, tsgs=None): Escrow process logic is route dependent and is dispatched by route, i.e. route is address of buffer with route specific handler of escrow. """ - for k in eventing.RPY_LABELS: + for k in kering.RPY_LABELS: if k not in serder.ked: raise kering.ValidationError(f"Missing element={k} from {coring.Ilks.rpy}" f" msg={serder.ked}.") @@ -265,26 +265,30 @@ def acceptReply(self, serder, saider, route, aid, osaider=None, if not self.lax and cigar.verfer.qb64 in self.prefixes: # own cig if not self.local: # own cig when not local so ignore - logger.info("Kevery process: skipped own attachment" - " on nonlocal reply msg=\n%s\n", serder.pretty()) + logger.info("Kevery: skipped own attachment for AID %s" + " on non-local reply at route = %s", aid, serder.ked['r']) + logger.debug("Reply Body=\n%s\n", serder.pretty()) continue # skip own cig attachment on non-local reply msg if aid != cigar.verfer.qb64: # cig not by aid - logger.info("Kevery process: skipped cig not from aid=" - "%s on reply msg=\n%s\n", aid, serder.pretty()) + logger.info("Kevery: skipped cig not from aid=" + "%s on reply at route %s", aid, serder.ked['r']) + logger.debug("Reply Body=\n%s\n", serder.pretty()) continue # skip invalid cig's verfer is not aid if odater: # get old compare datetimes to see if later if dater.datetime <= odater.datetime: - logger.info("Kevery process: skipped stale update from " - "%s of reply msg=\n%s\n", aid, serder.pretty()) + logger.trace("Kevery: skipped stale update from " + "%s of reply at route= %s", aid, serder.ked['r']) + logger.trace("Reply Body=\n%s\n", serder.pretty()) continue # skip if not later # raise ValidationError(f"Stale update of {route} from {aid} " # f"via {Ilks.rpy}={serder.ked}.") if not cigar.verfer.verify(cigar.raw, serder.raw): # cig not verify - logger.info("Kevery process: skipped nonverifying cig from " - "%s on reply msg=\n%s\n", cigar.verfer.qb64, serder.pretty()) + logger.info("Kevery: skipped non-verifying cig from " + "%s on reply at route = %s", cigar.verfer.qb64, serder.ked['r']) + logger.debug("Reply Body=\n%s\n", serder.pretty()) continue # skip if cig not verify # All constraints satisfied so update @@ -319,9 +323,10 @@ def acceptReply(self, serder, saider, route, aid, osaider=None, if seqner.sn == osqr.sn: # sn same so check datetime if odater: if dater.datetime <= odater.datetime: - logger.info("Kevery process: skipped stale key" - "state sig datetime from %s on reply msg=\n%s\n", - aid, serder.pretty()) + logger.debug("Kevery process: skipped stale key " + "state sig datetime from %s on reply msg = %s", + aid, serder.said) + logger.debug("Reply Body=\n%s\n", serder.pretty()) continue # skip if not later # retrieve sdig of last event at sn of signer. @@ -482,10 +487,9 @@ def processEscrowReply(self): except kering.UnverifiedReplyError as ex: # still waiting on missing prior event to validate - if logger.isEnabledFor(logging.DEBUG): + if logger.isEnabledFor(logging.TRACE): + logger.trace("Kevery unescrow attempt failed: %s\n", ex.args[0]) logger.exception("Kevery unescrow attempt failed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrow attempt failed: %s\n", ex.args[0]) except Exception as ex: # other error so remove from reply escrow self.db.rpes.rem(keys=(route, ), val=saider) # remove escrow only @@ -497,8 +501,8 @@ def processEscrowReply(self): else: # unescrow succeded self.db.rpes.rem(keys=(route, ), val=saider) # remove escrow only - logger.info("Kevery unescrow succeeded for reply=\n%s\n", - serder.pretty()) + logger.info("Kevery unescrow succeeded for reply = %s", serder.said) + logger.debug("Reply Body=\n%s\n", serder.pretty()) except Exception as ex: # log diagnostics errors etc self.db.rpes.rem(keys=(route,), val=saider) # remove escrow only diff --git a/src/keri/core/serdering.py b/src/keri/core/serdering.py index a6a464e0f..92db8221a 100644 --- a/src/keri/core/serdering.py +++ b/src/keri/core/serdering.py @@ -248,7 +248,7 @@ class Serder: Ilks.bar: Fieldage(saids={Saids.d: DigDex.Blake3_256}, alls=dict(v='', t='',d='', dt='', r='',a=[])), Ilks.exn: Fieldage(saids={Saids.d: DigDex.Blake3_256}, - alls=dict(v='', t='', d='', i="", p="", dt='', r='',q={}, + alls=dict(v='', t='', d='', i="", rp='', p="", dt='', r='',q={}, a=[], e={})), Ilks.vcp: Fieldage(saids={Saids.d: DigDex.Blake3_256, Saids.i: DigDex.Blake3_256,}, @@ -304,7 +304,7 @@ class Serder: Ilks.bar: Fieldage(saids={Saids.d: DigDex.Blake3_256}, alls=dict(v='', t='',d='', i='', dt='', r='',a=[])), Ilks.exn: Fieldage(saids={Saids.d: DigDex.Blake3_256}, - alls=dict(v='', t='', d='', i="", p="", dt='', r='', q={}, + alls=dict(v='', t='', d='', i="", rp="", p="", dt='', r='', q={}, a=[], e={})), }, }, @@ -523,9 +523,19 @@ def _verify(self): del keys[keys.index(key)] # remove non required fields if list(alls.keys()) != keys: # forces ordering of labels in .sad - raise MissingFieldError(f"Missing one or more required fields from" - f"= {list(alls.keys())} in sad = " - f"{self._sad}.") + # special handling for 1.1.18 -> 1.1.32 + skip_missing_field = False + if self.ilk == Ilks.exn: + missing_keys = [key for key in alls.keys() if key not in keys] + if missing_keys: + if len(missing_keys) == 1 and 'rp' in missing_keys: + skip_missing_field = True + if skip_missing_field: + pass + else: + raise MissingFieldError(f"Missing one or more required fields from" + f"= {list(alls.keys())} in sad = " + f"{self._sad}.") # said field labels are not order dependent with respect to all fields # in sad so use set() to test inclusion @@ -1105,8 +1115,19 @@ def _verify(self, **kwa): allkeys = list(self.Fields[self.proto][self.vrsn][self.ilk].alls.keys()) keys = list(self.sad.keys()) if allkeys != keys: - raise ValidationError(f"Invalid top level field list. Expected " - f"{allkeys} got {keys}.") + # special handling for 1.1.18 -> 1.1.32 + skip_missing_field = False + if self.ilk == Ilks.exn: + missing_keys = [key for key in allkeys if key not in keys] + if missing_keys: + if len(missing_keys) == 1 and 'rp' in missing_keys: + skip_missing_field = True + + if skip_missing_field: + pass + else: + raise ValidationError(f"Invalid top level field list. Expected " + f"{allkeys} got {keys}.") if (self.vrsn.major < 2 and self.vrsn.minor < 1 and self.ilk in (Ilks.qry, Ilks.rpy, Ilks.pro, Ilks.bar, Ilks.exn)): diff --git a/src/keri/db/basing.py b/src/keri/db/basing.py index d1c83563f..b4f0f095d 100644 --- a/src/keri/db/basing.py +++ b/src/keri/db/basing.py @@ -18,7 +18,7 @@ So only need to set dupsort first time opened each other opening does not need to call it """ - +import importlib import os import shutil from collections import namedtuple @@ -30,11 +30,14 @@ import cbor2 as cbor import msgpack import lmdb +import semver from ordered_set import OrderedSet as oset from hio.base import doing +import keri from . import dbing, koming, subing +from .dbing import splitKey, dgKey from .. import kering from ..core import coring, eventing, parsing, serdering @@ -45,6 +48,11 @@ logger = help.ogler.getLogger() +MIGRATIONS = [ + ("0.6.8", ["hab_data_rename"]), + ("1.0.0", ["add_key_and_reg_state_schemas"]) +] + class dbdict(dict): """ @@ -836,6 +844,10 @@ def reopen(self, **kwa): # events as ordered by first seen ordinals self.fons = subing.CesrSuber(db=self, subkey='fons.', klas=coring.Seqner) + + self.migs = subing.CesrSuber(db=self, subkey="migs.", klas=coring.Dater) + self.vers = subing.Suber(db=self, subkey="vers.") + # Kever state made of KeyStateRecord key states self.states = koming.Komer(db=self, schema=KeyStateRecord, @@ -893,7 +905,7 @@ def reopen(self, **kwa): self.ends = koming.Komer(db=self, subkey='ends.', schema=EndpointRecord, ) - # service endpont locations keyed by eid.scheme (endpoint identifier) + # service endpoint locations keyed by eid.scheme (endpoint identifier) # data extracted from reply loc self.locs = koming.Komer(db=self, subkey='locs.', @@ -1025,12 +1037,14 @@ def reopen(self, **kwa): # Global settings for the Habery environment self.hbys = subing.Suber(db=self, subkey='hbys.') + # Signed contact data, keys by prefix self.cons = subing.Suber(db=self, subkey="cons.") # Transferable signatures on contact data self.ccigs = subing.CesrSuber(db=self, subkey='ccigs.', klas=coring.Cigar) + # Chunked image data for contact information for remote identifiers self.imgs = self.env.open_db(key=b'imgs.') @@ -1068,6 +1082,10 @@ def reload(self): Reload stored prefixes and Kevers from .habs """ + # Check migrations to see if this database is up to date. Error otherwise + if not self.current: + raise kering.DatabaseError(f"Database migrations must be run. DB version {self.version}; current {keri.__version__}") + removes = [] for keys, data in self.habs.getItemIter(): if (ksr := self.states.get(keys=data.hid)) is not None: @@ -1107,6 +1125,174 @@ def reload(self): for keys in removes: # remove bare .habs records self.nmsp.rem(keys=keys) + def migrate(self): + """ Run all migrations required + + Run all migrations that are required from the current version of database up to the current version + of the software that have not already been run. + + Sets the version of the database to the current version of the software after successful completion + of required migrations + + """ + for (version, migrations) in MIGRATIONS: + # Only run migration if current source code version is at or below the migration version + ver = semver.VersionInfo.parse(keri.__version__) + ver_no_prerelease = semver.Version(ver.major, ver.minor, ver.patch) + if self.version is not None and semver.compare(version, str(ver_no_prerelease)) > 0: + print(f"Skipping migration {version} as higher than the current KERI version {keri.__version__}") + continue + # Check to see if migration version is for an older database version + if self.version is not None and semver.compare(version, self.version) != 1: + continue + print(f"Migrating database v{self.version} --> v{version} ...") + + for migration in migrations: + modName = f"keri.db.migrations.{migration}" + if self.migs.get(keys=(migration,)) is not None: + continue + + mod = importlib.import_module(modName) + try: + mod.migrate(self) + except Exception as e: + print(f"\nAbandoning migration {migration} at version {version} with error: {e}") + return + + self.migs.pin(keys=(migration,), val=coring.Dater()) + + # update database version after successful migration + self.version = version + + self.version = keri.__version__ + + def clearEscrows(self): + """ + Clear all escrows + """ + count = 0 + for (k, _) in self.getUreItemIter(): + count += 1 + self.delUres(key=k) + logger.info(f"KEL: Cleared {count} unverified receipt escrows") + + count = 0 + for (k, _) in self.getVreItemIter(): + count += 1 + self.delVres(key=k) + logger.info(f"KEL: Cleared {count} verified receipt escrows") + + count = 0 + for (k, _) in self.getPseItemsNextIter(): + count += 1 + self.delPses(key=k) + logger.info(f"KEL: Cleared {count} partially signed escrows") + + count = 0 + for (k, _) in self.getPweItemIter(): + count += 1 + self.delPwes(key=k) + logger.info(f"KEL: Cleared {count} partially witnessed escrows") + + count = 0 + for (k, _) in self.getUweItemIter(): + count += 1 + self.delUwes(key=k) + logger.info(f"KEL: Cleared {count} unverified event indexed escrowed couples") + + count = 0 + for (k, _) in self.getOoeItemIter(): + count += 1 + self.delOoes(key=k) + logger.info(f"KEL: Cleared {count} out of order escrows") + + count = 0 + for (k, _) in self.getLdeItemIter(): + count += 1 + self.delLdes(key=k) + logger.info(f"KEL: Cleared {count} likely duplicitous escrows") + + count = 0 + for k, _ in self.getQnfItemsNextIter(): + self.delQnfs(key=k) + logger.info(f"KEL: Cleared {count} query not found escrows") + + count = 0 + for (key, _) in self.getPdeItemsNextIter(): + count += 1 + self.delPde(key=key) + logger.info(f"KEL: Cleared {count} partially delegated key event escrows") + + for name, escrow, desc in [ + ('rpes', self.rpes, 'reply escrows'), + ('eoobi', self.eoobi, 'failed, retryable OOBI escrow'), + ('gpwe', self.gpwe, 'group partial witness escrow'), + ('gdee', self.gdee, 'group delegate escrow'), + ('dpwe', self.dpwe, 'delegated partial witness escrow'), + ('gpse', self.gpse, 'group partial signature escrow'), + ('epse', self.epse, 'exchange partial signature escrow'), + ('dune', self.dune, 'delegated unanchored escrow')]: + count = escrow.cntAll() + escrow.trim() + logger.info(f"KEL: Cleared {count} escrows from ({name.ljust(5)}): {desc}") + + logger.info("Cleared KEL escrows") + + @property + def current(self): + """ Current property determines if we are at the current database migration state. + + If the database version matches the library version return True + If the current database version is behind the current library version, check for migrations + - If there are migrations to run, return False + - If there are no migrations to run, reset database version to library version and return True + If the current database version is ahead of the current library version, raise exception + + """ + if self.version == keri.__version__: + return True + + # If database version is ahead of library version, throw exception + ver = semver.VersionInfo.parse(keri.__version__) + ver_no_prerelease = semver.Version(ver.major, ver.minor, ver.patch) + if self.version is not None and semver.compare(self.version, str(ver_no_prerelease)) == 1: + raise kering.ConfigurationError( + f"Database version={self.version} is ahead of library version={keri.__version__}") + + last = MIGRATIONS[-1] + # If we aren't at latest version, but there are no outstanding migrations, + # reset version to latest (rightmost (-1) migration is latest) + if self.migs.get(keys=(last[1][-1],)) is not None: + return True + + # We have migrations to run + return False + + def complete(self, name=None): + """ Returns list of tuples of migrations completed with date of completion + + Parameters: + name(str): optional name of migration to check completeness + + Returns: + list: tuples of migration,date of completed migration names and the date of completion + + """ + migrations = [] + if not name: + for version, migs in MIGRATIONS: + # Only get migration completion dates for migrations that have been run + if self.version is not None and semver.compare(version, self.version) <= 0: + for mig in migs: + dater = self.migs.get(keys=(mig,)) + migrations.append((mig, dater)) + else: + for version, migs in MIGRATIONS: # check all migrations for each version + if name not in migs or not self.migs.get(keys=(name,)): + raise ValueError(f"No migration named {name}") + migrations.append((name, self.migs.get(keys=(name,)))) + + return migrations def clean(self): """ @@ -1269,6 +1455,9 @@ def cloneEvtMsg(self, pre, fn, dig): atc.extend(coring.Counter(code=coring.CtrDex.SealSourceCouples, count=1).qb64b) atc.extend(couple) + elif self.kevers[pre].delegated: + if serdering.SerderKERI(raw=bytes(raw)).estive: + raise kering.MissingEntryError("Missing delegator anchor seal for dig={}.".format(dig)) # add trans receipts quadruples to attachments if quads := self.getVrcs(key=dgkey): @@ -2033,6 +2222,20 @@ def getUreLast(self, key): """ return self.getIoValLast(self.ures, key) + def getUreItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of partial signed escrowed event triple items at next + key after key. + Items is (key, val) where proem has already been stripped from val + val is triple dig+pre+cig + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.ures, key) + def getUreItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -2200,6 +2403,20 @@ def getVreLast(self, key): """ return self.getIoValLast(self.vres, key) + def getVreItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of partial signed escrowed event quintuple items at next + key after key. + Items is (key, val) where proem has already been stripped from val + val is Quinlet is edig + spre + ssnu + sdig +sig + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.vres, key) + def getVreItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -2509,6 +2726,24 @@ def getPde(self, key): """ return self.getVal(self.pdes, key) + def getPdes(self, key): + """ + Use dgKey() + Return list of out of order escrow event dig vals at key + Returns empty list if no entry at key + Duplicates are retrieved in insertion order. + """ + return self.getIoVals(self.pdes, key) + + def getPdeItemsNextIter(self, key=b'', skip=True): + """ + Use dgKey() + Return list of witnessed signed escrowed event dig vals at key + Returns empty list if no entry at key + Duplicates are retrieved in insertion order. + """ + return self.getIoItemsNextIter(self.pdes, key, skip) + def delPde(self, key): """ Use dgKey() @@ -2564,6 +2799,18 @@ def getPweLast(self, key): """ return self.getIoValLast(self.pwes, key) + def getPweItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of partial witnessed escrowed event dig items at next key after key. + Items is (key, val) where proem has already been stripped from val + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.pwes, key) + def getPweItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -2668,6 +2915,20 @@ def getUweLast(self, key): """ return self.getIoValLast(self.uwes, key) + def getUweItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of partial signed escrowed receipt couple items at next + key after key. + Items is (key, val) where proem has already been stripped from val + val is couple edig+wig + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.uwes, key) + def getUweItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -2762,6 +3023,18 @@ def getOoeLast(self, key): """ return self.getIoValLast(self.ooes, key) + def getOoeItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of out of order escrowed event dig items at next key after key. + Items is (key, val) where proem has already been stripped from val + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.ooes, key) + def getOoeItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -3018,6 +3291,18 @@ def getLdeLast(self, key): """ return self.getIoValLast(self.ldes, key) + def getLdeItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of likely duplicitous escrowed event dig items at next key after key. + Items is (key, val) where proem has already been stripped from val + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.ldes, key) + def getLdeItemsNext(self, key=b'', skip=True): """ Use snKey() diff --git a/src/keri/db/dbing.py b/src/keri/db/dbing.py index 62a2b40f6..3b5b967b8 100644 --- a/src/keri/db/dbing.py +++ b/src/keri/db/dbing.py @@ -59,11 +59,9 @@ from hio.base import filing -from .. import help +import keri from ..help import helping -logger = help.ogler.getLogger() - ProemSize = 32 # does not include trailing separator MaxProem = int("f"*(ProemSize), 16) MaxON = int("f"*32, 16) # largest possible ordinal number, sequence or first seen @@ -71,8 +69,6 @@ SuffixSize = 32 # does not include trailing separator MaxSuffix = int("f"*(SuffixSize), 16) -KERIDBMapSizeKey = "KERI_DB_MAP_SIZE" - def dgKey(pre, dig): """ Returns bytes DB key from concatenation of '.' with qualified Base64 prefix @@ -86,7 +82,7 @@ def dgKey(pre, dig): return (b'%s.%s' % (pre, dig)) -def onKey(pre, sn): +def onKey(pre, sn, *, sep=b'.'): """ Returns bytes DB key from concatenation with '.' of qualified Base64 prefix bytes pre and int ordinal number of event, such as sequence number or first @@ -94,7 +90,7 @@ def onKey(pre, sn): """ if hasattr(pre, "encode"): pre = pre.encode("utf-8") # convert str to bytes - return (b'%s.%032x' % (pre, sn)) + return (b'%s%s%032x' % (pre, sep, sn)) snKey = onKey # alias so intent is clear, sn vs fn fnKey = onKey # alias so intent is clear, sn vs fn @@ -141,7 +137,7 @@ def splitKey(key, sep=b'.'): return tuple(splits) -def splitKeyON(key): +def splitKeyON(key, *, sep=b'.'): """ Returns list of pre and int on from key Accepts either bytes or str key @@ -149,9 +145,12 @@ def splitKeyON(key): """ if isinstance(key, memoryview): key = bytes(key) - pre, on = splitKey(key) + top, on = splitKey(key, sep=sep) on = int(on, 16) - return (pre, on) + return (top, on) + +splitSnKey = splitKeyON # alias so intent is clear, sn vs fn; backport of 1.2.x alias +splitFnKey = splitKeyON # alias so intent is clear, sn vs fn; backport of 1.2.x alias splitKeySN = splitKeyON # alias so intent is clear, sn vs fn splitKeyFN = splitKeyON # alias so intent is clear, sn vs fn @@ -351,6 +350,7 @@ def __init__(self, readonly=False, **kwa): """ self.env = None + self._version = None self.readonly = True if readonly else False super(LMDBer, self).__init__(**kwa) @@ -380,25 +380,59 @@ def reopen(self, readonly=False, **kwa): readonly (bool): True means open database in readonly mode False means open database in read/write mode """ + exists = self.exists(name=self.name, base=self.base) opened = super(LMDBer, self).reopen(**kwa) if readonly is not None: self.readonly = readonly # open lmdb major database instance # creates files data.mdb and lock.mdb in .dbDirPath - if (mapSize := os.getenv(KERIDBMapSizeKey)) is not None: - try: - self.MapSize = int(mapSize) - except ValueError: - logger.error("KERI_DB_MAP_SIZE must be an integer value >1!") - raise - - self.env = lmdb.open(self.path, max_dbs=self.MaxNamedDBs, map_size=self.MapSize, + map_size = os.getenv("KERI_LMDB_MAP_SIZE", '4294967296') # 4GB + try: + map_size = int(map_size) + except ValueError: + map_size = 4 * 1024**3 # 4GB + self.env = lmdb.open(self.path, max_dbs=self.MaxNamedDBs, map_size=map_size, mode=self.perm, readonly=self.readonly) + self.opened = True if opened and self.env else False + + if self.opened and not self.readonly and (not exists or self.temp): + self.version = keri.__version__ + return self.opened + + @property + def version(self): + """ Return the version of database stored in __version__ key. + + This value is read through cached in memory + + Returns: + str: the version of the database or None if not set in the database + + """ + if self._version is None: + self._version = self.getVer() + + return self._version + + @version.setter + def version(self, val): + """ Set the version of the database in memory and in the __version__ key + + Parameters: + val (str): The new semver formatted version of the database + + """ + if hasattr(val, "decode"): + val = val.decode("utf-8") # convert bytes to str + + self._version = val + self.setVer(self._version) + def close(self, clear=False): """ Close lmdb at .env and if clear or .temp then remove lmdb directory at .path @@ -413,7 +447,33 @@ def close(self, clear=False): self.env = None - return(super(LMDBer, self).close(clear=clear)) + return (super(LMDBer, self).close(clear=clear)) + + def getVer(self): + """ Returns the value of the the semver formatted version in the __version__ key in this database + + Returns: + str: semver formatted version of the database + + """ + with self.env.begin() as txn: + cursor = txn.cursor() + version = cursor.get(b'__version__') + return version.decode("utf-8") if version is not None else None + + def setVer(self, val): + """ Set the version of the database in the __version__ key + + Parameters: + val (str): The new semver formatted version of the database + + """ + if hasattr(val, "encode"): + val = val.encode("utf-8") # convert str to bytes + + with self.env.begin(write=True) as txn: + cursor = txn.cursor() + cursor.replace(b'__version__', val) # For subdbs with no duplicate values allowed at each key. (dupsort==False) @@ -606,6 +666,71 @@ def delTopVal(self, db, key=b''): ckey, cval = cursor.item() # cursor now at next item after deleted return result + # ported from OnIoDupSuber + def getOnIoDupItemIter(self, db, key=b'', on=0, *, sep=b'.'): + """ + Returns iterator of triples (key, on, val), at each key over all ordinal + numbered keys with same key + sep + on in db. Values are sorted by + onKey(key, on) where on is ordinal number int and key is prefix sans on. + Values duplicates are sorted internally by hidden prefixed insertion order + proem ordinal + Returned items are triples of (key, on, val) + when key is empty then retrieves whole db + + Raises StopIteration Error when empty. + + Returns: + items (Iterator[(key, on, val)]): triples of key, on, val + + Parameters: + db (subdb): named sub db in lmdb + key (bytes): key within sub db's keyspace plus trailing part on + when key is empty then retrieves whole db + on (int): ordinal number at which to initiate retrieval + sep (bytes): separator character for split + """ + for key, on, val in self.getOnItemIter(db=db, key=key, on=on, sep=sep): + val = val[33:] # strip proem + yield (key, on, val) + + # ported from OnSuberBase + def getOnItemIter(self, db, key=b'', on=0, *, sep=b'.'): + """ + Returns iterator of triples (key, on, val), at each key over all ordinal + numbered keys with same key + sep + on in db. Values are sorted by + onKey(key, on) where on is ordinal number int and key is prefix sans on. + Returned items are triples of (key, on, val) + When dupsort==true then duplicates are included in items since .iternext + includes duplicates. + when key is empty then retrieves whole db + + Raises StopIteration Error when empty. + + Returns: + items (Iterator[(key, on, val)]): triples of key, on, val with same + key but increments of on beginning with on + + Parameters: + db (subdb): named sub db in lmdb + key (bytes): key within sub db's keyspace plus trailing part on + when key is empty then retrieves whole db + on (int): ordinal number at which to initiate retrieval + sep (bytes): separator character for split + """ + with self.env.begin(db=db, write=False, buffers=True) as txn: + cursor = txn.cursor() + if key: # not empty + onkey = onKey(key, on, sep=sep) # start replay at this enty 0 is earliest + else: # empty + onkey = key + if not cursor.set_range(onkey): # moves to val at key >= onkey + return # no values end of db raises StopIteration + + for ckey, cval in cursor.iternext(): # get key, val at cursor + ckey, cn = splitKeyON(ckey, sep=sep) + if key and not ckey == key: + break + yield (ckey, cn, cval) # For subdbs with no duplicate values allowed at each key. (dupsort==False) # and use keys with ordinal as monotonically increasing number part @@ -1583,6 +1708,51 @@ def cntIoVals(self, db, key): " or wrong DUPFIXED size. ref) lmdb.BadValsizeError") return count + def getTopIoDupItemIter(self, db, top=b''): + """ + Iterates over top branch of db given by key of IoDup items where each value + has 33 byte insertion ordinal number proem (prefixed) with separator. + Automagically removes (strips) proem before returning items. + + Assumes DB opened with dupsort=True + + Returns: + items (abc.Iterator): iterator of (full key, val) tuples of all + dup items over a branch of the db given by top key where returned + full key is full database key for val not truncated top key. + Item is (key, val) with proem stripped from val stored in db. + If key = b'' then returns list of dup items for all keys in db. + + + Because cursor.iternext() advances cursor after returning item its safe + to delete the item within the iteration loop. curson.iternext() works + for both dupsort==False and dupsort==True + + Raises StopIteration Error when empty. + + Parameters: + db (lmdb._Database): instance of named sub db with dupsort==False + top (bytes): truncated top key, a key space prefix to get all the items + from multiple branches of the key space. If top key is + empty then gets all items in database + + Duplicates at a given key preserve insertion order of duplicate. + Because lmdb is lexocographic an insertion ordering proem is prepended to + all values that makes lexocographic order that same as insertion order. + + Duplicates are ordered as a pair of key plus value so prepending proem + to each value changes duplicate ordering. Proem is 33 characters long. + With 32 character hex string followed by '.' for essentiall unlimited + number of values which will be limited by memory. + + With prepended proem ordinal must explicity check for duplicate values + before insertion. Uses a python set for the duplicate inclusion test. + Set inclusion scales with O(1) whereas list inclusion scales with O(n). + """ + for top, val in self.getTopItemIter(db=db, key=top): + val = val[33:] # strip proem + yield (top, val) + def delIoVals(self, db, key): """ diff --git a/src/keri/db/escrowing.py b/src/keri/db/escrowing.py index 03ea429ef..b903373ea 100644 --- a/src/keri/db/escrowing.py +++ b/src/keri/db/escrowing.py @@ -1,4 +1,4 @@ -# -*- encoding: utf-8 -*- +1.1# -*- encoding: utf-8 -*- """ keri.core.escrowing module @@ -101,40 +101,39 @@ def processEscrowState(self, typ, processReply, extype: Type[Exception]): if ((helping.nowUTC() - dater.datetime) > datetime.timedelta(seconds=self.timeout)): # escrow stale so raise ValidationError which unescrows below - logger.info("Kevery unescrow error: Stale txn state escrow " - " at pre = %s\n", pre) - - raise kering.ValidationError(f"Stale txn state escrow at pre = {pre}.") + msg = f"{typ} escrow unescrow error: Stale txn state escrow at pre = {pre}" + logger.trace(msg) + raise kering.ValidationError(msg) processReply(serder=serder, saider=saider, route=serder.ked["r"], cigars=cigars, tsgs=tsgs, aid=aid) except extype as ex: # still waiting on missing prior event to validate - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrow attempt failed: %s\n", ex.args[0]) - else: - logger.error("Kevery unescrow attempt failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("%s escrow unescrow attempt failed: %s\n", typ, ex.args[0]) + logger.exception("%s escrow unescrow attempt failed: %s\n", typ, ex.args[0]) except Exception as ex: # other error so remove from reply escrow self.escrowdb.remIokey(iokeys=(typ, pre, aid, ion)) # remove escrow if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrowed due to error: %s\n", ex.args[0]) + logger.exception("%s escrow other error on unescrow: %s\n", typ, ex.args[0]) else: - logger.error("Kevery unescrowed due to error: %s\n", ex.args[0]) + logger.error("%s escrow other error on unescrow: %s\n", typ, ex.args[0]) else: # unescrow succeded self.escrowdb.remIokey(iokeys=(typ, pre, aid, ion)) # remove escrow only - logger.info("Kevery unescrow succeeded for txn state=\n%s\n", - serder.pretty()) + logger.info("%s escrow unescrow succeeded for txn state = %s", + typ, serder.said) + logger.debug("TXN State Body=\n%s\n", serder.pretty()) except Exception as ex: # log diagnostics errors etc self.escrowdb.remIokey(iokeys=(typ, pre, aid, ion)) # remove escrow self.removeState(saider) if logger.isEnabledFor(logging.DEBUG): - logger.exception("Kevery unescrowed due to error: %s\n", ex.args[0]) + logger.exception("%s escrow unescrowed due to error: %s\n", typ, ex.args[0]) else: - logger.error("Kevery unescrowed due to error: %s\n", ex.args[0]) + logger.error("%s escrow unescrowed due to error: %s\n", typ, ex.args[0]) def escrowStateNotice(self, *, typ, pre, aid, serder, saider, dater, cigars=None, tsgs=None): """ diff --git a/src/keri/db/migrations/__init__.py b/src/keri/db/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/keri/db/migrations/add_key_and_reg_state_schemas.py b/src/keri/db/migrations/add_key_and_reg_state_schemas.py new file mode 100644 index 000000000..c626bd2fb --- /dev/null +++ b/src/keri/db/migrations/add_key_and_reg_state_schemas.py @@ -0,0 +1,150 @@ +from keri import help +from keri.core import coring, serdering +from keri.db import koming, subing, dbing +from keri.db.basing import StateEERecord, KeyStateRecord +from keri.db.dbing import dgKey, splitKey +from keri.kering import ConfigurationError, Version +from keri.vdr import viring + +logger = help.ogler.getLogger() + +def _check_if_needed(db): + states = koming.Komer(db=db, + schema=dict, + subkey='stts.') + first = next(states.getItemIter(), None) + if first is None: + return False + keys, sad = first + if 'vn' in sad: + return False + return True + +def migrate(db): + """Adds schema for KeyStateRecord , RegStateRecord, and migrates the rgy.cancs., hby.db.pubs., + and hby.db.digs. to be up to date as of 2022-??-?? + + This migration performs the following: + - hby.db -> "stts." schema from dict -> KeyStateRecord + - rgy -> "stts." schema from dict -> RegStateRecord + - rgy -> "cancs." reset to (ACDC SAID, SN 0, TEL evt 0 digest) + - hby.db -> "pubs." and + hby.db -> "digs." + that don't exist are populated with verification keys and event digests for the first seen events and + Keys: + "pubs." Verfer of each Verfer for each FEL event + "digs." Diger of next Diger (ndiger) of each FEL event + Value: (prefix, sn) of each event + + Parameters: + db(Baser): Baser database object on which to run the migration + """ + # May be running on a database that is already in the right state yet has no migrations run + # so we need to check if the migration is needed + if not _check_if_needed(db): + print(f"{__name__} migration not needed, already ran") + return + + try: + logger.debug(f"Migrating keystate and regstate dict to schema for {db.path}") + states = koming.Komer(db=db, + schema=dict, + subkey='stts.') + nstates = koming.Komer(db=db, + schema=KeyStateRecord, + subkey='stts.') + + for keys, sad in states.getItemIter(): + ksr = KeyStateRecord( + vn=Version, # version number as list [major, minor] + i=sad['i'], # qb64 prefix + s=sad['s'], # lowercase hex string no leading zeros + p=sad['p'], + d=sad['d'], + f=sad['f'], # lowercase hex string no leading zeros + dt=sad['dt'], + et=sad['et'], + kt=sad['kt'], + k=sad['k'], + nt=sad['nt'], + n=sad['n'], + bt=sad['bt'], + b=sad['b'], + c=sad['c'], + ee=StateEERecord._fromdict(sad['ee']), # latest est event dict + di=sad['di'] if sad['di'] else None + ) + + nstates.pin(keys=keys, val=ksr) + + rgy = viring.Reger(name=db.name, base=db.base, db=db, temp=db.temp, reopen=True) + + rstates = koming.Komer(db=rgy, + schema=dict, + subkey='stts.') + + for _, sad in rstates.getItemIter(): + rsr = viring.RegStateRecord( + vn=list(Version), # version number as list [major, minor] + i=sad['i'], # qb64 registry SAID + s=sad['s'], # lowercase hex string no leading zeros + d=sad['d'], + ii=sad['ii'], + dt=sad['dt'], + et=sad['et'], + bt=sad['bt'], # hex string no leading zeros lowercase + b=sad['b'], # list of qb64 may be empty + c=sad['c'], + ) + rgy.states.pin(sad['i'], val=rsr) + + for (said,), _ in rgy.saved.getItemIter(): + snkey = dbing.snKey(said, 0) + dig = rgy.getTel(key=snkey) + + prefixer = coring.Prefixer(qb64=said) + seqner = coring.Seqner(sn=0) + saider = coring.Saider(qb64b=bytes(dig)) + rgy.cancs.pin(keys=said, val=[prefixer, seqner, saider]) + + migrateKeys(db) + + # clear escrows + logger.info("clearing escrows") + db.gpwe.trim() + db.gdee.trim() + db.dpwe.trim() + db.gpse.trim() + db.epse.trim() + db.dune.trim() + for ekey, edig in db.getQnfItemsNextIter(): + pre, _ = splitKey(ekey) + db.delQnf(dgKey(pre, edig), edig) + + except ConfigurationError: + logger.error(f"identifier prefix for {db.name} does not exist, incept must be run first", ) + return -1 + + +def migrateKeys(db): + # public keys mapped to the AID and event seq no they appeared in + pubs = subing.CatCesrIoSetSuber(db=db, subkey="pubs.", + klas=(coring.Prefixer, coring.Seqner)) + + # next key digests mapped to the AID and event seq no they appeared in + digs = subing.CatCesrIoSetSuber(db=db, subkey="digs.", + klas=(coring.Prefixer, coring.Seqner)) + + for pre, fn, dig in db.getFelItemAllPreIter(key=b''): + dgkey = dbing.dgKey(pre, dig) # get message + if not (raw := db.getEvt(key=dgkey)): + logger.info(f"Migrate keys: missing event for dig={dig}, skipped.") + continue + serder = serdering.SerderKERI(raw=bytes(raw)) + val = (coring.Prefixer(qb64b=serder.preb), coring.Seqner(sn=serder.sn)) + verfers = serder.verfers or [] + for verfer in verfers: + pubs.add(keys=(verfer.qb64,), val=val) + ndigers = serder.ndigers or [] + for diger in ndigers: + digs.add(keys=(diger.qb64,), val=val) diff --git a/src/keri/db/migrations/hab_data_rename.py b/src/keri/db/migrations/hab_data_rename.py new file mode 100644 index 000000000..7ac814514 --- /dev/null +++ b/src/keri/db/migrations/hab_data_rename.py @@ -0,0 +1,88 @@ +from dataclasses import dataclass, field, asdict +from typing import Optional + +from keri.db import koming, basing +from keri.db.basing import HabitatRecord, Baser +from keri.vdr.viring import Reger + + +@dataclass +class HabitatRecordV0_6_7: # baser.habs + """ + Habitat application state information keyed by habitat name (baser.habs) + + Attributes: + prefix (str): identifier prefix of hab qb64 + pid (str | None): group member identifier qb64 when hid is group + aids (list | None): group signing member identifiers qb64 when hid is group + watchers: (list[str]) = list of id prefixes qb64 of watchers + """ + prefix: str # aid qb64 + pid: Optional[str] # participant aid of group aid + aids: Optional[list] # all identifiers participating in the group identity + + watchers: list[str] = field(default_factory=list) # aids qb64 of watchers + +def _check_if_needed(db): + """ + Check if the migration is needed + + Parameters: + db(Baser): Baser database object on which to run the migration + + Returns: + bool: True if the migration is needed, False otherwise + """ + habs = koming.Komer(db=db, subkey='habs.', schema=dict, ) + first = next(habs.getItemIter(), None) + if first is None: + return False + name, habord = first + if 'prefix' in habord: + return True + return False + +def migrate(db): + """Rename data in HabitatRecord from the old labels to the new labels as of 2022-10-17 + + This migration performs the following: + 1. rename prefix -> hid + 2. rename pid -> mid + 3. rename aids -> smids, rmids + + Parameters: + db(Baser): Baser database object on which to run the migration + """ + # May be running on a database that is already in the right state yet has no migrations run + # so we need to check if the migration is needed + if not _check_if_needed(db): + print(f"{__name__} migration not needed, already ran") + return + + habs = koming.Komer(db=db, + subkey='habs.', + schema=HabitatRecordV0_6_7, ) + + habords = dict() + # Update Hab records from .habs with name + for name, habord in habs.getItemIter(): + existing = asdict(habord) + habord_0_6_7 = HabitatRecordV0_6_7(**existing) + habord_0_6_8 = HabitatRecord( + hid=habord_0_6_7.prefix, + mid=habord_0_6_7.pid, + smids=habord_0_6_7.aids, + rmids=habord_0_6_7.aids, + sid=None, + watchers=habord_0_6_7.watchers + ) + habords[name] = habord_0_6_8 + + habs.trim() # remove existing records + + # Add in the renamed records + for name, habord in habords.items(): + (name,) = name + db.habs.pin(keys=(name,), val=habord) + + diff --git a/src/keri/db/subing.py b/src/keri/db/subing.py index 754cc553a..76422692c 100644 --- a/src/keri/db/subing.py +++ b/src/keri/db/subing.py @@ -187,6 +187,20 @@ def trim(self, keys: Union[str, Iterable]=b""): """ return(self.db.delTopVal(db=self.sdb, key=self._tokey(keys))) + def cntAll(self): + """ + Return iterator over the all the items in subdb + + Returns: + iterator: of tuples of keys tuple and val dataclass instance for + each entry in db. Raises StopIteration when done + + Example: + if key in database is "a.b" and val is serialization of dataclass + with attributes x and y then returns + (("a","b"), dataclass(x=1,y=2)) + """ + return self.db.cnt(db=self.sdb) class Suber(SuberBase): """ diff --git a/src/keri/demo/demo_kev.py b/src/keri/demo/demo_kev.py index 00b9cca7c..7b125b0a3 100644 --- a/src/keri/demo/demo_kev.py +++ b/src/keri/demo/demo_kev.py @@ -8,11 +8,10 @@ import argparse import logging -from hio import help +from keri import help from hio.base import doing -from ..app import habbing, keeping, apping -from ..db import basing +from ..app import apping logger = help.ogler.getLogger() diff --git a/src/keri/end/ending.py b/src/keri/end/ending.py index 9e65ab7f0..4746e0bf6 100644 --- a/src/keri/end/ending.py +++ b/src/keri/end/ending.py @@ -577,6 +577,10 @@ def on_get(self, req, rep, aid=None, role=None, eid=None): rep.status = falcon.HTTP_NOT_FOUND return + if kever.delegated and kever.delegator not in self.hby.kevers: + rep.status = falcon.HTTP_NOT_FOUND + return + owits = oset(kever.wits) if kever.prefixer.qb64 in self.hby.prefixes: # One of our identifiers hab = self.hby.habs[kever.prefixer.qb64] diff --git a/src/keri/help/__init__.py b/src/keri/help/__init__.py index 72f5f27eb..6922651ad 100644 --- a/src/keri/help/__init__.py +++ b/src/keri/help/__init__.py @@ -13,6 +13,18 @@ from hio.help import ogling +import logging + +# Custom TRACE log level configuration +TRACE = 5 # TRACE (5) logging level value between DEBUG (10) and NOTSET (0) +logging.TRACE = TRACE # add TRACE logging level to logging module +logging.addLevelName(logging.TRACE, "TRACE") +def trace(self, message, *args, **kwargs): + """Trace logging function - logs message if TRACE (5) level enabled""" + if self.isEnabledFor(TRACE): + self._log(TRACE, message, args, **kwargs) +logging.Logger.trace = trace + # want help.ogler always defined by default ogler = ogling.initOgler(prefix='keri', syslogged=False) # inits once only on first import diff --git a/src/keri/peer/exchanging.py b/src/keri/peer/exchanging.py index fc1b3c234..662cfef54 100644 --- a/src/keri/peer/exchanging.py +++ b/src/keri/peer/exchanging.py @@ -92,8 +92,10 @@ def processEvent(self, serder, tsgs=None, cigars=None, **kwargs): if not tholder.satisfy(indices): # We still don't have all the sigers, need to escrow if self.escrowPSEvent(serder=serder, tsgs=tsgs, pathed=pathed): self.cues.append(dict(kin="query", q=dict(r="logs", pre=prefixer.qb64, sn=seqner.snh))) - raise MissingSignatureError(f"Not enough signatures in {indices}" - f" for evt = {serder.ked}.") + msg = f"Not enough signatures in {indices} for evt = {serder.said}" + logger.info(msg) + logger.debug(f"Event body=\n%20\n", serder.pretty()) + raise MissingSignatureError(msg) elif cigars is not None: for cigar in cigars: @@ -121,11 +123,12 @@ def processEvent(self, serder, tsgs=None, cigars=None, **kwargs): # Perform behavior specific verification, think IPEX chaining requirements try: if not behavior.verify(serder=serder, attachments=attachments): - logger.info(f"exn event for route {route} failed behavior verfication. exn={serder.ked}") + logger.info(f"exn event for route {route} failed behavior verification. exn={serder.said}") + logger.debug(f"exn body=\n{serder.ked}\n") return - except AttributeError: - logger.info(f"Behavior for {route} missing or does not have verify for exn={serder.ked}") + logger.debug(f"Behavior for {route} missing or does not have verify for exn={serder.said}") + logger.debug(f"exn Body=\n{serder.ked}\n") # Always persis events self.logEvent(serder, pathed, tsgs, cigars) @@ -135,7 +138,9 @@ def processEvent(self, serder, tsgs=None, cigars=None, **kwargs): try: behavior.handle(serder=serder, attachments=attachments) except AttributeError: - logger.info(f"Behavior for {route} missing or does not have handle for exn={serder.ked}") + logger.debug(f"Behavior for {route} missing or does not have handle for exn={serder.said}") + logger.debug( + f"exn body=\n{serder.ked}\n") def processEscrow(self): """ Process all escrows for `exn` messages @@ -187,24 +192,21 @@ def processEscrowPartialSigned(self): try: self.processEvent(serder=serder, tsgs=tsgs, pathed=pathed) - except MissingSignatureError as ex: - if logger.isEnabledFor(logging.DEBUG): - logger.info("Exchange partially signed unescrow failed: %s\n", ex.args[0]) - else: - logger.info("Exchange partially signed failed: %s\n", ex.args[0]) + logger.trace("Exchange partially signed unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.debug("Event body=\n%s\n", serder.pretty()) except Exception as ex: self.hby.db.epse.rem(dig) self.hby.db.esigs.rem(dig) + logger.info("Exchange partially signed unescrowed: %s\n", ex.args[0]) if logger.isEnabledFor(logging.DEBUG): - logger.info("Exchange partially signed unescrowed: %s\n", ex.args[0]) - else: - logger.info("Exchange partially signed unescrowed: %s\n", ex.args[0]) + logger.debug("Event body=\n%s\n", serder.pretty()) else: self.hby.db.epse.rem(dig) self.hby.db.esigs.rem(dig) - logger.info("Exchanger unescrow succeeded in valid exchange: " - "creder=\n%s\n", serder.pretty()) + logger.info("Exchanger unescrow succeeded in valid exchange: serder = %s", serder.said) + logger.debug("Serder Body=\n%s\n", serder.pretty()) def logEvent(self, serder, pathed=None, tsgs=None, cigars=None): dig = serder.said @@ -226,6 +228,15 @@ def logEvent(self, serder, pathed=None, tsgs=None, cigars=None): self.hby.db.erpy.pin(keys=(pdig,), val=saider) self.hby.db.exns.put(keys=(dig,), val=serder) + # special handling for 1.1.18 -> 1.1.32 + recipient = None + if 'rp' in serder.ked: + recipient = serder.ked['rp'] + sender = serder.ked['i'] + route = serder.ked['r'] + logger.info("Saved exn event route = %s SAID = %s sender %s -> recipient %s", + route, dig, sender, recipient) + logger.debug("EXN Event Body=\n%s\n", serder.pretty()) def lead(self, hab, said): """ Determines is current member represented by hab is the lead of an exn message @@ -338,6 +349,7 @@ def exchange(route, t=ilk, d="", i=sender, + rp="", p=p, dt=dt, r=route, @@ -479,8 +491,10 @@ def verify(hby, serder): _, indices = eventing.verifySigs(serder.raw, sigers, verfers) if not tholder.satisfy(indices): # We still don't have all the sigers, need to escrow - raise MissingSignatureError(f"Not enough signatures in {indices}" - f" for evt = {serder.ked}.") + msg = f"Not enough signatures in {indices} for evt = {serder.said}" + logger.info("exchanging.verify: %s", msg) + logger.debug(f"Event body=\n%s\n", serder.pretty()) + raise MissingSignatureError(msg) accepted = True cigars = hby.db.ecigs.get(keys=(serder.said,)) diff --git a/src/keri/vc/protocoling.py b/src/keri/vc/protocoling.py index 12f4c991b..65900e6cb 100644 --- a/src/keri/vc/protocoling.py +++ b/src/keri/vc/protocoling.py @@ -264,13 +264,14 @@ def ipexGrantExn(hab, recp, message, acdc, iss=None, anc=None, agree=None, dt=No return exn, ims -def ipexAdmitExn(hab, message, grant): +def ipexAdmitExn(hab, message, grant, dt=None): """ Admit a disclosure Parameters: hab(Hab): identifier environment for issuer of credential message(str): Human readable message regarding the admission grant (Serder): IPEX grant exn message serder + dt (str): timestamp Returns: Serder: credential issuance exn peer to peer message @@ -281,7 +282,7 @@ def ipexAdmitExn(hab, message, grant): m=message, ) - exn, end = exchanging.exchange(route="/ipex/admit", payload=data, sender=hab.pre, dig=grant.said) + exn, end = exchanging.exchange(route="/ipex/admit", payload=data, sender=hab.pre, dig=grant.said, date=dt) ims = hab.endorse(serder=exn, last=False, pipelined=False) del ims[:exn.size] ims.extend(end) diff --git a/src/keri/vc/proving.py b/src/keri/vc/proving.py index e724648a5..d99f85b4c 100644 --- a/src/keri/vc/proving.py +++ b/src/keri/vc/proving.py @@ -5,7 +5,7 @@ """ from collections.abc import Iterable -from typing import Union +from typing import Union, Optional from .. import help from ..core import coring, serdering @@ -23,8 +23,9 @@ def credential(schema, issuer, data, recipient=None, - private=False, - salt=None, + private: bool = False, + private_credential_nonce: Optional[str] = None, + private_subject_nonce: Optional[str] = None, status=None, source=None, rules=None, @@ -40,7 +41,8 @@ def credential(schema, recipient (Option[str|None]): qb64 identifier prefix of the recipient data (dict): of the values being assigned to the subject of this credential private (bool): apply nonce used for privacy preserving ACDC - salt (string): salt for nonce + private_credential_nonce (Optional[str]): nonce used for privacy vc + private_subject_nonce (Optional[str]): nonce used for subject source (dict | list): of source credentials to which this credential is chained rules (dict | list): ACDC rules section for credential version (Version): version instance @@ -62,8 +64,8 @@ def credential(schema, ) if private: - vc["u"] = salt if salt is not None else coring.Salter().qb64 - subject["u"] = salt if salt is not None else coring.Salter().qb64 + vc["u"] = private_credential_nonce if private_credential_nonce is not None else coring.Salter().qb64 + subject["u"] = private_subject_nonce if private_subject_nonce is not None else coring.Salter().qb64 if recipient is not None: subject['i'] = recipient diff --git a/src/keri/vdr/credentialing.py b/src/keri/vdr/credentialing.py index 9b6af7f13..3b9632995 100644 --- a/src/keri/vdr/credentialing.py +++ b/src/keri/vdr/credentialing.py @@ -5,6 +5,8 @@ VC issuer support """ +from typing import Optional + from hio.base import doing from hio.help import decking @@ -234,7 +236,8 @@ def processEvent(self, serder): try: self.tvy.processEvent(serder=serder) except kering.MissingAnchorError: - logger.info("Credential registry missing anchor for inception = {}".format(serder.ked)) + logger.info("Credential registry missing anchor for inception = {}".format(serder.said)) + logger.debug("Inception body = {}".format(serder.pretty())) def anchorMsg(self, pre, regd, seqner, saider): """ Create key event with seal to serder anchored as data. @@ -474,7 +477,7 @@ def revoke(self, said, dt=None): raise kering.ValidationError("Invalid revoke of {} that has not been issued " "pre={}.".format(vci, self.regk)) ievt = self.reger.getTvt(dgKey(pre=vci, dig=vcser)) - iserder = serdering.serderACDC(raw=bytes(ievt)) # Serder(raw=bytes(ievt)) + iserder = serdering.SerderACDC(raw=bytes(ievt)) # Serder(raw=bytes(ievt)) if self.noBackers: serder = eventing.revoke(vcdig=vci, regk=self.regk, dig=iserder.said, dt=dt) @@ -772,7 +775,8 @@ def __init__(self, hby, rgy, registrar, verifier): super(Credentialer, self).__init__(doers=doers) - def create(self, regname, recp: str, schema, source, rules, data, private=False): + def create(self, regname, recp: str, schema, source, rules, data, private: bool = False, + private_credential_nonce: Optional[str] = None, private_subject_nonce: Optional[str] = None): """ Create and validate a credential returning the fully populated Creder Parameters: @@ -782,7 +786,9 @@ def create(self, regname, recp: str, schema, source, rules, data, private=False) source: rules: data: - private: add nonce for privacy preserving + private (bool): apply nonce used for privacy preserving ACDC + private_credential_nonce (Optional[str]): nonce used for privacy vc + private_subject_nonce (Optional[str]): nonce used for subject Returns: Creder: Creder class for the issued credential @@ -803,6 +809,8 @@ def create(self, regname, recp: str, schema, source, rules, data, private=False) data=data, source=source, private=private, + private_credential_nonce=private_credential_nonce, + private_subject_nonce=private_subject_nonce, rules=rules, status=registry.regk) self.validate(creder) diff --git a/src/keri/vdr/eventing.py b/src/keri/vdr/eventing.py index 730e9d874..67914b141 100644 --- a/src/keri/vdr/eventing.py +++ b/src/keri/vdr/eventing.py @@ -1267,8 +1267,9 @@ def logEvent(self, pre, sn, serder, seqner, saider, bigers=None, baks=None): self.reger.tets.pin(keys=(pre.decode("utf-8"), dig.decode("utf-8")), val=coring.Dater()) self.reger.putTvt(key, serder.raw) self.reger.putTel(snKey(pre, sn), dig) - logger.info("Tever state: %s Added to TEL valid event=\n%s\n", - pre, json.dumps(serder.ked, indent=1)) + logger.info("Tever: Added to TEL %s valid event=%s SAID=%s reg=%.8s... iss=%s", + serder.ilk, serder.pre, serder.said, self.regk, self.pre) + logger.debug("TEL Event Body=\n%s\n", serder.pretty()) def valAnchorBigs(self, serder, seqner, saider, bigers, toad, baks): """ Validate anchor and backer signatures (bigers) when provided. @@ -1319,12 +1320,12 @@ def valAnchorBigs(self, serder, seqner, saider, bigers, toad, baks): if len(bindices) < toad: # not fully witnessed yet self.escrowPWEvent(serder=serder, seqner=seqner, saider=saider, bigers=bigers) - - raise MissingWitnessSignatureError("Failure satisfying toad = {} " - "on witness sigs for {} for evt = {}.".format(toad, - [siger.qb64 for siger - in bigers], - serder.ked)) + msg = (f"Failure satisfying toad={toad} on witness sigs " + f"for {[siger.qb64 for siger in bigers]} " + f"for evt = {serder.said}.") + logger.info(msg) + logger.debug(f"Event Body=\n{serder.ked}\n") + raise MissingWitnessSignatureError(msg) return bigers def verifyAnchor(self, serder, seqner=None, saider=None): @@ -1423,8 +1424,8 @@ def escrowALEvent(self, serder, seqner, saider, bigers=None, baks=None): self.reger.delBaks(key) self.reger.putBaks(key, [bak.encode("utf-8") for bak in baks]) self.reger.putTvt(key, serder.raw) - logger.info("Tever state: Escrowed anchorless event " - "event = %s\n", serder.ked) + logger.info("Tever: Escrowed anchorless event event = %s", serder.said) + logger.debug("Event body=\n%s\n", serder.ked) return self.reger.putTae(snKey(serder.preb, serder.sn), serder.saidb) def getBackerState(self, ked): @@ -1589,7 +1590,8 @@ def processEvent(self, serder, seqner=None, saider=None, wigers=None): if ilk in (Ilks.vcp,): # we don't have multiple signatures to verify so this # is already first seen and then lifely duplicitious - raise LikelyDuplicitousError("Likely Duplicitous event={}.".format(ked)) + logger.debug("Likely Duplicitous event Body=%s", serder.pretty()) + raise LikelyDuplicitousError(f"Likely Duplicitous event={serder.said}") tever = self.tevers[regk] tever.cues = self.cues @@ -1615,7 +1617,8 @@ def processEvent(self, serder, seqner=None, saider=None, wigers=None): # self.cues.append(dict(kin="receipt", serder=serder)) pass else: # duplicitious - raise LikelyDuplicitousError("Likely Duplicitous event={} with sn {}.".format(ked, sn)) + logger.debug("Likely Duplicitous event Body=%s", serder.pretty()) + raise LikelyDuplicitousError(f"Likely Duplicitous event={serder.said} with sn {serder.sn}") def processQuery(self, serder, source=None, sigers=None, cigars=None): """ Process TEL query event message (qry) @@ -2078,10 +2081,9 @@ def processEscrowOutOfOrders(self): except OutOfOrderError as ex: # still waiting on missing prior event to validate - if logger.isEnabledFor(logging.DEBUG): + if logger.isEnabledFor(logging.TRACE): + logger.trace("Tevery unescrow failed: %s\n", ex.args[0]) logger.exception("Tevery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Tevery unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than out of order so remove from OO escrow @@ -2096,8 +2098,8 @@ def processEscrowOutOfOrders(self): # duplicitous so we process remaining escrows in spite of found # valid event escrow. self.reger.delOot(snKey(pre, sn)) # removes from escrow - logger.info("Tevery unescrow succeeded in valid event: " - "event=\n%s\n", json.dumps(tserder.ked, indent=1)) + logger.info("Tevery unescrow succeeded in valid event: event = %s", tserder.said) + logger.debug("Event Body=\n%s\n", tserder.pretty()) def processEscrowAnchorless(self): """ Process escrow of TEL events received before the anchoring KEL event. @@ -2118,11 +2120,9 @@ def processEscrowAnchorless(self): traw = self.reger.getTvt(dgkey) if traw is None: # no event so raise ValidationError which unescrows below - logger.info("Tevery unescrow error: Missing event at." - "dig = %s\n", bytes(digb)) - - raise ValidationError("Missing escrowed evt at dig = {}." - "".format(bytes(digb))) + msg = f"Tevery: anchorless escrow unescrow error: Missing event at dig = {bytes(digb)}" + logger.trace(msg) + raise ValidationError(msg) tserder = serdering.SerderKERI(raw=bytes(traw)) # escrowed event @@ -2132,11 +2132,9 @@ def processEscrowAnchorless(self): couple = self.reger.getAnc(dgkey) if couple is None: - logger.info("Tevery unescrow error: Missing anchor at." - "dig = %s\n", bytes(digb)) - - raise MissingAnchorError("Missing escrowed anchor at dig = {}." - "".format(bytes(digb))) + msg = f"Tevery: anchorless escrow unescrow error: Missing anchor at dig = {bytes(digb)}" + logger.trace(msg) + raise MissingAnchorError(msg) ancb = bytearray(couple) seqner = coring.Seqner(qb64b=ancb, strip=True) saider = coring.Saider(qb64b=ancb, strip=True) @@ -2145,23 +2143,23 @@ def processEscrowAnchorless(self): except MissingAnchorError as ex: # still waiting on missing prior event to validate - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Tevery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Tevery unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("Tevery: anchorless escrow unescrow failed: %s\n", ex.args[0]) + logger.exception("Tevery: anchorless escrow unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than out of order so remove from OO escrow self.reger.delTae(snKey(pre, sn)) # removes one escrow at key val if logger.isEnabledFor(logging.DEBUG): - logger.exception("Tevery unescrowed: %s\n", ex.args[0]) + logger.exception("Tevery: anchorless escrow other error on unescrow: %s\n", ex.args[0]) else: - logger.error("Tevery unescrowed: %s\n", ex.args[0]) + logger.error("Tevery: anchorless escrow other error on unescrow: %s\n", ex.args[0]) else: # unescrow succeeded, remove from escrow # We don't remove all escrows at pre,sn because some might be # duplicitous so we process remaining escrows in spite of found # valid event escrow. self.reger.delTae(snKey(pre, sn)) # removes from escrow - logger.info("Tevery unescrow succeeded in valid event: " - "event=\n%s\n", json.dumps(tserder.ked, indent=1)) + logger.info("Tevery: anchorless escrow unescrow succeeded in valid event: " + "event = %s", tserder.said) + logger.debug("Event body=\n%s\n", tserder.pretty()) diff --git a/src/keri/vdr/verifying.py b/src/keri/vdr/verifying.py index d7fcb245b..4ebf9b420 100644 --- a/src/keri/vdr/verifying.py +++ b/src/keri/vdr/verifying.py @@ -263,10 +263,9 @@ def _processEscrow(self, db, timeout, etype: Type[Exception]): self.processCredential(creder, prefixer, seqner, saider) except etype as ex: - if logger.isEnabledFor(logging.DEBUG): - logger.exception("Verifiery unescrow failed: %s\n", ex.args[0]) - else: - logger.error("Verifier unescrow failed: %s\n", ex.args[0]) + if logger.isEnabledFor(logging.TRACE): + logger.trace("Verifier unescrow failed: %s\n", ex.args[0]) + logger.exception("Verifier unescrow failed: %s\n", ex.args[0]) except Exception as ex: # log diagnostics errors etc # error other than missing sigs so remove from PA escrow db.rem(said) @@ -277,7 +276,8 @@ def _processEscrow(self, db, timeout, etype: Type[Exception]): else: db.rem(said) logger.info("Verifier unescrow succeeded in valid group op: " - "creder=\n%s\n", creder.pretty()) + "creder = %s", creder.said) + logger.debug("Creder body=\n%s\n", creder.pretty()) def saveCredential(self, creder, prefixer, seqner, saider): """ Write the credential and associated indicies to the database diff --git a/src/keri/vdr/viring.py b/src/keri/vdr/viring.py index 49edfd2b5..0c932e128 100644 --- a/src/keri/vdr/viring.py +++ b/src/keri/vdr/viring.py @@ -17,11 +17,12 @@ from ..app import signing from ..core import coring, serdering from ..db import dbing, basing -from ..db.dbing import snKey -from ..help import helping -from ..vc import proving from ..vdr import eventing +from keri import help + +logger = help.ogler.getLogger() + class rbdict(dict): """ Reger backed read through cache for registry state @@ -384,6 +385,40 @@ def reopen(self, **kwa): return self.env + def clearEscrows(self): + """Clear credential event escrows""" + # self.oots, self.twes, self.taes + count = 0 + for (k, _) in self.getOotItemIter(): + count += 1 + self.delOot(k) + logger.info(f"TEL: Cleared {count} out of order escrows.") + + count = 0 + for (k, ) in self.getAllItemIter(self.twes): + count += 1 + self.delTwe(k) + logger.info(f"TEL: Cleared {count} partially witnessed escrows.") + + count = 0 + for (k, _) in self.getAllItemIter(self.taes): + count += 1 + self.delTae(k) + logger.info(f"TEL: Cleared {count} anchorless escrows.") + + for name, sub, desc in [ + ( 'mre', self.mre, 'missing registry escrows'), + ( 'mce', self.mce, 'broken chain escrows'), + ( 'mse', self.mse, 'missing schema escrows'), + ('cmse', self.cmse, 'missing signature escrows'), + ('tpwe', self.tpwe, 'partial witness escrows'), + ('tmse', self.tmse, 'multisig escrows'), + ('tede', self.tede, 'event dissemination escrows') + ]: + sub.trim() + logger.info(f"TEL: Cleared escrow ({name.ljust(5)}): {desc}") + logger.info("Cleared TEL escrows") + def cloneCreds(self, saids, db): """ Returns fully expanded credential with chained credentials attached. diff --git a/tests/app/cli/test_kli_commands.py b/tests/app/cli/test_kli_commands.py index 1fcfd566f..afb021102 100644 --- a/tests/app/cli/test_kli_commands.py +++ b/tests/app/cli/test_kli_commands.py @@ -233,19 +233,39 @@ def test_standalone_kli_commands(helpers, capsys): '\t3. DEMwUl3u8mJ-cWxSnReA0rQesIgZ8SFoHp0U2WyiZjRt\n' '\n') - args = parser.parse_args(["escrow", "--name", "test"]) + args = parser.parse_args(["escrow", "list", "--name", "test"]) assert args.handler is not None doers = args.handler(args) directing.runController(doers=doers) capesc = capsys.readouterr() assert capesc.out == ('{\n' - ' "out-of-order-events": [],\n' - ' "partially-witnessed-events": [],\n' + ' "unverified-receipts": 0,\n' + ' "verified-receipts": 0,\n' ' "partially-signed-events": [],\n' + ' "partially-witnessed-events": [],\n' + ' "unverified-event-indexed-couples": 0,\n' + ' "out-of-order-events": [],\n' ' "likely-duplicitous-events": [],\n' + ' "query-not-found": 0,\n' + ' "partially-delegated-events": 0,\n' + ' "reply": 0,\n' + ' "failed-oobi": 0,\n' + ' "group-partial-witness": 0,\n' + ' "group-delegate": 0,\n' + ' "delegated-partial-witness": 0,\n' + ' "group-partial-signed": 0,\n' + ' "exchange-partial-signed": 0,\n' + ' "delegated-unanchored": 0,\n' + ' "tel-out-of-order": 0,\n' + ' "tel-partially-witnessed": 0,\n' + ' "tel-anchorless": 0,\n' ' "missing-registry-escrow": [],\n' ' "broken-chain-escrow": [],\n' - ' "missing-schema-escrow": []\n' + ' "missing-schema-escrow": [],\n' + ' "tel-missing-signature": 0,\n' + ' "tel-partial-witness-escrow": 0,\n' + ' "tel-multisig": 0,\n' + ' "tel-event-dissemination": 0\n' '}\n') diff --git a/tests/app/test_delegating.py b/tests/app/test_delegating.py index 7742a1d2b..f72659036 100644 --- a/tests/app/test_delegating.py +++ b/tests/app/test_delegating.py @@ -124,17 +124,15 @@ def test_delegation_request(mockHelpingNowUTC): evt = hab.endorse(serder=serder) exn, atc = delegating.delegateRequestExn(hab=hab, delpre=delpre, evt=evt) - assert atc == (b'-FABEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI30AAAAAAAAAAAAAAA' - b'AAAAAAAAEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3-AABAADECnBl' - b'0c14SVi7Keh__sd1PVhinSy-itPr33ZxvSjJYFastqXw9ZTFGNKsY6iALUk5xP3S' - b'399tJrPFe7PtuNAN') + assert atc == (b'-FABEIaGMMWJFPmtXznY1IIiKDIrg' + b'-vIyge6mBl2QV8dDjI30AAAAAAAAAAAAAAAAAAAAAAAEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3' + b'-AABAACzeUyP6__0oDca-Oiv2iGXKghBw_8sI4ZHyyeMedvz0iZIIQYqJd2Zt7cDHRh7xBGWI85J_oOixLET3mFZUu0A') assert exn.ked["r"] == '/delegate/request' - assert exn.saidb == b'EOiDc2wEmhHc7sbLG64y2gveCIRlFe4BuISaz0mlOuZz' - assert atc == (b'-FABEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI30AAAAAAAAAAAAAAA' - b'AAAAAAAAEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3-AABAADECnBl' - b'0c14SVi7Keh__sd1PVhinSy-itPr33ZxvSjJYFastqXw9ZTFGNKsY6iALUk5xP3S' - b'399tJrPFe7PtuNAN') + assert exn.saidb == b'EHPkcmdLGql9_1WD0wl0OalYk8PcF4HMMd7gGi-iqfSe' + assert atc == (b'-FABEIaGMMWJFPmtXznY1IIiKDIrg' + b'-vIyge6mBl2QV8dDjI30AAAAAAAAAAAAAAAAAAAAAAAEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3' + b'-AABAACzeUyP6__0oDca-Oiv2iGXKghBw_8sI4ZHyyeMedvz0iZIIQYqJd2Zt7cDHRh7xBGWI85J_oOixLET3mFZUu0A') data = exn.ked["a"] assert data["delpre"] == delpre embeds = exn.ked['e'] diff --git a/tests/app/test_grouping.py b/tests/app/test_grouping.py index 5df6a805b..6141a0562 100644 --- a/tests/app/test_grouping.py +++ b/tests/app/test_grouping.py @@ -37,7 +37,7 @@ def test_counselor(): parsing.Parser().parse(ims=bytearray(icp3), kvy=kev2) smids = [hab1.pre, hab2.pre, hab3.pre] - rmids = None # need to fixe this + rmids = [hab1.pre, hab2.pre, hab3.pre] inits = dict(isith='["1/2", "1/2", "1/2"]', nsith='["1/2", "1/2", "1/2"]', toad=0, wits=[]) # Create group hab with init params @@ -84,7 +84,7 @@ def test_counselor(): migers = [hab1.kever.ndigers[0], hab2.kever.ndigers[0]] prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) - rot = ghab.rotate(isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) + rot = ghab.rotate(smids=smids, rmids=rmids, isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) rserder = serdering.SerderKERI(raw=rot) counselor.start(ghab=ghab, prefixer=prefixer, seqner=seqner, saider=coring.Saider(qb64=rserder.said)) @@ -139,7 +139,7 @@ def test_counselor(): migers = [hab1.kever.ndigers[0], hab2.kever.ndigers[0], hab3.kever.ndigers[0]] prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) - rot = ghab.rotate(isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) + rot = ghab.rotate(smids=smids, rmids=rmids, isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) rserder = serdering.SerderKERI(raw=rot) counselor.start(ghab=ghab, prefixer=prefixer, seqner=seqner, saider=coring.Saider(qb64=rserder.said)) @@ -195,7 +195,7 @@ def test_counselor(): migers = [hab1.kever.ndigers[0], hab3.kever.ndigers[0]] prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) - rot = ghab.rotate(isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) + rot = ghab.rotate(smids=smids, rmids=rmids, isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) rserder = serdering.SerderKERI(raw=rot) counselor.start(ghab=ghab, prefixer=prefixer, seqner=seqner, saider=coring.Saider(qb64=rserder.said)) @@ -274,7 +274,7 @@ def test_the_seven(): parsing.Parser().parse(ims=bytearray(icp), kvy=kev) smids = [hab1.pre, hab2.pre, hab3.pre, hab4.pre, hab5.pre, hab6.pre, hab7.pre] - rmids = None # need to fixe this + rmids = [hab1.pre, hab2.pre, hab3.pre, hab4.pre, hab5.pre, hab6.pre, hab7.pre] inits = dict(isith='["1/3", "1/3", "1/3", "1/3", "1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3", "1/3", "1/3", "1/3", "1/3"]', toad=0, wits=[]) @@ -376,7 +376,7 @@ def test_the_seven(): hab5.kever.ndigers[0], hab6.kever.ndigers[0], hab7.kever.ndigers[0]] prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) - rot = ghab.rotate(isith='["1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3", "1/3", "1/3", "1/3", "1/3"]', + rot = ghab.rotate(smids=smids, rmids=rmids, isith='["1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3", "1/3", "1/3", "1/3", "1/3"]', toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) rserder = serdering.SerderKERI(raw=rot) @@ -440,7 +440,7 @@ def test_the_seven(): hab5.kever.ndigers[0], hab6.kever.ndigers[0], hab7.kever.ndigers[0]] prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) - rot = ghab.rotate(isith='["1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3", "1/3", "1/3", "1/3", "1/3"]', + rot = ghab.rotate(smids=smids, rmids=rmids, isith='["1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3", "1/3", "1/3", "1/3", "1/3"]', toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) rserder = serdering.SerderKERI(raw=rot) @@ -519,7 +519,7 @@ def test_the_seven(): migers = [hab4.kever.ndigers[0], hab5.kever.ndigers[0], hab6.kever.ndigers[0]] prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) - rot = ghab4.rotate(isith='["1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3"]', + rot = ghab4.rotate(smids=smids, rmids=rmids, isith='["1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3"]', toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) rserder = serdering.SerderKERI(raw=rot) @@ -638,12 +638,11 @@ def test_multisig_incept(mockHelpingNowUTC): icp=hab.makeOwnEvent(sn=hab.kever.sn)) assert exn.ked["r"] == '/multisig/icp' - assert exn.saidb == b'EGDEBUZW--n-GqOOwRflzBeqoQsYWKMOQVU_1YglG-BL' - assert atc == (b'-FABEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI30AAAAAAAAAAAAAAA' - b'AAAAAAAAEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3-AABAAC84-o2' - b'HKwKxhL1ttzykB9zuFaGV6OpQ05b1ZJYAeBFrR7kVON1aNjpLgQCG_0bY4FUiP7F' - b'GTVDrBjuFhbeDKAH-LAa5AACAA-e-icp-AABAACihaKoLnoXxRoxGbFfOy67YSh6' - b'UxtgjT2oxupnLDz2FlhevGJKTMObbdex9f0Hqob6uTavSJvsXf5RzitskkkC') + assert exn.saidb == b'EJ6Kl50IBicAa8zND_3wMSQ5itw555V7NKid9y1SKobe' + assert atc == (b'-FABEIaGMMWJFPmtXznY1IIiKDIrg' + b'-vIyge6mBl2QV8dDjI30AAAAAAAAAAAAAAAAAAAAAAAEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3' + b'-AABAACL4cf7LxzKJgaJbb7wWHLuTfj3wManDV0SW7euFNZDiEhD1kUiP3_wtOIfqB_ZsEceE4oIgOOZwFROyrcf9ScB' + b'-LAa5AACAA-e-icp-AABAACihaKoLnoXxRoxGbFfOy67YSh6UxtgjT2oxupnLDz2FlhevGJKTMObbdex9f0Hqob6uTavSJvsXf5RzitskkkC') data = exn.ked["a"] assert data["smids"] == aids assert "icp" in exn.ked['e'] @@ -662,17 +661,110 @@ def test_multisig_rotate(mockHelpingNowUTC): exn, atc = grouping.multisigRotateExn(ghab=ghab1, smids=ghab1.smids, rmids=ghab1.rmids, rot=rot) assert exn.ked["r"] == '/multisig/rot' - assert exn.saidb == b'ENfCk9DUUck6Ixe6cYnbCbJfIsisA3H4kHPwm5Z-2Tf8' - assert atc == (b'-FABEH__mobl7NDyyQCB1DoLK-OPSueraPtZAlWEjfOYkaba0AAAAAAAAAAAAAAA' - b'AAAAAAAAEH__mobl7NDyyQCB1DoLK-OPSueraPtZAlWEjfOYkaba-AABAADChiAf' - b'iExAQ2ETkzzf7MOubXV9mL-r6fPsOI4yn348yeE5dXqdI7ddn5-wnPwNVjqqKkDp' - b'xlOEFYRiBQEbwZQC') + assert exn.saidb == b'EC2IKkvJh6_Ukx-ZWP20qyHPWpXYfZdCQkydA9HwYE9c' + assert atc == (b'-FABEH__mobl7NDyyQCB1DoLK-OPSueraPtZAlWEjfOYkaba0AAAAAAAAAAAAAAAAAAAAAAAEH__mobl7NDyyQCB1DoLK' + b'-OPSueraPtZAlWEjfOYkaba-AABAABxikcUcQLQyCuOfQXYBeyFd3hzMaaZ_wHV_KPPX8DyFcold4P8mdGC' + b'-meFY9P7qoJd3lPA1khblmqY5jhK2kAL') data = exn.ked["a"] assert data["smids"] == ghab1.smids assert data["gid"] == ghab1.pre assert "rot" in exn.ked["e"] +def test_multisig_rotate_new_group_member_updates_smids(mockHelpingNowUTC): + # Create a multisig with three members, test_1, test_2, and test_3 + with openMultiSig(prefix="test") as ((hby1, ghab1), (hby2, ghab2), (hby3, ghab3)): + # Create a new member, test_4 + with habbing.openHab(name="test_4", salt=b'0123456789abcdef', transferable=True, temp=True) as (hby4, hab4): + icp4 = hab4.makeOwnEvent(sn=0) # Get test_4's inception event to introduce to group members + + hab1 = hby1.habByName("test_1") + hab2 = hby2.habByName("test_2") + hab3 = hby3.habByName("test_3") + # Create member Kevery instances to parse each other's events and update their keystate + kev1 = eventing.Kevery(db=hab1.db, lax=True, local=False) + kev2 = eventing.Kevery(db=hab2.db, lax=True, local=False) + kev3 = eventing.Kevery(db=hab3.db, lax=True, local=False) + kev4 = eventing.Kevery(db=hab4.db, lax=True, local=False) + + # Introduce test_4 member to group by parsing test_4's inception event (latest key state) + parsing.Parser().parse(ims=bytearray(icp4), kvy=kev1) + parsing.Parser().parse(ims=bytearray(icp4), kvy=kev2) + parsing.Parser().parse(ims=bytearray(icp4), kvy=kev3) + # introduce each member to 4 + parsing.Parser().parse(ims=bytearray(hab1.makeOwnEvent(sn=0)), kvy=kev4) + parsing.Parser().parse(ims=bytearray(hab2.makeOwnEvent(sn=0)), kvy=kev4) + parsing.Parser().parse(ims=bytearray(hab3.makeOwnEvent(sn=0)), kvy=kev4) + + # rotate each individual hab to satisfy the rotation threshold with new keys + hab1.rotate() + hab2.rotate() + hab3.rotate() + + # Update keystate in each hab for each other member + rot1 = hab1.makeOwnEvent(sn=1) # get latest event for hab1 and update keystate for other members + parsing.Parser().parse(ims=bytearray(rot1), kvy=kev2) + parsing.Parser().parse(ims=bytearray(rot1), kvy=kev3) + parsing.Parser().parse(ims=bytearray(rot1), kvy=kev4) + + rot2 = hab2.makeOwnEvent(sn=1) # get latest event for hab2 and update keystate for other members + parsing.Parser().parse(ims=bytearray(rot2), kvy=kev1) + parsing.Parser().parse(ims=bytearray(rot2), kvy=kev3) + parsing.Parser().parse(ims=bytearray(rot2), kvy=kev4) + + rot3 = hab3.makeOwnEvent(sn=1) # get latest event for hab3 and update keystate for other members + parsing.Parser().parse(ims=bytearray(rot3), kvy=kev1) + parsing.Parser().parse(ims=bytearray(rot3), kvy=kev2) + parsing.Parser().parse(ims=bytearray(rot3), kvy=kev4) + + # create signing and rotation member AID lists for upcoming rotation + smids = [hab1.pre, hab2.pre, hab3.pre, hab4.pre] + rmids = [hab1.pre, hab2.pre, hab3.pre, hab4.pre] + + # make group hab for test_4 + ghab4 = hby4.joinGroupHab(hab4.pre, group="test_group4", mhab=hab4, smids=smids, rmids=rmids) + + isith = '["1/4", "1/4", "1/4", "1/4"]' + nsith = '["1/4", "1/4", "1/4", "1/4"]' + merfers = [hab1.kever.verfers[0], hab2.kever.verfers[0], hab3.kever.verfers[0], hab4.kever.verfers[0]] + migers = [hab1.kever.ndigers[0], hab2.kever.ndigers[0], hab3.kever.ndigers[0], hab4.kever.ndigers[0]] + rot = ghab1.rotate(smids=smids, rmids=rmids, isith=isith, nsith=nsith, toad=None, cuts=None, adds=None, data=None, + verfers=merfers, digers=migers) + rserder = serdering.SerderKERI(raw=rot) + + # start counselor for group hab 1 + prefixer = coring.Prefixer(qb64=ghab1.pre) + seqner = coring.Seqner(sn=ghab1.kever.sn + 1) + saider = coring.Saider(qb64=rserder.said) + counselor = grouping.Counselor(hby=hby1) + counselor.start(ghab=ghab1, prefixer=prefixer, seqner=seqner, saider=saider) + + # hab2 signs rotation event and parse into hab1 Kevery + sigers2 = hab2.mgr.sign(rserder.raw, verfers=hab2.kever.verfers, indexed=True, indices=[1]) + msg2 = eventing.messagize(serder=rserder, sigers=sigers2) + parsing.Parser().parse(ims=bytearray(msg2), kvy=kev1) + + # hab3 signs rotation event and parse into hab1 Kevery + sigers3 = hab3.mgr.sign(rserder.raw, verfers=hab3.kever.verfers, indexed=True, indices=[2]) + msg3 = eventing.messagize(serder=rserder, sigers=sigers3) + parsing.Parser().parse(ims=bytearray(msg3), kvy=kev1) + + # hab4 signs rotation event and parse into hab1 Kevery. This should commit the event + sigers4 = hab4.mgr.sign(rserder.raw, verfers=hab4.kever.verfers, indexed=True, indices=[3]) + msg4 = eventing.messagize(serder=rserder, sigers=sigers4) + parsing.Parser().parse(ims=bytearray(msg4), kvy=kev1) + + kev1.processEscrows() # Runs escrows for Kevery1 so he processes all sigs together + + counselor.processEscrows() # Get the rest of the way through counselor. + assert counselor.complete(prefixer=prefixer, seqner=seqner, saider=saider) + assert ghab4.smids == smids + assert ghab4.rmids == rmids + hby1.loadHabs() + ghab1 = hby1.habByName("test_group1") # reload hab to get updated smids and rmids values + assert ghab1.smids == smids + assert ghab1.rmids == rmids + def test_multisig_interact(mockHelpingNowUTC): with openMultiSig(prefix="test") as ((hby1, ghab1), (_, _), (_, _)): @@ -681,12 +773,12 @@ def test_multisig_interact(mockHelpingNowUTC): ixn=ixn) assert exn.ked["r"] == '/multisig/ixn' - assert exn.saidb == b'EGQ_DqGlSBx2MKJfHr6liXAngFpQ0UCtV1cdVMUtJHdN' - assert atc == (b'-FABEH__mobl7NDyyQCB1DoLK-OPSueraPtZAlWEjfOYkaba0AAAAAAAAAAAAAAA' - b'AAAAAAAAEH__mobl7NDyyQCB1DoLK-OPSueraPtZAlWEjfOYkaba-AABAAB3yX6b' - b'EXb8N63PKaMaFqijZVT5TqVtoO8q1BFnoJW3rDkNuJ9lEMpEN-44HKGtvniWZ6-d' - b'CVPS4fsEXKZAKGkB-LAa5AACAA-e-ixn-AABAABG58m7gibjdrQ8YU-8WQ8A70nc' - b'tYekYr3xdfZ5WgDQOD0bb9pI7SuuaJvzfAQisLAYQnztA82pAo1Skhf1vQwD') + assert exn.saidb == b'EDF8o6SK-s2jxUVnlGtqAVtXTF-wyZ26c0dUsS5p766q' + assert atc == (b'-FABEH__mobl7NDyyQCB1DoLK-OPSueraPtZAlWEjfOYkaba0AAAAAAAAAAAAAAAAAAAAAAAEH__mobl7NDyyQCB1DoLK' + b'-OPSueraPtZAlWEjfOYkaba' + b'-AABAABFfU5so86inNogCPN7Ko8WXvkMKeiUKPScQ3FYrVmngNpVmW8xmhOTfixuWFlLcQPjEf3bRQhvNvx7azcI_vwB' + b'-LAa5AACAA-e-ixn-AABAABG58m7gibjdrQ8YU' + b'-8WQ8A70nctYekYr3xdfZ5WgDQOD0bb9pI7SuuaJvzfAQisLAYQnztA82pAo1Skhf1vQwD') data = exn.ked["a"] assert data["smids"] == ghab1.smids assert data["gid"] == ghab1.pre @@ -701,12 +793,12 @@ def test_multisig_registry_incept(mockHelpingNowUTC, mockCoringRandomNonce): usage="Issue vLEI Credentials") assert exn.ked["r"] == '/multisig/vcp' - assert exn.saidb == b'ECKiNFo7fpG4vS5tUeja3EvOqT8ctq4AW8E3HKsP7dJo' - assert atc == (b'-FABEH__mobl7NDyyQCB1DoLK-OPSueraPtZAlWEjfOYkaba0AAAAAAAAAAAAAAA' - b'AAAAAAAAEH__mobl7NDyyQCB1DoLK-OPSueraPtZAlWEjfOYkaba-AABAABh6d0m' - b'lebT57L8o2si7DfEvPCoXJP0ekPiBqkzQns3-P7dz36MPXhjNFW6xRRdUstDLAZe' - b'BEqBxBCltMpTZGsD-LAa5AACAA-e-anc-AABAAD2mK9ICW9x1-0NZGkEDOcAbZ58' - b'VWK9LOTwyN2lSfHr2zY638P1SBStoh8mjgy7nOTGMyujOXMKvF_ZDeQ_ISYA') + assert exn.saidb == b'ELTlVFjqhqLkGBqItC9P6RADjranADW8FwD7nnz5ngwO' + assert atc == (b'-FABEH__mobl7NDyyQCB1DoLK-OPSueraPtZAlWEjfOYkaba0AAAAAAAAAAAAAAAAAAAAAAAEH__mobl7NDyyQCB1DoLK' + b'-OPSueraPtZAlWEjfOYkaba' + b'-AABAABY2UZSi_FQRViWfk_wdBmbgPUus1PtJzBPUDpfKEYvhHhsT6IB7z3IswPlrwUc0rTwjN1ON9ssFoTtTlMJqG8K' + b'-LAa5AACAA-e-anc-AABAAD2mK9ICW9x1' + b'-0NZGkEDOcAbZ58VWK9LOTwyN2lSfHr2zY638P1SBStoh8mjgy7nOTGMyujOXMKvF_ZDeQ_ISYA') data = exn.ked["a"] assert data == {'gid': 'EERn_laF0qwP8zTBGL86LbF84J0Yh2IvQSRskH3BZZiy', 'usage': 'Issue vLEI Credentials'} diff --git a/tests/app/test_oobiing.py b/tests/app/test_oobiing.py index fbd92f2f4..4963e1e8a 100644 --- a/tests/app/test_oobiing.py +++ b/tests/app/test_oobiing.py @@ -55,21 +55,21 @@ def test_oobi_share(mockHelpingNowUTC): exn, atc = oobiing.oobiRequestExn(hab=hab, dest="EO2kxXW0jifQmuPevqg6Zpi3vE-WYoj65i_XhpruWtOg", oobi="http://127.0.0.1/oobi") - assert exn.ked == {'a': {'dest': 'EO2kxXW0jifQmuPevqg6Zpi3vE-WYoj65i_XhpruWtOg', - 'oobi': 'http://127.0.0.1/oobi'}, - 'd': 'EMAhEMPbBU2B-Ha-yLxMEZk49KHYkzZgMv9aZS8gDl1m', - 'dt': '2021-01-01T00:00:00.000000+00:00', - 'e': {}, + assert exn.ked == {'v': 'KERI10JSON000136_', 't': 'exn', 'd': 'EII7EvdWFqv0jkjRv10t01zAUcRYbjVhZ_yo3VPZEbpS', 'i': 'EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3', + 'rp': '', 'p': '', - 'q': {}, + 'dt': '2021-01-01T00:00:00.000000+00:00', 'r': '/oobis', - 't': 'exn', - 'v': 'KERI10JSON00012e_'} - assert atc == (b'-FABEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI30AAAAAAAAAAAAAAA' - b'AAAAAAAAEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3-AABAACsgmsu' - b'VJoY5a7vicZQ7pT_MZqCe-0psgReRxyoBfFaAPxZ7Vss2eteFuvwDWBeyKc1B-yc' - b'p-2QZzIZJ94_9hIP') + 'q': {}, + 'a': { + 'dest': 'EO2kxXW0jifQmuPevqg6Zpi3vE-WYoj65i_XhpruWtOg', + 'oobi': 'http://127.0.0.1/oobi'}, + 'e': {} + } + assert atc == (b'-FABEIaGMMWJFPmtXznY1IIiKDIrg' + b'-vIyge6mBl2QV8dDjI30AAAAAAAAAAAAAAAAAAAAAAAEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3' + b'-AABAABdw3eSw_7BW2o3z1ufxxs1CPgX1TgtJzn-MxvMjLYTidUd8KSxNKbPU9M3A4orYJDMGMIzhabHJmKA4ZIGbcgK') def test_oobiery(): diff --git a/tests/core/test_serdering.py b/tests/core/test_serdering.py index fde03b557..f826b9c66 100644 --- a/tests/core/test_serdering.py +++ b/tests/core/test_serdering.py @@ -39,7 +39,7 @@ def test_serder(): 'rpy': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'dt': '', 'r': '', 'a': []}), 'pro': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'dt': '', 'r': '', 'rr': '', 'q': {}}), 'bar': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'dt': '', 'r': '', 'a': []}), - 'exn': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'p': '', 'dt': '', 'r': '', 'q': {}, 'a': [], 'e': {}}), + 'exn': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'rp': '', 'p': '', 'dt': '', 'r': '', 'q': {}, 'a': [], 'e': {}}), 'vcp': Fieldage(saids={'d': 'E', 'i': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'ii': '', 's': '0', 'c': [], 'bt': '0', 'b': [], 'n': ''}), 'vrt': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'p': '', 's': '0', 'bt': '0', 'br': [], 'ba': []}), 'iss': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 's': '0', 'ri': '', 'dt': ''}), @@ -56,7 +56,7 @@ def test_serder(): 'rpy': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'dt': '', 'r': '', 'a': []}), 'pro': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'dt': '', 'r': '', 'rr': '', 'q': {}}), 'bar': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'dt': '', 'r': '', 'a': []}), - 'exn': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'p': '', 'dt': '', 'r': '', 'q': {}, 'a': [], 'e': {}})}}, + 'exn': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'rp': '', 'p': '', 'dt': '', 'r': '', 'q': {}, 'a': [], 'e': {}})}}, 'CREL': {Versionage(major=1, minor=1): {'vcp': Fieldage(saids={'d': 'E', 'i': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'ii': '', 's': '0', 'c': [], 'bt': '0', 'b': [], 'u': ''}), 'vrt': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 'p': '', 's': '0', 'bt': '0', 'br': [], 'ba': []}), 'iss': Fieldage(saids={'d': 'E'}, alls={'v': '', 't': '', 'd': '', 'i': '', 's': '0', 'ri': '', 'dt': ''}), @@ -2087,15 +2087,27 @@ def test_serderkeri_bar(): """End Test""" +def test_serderkeri_exn_old(): + """Test SerderKERI exn msg""" + serder = SerderKERI(raw=b'{"v":"KERI10JSON000088_","t":"exn",' + b'"d":"EMuAoRSE4zREKKYyvuNeYCDM9_MwPQIh1WL0' + b'cFC4e-bU","i":"","p":"","dt":"","r":"","q":{},"a":[],"e":{}}', ilk=kering.Ilks.exn) + + assert serder.verify() # because pre is empty + assert serder.ilk == kering.Ilks.exn + assert serder.pre == '' + assert serder.prior == '' + def test_serderkeri_exn(): """Test SerderKERI exn msg""" # Test KERI JSON with makify defaults for self bootstrap with ilk ixn serder = SerderKERI(makify=True, ilk=kering.Ilks.exn) # make with defaults - assert serder.sad == {'v': 'KERI10JSON000088_', + assert serder.sad == {'v': 'KERI10JSON000090_', 't': 'exn', - 'd': 'EMuAoRSE4zREKKYyvuNeYCDM9_MwPQIh1WL0cFC4e-bU', + 'd': 'EPx9pShQTfv2FoISZJAZ4dlUcekG8-CSkgJh0i0q_iJn', 'i': '', + 'rp': '', 'p': '', 'dt': '', 'r': '', @@ -2103,11 +2115,8 @@ def test_serderkeri_exn(): 'a': [], 'e': {}} - assert serder.raw == (b'{"v":"KERI10JSON000088_","t":"exn",' - b'"d":"EMuAoRSE4zREKKYyvuNeYCDM9_MwPQIh1WL0' - b'cFC4e-bU","i":"","p":"","dt":"","r":"","q":{},"a":[],"e":{}}') - - + assert serder.raw == (b'{"v":"KERI10JSON000090_","t":"exn","d":"EPx9pShQTfv2FoISZJAZ4dlUcekG8-CSkgJh0i0q_iJn",' + b'"i":"","rp":"","p":"","dt":"","r":"","q":{},"a":[],"e":{}}') assert serder.verify() # because pre is empty assert serder.ilk == kering.Ilks.exn @@ -2494,6 +2503,7 @@ def test_serdery_noversion(): test_serderkeri_pro() test_serderkeri_bar() test_serderkeri_exn() + test_serderkeri_exn_old() test_serderkeri_vcp() test_serderacdc() test_serdery() diff --git a/tests/db/test_basing.py b/tests/db/test_basing.py index 87428731e..de76c1ecb 100644 --- a/tests/db/test_basing.py +++ b/tests/db/test_basing.py @@ -11,6 +11,7 @@ import pytest from hio.base import doing +from keri.core.serdering import Serder from tests.app import openMultiSig from keri.kering import Versionage from keri.app import habbing @@ -21,7 +22,7 @@ from keri.core.eventing import incept, rotate, interact, Kever from keri.db import basing from keri.db import dbing -from keri.db.basing import openDB, Baser, KeyStateRecord +from keri.db.basing import openDB, Baser, KeyStateRecord, OobiRecord from keri.db.dbing import (dgKey, onKey, snKey) from keri.db.dbing import openLMDB from keri.help.helping import datify, dictify @@ -2290,6 +2291,76 @@ def test_KERI_BASER_MAP_SIZE_handles_bad_values(caplog): os.environ.pop("KERI_BASER_MAP_SIZE") +def test_clear_escrows(): + with openDB() as db: + key = b'A.a' + vals = [b"z", b"m", b"x", b"a"] + + db.putUres(key, vals) + db.putVres(key, vals) + db.putPses(key, vals) + db.putPwes(key, vals) + db.putUwes(key, vals) + db.putOoes(key, vals) + db.putLdes(key, vals) + db.putQnfs(key, vals) + + preb = 'DAzwEHHzq7K0gzQPYGGwTmuupUhPx5_yZ-Wk1x4ejhcc'.encode("utf-8") + digb = 'EGAPkzNZMtX-QiVgbRbyAIZGoXvbGv9IPb0foWTZvI_4'.encode("utf-8") + key = dgKey(preb, digb) + ssnu1 = b'0AAAAAAAAAAAAAAAAAAAAAAB' + sdig1 = b'EALkveIFUPvt38xhtgYYJRCCpAGO7WjjHVR37Pawv67E' + val1 = ssnu1 + sdig1 + + db.putPde(key, val1) + + pre = 'k' + saider = coring.Saider(qb64b='EGAPkzNZMtX-QiVgbRbyAIZGoXvbGv9IPb0foWTZvI_4') + db.rpes.put(keys=('route',), vals=[saider]) + assert db.rpes.cntAll() == 1 + + db.eoobi.pin(keys=('url',), val=OobiRecord()) + assert db.eoobi.cntAll() == 1 + + db.gpwe.add(keys=(pre,), val=(coring.Seqner(qb64b=b'0AAAAAAAAAAAAAAAAAAAAAAB'), saider)) + assert db.gpwe.cntAll() == 1 + + db.gdee.add(keys=(pre,), val=(coring.Seqner(qb64b=b'0AAAAAAAAAAAAAAAAAAAAAAB'), saider)) + assert db.gdee.cntAll() == 1 + + serder = Serder(raw=b'{"v":"KERI10JSON0000cb_","t":"ixn","d":"EG8WAmM29ZBdoXbnb87yiPxQw4Y7gcQjqZS74vBAKsRm","i":"DApYGFaqnrALTyejaJaGAVhNpSCtqyerPqWVK9ZBNZk0","s":"4","p":"EAskHI462CuIMS_gNkcl_QewzrRSKH2p9zHQIO132Z30","a":[]}') + db.dpwe.pin(keys=(pre, 'said'), val=serder) + assert db.dpwe.cntAll() == 1 + + db.gpse.add(keys=('qb64',), val=(coring.Seqner(qb64b=b'0AAAAAAAAAAAAAAAAAAAAAAB'), saider)) + assert db.gpse.cntAll() == 1 + + db.epse.put(keys=('dig',), val=serder) + assert db.epse.cntAll() == 1 + + db.dune.pin(keys=(pre, 'said'), val=serder) + assert db.dune.cntAll() == 1 + + db.clearEscrows() + + assert db.getUres(key) == [] + assert db.getVres(key) == [] + assert db.getPses(key) == [] + assert db.getPwes(key) == [] + assert db.getUwes(key) == [] + assert db.getOoes(key) == [] + assert db.getLdes(key) == [] + assert db.getQnfs(key) == [] + assert db.getPdes(key) == [] + assert db.rpes.cntAll() == 0 + assert db.eoobi.cntAll() == 0 + assert db.gpwe.cntAll() == 0 + assert db.gdee.cntAll() == 0 + assert db.dpwe.cntAll() == 0 + assert db.gpse.cntAll() == 0 + assert db.epse.cntAll() == 0 + assert db.dune.cntAll() == 0 + if __name__ == "__main__": test_baser() test_clean_baser() diff --git a/tests/db/test_dbing.py b/tests/db/test_dbing.py index c88cca4f8..56c1fa40a 100644 --- a/tests/db/test_dbing.py +++ b/tests/db/test_dbing.py @@ -227,8 +227,15 @@ def test_lmdber(): assert databaser.path == None assert databaser.env == None + os.environ['KERI_LMDB_MAP_SIZE'] = f'{2 * 1024**3}' # 2GB databaser.reopen() assert databaser.opened + assert databaser.env.info()['map_size'] == 2 * 1024**3 # 2GB + + os.environ['KERI_LMDB_MAP_SIZE'] = f'invalid-size' # will trigger default + databaser.reopen() + assert databaser.opened + assert databaser.env.info()['map_size'] == 4 * 1024 ** 3 # 4GB default value assert isinstance(databaser.env, lmdb.Environment) assert databaser.path.endswith("keri/db/main") assert databaser.env.path() == databaser.path diff --git a/tests/end/test_ending.py b/tests/end/test_ending.py index c08a601fe..9b62a0b36 100644 --- a/tests/end/test_ending.py +++ b/tests/end/test_ending.py @@ -434,6 +434,49 @@ def test_get_oobi(): assert serder.ked['t'] == coring.Ilks.icp assert serder.ked['i'] == "EOaICQwhOy3wMwecjAuHQTbv_Cmuu1azTMnHi4QtUmEU" + delname = "delegator" + with habbing.openHby(name=name, base=base, salt=salt) as hby, \ + habbing.openHby(name=delname, base=base, salt=salt) as delhby: + delhab = delhby.makeHab(name=delname) + hab = hby.makeHab(name=name, delpre=delhab.pre) + + assert hab.pre == "EPERMS4wKU7ejhCdhI2qQR8snEx1cislR9C9bSEs0kS5" + assert hab.kever.delegator == delhab.pre + + msgs.extend(hab.makeEndRole(eid=hab.pre, + role=kering.Roles.controller, + stamp=help.nowIso8601())) + + msgs.extend(hab.makeLocScheme(url='http://127.0.0.1:5555', + scheme=kering.Schemes.http, + stamp=help.nowIso8601())) + hab.psr.parse(ims=msgs) + + # must do it here to inject into Falcon endpoint resource instances + tymist = tyming.Tymist(tyme=0.0) + + app = falcon.App() # falcon.App instances are callable WSGI apps + ending.loadEnds(app, tymth=tymist.tymen(), hby=hby, default=hab.pre) + + client = testing.TestClient(app=app) + + # This should fail with 404 because we haven't been approved yet so we don't exist + rep = client.simulate_get('/oobi', ) + assert rep.status == falcon.HTTP_NOT_FOUND + + # Approve the delegation manually + delhab.interact(data=[dict(i=hab.pre, s="0", d=hab.pre)]) + for msg in delhab.db.clonePreIter(pre=delhab.pre, fn=0): + hab.psr.parse(ims=msg) + + rep = client.simulate_get('/oobi', ) + assert rep.status == falcon.HTTP_OK + + # We'll get the delegator first + serder = serdering.SerderKERI(raw=rep.text.encode("utf-8")) + assert serder.ked['t'] == coring.Ilks.icp + assert serder.ked['i'] == "EKL3to0Q059vtxKi7wWmaNFJ3NKE1nQsOPasRXqPzpjS" + """Done Test""" diff --git a/tests/help/test_ogling.py b/tests/help/test_ogling.py index 78cd3777d..55ff5521c 100644 --- a/tests/help/test_ogling.py +++ b/tests/help/test_ogling.py @@ -8,7 +8,7 @@ import os import logging -from hio.help import ogling +from keri.help import ogling from keri import help diff --git a/tests/peer/test_exchanging.py b/tests/peer/test_exchanging.py index 378f08acb..3c555bb12 100644 --- a/tests/peer/test_exchanging.py +++ b/tests/peer/test_exchanging.py @@ -98,26 +98,24 @@ def test_hab_exchange(mockHelpingNowUTC): data = dict(m="Let's create a registry") msg = hab.exchange(route="/multisig/registry/incept", recipient="", payload=data, embeds=embeds) - assert msg == (b'{"v":"KERI10JSON000398_","t":"exn","d":"ECcmfGnlqnc5-1_oXNpbfowv' - b'RsEa-V8tfeKmQDRJJ50i","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2Q' - b'V8dDjI3","p":"","dt":"2021-01-01T00:00:00.000000+00:00","r":"/mu' - b'ltisig/registry/incept","q":{},"a":{"i":"","m":"Let\'s create a r' - b'egistry"},"e":{"vcp":{"v":"KERI10JSON00010f_","t":"vcp","d":"EI6' - b'hBlgkWoJgkZyfLW35_UyM4nIK44OgsSwFR_WOfvVB","i":"EI6hBlgkWoJgkZyf' - b'LW35_UyM4nIK44OgsSwFR_WOfvVB","ii":"EIaGMMWJFPmtXznY1IIiKDIrg-vI' - b'yge6mBl2QV8dDjI3","s":"0","c":[],"bt":"0","b":[],"n":"AH3-1EZWXU' - b'9I0fv3Iz_9ZIhjj13JO7u4GNFYC3-l8_K-"},"ixn":{"v":"KERI10JSON00013' - b'8_","t":"ixn","d":"EFuFnevyDFfpWG6il-6Qcv0ne0ZIItLwanCwI-SU8A9j"' - b',"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","s":"1","p":' - b'"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","a":[{"i":"EI6hBl' - b'gkWoJgkZyfLW35_UyM4nIK44OgsSwFR_WOfvVB","s":0,"d":"EI6hBlgkWoJgk' - b'ZyfLW35_UyM4nIK44OgsSwFR_WOfvVB"}]},"d":"EL5Nkm6T7HG_0GW6uwqYSZw' - b'lH23khtXvsVE-dq8eO_eE"}}-FABEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2' - b'QV8dDjI30AAAAAAAAAAAAAAAAAAAAAAAEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6' - b'mBl2QV8dDjI3-AABAACahD6g7IwjUyQRyGUPGLvlr5-DsvLxeJtCUVIIECYfAQ_q' - b'p3Z2pe__HRqIl-NrUv85oQrZBm0kpKn8LBQtQfkO-LAa5AACAA-e-ixn-AABAADp' - b'rTWp4llIzVzBM7VVsDOgXVJdoiVXutsWJEbDJ2pMdjXjNi1xKALBSZ1ZgRoUsD--' - b'LgUQkXIdjLoQ19XPvJMJ') + assert msg == (b'{"v":"KERI10JSON0003a0_","t":"exn","d":"ELkHqph-Tj4LGHYfFfoVmJJo09S2gp6ci8rK96upIAKE",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","rp":"","p":"",' + b'"dt":"2021-01-01T00:00:00.000000+00:00","r":"/multisig/registry/incept","q":{},"a":{"i":"",' + b'"m":"Let\'s create a registry"},"e":{"vcp":{"v":"KERI10JSON00010f_","t":"vcp",' + b'"d":"EI6hBlgkWoJgkZyfLW35_UyM4nIK44OgsSwFR_WOfvVB",' + b'"i":"EI6hBlgkWoJgkZyfLW35_UyM4nIK44OgsSwFR_WOfvVB",' + b'"ii":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","s":"0","c":[],"bt":"0","b":[],' + b'"n":"AH3-1EZWXU9I0fv3Iz_9ZIhjj13JO7u4GNFYC3-l8_K-"},"ixn":{"v":"KERI10JSON000138_",' + b'"t":"ixn","d":"EFuFnevyDFfpWG6il-6Qcv0ne0ZIItLwanCwI-SU8A9j",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","s":"1",' + b'"p":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3",' + b'"a":[{"i":"EI6hBlgkWoJgkZyfLW35_UyM4nIK44OgsSwFR_WOfvVB","s":0,' + b'"d":"EI6hBlgkWoJgkZyfLW35_UyM4nIK44OgsSwFR_WOfvVB"}]},' + b'"d":"EL5Nkm6T7HG_0GW6uwqYSZwlH23khtXvsVE-dq8eO_eE"}}-FABEIaGMMWJFPmtXznY1IIiKDIrg' + b'-vIyge6mBl2QV8dDjI30AAAAAAAAAAAAAAAAAAAAAAAEIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3' + b'-AABAAB-teJc_7zot5TAZT6lQi2-GlBzMHXICvt3tIYoPo2gYXF7PpWDozo3y3wVW9mgHln-1DvQlqn9Aip1YnBgKUQB' + b'-LAa5AACAA-e-ixn-AABAADprTWp4llIzVzBM7VVsDOgXVJdoiVXutsWJEbDJ2pMdjXjNi1xKALBSZ1ZgRoUsD' + b'--LgUQkXIdjLoQ19XPvJMJ') exn = serdering.SerderKERI(raw=msg) @@ -140,27 +138,25 @@ def test_hab_exchange(mockHelpingNowUTC): data = dict(m="Lets create this registry instead") msg = hab2.exchange(route="/multisig/registry/incept", payload=data, recipient="", dig=exn.said, embeds=embeds) - assert msg == (b'{"v":"KERI10JSON0003ce_","t":"exn","d":"EEMxkjO9HzZoekfzmjrkE19y' - b'pU259apUWuY7alFu_GmE","i":"EIREQlatUJODbKogZfa3IqXZ90XdZA0qJMVli' - b'I61Bcc2","p":"ECcmfGnlqnc5-1_oXNpbfowvRsEa-V8tfeKmQDRJJ50i","dt"' - b':"2021-01-01T00:00:00.000000+00:00","r":"/multisig/registry/ince' - b'pt","q":{},"a":{"i":"","m":"Lets create this registry instead"},' - b'"e":{"vcp":{"v":"KERI10JSON00010f_","t":"vcp","d":"EB5mts6qrWOZr' - b'xjma6lSTjAdPZ0NSHM1HC3IndbS_giB","i":"EB5mts6qrWOZrxjma6lSTjAdPZ' - b'0NSHM1HC3IndbS_giB","ii":"EIREQlatUJODbKogZfa3IqXZ90XdZA0qJMVliI' - b'61Bcc2","s":"0","c":[],"bt":"0","b":[],"n":"AH3-1EZWXU9I0fv3Iz_9' - b'ZIhjj13JO7u4GNFYC3-l8_K-"},"ixn":{"v":"KERI10JSON000138_","t":"i' - b'xn","d":"EOek9JVKNeuW-5UNeHYCTDe70_GtvRwP672oWMNBJpA5","i":"EIRE' - b'QlatUJODbKogZfa3IqXZ90XdZA0qJMVliI61Bcc2","s":"1","p":"EIREQlatU' - b'JODbKogZfa3IqXZ90XdZA0qJMVliI61Bcc2","a":[{"i":"EB5mts6qrWOZrxjm' - b'a6lSTjAdPZ0NSHM1HC3IndbS_giB","s":0,"d":"EB5mts6qrWOZrxjma6lSTjA' - b'dPZ0NSHM1HC3IndbS_giB"}]},"d":"EM3gLTzQ9GmKd50Rlm_kiIkeYkxb004eo' - b'OsWahz70TqJ"}}-FABEIREQlatUJODbKogZfa3IqXZ90XdZA0qJMVliI61Bcc20A' - b'AAAAAAAAAAAAAAAAAAAAAAEIREQlatUJODbKogZfa3IqXZ90XdZA0qJMVliI61Bc' - b'c2-AABAAAxpwQLr9-D7hOZYHvvDB_ffo5sRgBf0NufowF0g_YMI1wdnttlYA2o_d' - b'wtK_WNbfh_iAytFw9nHZziCED13AwH-LAa5AACAA-e-ixn-AABAACaoxfQp5L_Gd' - b'0nKqJXMbLTXzkrJJDd8RFxWdTSesAMydUzmJQlGt0T9h8L7SwIrq8yBinj990PLJ' - b'Hl7sXmq04I') + assert msg == (b'{"v":"KERI10JSON0003d6_","t":"exn","d":"EPO_XC9nwSixqSoOvsHymFr-l3udclHBdOh4OUEqZ33P",' + b'"i":"EIREQlatUJODbKogZfa3IqXZ90XdZA0qJMVliI61Bcc2","rp":"",' + b'"p":"ELkHqph-Tj4LGHYfFfoVmJJo09S2gp6ci8rK96upIAKE","dt":"2021-01-01T00:00:00.000000+00:00",' + b'"r":"/multisig/registry/incept","q":{},"a":{"i":"","m":"Lets create this registry instead"},' + b'"e":{"vcp":{"v":"KERI10JSON00010f_","t":"vcp",' + b'"d":"EB5mts6qrWOZrxjma6lSTjAdPZ0NSHM1HC3IndbS_giB",' + b'"i":"EB5mts6qrWOZrxjma6lSTjAdPZ0NSHM1HC3IndbS_giB",' + b'"ii":"EIREQlatUJODbKogZfa3IqXZ90XdZA0qJMVliI61Bcc2","s":"0","c":[],"bt":"0","b":[],' + b'"n":"AH3-1EZWXU9I0fv3Iz_9ZIhjj13JO7u4GNFYC3-l8_K-"},"ixn":{"v":"KERI10JSON000138_",' + b'"t":"ixn","d":"EOek9JVKNeuW-5UNeHYCTDe70_GtvRwP672oWMNBJpA5",' + b'"i":"EIREQlatUJODbKogZfa3IqXZ90XdZA0qJMVliI61Bcc2","s":"1",' + b'"p":"EIREQlatUJODbKogZfa3IqXZ90XdZA0qJMVliI61Bcc2",' + b'"a":[{"i":"EB5mts6qrWOZrxjma6lSTjAdPZ0NSHM1HC3IndbS_giB","s":0,' + b'"d":"EB5mts6qrWOZrxjma6lSTjAdPZ0NSHM1HC3IndbS_giB"}]},' + b'"d":"EM3gLTzQ9GmKd50Rlm_kiIkeYkxb004eoOsWahz70TqJ' + b'"}}-FABEIREQlatUJODbKogZfa3IqXZ90XdZA0qJMVliI61Bcc20AAAAAAAAAAAAAAAAAAAAAAAEIREQlatUJODbKogZ' + b'fa3IqXZ90XdZA0qJMVliI61Bcc2-AABAADY5nUsBgL23ulcrTgkV09hSzktNHZSlEH1zmVpEggrGgQUq0tLQeOXztUFD' + b'xNQ4Kq2ddIYDVz6d_y0kkU3__YJ-LAa5AACAA-e-ixn-AABAACaoxfQp5L_Gd0nKqJXMbLTXzkrJJDd8RFxWdTSesAMy' + b'dUzmJQlGt0T9h8L7SwIrq8yBinj990PLJHl7sXmq04I') # Test exn from non-transferable AID hab = hby.makeHab(name="test1", transferable=False) @@ -171,18 +167,16 @@ def test_hab_exchange(mockHelpingNowUTC): ) msg = hab.exchange(route="/multisig/registry/incept", payload=data, embeds=embeds, recipient="") - assert msg == (b'{"v":"KERI10JSON000263_","t":"exn","d":"ENRFAVDU_ZbcVpx6l6lrC5Mu' - b'UqHXfT3N9VjUkvU4t29S","i":"BJZ_LF61JTCCSCIw2Q4ozE2MsbRC4m-N6-tFV' - b'lCeiZPG","p":"","dt":"2021-01-01T00:00:00.000000+00:00","r":"/mu' - b'ltisig/registry/incept","q":{},"a":{"i":"","m":"Lets create this' - b' registry instead"},"e":{"vcp":{"v":"KERI10JSON00010f_","t":"vcp' - b'","d":"EB5mts6qrWOZrxjma6lSTjAdPZ0NSHM1HC3IndbS_giB","i":"EB5mts' - b'6qrWOZrxjma6lSTjAdPZ0NSHM1HC3IndbS_giB","ii":"EIREQlatUJODbKogZf' - b'a3IqXZ90XdZA0qJMVliI61Bcc2","s":"0","c":[],"bt":"0","b":[],"n":"' - b'AH3-1EZWXU9I0fv3Iz_9ZIhjj13JO7u4GNFYC3-l8_K-"},"d":"ENC6w8wUj-Gp' - b'_RpAJN5q4Lf00IHstzNLUvkh3ZvgHGP_"}}-CABBJZ_LF61JTCCSCIw2Q4ozE2Ms' - b'bRC4m-N6-tFVlCeiZPG0BCxLApuSnk1MF9IUq1RJNjVmr6s-fLwvP6aAPa0ag34t' - b'4G7EKKk-UFwy74-0StSlHcS8KBkN5ZbtuHvV9tXRqUJ-LAl5AACAA-e-vcp-CABB' - b'JZ_LF61JTCCSCIw2Q4ozE2MsbRC4m-N6-tFVlCeiZPG0BDjOC4j0Co6P0giMylR4' - b'7149eJ8Yf_hO-32_TpY77KMVCWCf0U8GuZPIN76R2zsyT_eARvS_zQsX1ebjl3PM' - b'P0D') + assert msg == (b'{"v":"KERI10JSON00026b_","t":"exn","d":"EMBm0p7fCIqJrP4Z-PBI-yEvXin_-eY1dU4XTCM9ykRC",' + b'"i":"BJZ_LF61JTCCSCIw2Q4ozE2MsbRC4m-N6-tFVlCeiZPG","rp":"","p":"",' + b'"dt":"2021-01-01T00:00:00.000000+00:00","r":"/multisig/registry/incept","q":{},"a":{"i":"",' + b'"m":"Lets create this registry instead"},"e":{"vcp":{"v":"KERI10JSON00010f_","t":"vcp",' + b'"d":"EB5mts6qrWOZrxjma6lSTjAdPZ0NSHM1HC3IndbS_giB",' + b'"i":"EB5mts6qrWOZrxjma6lSTjAdPZ0NSHM1HC3IndbS_giB",' + b'"ii":"EIREQlatUJODbKogZfa3IqXZ90XdZA0qJMVliI61Bcc2","s":"0","c":[],"bt":"0","b":[],' + b'"n":"AH3-1EZWXU9I0fv3Iz_9ZIhjj13JO7u4GNFYC3-l8_K-"},' + b'"d":"ENC6w8wUj-Gp_RpAJN5q4Lf00IHstzNLUvkh3ZvgHGP_"}}-CABBJZ_LF61JTCCSCIw2Q4ozE2MsbRC4m-N6' + b'-tFVlCeiZPG0BB-sQs0WS9wsyuT4hXQD7rbczSfpnQz21wZGYucRkE0ynKy5draELEKBsckeD0Im1i' + b'-kIfMEdbY08YqVfSrEoAA-LAl5AACAA-e-vcp-CABBJZ_LF61JTCCSCIw2Q4ozE2MsbRC4m-N6' + b'-tFVlCeiZPG0BDjOC4j0Co6P0giMylR47149eJ8Yf_hO' + b'-32_TpY77KMVCWCf0U8GuZPIN76R2zsyT_eARvS_zQsX1ebjl3PMP0D') diff --git a/tests/vc/test_protocoling.py b/tests/vc/test_protocoling.py index befb8dcbb..cff1ae6d0 100644 --- a/tests/vc/test_protocoling.py +++ b/tests/vc/test_protocoling.py @@ -115,26 +115,29 @@ def test_ipex(seeder, mockCoringRandomNonce, mockHelpingNowIso8601, mockHelpingN apply0, apply0atc = protocoling.ipexApplyExn(sidHab, message="Please give me a credential", schema=schema, recp=redPre, attrs={}) - assert apply0.raw == (b'{"v":"KERI10JSON00016d_","t":"exn","d":"EI1MnUrT0aUprMN97FabgJdxVQtoCPqamVUp' - b'3iFgnDBE","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","p":"","dt":"20' - b'21-06-27T21:26:21.233257+00:00","r":"/ipex/apply","q":{},"a":{"m":"Please gi' - b've me a credential","s":"EMQWEcCnVRk1hatTNyK3sIykYSrrFvafX3bHQ9Gkk1kC","a":{' - b'},"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3"},"e":{}}') + assert apply0.raw == (b'{"v":"KERI10JSON000175_","t":"exn","d":"EK9Q7n1y-Rlf-A7-T0uWSKuOlHZHBy_4zVaNp60GxXEr",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","rp":"","p":"",' + b'"dt":"2021-06-27T21:26:21.233257+00:00","r":"/ipex/apply","q":{},"a":{"m":"Please ' + b'give me a credential","s":"EMQWEcCnVRk1hatTNyK3sIykYSrrFvafX3bHQ9Gkk1kC","a":{},' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3"},"e":{}}') # No requirements for apply, except that its first, no `p` assert ipexhan.verify(serder=apply0) is True offer0, offer0atc = protocoling.ipexOfferExn(sidHab, "How about this", acdc=creder.raw, apply=apply0) - assert offer0.raw == (b'{"v":"KERI10JSON0002f0_","t":"exn","d":"EO_wiH5ZEikfLQb8rKBjPATnjiSOHGBvvN3m' - b'F0LDvaIC","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","p":"EI1MnUrT0a' - b'UprMN97FabgJdxVQtoCPqamVUp3iFgnDBE","dt":"2021-06-27T21:26:21.233257+00:00",' - b'"r":"/ipex/offer","q":{},"a":{"m":"How about this"},"e":{"acdc":{"v":"ACDC10' - b'JSON000197_","d":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG","i":"EIaGMMW' - b'JFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGs' - b'jaXFOaO_kCfS4","s":"EMQWEcCnVRk1hatTNyK3sIykYSrrFvafX3bHQ9Gkk1kC","a":{"d":"' - b'EF2__B6DiLQHpdJZ_C0bddxy2o6nXIHEwchO9yylr3xx","dt":"2021-06-27T21:26:21.2332' - b'57+00:00","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","LEI":"254900OP' - b'PU84GM83MG36"}},"d":"EOVRKHUAEjvfyWzQ8IL4icBiaVuy_CSTse_W_AssaAeE"}}') + assert offer0.raw == (b'{"v":"KERI10JSON0002f8_","t":"exn","d":"ED-uXbt7hRH3cmQY9vtwmcPOGvdmPEq_bnQ4sgQK9KhB",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","rp":"",' + b'"p":"EK9Q7n1y-Rlf-A7-T0uWSKuOlHZHBy_4zVaNp60GxXEr",' + b'"dt":"2021-06-27T21:26:21.233257+00:00","r":"/ipex/offer","q":{},"a":{"m":"How about ' + b'this"},"e":{"acdc":{"v":"ACDC10JSON000197_",' + b'"d":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3",' + b'"ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGsjaXFOaO_kCfS4",' + b'"s":"EMQWEcCnVRk1hatTNyK3sIykYSrrFvafX3bHQ9Gkk1kC",' + b'"a":{"d":"EF2__B6DiLQHpdJZ_C0bddxy2o6nXIHEwchO9yylr3xx",' + b'"dt":"2021-06-27T21:26:21.233257+00:00",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","LEI":"254900OPPU84GM83MG36"}},' + b'"d":"EOVRKHUAEjvfyWzQ8IL4icBiaVuy_CSTse_W_AssaAeE"}}') # This should fail because it is not first and the apply isn't persisted yet assert ipexhan.verify(serder=offer0) is False @@ -161,26 +164,29 @@ def test_ipex(seeder, mockCoringRandomNonce, mockHelpingNowIso8601, mockHelpingN # Let's see if we can spurn a message we previously accepted. spurn0, spurn0atc = protocoling.ipexSpurnExn(sidHab, "I reject you", spurned=apply0) - assert spurn0.raw == (b'{"v":"KERI10JSON00011d_","t":"exn","d":"EKvtmxPkOklgRNgWxLj-1ZW4Zb0MwZIUloWx' - b'A_dam95r","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","p":"EI1MnUrT0a' - b'UprMN97FabgJdxVQtoCPqamVUp3iFgnDBE","dt":"2021-06-27T21:26:21.233257+00:00",' - b'"r":"/ipex/spurn","q":{},"a":{"m":"I reject you"},"e":{}}') + assert spurn0.raw == (b'{"v":"KERI10JSON000125_","t":"exn","d":"EP9mGfCLrVs-A2KiSwruQ8bdQVlPIiIFZ2t4f6Dj4gnc",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","rp":"",' + b'"p":"EK9Q7n1y-Rlf-A7-T0uWSKuOlHZHBy_4zVaNp60GxXEr",' + b'"dt":"2021-06-27T21:26:21.233257+00:00","r":"/ipex/spurn","q":{},"a":{"m":"I reject ' + b'you"},"e":{}}') # This will fail, we've already responded with an offer assert ipexhan.verify(spurn0) is False # Now lets try an offer without a pointer back to a reply offer1, offer1atc = protocoling.ipexOfferExn(sidHab, "Here a credential offer", acdc=creder.raw) - assert offer1.raw == (b'{"v":"KERI10JSON0002cd_","t":"exn","d":"EMEmoi4k9gxWu4uZyYuEK3MvFPn-5B0LHnNx' - b'uQ4vRqRA","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","p":"","dt":"20' - b'21-06-27T21:26:21.233257+00:00","r":"/ipex/offer","q":{},"a":{"m":"Here a cr' - b'edential offer"},"e":{"acdc":{"v":"ACDC10JSON000197_","d":"EDkftEwWBpohjTpem' - b'h_6xkaGNuoDsRU3qwvHdlvgfOyG","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDj' - b'I3","ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGsjaXFOaO_kCfS4","s":"EMQWEcCnVRk1hat' - b'TNyK3sIykYSrrFvafX3bHQ9Gkk1kC","a":{"d":"EF2__B6DiLQHpdJZ_C0bddxy2o6nXIHEwch' - b'O9yylr3xx","dt":"2021-06-27T21:26:21.233257+00:00","i":"EIaGMMWJFPmtXznY1IIi' - b'KDIrg-vIyge6mBl2QV8dDjI3","LEI":"254900OPPU84GM83MG36"}},"d":"EOVRKHUAEjvfyW' - b'zQ8IL4icBiaVuy_CSTse_W_AssaAeE"}}') + assert offer1.raw == (b'{"v":"KERI10JSON0002d5_","t":"exn","d":"EJC9_GH0TeYJ3_cyutkS1gZfcgaGUQnk3v7F_gwJShVM",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","rp":"","p":"",' + b'"dt":"2021-06-27T21:26:21.233257+00:00","r":"/ipex/offer","q":{},"a":{"m":"Here a ' + b'credential offer"},"e":{"acdc":{"v":"ACDC10JSON000197_",' + b'"d":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3",' + b'"ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGsjaXFOaO_kCfS4",' + b'"s":"EMQWEcCnVRk1hatTNyK3sIykYSrrFvafX3bHQ9Gkk1kC",' + b'"a":{"d":"EF2__B6DiLQHpdJZ_C0bddxy2o6nXIHEwchO9yylr3xx",' + b'"dt":"2021-06-27T21:26:21.233257+00:00",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","LEI":"254900OPPU84GM83MG36"}},' + b'"d":"EOVRKHUAEjvfyWzQ8IL4icBiaVuy_CSTse_W_AssaAeE"}}') # Will work because it is starting a new conversation assert ipexhan.verify(serder=offer1) is True @@ -192,10 +198,11 @@ def test_ipex(seeder, mockCoringRandomNonce, mockHelpingNowIso8601, mockHelpingN assert serder.ked == offer1.ked agree, argeeAtc = protocoling.ipexAgreeExn(sidHab, "I'll accept that offer", offer=offer0) - assert agree.raw == (b'{"v":"KERI10JSON000127_","t":"exn","d":"EGpJ9S0TqIVHkRmDsbgP59NC8ZLCaSUirslB' - b'KDeYKOR7","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","p":"EO_wiH5ZEi' - b'kfLQb8rKBjPATnjiSOHGBvvN3mF0LDvaIC","dt":"2021-06-27T21:26:21.233257+00:00",' - b'"r":"/ipex/agree","q":{},"a":{"m":"I\'ll accept that offer"},"e":{}}') + assert agree.raw == (b'{"v":"KERI10JSON00012f_","t":"exn","d":"ELLFpKUv8qt6UKaNFj2_s-3Hs1vFeRgWdq_LIQm2HEER",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","rp":"",' + b'"p":"ED-uXbt7hRH3cmQY9vtwmcPOGvdmPEq_bnQ4sgQK9KhB",' + b'"dt":"2021-06-27T21:26:21.233257+00:00","r":"/ipex/agree","q":{},"a":{"m":"I\'ll ' + b'accept that offer"},"e":{}}') # Can not create an agree without an offer, so this will pass since it has an offer that has no response assert ipexhan.verify(serder=agree) is True @@ -210,24 +217,28 @@ def test_ipex(seeder, mockCoringRandomNonce, mockHelpingNowIso8601, mockHelpingN anc = sidHab.makeOwnEvent(sn=2) grant0, grant0atc = protocoling.ipexGrantExn(sidHab, message="Here's a credential", recp=sidHab.pre, acdc=msg, iss=iss.raw, anc=anc) - assert grant0.raw == (b'{"v":"KERI10JSON000531_","t":"exn","d":"EJxM3em5fSpAIQsyXYovrr0UjblWLtmbTnFp' - b'xAUqnwG-","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","p":"","dt":"20' - b'21-06-27T21:26:21.233257+00:00","r":"/ipex/grant","q":{},"a":{"m":"Here\'' - b's a credential","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3"},"e":{"ac' - b'dc":{"v":"ACDC10JSON000197_","d":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfO' - b'yG","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","ri":"EO0_SyqPS1-EVYS' - b'ITakYpUHaUZZpZGsjaXFOaO_kCfS4","s":"EMQWEcCnVRk1hatTNyK3sIykYSrrFvafX3bHQ9Gk' - b'k1kC","a":{"d":"EF2__B6DiLQHpdJZ_C0bddxy2o6nXIHEwchO9yylr3xx","dt":"2021-06-' - b'27T21:26:21.233257+00:00","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3"' - b',"LEI":"254900OPPU84GM83MG36"}},"iss":{"v":"KERI10JSON0000ed_","t":"iss","d"' - b':"EK2WxcpF3oL1yqS3Z8i08WDYkHDcYhJL9afqdCIZjMy3","i":"EDkftEwWBpohjTpemh_6xka' - b'GNuoDsRU3qwvHdlvgfOyG","s":"0","ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGsjaXFOaO_' - b'kCfS4","dt":"2021-06-27T21:26:21.233257+00:00"},"anc":{"v":"KERI10JSON00013a' - b'_","t":"ixn","d":"EOjAxp-AMLzicGz2h-DxvMK9kicajpZEwdN8-8k54hvz","i":"EIaGMMW' - b'JFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","s":"2","p":"EGKglEgIpdHuhuwl-IiSDG9x' - b'094gMrRxVaXGgXvCzCYM","a":[{"i":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOy' - b'G","s":"0","d":"EK2WxcpF3oL1yqS3Z8i08WDYkHDcYhJL9afqdCIZjMy3"}]},"d":"EI5mZX' - b'Z84Su4DrEUOxtl-NaUURQtTJeAn12xf146beg3"}}') + assert grant0.raw == (b'{"v":"KERI10JSON000539_","t":"exn","d":"EBXaaGfKREvo3UbvNEtgTiZySHe71tTU4-ZmcFETVdn8",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","rp":"","p":"",' + b'"dt":"2021-06-27T21:26:21.233257+00:00","r":"/ipex/grant","q":{},"a":{"m":"Here\'s a ' + b'credential","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3"},"e":{"acdc":{' + b'"v":"ACDC10JSON000197_","d":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3",' + b'"ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGsjaXFOaO_kCfS4",' + b'"s":"EMQWEcCnVRk1hatTNyK3sIykYSrrFvafX3bHQ9Gkk1kC",' + b'"a":{"d":"EF2__B6DiLQHpdJZ_C0bddxy2o6nXIHEwchO9yylr3xx",' + b'"dt":"2021-06-27T21:26:21.233257+00:00",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","LEI":"254900OPPU84GM83MG36"}},' + b'"iss":{"v":"KERI10JSON0000ed_","t":"iss",' + b'"d":"EK2WxcpF3oL1yqS3Z8i08WDYkHDcYhJL9afqdCIZjMy3",' + b'"i":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG","s":"0",' + b'"ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGsjaXFOaO_kCfS4",' + b'"dt":"2021-06-27T21:26:21.233257+00:00"},"anc":{"v":"KERI10JSON00013a_","t":"ixn",' + b'"d":"EOjAxp-AMLzicGz2h-DxvMK9kicajpZEwdN8-8k54hvz",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","s":"2",' + b'"p":"EGKglEgIpdHuhuwl-IiSDG9x094gMrRxVaXGgXvCzCYM",' + b'"a":[{"i":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG","s":"0",' + b'"d":"EK2WxcpF3oL1yqS3Z8i08WDYkHDcYhJL9afqdCIZjMy3"}]},' + b'"d":"EI5mZXZ84Su4DrEUOxtl-NaUURQtTJeAn12xf146beg3"}}') assert ipexhan.verify(serder=grant0) is True @@ -240,10 +251,11 @@ def test_ipex(seeder, mockCoringRandomNonce, mockHelpingNowIso8601, mockHelpingN # Let's see if we can spurn a message we previously accepted. spurn1, spurn1atc = protocoling.ipexSpurnExn(sidHab, "I reject you", spurned=grant0) - assert spurn1.raw == (b'{"v":"KERI10JSON00011d_","t":"exn","d":"EEs0bIGplWsjSOw5BMhAdFmgv-jm3-4nPgcK' - b'-LDv8tdB","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","p":"EJxM3em5fS' - b'pAIQsyXYovrr0UjblWLtmbTnFpxAUqnwG-","dt":"2021-06-27T21:26:21.233257+00:00",' - b'"r":"/ipex/spurn","q":{},"a":{"m":"I reject you"},"e":{}}') + assert spurn1.raw == (b'{"v":"KERI10JSON000125_","t":"exn","d":"EIoMDwEvyR4j43W5Hh9CyJ4ttwNHlrmABwyUvKHhF9mp",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","rp":"",' + b'"p":"EBXaaGfKREvo3UbvNEtgTiZySHe71tTU4-ZmcFETVdn8",' + b'"dt":"2021-06-27T21:26:21.233257+00:00","r":"/ipex/spurn","q":{},"a":{"m":"I reject ' + b'you"},"e":{}}') smsg = bytearray(spurn1.raw) smsg.extend(spurn1atc) parsing.Parser().parse(ims=smsg, exc=sidExc) @@ -253,25 +265,29 @@ def test_ipex(seeder, mockCoringRandomNonce, mockHelpingNowIso8601, mockHelpingN # Now we'll run a grant pointing back to the agree all the way to the database grant1, grant1atc = protocoling.ipexGrantExn(sidHab, message="Here's a credential", acdc=msg, iss=iss.raw, recp=sidHab.pre, anc=anc, agree=agree) - assert grant1.raw == (b'{"v":"KERI10JSON00055d_","t":"exn","d":"EIqh-L9GnnVSdNLeqwmx-vpE9V1DvOQAlVWf' - b'wENpm8sW","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","p":"EGpJ9S0TqI' - b'VHkRmDsbgP59NC8ZLCaSUirslBKDeYKOR7","dt":"2021-06-27T21:26:21.233257+00:00",' - b'"r":"/ipex/grant","q":{},"a":{"m":"Here\'s a credential","i":"EIaGMMWJFPm' - b'tXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3"},"e":{"acdc":{"v":"ACDC10JSON000197_","d"' - b':"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG","i":"EIaGMMWJFPmtXznY1IIiKDI' - b'rg-vIyge6mBl2QV8dDjI3","ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGsjaXFOaO_kCfS4","' - b's":"EMQWEcCnVRk1hatTNyK3sIykYSrrFvafX3bHQ9Gkk1kC","a":{"d":"EF2__B6DiLQHpdJZ' - b'_C0bddxy2o6nXIHEwchO9yylr3xx","dt":"2021-06-27T21:26:21.233257+00:00","i":"E' - b'IaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","LEI":"254900OPPU84GM83MG36"}},' - b'"iss":{"v":"KERI10JSON0000ed_","t":"iss","d":"EK2WxcpF3oL1yqS3Z8i08WDYkHDcYh' - b'JL9afqdCIZjMy3","i":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG","s":"0","' - b'ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGsjaXFOaO_kCfS4","dt":"2021-06-27T21:26:21' - b'.233257+00:00"},"anc":{"v":"KERI10JSON00013a_","t":"ixn","d":"EOjAxp-AMLzicG' - b'z2h-DxvMK9kicajpZEwdN8-8k54hvz","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8' - b'dDjI3","s":"2","p":"EGKglEgIpdHuhuwl-IiSDG9x094gMrRxVaXGgXvCzCYM","a":[{"i":' - b'"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG","s":"0","d":"EK2WxcpF3oL1yqS3' - b'Z8i08WDYkHDcYhJL9afqdCIZjMy3"}]},"d":"EI5mZXZ84Su4DrEUOxtl-NaUURQtTJeAn12xf1' - b'46beg3"}}') + assert grant1.raw == (b'{"v":"KERI10JSON000565_","t":"exn","d":"ENy1ktZHowD73mn0vJL-xpTzCDpa4RuISZldAZImiKD_",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","rp":"",' + b'"p":"ELLFpKUv8qt6UKaNFj2_s-3Hs1vFeRgWdq_LIQm2HEER",' + b'"dt":"2021-06-27T21:26:21.233257+00:00","r":"/ipex/grant","q":{},"a":{"m":"Here\'s a ' + b'credential","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3"},"e":{"acdc":{' + b'"v":"ACDC10JSON000197_","d":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3",' + b'"ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGsjaXFOaO_kCfS4",' + b'"s":"EMQWEcCnVRk1hatTNyK3sIykYSrrFvafX3bHQ9Gkk1kC",' + b'"a":{"d":"EF2__B6DiLQHpdJZ_C0bddxy2o6nXIHEwchO9yylr3xx",' + b'"dt":"2021-06-27T21:26:21.233257+00:00",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","LEI":"254900OPPU84GM83MG36"}},' + b'"iss":{"v":"KERI10JSON0000ed_","t":"iss",' + b'"d":"EK2WxcpF3oL1yqS3Z8i08WDYkHDcYhJL9afqdCIZjMy3",' + b'"i":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG","s":"0",' + b'"ri":"EO0_SyqPS1-EVYSITakYpUHaUZZpZGsjaXFOaO_kCfS4",' + b'"dt":"2021-06-27T21:26:21.233257+00:00"},"anc":{"v":"KERI10JSON00013a_","t":"ixn",' + b'"d":"EOjAxp-AMLzicGz2h-DxvMK9kicajpZEwdN8-8k54hvz",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","s":"2",' + b'"p":"EGKglEgIpdHuhuwl-IiSDG9x094gMrRxVaXGgXvCzCYM",' + b'"a":[{"i":"EDkftEwWBpohjTpemh_6xkaGNuoDsRU3qwvHdlvgfOyG","s":"0",' + b'"d":"EK2WxcpF3oL1yqS3Z8i08WDYkHDcYhJL9afqdCIZjMy3"}]},' + b'"d":"EI5mZXZ84Su4DrEUOxtl-NaUURQtTJeAn12xf146beg3"}}') assert ipexhan.verify(serder=grant1) is True gmsg = bytearray(grant1.raw) @@ -282,10 +298,11 @@ def test_ipex(seeder, mockCoringRandomNonce, mockHelpingNowIso8601, mockHelpingN # And now the last... admit the granted credential to complete the full flow admit0, admit0atc = protocoling.ipexAdmitExn(sidHab, "Thanks for the credential", grant=grant1) - assert admit0.raw == (b'{"v":"KERI10JSON00012a_","t":"exn","d":"ELNz82kqV94vlbT7lJulVFWtf6_jhGRgH556' - b'Z-xYRaGY","i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","p":"EIqh-L9Gnn' - b'VSdNLeqwmx-vpE9V1DvOQAlVWfwENpm8sW","dt":"2021-06-27T21:26:21.233257+00:00",' - b'"r":"/ipex/admit","q":{},"a":{"m":"Thanks for the credential"},"e":{}}') + assert admit0.raw == (b'{"v":"KERI10JSON000132_","t":"exn","d":"EAnQEaL-jSGK22VSbPN7WAUWVcxJ9LV8S5fORVAqVQzN",' + b'"i":"EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3","rp":"",' + b'"p":"ENy1ktZHowD73mn0vJL-xpTzCDpa4RuISZldAZImiKD_",' + b'"dt":"2021-06-27T21:26:21.233257+00:00","r":"/ipex/admit","q":{},"a":{"m":"Thanks for ' + b'the credential"},"e":{}}') assert ipexhan.verify(serder=admit0) is True amsg = bytearray(admit0.raw) diff --git a/tests/vc/test_proving.py b/tests/vc/test_proving.py index bdb273be1..128209153 100644 --- a/tests/vc/test_proving.py +++ b/tests/vc/test_proving.py @@ -248,18 +248,19 @@ def test_privacy_preserving_credential(mockHelpingNowIso8601): cred = credential(schema="EZllThM1rLBSMZ_ozM1uAnFvSfC0N1jaQ42aKU5sCZ5Q", recipient="EM_S2MdMaKgP6P2Yyno6-flV6GqrwPencTIw8tCMR7iB", private=True, - salt=salt, + private_credential_nonce=coring.Salter(raw=b'0123456789abcdef').qb64, + private_subject_nonce=coring.Salter(raw=b'abcdef0123456789').qb64, issuer="EMZeK1yLZd1JV6Ktdq_YUt-YbyoTWB9UMcFzuiDly2Y6", data=d, status="ETQoH02zJRCTNz-Wl3nnkUD_RVSzSwcoNvmfa18AWt3M") assert cred.size == len(cred.raw) assert "u" in cred.sad print(cred.raw) - assert cred.raw == (b'{"v":"ACDC10JSON00021c_","d":"ELFOCm58xUlId994cS6m6bsfYOkNHEKoe15Cav-Sj8__",' + assert cred.raw == (b'{"v":"ACDC10JSON00021c_","d":"EMMDzhHHlpQP0XNMRThDeIFkYD1WkDHF7Tp-8kt8X5pn",' b'"u":"0AAwMTIzNDU2Nzg5YWJjZGVm","i":"EMZeK1yLZd1JV6Ktdq_YUt-YbyoTWB9UMcFzuiDl' b'y2Y6","ri":"ETQoH02zJRCTNz-Wl3nnkUD_RVSzSwcoNvmfa18AWt3M","s":"EZllThM1rLBSM' - b'Z_ozM1uAnFvSfC0N1jaQ42aKU5sCZ5Q","a":{"d":"EFwWs1d_fe_VeLZ0vQQKO-gkRvGrpfWAR' - b'bI4e9tzcqlV","u":"0AAwMTIzNDU2Nzg5YWJjZGVm","i":"EM_S2MdMaKgP6P2Yyno6-flV6Gq' + b'Z_ozM1uAnFvSfC0N1jaQ42aKU5sCZ5Q","a":{"d":"EK3MRnlg-bMUnHtYKyZ8HD_IbBeI0v4N8' + b'YB4UnNVBqrv","u":"0ABhYmNkZWYwMTIzNDU2Nzg5","i":"EM_S2MdMaKgP6P2Yyno6-flV6Gq' b'rwPencTIw8tCMR7iB","dt":"2021-06-27T21:26:21.233257+00:00","LEI":"254900OPPU' b'84GM83MG36","personLegalName":"John Doe","engagementContextRole":"Project Ma' b'nager"}}')